example-portal-fast-withdrawal/evm/contracts/FastTransfer.sol

115 lines
3.6 KiB
Solidity

// contracts/FastTransfer.sol
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "./libraries/external/BytesLib.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWormhole {
function publishMessage(
uint32 nonce,
bytes memory payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function messageFee() external view returns (uint256);
}
interface ITokenBridge {
function wrapAndTransferETH(
uint16 recipientChain,
bytes32 recipient,
uint256 arbiterFee,
uint32 nonce
) external payable returns (uint64 sequence);
function chainId() external view returns (uint16);
function WETH() external view returns (IWETH);
}
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint amount) external;
}
// reuse Portal Transfer struct for convenience
struct Transfer {
// PayloadID uint8 = 1
uint8 payloadID;
// Amount being transferred (big-endian uint256)
uint256 amount;
// Address of the token. Left-zero-padded if shorter than 32 bytes
bytes32 tokenAddress;
// Chain ID of the token
uint16 tokenChain;
// Address of the recipient. Left-zero-padded if shorter than 32 bytes
bytes32 to;
// Chain ID of the recipient
uint16 toChain;
// Amount of tokens (big-endian uint256) that the user is willing to pay as relayer fee. Must be <= Amount.
uint256 fee;
}
contract FastTransfer {
IWormhole wormhole;
ITokenBridge portal;
constructor(address wormholeAddress, address portalAddress) {
wormhole = IWormhole(wormholeAddress);
portal = ITokenBridge(portalAddress);
}
function wrapAndTransferETH(
uint16 recipientChain,
bytes32 recipient,
uint256 arbiterFee,
uint32 nonce
) public payable returns (uint64 fastSequence, uint64 portalSequence) {
// Portal accounts for 1 fee, but we must account for 2
uint wormholeFee = wormhole.messageFee();
require(wormholeFee * 2 < msg.value, "value is smaller than wormhole fees");
uint amount = msg.value - wormholeFee * 2;
// Portal will normalize the amount to 8 decimals, so we should do the same
uint normalizedAmount = normalizeAmount(amount, 18);
Transfer memory fastTransfer = Transfer({
payloadID: 1,
amount: normalizedAmount,
tokenAddress: bytes32(uint256(uint160(address(portal.WETH())))),
tokenChain: portal.chainId(),
to: recipient,
toChain: recipientChain,
fee: 0
});
fastSequence = wormhole.publishMessage{
value : wormholeFee
}(
0,
abi.encodePacked(
fastTransfer.payloadID,
fastTransfer.amount,
fastTransfer.tokenAddress,
fastTransfer.tokenChain,
fastTransfer.to,
fastTransfer.toChain,
fastTransfer.fee
),
200
);
// Forward the remaining value sans the first fee
portalSequence = portal.wrapAndTransferETH{
value: msg.value - wormholeFee
}(
recipientChain,
recipient, // TODO: receiving chain pool
arbiterFee,
nonce
);
}
function normalizeAmount(uint256 amount, uint8 decimals) internal pure returns(uint256){
if (decimals > 8) {
amount /= 10 ** (decimals - 8);
}
return amount;
}
}