-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'development' into feat/veion-ui
- Loading branch information
Showing
30 changed files
with
4,604 additions
and
46 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
packages/contracts/contracts/bridge/hyperlane/interfaces/IInterchainSecurityModule.sol
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,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
109
packages/contracts/contracts/bridge/hyperlane/interfaces/IMailbox.sol
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,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); | ||
} |
67 changes: 67 additions & 0 deletions
67
packages/contracts/contracts/bridge/hyperlane/interfaces/hooks/IPostDispatchHook.sol
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,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
162
packages/contracts/contracts/bridge/xERC20Hyperlane.sol
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,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 {} | ||
} |
Oops, something went wrong.