-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from 0xdevhub/feat/crosschain-erc721-ccip
WIP: Feat/crosschain erc721 ccip
- Loading branch information
Showing
23 changed files
with
576 additions
and
139 deletions.
There are no files selected for viewing
This file contains 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 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 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 was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains 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,54 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol"; | ||
import {IBaseAdapter} from "./interfaces/IBaseAdapter.sol"; | ||
import {IBridge} from "./interfaces/IBridge.sol"; | ||
|
||
contract Bridge is IBridge, AccessManaged { | ||
address private s_adapter; | ||
|
||
mapping(uint256 => IBridge.MessageSend) public s_sentMessages; | ||
mapping(uint256 => IBridge.MessageReceive) public s_receivedMessages; | ||
|
||
constructor(address accessManagement_, address adapter_) AccessManaged(accessManagement_) { | ||
_setAdapter(adapter_); | ||
} | ||
|
||
function _setAdapter(address adapter_) internal { | ||
s_adapter = adapter_; | ||
|
||
emit IBridge.AdapterChanged(adapter_); | ||
} | ||
|
||
/// @inheritdoc IBridge | ||
function setAdapter(address adapter_) public override restricted { | ||
_setAdapter(adapter_); | ||
} | ||
|
||
/// @inheritdoc IBridge | ||
function adapter() external view override returns (address) { | ||
return s_adapter; | ||
} | ||
|
||
/// @inheritdoc IBridge | ||
function lockAndMintERC721() external override {} | ||
|
||
/// @inheritdoc IBridge | ||
function burnAndUnlockERC721() external override {} | ||
|
||
/** | ||
* @notice send message to adapter | ||
* @param calldata_ encoded data to send to adapter | ||
*/ | ||
function _commitOnRamp(IBridge.MessageSend memory calldata_) private { | ||
IBaseAdapter(s_adapter).sendMessage(calldata_); | ||
emit IBridge.MessageSent(calldata_); | ||
} | ||
|
||
/// @inheritdoc IBridge | ||
function commitOffRamp(IBridge.MessageReceive memory calldata_) external override restricted { | ||
/// todo: handle offramp message | ||
emit IBridge.MessageReceived(calldata_); | ||
} | ||
} |
This file contains 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,8 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.21; | ||
|
||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | ||
|
||
contract MyToken is ERC721 { | ||
constructor() ERC721("MyToken", "MTK") {} | ||
} |
This file contains 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,3 @@ | ||
## Crosschain NFT | ||
|
||
A PoC project to handle crosschain message passing for NFTs. |
This file contains 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,63 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol"; | ||
import {IBridge} from "../interfaces/IBridge.sol"; | ||
import {IBaseAdapter} from "../interfaces/IBaseAdapter.sol"; | ||
|
||
abstract contract BaseAdapter is IBaseAdapter, AccessManaged { | ||
/// @dev bridge address set once in constructor | ||
address private immutable s_bridge; | ||
|
||
constructor(address bridge_, address accessManagement_) AccessManaged(accessManagement_) { | ||
s_bridge = bridge_; | ||
} | ||
|
||
/// @inheritdoc IBaseAdapter | ||
function bridge() public view override returns (address) { | ||
return s_bridge; | ||
} | ||
|
||
/// @inheritdoc IBaseAdapter | ||
function router() public view virtual returns (address); | ||
|
||
/// @inheritdoc IBaseAdapter | ||
function getFee(bytes memory payload_) public view virtual override returns (uint256); | ||
|
||
/// @inheritdoc IBaseAdapter | ||
function feeToken() public view virtual override returns (address); | ||
|
||
/// @inheritdoc IBaseAdapter | ||
/// @dev only bridge can call sendMessage | ||
function sendMessage(IBridge.MessageSend memory payload_) external override restricted { | ||
_sendMessage(payload_); | ||
emit IBaseAdapter.MessageSent(payload_); | ||
} | ||
|
||
/** | ||
* @notice {override} to send crosschain message | ||
* @param payload_ encoded data to send to router | ||
*/ | ||
function _sendMessage(IBridge.MessageSend memory payload_) internal virtual; | ||
|
||
/** | ||
* @notice {override} to receive crosschain message | ||
* @param payload_ encoded data to send to bridge | ||
*/ | ||
function _receiveMessage(IBridge.MessageReceive memory payload_) internal virtual { | ||
IBridge(s_bridge).commitOffRamp(payload_); | ||
emit IBaseAdapter.MessageReceived(payload_); | ||
} | ||
|
||
/// @dev enable to receive native token | ||
receive() external payable { | ||
if (feeToken() != address(0)) { | ||
revert IBaseAdapter.NativeTokenNotSupported(); | ||
} | ||
} | ||
|
||
/// @dev prevent fallback calls | ||
fallback() external payable { | ||
revert(); | ||
} | ||
} |
This file contains 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.21; | ||
|
||
import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol"; | ||
import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol"; | ||
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; | ||
import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol"; | ||
import {IBaseAdapter, BaseAdapter} from "./BaseAdapter.sol"; | ||
import {IBridge} from "../interfaces/IBridge.sol"; | ||
|
||
contract CCIPAdapter is BaseAdapter, CCIPReceiver { | ||
constructor( | ||
address bridge_, | ||
address accessManagement_, | ||
address router_ | ||
) BaseAdapter(bridge_, accessManagement_) CCIPReceiver(router_) {} | ||
|
||
/// @inheritdoc IBaseAdapter | ||
function router() public view override returns (address) { | ||
return this.getRouter(); | ||
} | ||
|
||
/// @inheritdoc IBaseAdapter | ||
function getFee(bytes memory payload) public view override returns (uint256) { | ||
(uint64 toChain, Client.EVM2AnyMessage memory message) = abi.decode(payload, (uint64, Client.EVM2AnyMessage)); | ||
|
||
return IRouterClient(this.router()).getFee(toChain, message); | ||
} | ||
|
||
/// @inheritdoc IBaseAdapter | ||
/// @dev pay fees using native token | ||
function feeToken() public pure override returns (address) { | ||
return address(0); | ||
} | ||
|
||
/// @inheritdoc CCIPReceiver | ||
/// @dev only ccip router can call | ||
function ccipReceive(Client.Any2EVMMessage memory any2EvmMessage) external override restricted { | ||
_ccipReceive(any2EvmMessage); | ||
} | ||
|
||
/// @inheritdoc CCIPReceiver | ||
/// @dev override ccip receive and implement _receiveMessage from BaseAdapter | ||
function _ccipReceive(Client.Any2EVMMessage memory any2EvmMessage) internal override { | ||
IBridge.MessageReceive memory payload = IBridge.MessageReceive({ | ||
fromChain: any2EvmMessage.sourceChainSelector, | ||
sender: abi.decode(any2EvmMessage.sender, (address)), | ||
data: any2EvmMessage.data | ||
}); | ||
|
||
_receiveMessage(payload); | ||
} | ||
|
||
/// @inheritdoc BaseAdapter | ||
function _sendMessage(IBridge.MessageSend memory payload) internal override { | ||
_ccipSend(uint64(payload.toChain), payload.receiver, payload.data); | ||
emit IBaseAdapter.MessageSent(payload); | ||
} | ||
|
||
/** | ||
* @notice send message to other chain via CCIP | ||
* @param toChain target chain id | ||
* @param receiver_ receiver address | ||
* @param data_ encoded message data | ||
*/ | ||
function _ccipSend(uint64 toChain, address receiver_, bytes memory data_) private { | ||
Client.EVM2AnyMessage memory evm2AnyMessage = _buildCCIPMessage(receiver_, data_); | ||
|
||
uint256 fees = getFee(abi.encode(toChain, evm2AnyMessage)); | ||
|
||
IRouterClient(router()).ccipSend{value: fees}(toChain, evm2AnyMessage); | ||
} | ||
|
||
/** | ||
* @notice build CCIP message | ||
* @param receiver_ address of receiver | ||
* @param data_ encoded message data | ||
*/ | ||
function _buildCCIPMessage( | ||
address receiver_, | ||
bytes memory data_ | ||
) private pure returns (Client.EVM2AnyMessage memory) { | ||
return | ||
Client.EVM2AnyMessage({ | ||
receiver: abi.encode(receiver_), | ||
data: data_, | ||
tokenAmounts: new Client.EVMTokenAmount[](0), | ||
extraArgs: Client._argsToBytes(Client.EVMExtraArgsV1({gasLimit: 200_000, strict: false})), | ||
feeToken: address(0) // todo: baseAdapter getFeeToken if zero, use native | ||
}); | ||
} | ||
} |
Oops, something went wrong.