Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(btc-staking): UTXOGateway and BTC staking test #132

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

adu-web3
Copy link
Collaborator

@adu-web3 adu-web3 commented Dec 13, 2024

Description

since we have refactored the original ExocoreBTCGateway in https://github.com/ExocoreNetwork/exocore-contracts/tree/btc-bridge and add unit tests to make btc staking work, we should also perform some e2e test on a local exocore node to make sure exocored works well with UTXOGateway contract.

As we need to do integration test with exocore's assets precompiles, and even with other off-chain extensions like bridge, using hardhat test is a better way to work around some of foundry's problem related with custom stateful precompiles

Summary by CodeRabbit

Based on the comprehensive summary, here are the concise release notes:

  • New Features

    • Added UTXOGateway contract for Bitcoin-like token interactions
    • Introduced new interfaces for Assets and Delegation precompiles
    • Added support for Bitcoin staking and cross-chain operations
  • Improvements

    • Updated LayerZero library imports to new package namespace
    • Enhanced error handling across multiple contracts
    • Reorganized deployment and script file structures
  • Bug Fixes

    • Added additional validation checks in contract initializations
    • Improved signature and address verification mechanisms
  • Chores

    • Updated Hardhat and Foundry test configurations
    • Cleaned up unused test and deployment fixtures

Copy link
Contributor

coderabbitai bot commented Dec 13, 2024

Walkthrough

This pull request introduces comprehensive changes across the Exocore project's codebase, focusing on library updates, file structure reorganization, and the introduction of a new UTXO Gateway contract. The primary modifications involve migrating from LayerZero V2 libraries to the LayerZero Labs namespace, restructuring script and deployment directories, and adding a new contract for handling Bitcoin-like token operations.

Changes

File/Directory Change Summary
.gitignore Added yarn-error.log to ignored files
hardhat.config.js Updated Solidity compiler version, network configuration, and added environment variables
package.json Restructured dependencies, added new scripts, updated main entry point
remappings.txt Removed redundant LayerZero library mappings
script/ Reorganized deployment scripts, updated import paths, added UTXO gateway deployment script
src/core/ Updated import paths, added UTXOGateway contract
src/interfaces/ Updated import paths for LayerZero interfaces
src/libraries/ Added new libraries like ExocoreBytes and SignatureVerifier
test/ Updated test files with new import paths, added UTXO gateway tests

Sequence Diagram

sequenceDiagram
    participant Owner
    participant UTXOGateway
    participant Witnesses
    participant AssetContract

    Owner->>UTXOGateway: Initialize Gateway
    Owner->>UTXOGateway: Add Witnesses
    Owner->>UTXOGateway: Activate Staking

    Witnesses->>UTXOGateway: Submit Proof
    UTXOGateway->>AssetContract: Verify and Process Stake
    AssetContract-->>UTXOGateway: Confirm Stake
Loading

Possibly Related PRs

Suggested Reviewers

  • bwhour
  • leonz789
  • cloud8little
  • MaxMustermann2

Poem

🐰 Hop, hop, through code's vast terrain,
LayerZero paths, we'll rearrange!
UTXO Gateway, a new frontier,
Witnesses dance, blockchain's frontier cheer!
Migrations bloom like springtime's reign 🌱


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Dec 24, 2024

✅ The Slither Analysis workflow has completed successfully. Check the workflow run for details. (b06f029)

Copy link

github-actions bot commented Dec 24, 2024

✅ The Solhint workflow has completed successfully. Check the workflow run for details. (b06f029)

Copy link

github-actions bot commented Dec 24, 2024

❌ The Forge CI workflow has failed! Check the workflow run for details. (b06f029)

Copy link

github-actions bot commented Dec 24, 2024

⏭ The Compare Storage Layouts workflow was skipped. Check the workflow run for details. (b06f029)

src/core/UTXOGateway.sol Outdated Show resolved Hide resolved
test.sh Outdated Show resolved Hide resolved
@adu-web3 adu-web3 marked this pull request as ready for review January 8, 2025 02:06
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🔭 Outside diff range comments (3)
script/deployBeaconOracle.s.sol (1)

Line range hint 21-24: Consider parameterizing hardcoded values.

The beacon oracle address and timestamp are hardcoded:

  1. The beacon oracle address 0xd3D285cd1516038dAED61B8BF7Ae2daD63662492 should be configurable
  2. The timestamp 1_715_918_948 (May 17, 2024) is a future date and should be dynamically set

Consider:

  1. Moving the beacon oracle address to a configuration file
  2. Using a dynamic timestamp based on deployment time
-        beaconOracle = EigenLayerBeaconOracle(0xd3D285cd1516038dAED61B8BF7Ae2daD63662492);
-        (bool success,) =
-            address(beaconOracle).call(abi.encodeWithSelector(beaconOracle.addTimestamp.selector, 1_715_918_948));
+        beaconOracle = EigenLayerBeaconOracle(config.beaconOracleAddress);
+        (bool success,) =
+            address(beaconOracle).call(abi.encodeWithSelector(beaconOracle.addTimestamp.selector, block.timestamp));
script/16_UpgradeExoCapsule.s.sol (1)

The review comment is correct - safety checks are necessary for the upgrade operation

The suggested safety checks are valid and align with the codebase patterns:

  1. Using address(0) in ExoCapsule constructor bypasses validation in ExoCapsuleStorage
  2. The ownership and implementation verifications follow security patterns seen in other contracts (e.g., OAppCoreUpgradeable)
  3. The suggested diff provides appropriate safety measures consistent with the codebase's practices
🔗 Analysis chain

Line range hint 26-31: Add safety checks for beacon upgrade operation.

The upgrade operation lacks proper safety checks:

  1. No verification of the new implementation
  2. No error handling for the upgrade operation
  3. Initializing capsule with address(0) could be problematic

Consider adding these safety checks:

         vm.startBroadcast(deployer.privateKey);
         console.log("owner", capsuleBeaconContract.owner());
-        ExoCapsule capsule = new ExoCapsule(address(0));
+        // Verify deployer is the owner
+        require(capsuleBeaconContract.owner() == deployer.addr, "Not authorized");
+        
+        // Deploy new implementation with proper initialization
+        ExoCapsule capsule = new ExoCapsule(config.initialAddress);
+        
+        // Verify new implementation
+        require(address(capsule).code.length > 0, "Invalid implementation");
+        
+        // Perform upgrade
         capsuleBeaconContract.upgradeTo(address(capsule));
+        
+        // Verify upgrade
+        require(capsuleBeaconContract.implementation() == address(capsule), "Upgrade failed");
         vm.stopBroadcast();
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Find ExoCapsule contract definition
ast-grep --pattern 'contract ExoCapsule {
  $$$
}'

# Find other upgrade scripts
fd -e sol -e s.sol upgrade

# Find beacon contract implementation
ast-grep --pattern 'contract $_ is BeaconProxy {
  $$$
}'

# Search for upgrade patterns
rg -A 5 "upgradeTo\(" 

Length of output: 718


Script:

#!/bin/bash
# Find ExoCapsule source file
fd -t f -e sol ExoCapsule

# Search for constructor implementation
ast-grep --pattern 'constructor($$$) {
  $$$
}'

# Look for beacon contract usage
rg -l "BeaconProxy|UpgradeableBeacon"

# Check initialization patterns
rg -A 5 "constructor.*address.*\)" 

Length of output: 5893

script/19_DeployFaucet.s.sol (1)

Line range hint 41-44: Parameterize initialization values and add proxy deployment checks.

The faucet deployment has several areas for improvement:

  1. Hardcoded initialization amount of 1 ether
  2. Empty proxy initialization data
  3. Lack of proxy deployment verification

Consider:

  1. Making the initialization amount configurable
  2. Adding proper initialization data
  3. Adding deployment verification checks
-        faucet.initialize(exocoreValidatorSet.addr, exoEthFaucet ? tokenAddr : address(0), 1 ether);
+        // Get configurable amount from environment
+        uint256 initAmount = vm.envUint("FAUCET_INIT_AMOUNT");
+        
+        // Prepare initialization data
+        bytes memory initData = abi.encodeCall(
+            CombinedFaucet.initialize,
+            (exocoreValidatorSet.addr, exoEthFaucet ? tokenAddr : address(0), initAmount)
+        );
+        
+        // Deploy proxy with initialization data
+        TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
+            address(faucetLogic),
+            address(proxyAdmin),
+            initData
+        );
+        
+        // Verify deployment
+        CombinedFaucet faucet = CombinedFaucet(payable(address(proxy)));
+        require(faucet.owner() == exocoreValidatorSet.addr, "Initialization failed");
🧹 Nitpick comments (22)
src/libraries/SignatureVerifier.sol (1)

7-64: Consider using OpenZeppelin's ECDSA library for signature verification

To enhance security and maintainability, consider using OpenZeppelin's ECDSA library, which provides robust and well-tested functions for signature verification, including handling of Ethereum Signed Messages and support for EIP-2098 short signatures. Leveraging a widely used library can reduce potential bugs and improve code reliability.

test/hardhat/integration/btc-stake-e2e.test.js (3)

38-129: Add error handling for network failures in fundBitcoinAddress

The fundBitcoinAddress function may encounter network errors when making API calls. Consider adding specific error handling for network failures to provide clearer feedback and retry mechanisms.


208-215: Calculate transaction fees dynamically in createStakingTransaction

The transaction fee is hardcoded as 1000 satoshis. To ensure timely confirmations and adapt to network conditions, consider calculating the fee based on the transaction size and current fee rates.

Apply this diff to calculate fees dynamically:

 const fee = 1000; // 1000 satoshis fee
+// Alternatively, calculate fee based on transaction size and fee rate
+const txSize = psbt.virtualSize();
+const feeRate = await getFeeRate(); // Implement getFeeRate() to fetch current fee rates
+const fee = txSize * feeRate;

289-293: Handle potential errors when funding the staker's Bitcoin address

When calling fundBitcoinAddress, consider adding a try-catch block to handle any potential errors and provide informative feedback if the funding process fails.

src/storage/UTXOGatewayStorage.sol (3)

471-485: Enhance validation in isValidOperatorAddress

The isValidOperatorAddress function only checks the length and prefix of the address. For better security and validation, consider implementing full Bech32 address decoding and checksum verification to ensure the operator address is valid.


492-496: Consider overflow checks in _verifyInboundNonce

While Solidity 0.8+ automatically reverts on overflows, explicitly handling potential overflows can improve code readability and safety. Consider adding a check or comment to clarify.


468-485: Visibility of isValidOperatorAddress function

The isValidOperatorAddress function is marked as public. If it's only used internally, consider changing the visibility to internal or private to reduce the contract's public interface.

Apply this diff to change the function visibility:

-function isValidOperatorAddress(string calldata addressToValidate) public pure returns (bool) {
+function isValidOperatorAddress(string calldata addressToValidate) internal pure returns (bool) {
src/core/UTXOGateway.sol (4)

792-800: Handle unsuccessful delegation in _delegate function

In the _delegate function, the boolean success is returned but not immediately acted upon. Ensure that the calling function properly handles cases when delegation fails and provides appropriate feedback.


730-756: Add checks for potential overflows when incrementing nonces

When incrementing delegationNonce, pegOutNonce, and inboundNonce, consider adding checks to prevent potential overflows, even though Solidity 0.8+ handles them. This can improve code clarity and safety.


813-815: Consistent error messages in _registerAddress

In the _registerAddress function, different require statements use different error messages. For consistency and clarity, consider using custom errors or consistent revert messages.

Apply this diff to use consistent error handling:

-require(depositor.length > 0 && exocoreAddress != address(0), "Invalid address");
-require(inboundRegistry[clientChainId][depositor] == address(0), "Depositor address already registered");
-require(outboundRegistry[clientChainId][exocoreAddress].length == 0, "Exocore address already registered");
+if (depositor.length == 0 || exocoreAddress == address(0)) {
+    revert Errors.InvalidAddress();
+}
+if (inboundRegistry[clientChainId][depositor] != address(0)) {
+    revert Errors.AddressAlreadyRegistered();
+}
+if (outboundRegistry[clientChainId][exocoreAddress].length != 0) {
+    revert Errors.AddressAlreadyRegistered();
+}

240-250: Avoid code duplication in processStakeMessage

The processStakeMessage function and submitProofForStakeMsg share similar logic. Consider refactoring common functionality into internal functions to adhere to the DRY (Don't Repeat Yourself) principle.

test/foundry/unit/UTXOGateway.t.sol (1)

488-585: Consider refactoring repeated mocking code into helper functions to improve maintainability

The setup of mock calls to external precompile addresses is repeated across multiple tests (e.g., lines 491-521 and 534-576). Extracting this setup into helper functions would reduce code duplication and improve readability.

src/interfaces/precompiles/IDelegation.sol (2)

80-86: Add documentation for the BTC gateway delegation method.

The new method lacks documentation explaining:

  • Purpose and use cases
  • Parameter descriptions
  • Security considerations

Add documentation similar to existing methods:

+    /// TRANSACTIONS
+    /// @dev delegate the client chain BTC assets to the operator through BTC gateway
+    /// @param clientChainId The layerZero chainID of the client chain
+    /// @param assetsAddress The client chain asset address
+    /// @param stakerAddress The staker address
+    /// @param operatorAddr The operator address to delegate to
+    /// @param opAmount The delegation amount

88-94: Add documentation for the BTC gateway undelegation method.

The new method lacks documentation explaining:

  • Purpose and use cases
  • Parameter descriptions
  • Security considerations

Add documentation similar to existing methods:

+    /// TRANSACTIONS
+    /// @dev undelegate the client chain BTC assets from the operator through BTC gateway
+    /// @param clientChainId The layerZero chainID of the client chain
+    /// @param assetsAddress The client chain asset address
+    /// @param stakerAddress The staker address
+    /// @param operatorAddr The operator address to undelegate from
+    /// @param opAmount The undelegation amount
script/hardhat/deploy-and-setup-utxogateway.script.js (1)

18-26: Consider using Promise.all for parallel transactions.

The sequential distribution of ether to accounts could be optimized by using Promise.all to execute transactions in parallel.

-  for (const account of initialAccounts) {
-      const tx = await faucet.sendTransaction({
-          to: account.address,
-          value: ethers.parseEther("1"),
-      });
-      await tx.wait();
-      assert(await ethers.provider.getBalance(account.address) > 0, "no enough balance for account")
-  }
+  await Promise.all(initialAccounts.map(async (account) => {
+      const tx = await faucet.sendTransaction({
+          to: account.address,
+          value: ethers.parseEther("1"),
+      });
+      await tx.wait();
+      assert(await ethers.provider.getBalance(account.address) > 0, "no enough balance for account")
+  }));
test/hardhat/integration/btc-stake.test.js (2)

1-92: Test setup looks good but consider adding error handling for ETH transfers.

The test setup is well-structured with clear initialization of accounts and contract deployment. However, the ETH transfer operations could benefit from error handling.

Consider adding try-catch blocks for ETH transfers:

-            const tx = await faucet.sendTransaction({
-                to: account.address,
-                value: ethers.parseEther("1"),
-            });
+            try {
+                const tx = await faucet.sendTransaction({
+                    to: account.address,
+                    value: ethers.parseEther("1"),
+                });
+                await tx.wait();
+            } catch (error) {
+                console.error(`Failed to transfer ETH to ${account.address}:`, error);
+                throw error;
+            }

141-247: Consider adding negative test cases for BTC staking.

While the happy path is well tested, consider adding test cases for:

  • Invalid message signatures
  • Insufficient proofs
  • Duplicate proof submissions
  • Invalid staking amounts

Example negative test case:

it("should fail when submitting duplicate proofs", async () => {
    // ... setup code ...
    
    // Submit first proof
    await utxoGateway.connect(relayer).submitProofForStakeMsg(
        witness1.address,
        stakeMsg,
        proofs[0]
    );
    
    // Try to submit the same proof again
    await expect(
        utxoGateway.connect(relayer).submitProofForStakeMsg(
            witness1.address,
            stakeMsg,
            proofs[0]
        )
    ).to.be.revertedWith("Duplicate proof");
});
src/libraries/Errors.sol (1)

317-398: LGTM! Comprehensive error handling for UTXO operations.

The error definitions are well-structured and cover all critical aspects of UTXO operations, including witness management, transaction validation, and state checks.

Consider adding these additional error cases for enhanced robustness:

  1. Timeout-related errors for witness consensus
  2. Gas limit exceeded errors for complex operations
script/deployments/deployedContracts.json (1)

24-31: Ensure configuration consistency across deployment files.

The UTXOGateway configuration matches the localnet deployment file but inherits the same witness configuration issue. Consider consolidating deployment configurations into a single source of truth to prevent inconsistencies.

package.json (1)

40-42: Move hardhat-gas-reporter to devDependencies.

The hardhat-gas-reporter package is a development tool and should be in devDependencies instead of dependencies.

  "dependencies": {
    "@openzeppelin/upgrades-core": "^1.40.0",
-   "ethers": "^6.9.0",
-   "hardhat-gas-reporter": "^1.0.9"
+   "ethers": "^6.9.0"
  },
  "devDependencies": {
+   "hardhat-gas-reporter": "^1.0.9",
    // ... other dev dependencies
  }
script/11_SetPeers.s.sol (2)

Line range hint 1-1: Update Solidity version to match the project configuration.

The script uses Solidity 0.8.19, but the project configuration in hardhat.config.js (mentioned in the AI summary) uses 0.8.22.

-pragma solidity ^0.8.19;
+pragma solidity ^0.8.22;

Line range hint 42-45: Optimize gas usage in setPeer transactions.

The current implementation makes separate transactions for setPeer calls. Consider batching these operations to reduce gas costs.

Would you like me to propose an implementation for batching these operations?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47a9a15 and b06f029.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (67)
  • .gitignore (1 hunks)
  • hardhat.config.js (2 hunks)
  • package.json (1 hunks)
  • remappings.txt (0 hunks)
  • script/10_DeployExocoreGatewayOnly.s.sol (3 hunks)
  • script/11_SetPeers.s.sol (2 hunks)
  • script/12_RedeployClientChainGateway.s.sol (3 hunks)
  • script/13_DepositValidator.s.sol (2 hunks)
  • script/14_CorrectBootstrapErrors.s.sol (4 hunks)
  • script/15_DeploySafeMulstisigWallet.s.sol (1 hunks)
  • script/16_UpgradeExoCapsule.s.sol (1 hunks)
  • script/17_WithdrawalValidator.s.sol (4 hunks)
  • script/18_SimulateReceive.s.sol (1 hunks)
  • script/19_DeployFaucet.s.sol (1 hunks)
  • script/1_Prerequisites.s.sol (2 hunks)
  • script/2_DeployBoth.s.sol (2 hunks)
  • script/3_Setup.s.sol (2 hunks)
  • script/4_Deposit.s.sol (2 hunks)
  • script/5_Withdraw.s.sol (2 hunks)
  • script/6_CreateExoCapsule.s.sol (3 hunks)
  • script/7_DeployBootstrap.s.sol (3 hunks)
  • script/8_RegisterValidatorsAndDelegate.s.sol (1 hunks)
  • script/9_ExtendBootstrapTime.s.sol (1 hunks)
  • script/BaseScript.sol (1 hunks)
  • script/TestPrecompileErrorFixed.s.sol (0 hunks)
  • script/TestPrecompileErrorFixed_Deploy.s.sol (0 hunks)
  • script/TokenTransfer.s.sol (0 hunks)
  • script/deployBeaconOracle.s.sol (1 hunks)
  • script/deployments/deployedContracts.json (1 hunks)
  • script/deployments/utxo-gateway-exocore_localnet.json (1 hunks)
  • script/hardhat/deploy-and-setup-utxogateway.script.js (1 hunks)
  • slither.config.json (1 hunks)
  • src/core/BaseRestakingController.sol (1 hunks)
  • src/core/ClientChainGateway.sol (1 hunks)
  • src/core/ExocoreGateway.sol (1 hunks)
  • src/core/RewardVault.sol (1 hunks)
  • src/core/UTXOGateway.sol (1 hunks)
  • src/interfaces/IClientChainGateway.sol (1 hunks)
  • src/interfaces/IExocoreGateway.sol (1 hunks)
  • src/interfaces/precompiles/IAssets.sol (6 hunks)
  • src/interfaces/precompiles/IDelegation.sol (1 hunks)
  • src/libraries/Errors.sol (1 hunks)
  • src/libraries/ExocoreBytes.sol (1 hunks)
  • src/libraries/SignatureVerifier.sol (1 hunks)
  • src/lzApp/OAppCoreUpgradeable.sol (1 hunks)
  • src/lzApp/OAppReceiverUpgradeable.sol (1 hunks)
  • src/lzApp/OAppSenderUpgradeable.sol (1 hunks)
  • src/storage/UTXOGatewayStorage.sol (1 hunks)
  • test/foundry/Delegation.t.sol (1 hunks)
  • test/foundry/DepositThenDelegateTo.t.sol (1 hunks)
  • test/foundry/DepositWithdrawPrinciple.t.sol (1 hunks)
  • test/foundry/ExocoreDeployer.t.sol (1 hunks)
  • test/foundry/Governance.t.sol (1 hunks)
  • test/foundry/TvlLimits.t.sol (1 hunks)
  • test/foundry/unit/ClientChainGateway.t.sol (1 hunks)
  • test/foundry/unit/ExocoreGateway.t.sol (2 hunks)
  • test/foundry/unit/UTXOGateway.t.sol (1 hunks)
  • test/hardhat/client-chain/deploy_fixture.js (0 hunks)
  • test/hardhat/client-chain/gateway.test.js (0 hunks)
  • test/hardhat/exocore/deploy_fixture.js (0 hunks)
  • test/hardhat/exocore/gateway.test.js (0 hunks)
  • test/hardhat/integration/btc-stake-e2e.test.js (1 hunks)
  • test/hardhat/integration/btc-stake.test.js (1 hunks)
  • test/mocks/AssetsMock.sol (6 hunks)
  • test/mocks/DelegationMock.sol (5 hunks)
  • test/mocks/ExocoreGatewayMock.sol (1 hunks)
  • test/mocks/NonShortCircuitLzEndpointMock.sol (0 hunks)
💤 Files with no reviewable changes (9)
  • test/hardhat/exocore/gateway.test.js
  • test/hardhat/client-chain/gateway.test.js
  • script/TestPrecompileErrorFixed.s.sol
  • script/TokenTransfer.s.sol
  • remappings.txt
  • script/TestPrecompileErrorFixed_Deploy.s.sol
  • test/hardhat/client-chain/deploy_fixture.js
  • test/mocks/NonShortCircuitLzEndpointMock.sol
  • test/hardhat/exocore/deploy_fixture.js
✅ Files skipped from review due to trivial changes (19)
  • test/foundry/DepositWithdrawPrinciple.t.sol
  • .gitignore
  • script/18_SimulateReceive.s.sol
  • src/lzApp/OAppCoreUpgradeable.sol
  • src/lzApp/OAppReceiverUpgradeable.sol
  • src/lzApp/OAppSenderUpgradeable.sol
  • test/foundry/TvlLimits.t.sol
  • script/BaseScript.sol
  • test/foundry/Governance.t.sol
  • src/core/BaseRestakingController.sol
  • test/foundry/ExocoreDeployer.t.sol
  • test/foundry/Delegation.t.sol
  • src/core/ClientChainGateway.sol
  • src/interfaces/IClientChainGateway.sol
  • test/foundry/DepositThenDelegateTo.t.sol
  • test/foundry/unit/ExocoreGateway.t.sol
  • src/interfaces/IExocoreGateway.sol
  • src/core/ExocoreGateway.sol
  • test/mocks/ExocoreGatewayMock.sol
🧰 Additional context used
📓 Learnings (1)
test/mocks/AssetsMock.sol (1)
Learnt from: adu-web3
PR: ExocoreNetwork/exocore-contracts#23
File: test/mocks/AssetsMock.sol:9-28
Timestamp: 2024-11-12T10:03:05.536Z
Learning: The `AssetsMock` contract is used for simulating a production precompiled contract, and its complexity in data structures is intentional to mimic production behavior.
🪛 Biome (1.9.4)
hardhat.config.js

[error] 21-21: This property value named chainId is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named chainId

(lint/suspicious/noDuplicateObjectKeys)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (47)
test/mocks/AssetsMock.sol (2)

37-39: Verify staker address validation for BTC chain IDs

In the depositLST function, the staker address length check is skipped for BTC chain IDs (clientChainLzId == clientBtcChainId). Please verify that omitting this validation for BTC is intentional and does not introduce security vulnerabilities.


81-82: Verify withdrawal logic for BTC and unregistered tokens

In the withdrawLST function, the condition allows withdrawals for BTC (even if not registered) but disallows withdrawals for ETH and unregistered tokens. Please confirm that this behavior is intentional and aligns with the expected policies for token withdrawals.

src/interfaces/precompiles/IAssets.sol (3)

10-45: New structs TokenInfo and StakerBalance are well-defined

The introduction of TokenInfo and StakerBalance structs provides comprehensive data structures for managing token information and staker balances. The fields are appropriately defined to encapsulate necessary details.


158-162: Ensure proper access control for updateAuthorizedGateways function

The updateAuthorizedGateways function allows updating authorized gateways. Please ensure that appropriate access controls are implemented to prevent unauthorized modifications to the list of authorized gateways.


180-199: Addition of query functions enhances contract interaction

The new functions getTokenInfo and getStakerBalanceByToken improve the interface by providing essential data retrieval capabilities for tokens and staker balances, facilitating better contract interaction.

test/hardhat/integration/btc-stake-e2e.test.js (1)

96-99: Adjust dust threshold check for the change output

When calculating the change output, you check if change > 546. The dust threshold can vary depending on the Bitcoin network and policy settings. Ensure that the dust limit is appropriate for the regtest network or make it configurable.

script/9_ExtendBootstrapTime.s.sol (1)

16-16: File path updated correctly

The file path for reading deployed contracts has been updated to match the new directory structure, ensuring the script functions properly.

hardhat.config.js (2)

20-31: Verify environment variables for local testing

The configuration now uses multiple test account private keys from environment variables. Ensure these are properly documented and included in the local development setup instructions.

Consider creating a .env.example file with placeholder values for all required environment variables to help other developers set up their local environment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 21-21: This property value named chainId is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named chainId

(lint/suspicious/noDuplicateObjectKeys)


8-8: Verify compatibility with upgraded Solidity version

The upgrade to Solidity 0.8.22 includes breaking changes and new features. Please ensure all smart contracts, especially the UTXOGateway and BTC staking contracts, are compatible with this version.

✅ Verification successful

Solidity 0.8.22 upgrade is compatible with the codebase

All core contracts, including UTXOGateway and staking-related contracts, use ^0.8.19 which is compatible with 0.8.22. The few contracts using different versions are either non-critical components or external interfaces that won't be affected by this upgrade.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential compatibility issues in Solidity files
echo "Searching for Solidity files with explicit version pragma..."
fd -e sol | xargs grep -l "pragma solidity"

Length of output: 3815


Script:

#!/bin/bash
# Check pragma versions in core contracts
echo "=== Checking pragma versions in core contracts ==="
rg "pragma solidity" src/core/ -B 0 -A 1

echo -e "\n=== Checking pragma versions in interfaces ==="
rg "pragma solidity" src/interfaces/ -B 0 -A 1

echo -e "\n=== Looking for potential version-specific features ==="
rg "pragma solidity [\^>=]0\.8\." -l | xargs rg -l "assembly|unchecked|custom error|error [A-Z]|using.*for.*global"

Length of output: 3758

script/6_CreateExoCapsule.s.sol (2)

11-12: LGTM: Updated LayerZero import paths

The import paths have been updated to use the new @layerzerolabs namespace, which aligns with the library's organization.


26-26: LGTM: Improved deployment artifacts organization

Moving JSON files to the script/deployments/ directory improves the organization of deployment-related artifacts.

Also applies to: 56-56

script/15_DeploySafeMulstisigWallet.s.sol (1)

27-27: LGTM: Consistent deployment artifacts location

Moving the safe contracts JSON file to script/deployments/ maintains consistency with the project's deployment file organization.

src/core/RewardVault.sol (1)

28-29: LGTM: Added critical address validation

The zero-address check for the gateway parameter is a crucial security measure that prevents initialization with an invalid address.

Let's verify that similar checks exist in related contracts:

✅ Verification successful

Zero-address validation is consistently implemented

The zero-address check for the gateway parameter is consistently implemented across related contracts:

  • RewardVault.sol: require(gateway_ != address(0), "Gateway address cannot be zero")
  • ExoCapsule.sol: require(gateway_ != address(0), "ExoCapsule: gateway address can not be empty")
  • Vault.sol: if (underlyingToken_ == address(0) || gateway_ == address(0))
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for zero-address checks in gateway-related contracts
ast-grep --pattern 'require($_ != address(0), $_)'

Length of output: 52


Script:

#!/bin/bash
# Search for various forms of zero-address validation
echo "=== Searching require statements ==="
rg -U "require\(.*address.*!= address\(0\).*\)" --type sol

echo -e "\n=== Searching revert statements ==="
rg -U "revert.*address.*zero" --type sol

echo -e "\n=== Searching if conditions ==="
rg -U "if.*address.*== address\(0\).*revert" --type sol

echo -e "\n=== Searching custom errors ==="
rg "error.*ZeroAddress" --type sol

Length of output: 638


Script:

#!/bin/bash
# Find Solidity files first
echo "=== Finding Solidity files ==="
fd "\.sol$"

echo -e "\n=== Searching gateway initialization and zero-address checks ==="
rg -U "gateway.*=.*" -A 2 -B 2

echo -e "\n=== Searching zero-address validations ==="
rg -U "require\(.*!= address\(0\).*\)"

echo -e "\n=== Searching constructor and initialize patterns ==="
rg -U "function (constructor|initialize).*\{" -A 5

Length of output: 75015

script/10_DeployExocoreGatewayOnly.s.sol (2)

3-3: LGTM: Updated LayerZero import path

The import path has been updated to use the new @layerzerolabs namespace, maintaining consistency with the library organization.


19-19: LGTM: Consistent deployment artifacts organization

JSON files have been moved to the script/deployments/ directory, maintaining a consistent structure for deployment artifacts.

Also applies to: 58-58

script/1_Prerequisites.s.sol (2)

5-5: LGTM! Library update aligned with LayerZero V2 migration.

The import path has been updated to use the official LayerZero Labs namespace.


76-76: Verify deployment artifacts directory exists.

The JSON output path has been updated to use a deployments subdirectory.

✅ Verification successful

Directory structure verified successfully

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the deployments directory exists
if [ ! -d "script/deployments" ]; then
  echo "Warning: script/deployments directory does not exist"
  exit 1
fi

Length of output: 35


Script:

#!/bin/bash
# List all directories under script/
echo "=== Directories under script/ ==="
fd --type d . script/

# Check for JSON files in script/ and subdirectories
echo -e "\n=== JSON files in script/ ==="
fd --extension json . script/

# Look for directory creation/setup in script files
echo -e "\n=== Directory setup in scripts ==="
rg -l "mkdir.*deployments|mkdirp.*deployments" script/

Length of output: 1191

script/5_Withdraw.s.sol (1)

11-12: LGTM! Consistent library updates.

Import paths have been updated to use the official LayerZero Labs namespace, maintaining consistency across the codebase.

src/interfaces/precompiles/IDelegation.sol (1)

80-94: Verify replay protection mechanism for BTC gateway operations.

The new BTC gateway methods don't include the lzNonce parameter present in the regular delegation methods. This could affect replay protection.

script/4_Deposit.s.sol (2)

10-11: LGTM! Consistent library updates.

Import paths have been updated to use the official LayerZero Labs namespace, maintaining consistency across the codebase.


23-23: Verify JSON file path consistency.

The JSON input path has been updated to match the new deployments directory structure.

✅ Verification successful

JSON file path is correctly structured and consistent

The file exists at script/deployments/deployedContracts.json and is consistently referenced across multiple deployment scripts and tests. The path aligns with the repository's organized structure where all deployment-related JSON files are stored in the script/deployments/ directory.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the deployedContracts.json exists in the new location
if [ ! -f "script/deployments/deployedContracts.json" ]; then
  echo "Warning: deployedContracts.json not found in script/deployments/"
  exit 1
fi

Length of output: 58


Script:

#!/bin/bash
# Check directory structure
echo "=== Directory Structure ==="
ls -R script/ 2>/dev/null || echo "script/ directory not found"

echo -e "\n=== All JSON Files ==="
fd -e json

echo -e "\n=== References to deployedContracts.json ==="
rg "deployedContracts.json" -l

Length of output: 2909

script/8_RegisterValidatorsAndDelegate.s.sol (1)

51-51: LGTM! File path update aligns with new directory structure.

The change to use the new deployments directory is consistent with the project's reorganization.

script/12_RedeployClientChainGateway.s.sol (2)

22-22: LGTM! Updated LayerZero import path.

The import path has been updated to use the new @layerzerolabs namespace, which is the correct package.


34-34: LGTM! File paths updated to use deployments directory.

File paths have been consistently updated to use the new deployments directory structure.

Also applies to: 89-89

script/13_DepositValidator.s.sol (2)

10-12: LGTM! Updated LayerZero imports.

The imports have been correctly updated to use the new @layerzerolabs namespace.


38-39: LGTM! File paths updated to use appropriate subdirectories.

Files have been organized into appropriate subdirectories:

  • Validator proof moved to data/ directory
  • Deployed contracts moved to deployments/ directory
test/mocks/DelegationMock.sol (1)

9-9: LGTM! Mapping name change improves clarity.

The rename from delegateTo to delegateToRecords better describes the purpose of the mapping.

Also applies to: 44-44, 66-69, 113-113

script/hardhat/deploy-and-setup-utxogateway.script.js (1)

63-67: Verify the security implications of the required proofs parameter.

The hardcoded value of 3 for requiredProofs is a critical security parameter. Ensure this value aligns with the security requirements of the system.

Run this script to check if this value is consistent across the codebase:

✅ Verification successful

The requiredProofs value of 3 is secure and properly validated

The value is protected by:

  • Explicit bounds (1-10) defined in contract storage
  • Validation in both initialization and updates
  • Owner-only modification with pause protection
  • Event emission for transparency
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for requiredProofs parameter usage and its values
rg -A 2 "requiredProofs" 

Length of output: 7697


Script:

#!/bin/bash
# Search for MIN_REQUIRED_PROOFS and MAX_REQUIRED_PROOFS constants and related documentation
rg -B 2 -A 2 "MIN_REQUIRED_PROOFS|MAX_REQUIRED_PROOFS"

# Also check for any documentation in README or docs
fd -e md | xargs rg -i "proof|witness|consensus" -A 2

Length of output: 1253

script/3_Setup.s.sol (2)

12-13: LGTM! Updated import paths for LayerZero V2.

The import paths have been correctly updated to use the new @layerzerolabs/lz-evm-protocol-v2 package.


24-24: LGTM! Updated file path for deployedContracts.json.

The file path has been correctly updated to use the new deployments directory structure.

script/14_CorrectBootstrapErrors.s.sol (2)

18-18: LGTM! Updated import path for LayerZero V2.

The import path has been correctly updated to use the new @layerzerolabs/lz-evm-protocol-v2 package.


40-40: LGTM! Updated file paths for JSON files.

The file paths have been correctly updated to use the new deployments directory structure:

  • prerequisiteContracts.json
  • deployedBootstrapOnly.json
  • correctBootstrapErrors.json

Also applies to: 59-59, 139-139

script/7_DeployBootstrap.s.sol (2)

17-17: LGTM! Updated import path for LayerZero V2.

The import path has been correctly updated to use the new @layerzerolabs/lz-evm-protocol-v2 package.


32-32: LGTM! Updated file paths for JSON files.

The file paths have been correctly updated to use the new deployments directory structure:

  • prerequisiteContracts.json
  • deployedBootstrapOnly.json

Also applies to: 150-150

script/17_WithdrawalValidator.s.sol (2)

12-13: LGTM! Library imports updated to use the new LayerZero package.

The imports have been correctly updated to use @layerzerolabs/lz-evm-protocol-v2 instead of the older @layerzero-v2 package.


45-45: LGTM! JSON file paths updated for better organization.

The file paths have been correctly updated to use the new directory structure:

  • Deployment data moved to script/deployments/
  • Withdrawal proof data moved to script/data/

Also applies to: 98-98, 105-105, 121-121, 128-128

script/2_DeployBoth.s.sol (2)

17-17: LGTM! LayerZero endpoint import updated.

The import has been correctly updated to use @layerzerolabs/lz-evm-protocol-v2.


28-28: LGTM! Prerequisites file path updated.

The file path has been correctly updated to use the new script/deployments/ directory.

test/hardhat/integration/btc-stake.test.js (2)

94-114: LGTM! Comprehensive deployment verification.

The test thoroughly verifies:

  • Contract deployment
  • Owner settings
  • Witness authorization
  • Threshold configuration
  • Gateway authorization

116-139: LGTM! Thorough BTC staking activation test.

The test properly verifies:

  • Staking activation
  • Client chain registration
  • Token registration with correct parameters
test/foundry/unit/ClientChainGateway.t.sol (1)

5-5: LGTM! LayerZero imports updated consistently.

The imports have been correctly updated to use @layerzerolabs/lz-evm-protocol-v2 package, maintaining consistency with other files.

Also applies to: 7-8

slither.config.json (1)

2-2: Reconsider excluding the mapping-deletion detector.

Excluding the mapping-deletion detector from Slither analysis could hide potential issues with improper storage cleanup in mappings, which is particularly important for upgradeable contracts like UTXOGateway.

package.json (1)

18-37: Review dependency version constraints.

Several dependencies lack version constraints or use potentially unstable versions:

  • @evmos/evmosjs is pinned to a git branch (main) which could lead to unexpected breaking changes
  • hardhat v2.22.17 is very recent and might have stability issues

Please verify the stability of these versions by running:

script/deployBeaconOracle.s.sol (1)

5-5: LGTM! Import path updated to use LayerZero Labs namespace.

The import path has been correctly updated to use the new LayerZero Labs namespace.

script/16_UpgradeExoCapsule.s.sol (1)

15-15: LGTM! JSON file path updated to new deployments directory.

The path has been correctly updated to use the new standardized deployments directory structure.

script/19_DeployFaucet.s.sol (2)

19-19: LGTM! JSON file path updated to new deployments directory.

The path has been correctly updated to use the new standardized deployments directory structure.


Line range hint 45-49: Consider storing deployment addresses securely.

The comment indicates addresses are intentionally kept private, but console logging them might expose them in logs.

Consider:

  1. Using a secure logging mechanism
  2. Encrypting sensitive data before logging
  3. Implementing proper access controls for deployment logs

Comment on lines +44 to +46
(r, s) = abi.decode(signature, (bytes32, bytes32));
v = uint8(signature[64]);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect decoding of 65-byte signatures to prevent runtime errors

When handling signatures of length 65 bytes, the code attempts to decode (r, s) using abi.decode(signature, (bytes32, bytes32)); on a signature that is 65 bytes long. This will cause a decoding error because abi.decode expects the bytes array length to match the total size of the decoded types (which is 64 bytes for two bytes32 values). To correctly extract r, s, and v from a 65-byte signature, you should manually read the bytes.

Apply this diff to fix the issue:

             } else if (signature.length == 65) {
-                (r, s) = abi.decode(signature, (bytes32, bytes32));
-                v = uint8(signature[64]);
+                // Extract r, s, and v from the signature
+                assembly {
+                    r := mload(add(signature, 0x20))           // first 32 bytes
+                    s := mload(add(signature, 0x40))           // next 32 bytes
+                    v := byte(0, mload(add(signature, 0x60)))  // final byte
+                }
+                // Ensure v value is properly formatted.
+                if (v != 27 && v != 28) {
+                    revert BadSignatureV(v);
+                }
             } else {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(r, s) = abi.decode(signature, (bytes32, bytes32));
v = uint8(signature[64]);
} else if (signature.length == 65) {
// Extract r, s, and v from the signature
assembly {
r := mload(add(signature, 0x20)) // first 32 bytes
s := mload(add(signature, 0x40)) // next 32 bytes
v := byte(0, mload(add(signature, 0x60))) // final byte
}
// Ensure v value is properly formatted.
if (v != 27 && v != 28) {
revert BadSignatureV(v);
}
} else {

Comment on lines +295 to +364
it("should complete the full staking flow", async function() {
// Get initial balance
const [success, initialBalance] = await assetsPrecompile.getStakerBalanceByToken(
CLIENT_CHAIN.BTC,
ethers.getBytes(staker.address),
BTC_ID
);

// Create and broadcast the Bitcoin transaction
const txid = await createStakingTransaction(
staker.privateKey,
BITCOIN_VAULT_ADDRESS,
depositAmount
);
console.log('Staking transaction broadcasted. TXID:', txid);

// Wait for Bitcoin confirmation
console.log('Waiting for Bitcoin confirmation...');
const confirmedTx = await waitForBitcoinConfirmation(txid);
console.log('Transaction confirmed with', confirmedTx.confirmations, 'confirmations');

// Wait for DepositCompleted event
console.log('Waiting for DepositCompleted event...');
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Timeout waiting for DepositCompleted event'));
}, 60000);

utxoGateway.on('DepositCompleted', async (
clientChainId,
txTag,
depositorExoAddr,
depositorClientChainAddr,
amount,
updatedBalance
) => {
try {
if (depositorExoAddr.toLowerCase() === staker.address.toLowerCase()) {
clearTimeout(timeout);

// Verify final balance
const [finalSuccess, finalBalance] = await assetsPrecompile.getStakerBalanceByToken(
CLIENT_CHAIN.BTC,
ethers.getBytes(staker.address),
BTC_ID
);

expect(finalSuccess).to.be.true;
const expectedIncrease = ethers.parseUnits('0.01', 8); // 0.01 BTC in satoshis

// Check balance components
expect(finalBalance[0]).to.equal(CLIENT_CHAIN.BTC); // clientChainID
expect(ethers.hexlify(finalBalance[1])).to.equal(ethers.hexlify(staker.address)); // stakerAddress
expect(ethers.hexlify(finalBalance[2])).to.equal(ethers.hexlify(BTC_ID)); // tokenId
expect(finalBalance[3]).to.equal(expectedIncrease); // balance
expect(finalBalance[7]).to.equal(expectedIncrease); // totalDeposited

console.log('Deposit completed successfully');
console.log('Initial balance:', initialBalance ? initialBalance[3] : 0);
console.log('Final balance:', finalBalance[3].toString());

resolve();
}
} catch (error) {
clearTimeout(timeout);
reject(error);
}
});
});
}).timeout(120000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure proper cleanup after the test completes

In the test case "should complete the full staking flow", ensure that event listeners are properly removed after the test to prevent side effects on subsequent tests.

Apply this diff to remove the event listener after it's no longer needed:

 utxoGateway.on('DepositCompleted', async (
     // ... parameters ...
 ) => {
     // ... test logic ...
+    utxoGateway.removeAllListeners('DepositCompleted');
     resolve();
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("should complete the full staking flow", async function() {
// Get initial balance
const [success, initialBalance] = await assetsPrecompile.getStakerBalanceByToken(
CLIENT_CHAIN.BTC,
ethers.getBytes(staker.address),
BTC_ID
);
// Create and broadcast the Bitcoin transaction
const txid = await createStakingTransaction(
staker.privateKey,
BITCOIN_VAULT_ADDRESS,
depositAmount
);
console.log('Staking transaction broadcasted. TXID:', txid);
// Wait for Bitcoin confirmation
console.log('Waiting for Bitcoin confirmation...');
const confirmedTx = await waitForBitcoinConfirmation(txid);
console.log('Transaction confirmed with', confirmedTx.confirmations, 'confirmations');
// Wait for DepositCompleted event
console.log('Waiting for DepositCompleted event...');
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Timeout waiting for DepositCompleted event'));
}, 60000);
utxoGateway.on('DepositCompleted', async (
clientChainId,
txTag,
depositorExoAddr,
depositorClientChainAddr,
amount,
updatedBalance
) => {
try {
if (depositorExoAddr.toLowerCase() === staker.address.toLowerCase()) {
clearTimeout(timeout);
// Verify final balance
const [finalSuccess, finalBalance] = await assetsPrecompile.getStakerBalanceByToken(
CLIENT_CHAIN.BTC,
ethers.getBytes(staker.address),
BTC_ID
);
expect(finalSuccess).to.be.true;
const expectedIncrease = ethers.parseUnits('0.01', 8); // 0.01 BTC in satoshis
// Check balance components
expect(finalBalance[0]).to.equal(CLIENT_CHAIN.BTC); // clientChainID
expect(ethers.hexlify(finalBalance[1])).to.equal(ethers.hexlify(staker.address)); // stakerAddress
expect(ethers.hexlify(finalBalance[2])).to.equal(ethers.hexlify(BTC_ID)); // tokenId
expect(finalBalance[3]).to.equal(expectedIncrease); // balance
expect(finalBalance[7]).to.equal(expectedIncrease); // totalDeposited
console.log('Deposit completed successfully');
console.log('Initial balance:', initialBalance ? initialBalance[3] : 0);
console.log('Final balance:', finalBalance[3].toString());
resolve();
}
} catch (error) {
clearTimeout(timeout);
reject(error);
}
});
});
}).timeout(120000);
it("should complete the full staking flow", async function() {
// Get initial balance
const [success, initialBalance] = await assetsPrecompile.getStakerBalanceByToken(
CLIENT_CHAIN.BTC,
ethers.getBytes(staker.address),
BTC_ID
);
// Create and broadcast the Bitcoin transaction
const txid = await createStakingTransaction(
staker.privateKey,
BITCOIN_VAULT_ADDRESS,
depositAmount
);
console.log('Staking transaction broadcasted. TXID:', txid);
// Wait for Bitcoin confirmation
console.log('Waiting for Bitcoin confirmation...');
const confirmedTx = await waitForBitcoinConfirmation(txid);
console.log('Transaction confirmed with', confirmedTx.confirmations, 'confirmations');
// Wait for DepositCompleted event
console.log('Waiting for DepositCompleted event...');
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Timeout waiting for DepositCompleted event'));
}, 60000);
utxoGateway.on('DepositCompleted', async (
clientChainId,
txTag,
depositorExoAddr,
depositorClientChainAddr,
amount,
updatedBalance
) => {
try {
if (depositorExoAddr.toLowerCase() === staker.address.toLowerCase()) {
clearTimeout(timeout);
// Verify final balance
const [finalSuccess, finalBalance] = await assetsPrecompile.getStakerBalanceByToken(
CLIENT_CHAIN.BTC,
ethers.getBytes(staker.address),
BTC_ID
);
expect(finalSuccess).to.be.true;
const expectedIncrease = ethers.parseUnits('0.01', 8); // 0.01 BTC in satoshis
// Check balance components
expect(finalBalance[0]).to.equal(CLIENT_CHAIN.BTC); // clientChainID
expect(ethers.hexlify(finalBalance[1])).to.equal(ethers.hexlify(staker.address)); // stakerAddress
expect(ethers.hexlify(finalBalance[2])).to.equal(ethers.hexlify(BTC_ID)); // tokenId
expect(finalBalance[3]).to.equal(expectedIncrease); // balance
expect(finalBalance[7]).to.equal(expectedIncrease); // totalDeposited
console.log('Deposit completed successfully');
console.log('Initial balance:', initialBalance ? initialBalance[3] : 0);
console.log('Final balance:', finalBalance[3].toString());
utxoGateway.removeAllListeners('DepositCompleted');
resolve();
}
} catch (error) {
clearTimeout(timeout);
reject(error);
}
});
});
}).timeout(120000);

src/core/UTXOGateway.sol Show resolved Hide resolved
src/libraries/ExocoreBytes.sol Show resolved Hide resolved
Comment on lines +111 to +118
const deploymentsDir = path.join(__dirname, '../deployments');
if (!fs.existsSync(deploymentsDir)) {
fs.mkdirSync(deploymentsDir);
}
fs.writeFileSync(
path.join(deploymentsDir, `utxo-gateway-${network.name}.json`),
JSON.stringify(deploymentInfo, null, 2)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for file operations.

File system operations should include proper error handling to manage potential failures gracefully.

-    if (!fs.existsSync(deploymentsDir)) {
-      fs.mkdirSync(deploymentsDir);
-    }
-    fs.writeFileSync(
-      path.join(deploymentsDir, `utxo-gateway-${network.name}.json`),
-      JSON.stringify(deploymentInfo, null, 2)
-    );
+    try {
+        if (!fs.existsSync(deploymentsDir)) {
+            fs.mkdirSync(deploymentsDir, { recursive: true });
+        }
+        fs.writeFileSync(
+            path.join(deploymentsDir, `utxo-gateway-${network.name}.json`),
+            JSON.stringify(deploymentInfo, null, 2)
+        );
+        console.log("Successfully saved deployment info");
+    } catch (error) {
+        console.error("Failed to save deployment info:", error);
+        throw error;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const deploymentsDir = path.join(__dirname, '../deployments');
if (!fs.existsSync(deploymentsDir)) {
fs.mkdirSync(deploymentsDir);
}
fs.writeFileSync(
path.join(deploymentsDir, `utxo-gateway-${network.name}.json`),
JSON.stringify(deploymentInfo, null, 2)
);
const deploymentsDir = path.join(__dirname, '../deployments');
try {
if (!fs.existsSync(deploymentsDir)) {
fs.mkdirSync(deploymentsDir, { recursive: true });
}
fs.writeFileSync(
path.join(deploymentsDir, `utxo-gateway-${network.name}.json`),
JSON.stringify(deploymentInfo, null, 2)
);
console.log("Successfully saved deployment info");
} catch (error) {
console.error("Failed to save deployment info:", error);
throw error;
}

Comment on lines +11 to +14
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"deploy:utxo-gateway": "hardhat run script/hardhat/deploy-and-setup-utxogateway.script.js --network exocore_localnet"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix the test script implementation.

The current test script exits with an error message, which contradicts the PR's focus on adding unit tests for BTC staking. This should be updated to run the actual test suite.

- "test": "echo \"Error: no test specified\" && exit 1",
+ "test": "hardhat test",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"deploy:utxo-gateway": "hardhat run script/hardhat/deploy-and-setup-utxogateway.script.js --network exocore_localnet"
},
"scripts": {
"test": "hardhat test",
"deploy:utxo-gateway": "hardhat run script/hardhat/deploy-and-setup-utxogateway.script.js --network exocore_localnet"
},

Comment on lines +23 to 24
string memory deployed = vm.readFile("script/deployments/deployedBootstrapOnly.json");
bootstrapAddr = stdJson.readAddress(deployed, ".clientChain.bootstrap");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for JSON file reading.

The vm.readFile and stdJson.readAddress operations lack proper error handling. If the files don't exist or the JSON structure is invalid, it will fail silently.

-        string memory deployed = vm.readFile("script/deployments/deployedBootstrapOnly.json");
-        bootstrapAddr = stdJson.readAddress(deployed, ".clientChain.bootstrap");
+        try vm.readFile("script/deployments/deployedBootstrapOnly.json") returns (string memory deployed) {
+            try stdJson.readAddress(deployed, ".clientChain.bootstrap") returns (address addr) {
+                bootstrapAddr = addr;
+            } catch {
+                revert("Invalid JSON structure or missing bootstrap address");
+            }
+        } catch {
+            revert("Failed to read deployedBootstrapOnly.json");
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
string memory deployed = vm.readFile("script/deployments/deployedBootstrapOnly.json");
bootstrapAddr = stdJson.readAddress(deployed, ".clientChain.bootstrap");
try vm.readFile("script/deployments/deployedBootstrapOnly.json") returns (string memory deployed) {
try stdJson.readAddress(deployed, ".clientChain.bootstrap") returns (address addr) {
bootstrapAddr = addr;
} catch {
revert("Invalid JSON structure or missing bootstrap address");
}
} catch {
revert("Failed to read deployedBootstrapOnly.json");
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants