编译器
0.8.17+commit.8df45f5f
文件 1 的 16:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 16:BytesLib.sol
pragma solidity >=0.8.0 <0.9.0;
library BytesLib {
function concat(
bytes memory _preBytes,
bytes memory _postBytes
)
internal
pure
returns (bytes memory)
{
bytes memory tempBytes;
assembly {
tempBytes := mload(0x40)
let length := mload(_preBytes)
mstore(tempBytes, length)
let mc := add(tempBytes, 0x20)
let end := add(mc, length)
for {
let cc := add(_preBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
length := mload(_postBytes)
mstore(tempBytes, add(length, mload(tempBytes)))
mc := end
end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(0x40, and(
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
not(31)
))
}
return tempBytes;
}
function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
assembly {
let fslot := sload(_preBytes.slot)
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
let mlength := mload(_postBytes)
let newlength := add(slength, mlength)
switch add(lt(slength, 32), lt(newlength, 32))
case 2 {
sstore(
_preBytes.slot,
add(
fslot,
add(
mul(
div(
mload(add(_postBytes, 0x20)),
exp(0x100, sub(32, mlength))
),
exp(0x100, sub(32, newlength))
),
mul(mlength, 2)
)
)
)
}
case 1 {
mstore(0x0, _preBytes.slot)
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
let submod := sub(32, slength)
let mc := add(_postBytes, submod)
let end := add(_postBytes, mlength)
let mask := sub(exp(0x100, submod), 1)
sstore(
sc,
add(
and(
fslot,
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
),
and(mload(mc), mask)
)
)
for {
mc := add(mc, 0x20)
sc := add(sc, 1)
} lt(mc, end) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
sstore(sc, mload(mc))
}
mask := exp(0x100, sub(mc, end))
sstore(sc, mul(div(mload(mc), mask), mask))
}
default {
mstore(0x0, _preBytes.slot)
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
let slengthmod := mod(slength, 32)
let mlengthmod := mod(mlength, 32)
let submod := sub(32, slengthmod)
let mc := add(_postBytes, submod)
let end := add(_postBytes, mlength)
let mask := sub(exp(0x100, submod), 1)
sstore(sc, add(sload(sc), and(mload(mc), mask)))
for {
sc := add(sc, 1)
mc := add(mc, 0x20)
} lt(mc, end) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
sstore(sc, mload(mc))
}
mask := exp(0x100, sub(mc, end))
sstore(sc, mul(div(mload(mc), mask), mask))
}
}
}
function slice(
bytes memory _bytes,
uint256 _start,
uint256 _length
)
internal
pure
returns (bytes memory)
{
require(_length + 31 >= _length, "slice_overflow");
require(_bytes.length >= _start + _length, "slice_outOfBounds");
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
tempBytes := mload(0x40)
let lengthmod := and(_length, 31)
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
mstore(0x40, and(add(mc, 31), not(31)))
}
default {
tempBytes := mload(0x40)
mstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
address tempAddress;
assembly {
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
}
return tempAddress;
}
function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
uint8 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x1), _start))
}
return tempUint;
}
function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
uint16 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x2), _start))
}
return tempUint;
}
function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
uint32 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x4), _start))
}
return tempUint;
}
function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
uint64 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x8), _start))
}
return tempUint;
}
function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
uint96 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0xc), _start))
}
return tempUint;
}
function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
uint128 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x10), _start))
}
return tempUint;
}
function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
uint256 tempUint;
assembly {
tempUint := mload(add(add(_bytes, 0x20), _start))
}
return tempUint;
}
function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
bytes32 tempBytes32;
assembly {
tempBytes32 := mload(add(add(_bytes, 0x20), _start))
}
return tempBytes32;
}
function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
bool success = true;
assembly {
let length := mload(_preBytes)
switch eq(length, mload(_postBytes))
case 1 {
let cb := 1
let mc := add(_preBytes, 0x20)
let end := add(mc, length)
for {
let cc := add(_postBytes, 0x20)
} eq(add(lt(mc, end), cb), 2) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
if iszero(eq(mload(mc), mload(cc))) {
success := 0
cb := 0
}
}
}
default {
success := 0
}
}
return success;
}
function equalStorage(
bytes storage _preBytes,
bytes memory _postBytes
)
internal
view
returns (bool)
{
bool success = true;
assembly {
let fslot := sload(_preBytes.slot)
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
let mlength := mload(_postBytes)
switch eq(slength, mlength)
case 1 {
if iszero(iszero(slength)) {
switch lt(slength, 32)
case 1 {
fslot := mul(div(fslot, 0x100), 0x100)
if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
success := 0
}
}
default {
let cb := 1
mstore(0x0, _preBytes.slot)
let sc := keccak256(0x0, 0x20)
let mc := add(_postBytes, 0x20)
let end := add(mc, mlength)
for {} eq(add(lt(mc, end), cb), 2) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
if iszero(eq(sload(sc), mload(mc))) {
success := 0
cb := 0
}
}
}
}
}
default {
success := 0
}
}
return success;
}
}
文件 3 的 16:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
文件 4 的 16:ITokenBridge.sol
pragma solidity ^0.8.17;
import "./IWETH.sol";
import "./IWormhole.sol";
interface ITokenBridge {
struct Transfer {
uint8 payloadID;
uint256 amount;
bytes32 tokenAddress;
uint16 tokenChain;
bytes32 to;
uint16 toChain;
uint256 fee;
}
struct TransferWithPayload {
uint8 payloadID;
uint256 amount;
bytes32 tokenAddress;
uint16 tokenChain;
bytes32 to;
uint16 toChain;
bytes32 fromAddress;
bytes payload;
}
struct AssetMeta {
uint8 payloadID;
bytes32 tokenAddress;
uint16 tokenChain;
uint8 decimals;
bytes32 symbol;
bytes32 name;
}
struct RegisterChain {
bytes32 module;
uint8 action;
uint16 chainId;
uint16 emitterChainID;
bytes32 emitterAddress;
}
struct UpgradeContract {
bytes32 module;
uint8 action;
uint16 chainId;
bytes32 newContract;
}
struct RecoverChainId {
bytes32 module;
uint8 action;
uint256 evmChainId;
uint16 newChainId;
}
event ContractUpgraded(address indexed oldContract, address indexed newContract);
function _parseTransferCommon(bytes memory encoded) external pure returns (Transfer memory transfer);
function attestToken(address tokenAddress, uint32 nonce) external payable returns (uint64 sequence);
function wrapAndTransferETH(uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence);
function wrapAndTransferETHWithPayload(uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence);
function transferTokens(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence);
function transferTokensWithPayload(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence);
function updateWrapped(bytes memory encodedVm) external returns (address token);
function createWrapped(bytes memory encodedVm) external returns (address token);
function completeTransferWithPayload(bytes memory encodedVm) external returns (bytes memory);
function completeTransferAndUnwrapETHWithPayload(bytes memory encodedVm) external returns (bytes memory);
function completeTransfer(bytes memory encodedVm) external;
function completeTransferAndUnwrapETH(bytes memory encodedVm) external;
function encodeAssetMeta(AssetMeta memory meta) external pure returns (bytes memory encoded);
function encodeTransfer(Transfer memory transfer) external pure returns (bytes memory encoded);
function encodeTransferWithPayload(TransferWithPayload memory transfer) external pure returns (bytes memory encoded);
function parsePayloadID(bytes memory encoded) external pure returns (uint8 payloadID);
function parseAssetMeta(bytes memory encoded) external pure returns (AssetMeta memory meta);
function parseTransfer(bytes memory encoded) external pure returns (Transfer memory transfer);
function parseTransferWithPayload(bytes memory encoded) external pure returns (TransferWithPayload memory transfer);
function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
function isInitialized(address impl) external view returns (bool);
function isTransferCompleted(bytes32 hash) external view returns (bool);
function wormhole() external view returns (IWormhole);
function chainId() external view returns (uint16);
function evmChainId() external view returns (uint256);
function isFork() external view returns (bool);
function governanceChainId() external view returns (uint16);
function governanceContract() external view returns (bytes32);
function wrappedAsset(uint16 tokenChainId, bytes32 tokenAddress) external view returns (address);
function bridgeContracts(uint16 chainId_) external view returns (bytes32);
function tokenImplementation() external view returns (address);
function WETH() external view returns (IWETH);
function outstandingBridged(address token) external view returns (uint256);
function isWrappedAsset(address token) external view returns (bool);
function finality() external view returns (uint8);
function implementation() external view returns (address);
function initialize() external;
function registerChain(bytes memory encodedVM) external;
function upgrade(bytes memory encodedVM) external;
function submitRecoverChainId(bytes memory encodedVM) external;
function parseRegisterChain(bytes memory encoded) external pure returns (RegisterChain memory chain);
function parseUpgrade(bytes memory encoded) external pure returns (UpgradeContract memory chain);
function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci);
}
文件 5 的 16:IWETH.sol
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint amount) external;
function balanceOf() external returns (uint256);
}
文件 6 的 16:IWormhole.sol
pragma solidity ^0.8.17;
interface IWormhole {
struct GuardianSet {
address[] keys;
uint32 expirationTime;
}
struct Signature {
bytes32 r;
bytes32 s;
uint8 v;
uint8 guardianIndex;
}
struct VM {
uint8 version;
uint32 timestamp;
uint32 nonce;
uint16 emitterChainId;
bytes32 emitterAddress;
uint64 sequence;
uint8 consistencyLevel;
bytes payload;
uint32 guardianSetIndex;
Signature[] signatures;
bytes32 hash;
}
event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
function publishMessage(
uint32 nonce,
bytes memory payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason);
function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);
function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);
function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);
function getGuardianSet(uint32 index) external view returns (GuardianSet memory);
function getCurrentGuardianSetIndex() external view returns (uint32);
function getGuardianSetExpiry() external view returns (uint32);
function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
function isInitialized(address impl) external view returns (bool);
function chainId() external view returns (uint16);
function governanceChainId() external view returns (uint16);
function governanceContract() external view returns (bytes32);
function messageFee() external view returns (uint256);
function evmChainId() external view returns (uint256);
function nextSequence(address emitter) external view returns (uint64);
}
文件 7 的 16:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
}
文件 8 的 16:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 9 的 16:TokenBridgeRelayer.sol
pragma solidity ^0.8.17;
import {IWormhole} from "../interfaces/IWormhole.sol";
import {ITokenBridge} from "../interfaces/ITokenBridge.sol";
import "../libraries/BytesLib.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokenBridgeRelayerGovernance.sol";
import "./TokenBridgeRelayerMessages.sol";
contract TokenBridgeRelayer is TokenBridgeRelayerGovernance, TokenBridgeRelayerMessages, ReentrancyGuard {
using BytesLib for bytes;
string public constant VERSION = "0.2.0";
constructor(
address tokenBridge_,
address wethAddress,
address feeRecipient_,
address ownerAssistant_,
bool unwrapWeth_
) {
require(tokenBridge_ != address(0), "invalid token bridge address");
require(wethAddress != address(0), "invalid weth address");
require(feeRecipient_ != address(0), "invalid fee recipient");
require(ownerAssistant_ != address(0), "invalid owner assistant");
setOwner(msg.sender);
setFeeRecipient(feeRecipient_);
setTokenBridge(tokenBridge_);
setWethAddress(wethAddress);
setOwnerAssistant(ownerAssistant_);
setUnwrapWethFlag(unwrapWeth_);
ITokenBridge bridge = ITokenBridge(tokenBridge_);
setChainId(bridge.chainId());
setWormhole(address(bridge.wormhole()));
setSwapRatePrecision(1e8);
setRelayerFeePrecision(1e8);
}
event TransferRedeemed(
uint16 indexed emitterChainId,
bytes32 indexed emitterAddress,
uint64 indexed sequence
);
event SwapExecuted(
address indexed recipient,
address indexed relayer,
address indexed token,
uint256 tokenAmount,
uint256 nativeAmount
);
function transferTokensWithRelay(
address token,
uint256 amount,
uint256 toNativeTokenAmount,
uint16 targetChain,
bytes32 targetRecipient,
uint32 batchId
) public payable nonReentrant notPaused returns (uint64 messageSequence) {
uint256 wormholeFee = wormhole().messageFee();
require(msg.value == wormholeFee, "insufficient value");
uint8 tokenDecimals = getDecimals(token);
amount = denormalizeAmount(
normalizeAmount(amount, tokenDecimals),
tokenDecimals
);
amount = custodyTokens(token, amount);
messageSequence = _transferTokensWithRelay(
InternalTransferParams({
token: token,
amount: amount,
tokenDecimals: tokenDecimals,
toNativeTokenAmount: toNativeTokenAmount,
targetChain: targetChain,
targetRecipient: targetRecipient
}),
batchId,
wormholeFee
);
}
function wrapAndTransferEthWithRelay(
uint256 toNativeTokenAmount,
uint16 targetChain,
bytes32 targetRecipient,
uint32 batchId
) public payable notPaused returns (uint64 messageSequence) {
require(unwrapWeth(), "WETH functionality not supported");
uint256 wormholeFee = wormhole().messageFee();
require(msg.value > wormholeFee, "insufficient value");
uint256 amount = msg.value - wormholeFee;
uint256 dust = amount - denormalizeAmount(normalizeAmount(amount, 18), 18);
if (dust > 0) {
payable(msg.sender).transfer(dust);
}
uint256 amountLessDust = amount - dust;
IWETH weth = WETH();
weth.deposit{
value : amountLessDust
}();
messageSequence = _transferTokensWithRelay(
InternalTransferParams({
token: address(weth),
tokenDecimals: 18,
amount: amountLessDust,
toNativeTokenAmount: toNativeTokenAmount,
targetChain: targetChain,
targetRecipient: targetRecipient
}),
batchId,
wormholeFee
);
}
function _transferTokensWithRelay(
InternalTransferParams memory params,
uint32 batchId,
uint256 wormholeFee
) internal returns (uint64 messageSequence) {
require(isAcceptedToken(params.token), "token not accepted");
require(
params.targetRecipient != bytes32(0),
"targetRecipient cannot be bytes32(0)"
);
uint256 normalizedAmount = normalizeAmount(
params.amount,
params.tokenDecimals
);
require(normalizedAmount > 0, "normalized amount must be > 0");
uint256 normalizedToNativeTokenAmount = normalizeAmount(
params.toNativeTokenAmount,
params.tokenDecimals
);
require(
params.toNativeTokenAmount == 0 || normalizedToNativeTokenAmount > 0,
"invalid toNativeTokenAmount"
);
bytes32 targetContract = getRegisteredContract(params.targetChain);
require(targetContract != bytes32(0), "target not registered");
uint256 normalizedRelayerFee = normalizeAmount(
calculateRelayerFee(
params.targetChain,
params.token,
params.tokenDecimals
),
params.tokenDecimals
);
require(
normalizedAmount > normalizedRelayerFee + normalizedToNativeTokenAmount,
"insufficient amount"
);
bytes memory messagePayload = encodeTransferWithRelay(
TransferWithRelay({
payloadId: 1,
targetRelayerFee: normalizedRelayerFee,
toNativeTokenAmount: normalizedToNativeTokenAmount,
targetRecipient: params.targetRecipient
})
);
ITokenBridge bridge = tokenBridge();
SafeERC20.safeApprove(
IERC20(params.token),
address(bridge),
params.amount
);
messageSequence = bridge.transferTokensWithPayload{value: wormholeFee}(
params.token,
params.amount,
params.targetChain,
targetContract,
batchId,
messagePayload
);
}
function completeTransferWithRelay(bytes calldata encodedTransferMessage) public payable {
(bytes memory payload, uint256 amount, address token) =
_completeTransfer(encodedTransferMessage);
TransferWithRelay memory transferWithRelay = decodeTransferWithRelay(
payload
);
address recipient = bytes32ToAddress(transferWithRelay.targetRecipient);
bool unwrapWeth = unwrapWeth();
if (msg.sender == recipient) {
_completeSelfRedemption(
token,
recipient,
amount,
unwrapWeth
);
return;
}
uint8 tokenDecimals = getDecimals(token);
transferWithRelay.targetRelayerFee = denormalizeAmount(
transferWithRelay.targetRelayerFee,
tokenDecimals
);
if (token == address(WETH())) {
_completeWethTransfer(
amount,
recipient,
transferWithRelay.targetRelayerFee,
unwrapWeth
);
return;
}
if (transferWithRelay.toNativeTokenAmount > 0) {
transferWithRelay.toNativeTokenAmount = denormalizeAmount(
transferWithRelay.toNativeTokenAmount,
tokenDecimals
);
uint256 maxToNativeAllowed = calculateMaxSwapAmountIn(token);
if (transferWithRelay.toNativeTokenAmount > maxToNativeAllowed) {
transferWithRelay.toNativeTokenAmount = maxToNativeAllowed;
}
uint256 nativeAmountForRecipient = calculateNativeSwapAmountOut(
token,
transferWithRelay.toNativeTokenAmount
);
if (nativeAmountForRecipient > 0) {
require(
msg.value >= nativeAmountForRecipient,
"insufficient native asset amount"
);
uint256 relayerRefund = msg.value - nativeAmountForRecipient;
if (relayerRefund > 0) {
payable(msg.sender).transfer(relayerRefund);
}
payable(recipient).transfer(nativeAmountForRecipient);
emit SwapExecuted(
recipient,
msg.sender,
token,
transferWithRelay.toNativeTokenAmount,
nativeAmountForRecipient
);
} else {
transferWithRelay.toNativeTokenAmount = 0;
if (msg.value > 0) {
payable(msg.sender).transfer(msg.value);
}
}
}
uint256 amountForRelayer =
transferWithRelay.targetRelayerFee + transferWithRelay.toNativeTokenAmount;
if (amountForRelayer > 0) {
SafeERC20.safeTransfer(
IERC20(token),
feeRecipient(),
amountForRelayer
);
}
SafeERC20.safeTransfer(
IERC20(token),
recipient,
amount - amountForRelayer
);
}
function _completeTransfer(
bytes memory encodedTransferMessage
) internal returns (bytes memory, uint256, address) {
IWormhole.VM memory parsedMessage = wormhole().parseVM(
encodedTransferMessage
);
address localTokenAddress = fetchLocalAddressFromTransferMessage(
parsedMessage.payload
);
require(isAcceptedToken(localTokenAddress), "token not registered");
uint256 balanceBefore = getBalance(localTokenAddress);
ITokenBridge bridge = tokenBridge();
bytes memory transferPayload = bridge.completeTransferWithPayload(
encodedTransferMessage
);
uint256 amountReceived = getBalance(localTokenAddress) - balanceBefore;
ITokenBridge.TransferWithPayload memory transfer =
bridge.parseTransferWithPayload(transferPayload);
require(
transfer.fromAddress == getRegisteredContract(parsedMessage.emitterChainId),
"contract not registered"
);
emit TransferRedeemed(
parsedMessage.emitterChainId,
parsedMessage.emitterAddress,
parsedMessage.sequence
);
return (
transfer.payload,
amountReceived,
localTokenAddress
);
}
function _completeSelfRedemption(
address token,
address recipient,
uint256 amount,
bool unwrapWeth
) internal {
require(msg.value == 0, "recipient cannot swap native assets");
IWETH weth = WETH();
if (token == address(weth) && unwrapWeth) {
weth.withdraw(amount);
payable(recipient).transfer(amount);
} else {
SafeERC20.safeTransfer(
IERC20(token),
recipient,
amount
);
}
}
function _completeWethTransfer(
uint256 amount,
address recipient,
uint256 relayerFee,
bool unwrapWeth
) internal {
require(msg.value == 0, "value must be zero");
if (unwrapWeth) {
WETH().withdraw(amount);
payable(recipient).transfer(amount - relayerFee);
if (relayerFee > 0) {
payable(feeRecipient()).transfer(relayerFee);
}
} else {
IWETH weth = WETH();
SafeERC20.safeTransfer(
IERC20(address(weth)),
recipient,
amount - relayerFee
);
if (relayerFee > 0) {
SafeERC20.safeTransfer(
IERC20(address(weth)),
feeRecipient(),
relayerFee
);
}
}
}
function fetchLocalAddressFromTransferMessage(
bytes memory payload
) public view returns (address localAddress) {
bytes32 sourceAddress = payload.toBytes32(33);
uint16 tokenChain = payload.toUint16(65);
if (tokenChain != chainId()) {
localAddress = tokenBridge().wrappedAsset(tokenChain, sourceAddress);
require(localAddress != address(0), "token not attested");
} else {
localAddress = bytes32ToAddress(sourceAddress);
}
}
function calculateMaxSwapAmountIn(
address token
) public view returns (uint256 maxAllowed) {
uint8 tokenDecimals = getDecimals(token);
uint8 nativeDecimals = getDecimals(address(WETH()));
if (tokenDecimals > nativeDecimals) {
maxAllowed =
maxNativeSwapAmount(token) * nativeSwapRate(token) *
10 ** (tokenDecimals - nativeDecimals) / swapRatePrecision();
} else {
maxAllowed =
(maxNativeSwapAmount(token) * nativeSwapRate(token)) /
(10 ** (nativeDecimals - tokenDecimals) * swapRatePrecision());
}
}
function calculateNativeSwapAmountOut(
address token,
uint256 toNativeAmount
) public view returns (uint256 nativeAmount) {
uint8 tokenDecimals = getDecimals(token);
uint8 nativeDecimals = getDecimals(address(WETH()));
if (tokenDecimals > nativeDecimals) {
nativeAmount =
swapRatePrecision() * toNativeAmount /
(nativeSwapRate(token) * 10 ** (tokenDecimals - nativeDecimals));
} else {
nativeAmount =
swapRatePrecision() * toNativeAmount *
10 ** (nativeDecimals - tokenDecimals) /
nativeSwapRate(token);
}
}
function calculateRelayerFee(
uint16 targetChainId,
address token,
uint8 decimals
) public view returns (uint256 feeInTokenDenomination) {
uint256 tokenSwapRate = swapRate(token);
require(tokenSwapRate != 0, "swap rate not set");
feeInTokenDenomination =
10 ** decimals * relayerFee(targetChainId) * swapRatePrecision() /
(tokenSwapRate * relayerFeePrecision());
}
function custodyTokens(
address token,
uint256 amount
) internal returns (uint256) {
uint256 balanceBefore = getBalance(token);
SafeERC20.safeTransferFrom(
IERC20(token),
msg.sender,
address(this),
amount
);
return getBalance(token) - balanceBefore;
}
function bytes32ToAddress(bytes32 address_) internal pure returns (address) {
require(bytes12(address_) == 0, "invalid EVM address");
return address(uint160(uint256(address_)));
}
receive() external payable {}
}
文件 10 的 16:TokenBridgeRelayerGetters.sol
pragma solidity ^0.8.17;
import {IWormhole} from "../interfaces/IWormhole.sol";
import {ITokenBridge} from "../interfaces/ITokenBridge.sol";
import {IWETH} from "../interfaces/IWETH.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./TokenBridgeRelayerSetters.sol";
abstract contract TokenBridgeRelayerGetters is TokenBridgeRelayerSetters {
function owner() public view returns (address) {
return _state.owner;
}
function pendingOwner() public view returns (address) {
return _state.pendingOwner;
}
function ownerAssistant() public view returns (address) {
return _state.ownerAssistant;
}
function feeRecipient() public view returns (address) {
return _state.feeRecipient;
}
function wormhole() public view returns (IWormhole) {
return IWormhole(_state.wormhole);
}
function tokenBridge() public view returns (ITokenBridge) {
return ITokenBridge(payable(_state.tokenBridge));
}
function WETH() public view returns (IWETH) {
return IWETH(_state.wethAddress);
}
function unwrapWeth() public view returns (bool) {
return _state.unwrapWeth;
}
function chainId() public view returns (uint16) {
return _state.chainId;
}
function getPaused() public view returns (bool) {
return _state.paused;
}
function getRegisteredContract(uint16 emitterChainId) public view returns (bytes32) {
return _state.registeredContracts[emitterChainId];
}
function swapRatePrecision() public view returns (uint256) {
return _state.swapRatePrecision;
}
function isAcceptedToken(address token) public view returns (bool) {
return _state.acceptedTokens[token];
}
function getAcceptedTokensList() public view returns (address[] memory) {
return _state.acceptedTokensList;
}
function relayerFeePrecision() public view returns (uint256) {
return _state.relayerFeePrecision;
}
function relayerFee(uint16 chainId_) public view returns (uint256) {
return _state.relayerFees[chainId_];
}
function maxNativeSwapAmount(address token) public view returns (uint256) {
return _state.maxNativeSwapAmount[token];
}
function swapRate(address token) public view returns (uint256) {
return _state.swapRates[token];
}
function nativeSwapRate(address token) public view returns (uint256) {
uint256 nativeSwapRate_ = swapRate(_state.wethAddress);
uint256 tokenSwapRate = swapRate(token);
require(
nativeSwapRate_ > 0 && tokenSwapRate > 0,
"swap rate not set"
);
return swapRatePrecision() * nativeSwapRate_ / tokenSwapRate;
}
function normalizeAmount(
uint256 amount,
uint8 decimals
) public pure returns (uint256) {
if (decimals > 8) {
amount /= 10 ** (decimals - 8);
}
return amount;
}
function denormalizeAmount(
uint256 amount,
uint8 decimals
) public pure returns (uint256) {
if (decimals > 8) {
amount *= 10 ** (decimals - 8);
}
return amount;
}
function getDecimals(address token) internal view returns (uint8) {
(,bytes memory queriedDecimals) = token.staticcall(
abi.encodeWithSignature("decimals()")
);
return abi.decode(queriedDecimals, (uint8));
}
function getBalance(address token) internal view returns (uint256 balance) {
(, bytes memory queriedBalance) =
token.staticcall(
abi.encodeWithSelector(IERC20.balanceOf.selector, address(this))
);
balance = abi.decode(queriedBalance, (uint256));
}
}
文件 11 的 16:TokenBridgeRelayerGovernance.sol
pragma solidity ^0.8.17;
import {IWormhole} from "../interfaces/IWormhole.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokenBridgeRelayerGetters.sol";
import "./TokenBridgeRelayerStructs.sol";
abstract contract TokenBridgeRelayerGovernance is TokenBridgeRelayerGetters {
event OwnershipTransfered(address indexed oldOwner, address indexed newOwner);
event FeeRecipientUpdated(address indexed oldRecipient, address indexed newRecipient);
event SwapRateUpdated(TokenBridgeRelayerStructs.SwapRateUpdate[] indexed swapRates);
function submitOwnershipTransferRequest(
uint16 chainId_,
address newOwner
) public onlyOwner onlyCurrentChain(chainId_) {
require(newOwner != address(0), "newOwner cannot equal address(0)");
setPendingOwner(newOwner);
}
function cancelOwnershipTransferRequest(
uint16 chainId_
) public onlyOwner onlyCurrentChain(chainId_) {
setPendingOwner(address(0));
}
function confirmOwnershipTransferRequest() public {
address newOwner = pendingOwner();
require(msg.sender == newOwner, "caller must be pendingOwner");
address currentOwner = owner();
setOwner(newOwner);
setPendingOwner(address(0));
emit OwnershipTransfered(currentOwner, newOwner);
}
function updateOwnerAssistant(
uint16 chainId_,
address newAssistant
) public onlyOwner onlyCurrentChain(chainId_) {
require(
newAssistant != address(0),
"newAssistant cannot equal address(0)"
);
setOwnerAssistant(newAssistant);
}
function updateFeeRecipient(
uint16 chainId_,
address newFeeRecipient
) public onlyOwner onlyCurrentChain(chainId_) {
require(
newFeeRecipient != address(0),
"newFeeRecipient cannot equal address(0)"
);
address currentFeeRecipient = feeRecipient();
setFeeRecipient(newFeeRecipient);
emit FeeRecipientUpdated(currentFeeRecipient, newFeeRecipient);
}
function updateUnwrapWethFlag(
uint16 chainId_,
bool unwrapWeth_
) public onlyOwner onlyCurrentChain(chainId_) {
setUnwrapWethFlag(unwrapWeth_);
}
function registerContract(
uint16 chainId_,
bytes32 contractAddress
) public onlyOwner {
require(
contractAddress != bytes32(0),
"contractAddress cannot equal bytes32(0)"
);
require(
chainId_ != 0 && chainId_ != chainId(),
"chainId_ cannot equal 0 or this chainId"
);
_registerContract(chainId_, contractAddress);
}
function registerToken(
uint16 chainId_,
address token
) public onlyOwner onlyCurrentChain(chainId_) {
require(token != address(0), "invalid token");
addAcceptedToken(token);
}
function deregisterToken(
uint16 chainId_,
address token
) public onlyOwner onlyCurrentChain(chainId_) {
require(token != address(0), "invalid token");
removeAcceptedToken(token);
}
function updateRelayerFee(
uint16 chainId_,
uint256 amount
) public onlyOwnerOrAssistant {
require(chainId_ != chainId(), "invalid chain");
require(
getRegisteredContract(chainId_) != bytes32(0),
"contract doesn't exist"
);
setRelayerFee(chainId_, amount);
}
function updateRelayerFeePrecision(
uint16 chainId_,
uint256 relayerFeePrecision_
) public onlyOwner onlyCurrentChain(chainId_) {
require(relayerFeePrecision_ > 0, "precision must be > 0");
setRelayerFeePrecision(relayerFeePrecision_);
}
function updateSwapRate(
uint16 chainId_,
TokenBridgeRelayerStructs.SwapRateUpdate[] calldata swapRateUpdate
) public onlyOwnerOrAssistant onlyCurrentChain(chainId_) {
uint256 numTokens = swapRateUpdate.length;
require(numTokens > 0, "invalid array size");
for (uint256 i = 0; i < numTokens;) {
require(
isAcceptedToken(swapRateUpdate[i].token),
"token not accepted"
);
require(
swapRateUpdate[i].value > 0,
"swap rate must be nonzero"
);
setSwapRate(swapRateUpdate[i].token, swapRateUpdate[i].value);
unchecked { i += 1; }
}
emit SwapRateUpdated(swapRateUpdate);
}
function updateSwapRatePrecision(
uint16 chainId_,
uint256 swapRatePrecision_
) public onlyOwner onlyCurrentChain(chainId_) {
require(swapRatePrecision_ > 0, "precision must be > 0");
setSwapRatePrecision(swapRatePrecision_);
}
function updateMaxNativeSwapAmount(
uint16 chainId_,
address token,
uint256 maxAmount
) public onlyOwner onlyCurrentChain(chainId_) {
require(isAcceptedToken(token), "token not accepted");
setMaxNativeSwapAmount(token, maxAmount);
}
function setPauseForTransfers(
uint16 chainId_,
bool paused
) public onlyOwner onlyCurrentChain(chainId_) {
setPaused(paused);
}
modifier onlyOwner() {
require(owner() == msg.sender, "caller not the owner");
_;
}
modifier onlyOwnerOrAssistant() {
require(
owner() == msg.sender ||
ownerAssistant() == msg.sender,
"caller not the owner or assistant"
);
_;
}
modifier onlyCurrentChain(uint16 chainId_) {
require(chainId() == chainId_, "wrong chain");
_;
}
modifier notPaused() {
require(!getPaused(), "relayer is paused");
_;
}
}
文件 12 的 16:TokenBridgeRelayerMessages.sol
pragma solidity ^0.8.17;
import "../libraries/BytesLib.sol";
import "./TokenBridgeRelayerStructs.sol";
abstract contract TokenBridgeRelayerMessages is TokenBridgeRelayerStructs {
using BytesLib for bytes;
function encodeTransferWithRelay(
TransferWithRelay memory transfer
) public pure returns (bytes memory encoded) {
require(transfer.payloadId == 1, "invalid payloadId");
encoded = abi.encodePacked(
transfer.payloadId,
transfer.targetRelayerFee,
transfer.toNativeTokenAmount,
transfer.targetRecipient
);
}
function decodeTransferWithRelay(
bytes memory encoded
) public pure returns (TransferWithRelay memory transfer) {
uint256 index = 0;
transfer.payloadId = encoded.toUint8(index);
index += 1;
require(transfer.payloadId == 1, "invalid payloadId");
transfer.targetRelayerFee = encoded.toUint256(index);
index += 32;
transfer.toNativeTokenAmount = encoded.toUint256(index);
index += 32;
transfer.targetRecipient = encoded.toBytes32(index);
index += 32;
require(index == encoded.length, "invalid message length");
}
}
文件 13 的 16:TokenBridgeRelayerSetters.sol
pragma solidity ^0.8.17;
import "./TokenBridgeRelayerState.sol";
abstract contract TokenBridgeRelayerSetters is TokenBridgeRelayerState {
function setOwner(address owner_) internal {
_state.owner = owner_;
}
function setPendingOwner(address pendingOwner_) internal {
_state.pendingOwner = pendingOwner_;
}
function setOwnerAssistant(address ownerAssistant_) internal {
_state.ownerAssistant = ownerAssistant_;
}
function setFeeRecipient(address feeRecipient_) internal {
_state.feeRecipient = feeRecipient_;
}
function setWormhole(address wormhole_) internal {
_state.wormhole = payable(wormhole_);
}
function setTokenBridge(address tokenBridge_) internal {
_state.tokenBridge = payable(tokenBridge_);
}
function setUnwrapWethFlag(bool unwrapWeth_) internal {
_state.unwrapWeth = unwrapWeth_;
}
function setWethAddress(address weth_) internal {
_state.wethAddress = weth_;
}
function setChainId(uint16 chainId_) internal {
_state.chainId = chainId_;
}
function setPaused(bool paused) internal {
_state.paused = paused;
}
function _registerContract(uint16 chainId_, bytes32 contract_) internal {
_state.registeredContracts[chainId_] = contract_;
}
function setSwapRatePrecision(uint256 precision) internal {
_state.swapRatePrecision = precision;
}
function setRelayerFeePrecision(uint256 precision) internal {
_state.relayerFeePrecision = precision;
}
function addAcceptedToken(address token) internal {
require(
_state.acceptedTokens[token] == false,
"token already registered"
);
_state.acceptedTokens[token] = true;
_state.acceptedTokensList.push(token);
}
function removeAcceptedToken(address token) internal {
require(
_state.acceptedTokens[token],
"token not registered"
);
_state.acceptedTokens[token] = false;
_state.swapRates[token] = 0;
_state.maxNativeSwapAmount[token] = 0;
uint256 length_ = _state.acceptedTokensList.length;
uint256 i = 0;
for (; i < length_;) {
if (_state.acceptedTokensList[i] == token) {
break;
}
unchecked { i += 1; }
}
if (i != length_) {
if (length_ > 1) {
_state.acceptedTokensList[i] = _state.acceptedTokensList[length_ - 1];
}
_state.acceptedTokensList.pop();
}
}
function setRelayerFee(uint16 chainId_, uint256 fee) internal {
_state.relayerFees[chainId_] = fee;
}
function setSwapRate(address token, uint256 swapRate) internal {
_state.swapRates[token] = swapRate;
}
function setMaxNativeSwapAmount(address token, uint256 maximum) internal {
_state.maxNativeSwapAmount[token] = maximum;
}
}
文件 14 的 16:TokenBridgeRelayerState.sol
pragma solidity ^0.8.17;
import {IWormhole} from "../interfaces/IWormhole.sol";
abstract contract TokenBridgeRelayerStorage {
struct State {
uint16 chainId;
bool unwrapWeth;
bool paused;
address wethAddress;
address owner;
address ownerAssistant;
address feeRecipient;
address pendingOwner;
address wormhole;
address tokenBridge;
uint256 swapRatePrecision;
uint256 relayerFeePrecision;
mapping(uint16 => bytes32) registeredContracts;
mapping(address => uint256) swapRates;
mapping(address => uint256) maxNativeSwapAmount;
mapping(uint16 => uint256) relayerFees;
mapping(address => bool) acceptedTokens;
address[] acceptedTokensList;
}
}
abstract contract TokenBridgeRelayerState {
TokenBridgeRelayerStorage.State _state;
}
文件 15 的 16:TokenBridgeRelayerStructs.sol
pragma solidity ^0.8.17;
abstract contract TokenBridgeRelayerStructs {
struct TransferWithRelay {
uint8 payloadId;
uint256 targetRelayerFee;
uint256 toNativeTokenAmount;
bytes32 targetRecipient;
}
struct InternalTransferParams {
address token;
uint8 tokenDecimals;
uint256 amount;
uint256 toNativeTokenAmount;
uint16 targetChain;
bytes32 targetRecipient;
}
struct SwapRateUpdate {
address token;
uint256 value;
}
}
文件 16 的 16:draft-IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
{
"compilationTarget": {
"src/token-bridge-relayer/TokenBridgeRelayer.sol": "TokenBridgeRelayer"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=node_modules/@openzeppelin/",
":@solidity-parser/=node_modules/@solidity-parser/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":wormhole-solidity/=modules/src/"
]
}
[{"inputs":[{"internalType":"address","name":"tokenBridge_","type":"address"},{"internalType":"address","name":"wethAddress","type":"address"},{"internalType":"address","name":"feeRecipient_","type":"address"},{"internalType":"address","name":"ownerAssistant_","type":"address"},{"internalType":"bool","name":"unwrapWeth_","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newRecipient","type":"address"}],"name":"FeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"relayer","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nativeAmount","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"indexed":true,"internalType":"struct TokenBridgeRelayerStructs.SwapRateUpdate[]","name":"swapRates","type":"tuple[]"}],"name":"SwapRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"emitterChainId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"indexed":true,"internalType":"uint64","name":"sequence","type":"uint64"}],"name":"TransferRedeemed","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"calculateMaxSwapAmountIn","outputs":[{"internalType":"uint256","name":"maxAllowed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"toNativeAmount","type":"uint256"}],"name":"calculateNativeSwapAmountOut","outputs":[{"internalType":"uint256","name":"nativeAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChainId","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"calculateRelayerFee","outputs":[{"internalType":"uint256","name":"feeInTokenDenomination","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"}],"name":"cancelOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedTransferMessage","type":"bytes"}],"name":"completeTransferWithRelay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"confirmOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"name":"decodeTransferWithRelay","outputs":[{"components":[{"internalType":"uint8","name":"payloadId","type":"uint8"},{"internalType":"uint256","name":"targetRelayerFee","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"}],"internalType":"struct TokenBridgeRelayerStructs.TransferWithRelay","name":"transfer","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"denormalizeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"}],"name":"deregisterToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"payloadId","type":"uint8"},{"internalType":"uint256","name":"targetRelayerFee","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"}],"internalType":"struct TokenBridgeRelayerStructs.TransferWithRelay","name":"transfer","type":"tuple"}],"name":"encodeTransferWithRelay","outputs":[{"internalType":"bytes","name":"encoded","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"fetchLocalAddressFromTransferMessage","outputs":[{"internalType":"address","name":"localAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAcceptedTokensList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"emitterChainId","type":"uint16"}],"name":"getRegisteredContract","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isAcceptedToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxNativeSwapAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"nativeSwapRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerAssistant","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"bytes32","name":"contractAddress","type":"bytes32"}],"name":"registerContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"}],"name":"registerToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"}],"name":"relayerFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"relayerFeePrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"bool","name":"paused","type":"bool"}],"name":"setPauseForTransfers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"submitOwnershipTransferRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"swapRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapRatePrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenBridge","outputs":[{"internalType":"contract ITokenBridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"},{"internalType":"uint32","name":"batchId","type":"uint32"}],"name":"transferTokensWithRelay","outputs":[{"internalType":"uint64","name":"messageSequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"unwrapWeth","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"updateFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"updateMaxNativeSwapAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"address","name":"newAssistant","type":"address"}],"name":"updateOwnerAssistant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"updateRelayerFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"uint256","name":"relayerFeePrecision_","type":"uint256"}],"name":"updateRelayerFeePrecision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct TokenBridgeRelayerStructs.SwapRateUpdate[]","name":"swapRateUpdate","type":"tuple[]"}],"name":"updateSwapRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"uint256","name":"swapRatePrecision_","type":"uint256"}],"name":"updateSwapRatePrecision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId_","type":"uint16"},{"internalType":"bool","name":"unwrapWeth_","type":"bool"}],"name":"updateUnwrapWethFlag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wormhole","outputs":[{"internalType":"contract IWormhole","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"toNativeTokenAmount","type":"uint256"},{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"bytes32","name":"targetRecipient","type":"bytes32"},{"internalType":"uint32","name":"batchId","type":"uint32"}],"name":"wrapAndTransferEthWithRelay","outputs":[{"internalType":"uint64","name":"messageSequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]