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

Update Hello and Swap to authenticated calls #218

Merged
merged 61 commits into from
Nov 24, 2024
Merged

Conversation

fadeev
Copy link
Member

@fadeev fadeev commented Nov 8, 2024

Call

npx hardhat compile --force
npx hardhat deploy --gateway 0x6c533f7fe93fae114d0954697069df33c9b74fd7 --network zeta_testnet --name Universal
npx hardhat deploy --gateway 0x0c487a766110c85d301d96e33579c5b317fa4995 --network base_sepolia --name Connected
🔑 Using account: 0x4955a3F38ff86ae92A914445099caa8eA2B9bA32

🚀 Successfully deployed "Universal" contract on zeta_testnet.
📜 Contract address: 0x496CD66950a1F1c5B02513809A2d37fFB942be1B

🔑 Using account: 0x4955a3F38ff86ae92A914445099caa8eA2B9bA32

🚀 Successfully deployed "Connected" contract on base_sepolia.
📜 Contract address: 0x775329c70A8d09AEb5e5ca92C369FF3155C5f1Ed

Call from Base to ZetaChain

npx hardhat connected-call \
  --contract 0x775329c70A8d09AEb5e5ca92C369FF3155C5f1Ed \
  --receiver 0x496CD66950a1F1c5B02513809A2d37fFB942be1B \
  --network base_sepolia \
  --tx-options-gas-price 20000 --types '["string"]' alice

https://sepolia.basescan.org/tx/0x133cdf3a06195de0a6bb89dd4761ca98d1301534b3c4987f0ff93c95c3fff78c

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0x133cdf3a06195de0a6bb89dd4761ca98d1301534b3c4987f0ff93c95c3fff78c

Call from ZetaChain to Base

npx hardhat universal-call \
  --contract 0x496CD66950a1F1c5B02513809A2d37fFB942be1B \
  --receiver 0x775329c70A8d09AEb5e5ca92C369FF3155C5f1Ed \
  --zrc20 0x236b0DE675cC8F46AE186897fCCeFe3370C9eDeD \
  --function "hello(string)" \
  --network zeta_testnet \
  --tx-options-gas-price 200000 --types '["string"]' alice

https://zetachain-testnet.blockscout.com/tx/0x19d476fa2c3d29ba41467ae7f2742541fd11e0b67d6548fe7655a3d40274323e

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0x19d476fa2c3d29ba41467ae7f2742541fd11e0b67d6548fe7655a3d40274323e

Withdraw And Call from ZetaChain to Base

npx hardhat universal-withdraw-and-call \
  --contract 0x496CD66950a1F1c5B02513809A2d37fFB942be1B \
  --receiver 0x775329c70A8d09AEb5e5ca92C369FF3155C5f1Ed \
  --zrc20 0x236b0DE675cC8F46AE186897fCCeFe3370C9eDeD \
  --function "hello(string)" \
  --amount 0.005 \
  --network zeta_testnet \
  --types '["string"]' hello

https://zetachain-testnet.blockscout.com/tx/0x67099389ab6cb44ee03602d164320c615720b57762c5ddab042d65bdbe30c7a2

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0x67099389ab6cb44ee03602d164320c615720b57762c5ddab042d65bdbe30c7a2

Swap

npx hardhat compile --force 

npx hardhat deploy --name Swap --gateway 0x6c533f7fe93fae114d0954697069df33c9b74fd7 --system-contract 0xEdf1c3275d13489aCdC6cD6eD246E72458B8795B --network zeta_testnet

🚀 Successfully deployed contract on zeta_testnet.
📜 Contract address: 0xf4fE3154c6dBb5492D3A4BbcF6A68878DF61C8A0
npx hardhat evm-deposit-and-call \
  --receiver 0xf4fE3154c6dBb5492D3A4BbcF6A68878DF61C8A0 \
  --amount 0.01 \
  --network bsc_testnet \
  --gateway-evm 0x0c487a766110c85d301d96e33579c5b317fa4995 \
  --types '["address", "bytes"]' 0x05BA149A7bd6dC1F937fA9046A9e05C05f3b18b0 0x4955a3F38ff86ae92A914445099caa8eA2B9bA32

https://testnet.bscscan.com/tx/0x0d944bbfd5c9f3a9ffba14d342c30808a0e61e9cd6e7889ea6e557a266f3527d

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0x0d944bbfd5c9f3a9ffba14d342c30808a0e61e9cd6e7889ea6e557a266f3527d

https://sepolia.basescan.org/tx/0x990d2f4c9dcbb96f0b61f6a9d06d6680e45cbdc6f4af9ab4fff2ba0207c9a199

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0x990d2f4c9dcbb96f0b61f6a9d06d6680e45cbdc6f4af9ab4fff2ba0207c9a199

https://amoy.polygonscan.com/tx/0xb162f4a9a2414a7fd63ef3e7d34a113af14d325a97cab1cc8036d7d958eafae9

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0xb162f4a9a2414a7fd63ef3e7d34a113af14d325a97cab1cc8036d7d958eafae9

NFT

npx hardhat compile --force

npx hardhat deploy --gateway 0x6c533f7fe93fae114d0954697069df33c9b74fd7 --network zeta_testnet --gas-limit 500000 --name Universal

🚀 Successfully deployed "Universal" contract on zeta_testnet.
📜 Contract address: 0xCD58653a854817692079efd8ef0f6928aA66F628

npx hardhat deploy --gateway 0x0c487a766110c85d301d96e33579c5b317fa4995 --network bsc_testnet --name Connected

🚀 Successfully deployed "Connected" contract on bsc_testnet.
📜 Contract address: 0x31F76a088dF9802A283088cC8976f2395944712A

npx hardhat deploy --gateway 0x0c487a766110c85d301d96e33579c5b317fa4995 --network base_sepolia --name Connected

🚀 Successfully deployed "Connected" contract on base_sepolia.
📜 Contract address: 0xe36eA381567Ac954D3d068EC7033724B545E5Ae2

npx hardhat connected-set-counterparty --network bsc_testnet --contract 0x31F76a088dF9802A283088cC8976f2395944712A --counterparty 0xCD58653a854817692079efd8ef0f6928aA66F628 --json

{"contractAddress":"0x31F76a088dF9802A283088cC8976f2395944712A","universalContract":"0xCD58653a854817692079efd8ef0f6928aA66F628","transactionHash":"0x7a7ba679db5210ce6b8290e20883749e0601408162b43bc37a10b6ee7146919b"}

npx hardhat connected-set-counterparty --network base_sepolia --contract 0xe36eA381567Ac954D3d068EC7033724B545E5Ae2 --counterparty 0xCD58653a854817692079efd8ef0f6928aA66F628 --json

{"contractAddress":"0xe36eA381567Ac954D3d068EC7033724B545E5Ae2","universalContract":"0xCD58653a854817692079efd8ef0f6928aA66F628","transactionHash":"0xe9b4039e138f7c068ca288a403a6a7499454286323b4b9b6cc12c93e80e88561"}

npx hardhat universal-set-counterparty --network zeta_testnet --contract 0xCD58653a854817692079efd8ef0f6928aA66F628 --counterparty 0x31F76a088dF9802A283088cC8976f2395944712A --zrc20 0xd97B1de3619ed2c6BEb3860147E30cA8A7dC9891 --json

{"contractAddress":"0xCD58653a854817692079efd8ef0f6928aA66F628","zrc20":"0xd97B1de3619ed2c6BEb3860147E30cA8A7dC9891","connectedContractAddress":"0x31F76a088dF9802A283088cC8976f2395944712A","transactionHash":"0x27e7c5a1878eca4fce74405e1fe722c7ef3660abfb5bc51c9b8912ceb65b5a5b"}

npx hardhat universal-set-counterparty --network zeta_testnet --contract 0xCD58653a854817692079efd8ef0f6928aA66F628 --counterparty 0xe36eA381567Ac954D3d068EC7033724B545E5Ae2 --zrc20 0x236b0DE675cC8F46AE186897fCCeFe3370C9eDeD --json

{"contractAddress":"0xCD58653a854817692079efd8ef0f6928aA66F628","zrc20":"0x236b0DE675cC8F46AE186897fCCeFe3370C9eDeD","connectedContractAddress":"0xe36eA381567Ac954D3d068EC7033724B545E5Ae2","transactionHash":"0x9ffc05de29db59a719bde8bc3f6de43baf660af1e5702e21f367ebfb5916c25c"}

npx hardhat mint --network zeta_testnet --json --contract 0xCD58653a854817692079efd8ef0f6928aA66F628 --token-uri https://example.com/nft/metadata/1

{"contractAddress":"0xCD58653a854817692079efd8ef0f6928aA66F628","mintTransactionHash":"0x76f359263dfcccd898e7564f102d54b11daa763028cc725ee5bd3f05bcc6c2c9","recipient":"0x4955a3F38ff86ae92A914445099caa8eA2B9bA32","tokenURI":"https://example.com/nft/metadata/1","tokenId":"456003645905194763481523353718654221867725429194"}

ZetaChain -> Base NFT transfer

npx hardhat transfer --network zeta_testnet --json --token-id 456003645905194763481523353718654221867725429194 --from 0xCD58653a854817692079efd8ef0f6928aA66F628 --to 0x236b0DE675cC8F46AE186897fCCeFe3370C9eDeD
{"contractAddress":"0xCD58653a854817692079efd8ef0f6928aA66F628","transferTransactionHash":"0x554dec1282f3c367b4a465e391a0887f5b891f86ff2f32048c16c4932c278c4c","sender":"0x4955a3F38ff86ae92A914445099caa8eA2B9bA32","tokenId":"456003645905194763481523353718654221867725429194"}

https://zetachain-testnet.blockscout.com/tx/0x554dec1282f3c367b4a465e391a0887f5b891f86ff2f32048c16c4932c278c4c

https://zetachain-athens.blockpi.network/lcd/v1/public/zeta-chain/crosschain/inboundHashToCctxData/0x554dec1282f3c367b4a465e391a0887f5b891f86ff2f32048c16c4932c278c4c

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced new tasks for deploying and interacting with contracts, including connected-deposit, connected-deposit-and-call, and universal-withdraw.
    • Added a new localnet.sh script for managing local blockchain interactions.
    • Enhanced the Connected and Universal contracts with improved error handling and gas limit management.
    • Updated README files with new tutorial links for better user guidance.
    • Implemented new Swap and SwapToAnyToken contracts for facilitating cross-chain token swaps.
    • Updated deployment scripts to include new parameters for Uniswap integration.
    • Added support for managing gas limits in cross-chain operations.
    • Updated the localnet.sh scripts across various examples to improve deployment logic and parameter handling.
    • Renamed tasks and parameters to reflect updated terminology, including changes from "counterparty" to "universal" in relevant tasks.
    • Removed obsolete files and scripts related to the omnichain swap functionality.
  • Bug Fixes

    • Improved error handling in various tasks to ensure better feedback during contract interactions.
  • Chores

    • Updated dependency versions across multiple package.json files to ensure compatibility and access to the latest features.
    • Added .gitignore entries for localnet.json across multiple directories to manage ignored files.

Copy link

coderabbitai bot commented Nov 8, 2024

📝 Walkthrough

Walkthrough

This pull request includes significant changes across multiple files, primarily focusing on the removal and modification of workflows, scripts, and smart contracts related to the ZetaChain ecosystem. Key alterations include the deletion of the GitHub Actions workflow for publishing NPM packages, updates to Slither and test workflows, the introduction of new Solidity contracts, and the restructuring of deployment scripts. Additionally, several tasks for interacting with smart contracts have been added or modified, enhancing the overall functionality and error handling of the contracts.

Changes

File Path Change Summary
.github/workflows/publish-npm.yml Deleted GitHub Actions workflow for publishing Node.js packages to NPM.
.github/workflows/slither.yaml Modified to add projects examples/call, examples/nft, and examples/token with their respective SARIF outputs; removed project omnichain/swap.
.github/workflows/test.yaml Updated to change the test script from test.sh to localnet.sh, reflecting a shift in the testing approach.
examples/call/README.md Added a tutorial link for "call" functionality in the README.
examples/call/contracts/Connected.sol Introduced a new Solidity contract Connected with multiple deposit functions, events, and error handling.
examples/call/contracts/Universal.sol Renamed contract from Hello to Universal, updated function signatures, and reintroduced onCall and onRevert functions.
examples/call/hardhat.config.ts Updated import statements and simplified Solidity compiler configuration to version 0.8.26.
examples/call/package.json Modified scripts section, replacing deploy with deploy:localnet and updated several devDependencies versions.
examples/call/scripts/localnet.sh Added a new script for local network deployment and interaction with smart contracts.
examples/call/tasks/connectedCall.ts Updated task name and description, adjusted parameter defaults, and refined logging.
examples/call/tasks/connectedDeposit.ts Introduced a new task for depositing tokens to ZetaChain with various parameters.
examples/call/tasks/connectedDepositAndCall.ts Added a new task for depositing tokens and executing a call to a universal application.
examples/call/tasks/deploy.ts Introduced a new deployment task with enhanced error handling and output options.
examples/call/tasks/universalCall.ts Updated task functionality and parameters for making calls to contracts on ZetaChain.
examples/call/tasks/universalWithdraw.ts Added a new task for withdrawing ZRC-20 tokens.
examples/call/tasks/universalWithdrawAndCall.ts Enhanced task for withdrawing tokens and calling a function on revert.
examples/hello/README.md Removed code snippets related to EVM and ZetaChain calls; added a new tutorial link.
examples/hello/contracts/Echo.sol Deleted the Echo contract.
examples/hello/contracts/Universal.sol Introduced a new Universal contract with event handling and message processing.
examples/hello/hardhat.config.ts Removed import statements for specific tasks related to the hello functionality.
examples/hello/package.json Updated dependency versions for ZetaChain packages.
examples/hello/scripts/localnet.sh Added a new script for local network deployment and interaction.
examples/nft/contracts/Connected.sol Modified to include a new gas limit variable and enhanced error handling.
examples/nft/contracts/Universal.sol Updated to allow dynamic assignment of the systemContract address and improved error handling.
examples/nft/contracts/shared/Events.sol Updated event signature for CounterpartySet.
examples/nft/scripts/localnet.sh Modified to handle new deployment parameters and control flow.
examples/nft/tasks/deploy.ts Enhanced deployment task with additional parameters for system contract and gas limit.
omnichain/swap/README.md Deleted introductory documentation for the ZetaChain Contracts Template.
omnichain/swap/contracts/Swap.sol Deleted the Swap contract.
omnichain/swap/contracts/SwapToAnyToken.sol Deleted the SwapToAnyToken contract.
omnichain/swap/contracts/shared/MockContract.sol Deleted mock contract for testing purposes.
omnichain/swap/tasks/deploy.ts Deleted deployment task for the Swap contract.
omnichain/swap/tasks/interact.ts Deleted task for interacting with the contract.
omnichain/swap/tasks/solana/deposit.ts Deleted deposit function for Solana.
omnichain/swap/tasks/solana/gateway.json Deleted configuration file for Solana program.
omnichain/swap/tasks/solana/interact.ts Deleted task for interacting with Solana contracts.
omnichain/swap/tasks/swap.ts Deleted task for interacting with the Swap contract.
omnichain/swap/test/Swap.spec.ts Deleted test suite for the Swap contract.
omnichain/swap/test/SwapToAnyToken.spec.ts Deleted test suite for the SwapToAnyToken contract.
scripts/build.sh Modified to focus on the examples directory, removing references to omnichain.

Possibly related PRs

Suggested reviewers

  • andresaiello: Suggested for review due to expertise in the relevant areas of the project.

Warning

Rate limit exceeded

@fadeev has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a6d9809 and 83ab10b.


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 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 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.

@github-advanced-security
Copy link

This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation.

examples/call/contracts/Connected.sol Fixed Show fixed Hide fixed
Comment on lines 29 to 38
function deposit(
address receiver,
uint256 amount,
address asset,
RevertOptions memory revertOptions
) external {
IERC20(asset).transferFrom(msg.sender, address(this), amount);
IERC20(asset).approve(address(gateway), amount);
gateway.deposit(receiver, amount, asset, revertOptions);
}

Check warning

Code scanning / Slither

Unused return Medium

examples/call/contracts/Connected.sol Fixed Show fixed Hide fixed
Comment on lines 48 to 58
function depositAndCall(
address receiver,
uint256 amount,
address asset,
bytes calldata message,
RevertOptions memory revertOptions
) external {
IERC20(asset).transferFrom(msg.sender, address(this), amount);
IERC20(asset).approve(address(gateway), amount);
gateway.depositAndCall(receiver, amount, asset, message, revertOptions);
}

Check warning

Code scanning / Slither

Unused return Medium

Copy link

@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: 2

🧹 Outside diff range and nitpick comments (2)
examples/token/contracts/Universal.sol (1)

Add nonReentrant modifier to protect against reentrancy attacks

The contract makes external calls to untrusted contracts in multiple functions without reentrancy protection:

  • transferCrossChain(): Makes external calls to IZRC20 and gateway
  • onCall(): Makes external calls to SwapHelperLib and gateway
  • State changes occur after these external calls

Required changes:

  • Import ReentrancyGuard from OpenZeppelin
  • Add nonReentrant modifier to transferCrossChain() and onCall()
  • Consider following checks-effects-interactions pattern
🔗 Analysis chain

Line range hint 1-152: Verify reentrancy protection for external calls

The contract makes multiple external calls to Uniswap and the gateway. Let's verify the safety of these interactions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential reentrancy vulnerabilities in external calls

# Look for state changes after external calls
rg -A 5 "call\(" .
rg -A 5 "transfer" .
rg -A 5 "transferFrom" .

# Check for nonReentrant modifiers
ast-grep --pattern 'modifier nonReentrant'

Length of output: 23631

examples/nft/contracts/Universal.sol (1)

Line range hint 116-124: Enhance token ID generation uniqueness.

The current token ID generation using block number and contract address could potentially create collisions in high-frequency minting scenarios. Consider using a more robust approach:

-uint256 hash = uint256(
-    keccak256(
-        abi.encodePacked(address(this), block.number, _nextTokenId++)
-    )
-);
-uint256 tokenId = hash & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
+uint256 tokenId = uint256(
+    keccak256(
+        abi.encode(
+            address(this),
+            block.timestamp,
+            block.number,
+            msg.sender,
+            _nextTokenId++
+        )
+    )
+);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 75438c3 and aa55fa3.

📒 Files selected for processing (2)
  • examples/nft/contracts/Universal.sol (5 hunks)
  • examples/token/contracts/Universal.sol (5 hunks)
🔇 Additional comments (9)
examples/token/contracts/Universal.sol (8)

16-16: LGTM: Appropriate use of immutable state variable

The immutable declaration for uniswapRouter ensures the router address cannot be changed post-deployment, which is a security best practice.


29-29: LGTM: Improved error handling with custom errors

The use of custom errors instead of require statements is more gas-efficient and provides better error reporting.


38-45: Skip: Issue already reported

The missing zero address validation for uniswapRouterAddress has already been reported in previous reviews and remains valid.


72-72: LGTM: Enhanced message encoding with sender tracking

The addition of msg.sender to both the message and revert options improves cross-chain transaction traceability.

Also applies to: 80-80


106-111: LGTM: Clear and structured message decoding

The message decoding is well-structured and matches the encoding format used in transferCrossChain.


118-125: Skip: Slippage protection issue already reported

The missing slippage protection in swapExactTokensForTokens has already been reported in previous reviews and remains valid.


126-138: LGTM: Proper handling of cross-chain token transfers

The implementation correctly:

  • Manages gas fees
  • Formats messages consistently
  • Handles token approvals

145-147: LGTM: Consistent revert message handling

The revert message decoding properly aligns with the encoding format used in the revert options.

examples/nft/contracts/Universal.sol (1)

26-26: Consider upgrade implications of immutable router.

While the immutable uniswapRouter provides gas optimization and security benefits, it may limit future upgrades if Uniswap deploys new router versions. Consider implementing an upgradeable router pattern if protocol evolution is anticipated.

Also applies to: 36-36, 48-55, 58-58

Comment on lines +86 to +92
bytes memory message = abi.encode(
receiver,
tokenId,
uri,
0,
msg.sender
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add message format versioning for cross-chain compatibility.

The message encoding format has changed. Consider adding a version identifier to support backward compatibility and future format changes:

 bytes memory message = abi.encode(
+    uint8(1), // format version
     receiver,
     tokenId,
     uri,
     0,
     msg.sender
 );
📝 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
bytes memory message = abi.encode(
receiver,
tokenId,
uri,
0,
msg.sender
);
bytes memory message = abi.encode(
uint8(1), // format version
receiver,
tokenId,
uri,
0,
msg.sender
);

Copy link

@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: 2

🧹 Outside diff range and nitpick comments (3)
examples/swap/contracts/Swap.sol (3)

Line range hint 34-52: Add input validation for message parsing

The message parsing logic lacks validation for message length and content, which could lead to out-of-bounds reads or invalid data processing.

Consider adding these validations:

 ) external onlyGateway {
     Params memory params = Params({target: address(0), to: bytes("")});
     if (context.chainID == BITCOIN) {
+        require(message.length >= 40, "Invalid Bitcoin message length");
         params.target = BytesHelperLib.bytesToAddress(message, 0);
         params.to = abi.encodePacked(
             BytesHelperLib.bytesToAddress(message, 20)
         );
     } else {
+        require(message.length > 0, "Empty message");
         (address targetToken, bytes memory recipient) = abi.decode(
             message,
             (address, bytes)
         );
+        require(recipient.length > 0, "Empty recipient");
         params.target = targetToken;
         params.to = recipient;
     }

Line range hint 84-89: Add slippage protection to prevent sandwich attacks

The swap operation uses a minimum output amount of 0, which makes it vulnerable to sandwich attacks.

Consider adding a minimum output parameter:

-            0
+            SwapHelperLib.calculateMinimumOutputAmount(outputAmount)

Line range hint 91-97: Optimize token approvals

Multiple token approvals could be combined to save gas.

Consider consolidating the approvals:

-        if (gasZRC20 == targetToken) {
-            IZRC20(gasZRC20).approve(address(gateway), outputAmount + gasFee);
-        } else {
-            IZRC20(gasZRC20).approve(address(gateway), gasFee);
-            IZRC20(targetToken).approve(address(gateway), outputAmount);
-        }
+        uint256 gasTokenAmount = gasZRC20 == targetToken ? outputAmount + gasFee : gasFee;
+        IZRC20(gasZRC20).approve(address(gateway), gasTokenAmount);
+        if (gasZRC20 != targetToken) {
+            IZRC20(targetToken).approve(address(gateway), outputAmount);
+        }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fd03f00 and 91b8121.

📒 Files selected for processing (1)
  • examples/swap/contracts/Swap.sol (6 hunks)
🧰 Additional context used
🪛 GitHub Check: Slither
examples/swap/contracts/Swap.sol

[notice] 24-24: Missing zero address validation
Swap.constructor(address,address).uniswapRouterAddress (contracts/Swap.sol#24) lacks a zero-check on :
- uniswapRouter = uniswapRouterAddress (contracts/Swap.sol#25)

@@ -12,7 +12,7 @@
import {GatewayZEVM} from "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol";

contract Swap is UniversalContract {
SystemContract public systemContract;
address public immutable uniswapRouter;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add critical address validations in constructor

The constructor lacks zero-address validation for critical components. An invalid router or gateway address would render the contract unusable.

Apply this diff to add the necessary checks:

 constructor(address payable gatewayAddress, address uniswapRouterAddress) {
+    require(gatewayAddress != address(0), "Invalid gateway address");
+    require(uniswapRouterAddress != address(0), "Invalid router address");
     uniswapRouter = uniswapRouterAddress;
     gateway = GatewayZEVM(gatewayAddress);
 }

Also applies to: 24-26

Comment on lines 114 to +115
RevertContext calldata revertContext
) external override onlyGateway {}
) external onlyGateway {}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement revert handling logic

The empty onRevert implementation might miss critical error handling scenarios.

Consider implementing basic revert handling:

-    ) external onlyGateway {}
+    ) external onlyGateway {
+        // Emit event for monitoring
+        emit RevertReceived(
+            revertContext.sender,
+            revertContext.targetZRC20,
+            revertContext.amount
+        );
+        // Additional revert handling logic
+    }
+
+    event RevertReceived(address indexed sender, address indexed token, uint256 amount);

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@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: 5

🧹 Outside diff range and nitpick comments (2)
examples/nft/contracts/Connected.sol (1)

56-60: Consider implementing collision prevention in token ID generation

While the current implementation provides good entropy, it doesn't prevent potential token ID collisions. Consider adding a collision check before minting.

 uint256 tokenId = hash & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
+if (_exists(tokenId)) {
+    revert TokenIdCollision(tokenId);
+}
 _safeMint(to, tokenId);

Also applies to: 62-62

examples/nft/contracts/Universal.sol (1)

181-183: Enhance TokenTransferReverted event with failure context.

Include the failed destination address in the event to help with debugging and monitoring.

-emit TokenTransferReverted(sender, tokenId, uri);
+emit TokenTransferReverted(sender, tokenId, uri, context.destination);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 91b8121 and 4cfaa55.

📒 Files selected for processing (5)
  • examples/nft/contracts/Connected.sol (4 hunks)
  • examples/nft/contracts/Universal.sol (4 hunks)
  • examples/swap/contracts/Swap.sol (5 hunks)
  • examples/swap/contracts/SwapToAnyToken.sol (5 hunks)
  • examples/token/contracts/Universal.sol (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/swap/contracts/Swap.sol
  • examples/swap/contracts/SwapToAnyToken.sol
🧰 Additional context used
🪛 GitHub Check: Slither
examples/nft/contracts/Connected.sol

[notice] 119-119: Missing zero address validation
Connected.onCall(MessageContext,bytes).sender (contracts/Connected.sol#119) lacks a zero-check on :
- (success,None) = sender.call{value: gasAmount}() (contracts/Connected.sol#125)

🔇 Additional comments (11)
examples/token/contracts/Universal.sol (6)

16-16: LGTM: Immutable state variables properly declared

The use of immutable variables for uniswapRouter and gasLimit is optimal for gas efficiency and security.

Also applies to: 19-19


29-29: LGTM: Improved error handling with custom errors

The use of custom errors instead of require statements is more gas efficient and provides better error messaging.


38-49: LGTM: Constructor properly initialized with validations

The constructor correctly initializes all immutable variables with proper validation checks.


75-75: LGTM: Enhanced cross-chain message traceability

The addition of sender address in message encoding improves transaction traceability.

Also applies to: 83-83


148-150: LGTM: Consistent message decoding

The message decoding order matches the encoding pattern used in other functions.


121-128: ⚠️ Potential issue

Add slippage protection to prevent sandwich attacks

Setting minAmountOut to 0 provides no slippage protection, making transactions vulnerable to sandwich attacks and price manipulation.

examples/nft/contracts/Connected.sol (4)

23-23: LGTM: Well-structured state variable and error declarations

The immutable gasLimit and new error types enhance the contract's safety and efficiency.

Also applies to: 27-28


45-46: LGTM: Robust constructor implementation with proper validation

The gas parameter validation and assignment to immutable gasLimit enhance contract security.

Also applies to: 50-51


78-83: LGTM: Well-structured cross-chain transfer implementation

The message encoding and gateway calls are properly implemented with appropriate RevertOptions configuration.

Also applies to: 86-90, 95-101


133-135: LGTM: Correct message decoding implementation

The message decoding properly handles the updated message format.

examples/nft/contracts/Universal.sol (1)

Line range hint 63-68: Update CounterpartySet event parameters for consistency.

The function parameter type has been changed from bytes to address, but the corresponding event in Events.sol might still be using bytes. Ensure the event definition is updated to maintain consistency.

Comment on lines 121 to 141
uint256 out = SwapHelperLib.swapExactTokensForTokens(
uniswapRouter,
zrc20,
amount,
destination,
0
);
IZRC20(destination).approve(address(gateway), gasFee);
gateway.call(
counterparty[destination],
IZRC20(destination).approve(address(gateway), out);
gateway.withdrawAndCall(
abi.encodePacked(counterparty[destination]),
out - gasFee,
destination,
abi.encode(receiver, tokenAmount),
abi.encode(receiver, tokenAmount, out - gasFee, sender),
CallOptions(gasLimit, false),
RevertOptions(address(0), false, address(0), "", 0)
RevertOptions(
address(this),
true,
address(0),
abi.encode(tokenAmount, sender),
0
)
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Critical: Missing reentrancy protection in external calls

The contract lacks ReentrancyGuard implementation while making multiple external calls that could be vulnerable to reentrancy attacks:

  • In onCall(): Sequential external calls to swapExactTokensForTokens(), approve(), and withdrawAndCall() with state changes
  • In transferCrossChain(): Sequential external calls to transferFrom(), approve(), and call()

Recommended fixes:

  • Import OpenZeppelin's ReentrancyGuard
  • Add inheritance: contract Universal is ERC20, Ownable2Step, UniversalContract, Events, ReentrancyGuard
  • Add nonReentrant modifier to onCall() and transferCrossChain()
🔗 Analysis chain

Verify reentrancy protection in external calls

The contract makes external calls to swap tokens and withdraw. Let's verify the reentrancy protection.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for reentrancy vulnerabilities in external calls

# Search for ReentrancyGuard usage
rg -l "ReentrancyGuard"

# Check for potential reentrancy vulnerabilities in external calls
ast-grep --pattern 'function $FNAME($$$) {
  $$$
  $EXTERNAL_CALL($$$);
  $STATE_CHANGE($$$);
  $$$
}'

Length of output: 128


Script:

#!/bin/bash
# Let's check the contract implementation more thoroughly

# First, check if ReentrancyGuard is imported
rg -A 2 "import.*ReentrancyGuard" examples/token/contracts/

# Check contract inheritance
rg -A 5 "contract\s+Universal\s+is\s+" examples/token/contracts/

# Look for nonReentrant modifier usage
rg "nonReentrant" examples/token/contracts/

# Check the full contract implementation for state changes after external calls
cat examples/token/contracts/Universal.sol

Length of output: 5870

Comment on lines +114 to +120
(
address receiver,
uint256 tokenId,
string memory uri,
uint256 gasAmount,
address sender
) = abi.decode(message, (address, uint256, string, uint256, address));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance security measures for gas token transfers

The current implementation needs additional safety checks:

  1. Add zero-address validation for sender
  2. Use OpenZeppelin's Address.sendValue for safer ETH transfers
+import "@openzeppelin/contracts/utils/Address.sol";

 contract Connected is
     ERC721,
     ERC721Enumerable,
     ERC721URIStorage,
     Ownable2Step,
     Events
 {
+    using Address for address payable;
     
     // ... in onCall function
     ) = abi.decode(message, (address, uint256, string, uint256, address));
+    if (sender == address(0)) revert InvalidAddress();
     _safeMint(receiver, tokenId);
     _setTokenURI(tokenId, uri);
     if (gasAmount > 0) {
-        (bool success, ) = sender.call{value: gasAmount}("");
-        if (!success) revert GasTokenTransferFailed();
+        payable(sender).sendValue(gasAmount);
     }

Also applies to: 124-127

🧰 Tools
🪛 GitHub Check: Slither

[notice] 119-119: Missing zero address validation
Connected.onCall(MessageContext,bytes).sender (contracts/Connected.sol#119) lacks a zero-check on :
- (success,None) = sender.call{value: gasAmount}() (contracts/Connected.sol#125)

Comment on lines 162 to 174
gateway.withdrawAndCall(
abi.encodePacked(counterparty[destination]),
out - gasFee,
destination,
abi.encode(sender, tokenId, uri),
abi.encode(receiver, tokenId, uri, out - gasFee, sender),
CallOptions(gasLimit, false),
RevertOptions(address(0), false, address(0), "", 0)
RevertOptions(
address(this),
true,
address(0),
abi.encode(tokenId, uri, sender),
0
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add slippage protection for cross-chain transfers.

The current implementation doesn't protect against price slippage during cross-chain transfers. Consider adding a minimum output amount parameter.

 gateway.withdrawAndCall(
     abi.encodePacked(counterparty[destination]),
     out - gasFee,
     destination,
-    abi.encode(receiver, tokenId, uri, out - gasFee, sender),
+    abi.encode(receiver, tokenId, uri, out - gasFee, sender, block.timestamp + 1800), // 30 min deadline
     CallOptions(gasLimit, false),
     RevertOptions(
         address(this),
         true,
         address(0),
-        abi.encode(tokenId, uri, sender),
+        abi.encode(tokenId, uri, sender, block.timestamp + 1800),
         0
     )
 );
📝 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
gateway.withdrawAndCall(
abi.encodePacked(counterparty[destination]),
out - gasFee,
destination,
abi.encode(sender, tokenId, uri),
abi.encode(receiver, tokenId, uri, out - gasFee, sender),
CallOptions(gasLimit, false),
RevertOptions(address(0), false, address(0), "", 0)
RevertOptions(
address(this),
true,
address(0),
abi.encode(tokenId, uri, sender),
0
)
gateway.withdrawAndCall(
abi.encodePacked(counterparty[destination]),
out - gasFee,
destination,
abi.encode(receiver, tokenId, uri, out - gasFee, sender, block.timestamp + 1800), // 30 min deadline
CallOptions(gasLimit, false),
RevertOptions(
address(this),
true,
address(0),
abi.encode(tokenId, uri, sender, block.timestamp + 1800),
0
)

Comment on lines 134 to 142
if (context.sender != counterparty[zrc20]) revert Unauthorized();

(
address destination,
address sender,
address receiver,
uint256 tokenId,
string memory uri
) = abi.decode(message, (address, address, uint256, string));
string memory uri,
address sender
) = abi.decode(message, (address, address, uint256, string, address));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation for decoded receiver address.

The decoded receiver address should be validated to prevent minting to zero address.

 ) = abi.decode(message, (address, address, uint256, string, address));
+if (receiver == address(0)) revert InvalidAddress();
📝 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
if (context.sender != counterparty[zrc20]) revert Unauthorized();
(
address destination,
address sender,
address receiver,
uint256 tokenId,
string memory uri
) = abi.decode(message, (address, address, uint256, string));
string memory uri,
address sender
) = abi.decode(message, (address, address, uint256, string, address));
if (context.sender != counterparty[zrc20]) revert Unauthorized();
(
address destination,
address receiver,
uint256 tokenId,
string memory uri,
address sender
) = abi.decode(message, (address, address, uint256, string, address));
if (receiver == address(0)) revert InvalidAddress();

Comment on lines +48 to 59
uint256 gas,
address uniswapRouterAddress
) ERC721(name, symbol) Ownable(owner) {
if (gatewayAddress == address(0) || owner == address(0))
revert InvalidAddress();
if (
gatewayAddress == address(0) ||
owner == address(0) ||
uniswapRouterAddress == address(0)
) revert InvalidAddress();
if (gas == 0) revert InvalidGasLimit();
gateway = GatewayZEVM(gatewayAddress);
uniswapRouter = uniswapRouterAddress;
gasLimit = gas;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add upper bound validation for gas limit.

While the constructor validates that gas limit is non-zero, it should also check for an upper bound to prevent excessive gas consumption in cross-chain calls.

 if (gas == 0) revert InvalidGasLimit();
+if (gas > 1000000) revert InvalidGasLimit(); // adjust threshold as needed
 gateway = GatewayZEVM(gatewayAddress);

Committable suggestion skipped: line range outside the PR's diff.

@fadeev
Copy link
Member Author

fadeev commented Nov 19, 2024

@andresaiello please, review.

Copy link

@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: 19

🧹 Outside diff range and nitpick comments (9)
examples/nft/hardhat.config.ts (2)

Line range hint 1-2: Remove duplicate import statement

The "./tasks/deploy" module is imported twice, which is redundant.

Apply this diff to fix the duplicate import:

import "./tasks/deploy";
-import "./tasks/deploy";
import "./tasks/mint";

Line range hint 18-18: Fix invalid Solidity version

The specified Solidity version "0.8.26" is invalid. The latest stable version is 0.8.24.

Apply this diff to use the correct version:

-  solidity: "0.8.26",
+  solidity: "0.8.24",
examples/nft/contracts/shared/Events.sol (1)

5-6: Consider adding NatSpec documentation for events.

The events would benefit from NatSpec documentation explaining their purpose and parameters.

Consider adding documentation like this:

+    /// @notice Emitted when the Universal contract address is set
+    /// @param universalAddress The address of the Universal contract
     event SetUniversal(address indexed universalAddress);
 
+    /// @notice Emitted when a Connected contract is associated with a ZRC20 token
+    /// @param zrc20 The address of the ZRC20 token
+    /// @param contractAddress The address of the Connected contract
     event SetConnected(address indexed zrc20, address contractAddress);
examples/token/tasks/connectedSetUniversal.ts (1)

35-35: Add parameter validation for contract addresses

The task should validate that the provided addresses are valid Ethereum addresses.

Consider adding validation before the main execution:

const validateAddress = (address: string): boolean => {
  return hre.ethers.utils.isAddress(address);
};

task("connected-set-universal", "Sets the universal contract address", async (args, hre) => {
  if (!validateAddress(args.contract) || !validateAddress(args.universal)) {
    throw new Error("Invalid Ethereum address provided");
  }
  await main(args, hre);
})

Also applies to: 37-37

examples/nft/tasks/connectedSetUniversal.ts (1)

Line range hint 31-33: Fix inconsistent variable reference in console log

The console log uses args.universalContract but the correct variable is args.universal, which could cause undefined values in the output.

Apply this diff to fix the inconsistency:

    console.log(`🚀 Successfully set the universal contract.
📜 Contract address: ${args.contract}
-🔗 Universal contract address: ${args.universalContract}
+🔗 Universal contract address: ${args.universal}
🔗 Transaction hash: ${tx.hash}`);
examples/nft/tasks/universalSetConnected.ts (1)

Line range hint 29-34: Fix inconsistent variable name in console log message

The console log message uses args.contractAddress but the parameter is named args.connected.

Apply this fix:

 console.log(`🚀 Successfully set the connected contract.
 📜 Contract address: ${args.contract}
 🔗 ZRC20 address: ${args.zrc20}
-🔗 Connected contract address: ${args.contractAddress}
+🔗 Connected contract address: ${args.connected}
 🔗 Transaction hash: ${tx.hash}`);
examples/nft/scripts/localnet.sh (1)

4-5: Consider making debug mode conditional

While set -x is valuable for debugging, it should be conditionally enabled to prevent exposing sensitive information in production logs.

-set -x
+if [ "${DEBUG:-false}" = "true" ]; then
+  set -x
+fi
examples/nft/contracts/Connected.sol (1)

56-62: Consider using a more unique source of randomness for tokenId generation

The current implementation uses block.number which could lead to tokenId collisions if multiple mints occur in the same block. Consider using additional entropy sources or OpenZeppelin's Counters utility.

-        uint256 hash = uint256(
-            keccak256(
-                abi.encodePacked(address(this), block.number, _nextTokenId++)
-            )
-        );
+        uint256 hash = uint256(
+            keccak256(
+                abi.encodePacked(
+                    address(this),
+                    block.number,
+                    block.timestamp,
+                    msg.sender,
+                    _nextTokenId++
+                )
+            )
+        );
examples/nft/contracts/Universal.sol (1)

86-92: Add message format versioning for future compatibility.

The message encoding format should include a version identifier to support future format changes.

 bytes memory message = abi.encode(
+    uint8(1), // format version
     receiver,
     tokenId,
     uri,
     0,
     msg.sender
 );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4cfaa55 and 6a15652.

⛔ Files ignored due to path filters (1)
  • examples/token/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • examples/nft/contracts/Connected.sol (4 hunks)
  • examples/nft/contracts/Universal.sol (4 hunks)
  • examples/nft/contracts/shared/Events.sol (1 hunks)
  • examples/nft/hardhat.config.ts (1 hunks)
  • examples/nft/scripts/localnet.sh (3 hunks)
  • examples/nft/tasks/connectedSetUniversal.ts (2 hunks)
  • examples/nft/tasks/universalSetConnected.ts (2 hunks)
  • examples/token/contracts/Connected.sol (2 hunks)
  • examples/token/contracts/Universal.sol (4 hunks)
  • examples/token/contracts/shared/Events.sol (1 hunks)
  • examples/token/hardhat.config.ts (1 hunks)
  • examples/token/package.json (1 hunks)
  • examples/token/scripts/localnet.sh (4 hunks)
  • examples/token/tasks/connectedSetUniversal.ts (2 hunks)
  • examples/token/tasks/universalSetConnected.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • examples/token/hardhat.config.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/token/contracts/shared/Events.sol
  • examples/token/package.json
🧰 Additional context used
🪛 GitHub Check: Slither
examples/token/contracts/Connected.sol

[notice] 94-94: Missing zero address validation
Connected.onCall(MessageContext,bytes).sender (contracts/Connected.sol#94) lacks a zero-check on :
- (success,None) = sender.call{value: amount}() (contracts/Connected.sol#98)

🔇 Additional comments (26)
examples/nft/hardhat.config.ts (1)

5-6: LGTM! Task imports are well-structured

The renaming from "Counterparty" to "Connected/Universal" maintains a clear and consistent naming convention, improving code readability.

examples/nft/contracts/shared/Events.sol (1)

5-6: Event signature changes look good, verify consumers.

The new event signatures are well-structured with appropriate parameter indexing. The shift from counterparty to universal/connected terminology aligns with the architectural changes.

Let's verify the event consumers in the codebase:

✅ Verification successful

Let me try a different search strategy to verify the event consumers.


Let me try again without specifying the file type:


Event signature changes are properly implemented across the codebase

The verification confirms:

  • Both events are consistently used in both NFT and token implementations
  • No remnants of old event patterns (SetCounterparty/CounterpartySet)
  • Events are emitted in appropriate contexts:
    • SetUniversal in Connected.sol contracts
    • SetConnected in Universal.sol contracts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all contracts that might be affected by the event changes

# Search for old event names to ensure complete migration
echo "Checking for any remaining old event references..."
rg -l "SetCounterparty|CounterpartySet" 

# Search for new event implementations
echo "Checking new event implementations..."
ast-grep --pattern 'emit Set(Universal|Connected)($$$)'

Length of output: 295


Script:

#!/bin/bash
# Search for files that import or inherit from Events.sol
echo "Files importing or inheriting Events:"
rg -l "import.*Events" --type sol

# Search for event emissions with context
echo -e "\nEvent emission patterns:"
rg "emit Set(Universal|Connected)" -A 2 --type sol

# Search for event listeners/handlers
echo -e "\nPotential event listeners:"
rg "(SetUniversal|SetConnected).*event" --type sol

Length of output: 444


Script:

#!/bin/bash
# Search for files that import or inherit from Events
echo "Files importing or inheriting Events:"
rg -l "import.*Events"

# Search for event emissions with context
echo -e "\nEvent emission patterns:"
rg "emit Set(Universal|Connected)" -A 2

# Search for event listeners/handlers
echo -e "\nPotential event listeners:"
rg "(SetUniversal|SetConnected).*event"

# Check for any remaining old event patterns
echo -e "\nOld event patterns:"
rg "SetCounterparty|CounterpartySet"

Length of output: 1256

examples/token/tasks/connectedSetUniversal.ts (1)

Line range hint 18-37: Verify consistent renaming across the codebase

Let's ensure all references to "counterparty" have been updated to "universal" consistently.

✅ Verification successful

Rebranding verification completed: No remaining 'counterparty' references found

The codebase has been thoroughly checked and shows consistent usage of "universal" terminology with no lingering references to "counterparty". All relevant files and interfaces correctly use the updated terminology.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining instances of "counterparty" in the codebase
echo "Searching for remaining 'counterparty' references..."
rg -i "counterparty" --type ts

# Search for any potential inconsistencies in naming patterns
echo "Checking for consistent use of 'universal'..."
rg -i "setUniversal|getUniversal|universal" --type ts

Length of output: 4971

examples/nft/tasks/connectedSetUniversal.ts (2)

37-39: LGTM: Task definition is clear and consistent

The task name and parameter descriptions accurately reflect the new terminology and purpose.


18-18: Verify the contract interface update

The code calls setUniversal method on the Connected contract. Let's verify this method exists in the contract interface.

✅ Verification successful

Contract interface verification successful

The setUniversal method is properly defined in the Connected contract. The method:

  • Takes an address contractAddress parameter
  • Has external visibility
  • Is protected with onlyOwner modifier
  • Includes zero-address validation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the Connected contract interface includes the setUniversal method
# Expected: Find the setUniversal method definition in the Connected contract

# Search for the Connected contract and its setUniversal method
ast-grep --pattern 'contract Connected {
  $$$
  function setUniversal($$$) {
    $$$
  }
  $$$
}'

Length of output: 100


Script:

#!/bin/bash
# Let's try to find the contract file first
fd -e sol Connected

# Then let's search for any file containing 'Connected' and 'setUniversal'
rg -l "Connected.*setUniversal|setUniversal.*Connected"

# As a backup, let's look for any contract interface or implementation with setUniversal
rg "function setUniversal" -A 2

Length of output: 812

examples/token/tasks/universalSetConnected.ts (4)

33-34: LGTM! Clear and consistent console output

The console output is well-formatted and maintains consistency with the new terminology.


18-18: Verify contract interface update

The method call has been updated from setCounterparty to setConnected. Let's ensure the contract interface is consistently updated.

✅ Verification successful

Contract interface is correctly updated

The contract interface in Universal.sol has been properly updated with the setConnected method, and there are no remaining references to setCounterparty. The changes are consistent across both the task file and the contract implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old method name
rg "setCounterparty" --type ts --type sol

# Verify the new method exists in the contract
ast-grep --pattern 'function setConnected($$$) {$$$}'

Length of output: 125


Script:

#!/bin/bash
# Search for contract files and interface definitions
fd -e sol . 

# Search for any remaining references to setCounterparty
rg "setCounterparty"

# Search for setConnected method in TypeScript files
rg "setConnected" --type ts -A 2

Length of output: 947


Script:

#!/bin/bash
# Check the contract interface in Universal.sol for the token example
cat examples/token/contracts/Universal.sol

# Check if there are any remaining references to counterparty in the contract
rg "counterparty" examples/token/contracts/Universal.sol -i

Length of output: 5210


38-41: Verify task registration in Hardhat config

The task name has been updated. Let's ensure this change is reflected in the Hardhat configuration and any importing files.

✅ Verification successful

The search results show that:

  1. The old task name "universal-set-counterparty" is not referenced anywhere in the codebase
  2. The new task "universalSetConnected" is correctly imported in multiple Hardhat config files

Let me verify if there are any other potential references to the old name that might have been missed:


Task name update is properly reflected across the codebase

The task name change from 'universal-set-counterparty' to 'universal-set-connected' has been correctly implemented. The new task is properly imported in the Hardhat configuration files, and there are no lingering references to the old task name in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for old task name references
rg "universal-set-counterparty" --type ts --type js

# Check Hardhat config files
fd "hardhat.config.ts" --exec cat {}

Length of output: 2930


Script:

#!/bin/bash
# Search for variations of the old task name
rg -i "counterparty" --type ts
rg -i "set.*counterparty" --type ts

# Check the task file content
cat examples/token/tasks/universalSetConnected.ts

Length of output: 1544


25-27: Verify JSON output consumers

The JSON output field has been renamed from 'counterpartyContractAddress' to 'connectedContractAddress'. This change might affect any scripts or systems consuming this output.

examples/nft/tasks/universalSetConnected.ts (2)

38-41: LGTM! Task definition is clear and consistent

The task name and parameter descriptions have been updated to use the new "connected" terminology consistently. The changes improve clarity and maintain a coherent naming convention.


18-18: Verify the contract interface implementation

The method name has been changed from setCounterparty to setConnected. Let's verify this change in the contract interface.

✅ Verification successful

Let's analyze the contract files and interfaces to verify the method name change. The previous pattern might have been too specific, so we're casting a wider net to find relevant contract definitions and implementations.


Contract interface matches the task implementation

The setConnected method is correctly implemented in the Universal contract with matching signature and parameters. The contract contains:

  • A connected mapping to store ZRC20-to-contract address relationships
  • The setConnected function with correct parameters (address zrc20, address contractAddress)
  • Proper access control via onlyOwner modifier
  • Event emission for tracking changes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the contract interface matches the new method name

# Search for the Universal contract interface
ast-grep --pattern 'contract Universal {
  $$$
  function setConnected($$$) {
    $$$
  }
  $$$
}'

# Search for any remaining references to the old method name
rg 'setCounterparty'

Length of output: 121


Script:

#!/bin/bash
# Let's try a broader search for contract interfaces and implementations

# Search for contract files
fd -e sol

# Search for setConnected or setCounterparty method definitions
rg -l "function set[C|c]onnected|function set[C|c]ounterparty"

# Search for interface definitions
rg -l "interface I.*Universal"

# Look for any test files that might contain contract usage
fd -e test.ts -e spec.ts

Length of output: 639


Script:

#!/bin/bash
# Let's examine the Universal contract implementations specifically for the NFT example

# Check the NFT Universal contract implementation
cat examples/nft/contracts/Universal.sol

# Check the Connected contract as well since it's related
cat examples/nft/contracts/Connected.sol

# Look for any usage patterns of setConnected/setCounterparty in tasks
rg -A 2 -B 2 "setConnected|setCounterparty" examples/nft/tasks/

Length of output: 12777

examples/token/scripts/localnet.sh (1)

5-5: Previous review comment is still applicable

The existing review comment about enhancing network management robustness remains valid.

Also applies to: 74-74

examples/token/contracts/Universal.sol (7)

16-16: LGTM: Appropriate use of immutable state variables

The immutable declarations for uniswapRouter and gasLimit are well-designed, providing gas optimization and preventing post-deployment modifications.

Also applies to: 19-19


26-26: LGTM: Clear and well-structured mapping declaration

The connected mapping appropriately tracks the relationship between ZRC20 tokens and their corresponding contract addresses.


29-29: LGTM: Gas-efficient authorization check

The modifier uses a custom error for unauthorized access, which is more gas-efficient than string error messages.


38-49: LGTM: Robust parameter validation in constructor

The constructor properly validates all critical parameters including zero address checks and gas limit validation.


52-57: LGTM: Well-structured administrative function with event emission

The setConnected function properly restricts access and emits events for off-chain tracking.


147-149: LGTM: Clear and consistent message decoding

The message decoding structure properly extracts the required parameters for handling reverted transactions.


120-127: ⚠️ Potential issue

Add slippage protection to token swaps

The swap operation uses 0 as minAmountOut, which provides no protection against price manipulation.

Consider implementing slippage protection:

 uint256 out = SwapHelperLib.swapExactTokensForTokens(
     uniswapRouter,
     zrc20,
     amount,
     destination,
-    0
+    calculateMinAmountOut(amount, maxSlippage)
 );

Likely invalid or redundant comment.

examples/nft/contracts/Connected.sol (5)

22-28: LGTM! Good improvements to state variables and error handling

The changes enhance code clarity and gas efficiency:

  • Renaming counterparty to universal better reflects its purpose
  • Making gasLimit immutable optimizes gas usage
  • New error types follow best practices for custom errors

30-33: LGTM! Well-structured setter with proper validation

The function includes all necessary components:

  • Zero address validation
  • State update
  • Event emission

44-51: LGTM! Constructor properly validates and initializes new gas parameter

Good implementation of the gas limit feature with proper validation.


78-101: LGTM! Well-structured cross-chain transfer implementation

The changes properly implement the cross-chain transfer mechanism with appropriate message encoding and revert options.


133-135: LGTM! Message decoding matches the encoding structure

The decoding structure properly aligns with the encoding in transferCrossChain.

examples/nft/contracts/Universal.sol (2)

51-55: LGTM: Robust address validation in constructor.

The constructor properly validates all critical addresses to prevent zero address initialization.


62-67: LGTM: Proper access control and event emission.

The setConnected function maintains proper access control with onlyOwner modifier and emits appropriate events.

@@ -15,13 +15,13 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
args.contract
);

const tx = await contract.setCounterparty(args.counterparty);
const tx = await contract.setUniversal(args.universal);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling for contract interaction

The contract call should be wrapped in a try-catch block to handle potential transaction failures gracefully.

Consider applying this improvement:

-  const tx = await contract.setUniversal(args.universal);
+  let tx;
+  try {
+    tx = await contract.setUniversal(args.universal);
+    await tx.wait(); // Wait for transaction confirmation
+  } catch (error) {
+    throw new Error(`Failed to set universal contract: ${error.message}`);
+  }

Also applies to: 24-24

Comment on lines +41 to +45
echo -e "\n🔗 Setting universal and connected contracts..."
npx hardhat connected-set-universal --network localhost --contract "$CONTRACT_ETHEREUM" --universal "$CONTRACT_ZETACHAIN" --json &>/dev/null
npx hardhat connected-set-universal --network localhost --contract "$CONTRACT_BNB" --universal "$CONTRACT_ZETACHAIN" --json &>/dev/null
npx hardhat universal-set-connected --network localhost --contract "$CONTRACT_ZETACHAIN" --connected "$CONTRACT_ETHEREUM" --zrc20 "$ZRC20_ETHEREUM" --json &>/dev/null
npx hardhat universal-set-connected --network localhost --contract "$CONTRACT_ZETACHAIN" --connected "$CONTRACT_BNB" --zrc20 "$ZRC20_BNB" --json &>/dev/null
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling for contract relationship setup

The current implementation suppresses all output and lacks error handling for failed operations.

 echo -e "\n🔗 Setting universal and connected contracts..."
-npx hardhat connected-set-universal --network localhost --contract "$CONTRACT_ETHEREUM" --universal "$CONTRACT_ZETACHAIN" --json &>/dev/null
+
+function set_contract_relationship() {
+    local cmd=$1
+    local output
+    output=$(eval "$cmd" 2>&1)
+    if [ $? -ne 0 ]; then
+        echo "Error: Failed to set contract relationship"
+        echo "$output"
+        exit 1
+    fi
+}
+
+set_contract_relationship "npx hardhat connected-set-universal --network localhost --contract \"$CONTRACT_ETHEREUM\" --universal \"$CONTRACT_ZETACHAIN\" --json"

Apply similar error handling to other relationship setup commands.

Committable suggestion skipped: line range outside the PR's diff.

SENDER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266


CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --gas-limit 700000 --json | jq -r '.contractAddress')
CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --gateway "$GATEWAY_ZETACHAIN" --uniswap-router "$UNISWAP_ROUTER" --json | jq -r '.contractAddress')
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Validate gateway and router parameters before deployment

The deployment command should verify that the gateway and router parameters are valid before proceeding.

+# Validate deployment parameters
+if [[ -z "$GATEWAY_ZETACHAIN" || -z "$UNISWAP_ROUTER" ]]; then
+    echo "Error: Missing required parameters for deployment"
+    echo "GATEWAY_ZETACHAIN: $GATEWAY_ZETACHAIN"
+    echo "UNISWAP_ROUTER: $UNISWAP_ROUTER"
+    exit 1
+fi
+
 CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --gateway "$GATEWAY_ZETACHAIN" --uniswap-router "$UNISWAP_ROUTER" --json | jq -r '.contractAddress')
📝 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
CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --gateway "$GATEWAY_ZETACHAIN" --uniswap-router "$UNISWAP_ROUTER" --json | jq -r '.contractAddress')
# Validate deployment parameters
if [[ -z "$GATEWAY_ZETACHAIN" || -z "$UNISWAP_ROUTER" ]]; then
echo "Error: Missing required parameters for deployment"
echo "GATEWAY_ZETACHAIN: $GATEWAY_ZETACHAIN"
echo "UNISWAP_ROUTER: $UNISWAP_ROUTER"
exit 1
fi
CONTRACT_ZETACHAIN=$(npx hardhat deploy --network localhost --gateway "$GATEWAY_ZETACHAIN" --uniswap-router "$UNISWAP_ROUTER" --json | jq -r '.contractAddress')

Comment on lines +24 to 28
GATEWAY_ZETACHAIN=$(jq -r '.addresses[] | select(.type=="gatewayZEVM" and .chain=="zetachain") | .address' localnet.json)
GATEWAY_ETHEREUM=$(jq -r '.addresses[] | select(.type=="gatewayEVM" and .chain=="ethereum") | .address' localnet.json)
GATEWAY_BNB=$(jq -r '.addresses[] | select(.type=="gatewayEVM" and .chain=="bnb") | .address' localnet.json)
UNISWAP_ROUTER=$(jq -r '.addresses[] | select(.type=="uniswapRouterInstance" and .chain=="zetachain") | .address' localnet.json)
SENDER=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance JSON parsing error handling

The jq commands should handle cases where the JSON file is malformed or missing required entries.

-GATEWAY_ZETACHAIN=$(jq -r '.addresses[] | select(.type=="gatewayZEVM" and .chain=="zetachain") | .address' localnet.json)
+GATEWAY_ZETACHAIN=$(jq -r '.addresses[] | select(.type=="gatewayZEVM" and .chain=="zetachain") | .address' localnet.json 2>/dev/null)
+if [ -z "$GATEWAY_ZETACHAIN" ]; then
+    echo "Error: Failed to extract gatewayZEVM address for zetachain from localnet.json"
+    exit 1
+fi

Apply similar error handling to other address extractions.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +12 to +13
address public universal;
uint256 public immutable gasLimit;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider making universal immutable for better gas efficiency and security.

The universal address is a critical contract reference that should not change frequently. Making it immutable would improve gas efficiency and prevent potential security risks from unauthorized changes.

-    address public universal;
+    address public immutable universal;
📝 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
address public universal;
uint256 public immutable gasLimit;
address public immutable universal;
uint256 public immutable gasLimit;

Comment on lines +124 to +127
if (gasAmount > 0) {
(bool success, ) = sender.call{value: gasAmount}("");
if (!success) revert GasTokenTransferFailed();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using OpenZeppelin's Address.sendValue for safer ETH transfers

Replace the low-level call with OpenZeppelin's Address.sendValue for better security:

+import "@openzeppelin/contracts/utils/Address.sol";

contract Connected is
    ERC721,
    ERC721Enumerable,
    ERC721URIStorage,
    Ownable2Step,
    Events
{
+    using Address for address payable;

    // ... in onCall function
    if (gasAmount > 0) {
-        (bool success, ) = sender.call{value: gasAmount}("");
-        if (!success) revert GasTokenTransferFailed();
+        payable(sender).sendValue(gasAmount);
    }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +138 to +142
address receiver,
uint256 tokenId,
string memory uri
) = abi.decode(message, (address, address, uint256, string));
string memory uri,
address sender
) = abi.decode(message, (address, address, uint256, string, address));
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for decoded receiver address.

The decoded receiver address should be validated to prevent minting to zero address.

 address receiver,
 uint256 tokenId,
 string memory uri,
 address sender
) = abi.decode(message, (address, address, uint256, string, address));
+if (receiver == address(0)) revert InvalidAddress();
📝 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
address receiver,
uint256 tokenId,
string memory uri
) = abi.decode(message, (address, address, uint256, string));
string memory uri,
address sender
) = abi.decode(message, (address, address, uint256, string, address));
address receiver,
uint256 tokenId,
string memory uri,
address sender
) = abi.decode(message, (address, address, uint256, string, address));
if (receiver == address(0)) revert InvalidAddress();

Comment on lines +26 to +29
address public immutable uniswapRouter;
uint256 private _nextTokenId;
bool public isUniversal = true;
uint256 public gasLimit;
uint256 public immutable gasLimit;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding validation for gas limit upper bound.

While the contract includes immutable variables for uniswapRouter and gasLimit, consider adding an upper bound validation for gasLimit to prevent excessive gas consumption in cross-chain calls.

 uint256 public immutable gasLimit;
+uint256 public constant MAX_GAS_LIMIT = 1000000; // Adjust based on network specifics

Then in the constructor:

 if (gas == 0) revert InvalidGasLimit();
+if (gas > MAX_GAS_LIMIT) revert InvalidGasLimit();

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +153 to +161
uint256 out = SwapHelperLib.swapExactTokensForTokens(
uniswapRouter,
zrc20,
amount,
destination,
0
);

IZRC20(destination).approve(address(gateway), gasFee);
gateway.call(
counterparty[destination],
IZRC20(destination).approve(address(gateway), out);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add swap output validation and slippage protection.

The swap implementation needs output amount validation and slippage protection.

 uint256 out = SwapHelperLib.swapExactTokensForTokens(
     uniswapRouter,
     zrc20,
     amount,
     destination,
-    0
+    amount * 95 / 100 // 5% max slippage
 );
+require(out > gasFee, "Insufficient swap output");
+require(out >= amount * 95 / 100, "Excessive slippage");
 IZRC20(destination).approve(address(gateway), out);
📝 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
uint256 out = SwapHelperLib.swapExactTokensForTokens(
uniswapRouter,
zrc20,
amount,
destination,
0
);
IZRC20(destination).approve(address(gateway), gasFee);
gateway.call(
counterparty[destination],
IZRC20(destination).approve(address(gateway), out);
uint256 out = SwapHelperLib.swapExactTokensForTokens(
uniswapRouter,
zrc20,
amount,
destination,
amount * 95 / 100 // 5% max slippage
);
require(out > gasFee, "Insufficient swap output");
require(out >= amount * 95 / 100, "Excessive slippage");
IZRC20(destination).approve(address(gateway), out);

Comment on lines +162 to +174
gateway.withdrawAndCall(
abi.encodePacked(connected[destination]),
out - gasFee,
destination,
abi.encode(sender, tokenId, uri),
abi.encode(receiver, tokenId, uri, out - gasFee, sender),
CallOptions(gasLimit, false),
RevertOptions(address(0), false, address(0), "", 0)
RevertOptions(
address(this),
true,
address(0),
abi.encode(tokenId, uri, sender),
0
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add deadline protection for cross-chain transfers.

Cross-chain transfers should include a deadline to prevent pending transactions from being executed after significant delays.

 gateway.withdrawAndCall(
     abi.encodePacked(connected[destination]),
     out - gasFee,
     destination,
-    abi.encode(receiver, tokenId, uri, out - gasFee, sender),
+    abi.encode(receiver, tokenId, uri, out - gasFee, sender, block.timestamp + 1800), // 30 min deadline
     CallOptions(gasLimit, false),
     RevertOptions(
         address(this),
         true,
         address(0),
-        abi.encode(tokenId, uri, sender),
+        abi.encode(tokenId, uri, sender, block.timestamp + 1800),
         0
     )
 );
📝 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
gateway.withdrawAndCall(
abi.encodePacked(connected[destination]),
out - gasFee,
destination,
abi.encode(sender, tokenId, uri),
abi.encode(receiver, tokenId, uri, out - gasFee, sender),
CallOptions(gasLimit, false),
RevertOptions(address(0), false, address(0), "", 0)
RevertOptions(
address(this),
true,
address(0),
abi.encode(tokenId, uri, sender),
0
)
gateway.withdrawAndCall(
abi.encodePacked(connected[destination]),
out - gasFee,
destination,
abi.encode(receiver, tokenId, uri, out - gasFee, sender, block.timestamp + 1800), // 30 min deadline
CallOptions(gasLimit, false),
RevertOptions(
address(this),
true,
address(0),
abi.encode(tokenId, uri, sender, block.timestamp + 1800),
0
)

Copy link

@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: 6

🧹 Outside diff range and nitpick comments (7)
examples/call/contracts/Connected.sol (2)

1-16: Add NatSpec documentation for better code clarity.

The contract structure is well-organized with appropriate use of custom errors and events. Consider adding NatSpec documentation to describe the contract's purpose, functions, events, and errors.

 // SPDX-License-Identifier: MIT
 pragma solidity 0.8.26;
 
 import {RevertContext} from "@zetachain/protocol-contracts/contracts/Revert.sol";
 import "@zetachain/protocol-contracts/contracts/evm/GatewayEVM.sol";
 
+/// @title Connected
+/// @notice A contract that handles cross-chain communication via ZetaChain gateway
+/// @dev This contract enables deposits and calls between different chains
 contract Connected {
     GatewayEVM public immutable gateway;
 
     event RevertEvent(string, RevertContext);
     event HelloEvent(string, string);
 
+    /// @notice Thrown when an ERC20 transfer fails
     error TransferFailed();
+    /// @notice Thrown when an ERC20 approval fails
     error ApprovalFailed();
+    /// @notice Thrown when a caller is not the gateway
     error Unauthorized();

102-104: Add events to track received funds.

The receive and fallback functions should emit events to track received funds for better transparency and debugging.

+    event ReceivedETH(address indexed sender, uint256 amount);
+    event FallbackCalled(address indexed sender, uint256 amount, bytes data);

-    receive() external payable {}
+    receive() external payable {
+        emit ReceivedETH(msg.sender, msg.value);
+    }

-    fallback() external payable {}
+    fallback() external payable {
+        emit FallbackCalled(msg.sender, msg.value, msg.data);
+    }
examples/call/contracts/Universal.sol (2)

18-21: Maintain consistent if-revert style across the codebase.

For better readability and consistency, use braces with the if-revert statement.

-        if (msg.sender != address(gateway)) revert Unauthorized();
+        if (msg.sender != address(gateway)) {
+            revert Unauthorized();
+        }

101-115: Consider enhancing event logging for better traceability.

While the current implementation is secure, consider adding more context to events such as timestamps, amounts, and addresses for better transaction tracing and debugging.

-    event HelloEvent(string, string);
-    event RevertEvent(string, RevertContext);
+    event HelloEvent(string action, string name, uint256 timestamp, address indexed sender);
+    event RevertEvent(string action, RevertContext context, uint256 timestamp, address indexed sender);

     function onCall(...) {
         string memory name = abi.decode(message, (string));
-        emit HelloEvent("Hello on ZetaChain", name);
+        emit HelloEvent("Hello on ZetaChain", name, block.timestamp, context.sender);
     }

     function onRevert(...) {
-        emit RevertEvent("Revert on ZetaChain", revertContext);
+        emit RevertEvent("Revert on ZetaChain", revertContext, block.timestamp, revertContext.sender);
     }
examples/hello/contracts/Universal.sol (3)

9-9: Name event parameters for improved clarity.

Consider specifying parameter names in the HelloEvent to enhance readability and maintainability.

Apply this diff:

-event HelloEvent(string, string);
+event HelloEvent(string greeting, string name);

17-17: Use address instead of address payable for gatewayAddress if not handling Ether transfers.

If the contract does not send Ether to gatewayAddress, address suffices and avoids unnecessary use of payable.

Apply this diff:

-constructor(address payable gatewayAddress) {
+constructor(address gatewayAddress) {

6-30: Add NatSpec documentation to the contract and its functions.

Including NatSpec comments enhances code understanding and aids in the generation of documentation.

Example for the contract:

+/// @title Universal Contract
+/// @notice A contract that handles cross-chain calls via ZetaChain.
 contract Universal is UniversalContract {

Example for the onCall function:

+    /**
+     * @notice Handles incoming cross-chain calls from the gateway.
+     * @param context The message context provided by the gateway.
+     * @param zrc20 The address of the ZRC20 token used.
+     * @param amount The amount of tokens sent.
+     * @param message The encoded message data sent.
+     */
     function onCall(
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6a15652 and 786d629.

📒 Files selected for processing (5)
  • examples/call/contracts/Connected.sol (1 hunks)
  • examples/call/contracts/Universal.sol (2 hunks)
  • examples/hello/contracts/Universal.sol (1 hunks)
  • examples/swap/contracts/Swap.sol (5 hunks)
  • examples/swap/contracts/SwapToAnyToken.sol (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/swap/contracts/Swap.sol
  • examples/swap/contracts/SwapToAnyToken.sol
🧰 Additional context used
🪛 GitHub Check: Slither
examples/call/contracts/Universal.sol

[warning] 43-65: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#43-65) ignores return value by IZRC20(gasZRC20).approve(address(gateway),gasFee) (contracts/Universal.sol#62)


[warning] 43-65: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#43-65) ignores return value by IZRC20(zrc20).approve(address(gateway),target) (contracts/Universal.sol#53)

🔇 Additional comments (2)
examples/call/contracts/Connected.sol (1)

17-20: LGTM! Access control is properly implemented.

The modifier correctly restricts access to the gateway address using a custom error.

examples/call/contracts/Universal.sol (1)

9-16: LGTM! Well-structured contract declaration with clear error definitions.

The contract name change to Universal better reflects its purpose, and the error types are appropriately defined for specific failure scenarios.

Comment on lines +22 to +24
constructor(address payable gatewayAddress) {
gateway = GatewayEVM(gatewayAddress);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add zero address validation in constructor.

The constructor should validate that the gateway address is not zero to prevent deployment with an invalid gateway.

     constructor(address payable gatewayAddress) {
+        if (gatewayAddress == address(0)) revert("Zero address");
         gateway = GatewayEVM(gatewayAddress);
     }
📝 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
constructor(address payable gatewayAddress) {
gateway = GatewayEVM(gatewayAddress);
}
constructor(address payable gatewayAddress) {
if (gatewayAddress == address(0)) revert("Zero address");
gateway = GatewayEVM(gatewayAddress);
}

Comment on lines 41 to 70
function deposit(
address receiver,
uint256 amount,
address asset,
RevertOptions memory revertOptions
) external {
if (!IERC20(asset).transferFrom(msg.sender, address(this), amount)) {
revert TransferFailed();
}
if (!IERC20(asset).approve(address(gateway), amount)) {
revert ApprovalFailed();
}
gateway.deposit(receiver, amount, asset, revertOptions);
}

function depositAndCall(
address receiver,
uint256 amount,
address asset,
bytes calldata message,
RevertOptions memory revertOptions
) external {
if (!IERC20(asset).transferFrom(msg.sender, address(this), amount)) {
revert TransferFailed();
}
if (!IERC20(asset).approve(address(gateway), amount)) {
revert ApprovalFailed();
}
gateway.depositAndCall(receiver, amount, asset, message, revertOptions);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use SafeERC20 for token operations.

The current implementation uses unsafe ERC20 operations. Some tokens (like USDT) don't follow the standard return value pattern, which could cause the transfers to fail silently.

+import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

 contract Connected {
+    using SafeERC20 for IERC20;
     // ...
     function deposit(
         address receiver,
         uint256 amount,
         address asset,
         RevertOptions memory revertOptions
     ) external {
-        if (!IERC20(asset).transferFrom(msg.sender, address(this), amount)) {
-            revert TransferFailed();
-        }
-        if (!IERC20(asset).approve(address(gateway), amount)) {
-            revert ApprovalFailed();
-        }
+        IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
+        IERC20(asset).safeApprove(address(gateway), amount);
         gateway.deposit(receiver, amount, asset, revertOptions);
     }

Apply similar changes to the depositAndCall function.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +88 to +94
function onCall(
MessageContext calldata context,
bytes calldata message
) external payable onlyGateway returns (bytes4) {
emit HelloEvent("Hello on EVM from onCall()", "hey");
return "";
}
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Return value should be IMessageDispatcher.onCall.selector

The empty bytes4 return value in the onCall function is incorrect. Based on the codebase analysis, this function is a callback from the gateway contract and should return the function selector for IMessageDispatcher.onCall to indicate successful processing.

🔗 Analysis chain

Verify the expected return value for onCall.

The function returns an empty bytes4 value. Verify if this is the expected return value by the gateway contract.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the expected return value in the gateway contract

# Search for onCall function definition in the gateway contract
ast-grep --pattern 'function onCall($$$) $$ returns (bytes4) { $$$ }'

Length of output: 71


Script:

#!/bin/bash
# Let's try a broader search for gateway interfaces or contracts
rg -l "gateway|Gateway" --type sol

# Then look for any interface or contract definitions related to gateway callbacks
ast-grep --pattern 'interface $_ {
  $$$
  function onCall($$$)
  $$$
}'

# Also search for any documentation or comments about gateway callbacks
rg -i "oncall.*return" --type sol -A 2 -B 2

Length of output: 213


Script:

#!/bin/bash
# Search for any files with .sol extension
fd -e sol

# Then search for gateway-related content in all files
rg "gateway|Gateway" -A 5 -B 5

# Look for onCall implementations
rg "function onCall" -A 5 -B 5

Length of output: 82222

Comment on lines 43 to 64
function withdraw(
bytes memory receiver,
uint256 amount,
address zrc20,
RevertOptions memory revertOptions
) external {
(address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee();
uint256 target = zrc20 == gasZRC20 ? amount + gasFee : amount;
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), target))
revert TransferFailed();
IZRC20(zrc20).approve(address(gateway), target);
if (zrc20 != gasZRC20) {
if (
!IZRC20(gasZRC20).transferFrom(
msg.sender,
address(this),
gasFee
)
) revert TransferFailed();
IZRC20(gasZRC20).approve(address(gateway), gasFee);
}
gateway.withdraw(receiver, amount, zrc20, revertOptions);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Handle approve return values to prevent silent failures.

The approve calls' return values are not checked, which could lead to silent failures. This is particularly critical for cross-chain operations.

Apply this pattern to all approve calls in the contract:

-        IZRC20(zrc20).approve(address(gateway), target);
+        if (!IZRC20(zrc20).approve(address(gateway), target)) {
+            revert TransferFailed();
+        }

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Check: Slither

[warning] 43-65: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#43-65) ignores return value by IZRC20(gasZRC20).approve(address(gateway),gasFee) (contracts/Universal.sol#62)


[warning] 43-65: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#43-65) ignores return value by IZRC20(zrc20).approve(address(gateway),target) (contracts/Universal.sol#53)

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Import MessageContext for proper type definition.

The type MessageContext used in the onCall function is not imported or defined within this file.

Add the necessary import statement:

+import "@zetachain/protocol-contracts/contracts/zevm/MessageContext.sol";

 import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol";

Also applies to: 22-22


import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol";

contract Universal is UniversalContract {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Include the import statement for UniversalContract.

The contract Universal extends UniversalContract, but UniversalContract is neither imported nor defined in this file.

Apply this diff to include the import statement:

+import "@zetachain/zevm-example-contracts/contracts/UniversalContract.sol";

 contract Universal is UniversalContract {
📝 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
contract Universal is UniversalContract {
import "@zetachain/zevm-example-contracts/contracts/UniversalContract.sol";
contract Universal is UniversalContract {

Copy link

@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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 786d629 and a6d9809.

📒 Files selected for processing (1)
  • examples/call/contracts/Connected.sol (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Slither
examples/call/contracts/Connected.sol

[warning] 42-51: Unused return
Connected.deposit(address,uint256,address,RevertOptions) (contracts/Connected.sol#42-51) ignores return value by IERC20(asset).approve(address(gateway),amount) (contracts/Connected.sol#49)


[warning] 53-63: Unused return
Connected.depositAndCall(address,uint256,address,bytes,RevertOptions) (contracts/Connected.sol#53-63) ignores return value by IERC20(asset).approve(address(gateway),amount) (contracts/Connected.sol#61)

🔇 Additional comments (3)
examples/call/contracts/Connected.sol (3)

1-9: LGTM! Appropriate imports and SafeERC20 usage.

The contract setup follows best practices with specific version pinning and necessary imports.


11-16: LGTM! Well-defined state variables and events.

The immutable gateway variable and events provide good visibility into contract operations.


65-98: LGTM! Solid implementation of payable functions and error handling.

The implementation includes appropriate error handling, modifiers, and fallback functions.

Comment on lines +42 to +63
function deposit(
address receiver,
uint256 amount,
address asset,
RevertOptions memory revertOptions
) external {
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
IERC20(asset).approve(address(gateway), amount);
gateway.deposit(receiver, amount, asset, revertOptions);
}

function depositAndCall(
address receiver,
uint256 amount,
address asset,
bytes calldata message,
RevertOptions memory revertOptions
) external {
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
IERC20(asset).approve(address(gateway), amount);
gateway.depositAndCall(receiver, amount, asset, message, revertOptions);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use safeApprove instead of approve.

The contract uses SafeERC20 but doesn't utilize it for approve operations. Replace approve with safeApprove:

-        IERC20(asset).approve(address(gateway), amount);
+        IERC20(asset).safeApprove(address(gateway), amount);

This change should be applied to both deposit functions.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Check: Slither

[warning] 42-51: Unused return
Connected.deposit(address,uint256,address,RevertOptions) (contracts/Connected.sol#42-51) ignores return value by IERC20(asset).approve(address(gateway),amount) (contracts/Connected.sol#49)


[warning] 53-63: Unused return
Connected.depositAndCall(address,uint256,address,bytes,RevertOptions) (contracts/Connected.sol#53-63) ignores return value by IERC20(asset).approve(address(gateway),amount) (contracts/Connected.sol#61)

@fadeev fadeev requested a review from andresaiello November 20, 2024 17:49
Copy link

@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: 2

🧹 Outside diff range and nitpick comments (3)
examples/call/contracts/Universal.sol (3)

9-16: Consider adding descriptive error messages

While the error types are well-defined, consider adding descriptive messages to improve debugging:

-    error TransferFailed();
-    error Unauthorized();
+    error TransferFailed(string message);
+    error Unauthorized(string message);

18-21: Standardize revert statement style

For consistency with other parts of the contract and improved readability, use curly braces for the revert statement.

     modifier onlyGateway() {
-        if (msg.sender != address(gateway)) revert Unauthorized();
+        if (msg.sender != address(gateway)) {
+            revert Unauthorized();
+        }
         _;
     }

Line range hint 71-105: Apply consistent error handling and approve checks

The function has similar issues as the withdraw function:

  1. Inconsistent revert statement style
  2. Missing approve return value checks
  3. Duplicate gas fee handling logic

Consider using the same helper function suggested for the withdraw function and apply consistent error handling.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a6d9809 and 83ab10b.

📒 Files selected for processing (1)
  • examples/call/contracts/Universal.sol (2 hunks)
🧰 Additional context used
🪛 GitHub Check: Slither
examples/call/contracts/Universal.sol

[warning] 44-69: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#44-69) ignores return value by IZRC20(gasZRC20).approve(address(gateway),gasFee) (contracts/Universal.sol#66)


[warning] 44-69: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#44-69) ignores return value by IZRC20(zrc20).approve(address(gateway),target) (contracts/Universal.sol#55)

🔇 Additional comments (1)
examples/call/contracts/Universal.sol (1)

107-121: LGTM!

The onCall and onRevert functions are well-implemented with proper access control and event emission.

Comment on lines +44 to +68
function withdraw(
bytes memory receiver,
uint256 amount,
address zrc20,
RevertOptions memory revertOptions
) external {
(address gasZRC20, uint256 gasFee) = IZRC20(zrc20).withdrawGasFee();
uint256 target = zrc20 == gasZRC20 ? amount + gasFee : amount;
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), target)) {
revert TransferFailed();
}
IZRC20(zrc20).approve(address(gateway), target);
if (zrc20 != gasZRC20) {
if (
!IZRC20(gasZRC20).transferFrom(
msg.sender,
address(this),
gasFee
)
) {
revert TransferFailed();
}
IZRC20(gasZRC20).approve(address(gateway), gasFee);
}
gateway.withdraw(receiver, amount, zrc20, revertOptions);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor withdraw function for better maintainability

The function has multiple approve calls without return value checks and could benefit from helper methods.

  1. Add return value checks for approve calls:
-        IZRC20(zrc20).approve(address(gateway), target);
+        if (!IZRC20(zrc20).approve(address(gateway), target)) {
+            revert TransferFailed();
+        }
-        IZRC20(gasZRC20).approve(address(gateway), gasFee);
+        if (!IZRC20(gasZRC20).approve(address(gateway), gasFee)) {
+            revert TransferFailed();
+        }
  1. Consider extracting the gas fee handling logic into a helper function to improve readability and reusability:
    function handleGasFee(
        address zrc20,
        address gasZRC20,
        uint256 gasFee
    ) private {
        if (zrc20 != gasZRC20) {
            if (!IZRC20(gasZRC20).transferFrom(msg.sender, address(this), gasFee)) {
                revert TransferFailed();
            }
            if (!IZRC20(gasZRC20).approve(address(gateway), gasFee)) {
                revert TransferFailed();
            }
        }
    }
🧰 Tools
🪛 GitHub Check: Slither

[warning] 44-69: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#44-69) ignores return value by IZRC20(gasZRC20).approve(address(gateway),gasFee) (contracts/Universal.sol#66)


[warning] 44-69: Unused return
Universal.withdraw(bytes,uint256,address,RevertOptions) (contracts/Universal.sol#44-69) ignores return value by IZRC20(zrc20).approve(address(gateway),target) (contracts/Universal.sol#55)

Comment on lines +34 to +41
(, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(
callOptions.gasLimit
);
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), gasFee)) {
revert TransferFailed();
}
IZRC20(zrc20).approve(address(gateway), gasFee);
gateway.call(receiver, zrc20, message, gasLimit, revertOptions);
gateway.call(receiver, zrc20, message, callOptions, revertOptions);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle approve return value

The approve call's return value should be checked to prevent silent failures.

-        IZRC20(zrc20).approve(address(gateway), gasFee);
+        if (!IZRC20(zrc20).approve(address(gateway), gasFee)) {
+            revert TransferFailed();
+        }
📝 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
(, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(
callOptions.gasLimit
);
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), gasFee)) {
revert TransferFailed();
}
IZRC20(zrc20).approve(address(gateway), gasFee);
gateway.call(receiver, zrc20, message, gasLimit, revertOptions);
gateway.call(receiver, zrc20, message, callOptions, revertOptions);
(, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(
callOptions.gasLimit
);
if (!IZRC20(zrc20).transferFrom(msg.sender, address(this), gasFee)) {
revert TransferFailed();
}
if (!IZRC20(zrc20).approve(address(gateway), gasFee)) {
revert TransferFailed();
}
gateway.call(receiver, zrc20, message, callOptions, revertOptions);

@fadeev
Copy link
Member Author

fadeev commented Nov 21, 2024

@andresaiello please, review again.

@fadeev fadeev merged commit 02373a6 into main Nov 24, 2024
15 checks passed
@fadeev fadeev deleted the auth-call-hello-swap branch November 24, 2024 17:09
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