- 
                Notifications
    You must be signed in to change notification settings 
- Fork 12.3k
ERC-7786 based crosschain bridge for ERC-20 tokens #5914
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
          
     Open
      
      
            Amxx
  wants to merge
  28
  commits into
  OpenZeppelin:master
  
    
      
        
          
  
    
      Choose a base branch
      
     
    
      
        
      
      
        
          
          
        
        
          
            
              
              
              
  
           
        
        
          
            
              
              
           
        
       
     
  
        
          
            
          
            
          
        
       
    
      
from
Amxx:crosschain/erc20bridge
  
      
      
   
  
    
  
  
  
 
  
      
    base: master
Could not load branches
            
              
  
    Branch not found: {{ refName }}
  
            
                
      Loading
              
            Could not load tags
            
            
              Nothing to show
            
              
  
            
                
      Loading
              
            Are you sure you want to change the base?
            Some commits from the old base branch may be removed from the timeline,
            and old review comments may become outdated.
          
          
  
     Open
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            28 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      4a33948
              
                Migrate ERC7786Receiver from community
              
              
                Amxx fdb2e77
              
                add documentation
              
              
                Amxx 3651638
              
                Apply suggestion from @ernestognw
              
              
                ernestognw f2abf9f
              
                Update .changeset/silent-zebras-press.md
              
              
                Amxx ab9643f
              
                Apply suggestions from code review
              
              
                Amxx d3e2223
              
                rename ERC7786Receiver into ERC7786Recipient
              
              
                Amxx 9b015d3
              
                Update .changeset/silent-zebras-press.md
              
              
                Amxx b486a81
              
                Add CrosschainBridgeERC20, CrosschainBridgeERC20Custodial, Crosschain…
              
              
                Amxx 50be369
              
                refactor bridges
              
              
                Amxx 4cf52d0
              
                comments
              
              
                Amxx bd4095c
              
                remove the "with attributes" variant of crosschainTransfer + testing
              
              
                Amxx e6e5d79
              
                update Bridge: use chain specific gateway
              
              
                Amxx c0c421c
              
                refactor permission
              
              
                Amxx a051b91
              
                Merge branch 'crosschain/erc7786receiver' into crosschain/erc20bridge
              
              
                Amxx c938d68
              
                calldata
              
              
                Amxx 69d43a7
              
                Merge branch 'crosschain/erc7786receiver' into crosschain/erc20bridge
              
              
                Amxx d39c18e
              
                prevent message replay at the receiver level
              
              
                Amxx 3cc4acf
              
                Merge branch 'crosschain/erc7786receiver' into crosschain/erc20bridge
              
              
                Amxx 610362d
              
                update to match new ERC7786Recipient
              
              
                Amxx e0a3fd0
              
                Update contracts/crosschain/bridges/BridgeCore.sol
              
              
                Amxx 434ee70
              
                Merge branch 'master' into crosschain/erc20bridge
              
              
                Amxx c2a6d40
              
                Apply suggestions from code review
              
              
                Amxx 42b450c
              
                Merge branch 'master' into crosschain/erc20bridge
              
              
                Amxx b8cc512
              
                Merge remote-tracking branch 'amxx/crosschain/erc20bridge' into cross…
              
              
                Amxx 984b897
              
                fix tests
              
              
                Amxx 6a8e8f9
              
                pragma consistency
              
              
                Amxx 172b02d
              
                pragma consistency
              
              
                Amxx 0673a8a
              
                Merge branch 'master' into crosschain/erc20bridge
              
              
                Amxx File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
          Some comments aren't visible on the classic Files Changed page.
        
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| pragma solidity ^0.8.26; | ||
|  | ||
| import {IERC7786GatewaySource} from "../../interfaces/draft-IERC7786.sol"; | ||
| import {InteroperableAddress} from "../../utils/draft-InteroperableAddress.sol"; | ||
| import {Bytes} from "../../utils/Bytes.sol"; | ||
| import {ERC7786Recipient} from "../ERC7786Recipient.sol"; | ||
|  | ||
| /** | ||
| * @dev Core bridging mechanism. | ||
| * | ||
| * This contract contains the logic to register and send messages to counterparts on remote chains using ERC-7786 | ||
| * gateways. It ensure received messages originate from a counterpart. This is the base of token bridges such as``` | ||
| * {BridgeERC20}. | ||
| * | ||
| * Contract that inherit from this contract can use the internal {_sendMessage} to send messages to their counterpart``` | ||
| * on a foreign chain. They must override the {_processMessage} function to handle the message that have been verified. | ||
| */ | ||
| abstract contract BridgeCore is ERC7786Recipient { | ||
| using Bytes for bytes; | ||
| using InteroperableAddress for bytes; | ||
|  | ||
| struct Link { | ||
| address gateway; | ||
| bytes remote; | ||
| } | ||
| mapping(bytes chain => Link) private _links; | ||
|  | ||
| event RemoteRegistered(address gateway, bytes remote); | ||
|  | ||
| error RemoteAlreadyRegistered(bytes chain); | ||
|  | ||
| constructor(Link[] memory links) { | ||
| for (uint256 i = 0; i < links.length; ++i) { | ||
| _setLink(links[i].gateway, links[i].remote, false); | ||
| } | ||
| } | ||
|  | ||
| /// @dev Returns the ERC-7786 gateway used for sending and receiving cross-chain messages to a given chain | ||
| function link(bytes memory chain) public view virtual returns (address gateway, bytes memory remote) { | ||
| Link storage self = _links[chain]; | ||
| return (self.gateway, self.remote); | ||
| } | ||
|  | ||
| /// @dev Internal setter to change the ERC-7786 gateway and remote for a given chain. Called at construction. | ||
| function _setLink(address gateway, bytes memory remote, bool allowOverride) internal virtual { | ||
| // Sanity check, this should revert if gateway is not an ERC-7786 implementation. Note that since | ||
| // supportsAttribute returns data, an EOA would fail that test (nothing returned). | ||
| IERC7786GatewaySource(gateway).supportsAttribute(bytes4(0)); | ||
|  | ||
| bytes memory chain = _extractChain(remote); | ||
| if (allowOverride || _links[chain].gateway == address(0)) { | ||
| _links[chain] = Link(gateway, remote); | ||
| emit RemoteRegistered(gateway, remote); | ||
| } else { | ||
| revert RemoteAlreadyRegistered(chain); | ||
| } | ||
| } | ||
|  | ||
| /// @dev Internal messaging function. | ||
| function _sendMessage( | ||
| bytes memory chain, | ||
| bytes memory payload, | ||
| bytes[] memory attributes | ||
| ) internal virtual returns (bytes32) { | ||
| (address gateway, bytes memory remote) = link(chain); | ||
| return IERC7786GatewaySource(gateway).sendMessage(remote, payload, attributes); | ||
| } | ||
|  | ||
| /// @inheritdoc ERC7786Recipient | ||
| function _isAuthorizedGateway( | ||
| address instance, | ||
| bytes calldata sender | ||
| ) internal view virtual override returns (bool) { | ||
| (address gateway, bytes memory router) = link(_extractChain(sender)); | ||
| return instance == gateway && sender.equal(router); | ||
| } | ||
|  | ||
| function _extractChain(bytes memory self) private pure returns (bytes memory) { | ||
| (bytes2 chainType, bytes memory chainReference, ) = self.parseV1(); | ||
| return InteroperableAddress.formatV1(chainType, chainReference, hex""); | ||
| } | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| pragma solidity ^0.8.26; | ||
|  | ||
| import {InteroperableAddress} from "../../utils/draft-InteroperableAddress.sol"; | ||
| import {ERC7786Recipient} from "../ERC7786Recipient.sol"; | ||
| import {BridgeCore} from "./BridgeCore.sol"; | ||
|  | ||
| /** | ||
| * @dev Base contract for bridging ERC-20 between chains using an ERC-7786 gateway. | ||
| * | ||
| * In order to use this contract, two function must be implemented to link it to the token: | ||
| * * {lock}: called when a crosschain transfer is going out. Must take the sender tokens or revert. | ||
| * * {unlock}: called when a crosschain transfer is coming it. Must give tokens to the receiver. | ||
| * | ||
| * This base contract is used by the {BridgeERC20Custodial}, which interfaces with legacy ERC-20 tokens, and | ||
| * {BridgeERC20Bridgeable}, which interface with ERC-7802 to provide an approve-free user experience. It is also used``` | ||
| * by the {ERC20Crosschain} extension, which embeds the bridge logic directly in the token contract. | ||
| */ | ||
| abstract contract BridgeERC20 is BridgeCore { | ||
| using InteroperableAddress for bytes; | ||
|  | ||
| event CrossChainTransferSent(bytes32 indexed sendId, address indexed from, bytes to, uint256 amount); | ||
| event CrossChainTransferReceived(bytes32 indexed receiveId, bytes from, address indexed to, uint256 amount); | ||
|  | ||
| /// @dev Transfer `amount` tokens to a crosschain receiver. | ||
| function crosschainTransfer(bytes memory to, uint256 amount) public virtual returns (bytes32) { | ||
| return _crosschainTransfer(msg.sender, to, amount); | ||
| } | ||
|  | ||
| /// @dev Internal crosschain transfer function. | ||
| function _crosschainTransfer(address from, bytes memory to, uint256 amount) internal virtual returns (bytes32) { | ||
| _lock(from, amount); | ||
|  | ||
| (bytes2 chainType, bytes memory chainReference, bytes memory addr) = to.parseV1(); | ||
| bytes memory chain = InteroperableAddress.formatV1(chainType, chainReference, hex""); | ||
|  | ||
| bytes32 sendId = _sendMessage( | ||
| chain, | ||
| abi.encode(InteroperableAddress.formatEvmV1(block.chainid, from), addr, amount), | ||
| new bytes[](0) | ||
| ); | ||
|  | ||
| emit CrossChainTransferSent(sendId, from, to, amount); | ||
|  | ||
| return sendId; | ||
| } | ||
|  | ||
| /// @inheritdoc ERC7786Recipient | ||
| function _processMessage( | ||
| address /*gateway*/, | ||
| bytes32 receiveId, | ||
| bytes calldata /*sender*/, | ||
| bytes calldata payload | ||
| ) internal virtual override { | ||
| // split payload | ||
| (bytes memory from, bytes memory toBinary, uint256 amount) = abi.decode(payload, (bytes, bytes, uint256)); | ||
| address to = address(bytes20(toBinary)); | ||
|  | ||
| _unlock(to, amount); | ||
|  | ||
| emit CrossChainTransferReceived(receiveId, from, to, amount); | ||
| } | ||
|         
                  Amxx marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
|  | ||
| /// @dev Virtual function: implementation is required to handle token being burnt or locked on the source chain. | ||
| function _lock(address from, uint256 amount) internal virtual; | ||
|  | ||
| /// @dev Virtual function: implementation is required to handle token being minted or unlocked on the destination chain. | ||
| function _unlock(address to, uint256 amount) internal virtual; | ||
| } | ||
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| pragma solidity ^0.8.26; | ||
|  | ||
| import {IERC7802} from "../../interfaces/draft-IERC7802.sol"; | ||
| import {BridgeERC20} from "./BridgeERC20.sol"; | ||
|  | ||
| /** | ||
| * @dev This is a variant of {BridgeERC20} that implements the bridge logic for ERC-7802 compliant tokens. | ||
| */ | ||
| abstract contract BridgeERC20Bridgeable is BridgeERC20 { | ||
| IERC7802 private immutable _token; | ||
|  | ||
| constructor(IERC7802 token_) { | ||
| _token = token_; | ||
| } | ||
|  | ||
| /// @dev Return the address of the ERC20 token this bridge operates on. | ||
| function token() public view virtual returns (IERC7802) { | ||
| return _token; | ||
| } | ||
|  | ||
| /// @dev "Locking" tokens using an ERC-7802 crosschain burn | ||
| function _lock(address from, uint256 amount) internal virtual override { | ||
| token().crosschainBurn(from, amount); | ||
| } | ||
|  | ||
| /// @dev "Unlocking" tokens using an ERC-7802 crosschain mint | ||
| function _unlock(address to, uint256 amount) internal virtual override { | ||
| token().crosschainMint(to, amount); | ||
| } | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| pragma solidity ^0.8.26; | ||
|  | ||
| import {IERC20, SafeERC20} from "../../token/ERC20/utils/SafeERC20.sol"; | ||
| import {BridgeERC20} from "./BridgeERC20.sol"; | ||
|  | ||
| /** | ||
| * @dev This is a variant of {BridgeERC20} that implements the bridge logic for ERC-20 tokens that do not expose mint | ||
| * and burn mechanism. Instead it takes custody of bridged assets. | ||
| */ | ||
| abstract contract BridgeERC20Custodial is BridgeERC20 { | ||
| using SafeERC20 for IERC20; | ||
|  | ||
| IERC20 private immutable _token; | ||
|  | ||
| constructor(IERC20 token_) { | ||
| _token = token_; | ||
| } | ||
|         
                  Amxx marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
|  | ||
| /// @dev Return the address of the ERC20 token this bridge operates on. | ||
| function token() public view virtual returns (IERC20) { | ||
| return _token; | ||
| } | ||
|  | ||
| /// @dev "Locking" tokens is done by taking custody | ||
| function _lock(address from, uint256 amount) internal virtual override { | ||
| token().safeTransferFrom(from, address(this), amount); | ||
| } | ||
|  | ||
| /// @dev "Unlocking" tokens is done by releasing custody | ||
| function _unlock(address to, uint256 amount) internal virtual override { | ||
| token().safeTransfer(to, amount); | ||
| } | ||
| } | ||
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|  | ||
| pragma solidity ^0.8.26; | ||
|  | ||
| import {ERC20} from "../ERC20.sol"; | ||
| import {BridgeERC20} from "../../../crosschain/bridges/BridgeERC20.sol"; | ||
|  | ||
| abstract contract ERC20Crosschain is ERC20, BridgeERC20 { | ||
| /// @dev TransferFrom variant of {crosschainTransferFrom}, using ERC20 allowance from the sender to the caller. | ||
| function crosschainTransferFrom(address from, bytes memory to, uint256 amount) public virtual returns (bytes32) { | ||
| _spendAllowance(from, msg.sender, amount); | ||
| return _crosschainTransfer(from, to, amount); | ||
| } | ||
|  | ||
| /// @dev "Locking" tokens is achieved through burning | ||
| function _lock(address from, uint256 amount) internal virtual override { | ||
| _burn(from, amount); | ||
| } | ||
|  | ||
| /// @dev "Unlocking" tokens is achieved through minting | ||
| function _unlock(address to, uint256 amount) internal virtual override { | ||
| _mint(to, amount); | ||
| } | ||
| } | 
      
      Oops, something went wrong.
        
    
  
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.