Source Code
Latest 25 from a total of 2,070 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Repay Native | 127000791 | 469 days ago | IN | 0.0706280263592 ETH | 0.000000442891 | ||||
| Withdraw Collate... | 126996711 | 469 days ago | IN | 0.000847492637934 ETH | 0.000000210553 | ||||
| Deposit Collater... | 126980475 | 469 days ago | IN | 0.0003866063592 ETH | 0.000000369734 | ||||
| Repay Native | 126957606 | 470 days ago | IN | 0.0065059363592 ETH | 0.000000085964 | ||||
| Repay Native | 126953845 | 470 days ago | IN | 0.0078866063592 ETH | 0.000000099628 | ||||
| Repay Native | 126952659 | 470 days ago | IN | 0.0053866063592 ETH | 0.000000109226 | ||||
| Repay Native | 126944900 | 470 days ago | IN | 0.0103866063592 ETH | 0.000000189053 | ||||
| Repay | 126938226 | 470 days ago | IN | 0.0003866063592 ETH | 0.000000375138 | ||||
| Deposit Collater... | 126916245 | 471 days ago | IN | 0.0003866063592 ETH | 0.000000176759 | ||||
| Repay | 126910024 | 471 days ago | IN | 0.0003866063592 ETH | 0.000000117417 | ||||
| Deposit Collater... | 126894727 | 471 days ago | IN | 0.0003866063592 ETH | 0.000000369185 | ||||
| Withdraw Collate... | 126887805 | 471 days ago | IN | 0.000847492637934 ETH | 0.000000077149 | ||||
| Deposit Collater... | 126886868 | 471 days ago | IN | 0.0003866063592 ETH | 0.000000103721 | ||||
| Repay | 126880171 | 471 days ago | IN | 0.0003866063592 ETH | 0.000000423071 | ||||
| Repay | 126873403 | 472 days ago | IN | 0.0003866063592 ETH | 0.000000436244 | ||||
| Withdraw Collate... | 126872612 | 472 days ago | IN | 0.000847492637934 ETH | 0.000000254536 | ||||
| Repay | 126825612 | 473 days ago | IN | 0.0003866063592 ETH | 0.000000474071 | ||||
| Deposit Collater... | 126754841 | 474 days ago | IN | 0.0003866063592 ETH | 0.000004328597 | ||||
| Borrow | 126730945 | 475 days ago | IN | 0.000847492637934 ETH | 0.000000256356 | ||||
| Deposit Collater... | 126727861 | 475 days ago | IN | 0.0003866063592 ETH | 0.000000483974 | ||||
| Deposit Collater... | 126722876 | 475 days ago | IN | 0.0003866063592 ETH | 0.000007777231 | ||||
| Deposit Collater... | 126716848 | 475 days ago | IN | 0.0003866063592 ETH | 0.000001429482 | ||||
| Deposit Collater... | 126715848 | 475 days ago | IN | 0.0003866063592 ETH | 0.000003167523 | ||||
| Deposit Collater... | 126632005 | 477 days ago | IN | 0.000407669076 ETH | 0.000000164032 | ||||
| Withdraw Collate... | 126562671 | 479 days ago | IN | 0.000942557187027 ETH | 0.00000014607 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 127000791 | 469 days ago | 0.0003866063592 ETH | ||||
| 127000791 | 469 days ago | 0.07024142 ETH | ||||
| 126996711 | 469 days ago | 0.000847492637934 ETH | ||||
| 126980475 | 469 days ago | 0.0003866063592 ETH | ||||
| 126957606 | 470 days ago | 0.0003866063592 ETH | ||||
| 126957606 | 470 days ago | 0.00611933 ETH | ||||
| 126953845 | 470 days ago | 0.0003866063592 ETH | ||||
| 126953845 | 470 days ago | 0.0075 ETH | ||||
| 126952659 | 470 days ago | 0.0003866063592 ETH | ||||
| 126952659 | 470 days ago | 0.005 ETH | ||||
| 126944900 | 470 days ago | 0.0003866063592 ETH | ||||
| 126944900 | 470 days ago | 0.01 ETH | ||||
| 126938226 | 470 days ago | 0.0003866063592 ETH | ||||
| 126916245 | 471 days ago | 0.0003866063592 ETH | ||||
| 126910024 | 471 days ago | 0.0003866063592 ETH | ||||
| 126894727 | 471 days ago | 0.0003866063592 ETH | ||||
| 126887805 | 471 days ago | 0.000847492637934 ETH | ||||
| 126886868 | 471 days ago | 0.0003866063592 ETH | ||||
| 126880171 | 471 days ago | 0.0003866063592 ETH | ||||
| 126873403 | 472 days ago | 0.0003866063592 ETH | ||||
| 126872612 | 472 days ago | 0.000847492637934 ETH | ||||
| 126825612 | 473 days ago | 0.0003866063592 ETH | ||||
| 126808991 | 473 days ago | 0.02861644 ETH | ||||
| 126808991 | 473 days ago | 0.02861644 ETH | ||||
| 126754841 | 474 days ago | 0.0003866063592 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Spoke
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 2000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {TokenSender} from "@wormhole-upgradeable/WormholeRelayerSDK.sol";
import {CCTPSender, CCTPBase} from "@wormhole-upgradeable/CCTPBase.sol";
import {VaaKey} from "@wormhole-upgradeable/interfaces/IWormholeRelayer.sol";
import "@wormhole-upgradeable/Utils.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../HubSpokeStructs.sol";
import "./SpokeGetters.sol";
import "./SpokeUtilities.sol";
import "../wormhole/TokenReceiverWithCCTP.sol";
/**
* @title Spoke
* @notice The Spoke contract is the point of entry for cross-chain actions; users initiate an action by calling any of
* the `public payable` functions (ex: `#depositCollateral`, `#withdrawCollateral`) with their desired asset and amount,
* and using Wormhole we send the payload/tokens to the Hub on the target chain; if the action concludes with sending
* tokens back to the user, we receive the final payload/tokens from the Hub before sending tokens to the user. This
* contract also implements wormhole's CCTP contracts to send/receive USDC.
*/
contract Spoke is HubSpokeStructs, SpokeGetters, SpokeUtilities, TokenSender, CCTPSender, TokenReceiverWithCCTP, Ownable {
using SafeERC20 for IERC20;
/**
* @notice Spoke constructor - Initializes a new spoke with given parameters
*
* @param chainId: Chain ID of the chain that this Spoke is deployed on
* @param wormhole: Address of the Wormhole contract on this Spoke chain
* @param tokenBridge: Address of the TokenBridge contract on this Spoke chain
* @param relayer: Address of the WormholeRelayer contract on this Spoke chain
* @param hubChainId: Chain ID of the Hub
* @param hubContractAddress: Contract address of the Hub contract (on the Hub chain)
* @param _circleMessageTransmitter: Cicle Message Transmitter contract (cctp)
* @param _circleTokenMessenger: Cicle Token Messenger contract (cctp)
* @param _USDC: USDC token contract (cctp)
*/
constructor(
uint16 chainId,
address wormhole,
address tokenBridge,
address relayer,
uint16 hubChainId,
address hubContractAddress,
address _circleMessageTransmitter,
address _circleTokenMessenger,
address _USDC
) Ownable(msg.sender) {
CCTPBase.__CCTPBase_init(
relayer,
tokenBridge,
wormhole,
_circleMessageTransmitter,
_circleTokenMessenger,
_USDC
);
_state.chainId = chainId;
_state.hubChainId = hubChainId;
_state.hubContractAddress = hubContractAddress;
_state.defaultGasLimitRoundtrip = 650_000;
_state.isUsingCCTP = _circleMessageTransmitter != address(0); // zero address would indicate not using/supported
setRegisteredSender(hubChainId, toWormholeFormat(hubContractAddress));
// disable the registrationOwner so that the Hub can't be changed.
registrationOwner = address(0);
}
/**
* @notice Allows the contract deployer to set the default gas limit used in wormhole relay quotes
*
* @param value: the new value for `defaultGasLimitRoundtrip`
*/
function setDefaultGasLimitRoundtrip(uint256 value) external onlyOwner {
_state.defaultGasLimitRoundtrip = value;
}
/**
* @notice Allows the contract deployer to toggle whether we are using CCTP for USDC
* NOTE: If `_circleMessageTransmitter` is the null address, it indicates CCTP is not supported on this chain, thus
* we don't do anything.
*
* @param value: the new value for `isUsingCCTP`
*/
function setIsUsingCCTP(bool value) external onlyOwner {
if (address(circleMessageTransmitter) == address(0)) return; // zero address would indicate not using/supported
_state.isUsingCCTP = value;
}
/**
* @notice Allows the caller to initiate a cross-chain deposit. The caller must have approved the `assetAmount` of
* `asset` and must have provided enough `msg.value` to cover the relay
*
* @param asset: Addresss of the asset to deposit
* @param assetAmount: Amount of the asset to deposit
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub (for refunds)
* @return sequence number of the message sent.
*/
function depositCollateral(address asset, uint256 assetAmount, uint256 costForReturnDelivery)
public
payable
returns (uint64 sequence)
{
require(msg.value >= getDeliveryCostRoundtrip(costForReturnDelivery, true), "Insufficient value sent");
sequence = _doAction(Action.Deposit, asset, assetAmount, costForReturnDelivery, false);
}
/**
* @notice Allows the caller to initiate a cross-chain withdraw. The caller must have provided enough `msg.value`
* to cover the relay and the return delivery
*
* @param asset: Addresss of the asset to withdraw
* @param assetAmount: Amount of the asset to withdraw
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub
* @return sequence number of the message sent.
*/
function withdrawCollateral(address asset, uint256 assetAmount, uint256 costForReturnDelivery)
public
payable
returns (uint64 sequence)
{
require(costForReturnDelivery > 0, "Non-zero costForReturnDelivery");
sequence = _doAction(Action.Withdraw, asset, assetAmount, costForReturnDelivery, false);
}
/**
* @notice Allows the caller to initiate a cross-chain borrow. The caller must have provided enough `msg.value`
* to cover the relay and the return delivery
*
* @param asset: Addresss of the asset to borrow
* @param assetAmount: Amount of the asset to borrow
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub
* @return sequence number of the message sent.
*/
function borrow(address asset, uint256 assetAmount, uint256 costForReturnDelivery)
public
payable
returns (uint64 sequence)
{
require(costForReturnDelivery > 0, "Non-zero costForReturnDelivery");
sequence = _doAction(Action.Borrow, asset, assetAmount, costForReturnDelivery, false);
}
/**
* @notice Allows the caller to initiate a cross-chain repay. The caller must have approved the `assetAmount` of
* `asset` and must have provided enough `msg.value` to cover the relay
*
* @param asset: Addresss of the asset to borrow
* @param assetAmount: Amount of the asset to borrow
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub (for refunds)
* @return sequence number of the message sent.
*/
function repay(address asset, uint256 assetAmount, uint256 costForReturnDelivery)
public
payable
returns (uint64 sequence)
{
require(msg.value >= getDeliveryCostRoundtrip(costForReturnDelivery, true), "Insufficient value sent");
sequence = _doAction(Action.Repay, asset, assetAmount, costForReturnDelivery, false);
}
/**
* @notice Allows the caller to initiate a cross-chain deposit with native tokens. The caller must have provided
* enough `msg.value` to cover the relay+return and their desired deposit amount
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub (for refunds)
* enough `msg.value` to cover the relay and their desired deposit amount
* @return sequence number of the message sent.
*/
function depositCollateralNative(uint256 costForReturnDelivery) public payable returns (uint64 sequence) {
uint256 totalCost = getDeliveryCostRoundtrip(costForReturnDelivery, true);
require(msg.value >= totalCost, "Spoke::depositCollateralNative:Insufficient value sent");
uint256 amount = msg.value - totalCost;
sequence = _doAction(Action.DepositNative, address(0), amount, costForReturnDelivery, false);
}
/**
* @notice Allows the caller to initiate a cross-chain repay with native tokens. The caller must have provided
* enough `msg.value` to cover the relay+return and their desired repay amount
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub (for refunds)
* enough `msg.value` to cover the relay and their desired repay amount
* @return sequence number of the message sent.
*/
function repayNative(uint256 costForReturnDelivery) public payable returns (uint64 sequence) {
uint256 totalCost = getDeliveryCostRoundtrip(costForReturnDelivery, true);
require(msg.value >= totalCost, "Spoke::repayNative:Insufficient value sent");
uint256 amount = msg.value - totalCost;
sequence = _doAction(Action.RepayNative, address(0), amount, costForReturnDelivery, false);
}
/**
* @notice Allows the caller to initiate a cross-chain withdraw with native tokens. The caller must have provided
* enough `msg.value` to cover the relay and the return delivery
*
* @param assetAmount: Amount of the asset to withdraw
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub
* @param unwrap: Whether to unwrap the native tokens or not
* @return sequence number of the message sent.
*/
function withdrawCollateralNative(uint256 assetAmount, uint256 costForReturnDelivery, bool unwrap)
public
payable
returns (uint64 sequence)
{
sequence = _doAction(Action.Withdraw, address(tokenBridge.WETH()), assetAmount, costForReturnDelivery, unwrap);
}
/**
* @notice Allows the caller to initiate a cross-chain borrow with native tokens. The caller must have provided
* enough `msg.value` to cover the relay and the return delivery
*
* @param assetAmount: Amount of the asset to borrow
* @param costForReturnDelivery: The quoted cost for return delivery from the Hub
* @param unwrap: Whether to unwrap the native tokens or not
* @return sequence number of the message sent.
*/
function borrowNative(uint256 assetAmount, uint256 costForReturnDelivery, bool unwrap)
public
payable
returns (uint64 sequence)
{
sequence = _doAction(Action.Borrow, address(tokenBridge.WETH()), assetAmount, costForReturnDelivery, unwrap);
}
/**
* @notice Get the quote for the wormhole delivery cost, accounting for a forward() call on the Hub (in case of potential
* reverts or to receive tokens on borrow/withdraw)
*
* @param costForReturnDelivery: the result of Hub#getCostForReturnDelivery()
* @param withTokenTransfer: whether to include the message fee for a token bridge transfer (on deposit or repay)
* @return cost for the forward() call on the Hub
*/
function getDeliveryCostRoundtrip(uint256 costForReturnDelivery, bool withTokenTransfer)
public
view
returns (uint256)
{
(uint256 cost,) =
wormholeRelayer.quoteEVMDeliveryPrice(hubChainId(), costForReturnDelivery, defaultGasLimitRoundtrip());
if (withTokenTransfer) {
return cost + tokenBridge.wormhole().messageFee();
}
return cost;
}
/**
* @dev Initiates an action (deposit, borrow, withdraw, or repay) on the spoke by sending
* a Wormhole message (potentially a TokenBridge message with tokens) to the Hub
* @param action - the action to be performed. It can be Deposit, Borrow, Withdraw, Repay, DepositNative, RepayNative.
* @param asset - the address of the relevant asset. For native tokens like ETH, AVAX, this will be the zero address.
* @param assetAmount - the amount of the asset to be involved in the action.
* @param costForReturnDelivery - the cost to forward tokens back from the Hub
* @param unwrap - a boolean value indicating whether to unwrap the asset or not.
* @return sequence number of the message sent.
*/
function _doAction(Action action, address asset, uint256 assetAmount, uint256 costForReturnDelivery, bool unwrap)
internal
returns (uint64 sequence)
{
require(assetAmount > 0, "No zero asset amount");
bool withCCTP = asset == USDC && _state.isUsingCCTP;
// for token transfers, only validate amount if we're using the token bridge
if (asset != address(0) && !withCCTP) {
requireAssetAmountValidForTokenBridge(asset, assetAmount);
}
Action hubAction = action;
if (action == Action.DepositNative) {
hubAction = Action.Deposit;
} else if (action == Action.RepayNative) {
hubAction = Action.Repay;
}
bool sendingTokens = action == Action.Deposit || action == Action.Repay;
bool sendingUSDC = withCCTP && sendingTokens;
bool receivingUSDC = withCCTP && !sendingTokens;
bytes memory userPayload = abi.encode(uint8(hubAction), msg.sender, asset, unwrap, sendingUSDC, receivingUSDC);
bytes memory payload = sendingUSDC
? userPayload
: abi.encode(assetAmount, userPayload); // encoding again so it's the same format as cctp messages
if (sendingTokens) {
sequence = withCCTP
? _sendUSDCWithPayload(payload, assetAmount, costForReturnDelivery)
: _sendTokenBridgeMessage(payload, asset, assetAmount, costForReturnDelivery);
} else if (action == Action.Withdraw || action == Action.Borrow) {
sequence = wormholeRelayer.sendPayloadToEvm{value: msg.value}(
hubChainId(),
hubContractAddress(),
payload,
costForReturnDelivery,
defaultGasLimitRoundtrip(),
chainId(), // refundChain
msg.sender // refundAddress
);
} else if (action == Action.DepositNative || action == Action.RepayNative) {
sequence = _sendTokenBridgeMessageNative(payload, assetAmount, costForReturnDelivery);
}
}
/**
* @dev Sends EVM Worlhole Relayer a message with the given payload and value
* @param value - amount of ETH to be sent to relayer
* @param payload - the payload to be sent
* @param costForReturnDelivery - the cost to forward tokens back from the Hub
* @param refundAddress The address on `refundChain` to deliver any refund to
* @return sequence - the sequence number of the message sent
*/
function _sendToEvmWormHoleRelayer(
uint256 value,
bytes memory payload,
uint256 costForReturnDelivery,
address refundAddress
) internal returns(uint64 sequence) {
return wormholeRelayer.sendPayloadToEvm{value: value}(
hubChainId(),
hubContractAddress(),
payload,
costForReturnDelivery,
defaultGasLimitRoundtrip(),
hubChainId(),
refundAddress
);
}
/**
* @dev Sends a TokenBridge message with the given payload, asset, and amount
* @param payload - the payload to be sent
* @param asset - the address of the asset to be sent
* @param amount - the amount of the asset to be sent
* @param costForReturnDelivery - the cost to forward tokens back from the Hub
* @return sequence - the sequence number of the message sent
*/
function _sendTokenBridgeMessage(bytes memory payload, address asset, uint256 amount, uint256 costForReturnDelivery)
internal
returns (uint64)
{
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
return sendTokenWithPayloadToEvm(
hubChainId(),
hubContractAddress(),
payload,
costForReturnDelivery,
defaultGasLimitRoundtrip(),
asset,
amount,
chainId(), // refundChain
msg.sender // refundAddress
);
}
/**
* @dev Sends a TokenBridge message with the given payload and amount in native tokens
* @param payload - the payload to be sent
* @param amount - the amount of native tokens to be sent
* @param costForReturnDelivery - the cost to forward tokens back from the Hub
* @return sequence - the sequence number of the message sent
*/
function _sendTokenBridgeMessageNative(bytes memory payload, uint256 amount, uint256 costForReturnDelivery)
internal
returns (uint64)
{
uint256 amountPlusFee = amount + tokenBridge.wormhole().messageFee();
uint64 sequence = tokenBridge.wrapAndTransferETHWithPayload{value: amountPlusFee}(
hubChainId(), toWormholeFormat(hubContractAddress()), 0, payload
);
VaaKey[] memory additionalVaas = new VaaKey[](1);
additionalVaas[0] = VaaKey(chainId(), toWormholeFormat(address(tokenBridge)), sequence);
uint256 deliveryCost = getDeliveryCostRoundtrip(costForReturnDelivery, false);
return wormholeRelayer.sendVaasToEvm{value: deliveryCost}(
hubChainId(),
hubContractAddress(),
payload,
costForReturnDelivery,
defaultGasLimitRoundtrip(),
additionalVaas,
chainId(), // refundChain
msg.sender // refundAddress
);
}
/**
* @dev Sends USDC with the given payload via wormhole's CCTP integration
* @param payload - the payload to be sent
* @param amount - the amount of the asset to be sent
* @param costForReturnDelivery - the cost to forward tokens back from the Hub
* @return sequence - the sequence number of the message sent
*/
function _sendUSDCWithPayload(bytes memory payload, uint256 amount, uint256 costForReturnDelivery)
internal
returns (uint64)
{
IERC20(USDC).safeTransferFrom(msg.sender, address(this), amount);
return sendUSDCWithPayloadToEvm(
hubChainId(),
hubContractAddress(),
payload,
costForReturnDelivery,
defaultGasLimitRoundtrip(),
amount,
chainId(), // refundChain
msg.sender // refundAddress
);
}
/**
* @dev Returns whether we are using CCTP while receiving wormhole messages, as specified in the encoded `payload`
* @param payload - the payload received
*/
function messageWithCCTP(bytes memory payload) internal pure override returns (bool) {
(,, bool withCCTP) = _decodePayload(payload);
// NOTE: we are not checking _state.isUsingCCTP here in order to handle it as best effort
return withCCTP;
}
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalVaas,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) public virtual override(TokenReceiverWithCCTP) payable {
if (additionalVaas.length == 0) {
(address recipient,,) = _decodePayload(payload);
// send any refund back to the recipient
if (msg.value > 0) {
(bool refundSuccess,) = recipient.call{value: msg.value}("");
require(refundSuccess, "refund failed");
}
} else {
super.receiveWormholeMessages(payload, additionalVaas, sourceAddress, sourceChain, deliveryHash);
}
}
/**
* @dev Receives a payload and tokens, and processes them
* @param payload - the payload received
* @param receivedTokens - the tokens received
* @param sourceAddress - the source address of the tokens
* @param sourceChain - the source chain of the tokens
* @param deliveryHash - the delivery hash of the tokens
*/
function receivePayloadAndTokens(
bytes memory payload,
TokenReceived[] memory receivedTokens,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
)
internal
override
onlyWormholeRelayer
isRegisteredSender(sourceChain, sourceAddress)
replayProtect(deliveryHash)
{
require(receivedTokens.length == 1, "Expecting one transfer");
TokenReceived memory receivedToken = receivedTokens[0];
(address recipient, bool unwrap,) = _decodePayload(payload);
if (unwrap) {
// unwrap and transfer to recipient
tokenBridge.WETH().withdraw(receivedToken.amount);
(bool withdrawSuccess,) = recipient.call{value: receivedToken.amount}("");
require(withdrawSuccess, "withdraw to native failed");
} else {
IERC20(receivedToken.tokenAddress).safeTransfer(recipient, receivedToken.amount);
}
// send any refund back to the recipient
if (msg.value > 0) {
(bool refundSuccess,) = recipient.call{value: msg.value}("");
require(refundSuccess, "refund failed");
}
}
/**
* @dev Receives a payload and USDC via CCTP
* @param userPayload - the payload received
* @param amountUSDCReceived - the amount of USDC received
* @param sourceAddress - the source address of the tokens
* @param sourceChain - the source chain of the tokens
* @param deliveryHash - the delivery hash of the tokens
*/
function receivePayloadAndUSDC(
bytes memory userPayload,
uint256 amountUSDCReceived,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
)
internal
override
onlyWormholeRelayer
isRegisteredSender(sourceChain, sourceAddress)
replayProtect(deliveryHash)
{
(address recipient,,) = abi.decode(userPayload, (address, bool, bool));
IERC20(USDC).safeTransfer(recipient, amountUSDCReceived);
// send any refund back to the recipient
if (msg.value > 0) {
(bool refundSuccess,) = recipient.call{value: msg.value}("");
require(refundSuccess, "refund failed");
}
}
/**
* @dev Decodes `payload` into expected arguments
* @param payload - the payload received
*/
function _decodePayload(
bytes memory payload
) internal pure returns (address recipient, bool unwrap, bool withCCTP) {
(, bytes memory userPayload) = abi.decode(payload, (uint256, bytes));
(recipient, unwrap, withCCTP) = abi.decode(userPayload, (address, bool, bool));
}
/**
* @notice fallback function to receive unwrapped native asset
*/
fallback() external payable {}
/**
* @notice Function to receive ETH
*/
receive() external payable {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.13;
import "./interfaces/IWormholeReceiver.sol";
import "./interfaces/IWormholeRelayer.sol";
import "./interfaces/ITokenBridge.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import "./interfaces/CCTPInterfaces/ITokenMessenger.sol";
import "./interfaces/CCTPInterfaces/IMessageTransmitter.sol";
import "./Utils.sol";
import {TokenBase} from "./WormholeRelayerSDK.sol";
library CCTPMessageLib {
uint8 constant CCTP_KEY_TYPE = 2;
// encoded using abi.encodePacked(domain, nonce)
struct CCTPKey {
uint32 domain;
uint64 nonce;
}
// encoded using abi.encode(message, signature)
struct CCTPMessage {
bytes message;
bytes signature;
}
}
abstract contract CCTPBase is TokenBase {
ITokenMessenger public circleTokenMessenger;
IMessageTransmitter public circleMessageTransmitter;
address public USDC;
function __CCTPBase_init(
address _wormholeRelayer,
address _tokenBridge,
address _wormhole,
address _circleMessageTransmitter,
address _circleTokenMessenger,
address _USDC
) public {
require(!_wormholeRelayerInitialized, "WRI");
TokenBase.__TokenBase_init(_wormholeRelayer, _tokenBridge, _wormhole);
circleTokenMessenger = ITokenMessenger(_circleTokenMessenger);
circleMessageTransmitter = IMessageTransmitter(_circleMessageTransmitter);
USDC = _USDC;
}
function getCCTPDomain(uint16 chain) internal pure returns (uint32) {
if (chain == 2) {
return 0;
} else if (chain == 6) {
return 1;
} else if (chain == 23) {
return 3;
} else if (chain == 24) {
return 2;
} else {
revert("Wrong CCTP Domain");
}
}
function redeemUSDC(bytes memory cctpMessage) internal returns (uint256 amount) {
(bytes memory message, bytes memory signature) = abi.decode(cctpMessage, (bytes, bytes));
uint256 beforeBalance = IERC20(USDC).balanceOf(address(this));
circleMessageTransmitter.receiveMessage(message, signature);
return IERC20(USDC).balanceOf(address(this)) - beforeBalance;
}
}
abstract contract CCTPSender is CCTPBase {
uint8 internal constant CONSISTENCY_LEVEL_FINALIZED = 15;
using CCTPMessageLib for *;
/**
* transferTokens wraps common boilerplate for sending tokens to another chain using IWormholeRelayer
* - approves tokenBridge to spend 'amount' of 'token'
* - emits token transfer VAA
* - returns VAA key for inclusion in WormholeRelayer `additionalVaas` argument
*
* Note: this requires that only the targetAddress can redeem transfers.
*
*/
function transferUSDC(uint256 amount, uint16 targetChain, address targetAddress)
internal
returns (MessageKey memory)
{
IERC20(USDC).approve(address(circleTokenMessenger), amount);
uint64 nonce = circleTokenMessenger.depositForBurnWithCaller(
amount,
getCCTPDomain(targetChain),
addressToBytes32CCTP(targetAddress),
USDC,
addressToBytes32CCTP(targetAddress)
);
return MessageKey(
CCTPMessageLib.CCTP_KEY_TYPE, abi.encodePacked(getCCTPDomain(wormhole.chainId()), nonce)
);
}
function sendUSDCWithPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
uint256 amount,
uint16 refundChain,
address refundAddress
) internal returns (uint64 sequence) {
MessageKey[] memory messageKeys = new MessageKey[](1);
messageKeys[0] = transferUSDC(amount, targetChain, targetAddress);
address defaultDeliveryProvider = wormholeRelayer.getDefaultDeliveryProvider();
(uint256 cost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit);
sequence = wormholeRelayer.sendToEvm{value: cost}(
targetChain,
targetAddress,
abi.encode(amount, payload),
receiverValue,
0,
gasLimit,
refundChain,
refundAddress,
defaultDeliveryProvider,
messageKeys,
CONSISTENCY_LEVEL_FINALIZED
);
}
function addressToBytes32CCTP(address addr) private pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}
}
abstract contract CCTPReceiver is CCTPBase {
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalMessages,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) external virtual payable {
_receiveWormholeMessagesWithCCTP(payload, additionalMessages, sourceAddress, sourceChain, deliveryHash);
}
function _receiveWormholeMessagesWithCCTP(
bytes memory payload,
bytes[] memory additionalMessages,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) internal {
require(additionalMessages.length <= 1, "CCTP: At most one Message is supported");
uint256 amountUSDCReceived;
if (additionalMessages.length == 1) {
amountUSDCReceived = redeemUSDC(additionalMessages[0]);
}
(uint256 amount, bytes memory userPayload) = abi.decode(payload, (uint256, bytes));
// Check that the correct amount was received
// It is important to verify that the 'USDC' received is
require(amount == amountUSDCReceived, "Wrong amount received");
receivePayloadAndUSDC(userPayload, amountUSDCReceived, sourceAddress, sourceChain, deliveryHash);
}
function receivePayloadAndUSDC(
bytes memory payload,
uint256 amountUSDCReceived,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) internal virtual {}
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
import "./IRelayer.sol";
import "./IReceiver.sol";
/**
* @title IMessageTransmitter
* @notice Interface for message transmitters, which both relay and receive messages.
*/
interface IMessageTransmitter is IRelayer, IReceiver {
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
/**
* @title IReceiver
* @notice Receives messages on destination chain and forwards them to IMessageDestinationHandler
*/
interface IReceiver {
/**
* @notice Receives an incoming message, validating the header and passing
* the body to application-specific handler.
* @param message The message raw bytes
* @param signature The message signature
* @return success bool, true if successful
*/
function receiveMessage(bytes calldata message, bytes calldata signature)
external
returns (bool success);
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
/**
* @title IRelayer
* @notice Sends messages from source domain to destination domain
*/
interface IRelayer {
/**
* @notice Sends an outgoing message from the source domain.
* @dev Increment nonce, format the message, and emit `MessageSent` event with message information.
* @param destinationDomain Domain of destination chain
* @param recipient Address of message recipient on destination domain as bytes32
* @param messageBody Raw bytes content of message
* @return nonce reserved by message
*/
function sendMessage(
uint32 destinationDomain,
bytes32 recipient,
bytes calldata messageBody
) external returns (uint64);
/**
* @notice Sends an outgoing message from the source domain, with a specified caller on the
* destination domain.
* @dev Increment nonce, format the message, and emit `MessageSent` event with message information.
* WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible
* to broadcast the message on the destination domain. This is an advanced feature, and the standard
* sendMessage() should be preferred for use cases where a specific destination caller is not required.
* @param destinationDomain Domain of destination chain
* @param recipient Address of message recipient on destination domain as bytes32
* @param destinationCaller caller on the destination domain, as bytes32
* @param messageBody Raw bytes content of message
* @return nonce reserved by message
*/
function sendMessageWithCaller(
uint32 destinationDomain,
bytes32 recipient,
bytes32 destinationCaller,
bytes calldata messageBody
) external returns (uint64);
/**
* @notice Replace a message with a new message body and/or destination caller.
* @dev The `originalAttestation` must be a valid attestation of `originalMessage`.
* @param originalMessage original message to replace
* @param originalAttestation attestation of `originalMessage`
* @param newMessageBody new message body of replaced message
* @param newDestinationCaller the new destination caller
*/
function replaceMessage(
bytes calldata originalMessage,
bytes calldata originalAttestation,
bytes calldata newMessageBody,
bytes32 newDestinationCaller
) external;
}pragma solidity ^0.8.0;
interface ITokenMessenger {
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain. The mint
* on the destination domain must be called by `destinationCaller`.
* WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible
* to broadcast the message on the destination domain. This is an advanced feature, and the standard
* depositForBurn() should be preferred for use cases where a specific destination caller is not required.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given destinationCaller is zero address
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - MessageTransmitter returns false or reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken address of contract to burn deposited tokens, on local domain
* @param destinationCaller caller on the destination domain, as bytes32
* @return nonce unique nonce reserved by message
*/
function depositForBurnWithCaller(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller
) external returns (uint64 nonce);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// contracts/Bridge.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
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);
}// contracts/Bridge.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}// contracts/Messages.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
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;
}
struct ContractUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
address newContract;
}
struct GuardianSetUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
GuardianSet newGuardianSet;
uint32 newGuardianSetIndex;
}
struct SetMessageFee {
bytes32 module;
uint8 action;
uint16 chain;
uint256 messageFee;
}
struct TransferFees {
bytes32 module;
uint8 action;
uint16 chain;
uint256 amount;
bytes32 recipient;
}
struct RecoverChainId {
bytes32 module;
uint8 action;
uint256 evmChainId;
uint16 newChainId;
}
event LogMessagePublished(
address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel
);
event ContractUpgraded(address indexed oldContract, address indexed newContract);
event GuardianSetAdded(uint32 indexed index);
function publishMessage(uint32 nonce, bytes memory payload, uint8 consistencyLevel)
external
payable
returns (uint64 sequence);
function initialize() external;
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 quorum(uint256 numGuardians) external pure returns (uint256 numSignaturesRequiredForQuorum);
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 isFork() external view returns (bool);
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);
function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu);
function parseGuardianSetUpgrade(bytes memory encodedUpgrade)
external
pure
returns (GuardianSetUpgrade memory gsu);
function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf);
function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf);
function parseRecoverChainId(bytes memory encodedRecoverChainId)
external
pure
returns (RecoverChainId memory rci);
function submitContractUpgrade(bytes memory _vm) external;
function submitSetMessageFee(bytes memory _vm) external;
function submitNewGuardianSet(bytes memory _vm) external;
function submitTransferFees(bytes memory _vm) external;
function submitRecoverChainId(bytes memory _vm) external;
}// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
/**
* @notice Interface for a contract which can receive Wormhole messages.
*/
interface IWormholeReceiver {
/**
* @notice When a `send` is performed with this contract as the target, this function will be
* invoked by the WormholeRelayer contract
*
* NOTE: This function should be restricted such that only the Wormhole Relayer contract can call it.
*
* We also recommend that this function:
* - Stores all received `deliveryHash`s in a mapping `(bytes32 => bool)`, and
* on every call, checks that deliveryHash has not already been stored in the
* map (This is to prevent other users maliciously trying to relay the same message)
* - Checks that `sourceChain` and `sourceAddress` are indeed who
* you expect to have requested the calling of `send` on the source chain
*
* The invocation of this function corresponding to the `send` request will have msg.value equal
* to the receiverValue specified in the send request.
*
* If the invocation of this function reverts or exceeds the gas limit
* specified by the send requester, this delivery will result in a `ReceiverFailure`.
*
* @param payload - an arbitrary message which was included in the delivery by the
* requester.
* @param additionalVaas - Additional VAAs which were requested to be included in this delivery.
* They are guaranteed to all be included and in the same order as was specified in the
* delivery request.
* @param sourceAddress - the (wormhole format) address on the sending chain which requested
* this delivery.
* @param sourceChain - the wormhole chain ID where this delivery was requested.
* @param deliveryHash - the VAA hash of the deliveryVAA.
*
* NOTE: These signedVaas are NOT verified by the Wormhole core contract prior to being provided
* to this call. Always make sure `parseAndVerify()` is called on the Wormhole core contract
* before trusting the content of a raw VAA, otherwise the VAA may be invalid or malicious.
*/
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalVaas,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) external payable;
}// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
/**
* @title WormholeRelayer
* @author
* @notice This project allows developers to build cross-chain applications powered by Wormhole without needing to
* write and run their own relaying infrastructure
*
* We implement the IWormholeRelayer interface that allows users to request a delivery provider to relay a payload (and/or additional VAAs)
* to a chain and address of their choice.
*/
/**
* @notice VaaKey identifies a wormhole message
*
* @custom:member chainId Wormhole chain ID of the chain where this VAA was emitted from
* @custom:member emitterAddress Address of the emitter of the VAA, in Wormhole bytes32 format
* @custom:member sequence Sequence number of the VAA
*/
struct VaaKey {
uint16 chainId;
bytes32 emitterAddress;
uint64 sequence;
}
// 0-127 are reserved for standardized KeyTypes, 128-255 are for custom use
uint8 constant VAA_KEY_TYPE = 1;
struct MessageKey {
uint8 keyType; // 0-127 are reserved for standardized KeyTypes, 128-255 are for custom use
bytes encodedKey;
}
interface IWormholeRelayerBase {
event SendEvent(
uint64 indexed sequence, uint256 deliveryQuote, uint256 paymentForExtraReceiverValue
);
function getRegisteredWormholeRelayerContract(uint16 chainId) external view returns (bytes32);
/**
* @notice Returns true if a delivery has been attempted for the given deliveryHash
* Note: invalid deliveries where the tx reverts are not considered attempted
*/
function deliveryAttempted(bytes32 deliveryHash) external view returns (bool attempted);
/**
* @notice block number at which a delivery was successfully executed
*/
function deliverySuccessBlock(bytes32 deliveryHash) external view returns (uint256 blockNumber);
/**
* @notice block number of the latest attempt to execute a delivery that failed
*/
function deliveryFailureBlock(bytes32 deliveryHash) external view returns (uint256 blockNumber);
}
/**
* @title IWormholeRelayerSend
* @notice The interface to request deliveries
*/
interface IWormholeRelayerSend is IWormholeRelayerBase {
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* Any refunds (from leftover gas) will be paid to the delivery provider. In order to receive the refunds, use the `sendPayloadToEvm` function
* with `refundChain` and `refundAddress` as parameters
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* Any refunds (from leftover gas) will be paid to the delivery provider. In order to receive the refunds, use the `sendVaasToEvm` function
* with `refundChain` and `refundAddress` as parameters
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendVaasToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
VaaKey[] memory vaaKeys
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the default delivery provider
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to `receiverValue`
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)`
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendVaasToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
VaaKey[] memory vaaKeys,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress,
address deliveryProviderAddress,
VaaKey[] memory vaaKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and external messages specified by `messageKeys` to the address `targetAddress` on chain `targetChain`
* with gas limit `gasLimit` and `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* MessageKeys can specify wormhole messages (VaaKeys) or other types of messages (ex. USDC CCTP attestations). Ensure the selected
* Note: DeliveryProvider supports all the MessageKey.keyType values specified or it will not be delivered!
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver)
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param messageKeys Additional messagess to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function sendToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress,
address deliveryProviderAddress,
MessageKey[] memory messageKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function send(
uint16 targetChain,
bytes32 targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
bytes memory encodedExecutionParameters,
uint16 refundChain,
bytes32 refundAddress,
address deliveryProviderAddress,
VaaKey[] memory vaaKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress`
* to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain`
* with `msg.value` equal to
* receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei.
*
* Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain`
* `targetAddress` must implement the IWormholeReceiver interface
*
* This function must be called with `msg.value` equal to
* quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress) + paymentForExtraReceiverValue
*
* MessageKeys can specify wormhole messages (VaaKeys) or other types of messages (ex. USDC CCTP attestations). Ensure the selected
* Note: DeliveryProvider supports all the MessageKey.keyType values specified or it will not be delivered!
*
* @param targetChain in Wormhole Chain ID format
* @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format
* @param payload arbitrary bytes to pass in as parameter in call to `targetAddress`
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue
* (in addition to the `receiverValue` specified)
* @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format
* @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @param messageKeys Additional messagess to pass in as parameter in call to `targetAddress`
* @param consistencyLevel Consistency level with which to publish the delivery instructions - see
* https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels
* @return sequence sequence number of published VAA containing delivery instructions
*/
function send(
uint16 targetChain,
bytes32 targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
bytes memory encodedExecutionParameters,
uint16 refundChain,
bytes32 refundAddress,
address deliveryProviderAddress,
MessageKey[] memory messageKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
/**
* @notice Requests a previously published delivery instruction to be redelivered
* (e.g. with a different delivery provider)
*
* This function must be called with `msg.value` equal to
* quoteEVMDeliveryPrice(targetChain, newReceiverValue, newGasLimit, newDeliveryProviderAddress)
*
* @notice *** This will only be able to succeed if the following is true **
* - newGasLimit >= gas limit of the old instruction
* - newReceiverValue >= receiver value of the old instruction
* - newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
*
* @param deliveryVaaKey VaaKey identifying the wormhole message containing the
* previously published delivery instructions
* @param targetChain The target chain that the original delivery targeted. Must match targetChain from original delivery instructions
* @param newReceiverValue new msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param newGasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the
* `targetChainRefundPerGasUnused` rate quoted by the delivery provider, to the refund chain and address specified in the original request
* @param newDeliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return sequence sequence number of published VAA containing redelivery instructions
*
* @notice *** This will only be able to succeed if the following is true **
* - newGasLimit >= gas limit of the old instruction
* - newReceiverValue >= receiver value of the old instruction
* - newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
*/
function resendToEvm(
VaaKey memory deliveryVaaKey,
uint16 targetChain,
uint256 newReceiverValue,
uint256 newGasLimit,
address newDeliveryProviderAddress
) external payable returns (uint64 sequence);
/**
* @notice Requests a previously published delivery instruction to be redelivered
*
*
* This function must be called with `msg.value` equal to
* quoteDeliveryPrice(targetChain, newReceiverValue, newEncodedExecutionParameters, newDeliveryProviderAddress)
*
* @param deliveryVaaKey VaaKey identifying the wormhole message containing the
* previously published delivery instructions
* @param targetChain The target chain that the original delivery targeted. Must match targetChain from original delivery instructions
* @param newReceiverValue new msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param newEncodedExecutionParameters new encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param newDeliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return sequence sequence number of published VAA containing redelivery instructions
*
* @notice *** This will only be able to succeed if the following is true **
* - (For EVM_V1) newGasLimit >= gas limit of the old instruction
* - newReceiverValue >= receiver value of the old instruction
* - (For EVM_V1) newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused`
*/
function resend(
VaaKey memory deliveryVaaKey,
uint16 targetChain,
uint256 newReceiverValue,
bytes memory newEncodedExecutionParameters,
address newDeliveryProviderAddress
) external payable returns (uint64 sequence);
/**
* @notice Returns the price to request a relay to chain `targetChain`, using the default delivery provider
*
* @param targetChain in Wormhole Chain ID format
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
* @return targetChainRefundPerGasUnused amount of target chain currency that will be refunded per unit of gas unused,
* if a refundAddress is specified
*/
function quoteEVMDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
uint256 gasLimit
) external view returns (uint256 nativePriceQuote, uint256 targetChainRefundPerGasUnused);
/**
* @notice Returns the price to request a relay to chain `targetChain`, using delivery provider `deliveryProviderAddress`
*
* @param targetChain in Wormhole Chain ID format
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param gasLimit gas limit with which to call `targetAddress`.
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
* @return targetChainRefundPerGasUnused amount of target chain currency that will be refunded per unit of gas unused,
* if a refundAddress is specified
*/
function quoteEVMDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
uint256 gasLimit,
address deliveryProviderAddress
) external view returns (uint256 nativePriceQuote, uint256 targetChainRefundPerGasUnused);
/**
* @notice Returns the price to request a relay to chain `targetChain`, using delivery provider `deliveryProviderAddress`
*
* @param targetChain in Wormhole Chain ID format
* @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units)
* @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress`
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return nativePriceQuote Price, in units of current chain currency, that the delivery provider charges to perform the relay
* @return encodedExecutionInfo encoded information on how the delivery will be executed
* e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` and `targetChainRefundPerGasUnused`
* (which is the amount of target chain currency that will be refunded per unit of gas unused,
* if a refundAddress is specified)
*/
function quoteDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
bytes memory encodedExecutionParameters,
address deliveryProviderAddress
) external view returns (uint256 nativePriceQuote, bytes memory encodedExecutionInfo);
/**
* @notice Returns the (extra) amount of target chain currency that `targetAddress`
* will be called with, if the `paymentForExtraReceiverValue` field is set to `currentChainAmount`
*
* @param targetChain in Wormhole Chain ID format
* @param currentChainAmount The value that `paymentForExtraReceiverValue` will be set to
* @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider
* @return targetChainAmount The amount such that if `targetAddress` will be called with `msg.value` equal to
* receiverValue + targetChainAmount
*/
function quoteNativeForChain(
uint16 targetChain,
uint256 currentChainAmount,
address deliveryProviderAddress
) external view returns (uint256 targetChainAmount);
/**
* @notice Returns the address of the current default delivery provider
* @return deliveryProvider The address of (the default delivery provider)'s contract on this source
* chain. This must be a contract that implements IDeliveryProvider.
*/
function getDefaultDeliveryProvider() external view returns (address deliveryProvider);
}
/**
* @title IWormholeRelayerDelivery
* @notice The interface to execute deliveries. Only relevant for Delivery Providers
*/
interface IWormholeRelayerDelivery is IWormholeRelayerBase {
enum DeliveryStatus {
SUCCESS,
RECEIVER_FAILURE
}
enum RefundStatus {
REFUND_SENT,
REFUND_FAIL,
CROSS_CHAIN_REFUND_SENT,
CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED,
CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH
}
/**
* @custom:member recipientContract - The target contract address
* @custom:member sourceChain - The chain which this delivery was requested from (in wormhole
* ChainID format)
* @custom:member sequence - The wormhole sequence number of the delivery VAA on the source chain
* corresponding to this delivery request
* @custom:member deliveryVaaHash - The hash of the delivery VAA corresponding to this delivery
* request
* @custom:member gasUsed - The amount of gas that was used to call your target contract
* @custom:member status:
* - RECEIVER_FAILURE, if the target contract reverts
* - SUCCESS, if the target contract doesn't revert
* @custom:member additionalStatusInfo:
* - If status is SUCCESS, then this is empty.
* - If status is RECEIVER_FAILURE, this is `RETURNDATA_TRUNCATION_THRESHOLD` bytes of the
* return data (i.e. potentially truncated revert reason information).
* @custom:member refundStatus - Result of the refund. REFUND_SUCCESS or REFUND_FAIL are for
* refunds where targetChain=refundChain; the others are for targetChain!=refundChain,
* where a cross chain refund is necessary
* @custom:member overridesInfo:
* - If not an override: empty bytes array
* - Otherwise: An encoded `DeliveryOverride`
*/
event Delivery(
address indexed recipientContract,
uint16 indexed sourceChain,
uint64 indexed sequence,
bytes32 deliveryVaaHash,
DeliveryStatus status,
uint256 gasUsed,
RefundStatus refundStatus,
bytes additionalStatusInfo,
bytes overridesInfo
);
/**
* @notice The delivery provider calls `deliver` to relay messages as described by one delivery instruction
*
* The delivery provider must pass in the specified (by VaaKeys[]) signed wormhole messages (VAAs) from the source chain
* as well as the signed wormhole message with the delivery instructions (the delivery VAA)
*
* The messages will be relayed to the target address (with the specified gas limit and receiver value) iff the following checks are met:
* - the delivery VAA has a valid signature
* - the delivery VAA's emitter is one of these WormholeRelayer contracts
* - the delivery provider passed in at least enough of this chain's currency as msg.value (enough meaning the maximum possible refund)
* - the instruction's target chain is this chain
* - the relayed signed VAAs match the descriptions in container.messages (the VAA hashes match, or the emitter address, sequence number pair matches, depending on the description given)
*
* @param encodedVMs - An array of signed wormhole messages (all from the same source chain
* transaction)
* @param encodedDeliveryVAA - Signed wormhole message from the source chain's WormholeRelayer
* contract with payload being the encoded delivery instruction container
* @param relayerRefundAddress - The address to which any refunds to the delivery provider
* should be sent
* @param deliveryOverrides - Optional overrides field which must be either an empty bytes array or
* an encoded DeliveryOverride struct
*/
function deliver(
bytes[] memory encodedVMs,
bytes memory encodedDeliveryVAA,
address payable relayerRefundAddress,
bytes memory deliveryOverrides
) external payable;
}
interface IWormholeRelayer is IWormholeRelayerDelivery, IWormholeRelayerSend {}
/*
* Errors thrown by IWormholeRelayer contract
*/
// Bound chosen by the following formula: `memoryWord * 4 + selectorSize`.
// This means that an error identifier plus four fixed size arguments should be available to developers.
// In the case of a `require` revert with error message, this should provide 2 memory word's worth of data.
uint256 constant RETURNDATA_TRUNCATION_THRESHOLD = 132;
//When msg.value was not equal to `delivery provider's quoted delivery price` + `paymentForExtraReceiverValue`
error InvalidMsgValue(uint256 msgValue, uint256 totalFee);
error RequestedGasLimitTooLow();
error DeliveryProviderDoesNotSupportTargetChain(address relayer, uint16 chainId);
error DeliveryProviderCannotReceivePayment();
error DeliveryProviderDoesNotSupportMessageKeyType(uint8 keyType);
//When calling `delivery()` a second time even though a delivery is already in progress
error ReentrantDelivery(address msgSender, address lockedBy);
error InvalidPayloadId(uint8 parsed, uint8 expected);
error InvalidPayloadLength(uint256 received, uint256 expected);
error InvalidVaaKeyType(uint8 parsed);
error TooManyMessageKeys(uint256 numMessageKeys);
error InvalidDeliveryVaa(string reason);
//When the delivery VAA (signed wormhole message with delivery instructions) was not emitted by the
// registered WormholeRelayer contract
error InvalidEmitter(bytes32 emitter, bytes32 registered, uint16 chainId);
error MessageKeysLengthDoesNotMatchMessagesLength(uint256 keys, uint256 vaas);
error VaaKeysDoNotMatchVaas(uint8 index);
//When someone tries to call an external function of the WormholeRelayer that is only intended to be
// called by the WormholeRelayer itself (to allow retroactive reverts for atomicity)
error RequesterNotWormholeRelayer();
//When trying to relay a `DeliveryInstruction` to any other chain but the one it was specified for
error TargetChainIsNotThisChain(uint16 targetChain);
//When a `DeliveryOverride` contains a gas limit that's less than the original
error InvalidOverrideGasLimit();
//When a `DeliveryOverride` contains a receiver value that's less than the original
error InvalidOverrideReceiverValue();
//When a `DeliveryOverride` contains a 'refund per unit of gas unused' that's less than the original
error InvalidOverrideRefundPerGasUnused();
//When the delivery provider doesn't pass in sufficient funds (i.e. msg.value does not cover the
// maximum possible refund to the user)
error InsufficientRelayerFunds(uint256 msgValue, uint256 minimum);
//When a bytes32 field can't be converted into a 20 byte EVM address, because the 12 padding bytes
// are non-zero (duplicated from Utils.sol)
error NotAnEvmAddress(bytes32);// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "./interfaces/IWormholeRelayer.sol";
function toWormholeFormat(address addr) pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}
function fromWormholeFormat(bytes32 whFormatAddress) pure returns (address) {
if (uint256(whFormatAddress) >> 160 != 0) {
revert NotAnEvmAddress(whFormatAddress);
}
return address(uint160(uint256(whFormatAddress)));
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "./interfaces/IWormholeReceiver.sol";
import "./interfaces/IWormholeRelayer.sol";
import "./interfaces/ITokenBridge.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import "./Utils.sol";
abstract contract Base {
IWormholeRelayer public wormholeRelayer;
IWormhole public wormhole;
mapping(bytes32 => bool) public seenDeliveryVaaHashes;
address registrationOwner;
mapping(uint16 => bytes32) registeredSenders;
bool internal _wormholeRelayerInitialized;
function __Base_init(address _wormholeRelayer, address _wormhole) public {
require(!_wormholeRelayerInitialized, "WRI");
_wormholeRelayerInitialized = true;
wormholeRelayer = IWormholeRelayer(_wormholeRelayer);
wormhole = IWormhole(_wormhole);
registrationOwner = msg.sender;
}
modifier onlyWormholeRelayer() {
require(msg.sender == address(wormholeRelayer), "Msg.sender is not Wormhole Relayer");
_;
}
modifier replayProtect(bytes32 deliveryHash) {
require(!seenDeliveryVaaHashes[deliveryHash], "Message already processed");
seenDeliveryVaaHashes[deliveryHash] = true;
_;
}
modifier isRegisteredSender(uint16 sourceChain, bytes32 sourceAddress) {
require(registeredSenders[sourceChain] == sourceAddress, "Not registered sender");
_;
}
/**
* Sets the registered address for 'sourceChain' to 'sourceAddress'
* So that for messages from 'sourceChain', only ones from 'sourceAddress' are valid
*
* Assumes only one sender per chain is valid
* Sender is the address that called 'send' on the Wormhole Relayer contract on the source chain)
*/
function setRegisteredSender(uint16 sourceChain, bytes32 sourceAddress) public {
require(msg.sender == registrationOwner, "Not allowed to set registered sender");
registeredSenders[sourceChain] = sourceAddress;
}
}
abstract contract TokenBase is Base {
ITokenBridge public tokenBridge;
function __TokenBase_init(address _wormholeRelayer, address _tokenBridge, address _wormhole) public {
require(!_wormholeRelayerInitialized, "WRI");
Base.__Base_init(_wormholeRelayer, _wormhole);
tokenBridge = ITokenBridge(_tokenBridge);
}
function getDecimals(address tokenAddress) internal view returns (uint8 decimals) {
// query decimals
(, bytes memory queriedDecimals) = address(tokenAddress).staticcall(abi.encodeWithSignature("decimals()"));
decimals = abi.decode(queriedDecimals, (uint8));
}
function getTokenAddressOnThisChain(uint16 tokenHomeChain, bytes32 tokenHomeAddress)
internal
view
returns (address tokenAddressOnThisChain)
{
return tokenHomeChain == wormhole.chainId()
? fromWormholeFormat(tokenHomeAddress)
: tokenBridge.wrappedAsset(tokenHomeChain, tokenHomeAddress);
}
}
abstract contract TokenSender is TokenBase {
/**
* transferTokens wraps common boilerplate for sending tokens to another chain using IWormholeRelayer
* - approves tokenBridge to spend 'amount' of 'token'
* - emits token transfer VAA
* - returns VAA key for inclusion in WormholeRelayer `additionalVaas` argument
*
* Note: this function uses transferTokensWithPayload instead of transferTokens since the former requires that only the targetAddress
* can redeem transfers. Otherwise it's possible for another address to redeem the transfer before the targetContract is invoked by
* the offchain relayer and the target contract would have to be hardened against this.
*
*/
function transferTokens(address token, uint256 amount, uint16 targetChain, address targetAddress)
internal
returns (VaaKey memory)
{
return transferTokens(token, amount, targetChain, targetAddress, bytes(""));
}
/**
* transferTokens wraps common boilerplate for sending tokens to another chain using IWormholeRelayer.
* A payload can be included in the transfer vaa. By including a payload here instead of the deliveryVaa,
* fewer trust assumptions are placed on the WormholeRelayer contract.
*
* - approves tokenBridge to spend 'amount' of 'token'
* - emits token transfer VAA
* - returns VAA key for inclusion in WormholeRelayer `additionalVaas` argument
*
* Note: this function uses transferTokensWithPayload instead of transferTokens since the former requires that only the targetAddress
* can redeem transfers. Otherwise it's possible for another address to redeem the transfer before the targetContract is invoked by
* the offchain relayer and the target contract would have to be hardened against this.
*/
function transferTokens(
address token,
uint256 amount,
uint16 targetChain,
address targetAddress,
bytes memory payload
) internal returns (VaaKey memory) {
IERC20(token).approve(address(tokenBridge), amount);
uint64 sequence = tokenBridge.transferTokensWithPayload{value: wormhole.messageFee()}(
token, amount, targetChain, toWormholeFormat(targetAddress), 0, payload
);
return VaaKey({
emitterAddress: toWormholeFormat(address(tokenBridge)),
chainId: wormhole.chainId(),
sequence: sequence
});
}
function sendTokenWithPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
address token,
uint256 amount
) internal returns (uint64) {
VaaKey[] memory vaaKeys = new VaaKey[](1);
vaaKeys[0] = transferTokens(token, amount, targetChain, targetAddress);
(uint256 cost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit);
return wormholeRelayer.sendVaasToEvm{value: cost}(
targetChain, targetAddress, payload, receiverValue, gasLimit, vaaKeys
);
}
function sendTokenWithPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
address token,
uint256 amount,
uint16 refundChain,
address refundAddress
) internal returns (uint64) {
VaaKey[] memory vaaKeys = new VaaKey[](1);
vaaKeys[0] = transferTokens(token, amount, targetChain, targetAddress);
(uint256 cost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit);
return wormholeRelayer.sendVaasToEvm{value: cost}(
targetChain, targetAddress, payload, receiverValue, gasLimit, vaaKeys, refundChain, refundAddress
);
}
}
abstract contract TokenReceiver is TokenBase {
struct TokenReceived {
bytes32 tokenHomeAddress;
uint16 tokenHomeChain;
address tokenAddress; // wrapped address if tokenHomeChain !== this chain, else tokenHomeAddress (in evm address format)
uint256 amount;
uint256 amountNormalized; // if decimals > 8, normalized to 8 decimal places
}
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalVaas,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) external virtual payable {
_receiveWormholeMessages(payload, additionalVaas, sourceAddress, sourceChain, deliveryHash);
}
function _receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalVaas,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) internal {
TokenReceived[] memory receivedTokens = new TokenReceived[](additionalVaas.length);
for (uint256 i = 0; i < additionalVaas.length; ++i) {
IWormhole.VM memory parsed = wormhole.parseVM(additionalVaas[i]);
require(
parsed.emitterAddress == tokenBridge.bridgeContracts(parsed.emitterChainId), "Not a Token Bridge VAA"
);
ITokenBridge.TransferWithPayload memory transfer = tokenBridge.parseTransferWithPayload(parsed.payload);
require(
transfer.to == toWormholeFormat(address(this)) && transfer.toChain == wormhole.chainId(),
"Token was not sent to this address"
);
tokenBridge.completeTransferWithPayload(additionalVaas[i]);
address thisChainTokenAddress = getTokenAddressOnThisChain(transfer.tokenChain, transfer.tokenAddress);
uint8 decimals = getDecimals(thisChainTokenAddress);
uint256 denormalizedAmount = transfer.amount;
if (decimals > 8) denormalizedAmount *= uint256(10) ** (decimals - 8);
receivedTokens[i] = TokenReceived({
tokenHomeAddress: transfer.tokenAddress,
tokenHomeChain: transfer.tokenChain,
tokenAddress: thisChainTokenAddress,
amount: denormalizedAmount,
amountNormalized: transfer.amount
});
}
// call into overriden method
receivePayloadAndTokens(payload, receivedTokens, sourceAddress, sourceChain, deliveryHash);
}
function receivePayloadAndTokens(
bytes memory payload,
TokenReceived[] memory receivedTokens,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) internal virtual {}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
/**
* @title HubSpokeStructs
* @notice A set of structs and enums used in the Hub and Spoke contracts
*/
contract HubSpokeStructs {
/**
* @param wormhole: Address of the Wormhole contract
* @param tokenBridge: Address of the TokenBridge contract
* @param wormholeRelayer: Address of the WormholeRelayer contract
* @param consistencyLevel: Desired level of finality the Wormhole guardians will reach before signing the messages
* NOTE: consistencyLevel = 200 will result in an instant message, while all other values will wait for finality
* Recommended finality levels can be found here: https://book.wormhole.com/reference/contracts.html
* @param pythAddress: Address of the Pyth oracle on the Hub chain
* @param priceStandardDeviations: priceStandardDeviations = (psd * priceStandardDeviationsPrecision), where psd is
* the number of standard deviations that we use for our price intervals in calculations relating to allowing
* withdraws, borrows, or liquidations
* @param priceStandardDeviationsPrecision: A precision number that allows us to represent our desired noninteger
* price standard deviation as an integer (psd = priceStandardDeviations/priceStandardDeviationsPrecision)
* @param maxLiquidationPortionPrecision: A precision number that allows us to represent our desired noninteger
* max liquidation portion mlp as an integer (mlp = maxLiquidationPortion/maxLiquidationPortionPrecision)
* @param interestAccrualIndexPrecision: A precision number that allows us to represent our noninteger interest
* accrual indices as integers; we store each index as its true value multiplied by interestAccrualIndexPrecision
* @param collateralizationRatioPrecision: A precision number that allows us to represent our noninteger
* collateralization ratios as integers; we store each ratio as its true value multiplied by
* collateralizationRatioPrecision
* @param liquidationFee: The fee taken by the protocol on liquidation
* @param _circleMessageTransmitter: Cicle Message Transmitter contract (cctp)
* @param _circleTokenMessenger: Cicle Token Messenger contract (cctp)
* @param _USDC: USDC token contract (cctp)
*/
struct ConstructorArgs {
/* Wormhole Information */
address wormhole;
address tokenBridge;
address wormholeRelayer;
uint8 consistencyLevel;
/* Liquidation Information */
uint256 interestAccrualIndexPrecision;
uint256 liquidationFee;
uint256 liquidationFeePrecision;
/* CCTP Information */
address circleMessageTransmitter;
address circleTokenMessenger;
address USDC;
}
struct StoredVaultAmount {
DenormalizedVaultAmount amounts;
AccrualIndices accrualIndices;
}
struct DenormalizedVaultAmount {
uint256 deposited;
uint256 borrowed;
}
struct NotionalVaultAmount {
uint256 deposited;
uint256 borrowed;
}
struct AccrualIndices {
uint256 deposited;
uint256 borrowed;
}
struct AssetInfo {
uint256 collateralizationRatioDeposit;
uint256 collateralizationRatioBorrow;
uint8 decimals;
address interestRateCalculator;
bool exists;
uint256 borrowLimit;
uint256 supplyLimit;
uint256 maxLiquidationPortion;
uint256 maxLiquidationBonus; // 1e6 precision; 130e4 = 130% = 1.3; the liquidator gets 30% over what he repays
}
/**
* @dev Struct to hold the decoded data from a Wormhole payload
* @param action The action to be performed (e.g., Deposit, Borrow, Withdraw, Repay)
* @param sender The address of the sender initiating the action
* @param wrappedAsset The address of the wrapped asset involved in the action
* @param amount The amount of the wrapped asset involved in the action
* @param unwrap A boolean indicating whether to unwrap the asset or not for native withdraws and borrows
*/
struct PayloadData {
Action action;
address sender;
address wrappedAsset;
uint256 amount;
bool unwrap;
}
enum Action {
Deposit,
Borrow,
Withdraw,
Repay,
DepositNative,
RepayNative
}
enum Round {
UP,
DOWN
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "./SpokeState.sol";
/**
* @title SpokeGetters
* @notice A set of public getter functions
*/
contract SpokeGetters is SpokeState {
function chainId() public view returns (uint16) {
return _state.chainId;
}
function consistencyLevel() public view returns (uint8) {
return _state.consistencyLevel;
}
function hubChainId() public view returns (uint16) {
return _state.hubChainId;
}
function hubContractAddress() public view returns (address) {
return _state.hubContractAddress;
}
function defaultGasLimitRoundtrip() public view returns (uint256) {
return _state.defaultGasLimitRoundtrip;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "../HubSpokeStructs.sol";
/**
* @title SpokeStorage
* @notice Contract defining state variables for the Spoke contract
*/
contract SpokeStorage is HubSpokeStructs {
struct State {
uint16 chainId;
// number of confirmations for wormhole messages
uint8 consistencyLevel;
uint16 hubChainId;
address hubContractAddress;
uint256 defaultGasLimitRoundtrip;
bool isUsingCCTP;
// @dev storage gap
uint256[50] ______gap;
}
}
/**
* @title SpokeState
* @notice Contract holding state variable for the Spoke contract
*/
contract SpokeState {
SpokeStorage.State _state;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "../../interfaces/IERC20decimals.sol";
/**
* @title SpokeUtilities
* @notice A set of internal utility functions
*/
contract SpokeUtilities {
/**
* @dev This function checks if the asset amount is valid for the token bridge
* @param assetAddress The address of the asset
* @param assetAmount The amount of the asset
*/
function requireAssetAmountValidForTokenBridge(address assetAddress, uint256 assetAmount) internal view {
uint8 decimals = IERC20decimals(assetAddress).decimals();
require(
deNormalizeAmount(normalizeAmount(assetAmount, decimals), decimals) == assetAmount,
"Too many decimal places"
);
}
/**
* @dev This function normalizes the amount based on the decimals
* @param amount The amount to be normalized
* @param decimals The number of decimals
* @return The normalized amount
*/
function normalizeAmount(uint256 amount, uint8 decimals) internal pure returns (uint256) {
if (decimals > 8) {
amount /= 10 ** (decimals - 8);
} else if (decimals < 8){
amount *= 10 ** (8 - decimals);
}
return amount;
}
/**
* @dev This function denormalizes the amount based on the decimals
* @param amount The amount to be denormalized
* @param decimals The number of decimals
* @return The denormalized amount
*/
function deNormalizeAmount(uint256 amount, uint8 decimals) internal pure returns (uint256) {
if (decimals > 8) {
amount *= 10 ** (decimals - 8);
} else if (decimals < 8) {
amount /= 10 ** (8 - decimals);
}
return amount;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {TokenReceiver} from "@wormhole-upgradeable/WormholeRelayerSDK.sol";
import {CCTPReceiver} from "@wormhole-upgradeable/CCTPBase.sol";
abstract contract TokenReceiverWithCCTP is CCTPReceiver, TokenReceiver {
/**
* @dev Overriding the superclasses' function to choose whether to use CCTP or not, based on the implemented
* `isUsingCCTP` function
* @param payload - the payload received
* @param additionalVaas - any wormhole VAAs received
* @param sourceAddress - the source address of the tokens
* @param sourceChain - the source chain of the tokens
* @param deliveryHash - the delivery hash of the tokens
*/
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory additionalVaas,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) public virtual override(TokenReceiver, CCTPReceiver) payable {
if (messageWithCCTP(payload)) {
_receiveWormholeMessagesWithCCTP(payload, additionalVaas, sourceAddress, sourceChain, deliveryHash);
} else {
_receiveWormholeMessages(payload, additionalVaas, sourceAddress, sourceChain, deliveryHash);
}
}
/**
* @dev Virtual function to decode `payload` and determine if using CCTP or not
* @param payload - the payload received
*/
function messageWithCCTP(bytes memory payload) internal view virtual returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IERC20decimals is IERC20 {
function decimals() external view returns (uint8);
}{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 2000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"address","name":"wormhole","type":"address"},{"internalType":"address","name":"tokenBridge","type":"address"},{"internalType":"address","name":"relayer","type":"address"},{"internalType":"uint16","name":"hubChainId","type":"uint16"},{"internalType":"address","name":"hubContractAddress","type":"address"},{"internalType":"address","name":"_circleMessageTransmitter","type":"address"},{"internalType":"address","name":"_circleTokenMessenger","type":"address"},{"internalType":"address","name":"_USDC","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"NotAnEvmAddress","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wormholeRelayer","type":"address"},{"internalType":"address","name":"_wormhole","type":"address"}],"name":"__Base_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wormholeRelayer","type":"address"},{"internalType":"address","name":"_tokenBridge","type":"address"},{"internalType":"address","name":"_wormhole","type":"address"},{"internalType":"address","name":"_circleMessageTransmitter","type":"address"},{"internalType":"address","name":"_circleTokenMessenger","type":"address"},{"internalType":"address","name":"_USDC","type":"address"}],"name":"__CCTPBase_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wormholeRelayer","type":"address"},{"internalType":"address","name":"_tokenBridge","type":"address"},{"internalType":"address","name":"_wormhole","type":"address"}],"name":"__TokenBase_init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"}],"name":"borrowNative","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"circleMessageTransmitter","outputs":[{"internalType":"contract IMessageTransmitter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"circleTokenMessenger","outputs":[{"internalType":"contract ITokenMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"consistencyLevel","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultGasLimitRoundtrip","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"}],"name":"depositCollateral","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"}],"name":"depositCollateralNative","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"},{"internalType":"bool","name":"withTokenTransfer","type":"bool"}],"name":"getDeliveryCostRoundtrip","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hubChainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hubContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bytes[]","name":"additionalVaas","type":"bytes[]"},{"internalType":"bytes32","name":"sourceAddress","type":"bytes32"},{"internalType":"uint16","name":"sourceChain","type":"uint16"},{"internalType":"bytes32","name":"deliveryHash","type":"bytes32"}],"name":"receiveWormholeMessages","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"}],"name":"repayNative","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"seenDeliveryVaaHashes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setDefaultGasLimitRoundtrip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setIsUsingCCTP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"sourceChain","type":"uint16"},{"internalType":"bytes32","name":"sourceAddress","type":"bytes32"}],"name":"setRegisteredSender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenBridge","outputs":[{"internalType":"contract ITokenBridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"}],"name":"withdrawCollateral","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"}],"name":"withdrawCollateralNative","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"wormhole","outputs":[{"internalType":"contract IWormhole","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wormholeRelayer","outputs":[{"internalType":"contract IWormholeRelayer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6080346200023257601f6200532e38819003918201601f19168301916001600160401b0383118484101762000237578084926101209460405283398101031262000232576200004e816200024d565b6200005c602083016200025d565b6200006a604084016200025d565b9162000079606085016200025d565b9162000088608086016200024d565b906200009760a087016200025d565b93620000a660c088016200025d565b91620000c4610100620000bc60e08b016200025d565b99016200025d565b9233156200021957600198603e54908a8060a01b03199a8b953387851617603e55818060a01b039b8c978880988197823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a381603a54956200014660ff881615620001358162000272565b620001408162000272565b62000272565b16836035541617603555169060365416176036558d339060385416176038556101008360a81b039060081b1690828060a81b0319161717603a551689603b541617603b5516918288603c541617603c551686603d541617603d556000549261ffff938465010000000000600160c81b038760281b1692169062ff0001600160c81b0319161764ffff0000008460181b1617176000556209eb1060015560ff801960025416911515161760025516600052603960205216604060002055603854166038556040516150889081620002a68239f35b604051631e4fbdf760e01b815260006004820152602490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b519061ffff821682036200023257565b51906001600160a01b03821682036200023257565b156200027a57565b60405162461bcd60e51b815260206004820152600360248201526257524960e81b6044820152606490fdfe60806040526004361015610018575b361561001657005b005b6000803560e01c80630c5927201461418157806314f29f8814614156578063180f6cc2146141275780631a74856b146135b25780632fd8f7e6146131e65780633757e53d146131b1578063378717b7146131825780633d3a066f14612ee6578063529dca3214611e7b578063715018a614611e125780637816976814611df457806384acd1bb14611dcd57806389a3027114611da65780638c1577c914611d7f5780638cd2e0c71461119d5780638da5cb5b14611176578063929f5840146111525780639a8a059214611131578063a489e28a14611088578063a7b4629e14610e68578063a8cbeb2914610a97578063c1bce0b714610777578063c6328a461461074d578063d8c71a9214610724578063da25b725146106fd578063dd12d68b1461064a578063decfba6114610614578063dfe6f20014610273578063e1cc30ee1461024c578063e8dfd508146102295763f2fde38b14610179575061000e565b34610226576020600319360112610226576101926141a4565b61019a614373565b6001600160a01b038091169081156101f557603e548273ffffffffffffffffffffffffffffffffffffffff19821617603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b602483604051907f1e4fbdf70000000000000000000000000000000000000000000000000000000082526004820152fd5b80fd5b503461022657806003193601126102265760ff6020915460101c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603c5416604051908152f35b5061027d36614350565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa918215610608579084926105db575b50169080156105975782603d541682148061058b575b82151580610583575b610443575b808697819796979261043a575b81610431575b6040519460018887015233604087015260608601521515608085015281151560a0850152151560c084015260c083526103278361427a565b156103fd57505b61037d8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e48701906148d1565b93606486015260848501521660a48301523360c4830152039134905af19081156103f1576000916103bc575b5067ffffffffffffffff60405191168152f35b908282813d83116103ea575b6103d281836142b2565b8101031261022657506103e4906148f6565b386103a9565b503d6103c8565b6040513d6000823e3d90fd5b61042c9061041e6040519384928784015260408084015260608301906148d1565b03601f1981018352826142b2565b61032e565b600191506102ef565b600092506102e9565b60405163313ce56760e01b81528681600481875afa80156103f157839160009161054c575b5060ff8116906008821190838215610528576104959061048f61048a84614df0565b614e32565b90614e56565b80926000146104ff57506104b6925061048a6104b091614df0565b90614e43565b146102dc576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610510575b50506104b6565b610521925061048a61048f91614e21565b3880610509565b6008841015610495575061054761054161048a83614e21565b85614e43565b610495565b91508782813d831161057c575b61056381836142b2565b8101031261022657506105768391614de2565b38610468565b503d610559565b5080156102d7565b5060ff600254166102ce565b6064856040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6105fb9150863d8811610601575b6105f381836142b2565b81019061459f565b386102b8565b503d6105e9565b604051903d90823e3d90fd5b5034610226576060600319360112610226576106476106316141a4565b6106396141ba565b6106416141d0565b9161445e565b80f35b50346102265760406003193601126102265760043561ffff81168091036106f8576001600160a01b0360385416330361068f5781526039602052602435604082205580f35b608460405162461bcd60e51b8152602060048201526024808201527f4e6f7420616c6c6f77656420746f20736574207265676973746572656420736560448201527f6e646572000000000000000000000000000000000000000000000000000000006064820152fd5b600080fd5b503461022657806003193601126102265760206001600160a01b0360355416604051908152f35b50346102265780600319360112610226576001600160a01b036020915460281c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603a5460081c16604051908152f35b610780366141e6565b909161078d821515614531565b8215610a53576001600160a01b038080603d541692169182149384610a46575b849083151580610a3e575b610907575b816108fe575b856108f5575b60405191602096879560018786015233604086015260608501526000608085015281151560a0850152151560c084015260c083526108068361427a565b156108cf57505b61085c8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e48701906148d1565b93606486015260848501521660a48301523360c4830152039134905af19081156103f15760009161089a575067ffffffffffffffff60405191168152f35b908282813d83116108c8575b6108b081836142b2565b8101031261022657506108c2906148f6565b826103a9565b503d6108a6565b6108f09061041e6040519384928784015260408084015260608301906148d1565b61080d565b600195506107c9565b600091506107c3565b905060405163313ce56760e01b8152602081600481875afa80156103f1578291600091610a03575b5060ff81169060088211908382156109e5576109519061048f61048a84614df0565b80926000146109bc575061096c925061048a6104b091614df0565b036109785784906107bd565b606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b91926008116109cd575b505061096c565b6109de925061048a61048f91614e21565b87806109c6565b600884101561095157506109fe61054161048a83614e21565b610951565b91506020823d8211610a36575b81610a1d602093836142b2565b810103126102265750610a308291614de2565b8761092f565b3d9150610a10565b5081156107b8565b60025460ff1694506107ad565b606460405162461bcd60e51b815260206004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b50602090816003193601126102265750600435610ab3816145e1565b803410610dfe57610ac4903461457c565b8015610dba576001600160a01b039081603d54161580610dae575b8080610da6575b81610d9d575b60405191600387840152336040840152600060608401526000608084015281151560a0840152151560c083015260c08252610b268261427a565b15610d76575b82603a5460081c16916040516384acd1bb60e01b81528681600481875afa9081156103f15785918891600091610d59575b50600460405180948193631a90a21960e01b8352165afa9081156103f157600091610d2a575b50610b8d916145d4565b928560005494604051948580927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528561ffff998a8160181c16600485015260281c166024830152600060448301526080606483015281610bf260848201896148d1565b03925af19384156103f1578693600095610ced575b50610c10614aed565b9160005491610c998184169280603a5460081c1660405190610c3182614242565b8582528982015267ffffffffffffffff809a166040820152610c5287614b29565b52610c5c86614b29565b50610c668a614738565b938160355416966001546040519c8d9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a01614b60565b03925af19182156103f157600092610cb6575b5060405191168152f35b90918382813d8311610ce6575b610ccd81836142b2565b810103126102265750610cdf906148f6565b9038610cac565b503d610cc3565b91929382819692963d8311610d23575b610d0781836142b2565b81010312610226575090610d1c8693926148f6565b9338610c07565b503d610cfd565b908782813d8311610d52575b610d4081836142b2565b81010312610226575051610b8d610b83565b503d610d36565b610d709150823d8411610601576105f381836142b2565b38610b5d565b610d9861041e91604051928391858984015260408084015260608301906148d1565b610b2c565b60019150610aec565b506000610ae6565b5060ff60025416610adf565b6064836040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084836040519062461bcd60e51b82526004820152602a60248201527f53706f6b653a3a72657061794e61746976653a496e73756666696369656e742060448201527f76616c75652073656e74000000000000000000000000000000000000000000006064820152fd5b50610e7236614350565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa9182156106085790849261106b575b50169080156105975782603d541682148061105f575b82151580611057575b610f2e575b8086978197969792610f25575b81610f1c575b6040519460028887015233604087015260608601521515608085015281151560a0850152151560c084015260c083526103278361427a565b60019150610ee4565b60009250610ede565b60405163313ce56760e01b81528681600481875afa80156103f1578391600091611020575b5060ff811690600882119083821561100257610f759061048f61048a84614df0565b8092600014610fd95750610f90925061048a6104b091614df0565b14610ed1576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610fea575b5050610f90565b610ffb925061048a61048f91614e21565b3880610fe3565b6008841015610f75575061101b61054161048a83614e21565b610f75565b91508782813d8311611050575b61103781836142b2565b81010312610226575061104a8391614de2565b38610f53565b503d61102d565b508015610ecc565b5060ff60025416610ec3565b6110829150863d8811610601576105f381836142b2565b38610ead565b50346102265760c0600319360112610226576110a26141a4565b6110aa6141ba565b6110b26141d0565b91606435906001600160a01b03938483168093036106f857608435938585168095036106f85760a4359586168096036106f8576110fd926110f860ff603a5416156143b7565b61445e565b73ffffffffffffffffffffffffffffffffffffffff199182603b541617603b5581603c541617603c55603d541617603d5580f35b503461022657806003193601126102265761ffff6020915416604051908152f35b503461022657806003193601126102265761ffff6020915460181c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603e5416604051908152f35b6111a6366141e6565b906111bb6111b3836145e1565b3410156144e6565b8015610a53576001600160a01b0380603d541693169283149283611d72575b80151580611d6a575b611c3c575b838080611c34575b81611c2b575b60405191600360208401523360408401528360608401526000608084015281151560a0840152151560c083015260c082526112308261427a565b15611c0257935b600090156117cd57505061125a816001600160a01b03603d54163090339061490b565b60005491600154906040519161126f8361425e565b6001835260005b602081106117b65750611287614c23565b50603d54603b5460405163095ea7b360e01b81526001600160a01b039182166004820152602481018790529160209183916044918391600091165af180156103f15761177d575b506001600160a01b03603b5416602063ffffffff60a46112f461ffff8a60181c16614c3d565b60006001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af19081156103f157600091611743575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa9081156103f1576000916116e4575b506113d27fffffffffffffffff00000000000000000000000000000000000000000000000091614c3d565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c81526114178161425e565b604051906114248261425e565b60028252602082015261143684614b29565b5261144083614b29565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa9283156103f1576000936116c3575b506040805163c23ee3c360e01b815261ffff601885901c16600482015260248101869052604481018390529690876064818b5afa9687156103f15760009761167a575b506001600160a01b039261156899979695949261151661ffff936115086040519b8c92602084015260408084015260608301906148d1565b03601f1981018b528a6142b2565b6040519a8b997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b01906148d1565b9560648a0152600060848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b8301019401926000915b83831061163357505050505060209391838092600f61014483015203925af180156103f1576000906115f9575b602091505b67ffffffffffffffff60405191168152f35b6020823d60201161162b575b81611612602093836142b2565b8101031261022657506116266020916148f6565b6115e2565b3d9150611605565b9193959650919360208061166783601f1986600196030187526040838b5160ff8151168452015191818582015201906148d1565b97019301930190928896959492936115b5565b6001600160a01b039493919750916115166116af61ffff9460403d6040116116bc575b6116a781836142b2565b8101906145be565b50989294955050916114d0565b503d61169d565b6116dd91935060203d602011610601576105f381836142b2565b918861148d565b906020823d60201161173b575b816116fe602093836142b2565b8101031261022657506113d26117347fffffffffffffffff00000000000000000000000000000000000000000000000092614c14565b91506113a7565b3d91506116f1565b906020823d602011611775575b8161175d602093836142b2565b81010312610226575061176f906148f6565b87611374565b3d9150611750565b6020813d6020116117ae575b81611796602093836142b2565b810103126106f8576117a79061496a565b50866112ce565b3d9150611789565b6020906117c1614c23565b82828701015201611276565b919092936117dd8230338761490b565b825493600154906117ec614aed565b936117f5614ace565b506040519161180383614296565b86835261180e614ace565b50603a5460405163095ea7b360e01b815260089190911c6001600160a01b03166004820152602481018390526020816044818b865af18015611bf757611bb7575b506004906001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611bac57908a949392918a93611b6b575b509160209493916119106001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c48301906148d1565b03925af18015611b60578590611b22575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611b17578793611acc575b509067ffffffffffffffff9161ffff6040519461197f86614242565b168452602084015216604082015261199684614b29565b526119a083614b29565b506001600160a01b0360355416916040519663c23ee3c360e01b8852604088806119eb868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611ac15786979860209793611a9b575b50611a4290604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a01614b60565b03925af1908115610608578091611a5e575b50602091506115e7565b90506020823d602011611a93575b81611a79602093836142b2565b810103126102265750611a8d6020916148f6565b82611a54565b3d9150611a6c565b611a42919350611ab99060403d6040116116bc576116a781836142b2565b509290611a03565b6040513d88823e3d90fd5b9092506020813d602011611b0f575b81611ae8602093836142b2565b81010312611b0b5790611b0367ffffffffffffffff92614c14565b929091611963565b8680fd5b3d9150611adb565b6040513d89823e3d90fd5b506020813d602011611b58575b81611b3c602093836142b2565b81010312611b5457611b4f6004916148f6565b611921565b8480fd5b3d9150611b2f565b6040513d87823e3d90fd5b9192509293506020813d602011611ba4575b81611b8a602093836142b2565b81010312611ba057518993929091906020611898565b8880fd5b3d9150611b7d565b6040513d8b823e3d90fd5b6020813d602011611bef575b81611bd0602093836142b2565b81010312611beb5790611be460049261496a565b509061184f565b8780fd5b3d9150611bc3565b6040513d8a823e3d90fd5b611c2561041e9160405192839186602084015260408084015260608301906148d1565b93611237565b600091506111f6565b5060016111f0565b60405163313ce56760e01b8152602081600481855afa80156103f1578391600091611d2f575b5060ff8116906008821190838215611d1157611c849061048f61048a84614df0565b8092600014611ce85750611c9f925061048a6104b091614df0565b146111e857606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811611cf9575b5050611c9f565b611d0a925061048a61048f91614e21565b8680611cf2565b6008841015611c845750611d2a61054161048a83614e21565b611c84565b91506020823d8211611d62575b81611d49602093836142b2565b810103126102265750611d5c8391614de2565b86611c62565b3d9150611d3c565b5083156111e3565b60025460ff1693506111da565b503461022657806003193601126102265760206001600160a01b03603b5416604051908152f35b503461022657806003193601126102265760206001600160a01b03603d5416604051908152f35b503461022657806003193601126102265760206001600160a01b0360365416604051908152f35b50346102265780600319360112610226576020600154604051908152f35b5034610226578060031936011261022657611e2b614373565b60006001600160a01b03603e5473ffffffffffffffffffffffffffffffffffffffff198116603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5060031960a081360112612ee25760043567ffffffffffffffff8111612de957611ea99036906004016142f1565b60243567ffffffffffffffff8111612ede5736602382011215612ede57806004013590611ed582614338565b91611ee360405193846142b2565b80835260051b810160240160208301368211611b0b5760248301905b828210612eae57505050506064359261ffff84168403611b54578151611f51575050611f2b9150614dac565b50508134611f3857505080f35b8080806106479434905af1611f4b614a0b565b50614cbb565b611f5c839293614dac565b9150506000146122ed5760018351116122835784926001815114612073575b5050611f91816020808594518301019101614d48565b920361202f5761ffff92612016612024936001600160a01b03958693611fbc85603554163314614ef6565b1687526039602052611fd5604435604089205414614f67565b6084358088526037602052611ff160ff60408a20541615614fb2565b8752603760205260408720600160ff1982541617905560208082518301019101614d76565b5050168093603d5416614ffd565b8134611f3857505080f35b606460405162461bcd60e51b815260206004820152601560248201527f57726f6e6720616d6f756e7420726563656976656400000000000000000000006044820152fd5b61207e919350614b29565b519182518301926040816020860195031261227f57602081015167ffffffffffffffff8111611b0b578460206120b692840101614d06565b9360408201519167ffffffffffffffff8311611beb576120d99201602001614d06565b906001600160a01b0380603d541692604051956020876024817f70a0823100000000000000000000000000000000000000000000000000000000988982523060048301525afa968715611bac578997612248575b509060209161217d8a85603c54169261218c604051988996879586947f57ecfd280000000000000000000000000000000000000000000000000000000086526040600487015260448601906148d1565b918483030160248501526148d1565b03925af18015611b175761220e575b60209150603d54169160246040518094819382523060048301525afa908115611b605785916121da575b50611f91926121d39161457c565b9138611f7b565b90506020813d602011612206575b816121f5602093836142b2565b810103126106f85751611f916121c5565b3d91506121e8565b6020823d602011612240575b81612227602093836142b2565b81010312611b0b5761223a60209261496a565b5061219b565b3d915061221a565b919096506020823d602011612277575b81612265602093836142b2565b810103126106f857905195602061212d565b3d9150612258565b8580fd5b608460405162461bcd60e51b815260206004820152602660248201527f434354503a204174206d6f7374206f6e65204d6573736167652069732073757060448201527f706f7274656400000000000000000000000000000000000000000000000000006064820152fd5b50929190805191601f1961231961230385614338565b9461231160405196876142b2565b808652614338565b01845b818110612e79575050835b8251811015612c31576123866001600160a01b03603654168661234a8487614b4c565b51604051809481927fa9e118930000000000000000000000000000000000000000000000000000000083526020600484015260248301906148d1565b0381845afa918215611b175787926129ec575b50608082015190603a549161ffff60608501511690604051917fad66a5f100000000000000000000000000000000000000000000000000000000835260048301526020826024816001600160a01b038860081c165afa9182156129e1578a926129ad575b5003612969578760e0612447940151604051809581927fea63738d0000000000000000000000000000000000000000000000000000000083526020600484015260248301906148d1565b03816001600160a01b038660081c165afa928315611bf7578893612855575b503060808401511490816127dd575b501561277357866124d16001600160a01b03926124928689614b4c565b51836040519586809581947fc3f511c10000000000000000000000000000000000000000000000000000000083526020600484015260248301906148d1565b039360081c165af18015611b1757612731575b5061ffff606082015116604082015190600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa8015611bac5789906126f6575b61ffff16821415905061267657508060a01c612645576001600160a01b03165b8680604051602081019063313ce56760e01b8252600481526125658161425e565b5190845afa50612573614a0b565b602081805181010312611beb57602061258c9101614de2565b602083019182519160ff9060088282161161261d575b50506001600160a01b039061ffff6060604087015196015116935193604051956125cb87614210565b86526020860152166040840152606083015260808201526125ec8286614b4c565b526125f78185614b4c565b50600019811461260957600101612327565b602485634e487b7160e01b81526011600452fd5b926104b061263d926126376001600160a01b039596614df0565b16614e87565b9190386125a2565b602490604051907f33b960d00000000000000000000000000000000000000000000000000000000082526004820152fd5b9060209060446001600160a01b03603a5460081c169360405194859384927f1ff1e286000000000000000000000000000000000000000000000000000000008452600484015260248301525afa908115611b175787916126d7575b50612544565b6126f0915060203d602011610601576105f381836142b2565b386126d1565b506020813d602011612729575b81612710602093836142b2565b81010312611ba05761272461ffff91614c14565b612524565b3d9150612703565b3d8088833e61274081836142b2565b6020828281010312611beb5781519067ffffffffffffffff8211611ba05761276c929081019101614d06565b50386124e4565b608460405162461bcd60e51b815260206004820152602260248201527f546f6b656e20776173206e6f742073656e7420746f207468697320616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b90506004602061ffff60a0860151169260405192838092634d4502c960e11b82525afa8015611bac57899061281a575b61ffff9150161438612475565b506020813d60201161284d575b81612834602093836142b2565b81010312611ba05761284861ffff91614c14565b61280d565b3d9150612827565b9092503d908189823e61286882826142b2565b6020818381010312611ba05780519167ffffffffffffffff831161296557610100838301828401031261296557604051928361010081011067ffffffffffffffff610100860111176129515761010084016040526128c7818401614de2565b845282810160208181015190860152604080820151908601526128ec90606001614c14565b606085015260808184010151608085015261290b60a082850101614c14565b60a085015282810160c0818101519086015260e001519167ffffffffffffffff831161294d576129419390810192910101614d06565b60e08201529138612466565b8b80fd5b60248b634e487b7160e01b81526041600452fd5b8980fd5b606460405162461bcd60e51b815260206004820152601660248201527f4e6f74206120546f6b656e2042726964676520564141000000000000000000006044820152fd5b9091506020813d6020116129d9575b816129c9602093836142b2565b81010312612965575190386123fd565b3d91506129bc565b6040513d8c823e3d90fd5b9091503d908188823e6129ff82826142b2565b6020818381010312611beb5780519067ffffffffffffffff8211611ba057610160928383830182840103126129655760405193848181011067ffffffffffffffff8287011117612951578401604052612a59838301614de2565b8452612a69602084840101614e76565b6020850152612a7c604084840101614e76565b6040850152612a8f606084840101614c14565b6060850152608083830101516080850152612aae60a0848401016148f6565b60a085015260c0612ac28185850101614de2565b9085015260e0838301015167ffffffffffffffff8111612c2d57612aed908284019085850101614d06565b60e0850152610100612b028185850101614e76565b90850152610120838301015167ffffffffffffffff8111612c2d57818301601f8286860101011215612c2d57808484010151612b3d81614338565b92612b4b60405194856142b2565b818452602084019281860160208460071b838a8a0101010111612c29576020818888010101935b60208460071b838a8a010101018510612ba35750505050506101208401520161014090810151908201529038612399565b6080858489010312612c255760405180608081011067ffffffffffffffff608083011117612c1057608091818360209301604052875181528288015183820152612bef60408901614de2565b6040820152612c0060608901614de2565b6060820152815201940193612b72565b5060248f634e487b7160e01b81526041600452fd5b8e80fd5b8d80fd5b8a80fd5b50929391905061ffff6001600160a01b0393612c5285603554163314614ef6565b1684526039602052612c6b604435604086205414614f67565b6084358085526037602052612c8760ff60408720541615614fb2565b8452603760205260408420600160ff198254161790556001815103612e3557612cba612cb38592614b29565b5192614dac565b5090809491600014612e175750806020600492603a5460081c16604051938480926315ab88c960e31b82525afa8015612e0c576060928491612ded575b5016920191825190803b15612de9576024839260405194859384927f2e1a7d4d00000000000000000000000000000000000000000000000000000000845260048401525af18015612dde57612dae575b50828080809351855af1612d59614a0b565b5015612d6a578134611f3857505080f35b606460405162461bcd60e51b815260206004820152601960248201527f776974686472617720746f206e6174697665206661696c6564000000000000006044820152fd5b67ffffffffffffffff8194929411612dca576040529138612d47565b602482634e487b7160e01b81526041600452fd5b6040513d86823e3d90fd5b8280fd5b612e06915060203d602011610601576105f381836142b2565b38612cf7565b6040513d85823e3d90fd5b908392506060906040612e309501511692015191614ffd565b612024565b606460405162461bcd60e51b815260206004820152601660248201527f457870656374696e67206f6e65207472616e73666572000000000000000000006044820152fd5b602090604051612e8881614210565b87815287838201528760408201528760608201528760808201528282880101520161231c565b813567ffffffffffffffff8111611ba057602091612ed38392602436918901016142f1565b815201910190611eff565b8380fd5b5080fd5b50612ef0366141e6565b909291612efe821515614531565b8315610a53576001600160a01b038080603d541692169182149485613175575b85908315158061316d575b613079575b81613071575b86613068575b604051916020978895600287860152336040860152606085015287608085015281151560a0850152151560c084015260c08352612f768361427a565b1561304257505b612fcb82603554169186549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e48701906148d1565b93606486015260848501521660a48301523360c4830152039134905af191821561060857809261300a575b505067ffffffffffffffff60405191168152f35b9091508282813d831161303b575b61302281836142b2565b810103126102265750613034906148f6565b3880612ff6565b503d613018565b6130639061041e6040519384928784015260408084015260608301906148d1565b612f7d565b60019650612f3a565b859150612f34565b905060405163313ce56760e01b8152602081600481875afa908115611ac1579082918791613132575b5060ff8116906008821190838215613114576130c49061048f61048a84614df0565b80926000146130eb57506130df925061048a6104b091614df0565b03610978578590612f2e565b91926008116130fc575b50506130df565b61310d925061048a61048f91614e21565b38806130f5565b60088410156130c4575061312d61054161048a83614e21565b6130c4565b9150506020813d8211613165575b8161314d602093836142b2565b8101031261227f5761315f8291614de2565b386130a2565b3d9150613140565b508115612f29565b60025460ff169550612f1e565b5034610226576020600319360112610226576004358015158103612ee257610647906131ac614373565b6144bf565b5034610226576040600319360112610226576024359081151582036102265760206131de836004356147b0565b604051908152f35b506020908160031936011261022657600435613201816145e1565b80341061354857613212903461457c565b908115613504576001600160a01b0380603d541615806134f8575b80806134f1575b816134e8575b60405191868884015233604084015286606084015286608084015281151560a0840152151560c083015260c082526132718261427a565b156134c057905b80603a5460081c16936040516384acd1bb60e01b81528781600481895afa908115611b17578391899189916134a3575b50600460405180948193631a90a21960e01b8352165afa908115611b17578791613474575b506132d7916145d4565b86865491604051968780927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528661ffff96878160181c16600485015260281c1660248301528a60448301526080606483015281613339608482018a6148d1565b03925af1948515611ac15790879392918796613438575b50613359614aed565b918754916133e18184169280603a5460081c166040519061337982614242565b8582528982015267ffffffffffffffff809b16604082015261339a87614b29565b526133a486614b29565b506133ae89614738565b938160355416966001546040519b8c9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a01614b60565b03925af19283156106085780936133fe575b505060405191168152f35b909192508382813d8311613431575b61341781836142b2565b810103126102265750613429906148f6565b9038806133f3565b503d61340d565b84819394959297503d831161346d575b61345281836142b2565b8101031261227f57906134668793926148f6565b9438613350565b503d613448565b90508781813d831161349c575b61348b81836142b2565b81010312611b0b57516132d76132cd565b503d613481565b6134ba9150823d8411610601576105f381836142b2565b386132a8565b6134e261041e91604051928391878a84015260408084015260608301906148d1565b90613278565b6001915061323a565b5084613234565b5060ff6002541661322d565b6064846040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084846040519062461bcd60e51b82526004820152603660248201527f53706f6b653a3a6465706f736974436f6c6c61746572616c4e61746976653a4960448201527f6e73756666696369656e742076616c75652073656e74000000000000000000006064820152fd5b506135bc366141e6565b906135cc6111b3839594956145e1565b8015610a53576001600160a01b0380603d54169416938414938461411a575b80151580614112575b613fe3575b848080613fdb575b81613fd3575b6040519186602084015233604084015283606084015286608084015281151560a0840152151560c083015260c0825261363f8261427a565b15613faa57945b15613bbe5750613665816001600160a01b03603d54163090339061490b565b82549160015490604051916136798361425e565b60018352855b60208110613ba75750613690614c23565b50603d54603b5460405163095ea7b360e01b81526001600160a01b0391821660048201526024810187905291602091839160449183918c91165af18015611b1757613b6e575b506001600160a01b03603b5416602063ffffffff60a46136fc61ffff8a60181c16614c3d565b8a6001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af1908115611b17578791613b34575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa908115611bf7578891613ad5575b506137d77fffffffffffffffff00000000000000000000000000000000000000000000000091614c3d565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c815261381c8161425e565b604051906138298261425e565b60028252602082015261383b84614b29565b5261384583614b29565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa928315611bf7578893613ab4575b506040805163c23ee3c360e01b815261ffff601885901c166004820152602481018690526044810183905299908a6064818b5afa968715611bac5760449a8a98613a75575b506139629a509261ffff9197969594926139106001600160a01b03956115086040519b8c92602084015260408084015260608301906148d1565b6040519b8c997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b01906148d1565b9560648a01528a60848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b83010194019288915b838310613a2e57505050505060209391838092600f61014483015203925af19081156106085780916139f1575b506020915067ffffffffffffffff60405191168152f35b90506020823d602011613a26575b81613a0c602093836142b2565b810103126102265750613a206020916148f6565b386139da565b3d91506139ff565b91939596509193602080613a6283601f1986600196030187526040838b5160ff8151168452015191818582015201906148d1565b97019301930190928996959492936139ad565b61ffff939198506001600160a01b03959492613910613aa56139629360403d6040116116bc576116a781836142b2565b509a93955050929495506138d6565b613ace91935060203d602011610601576105f381836142b2565b9138613891565b90506020813d602011613b2c575b81613af0602093836142b2565b81010312611beb576137d7613b257fffffffffffffffff00000000000000000000000000000000000000000000000092614c14565b91506137ac565b3d9150613ae3565b90506020813d602011613b66575b81613b4f602093836142b2565b81010312611b0b57613b60906148f6565b3861377a565b3d9150613b42565b6020813d602011613b9f575b81613b87602093836142b2565b81010312611b0b57613b989061496a565b50386136d6565b3d9150613b7a565b602090613bb2614c23565b8282870101520161367f565b929193613bcd8230338761490b565b82549360015490613bdc614aed565b93613be5614ace565b5060405191613bf383614296565b868352613bfe614ace565b50603a5460405163095ea7b360e01b815260089190911c6001600160a01b03166004820152602481018390526020816044818b865af18015611bf757613f6e575b506004906001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611bac57908a949392918a93613f31575b50916020949391613d006001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c48301906148d1565b03925af18015611b60578590613ef7575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611b17578793613eb0575b509067ffffffffffffffff9161ffff60405194613d6f86614242565b1684526020840152166040820152613d8684614b29565b52613d9083614b29565b506001600160a01b0360355416916040519663c23ee3c360e01b885260408880613ddb868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611ac15786979860209793613e8a575b50613e3290604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a01614b60565b03925af1908115610608578091613e4d5750602091506115e7565b90506020823d602011613e82575b81613e68602093836142b2565b810103126102265750613e7c6020916148f6565b38611a54565b3d9150613e5b565b613e32919350613ea89060403d6040116116bc576116a781836142b2565b509290613df3565b9092506020813d602011613eef575b81613ecc602093836142b2565b81010312611b0b5790613ee767ffffffffffffffff92614c14565b929091613d53565b3d9150613ebf565b506020813d602011613f29575b81613f11602093836142b2565b81010312611b5457613f246004916148f6565b613d11565b3d9150613f04565b9192509293506020813d602011613f66575b81613f50602093836142b2565b81010312611ba057518993929091906020613c88565b3d9150613f43565b6020813d602011613fa2575b81613f87602093836142b2565b81010312611beb5790613f9b60049261496a565b5090613c3f565b3d9150613f7a565b613fcd61041e9160405192839186602084015260408084015260608301906148d1565b94613646565b859150613607565b506001613601565b60405163313ce56760e01b8152602081600481855afa908115611b605790839186916140d7575b5060ff81169060088211908382156140b95761402c9061048f61048a84614df0565b80926000146140905750614047925061048a6104b091614df0565b146135f957606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b91926008116140a1575b5050614047565b6140b2925061048a61048f91614e21565b388061409a565b600884101561402c57506140d261054161048a83614e21565b61402c565b9150506020813d821161410a575b816140f2602093836142b2565b81010312611b54576141048391614de2565b3861400a565b3d91506140e5565b5084156135f4565b60025460ff1694506135eb565b50346102265760206003193601126102265760ff60406020926004358152603784522054166040519015158152f35b5034610226576040600319360112610226576106476141736141a4565b61417b6141ba565b90614402565b50346102265760206003193601126102265761419b614373565b60043560015580f35b600435906001600160a01b03821682036106f857565b602435906001600160a01b03821682036106f857565b604435906001600160a01b03821682036106f857565b60031960609101126106f8576004356001600160a01b03811681036106f857906024359060443590565b60a0810190811067ffffffffffffffff82111761422c57604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff82111761422c57604052565b6040810190811067ffffffffffffffff82111761422c57604052565b60e0810190811067ffffffffffffffff82111761422c57604052565b6020810190811067ffffffffffffffff82111761422c57604052565b90601f601f19910116810190811067ffffffffffffffff82111761422c57604052565b67ffffffffffffffff811161422c57601f01601f191660200190565b81601f820112156106f857803590614308826142d5565b9261431660405194856142b2565b828452602083830101116106f857816000926020809301838601378301015290565b67ffffffffffffffff811161422c5760051b60200190565b60031960609101126106f857600435906024359060443580151581036106f85790565b6001600160a01b03603e5416330361438757565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b156143be57565b606460405162461bcd60e51b815260206004820152600360248201527f57524900000000000000000000000000000000000000000000000000000000006044820152fd5b90600160ff19603a5461441860ff8216156143b7565b1617603a556001600160a01b03908173ffffffffffffffffffffffffffffffffffffffff1993168360355416176035551681603654161760365533906038541617603855565b91614478919261447360ff603a5416156143b7565b614402565b7fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff00603a549260081b16911617603a55565b6001600160a01b03603c5416156144e35760ff60ff19600254169115151617600255565b50565b156144ed57565b606460405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e742076616c75652073656e740000000000000000006044820152fd5b1561453857565b606460405162461bcd60e51b815260206004820152601e60248201527f4e6f6e2d7a65726f20636f7374466f7252657475726e44656c697665727900006044820152fd5b9190820391821161458957565b634e487b7160e01b600052601160045260246000fd5b908160209103126106f857516001600160a01b03811681036106f85790565b91908260409103126106f8576020825192015190565b9190820180921161458957565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152909392916001600160a01b039083908390831681806064810103915afa91821561472e57859261470e575b5080603a5460081c16835180916384acd1bb60e01b825281600460209485935afa9081156147045790829188916146e7575b506004865180958193631a90a21960e01b8352165afa9384156146de575085936146a7575b50506146a49293506145d4565b90565b9080929350813d83116146d7575b6146bf81836142b2565b81010312612ede576146a49293505190839238614697565b503d6146b5565b513d87823e3d90fd5b6146fe9150823d8411610601576105f381836142b2565b38614672565b85513d89823e3d90fd5b614726919250833d85116116bc576116a781836142b2565b509038614640565b83513d87823e3d90fd5b6035546000546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301949094526044820152919082906001600160a01b031681806064810103915afa9081156103f157600091614794575090565b6147ac915060403d81116116bc576116a781836142b2565b5090565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152939092916001600160a01b03919083908690841681806064810103915afa9485156148a1578495614881575b5061481b5750505090565b80603a95949293955460081c16835180916384acd1bb60e01b825281600460209485935afa9081156147045790829188916146e757506004865180958193631a90a21960e01b8352165afa9384156146de575085936146a75750506146a49293506145d4565b614899919550833d85116116bc576116a781836142b2565b509338614810565b50505051903d90823e3d90fd5b60005b8381106148c15750506000910152565b81810151838201526020016148b1565b90601f19601f6020936148ef815180928187528780880191016148ae565b0116010190565b519067ffffffffffffffff821682036106f857565b909261496893604051937f23b872dd0000000000000000000000000000000000000000000000000000000060208601526001600160a01b03809216602486015216604484015260648301526064825261496382614210565b614977565b565b519081151582036106f857565b6000806001600160a01b036149a193169360208151910182865af161499a614a0b565b9083614a3b565b80519081151591826149e8575b50506149b75750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b81925090602091810103126106f8576020614a03910161496a565b1538806149ae565b3d15614a36573d90614a1c826142d5565b91614a2a60405193846142b2565b82523d6000602084013e565b606090565b90614a7a5750805115614a5057805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580614ac5575b614a8b575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15614a83565b60405190614adb82614242565b60006040838281528260208201520152565b60405190614afa8261425e565b600182528160005b60209081811015614b2457602091614b18614ace565b90828501015201614b02565b505050565b805115614b365760200190565b634e487b7160e01b600052603260045260246000fd5b8051821015614b365760209160051b010190565b9591949896979390929861ffff80941687526001600160a01b0395866020911681890152614b9b6101009b8c60409d8e8c01528a01906148d1565b91606093848a0152608089015287820360a08901528080855193848152019401926000905b838210614be057505050505060e09495969750961660c085015216910152565b845180518816875280840151878501528d015167ffffffffffffffff168d8701529485019493820193600190910190614bc0565b519061ffff821682036106f857565b60405190614c308261425e565b6060602083600081520152565b61ffff1660028103614c4f5750600090565b60068103614c5d5750600190565b60178103614c6b5750600390565b601803614c7757600290565b606460405162461bcd60e51b815260206004820152601160248201527f57726f6e67204343545020446f6d61696e0000000000000000000000000000006044820152fd5b15614cc257565b606460405162461bcd60e51b815260206004820152600d60248201527f726566756e64206661696c6564000000000000000000000000000000000000006044820152fd5b81601f820112156106f8578051614d1c816142d5565b92614d2a60405194856142b2565b818452602082840101116106f8576146a491602080850191016148ae565b9190916040818403126106f857805192602082015167ffffffffffffffff81116106f8576146a49201614d06565b908160609103126106f85780516001600160a01b03811681036106f857916146a46040614da56020850161496a565b930161496a565b614ddb614dca6001600160a01b039260208082518301019101614d48565b905060208082518301019101614d76565b9190921692565b519060ff821682036106f857565b60ff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89116019060ff821161458957565b60ff166008039060ff821161458957565b60ff16604d811161458957600a0a90565b8181029291811591840414171561458957565b8115614e60570490565b634e487b7160e01b600052601260045260246000fd5b519063ffffffff821682036106f857565b8015614ef057600190602081108216604e8210831617614ee8578190600a925b808211614ebf57505081600019048111614589570290565b90928060001904811161458957818416614edf575b800292811c90614ea7565b80920291614ed4565b9050600a0a90565b50600190565b15614efd57565b608460405162461bcd60e51b815260206004820152602260248201527f4d73672e73656e646572206973206e6f7420576f726d686f6c652052656c617960448201527f65720000000000000000000000000000000000000000000000000000000000006064820152fd5b15614f6e57565b606460405162461bcd60e51b815260206004820152601560248201527f4e6f7420726567697374657265642073656e64657200000000000000000000006044820152fd5b15614fb957565b606460405162461bcd60e51b815260206004820152601960248201527f4d65737361676520616c72656164792070726f636573736564000000000000006044820152fd5b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b03929092166024830152604480830193909352918152614968916149636064836142b256fea26469706673582212200b035b85e7a736853716b9734d7deba6c678239d172b223c84861ef70ae4a6c064736f6c634300081400330000000000000000000000000000000000000000000000000000000000000018000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec97220000000000000000000000001d68124e65fafc907325e3edbf8c4d84499daa8b00000000000000000000000027428dd2d3dd32a4d7f7c497eaaa23130d89491100000000000000000000000000000000000000000000000000000000000000170000000000000000000000001e3f1f1ca8c62aabcb3b78d87223e988dfa3780e0000000000000000000000004d41f22c5a0e5c74090899e5a8fb597a8842b3e80000000000000000000000002b4069517957735be00cee0fadae88a26365528f0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85
Deployed Bytecode
0x60806040526004361015610018575b361561001657005b005b6000803560e01c80630c5927201461418157806314f29f8814614156578063180f6cc2146141275780631a74856b146135b25780632fd8f7e6146131e65780633757e53d146131b1578063378717b7146131825780633d3a066f14612ee6578063529dca3214611e7b578063715018a614611e125780637816976814611df457806384acd1bb14611dcd57806389a3027114611da65780638c1577c914611d7f5780638cd2e0c71461119d5780638da5cb5b14611176578063929f5840146111525780639a8a059214611131578063a489e28a14611088578063a7b4629e14610e68578063a8cbeb2914610a97578063c1bce0b714610777578063c6328a461461074d578063d8c71a9214610724578063da25b725146106fd578063dd12d68b1461064a578063decfba6114610614578063dfe6f20014610273578063e1cc30ee1461024c578063e8dfd508146102295763f2fde38b14610179575061000e565b34610226576020600319360112610226576101926141a4565b61019a614373565b6001600160a01b038091169081156101f557603e548273ffffffffffffffffffffffffffffffffffffffff19821617603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b602483604051907f1e4fbdf70000000000000000000000000000000000000000000000000000000082526004820152fd5b80fd5b503461022657806003193601126102265760ff6020915460101c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603c5416604051908152f35b5061027d36614350565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa918215610608579084926105db575b50169080156105975782603d541682148061058b575b82151580610583575b610443575b808697819796979261043a575b81610431575b6040519460018887015233604087015260608601521515608085015281151560a0850152151560c084015260c083526103278361427a565b156103fd57505b61037d8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e48701906148d1565b93606486015260848501521660a48301523360c4830152039134905af19081156103f1576000916103bc575b5067ffffffffffffffff60405191168152f35b908282813d83116103ea575b6103d281836142b2565b8101031261022657506103e4906148f6565b386103a9565b503d6103c8565b6040513d6000823e3d90fd5b61042c9061041e6040519384928784015260408084015260608301906148d1565b03601f1981018352826142b2565b61032e565b600191506102ef565b600092506102e9565b60405163313ce56760e01b81528681600481875afa80156103f157839160009161054c575b5060ff8116906008821190838215610528576104959061048f61048a84614df0565b614e32565b90614e56565b80926000146104ff57506104b6925061048a6104b091614df0565b90614e43565b146102dc576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610510575b50506104b6565b610521925061048a61048f91614e21565b3880610509565b6008841015610495575061054761054161048a83614e21565b85614e43565b610495565b91508782813d831161057c575b61056381836142b2565b8101031261022657506105768391614de2565b38610468565b503d610559565b5080156102d7565b5060ff600254166102ce565b6064856040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6105fb9150863d8811610601575b6105f381836142b2565b81019061459f565b386102b8565b503d6105e9565b604051903d90823e3d90fd5b5034610226576060600319360112610226576106476106316141a4565b6106396141ba565b6106416141d0565b9161445e565b80f35b50346102265760406003193601126102265760043561ffff81168091036106f8576001600160a01b0360385416330361068f5781526039602052602435604082205580f35b608460405162461bcd60e51b8152602060048201526024808201527f4e6f7420616c6c6f77656420746f20736574207265676973746572656420736560448201527f6e646572000000000000000000000000000000000000000000000000000000006064820152fd5b600080fd5b503461022657806003193601126102265760206001600160a01b0360355416604051908152f35b50346102265780600319360112610226576001600160a01b036020915460281c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603a5460081c16604051908152f35b610780366141e6565b909161078d821515614531565b8215610a53576001600160a01b038080603d541692169182149384610a46575b849083151580610a3e575b610907575b816108fe575b856108f5575b60405191602096879560018786015233604086015260608501526000608085015281151560a0850152151560c084015260c083526108068361427a565b156108cf57505b61085c8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e48701906148d1565b93606486015260848501521660a48301523360c4830152039134905af19081156103f15760009161089a575067ffffffffffffffff60405191168152f35b908282813d83116108c8575b6108b081836142b2565b8101031261022657506108c2906148f6565b826103a9565b503d6108a6565b6108f09061041e6040519384928784015260408084015260608301906148d1565b61080d565b600195506107c9565b600091506107c3565b905060405163313ce56760e01b8152602081600481875afa80156103f1578291600091610a03575b5060ff81169060088211908382156109e5576109519061048f61048a84614df0565b80926000146109bc575061096c925061048a6104b091614df0565b036109785784906107bd565b606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b91926008116109cd575b505061096c565b6109de925061048a61048f91614e21565b87806109c6565b600884101561095157506109fe61054161048a83614e21565b610951565b91506020823d8211610a36575b81610a1d602093836142b2565b810103126102265750610a308291614de2565b8761092f565b3d9150610a10565b5081156107b8565b60025460ff1694506107ad565b606460405162461bcd60e51b815260206004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b50602090816003193601126102265750600435610ab3816145e1565b803410610dfe57610ac4903461457c565b8015610dba576001600160a01b039081603d54161580610dae575b8080610da6575b81610d9d575b60405191600387840152336040840152600060608401526000608084015281151560a0840152151560c083015260c08252610b268261427a565b15610d76575b82603a5460081c16916040516384acd1bb60e01b81528681600481875afa9081156103f15785918891600091610d59575b50600460405180948193631a90a21960e01b8352165afa9081156103f157600091610d2a575b50610b8d916145d4565b928560005494604051948580927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528561ffff998a8160181c16600485015260281c166024830152600060448301526080606483015281610bf260848201896148d1565b03925af19384156103f1578693600095610ced575b50610c10614aed565b9160005491610c998184169280603a5460081c1660405190610c3182614242565b8582528982015267ffffffffffffffff809a166040820152610c5287614b29565b52610c5c86614b29565b50610c668a614738565b938160355416966001546040519c8d9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a01614b60565b03925af19182156103f157600092610cb6575b5060405191168152f35b90918382813d8311610ce6575b610ccd81836142b2565b810103126102265750610cdf906148f6565b9038610cac565b503d610cc3565b91929382819692963d8311610d23575b610d0781836142b2565b81010312610226575090610d1c8693926148f6565b9338610c07565b503d610cfd565b908782813d8311610d52575b610d4081836142b2565b81010312610226575051610b8d610b83565b503d610d36565b610d709150823d8411610601576105f381836142b2565b38610b5d565b610d9861041e91604051928391858984015260408084015260608301906148d1565b610b2c565b60019150610aec565b506000610ae6565b5060ff60025416610adf565b6064836040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084836040519062461bcd60e51b82526004820152602a60248201527f53706f6b653a3a72657061794e61746976653a496e73756666696369656e742060448201527f76616c75652073656e74000000000000000000000000000000000000000000006064820152fd5b50610e7236614350565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa9182156106085790849261106b575b50169080156105975782603d541682148061105f575b82151580611057575b610f2e575b8086978197969792610f25575b81610f1c575b6040519460028887015233604087015260608601521515608085015281151560a0850152151560c084015260c083526103278361427a565b60019150610ee4565b60009250610ede565b60405163313ce56760e01b81528681600481875afa80156103f1578391600091611020575b5060ff811690600882119083821561100257610f759061048f61048a84614df0565b8092600014610fd95750610f90925061048a6104b091614df0565b14610ed1576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610fea575b5050610f90565b610ffb925061048a61048f91614e21565b3880610fe3565b6008841015610f75575061101b61054161048a83614e21565b610f75565b91508782813d8311611050575b61103781836142b2565b81010312610226575061104a8391614de2565b38610f53565b503d61102d565b508015610ecc565b5060ff60025416610ec3565b6110829150863d8811610601576105f381836142b2565b38610ead565b50346102265760c0600319360112610226576110a26141a4565b6110aa6141ba565b6110b26141d0565b91606435906001600160a01b03938483168093036106f857608435938585168095036106f85760a4359586168096036106f8576110fd926110f860ff603a5416156143b7565b61445e565b73ffffffffffffffffffffffffffffffffffffffff199182603b541617603b5581603c541617603c55603d541617603d5580f35b503461022657806003193601126102265761ffff6020915416604051908152f35b503461022657806003193601126102265761ffff6020915460181c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603e5416604051908152f35b6111a6366141e6565b906111bb6111b3836145e1565b3410156144e6565b8015610a53576001600160a01b0380603d541693169283149283611d72575b80151580611d6a575b611c3c575b838080611c34575b81611c2b575b60405191600360208401523360408401528360608401526000608084015281151560a0840152151560c083015260c082526112308261427a565b15611c0257935b600090156117cd57505061125a816001600160a01b03603d54163090339061490b565b60005491600154906040519161126f8361425e565b6001835260005b602081106117b65750611287614c23565b50603d54603b5460405163095ea7b360e01b81526001600160a01b039182166004820152602481018790529160209183916044918391600091165af180156103f15761177d575b506001600160a01b03603b5416602063ffffffff60a46112f461ffff8a60181c16614c3d565b60006001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af19081156103f157600091611743575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa9081156103f1576000916116e4575b506113d27fffffffffffffffff00000000000000000000000000000000000000000000000091614c3d565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c81526114178161425e565b604051906114248261425e565b60028252602082015261143684614b29565b5261144083614b29565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa9283156103f1576000936116c3575b506040805163c23ee3c360e01b815261ffff601885901c16600482015260248101869052604481018390529690876064818b5afa9687156103f15760009761167a575b506001600160a01b039261156899979695949261151661ffff936115086040519b8c92602084015260408084015260608301906148d1565b03601f1981018b528a6142b2565b6040519a8b997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b01906148d1565b9560648a0152600060848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b8301019401926000915b83831061163357505050505060209391838092600f61014483015203925af180156103f1576000906115f9575b602091505b67ffffffffffffffff60405191168152f35b6020823d60201161162b575b81611612602093836142b2565b8101031261022657506116266020916148f6565b6115e2565b3d9150611605565b9193959650919360208061166783601f1986600196030187526040838b5160ff8151168452015191818582015201906148d1565b97019301930190928896959492936115b5565b6001600160a01b039493919750916115166116af61ffff9460403d6040116116bc575b6116a781836142b2565b8101906145be565b50989294955050916114d0565b503d61169d565b6116dd91935060203d602011610601576105f381836142b2565b918861148d565b906020823d60201161173b575b816116fe602093836142b2565b8101031261022657506113d26117347fffffffffffffffff00000000000000000000000000000000000000000000000092614c14565b91506113a7565b3d91506116f1565b906020823d602011611775575b8161175d602093836142b2565b81010312610226575061176f906148f6565b87611374565b3d9150611750565b6020813d6020116117ae575b81611796602093836142b2565b810103126106f8576117a79061496a565b50866112ce565b3d9150611789565b6020906117c1614c23565b82828701015201611276565b919092936117dd8230338761490b565b825493600154906117ec614aed565b936117f5614ace565b506040519161180383614296565b86835261180e614ace565b50603a5460405163095ea7b360e01b815260089190911c6001600160a01b03166004820152602481018390526020816044818b865af18015611bf757611bb7575b506004906001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611bac57908a949392918a93611b6b575b509160209493916119106001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c48301906148d1565b03925af18015611b60578590611b22575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611b17578793611acc575b509067ffffffffffffffff9161ffff6040519461197f86614242565b168452602084015216604082015261199684614b29565b526119a083614b29565b506001600160a01b0360355416916040519663c23ee3c360e01b8852604088806119eb868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611ac15786979860209793611a9b575b50611a4290604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a01614b60565b03925af1908115610608578091611a5e575b50602091506115e7565b90506020823d602011611a93575b81611a79602093836142b2565b810103126102265750611a8d6020916148f6565b82611a54565b3d9150611a6c565b611a42919350611ab99060403d6040116116bc576116a781836142b2565b509290611a03565b6040513d88823e3d90fd5b9092506020813d602011611b0f575b81611ae8602093836142b2565b81010312611b0b5790611b0367ffffffffffffffff92614c14565b929091611963565b8680fd5b3d9150611adb565b6040513d89823e3d90fd5b506020813d602011611b58575b81611b3c602093836142b2565b81010312611b5457611b4f6004916148f6565b611921565b8480fd5b3d9150611b2f565b6040513d87823e3d90fd5b9192509293506020813d602011611ba4575b81611b8a602093836142b2565b81010312611ba057518993929091906020611898565b8880fd5b3d9150611b7d565b6040513d8b823e3d90fd5b6020813d602011611bef575b81611bd0602093836142b2565b81010312611beb5790611be460049261496a565b509061184f565b8780fd5b3d9150611bc3565b6040513d8a823e3d90fd5b611c2561041e9160405192839186602084015260408084015260608301906148d1565b93611237565b600091506111f6565b5060016111f0565b60405163313ce56760e01b8152602081600481855afa80156103f1578391600091611d2f575b5060ff8116906008821190838215611d1157611c849061048f61048a84614df0565b8092600014611ce85750611c9f925061048a6104b091614df0565b146111e857606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811611cf9575b5050611c9f565b611d0a925061048a61048f91614e21565b8680611cf2565b6008841015611c845750611d2a61054161048a83614e21565b611c84565b91506020823d8211611d62575b81611d49602093836142b2565b810103126102265750611d5c8391614de2565b86611c62565b3d9150611d3c565b5083156111e3565b60025460ff1693506111da565b503461022657806003193601126102265760206001600160a01b03603b5416604051908152f35b503461022657806003193601126102265760206001600160a01b03603d5416604051908152f35b503461022657806003193601126102265760206001600160a01b0360365416604051908152f35b50346102265780600319360112610226576020600154604051908152f35b5034610226578060031936011261022657611e2b614373565b60006001600160a01b03603e5473ffffffffffffffffffffffffffffffffffffffff198116603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5060031960a081360112612ee25760043567ffffffffffffffff8111612de957611ea99036906004016142f1565b60243567ffffffffffffffff8111612ede5736602382011215612ede57806004013590611ed582614338565b91611ee360405193846142b2565b80835260051b810160240160208301368211611b0b5760248301905b828210612eae57505050506064359261ffff84168403611b54578151611f51575050611f2b9150614dac565b50508134611f3857505080f35b8080806106479434905af1611f4b614a0b565b50614cbb565b611f5c839293614dac565b9150506000146122ed5760018351116122835784926001815114612073575b5050611f91816020808594518301019101614d48565b920361202f5761ffff92612016612024936001600160a01b03958693611fbc85603554163314614ef6565b1687526039602052611fd5604435604089205414614f67565b6084358088526037602052611ff160ff60408a20541615614fb2565b8752603760205260408720600160ff1982541617905560208082518301019101614d76565b5050168093603d5416614ffd565b8134611f3857505080f35b606460405162461bcd60e51b815260206004820152601560248201527f57726f6e6720616d6f756e7420726563656976656400000000000000000000006044820152fd5b61207e919350614b29565b519182518301926040816020860195031261227f57602081015167ffffffffffffffff8111611b0b578460206120b692840101614d06565b9360408201519167ffffffffffffffff8311611beb576120d99201602001614d06565b906001600160a01b0380603d541692604051956020876024817f70a0823100000000000000000000000000000000000000000000000000000000988982523060048301525afa968715611bac578997612248575b509060209161217d8a85603c54169261218c604051988996879586947f57ecfd280000000000000000000000000000000000000000000000000000000086526040600487015260448601906148d1565b918483030160248501526148d1565b03925af18015611b175761220e575b60209150603d54169160246040518094819382523060048301525afa908115611b605785916121da575b50611f91926121d39161457c565b9138611f7b565b90506020813d602011612206575b816121f5602093836142b2565b810103126106f85751611f916121c5565b3d91506121e8565b6020823d602011612240575b81612227602093836142b2565b81010312611b0b5761223a60209261496a565b5061219b565b3d915061221a565b919096506020823d602011612277575b81612265602093836142b2565b810103126106f857905195602061212d565b3d9150612258565b8580fd5b608460405162461bcd60e51b815260206004820152602660248201527f434354503a204174206d6f7374206f6e65204d6573736167652069732073757060448201527f706f7274656400000000000000000000000000000000000000000000000000006064820152fd5b50929190805191601f1961231961230385614338565b9461231160405196876142b2565b808652614338565b01845b818110612e79575050835b8251811015612c31576123866001600160a01b03603654168661234a8487614b4c565b51604051809481927fa9e118930000000000000000000000000000000000000000000000000000000083526020600484015260248301906148d1565b0381845afa918215611b175787926129ec575b50608082015190603a549161ffff60608501511690604051917fad66a5f100000000000000000000000000000000000000000000000000000000835260048301526020826024816001600160a01b038860081c165afa9182156129e1578a926129ad575b5003612969578760e0612447940151604051809581927fea63738d0000000000000000000000000000000000000000000000000000000083526020600484015260248301906148d1565b03816001600160a01b038660081c165afa928315611bf7578893612855575b503060808401511490816127dd575b501561277357866124d16001600160a01b03926124928689614b4c565b51836040519586809581947fc3f511c10000000000000000000000000000000000000000000000000000000083526020600484015260248301906148d1565b039360081c165af18015611b1757612731575b5061ffff606082015116604082015190600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa8015611bac5789906126f6575b61ffff16821415905061267657508060a01c612645576001600160a01b03165b8680604051602081019063313ce56760e01b8252600481526125658161425e565b5190845afa50612573614a0b565b602081805181010312611beb57602061258c9101614de2565b602083019182519160ff9060088282161161261d575b50506001600160a01b039061ffff6060604087015196015116935193604051956125cb87614210565b86526020860152166040840152606083015260808201526125ec8286614b4c565b526125f78185614b4c565b50600019811461260957600101612327565b602485634e487b7160e01b81526011600452fd5b926104b061263d926126376001600160a01b039596614df0565b16614e87565b9190386125a2565b602490604051907f33b960d00000000000000000000000000000000000000000000000000000000082526004820152fd5b9060209060446001600160a01b03603a5460081c169360405194859384927f1ff1e286000000000000000000000000000000000000000000000000000000008452600484015260248301525afa908115611b175787916126d7575b50612544565b6126f0915060203d602011610601576105f381836142b2565b386126d1565b506020813d602011612729575b81612710602093836142b2565b81010312611ba05761272461ffff91614c14565b612524565b3d9150612703565b3d8088833e61274081836142b2565b6020828281010312611beb5781519067ffffffffffffffff8211611ba05761276c929081019101614d06565b50386124e4565b608460405162461bcd60e51b815260206004820152602260248201527f546f6b656e20776173206e6f742073656e7420746f207468697320616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b90506004602061ffff60a0860151169260405192838092634d4502c960e11b82525afa8015611bac57899061281a575b61ffff9150161438612475565b506020813d60201161284d575b81612834602093836142b2565b81010312611ba05761284861ffff91614c14565b61280d565b3d9150612827565b9092503d908189823e61286882826142b2565b6020818381010312611ba05780519167ffffffffffffffff831161296557610100838301828401031261296557604051928361010081011067ffffffffffffffff610100860111176129515761010084016040526128c7818401614de2565b845282810160208181015190860152604080820151908601526128ec90606001614c14565b606085015260808184010151608085015261290b60a082850101614c14565b60a085015282810160c0818101519086015260e001519167ffffffffffffffff831161294d576129419390810192910101614d06565b60e08201529138612466565b8b80fd5b60248b634e487b7160e01b81526041600452fd5b8980fd5b606460405162461bcd60e51b815260206004820152601660248201527f4e6f74206120546f6b656e2042726964676520564141000000000000000000006044820152fd5b9091506020813d6020116129d9575b816129c9602093836142b2565b81010312612965575190386123fd565b3d91506129bc565b6040513d8c823e3d90fd5b9091503d908188823e6129ff82826142b2565b6020818381010312611beb5780519067ffffffffffffffff8211611ba057610160928383830182840103126129655760405193848181011067ffffffffffffffff8287011117612951578401604052612a59838301614de2565b8452612a69602084840101614e76565b6020850152612a7c604084840101614e76565b6040850152612a8f606084840101614c14565b6060850152608083830101516080850152612aae60a0848401016148f6565b60a085015260c0612ac28185850101614de2565b9085015260e0838301015167ffffffffffffffff8111612c2d57612aed908284019085850101614d06565b60e0850152610100612b028185850101614e76565b90850152610120838301015167ffffffffffffffff8111612c2d57818301601f8286860101011215612c2d57808484010151612b3d81614338565b92612b4b60405194856142b2565b818452602084019281860160208460071b838a8a0101010111612c29576020818888010101935b60208460071b838a8a010101018510612ba35750505050506101208401520161014090810151908201529038612399565b6080858489010312612c255760405180608081011067ffffffffffffffff608083011117612c1057608091818360209301604052875181528288015183820152612bef60408901614de2565b6040820152612c0060608901614de2565b6060820152815201940193612b72565b5060248f634e487b7160e01b81526041600452fd5b8e80fd5b8d80fd5b8a80fd5b50929391905061ffff6001600160a01b0393612c5285603554163314614ef6565b1684526039602052612c6b604435604086205414614f67565b6084358085526037602052612c8760ff60408720541615614fb2565b8452603760205260408420600160ff198254161790556001815103612e3557612cba612cb38592614b29565b5192614dac565b5090809491600014612e175750806020600492603a5460081c16604051938480926315ab88c960e31b82525afa8015612e0c576060928491612ded575b5016920191825190803b15612de9576024839260405194859384927f2e1a7d4d00000000000000000000000000000000000000000000000000000000845260048401525af18015612dde57612dae575b50828080809351855af1612d59614a0b565b5015612d6a578134611f3857505080f35b606460405162461bcd60e51b815260206004820152601960248201527f776974686472617720746f206e6174697665206661696c6564000000000000006044820152fd5b67ffffffffffffffff8194929411612dca576040529138612d47565b602482634e487b7160e01b81526041600452fd5b6040513d86823e3d90fd5b8280fd5b612e06915060203d602011610601576105f381836142b2565b38612cf7565b6040513d85823e3d90fd5b908392506060906040612e309501511692015191614ffd565b612024565b606460405162461bcd60e51b815260206004820152601660248201527f457870656374696e67206f6e65207472616e73666572000000000000000000006044820152fd5b602090604051612e8881614210565b87815287838201528760408201528760608201528760808201528282880101520161231c565b813567ffffffffffffffff8111611ba057602091612ed38392602436918901016142f1565b815201910190611eff565b8380fd5b5080fd5b50612ef0366141e6565b909291612efe821515614531565b8315610a53576001600160a01b038080603d541692169182149485613175575b85908315158061316d575b613079575b81613071575b86613068575b604051916020978895600287860152336040860152606085015287608085015281151560a0850152151560c084015260c08352612f768361427a565b1561304257505b612fcb82603554169186549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e48701906148d1565b93606486015260848501521660a48301523360c4830152039134905af191821561060857809261300a575b505067ffffffffffffffff60405191168152f35b9091508282813d831161303b575b61302281836142b2565b810103126102265750613034906148f6565b3880612ff6565b503d613018565b6130639061041e6040519384928784015260408084015260608301906148d1565b612f7d565b60019650612f3a565b859150612f34565b905060405163313ce56760e01b8152602081600481875afa908115611ac1579082918791613132575b5060ff8116906008821190838215613114576130c49061048f61048a84614df0565b80926000146130eb57506130df925061048a6104b091614df0565b03610978578590612f2e565b91926008116130fc575b50506130df565b61310d925061048a61048f91614e21565b38806130f5565b60088410156130c4575061312d61054161048a83614e21565b6130c4565b9150506020813d8211613165575b8161314d602093836142b2565b8101031261227f5761315f8291614de2565b386130a2565b3d9150613140565b508115612f29565b60025460ff169550612f1e565b5034610226576020600319360112610226576004358015158103612ee257610647906131ac614373565b6144bf565b5034610226576040600319360112610226576024359081151582036102265760206131de836004356147b0565b604051908152f35b506020908160031936011261022657600435613201816145e1565b80341061354857613212903461457c565b908115613504576001600160a01b0380603d541615806134f8575b80806134f1575b816134e8575b60405191868884015233604084015286606084015286608084015281151560a0840152151560c083015260c082526132718261427a565b156134c057905b80603a5460081c16936040516384acd1bb60e01b81528781600481895afa908115611b17578391899189916134a3575b50600460405180948193631a90a21960e01b8352165afa908115611b17578791613474575b506132d7916145d4565b86865491604051968780927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528661ffff96878160181c16600485015260281c1660248301528a60448301526080606483015281613339608482018a6148d1565b03925af1948515611ac15790879392918796613438575b50613359614aed565b918754916133e18184169280603a5460081c166040519061337982614242565b8582528982015267ffffffffffffffff809b16604082015261339a87614b29565b526133a486614b29565b506133ae89614738565b938160355416966001546040519b8c9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a01614b60565b03925af19283156106085780936133fe575b505060405191168152f35b909192508382813d8311613431575b61341781836142b2565b810103126102265750613429906148f6565b9038806133f3565b503d61340d565b84819394959297503d831161346d575b61345281836142b2565b8101031261227f57906134668793926148f6565b9438613350565b503d613448565b90508781813d831161349c575b61348b81836142b2565b81010312611b0b57516132d76132cd565b503d613481565b6134ba9150823d8411610601576105f381836142b2565b386132a8565b6134e261041e91604051928391878a84015260408084015260608301906148d1565b90613278565b6001915061323a565b5084613234565b5060ff6002541661322d565b6064846040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084846040519062461bcd60e51b82526004820152603660248201527f53706f6b653a3a6465706f736974436f6c6c61746572616c4e61746976653a4960448201527f6e73756666696369656e742076616c75652073656e74000000000000000000006064820152fd5b506135bc366141e6565b906135cc6111b3839594956145e1565b8015610a53576001600160a01b0380603d54169416938414938461411a575b80151580614112575b613fe3575b848080613fdb575b81613fd3575b6040519186602084015233604084015283606084015286608084015281151560a0840152151560c083015260c0825261363f8261427a565b15613faa57945b15613bbe5750613665816001600160a01b03603d54163090339061490b565b82549160015490604051916136798361425e565b60018352855b60208110613ba75750613690614c23565b50603d54603b5460405163095ea7b360e01b81526001600160a01b0391821660048201526024810187905291602091839160449183918c91165af18015611b1757613b6e575b506001600160a01b03603b5416602063ffffffff60a46136fc61ffff8a60181c16614c3d565b8a6001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af1908115611b17578791613b34575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa908115611bf7578891613ad5575b506137d77fffffffffffffffff00000000000000000000000000000000000000000000000091614c3d565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c815261381c8161425e565b604051906138298261425e565b60028252602082015261383b84614b29565b5261384583614b29565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa928315611bf7578893613ab4575b506040805163c23ee3c360e01b815261ffff601885901c166004820152602481018690526044810183905299908a6064818b5afa968715611bac5760449a8a98613a75575b506139629a509261ffff9197969594926139106001600160a01b03956115086040519b8c92602084015260408084015260608301906148d1565b6040519b8c997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b01906148d1565b9560648a01528a60848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b83010194019288915b838310613a2e57505050505060209391838092600f61014483015203925af19081156106085780916139f1575b506020915067ffffffffffffffff60405191168152f35b90506020823d602011613a26575b81613a0c602093836142b2565b810103126102265750613a206020916148f6565b386139da565b3d91506139ff565b91939596509193602080613a6283601f1986600196030187526040838b5160ff8151168452015191818582015201906148d1565b97019301930190928996959492936139ad565b61ffff939198506001600160a01b03959492613910613aa56139629360403d6040116116bc576116a781836142b2565b509a93955050929495506138d6565b613ace91935060203d602011610601576105f381836142b2565b9138613891565b90506020813d602011613b2c575b81613af0602093836142b2565b81010312611beb576137d7613b257fffffffffffffffff00000000000000000000000000000000000000000000000092614c14565b91506137ac565b3d9150613ae3565b90506020813d602011613b66575b81613b4f602093836142b2565b81010312611b0b57613b60906148f6565b3861377a565b3d9150613b42565b6020813d602011613b9f575b81613b87602093836142b2565b81010312611b0b57613b989061496a565b50386136d6565b3d9150613b7a565b602090613bb2614c23565b8282870101520161367f565b929193613bcd8230338761490b565b82549360015490613bdc614aed565b93613be5614ace565b5060405191613bf383614296565b868352613bfe614ace565b50603a5460405163095ea7b360e01b815260089190911c6001600160a01b03166004820152602481018390526020816044818b865af18015611bf757613f6e575b506004906001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611bac57908a949392918a93613f31575b50916020949391613d006001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c48301906148d1565b03925af18015611b60578590613ef7575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611b17578793613eb0575b509067ffffffffffffffff9161ffff60405194613d6f86614242565b1684526020840152166040820152613d8684614b29565b52613d9083614b29565b506001600160a01b0360355416916040519663c23ee3c360e01b885260408880613ddb868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611ac15786979860209793613e8a575b50613e3290604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a01614b60565b03925af1908115610608578091613e4d5750602091506115e7565b90506020823d602011613e82575b81613e68602093836142b2565b810103126102265750613e7c6020916148f6565b38611a54565b3d9150613e5b565b613e32919350613ea89060403d6040116116bc576116a781836142b2565b509290613df3565b9092506020813d602011613eef575b81613ecc602093836142b2565b81010312611b0b5790613ee767ffffffffffffffff92614c14565b929091613d53565b3d9150613ebf565b506020813d602011613f29575b81613f11602093836142b2565b81010312611b5457613f246004916148f6565b613d11565b3d9150613f04565b9192509293506020813d602011613f66575b81613f50602093836142b2565b81010312611ba057518993929091906020613c88565b3d9150613f43565b6020813d602011613fa2575b81613f87602093836142b2565b81010312611beb5790613f9b60049261496a565b5090613c3f565b3d9150613f7a565b613fcd61041e9160405192839186602084015260408084015260608301906148d1565b94613646565b859150613607565b506001613601565b60405163313ce56760e01b8152602081600481855afa908115611b605790839186916140d7575b5060ff81169060088211908382156140b95761402c9061048f61048a84614df0565b80926000146140905750614047925061048a6104b091614df0565b146135f957606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b91926008116140a1575b5050614047565b6140b2925061048a61048f91614e21565b388061409a565b600884101561402c57506140d261054161048a83614e21565b61402c565b9150506020813d821161410a575b816140f2602093836142b2565b81010312611b54576141048391614de2565b3861400a565b3d91506140e5565b5084156135f4565b60025460ff1694506135eb565b50346102265760206003193601126102265760ff60406020926004358152603784522054166040519015158152f35b5034610226576040600319360112610226576106476141736141a4565b61417b6141ba565b90614402565b50346102265760206003193601126102265761419b614373565b60043560015580f35b600435906001600160a01b03821682036106f857565b602435906001600160a01b03821682036106f857565b604435906001600160a01b03821682036106f857565b60031960609101126106f8576004356001600160a01b03811681036106f857906024359060443590565b60a0810190811067ffffffffffffffff82111761422c57604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff82111761422c57604052565b6040810190811067ffffffffffffffff82111761422c57604052565b60e0810190811067ffffffffffffffff82111761422c57604052565b6020810190811067ffffffffffffffff82111761422c57604052565b90601f601f19910116810190811067ffffffffffffffff82111761422c57604052565b67ffffffffffffffff811161422c57601f01601f191660200190565b81601f820112156106f857803590614308826142d5565b9261431660405194856142b2565b828452602083830101116106f857816000926020809301838601378301015290565b67ffffffffffffffff811161422c5760051b60200190565b60031960609101126106f857600435906024359060443580151581036106f85790565b6001600160a01b03603e5416330361438757565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b156143be57565b606460405162461bcd60e51b815260206004820152600360248201527f57524900000000000000000000000000000000000000000000000000000000006044820152fd5b90600160ff19603a5461441860ff8216156143b7565b1617603a556001600160a01b03908173ffffffffffffffffffffffffffffffffffffffff1993168360355416176035551681603654161760365533906038541617603855565b91614478919261447360ff603a5416156143b7565b614402565b7fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff00603a549260081b16911617603a55565b6001600160a01b03603c5416156144e35760ff60ff19600254169115151617600255565b50565b156144ed57565b606460405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e742076616c75652073656e740000000000000000006044820152fd5b1561453857565b606460405162461bcd60e51b815260206004820152601e60248201527f4e6f6e2d7a65726f20636f7374466f7252657475726e44656c697665727900006044820152fd5b9190820391821161458957565b634e487b7160e01b600052601160045260246000fd5b908160209103126106f857516001600160a01b03811681036106f85790565b91908260409103126106f8576020825192015190565b9190820180921161458957565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152909392916001600160a01b039083908390831681806064810103915afa91821561472e57859261470e575b5080603a5460081c16835180916384acd1bb60e01b825281600460209485935afa9081156147045790829188916146e7575b506004865180958193631a90a21960e01b8352165afa9384156146de575085936146a7575b50506146a49293506145d4565b90565b9080929350813d83116146d7575b6146bf81836142b2565b81010312612ede576146a49293505190839238614697565b503d6146b5565b513d87823e3d90fd5b6146fe9150823d8411610601576105f381836142b2565b38614672565b85513d89823e3d90fd5b614726919250833d85116116bc576116a781836142b2565b509038614640565b83513d87823e3d90fd5b6035546000546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301949094526044820152919082906001600160a01b031681806064810103915afa9081156103f157600091614794575090565b6147ac915060403d81116116bc576116a781836142b2565b5090565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152939092916001600160a01b03919083908690841681806064810103915afa9485156148a1578495614881575b5061481b5750505090565b80603a95949293955460081c16835180916384acd1bb60e01b825281600460209485935afa9081156147045790829188916146e757506004865180958193631a90a21960e01b8352165afa9384156146de575085936146a75750506146a49293506145d4565b614899919550833d85116116bc576116a781836142b2565b509338614810565b50505051903d90823e3d90fd5b60005b8381106148c15750506000910152565b81810151838201526020016148b1565b90601f19601f6020936148ef815180928187528780880191016148ae565b0116010190565b519067ffffffffffffffff821682036106f857565b909261496893604051937f23b872dd0000000000000000000000000000000000000000000000000000000060208601526001600160a01b03809216602486015216604484015260648301526064825261496382614210565b614977565b565b519081151582036106f857565b6000806001600160a01b036149a193169360208151910182865af161499a614a0b565b9083614a3b565b80519081151591826149e8575b50506149b75750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b81925090602091810103126106f8576020614a03910161496a565b1538806149ae565b3d15614a36573d90614a1c826142d5565b91614a2a60405193846142b2565b82523d6000602084013e565b606090565b90614a7a5750805115614a5057805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580614ac5575b614a8b575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15614a83565b60405190614adb82614242565b60006040838281528260208201520152565b60405190614afa8261425e565b600182528160005b60209081811015614b2457602091614b18614ace565b90828501015201614b02565b505050565b805115614b365760200190565b634e487b7160e01b600052603260045260246000fd5b8051821015614b365760209160051b010190565b9591949896979390929861ffff80941687526001600160a01b0395866020911681890152614b9b6101009b8c60409d8e8c01528a01906148d1565b91606093848a0152608089015287820360a08901528080855193848152019401926000905b838210614be057505050505060e09495969750961660c085015216910152565b845180518816875280840151878501528d015167ffffffffffffffff168d8701529485019493820193600190910190614bc0565b519061ffff821682036106f857565b60405190614c308261425e565b6060602083600081520152565b61ffff1660028103614c4f5750600090565b60068103614c5d5750600190565b60178103614c6b5750600390565b601803614c7757600290565b606460405162461bcd60e51b815260206004820152601160248201527f57726f6e67204343545020446f6d61696e0000000000000000000000000000006044820152fd5b15614cc257565b606460405162461bcd60e51b815260206004820152600d60248201527f726566756e64206661696c6564000000000000000000000000000000000000006044820152fd5b81601f820112156106f8578051614d1c816142d5565b92614d2a60405194856142b2565b818452602082840101116106f8576146a491602080850191016148ae565b9190916040818403126106f857805192602082015167ffffffffffffffff81116106f8576146a49201614d06565b908160609103126106f85780516001600160a01b03811681036106f857916146a46040614da56020850161496a565b930161496a565b614ddb614dca6001600160a01b039260208082518301019101614d48565b905060208082518301019101614d76565b9190921692565b519060ff821682036106f857565b60ff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89116019060ff821161458957565b60ff166008039060ff821161458957565b60ff16604d811161458957600a0a90565b8181029291811591840414171561458957565b8115614e60570490565b634e487b7160e01b600052601260045260246000fd5b519063ffffffff821682036106f857565b8015614ef057600190602081108216604e8210831617614ee8578190600a925b808211614ebf57505081600019048111614589570290565b90928060001904811161458957818416614edf575b800292811c90614ea7565b80920291614ed4565b9050600a0a90565b50600190565b15614efd57565b608460405162461bcd60e51b815260206004820152602260248201527f4d73672e73656e646572206973206e6f7420576f726d686f6c652052656c617960448201527f65720000000000000000000000000000000000000000000000000000000000006064820152fd5b15614f6e57565b606460405162461bcd60e51b815260206004820152601560248201527f4e6f7420726567697374657265642073656e64657200000000000000000000006044820152fd5b15614fb957565b606460405162461bcd60e51b815260206004820152601960248201527f4d65737361676520616c72656164792070726f636573736564000000000000006044820152fd5b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b03929092166024830152604480830193909352918152614968916149636064836142b256fea26469706673582212200b035b85e7a736853716b9734d7deba6c678239d172b223c84861ef70ae4a6c064736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec97220000000000000000000000001d68124e65fafc907325e3edbf8c4d84499daa8b00000000000000000000000027428dd2d3dd32a4d7f7c497eaaa23130d89491100000000000000000000000000000000000000000000000000000000000000170000000000000000000000001e3f1f1ca8c62aabcb3b78d87223e988dfa3780e0000000000000000000000004d41f22c5a0e5c74090899e5a8fb597a8842b3e80000000000000000000000002b4069517957735be00cee0fadae88a26365528f0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85
-----Decoded View---------------
Arg [0] : chainId (uint16): 24
Arg [1] : wormhole (address): 0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722
Arg [2] : tokenBridge (address): 0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b
Arg [3] : relayer (address): 0x27428DD2d3DD32A4D7f7C497eAaa23130d894911
Arg [4] : hubChainId (uint16): 23
Arg [5] : hubContractAddress (address): 0x1e3f1f1cA8C62aABCB3B78D87223E988Dfa3780E
Arg [6] : _circleMessageTransmitter (address): 0x4D41f22c5a0e5c74090899E5a8Fb597a8842b3e8
Arg [7] : _circleTokenMessenger (address): 0x2B4069517957735bE00ceE0fadAE88a26365528f
Arg [8] : _USDC (address): 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000018
Arg [1] : 000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722
Arg [2] : 0000000000000000000000001d68124e65fafc907325e3edbf8c4d84499daa8b
Arg [3] : 00000000000000000000000027428dd2d3dd32a4d7f7c497eaaa23130d894911
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000017
Arg [5] : 0000000000000000000000001e3f1f1ca8c62aabcb3b78d87223e988dfa3780e
Arg [6] : 0000000000000000000000004d41f22c5a0e5c74090899e5a8fb597a8842b3e8
Arg [7] : 0000000000000000000000002b4069517957735be00cee0fadae88a26365528f
Arg [8] : 0000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$5.93
Net Worth in ETH
0.002662
Token Allocations
ETH
100.00%
Multichain Portfolio | 34 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.