Skip to content

Commit

Permalink
Merge branch 'development' into feat/veion-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
vidvidvid committed Nov 7, 2024
2 parents 045aa9c + d2d8f0f commit 2dfc463
Show file tree
Hide file tree
Showing 30 changed files with 4,604 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;

interface IInterchainSecurityModule {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
LEGACY_MULTISIG,
MERKLE_ROOT_MULTISIG,
MESSAGE_ID_MULTISIG,
NULL, // used with relayer carrying no metadata
CCIP_READ,
ARB_L2_TO_L1,
WEIGHTED_MERKLE_ROOT_MULTISIG,
WEIGHTED_MESSAGE_ID_MULTISIG,
OP_L2_TO_L1
}

/**
* @notice Returns an enum that represents the type of security model
* encoded by this ISM.
* @dev Relayers infer how to fetch and format metadata.
*/
function moduleType() external view returns (uint8);

/**
* @notice Defines a security model responsible for verifying interchain
* messages based on the provided metadata.
* @param _metadata Off-chain metadata provided by a relayer, specific to
* the security model encoded by the module (e.g. validator signatures)
* @param _message Hyperlane encoded interchain message
* @return True if the message was verified
*/
function verify(
bytes calldata _metadata,
bytes calldata _message
) external returns (bool);
}

interface ISpecifiesInterchainSecurityModule {
function interchainSecurityModule()
external
view
returns (IInterchainSecurityModule);
}
109 changes: 109 additions & 0 deletions packages/contracts/contracts/bridge/hyperlane/interfaces/IMailbox.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

import {IInterchainSecurityModule} from "./IInterchainSecurityModule.sol";
import {IPostDispatchHook} from "./hooks/IPostDispatchHook.sol";

interface IMailbox {
// ============ Events ============
/**
* @notice Emitted when a new message is dispatched via Hyperlane
* @param sender The address that dispatched the message
* @param destination The destination domain of the message
* @param recipient The message recipient address on `destination`
* @param message Raw bytes of message
*/
event Dispatch(
address indexed sender,
uint32 indexed destination,
bytes32 indexed recipient,
bytes message
);

/**
* @notice Emitted when a new message is dispatched via Hyperlane
* @param messageId The unique message identifier
*/
event DispatchId(bytes32 indexed messageId);

/**
* @notice Emitted when a Hyperlane message is processed
* @param messageId The unique message identifier
*/
event ProcessId(bytes32 indexed messageId);

/**
* @notice Emitted when a Hyperlane message is delivered
* @param origin The origin domain of the message
* @param sender The message sender address on `origin`
* @param recipient The address that handled the message
*/
event Process(
uint32 indexed origin,
bytes32 indexed sender,
address indexed recipient
);

function localDomain() external view returns (uint32);

function delivered(bytes32 messageId) external view returns (bool);

function defaultIsm() external view returns (IInterchainSecurityModule);

function defaultHook() external view returns (IPostDispatchHook);

function requiredHook() external view returns (IPostDispatchHook);

function latestDispatchedId() external view returns (bytes32);

function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) external payable returns (bytes32 messageId);

function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) external view returns (uint256 fee);

function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata body,
bytes calldata defaultHookMetadata
) external payable returns (bytes32 messageId);

function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata defaultHookMetadata
) external view returns (uint256 fee);

function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata body,
bytes calldata customHookMetadata,
IPostDispatchHook customHook
) external payable returns (bytes32 messageId);

function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata customHookMetadata,
IPostDispatchHook customHook
) external view returns (uint256 fee);

function process(
bytes calldata metadata,
bytes calldata message
) external payable;

function recipientIsm(
address recipient
) external view returns (IInterchainSecurityModule module);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/

interface IPostDispatchHook {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
MERKLE_TREE,
INTERCHAIN_GAS_PAYMASTER,
FALLBACK_ROUTING,
ID_AUTH_ISM,
PAUSABLE,
PROTOCOL_FEE,
LAYER_ZERO_V1,
RATE_LIMITED,
ARB_L2_TO_L1,
OP_L2_TO_L1
}

/**
* @notice Returns an enum that represents the type of hook
*/
function hookType() external view returns (uint8);

/**
* @notice Returns whether the hook supports metadata
* @param metadata metadata
* @return Whether the hook supports metadata
*/
function supportsMetadata(
bytes calldata metadata
) external view returns (bool);

/**
* @notice Post action after a message is dispatched via the Mailbox
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
*/
function postDispatch(
bytes calldata metadata,
bytes calldata message
) external payable;

/**
* @notice Compute the payment required by the postDispatch call
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
* @return Quoted payment for the postDispatch call
*/
function quoteDispatch(
bytes calldata metadata,
bytes calldata message
) external view returns (uint256);
}
162 changes: 162 additions & 0 deletions packages/contracts/contracts/bridge/xERC20Hyperlane.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IXERC20 } from "./interface/IXERC20.sol";
import { IMailbox } from "./hyperlane/interfaces/IMailbox.sol";

library TypeCasts {
// alignment preserving cast
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}

// alignment preserving cast
function bytes32ToAddress(bytes32 _buf) internal pure returns (address) {
return address(uint160(uint256(_buf)));
}
}

contract xERC20Hyperlane is Ownable {
using TypeCasts for address;
using TypeCasts for bytes32;

uint256 public feeBps;
mapping(address => mapping(uint32 => address)) public mappedTokens;
mapping(uint32 => address) public mappedBridges;
IMailbox immutable mailbox;

event TokenMapped(address indexed _token, uint32 indexed _chainId, address indexed _dstToken);
event BridgeMapped(uint32 indexed _chainId, address indexed _bridge);
event FeeBpsSet(uint256 indexed _feeBps);
event TokenSent(
address indexed _token,
uint256 _amount,
address indexed _to,
uint32 indexed _dstChainId,
bytes32 _guid
);
event TokenReceived(
address indexed _token,
uint256 _amount,
address indexed _to,
uint32 indexed _srcChainId,
bytes32 _guid
);

error TokenNotSet();
error ChainIdNotSet();
error DestinationBridgeNotSet(uint32 _chainId);
error OriginNotAllowed(uint32 _chainId, address _sender);

/**
* @notice Only accept messages from an Hyperlane Mailbox contract
*/
modifier onlyMailbox() {
require(msg.sender == address(mailbox), "MailboxClient: sender not mailbox");
_;
}

constructor(uint256 _feeBps, address _mailbox) Ownable() {
mailbox = IMailbox(_mailbox);
feeBps = _feeBps;
}

// ADMIN FUNCTIONS
function setFeeBps(uint256 _feeBps) public onlyOwner {
feeBps = _feeBps;
emit FeeBpsSet(_feeBps);
}

function setMappedToken(uint32 _chainId, address _srcToken, address _dstToken) public onlyOwner {
mappedTokens[_srcToken][_chainId] = _dstToken;
emit TokenMapped(_srcToken, _chainId, _dstToken);
}

function setMappedBridge(uint32 _chainId, address _bridge) public onlyOwner {
mappedBridges[_chainId] = _bridge;
emit BridgeMapped(_chainId, _bridge);
}

function withdrawFee(address _token) public onlyOwner {
uint256 _amount = IERC20(_token).balanceOf(address(this));
IERC20(_token).transfer(msg.sender, _amount);
}

function withdrawFee(address _token, uint256 _amount) public onlyOwner {
IERC20(_token).transfer(msg.sender, _amount);
}

function withdrawEth() public onlyOwner {
uint256 _amount = address(this).balance;
payable(msg.sender).transfer(_amount);
}

function withdrawEth(uint256 _amount) public onlyOwner {
payable(msg.sender).transfer(_amount);
}

// PUBLIC FUNCTIONS
function quote(uint32 _dstChainId, address _token, uint256 _amount, address _to) external view returns (uint256 fee) {
return _quoteInternal(_dstChainId, _token, _amount, _to);
}

function send(address _token, uint256 _amount, address _to, uint32 _dstChainId) external payable {
_send(_dstChainId, _token, _amount, _to);
}

// INTERNAL FUNCTIONS
function _quoteInternal(
uint32 _dstChainId,
address _token,
uint256 _amount,
address _to
) internal view returns (uint256 fee) {
address _bridge = mappedBridges[_dstChainId];
if (_bridge == address(0)) {
revert DestinationBridgeNotSet(_dstChainId);
}
uint256 _amountAfterFee = (_amount * (10000 - feeBps)) / 10000;
bytes memory _payload = abi.encode(_to, _token, _amountAfterFee);
fee = mailbox.quoteDispatch(_dstChainId, _bridge.addressToBytes32(), _payload);
return fee;
}

function _send(uint32 _dstChainId, address _token, uint256 _amount, address _to) internal {
address _bridge = mappedBridges[_dstChainId];
if (_bridge == address(0)) {
revert DestinationBridgeNotSet(_dstChainId);
}
// transfer tokens to this contract
IERC20(_token).transferFrom(msg.sender, address(this), _amount);

// take fee and burn the tokens
uint256 _amountAfterFee = (_amount * (10000 - feeBps)) / 10000;
IXERC20(_token).burn(address(this), _amountAfterFee);

bytes memory _payload = abi.encode(_to, _token, _amountAfterFee);
bytes32 _guid = mailbox.dispatch{ value: msg.value }(_dstChainId, _bridge.addressToBytes32(), _payload);
emit TokenSent(_token, _amountAfterFee, _to, _dstChainId, _guid);
}

function handle(uint32 _origin, bytes32 _sender, bytes calldata _data) external payable virtual onlyMailbox {
if (mappedBridges[_origin] != _sender.bytes32ToAddress()) {
revert OriginNotAllowed(_origin, _sender.bytes32ToAddress());
}
// Decode the payload to get the message
(address _to, address _srcToken, uint256 _amount) = abi.decode(_data, (address, address, uint256));

// get the mapped token using the current chain id and received source token
address _dstToken = mappedTokens[_srcToken][_origin];
if (_dstToken == address(0)) {
revert TokenNotSet();
}

// mint the tokens to the destination address
IXERC20(_dstToken).mint(_to, _amount);
emit TokenReceived(_dstToken, _amount, _to, _origin, bytes32(0));
}

receive() external payable {}
}
Loading

0 comments on commit 2dfc463

Please sign in to comment.