Hello World with Token Transfer
Here is a basic hello-world example that sends and receives cross-chain messages with associated token transfers:
Users call
sendTokenWithNote
on the source chain to send some tokens with an arbitrary bytes note to the destination chainThe receiver side implements
executeMessageWithTransfer
, which records received token balances for each sender, and emits events with transfer info including the note.
contract MsgExampleBasicTransfer is MessageApp {
using SafeERC20 for IERC20;
event MessageWithTransferReceived(
address sender,
address token,
uint256 amount,
uint64 srcChainId,
bytes note
);
event MessageWithTransferRefunded(
address sender,
address token,
uint256 amount,
bytes note
);
// acccount, token -> balance
mapping(address => mapping(address => uint256)) public balances;
constructor(address _messageBus) MessageApp(_messageBus) {}
// called by user on source chain to send token with note to destination chain
function sendTokenWithNote(
address _dstContract,
address _token,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage,
bytes calldata _note,
MsgDataTypes.BridgeSendType _bridgeSendType
) external payable {
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
bytes memory message = abi.encode(msg.sender, _note);
sendMessageWithTransfer(
_dstContract,
_token,
_amount,
_dstChainId,
_nonce,
_maxSlippage,
message,
_bridgeSendType,
msg.value
);
}
// called by MessageBus on the destination chain to receive message with token
// transfer, record and emit info.
// the associated token transfer is guaranteed to have already been received
function executeMessageWithTransfer(
address, // srcContract
address _token,
uint256 _amount,
uint64 _srcChainId,
bytes memory _message,
address // executor
) external payable override onlyMessageBus returns (ExecutionStatus) {
(address sender, bytes memory note) = abi.decode(
(_message),
(address, bytes)
);
balances[sender][_token] += _amount;
emit MessageWithTransferReceived(
sender,
_token,
_amount,
_srcChainId,
note
);
return ExecutionStatus.Success;
}
// called by MessageBus on the source chain to handle message with
// failed associated token transfer.
// the failed token transfer is guaranteed to have already been refunded
function executeMessageWithTransferRefund(
address _token,
uint256 _amount,
bytes calldata _message,
address // executor
) external payable override onlyMessageBus returns (ExecutionStatus) {
(address sender, bytes memory note) = abi.decode(
(_message),
(address, bytes)
);
IERC20(_token).safeTransfer(sender, _amount);
emit MessageWithTransferRefunded(sender, _token, _amount, note);
return ExecutionStatus.Success;
}
// called by user on destination chain to withdraw tokens
function withdraw(address _token, uint256 _amount) external {
balances[msg.sender][_token] -= _amount;
IERC20(_token).safeTransfer(msg.sender, _amount);
}
}
Last updated