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

Gho ccip bridge (#7) #347

Open
wants to merge 44 commits into
base: master
Choose a base branch
from

Conversation

LucasWongC
Copy link

@LucasWongC LucasWongC commented Sep 30, 2024

AaveCcipGhoBridge Smart Contract

Overview

The AaveCcipGhoBridge smart contract facilitates the secure bridging of GHO tokens across different blockchain networks using Chainlink's Cross-Chain Interoperability Protocol (CCIP). By leveraging Chainlink's decentralized infrastructure, it ensures reliable and transparent cross-chain token transfers while using GHO tokens exclusively for fees.

Key Components

  1. Router and GHO Addresses:

    • ROUTER: The Chainlink CCIP router address for cross-chain communication.
    • GHO: Address of the GHO token, which the contract bridges across chains.
  2. Bridges Mapping:

    • A mapping of destination chain selectors (chain identifiers) to corresponding bridge addresses on the destination chains.

Main Functionalities

  1. Token Transfers:

    • Facilitates secure GHO token transfers between chains.
    • Collects GHO tokens from the sender, validates the destination chain and bridge configuration, and transfers the tokens via Chainlink CCIP.
    • The checkDestination modifier ensures that the destination chain and bridge are properly configured before any transfer.
  2. Fee Payment:

    • Fees are paid exclusively in GHO tokens.
    • Calculates the required fee for a transfer and deducts it directly from the user's GHO balance.
  3. Cross-Chain Message Handling:

    • Processes incoming cross-chain messages using the _ccipReceive function.
    • Decodes the message, verifies its authenticity, and releases the corresponding GHO tokens to the recipient on the destination chain.
  4. Quote Transfer:

    • The quoteTransfer function allows users to estimate the GHO fee required for a transfer without executing the transfer.
  5. Setting Destination Bridges:

    • The contract owner can configure or update the bridge addresses for different destination chains via the setDestinationBridge function.

Security Features

  • Role-Based Access Control:
    Uses AccessControl to manage permissions:

    • DEFAULT_ADMIN_ROLE: Grants administrative rights.
    • BRIDGER_ROLE: Restricts who can initiate bridging operations.
      This ensures that only authorized users can perform critical functions.
  • Destination Validation:
    The checkDestination modifier ensures that the destination chain and bridge address are properly configured before executing transfers.

  • Token Approval Management:
    Securely handles GHO token approvals for the router, mitigating over-approval risks.

  • Rescue Functionality:
    Implements a Rescuable mechanism, allowing the EXECUTOR to rescue funds in emergencies while maintaining security boundaries.

  • CCIP Sender and Receiver Validation:

    • Verifies messages originate from the correct source bridge using Chainlink CCIP.
    • Incoming messages must match the expected sender address for the specified source chain.

Events

  • TransferIssued: Emitted when a transfer is successfully initiated.
  • TransferFinished: Emitted when a transfer completes successfully on the destination chain.
  • DestinationUpdated: Emitted when a new bridge address is set for a specific chain.

Error Handling

  • UnsupportedChain: Thrown when attempting to transfer to an unsupported destination chain.
  • InvalidTransferAmount: Thrown when the total amount of tokens to transfer is zero.
  • InsufficientFee: Thrown if the provided fee is insufficient to cover the transaction.
  • InvalidMessage: Thrown when a message received from a source chain does not come from the expected bridge address.

Deployed Addresses

Mainnet: 0x5648f519b2064ff30385828e76fefda749749ac2, Arbitrum: 0x03f589a825ee129c5c6d2f6ef5c259870019567b

Confirmed Bridges:

Direct CCIP Message ID Source Tx Destination Tx
Ethereum -> Arbitrum 0x87790f04c4cef490f46143efd7086147326ac65a3421c4aca82dd251f55bef82 0xa0c72c9e705ce20bb53ba0a57d249d082930d791c3f733a95ea07398b946e4b3 0xcea9e503c001119535abf0016f3e2ccc8e71fe0e202522dec92dd52811334393
Arbitrum -> Ethereum 0xe7eae1ddf0138b4a410a46b801aecbd1ca2ef29750980625fafa12adcf37946f 0xa9751980b3a66903031a9751d92ab56c331ad76916fea9b21ac3a92f8970d743 0x5310d08839775003e63a763c73721491d51439aea43de6b298388790922b8ba3

Conclusion

The AaveCcipGhoBridge smart contract provides a robust mechanism for bridging GHO tokens across multiple chains via Chainlink's CCIP. It offers flexibility in fee payments, supports secure cross-chain transfers, and allows for easy configuration of destination bridges.

foundry.toml Outdated Show resolved Hide resolved
scripts/DeployBridges.s.sol Outdated Show resolved Hide resolved
scripts/DeployBridges.s.sol Outdated Show resolved Hide resolved
src/bridges/chainlink-ccip/AaveCcipGhoBridge.sol Outdated Show resolved Hide resolved
src/bridges/chainlink-ccip/AaveCcipGhoBridge.sol Outdated Show resolved Hide resolved
src/bridges/chainlink-ccip/IAaveCcipGhoBridge.sol Outdated Show resolved Hide resolved

### Deployed Addresses

Mainnet: [0x5648f519b2064ff30385828e76fefda749749ac2](https://etherscan.io/address/0x5648f519b2064ff30385828e76fefda749749ac2), Arbitrum: [0x03f589a825ee129c5c6d2f6ef5c259870019567b](https://arbiscan.io/address/0x03f589a825ee129c5c6d2f6ef5c259870019567b)
Copy link
Contributor

Choose a reason for hiding this comment

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

Update with final deployment addresses

Copy link
Author

@LucasWongC LucasWongC Oct 25, 2024

Choose a reason for hiding this comment

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

It's not deployed yet for production and these addresses are deployed address to test main functionality

Copy link
Author

@LucasWongC LucasWongC Oct 25, 2024

Choose a reason for hiding this comment

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

will update this address after fix all feedbacks

@pavelvm5
Copy link

Can you clarify why all issues are considered resolved, although the code is not updated and does not compile with current versions?

We just don't understand a little, if the changes are in another PR, can you send a link?

@LucasWongC LucasWongC closed this Oct 28, 2024
@LucasWongC LucasWongC reopened this Oct 28, 2024
@LucasWongC
Copy link
Author

@pavelvm5 maybe you should run yarn install again

@pavelvm5
Copy link

pavelvm5 commented Oct 28, 2024

@pavelvm5 maybe you should run yarn install again

the problem is not yarn, it's that the new version has a maxRescuable function that needs to be implemented

You either need to set the commit in the solidity-utils library to the old one in foundry.toml config, or write an implementation for the new version.

I recommend doing the second version, solidity-utils will not be updated anytime soon.

The same goes for the chainlink dependency, which now requires 9 arguments instead of 6. And the same about guardians, their addresses exist in address-book for now.

@LucasWongC
Copy link
Author

Can you try after run yarn install and forge install.

chainlink packages updated and this updates only applied for testnet. so we should use prev persion and I configured this on package.json correctly now.

foundry.toml Outdated Show resolved Hide resolved
@LucasWongC LucasWongC requested review from pavelvm5 and sendra October 28, 2024 16:19
@CheyenneAtapour
Copy link

CheyenneAtapour commented Dec 10, 2024

Tests fail

Failing tests:
Encountered 1 failing test in tests/bridges/arbitrum/AaveArbEthERC20BridgeTest.t.sol:EmergencyTokenTransfer
[FAIL: Error != expected error: OnlyRescueGuardian() != ONLY_RESCUE_GUARDIAN] test_revertsIf_invalidCaller() (gas: 12705)

Encountered 1 failing test in tests/bridges/optimism/AaveOpEthERC20BridgeTest.t.sol:EmergencyTokenTransfer
[FAIL: Error != expected error: OnlyRescueGuardian() != ONLY_RESCUE_GUARDIAN] test_revertsIf_invalidCaller() (gas: 12907)

Encountered 1 failing test in tests/bridges/polygon/AavePolEthERC20BridgeTest.t.sol:EmergencyTokenTransfer
[FAIL: Error != expected error: OnlyRescueGuardian() != ONLY_RESCUE_GUARDIAN] test_revertsIf_invalidCaller() (gas: 12719)

Encountered 1 failing test in tests/bridges/polygon/AavePolEthPlasmaBridge.t.sol:EmergencyTokenTransfer
[FAIL: Error != expected error: OnlyRescueGuardian() != ONLY_RESCUE_GUARDIAN] test_revertsIf_invalidCaller() (gas: 12727)

Encountered 1 failing test in tests/swaps/AaveSwapperTest.t.sol:EmergencyTokenTransfer
[FAIL: Error != expected error: OnlyRescueGuardian() != ONLY_RESCUE_GUARDIAN] test_revertsIf_invalidCaller() (gas: 10878)

Encountered 1 failing test in tests/v2-config-engine/AaveV2ConfigEngineTest.t.sol:AaveV2ConfigEngineTest
[FAIL: EvmError: Revert] testV2RateStrategiesUpdates() (gas: 2777880)

Encountered a total of 6 failing tests, 217 tests succeeded

There is a custom error on IRescuable error OnlyRescueGuardian(); which is invoked upon calling emergencyTokenTransfer if it is called by the wrong party. It is not the string as currently checked for in the tests: vm.expectRevert('ONLY_RESCUE_GUARDIAN');

There is another error as well, please check the above

data: '',
tokenAmounts: tokenAmounts,
extraArgs: '',
feeToken: GHO

Choose a reason for hiding this comment

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

Imposing the fee in GHO can be problematic if GHO is not supported in that specific lane.
In other words, if GHO is not supported as fee token, bridge won't work.

Copy link
Author

Choose a reason for hiding this comment

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

This bridge won't be deployed on such network that doesn't support GHO. actually it's built for ARB -> ETH GHO bridge

/// @inheritdoc IAaveCcipGhoBridge
function getInvalidMessage(
bytes32 messageId
) external view checkInvalidMessage(messageId) returns (Client.Any2EVMMessage memory message) {

Choose a reason for hiding this comment

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

why this getter only works for invalid messages, rather than a general getter for messages, which can be helpful

Copy link
Author

Choose a reason for hiding this comment

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

we don't save another messages on contract. it will burn gas

/// @inheritdoc IAaveCcipGhoBridge
function handleInvalidMessage(
bytes32 messageId
) external onlyRole(DEFAULT_ADMIN_ROLE) checkInvalidMessage(messageId) {

Choose a reason for hiding this comment

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

unclear to me what this function does. Interface docs are not very descriptive neither.

Copy link

Choose a reason for hiding this comment

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

This is probably based on this receiver example. It is meant to rescue tokens for messages that might not ever be executable. This means that even if the message is somehow malformed, the funds attached to it can always be recovered.

data: '',
tokenAmounts: tokenAmounts,
extraArgs: Client._argsToBytes(
Client.EVMExtraArgsV2({gasLimit: gasLimit, allowOutOfOrderExecution: true})

Choose a reason for hiding this comment

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

the allowOutOfOrderExecution flag is not set in the other branch

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure how can I set this value if gasLimit is not set.

@RensR could you check this?

}

/// @inheritdoc CCIPReceiver
function ccipReceive(Client.Any2EVMMessage calldata message) external override onlyRouter {

Choose a reason for hiding this comment

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

why are we overriding this instead of overriding _ccipReceive internal function, which is the recommendation as far as I see.

Copy link
Author

Choose a reason for hiding this comment

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

calldata can be used for storage, but memory can't be used for storage.
In definition of _ccipReceive, message type is memory, so can't use this to save on storage

/// @inheritdoc CCIPReceiver
function ccipReceive(Client.Any2EVMMessage calldata message) external override onlyRouter {
try this.processMessage(message) {} catch {
emit FailedToDecodeMessage();
Copy link

Choose a reason for hiding this comment

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

Should this catch not be used to set the message in the failed state? See this example receiver code

Copy link
Author

Choose a reason for hiding this comment

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

I think we don't need to save failed status. we just need save failed or no.

mapping(uint64 selector => address bridge) public bridges;

/// @dev Saves invalid message
mapping(bytes32 messageId => Client.Any2EVMMessage message) private messageContents;
Copy link

Choose a reason for hiding this comment

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

Depending on what you aim to do with this message, you could consider storing only the relevant part of the message. The entire message is pretty large and would take hundreds of thousands of gas to store, which might not be ideal on chains like Ethereum. Also note that this gas would come out of the message ccip specified gasLimit. Say you always send a msg with a gasLimit of 100k, the storing of failed messages would always run out of gas and you end up needing to manually execute the tx with a higher gas limit to properly store it as a failed message. Only then you can run handleInvalidMessage to release the funds.

If you only need the tokens and amounts it's probably best to store those and potentially some context if required. With the messageID as key you could always get that context from CCIP as well but that would be an "external" dependency.

Copy link
Author

Choose a reason for hiding this comment

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

from where, can I get context of message with message Id?

@LucasWongC LucasWongC requested a review from RensR December 10, 2024 14:14
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.

8 participants