The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hyperlane-monorepo/solidity/contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol

141 lines
4.7 KiB

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import {Router} from "../../Router.sol";
import {IMessageRecipient} from "../../../interfaces/IMessageRecipient.sol";
import {ICircleBridge} from "./interfaces/circle/ICircleBridge.sol";
import {ICircleMessageTransmitter} from "./interfaces/circle/ICircleMessageTransmitter.sol";
import {ILiquidityLayerAdapter} from "./interfaces/ILiquidityLayerAdapter.sol";
import {ILiquidityLayerMessageRecipient} from "./interfaces/ILiquidityLayerMessageRecipient.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract LiquidityLayerRouter is Router {
// Token bridge => adapter address
mapping(string => address) public liquidityLayerAdapters;
event LiquidityLayerAdapterSet(string indexed bridge, address adapter);
function initialize(
address _owner,
2 years ago
address _mailbox,
address _interchainGasPaymaster
) public initializer {
__HyperlaneConnectionClient_initialize(
_mailbox,
_interchainGasPaymaster
);
_transferOwnership(_owner);
}
function dispatchWithTokens(
uint32 _destinationDomain,
bytes32 _recipientAddress,
bytes calldata _messageBody,
address _token,
uint256 _amount,
string calldata _bridge
) external payable {
ILiquidityLayerAdapter _adapter = _getAdapter(_bridge);
// Transfer the tokens to the adapter
// TODO: use safeTransferFrom
// TODO: Are there scenarios where a transferFrom fails and it doesn't revert?
require(
IERC20(_token).transferFrom(msg.sender, address(_adapter), _amount),
"!transfer in"
);
// Reverts if the bridge was unsuccessful.
// Gets adapter-specific data that is encoded into the message
// ultimately sent via Hyperlane.
bytes memory _adapterData = _adapter.sendTokens(
_destinationDomain,
_recipientAddress,
_token,
_amount
);
// The user's message "wrapped" with metadata required by this middleware
bytes memory _messageWithMetadata = abi.encode(
TypeCasts.addressToBytes32(msg.sender),
_recipientAddress, // The "user" recipient
_amount, // The amount of the tokens sent over the bridge
_bridge, // The destination token bridge ID
_adapterData, // The adapter-specific data
_messageBody // The "user" message
);
// Dispatch the _messageWithMetadata to the destination's LiquidityLayerRouter.
_dispatchWithGas(
_destinationDomain,
_messageWithMetadata,
0, // TODO eventually accommodate gas amounts
msg.value,
msg.sender
);
}
// Handles a message from an enrolled remote LiquidityLayerRouter
function _handle(
uint32 _origin,
bytes32, // _sender, unused
bytes calldata _message
) internal override {
// Decode the message with metadata, "unwrapping" the user's message body
(
bytes32 _originalSender,
bytes32 _userRecipientAddress,
uint256 _amount,
string memory _bridge,
bytes memory _adapterData,
bytes memory _userMessageBody
) = abi.decode(
_message,
(bytes32, bytes32, uint256, string, bytes, bytes)
);
ILiquidityLayerMessageRecipient _userRecipient = ILiquidityLayerMessageRecipient(
TypeCasts.bytes32ToAddress(_userRecipientAddress)
);
// Reverts if the adapter hasn't received the bridged tokens yet
(address _token, uint256 _receivedAmount) = _getAdapter(_bridge)
.receiveTokens(
_origin,
address(_userRecipient),
_amount,
_adapterData
);
_userRecipient.handleWithTokens(
_origin,
_originalSender,
_userMessageBody,
_token,
_receivedAmount
);
}
function setLiquidityLayerAdapter(string calldata _bridge, address _adapter)
external
onlyOwner
{
liquidityLayerAdapters[_bridge] = _adapter;
emit LiquidityLayerAdapterSet(_bridge, _adapter);
}
function _getAdapter(string memory _bridge)
internal
view
returns (ILiquidityLayerAdapter _adapter)
{
_adapter = ILiquidityLayerAdapter(liquidityLayerAdapters[_bridge]);
// Require the adapter to have been set
require(address(_adapter) != address(0), "No adapter found for bridge");
}
}