115 lines
3.6 KiB
Solidity
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;
|
||
|
}
|
||
|
|
||
|
}
|