Skip to content

Commit

Permalink
Merge pull request #5 from 0xdevhub/feat/crosschain-erc721-ccip
Browse files Browse the repository at this point in the history
WIP: Feat/crosschain erc721 ccip
  • Loading branch information
wellitongervickas authored Nov 13, 2023
2 parents f8558e5 + 9e66dea commit 32d71ae
Show file tree
Hide file tree
Showing 23 changed files with 576 additions and 139 deletions.
2 changes: 0 additions & 2 deletions config/networks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const networks = {}

console.log('NODE_ENV: ', process.env.NODE_ENV)

if (process.env.NODE_ENV !== 'development') {
Object.assign(networks, {
mumbai: {
Expand Down
5 changes: 0 additions & 5 deletions contracts/AccessManagement.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ pragma solidity 0.8.21;

import "@openzeppelin/contracts/access/manager/AccessManager.sol";

/**
* @title AccessManagement
* @notice This contract is used to manage access to the protocol
*/

contract AccessManagement is AccessManager {
constructor(address admin) AccessManager(admin) {}
}
36 changes: 8 additions & 28 deletions contracts/Hub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,42 @@ pragma solidity 0.8.21;

import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";

/**
* @title Hub
* @notice This contract is used to manage apps in the protocol
*/

contract Hub is AccessManaged {
struct App {
address appAddress;
string name;
string description;
}

/// @notice The salt used to generate appIds
uint256 private s_appIdSalt;

mapping(bytes32 => App) private s_apps;

/// @notice Emitted when an app is added to the hub
event Hub_AppAdded(bytes32 indexed appId_);

constructor(address accessManagement_) AccessManaged(accessManagement_) {}

/**
* @notice Adds an app to the hub
* @param appAddress_ The address of the app contract
* @return The id of the app
*/
function addApp(address appAddress_) external restricted returns (bytes32) {
function addApp(
address appAddress_,
string memory name_,
string memory description_
) external restricted returns (bytes32) {
bytes32 appId = _getNextAppId(appAddress_);
s_apps[appId] = App({appAddress: appAddress_});
s_apps[appId] = App({appAddress: appAddress_, name: name_, description: description_});

emit Hub_AppAdded(appId);

return appId;
}

/**
* @notice Gets the next app id
* @param appAddress_ The address of the app contract
* @return The id of the app
*/
function _getNextAppId(address appAddress_) private returns (bytes32) {
return keccak256(abi.encodePacked(_getNextAppIdSalt(), msg.sender, appAddress_));
}

/**
* @notice Gets the next app id salt
* @return The next app id salt
*/
function _getNextAppIdSalt() private returns (uint256) {
return s_appIdSalt++;
}

/**
* @notice Gets an app from the hub
* @param appId_ The id of the app
* @return The app
*/
function getApp(bytes32 appId_) external view returns (App memory) {
return s_apps[appId_];
}
Expand Down
13 changes: 0 additions & 13 deletions contracts/apps/ccip/crosschain-nft/Bridge.sol

This file was deleted.

13 changes: 0 additions & 13 deletions contracts/apps/ccip/crosschain-nft/Fees.sol

This file was deleted.

3 changes: 0 additions & 3 deletions contracts/apps/ccip/crosschain-nft/README.md

This file was deleted.

13 changes: 0 additions & 13 deletions contracts/apps/ccip/crosschain-nft/Router.sol

This file was deleted.

54 changes: 54 additions & 0 deletions contracts/apps/crosschain-nft/Bridge.sol
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_);
}
}
8 changes: 8 additions & 0 deletions contracts/apps/crosschain-nft/NFT.sol
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") {}
}
3 changes: 3 additions & 0 deletions contracts/apps/crosschain-nft/README.md
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.
63 changes: 63 additions & 0 deletions contracts/apps/crosschain-nft/adapters/BaseAdapter.sol
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();
}
}
92 changes: 92 additions & 0 deletions contracts/apps/crosschain-nft/adapters/CCIPAdapter.sol
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
});
}
}
Loading

0 comments on commit 32d71ae

Please sign in to comment.