-
Notifications
You must be signed in to change notification settings - Fork 12.4k
ERC-7786 based crosschain bridge for ERC-1155 tokens #6281
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
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
3304012
ERC-1155 crosschain bridge
Amxx 0081d47
add tests
Amxx 20d1fb1
add changeset
Amxx 333ca55
documentation
Amxx 549e179
update
Amxx 2c7147d
update
Amxx 8c64a33
Update contracts/crosschain/bridges/BridgeERC1155.sol
Amxx c5adcee
update
Amxx ff273ec
Update BridgeERC1155Core.sol
Amxx 59307fd
extended coverage
Amxx 4de449e
Apply suggestion from @Amxx
Amxx 0c71d04
Merge branch 'master' into crosschain/erc1155bridge
Amxx f1049d7
rename / reorganise following the ERC20 changes
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
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,5 @@ | ||
| --- | ||
| 'openzeppelin-solidity': minor | ||
| --- | ||
|
|
||
| `ERC1155Crosschain`: Added an ERC-1155 extension to embed an ERC-7786 based crosschain bridge directly in the token contract. |
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,5 @@ | ||
| --- | ||
| 'openzeppelin-solidity': minor | ||
| --- | ||
|
|
||
| `BridgeMultiToken` and `BridgeERC1155`: Added bridge contracts to handle crosschain movements of ERC-1155 tokens. |
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,101 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity ^0.8.26; | ||
|
|
||
| import {IERC1155} from "../../interfaces/IERC1155.sol"; | ||
| import {IERC1155Receiver} from "../../interfaces/IERC1155Receiver.sol"; | ||
| import {IERC1155Errors} from "../../interfaces/draft-IERC6093.sol"; | ||
| import {ERC1155Holder} from "../../token/ERC1155/utils/ERC1155Holder.sol"; | ||
| import {BridgeMultiToken} from "./abstract/BridgeMultiToken.sol"; | ||
|
|
||
| /** | ||
| * @dev This is a variant of {BridgeMultiToken} that implements the bridge logic for ERC-1155 tokens that do not expose | ||
| * a crosschain mint and burn mechanism. Instead, it takes custody of bridged assets. | ||
| */ | ||
| // slither-disable-next-line locked-ether | ||
| abstract contract BridgeERC1155 is BridgeMultiToken, ERC1155Holder { | ||
| IERC1155 private immutable _token; | ||
|
|
||
| constructor(IERC1155 token_) { | ||
| _token = token_; | ||
| } | ||
|
|
||
| /// @dev Return the address of the ERC1155 token this bridge operates on. | ||
| function token() public view virtual returns (IERC1155) { | ||
| return _token; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Transfer `amount` tokens to a crosschain receiver. | ||
| * | ||
| * Note: The `to` parameter is the full InteroperableAddress (chain ref + address). | ||
| */ | ||
| function crosschainTransferFrom(address from, bytes memory to, uint256 id, uint256 value) public returns (bytes32) { | ||
| uint256[] memory ids = new uint256[](1); | ||
| uint256[] memory values = new uint256[](1); | ||
| ids[0] = id; | ||
| values[0] = value; | ||
|
|
||
| return crosschainTransferFrom(from, to, ids, values); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Transfer `amount` tokens to a crosschain receiver. | ||
| * | ||
| * Note: The `to` parameter is the full InteroperableAddress (chain ref + address). | ||
| */ | ||
| function crosschainTransferFrom( | ||
| address from, | ||
| bytes memory to, | ||
| uint256[] memory ids, | ||
| uint256[] memory values | ||
| ) public virtual returns (bytes32) { | ||
| // Permission is handled using the ERC1155's allowance system. This check replicates `ERC1155._checkAuthorized`. | ||
| address spender = _msgSender(); | ||
| require( | ||
| from == spender || token().isApprovedForAll(from, spender), | ||
| IERC1155Errors.ERC1155MissingApprovalForAll(spender, from) | ||
| ); | ||
|
|
||
| // Perform the crosschain transfer and return the handler | ||
| return _crosschainTransfer(from, to, ids, values); | ||
| } | ||
|
|
||
| /// @dev "Locking" tokens is done by taking custody | ||
| function _onSend(address from, uint256[] memory ids, uint256[] memory values) internal virtual override { | ||
| token().safeBatchTransferFrom(from, address(this), ids, values, ""); | ||
| } | ||
|
|
||
| /// @dev "Unlocking" tokens is done by releasing custody | ||
| function _onReceive(address to, uint256[] memory ids, uint256[] memory values) internal virtual override { | ||
| token().safeBatchTransferFrom(address(this), to, ids, values, ""); | ||
| } | ||
|
|
||
| /// @dev Support receiving tokens only if the transfer was initiated by the bridge itself. | ||
| function onERC1155Received( | ||
| address operator, | ||
| address /* from */, | ||
| uint256 /* id */, | ||
| uint256 /* value */, | ||
| bytes memory /* data */ | ||
| ) public virtual override returns (bytes4) { | ||
| return | ||
| msg.sender == address(_token) && operator == address(this) | ||
| ? IERC1155Receiver.onERC1155Received.selector | ||
| : bytes4(0); | ||
| } | ||
|
|
||
| /// @dev Support receiving tokens only if the transfer was initiated by the bridge itself. | ||
| function onERC1155BatchReceived( | ||
| address operator, | ||
| address /* from */, | ||
| uint256[] memory /* ids */, | ||
| uint256[] memory /* values */, | ||
| bytes memory /* data */ | ||
| ) public virtual override returns (bytes4) { | ||
| return | ||
| msg.sender == address(_token) && operator == address(this) | ||
| ? IERC1155Receiver.onERC1155BatchReceived.selector | ||
| : bytes4(0); | ||
| } | ||
| } |
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
92 changes: 92 additions & 0 deletions
92
contracts/crosschain/bridges/abstract/BridgeMultiToken.sol
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,92 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity ^0.8.26; | ||
|
|
||
| import {InteroperableAddress} from "../../../utils/draft-InteroperableAddress.sol"; | ||
| import {Context} from "../../../utils/Context.sol"; | ||
| import {ERC7786Recipient} from "../../ERC7786Recipient.sol"; | ||
| import {CrosschainLinked} from "../../CrosschainLinked.sol"; | ||
|
|
||
| /** | ||
| * @dev Base contract for bridging ERC-1155 between chains using an ERC-7786 gateway. | ||
| * | ||
| * In order to use this contract, two functions must be implemented to link it to the token: | ||
| * * {_onSend}: called when a crosschain transfer is going out. Must take the sender tokens or revert. | ||
| * * {_onReceive}: called when a crosschain transfer is coming in. Must give tokens to the receiver. | ||
| * | ||
| * This base contract is used by the {BridgeERC1155}, which interfaces with legacy ERC-1155 tokens. It is also used by | ||
| * the {ERC1155Crosschain} extension, which embeds the bridge logic directly in the token contract. | ||
| * | ||
| * This base contract implements the crosschain transfer operation though internal functions. It is for the the "child | ||
| * contracts" that inherit from this to implement the external interfaces and make this functions accessible. | ||
| */ | ||
| abstract contract BridgeMultiToken is Context, CrosschainLinked { | ||
| using InteroperableAddress for bytes; | ||
|
|
||
| event CrosschainMultiTokenTransferSent( | ||
| bytes32 indexed sendId, | ||
| address indexed from, | ||
| bytes to, | ||
| uint256[] ids, | ||
| uint256[] values | ||
| ); | ||
| event CrosschainMultiTokenTransferReceived( | ||
| bytes32 indexed receiveId, | ||
| bytes from, | ||
| address indexed to, | ||
| uint256[] ids, | ||
| uint256[] values | ||
| ); | ||
| /** | ||
| * @dev Internal crosschain transfer function. | ||
| * | ||
| * Note: The `to` parameter is the full InteroperableAddress (chain ref + address). | ||
| */ | ||
| function _crosschainTransfer( | ||
| address from, | ||
| bytes memory to, | ||
| uint256[] memory ids, | ||
| uint256[] memory values | ||
| ) internal virtual returns (bytes32) { | ||
| _onSend(from, ids, values); | ||
|
|
||
| (bytes2 chainType, bytes memory chainReference, bytes memory addr) = to.parseV1(); | ||
| bytes memory chain = InteroperableAddress.formatV1(chainType, chainReference, hex""); | ||
|
|
||
| bytes32 sendId = _sendMessageToCounterpart( | ||
| chain, | ||
| abi.encode(InteroperableAddress.formatEvmV1(block.chainid, from), addr, ids, values), | ||
| new bytes[](0) | ||
| ); | ||
|
|
||
| emit CrosschainMultiTokenTransferSent(sendId, from, to, ids, values); | ||
| return sendId; | ||
| } | ||
|
|
||
| /// @inheritdoc ERC7786Recipient | ||
| function _processMessage( | ||
| address /*gateway*/, | ||
Amxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| bytes32 receiveId, | ||
| bytes calldata /*sender*/, | ||
| bytes calldata payload | ||
| ) internal virtual override { | ||
| // NOTE: Gateway is validated by {_isAuthorizedGateway} (implemented in {CrosschainLinked}). No need to check here. | ||
|
|
||
| // split payload | ||
| (bytes memory from, bytes memory toEvm, uint256[] memory ids, uint256[] memory values) = abi.decode( | ||
| payload, | ||
| (bytes, bytes, uint256[], uint256[]) | ||
| ); | ||
| address to = address(bytes20(toEvm)); | ||
|
|
||
| _onReceive(to, ids, values); | ||
|
|
||
| emit CrosschainMultiTokenTransferReceived(receiveId, from, to, ids, values); | ||
| } | ||
|
|
||
| /// @dev Virtual function: implementation is required to handle token being burnt or locked on the source chain. | ||
| function _onSend(address from, uint256[] memory ids, uint256[] memory values) internal virtual; | ||
|
|
||
| /// @dev Virtual function: implementation is required to handle token being minted or unlocked on the destination chain. | ||
| function _onReceive(address to, uint256[] memory ids, uint256[] memory values) 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
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,53 @@ | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| pragma solidity ^0.8.26; | ||
|
|
||
| import {ERC1155} from "../ERC1155.sol"; | ||
| import {BridgeMultiToken} from "../../../crosschain/bridges/abstract/BridgeMultiToken.sol"; | ||
|
|
||
| /** | ||
| * @dev Extension of {ERC1155} that makes it natively cross-chain using the ERC-7786 based {BridgeMultiToken}. | ||
| * | ||
| * This extension makes the token compatible with: | ||
| * * {ERC1155Crosschain} instances on other chains, | ||
| * * {ERC1155} instances on other chains that are bridged using {BridgeERC1155}, | ||
| */ | ||
| // slither-disable-next-line locked-ether | ||
| abstract contract ERC1155Crosschain is ERC1155, BridgeMultiToken { | ||
| /// @dev TransferFrom variant of {crosschainTransferFrom}, using ERC1155 allowance from the sender to the caller. | ||
| function crosschainTransferFrom( | ||
| address from, | ||
| bytes memory to, | ||
| uint256 id, | ||
| uint256 value | ||
| ) public virtual returns (bytes32) { | ||
| _checkAuthorized(_msgSender(), from); | ||
|
|
||
| uint256[] memory ids = new uint256[](1); | ||
| uint256[] memory values = new uint256[](1); | ||
| ids[0] = id; | ||
| values[0] = value; | ||
| return _crosschainTransfer(from, to, ids, values); | ||
| } | ||
|
|
||
| /// @dev TransferFrom variant of {crosschainTransferFrom}, using ERC1155 allowance from the sender to the caller. | ||
| function crosschainTransferFrom( | ||
| address from, | ||
| bytes memory to, | ||
| uint256[] memory ids, | ||
| uint256[] memory values | ||
| ) public virtual returns (bytes32) { | ||
| _checkAuthorized(_msgSender(), from); | ||
| return _crosschainTransfer(from, to, ids, values); | ||
| } | ||
|
|
||
| /// @dev "Locking" tokens is achieved through burning | ||
| function _onSend(address from, uint256[] memory ids, uint256[] memory values) internal virtual override { | ||
| _burnBatch(from, ids, values); | ||
| } | ||
|
|
||
| /// @dev "Unlocking" tokens is achieved through minting | ||
| function _onReceive(address to, uint256[] memory ids, uint256[] memory values) internal virtual override { | ||
| _mintBatch(to, ids, values, ""); | ||
| } | ||
| } |
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.