More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 735 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw Collate... | 21016593 | 82 days ago | IN | 0.0075869 ETH | 0.00137756 | ||||
Withdraw Collate... | 21016167 | 82 days ago | IN | 0.0075869 ETH | 0.00181891 | ||||
Withdraw Collate... | 20995051 | 85 days ago | IN | 0.00911125 ETH | 0.00151133 | ||||
Deposit Collater... | 20991963 | 85 days ago | IN | 0.0003866 ETH | 0.00505914 | ||||
Repay | 20976055 | 87 days ago | IN | 0.0003866 ETH | 0.00295717 | ||||
Withdraw Collate... | 20975735 | 87 days ago | IN | 0.01126986 ETH | 0.00169565 | ||||
Repay | 20975479 | 87 days ago | IN | 0.0003866 ETH | 0.00395612 | ||||
Withdraw Collate... | 20975218 | 87 days ago | IN | 0.01417636 ETH | 0.00192312 | ||||
Deposit Collater... | 20969169 | 88 days ago | IN | 0.0003866 ETH | 0.00344814 | ||||
Withdraw Collate... | 20968895 | 88 days ago | IN | 0.01053797 ETH | 0.00189354 | ||||
Deposit Collater... | 20962353 | 89 days ago | IN | 0.00041198 ETH | 0.00349542 | ||||
Withdraw Collate... | 20959229 | 90 days ago | IN | 0.01427636 ETH | 0.0025382 | ||||
Withdraw Collate... | 20959192 | 90 days ago | IN | 0.01427636 ETH | 0.00226673 | ||||
Withdraw Collate... | 20957664 | 90 days ago | IN | 0.01190848 ETH | 0.00229838 | ||||
Deposit Collater... | 20957622 | 90 days ago | IN | 0.00141198 ETH | 0.00383925 | ||||
Deposit Collater... | 20940590 | 92 days ago | IN | 0.00039184 ETH | 0.00248601 | ||||
Withdraw Collate... | 20936772 | 93 days ago | IN | 0.01174726 ETH | 0.00248141 | ||||
Withdraw Collate... | 20935078 | 93 days ago | IN | 0.00882914 ETH | 0.0015946 | ||||
Deposit Collater... | 20931200 | 94 days ago | IN | 0.00039184 ETH | 0.00268607 | ||||
Deposit Collater... | 20913387 | 96 days ago | IN | 0.00039184 ETH | 0.00292606 | ||||
Deposit Collater... | 20898772 | 98 days ago | IN | 0.00039184 ETH | 0.00375105 | ||||
Deposit Collater... | 20898343 | 98 days ago | IN | 0.00039184 ETH | 0.00107234 | ||||
Deposit Collater... | 20897130 | 98 days ago | IN | 0.00039184 ETH | 0.00121076 | ||||
Repay | 20886270 | 100 days ago | IN | 0.00039184 ETH | 0.00422407 | ||||
Deposit Collater... | 20884799 | 100 days ago | IN | 0.00039184 ETH | 0.00134227 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
21016593 | 82 days ago | 0.0075869 ETH | ||||
21016167 | 82 days ago | 0.0075869 ETH | ||||
20995051 | 85 days ago | 0.00911125 ETH | ||||
20991963 | 85 days ago | 0.0003866 ETH | ||||
20976055 | 87 days ago | 0.0003866 ETH | ||||
20975735 | 87 days ago | 0.01126986 ETH | ||||
20975479 | 87 days ago | 0.0003866 ETH | ||||
20975218 | 87 days ago | 0.01417636 ETH | ||||
20969169 | 88 days ago | 0.0003866 ETH | ||||
20968895 | 88 days ago | 0.01053797 ETH | ||||
20962353 | 89 days ago | 0.00041198 ETH | ||||
20959229 | 90 days ago | 0.01427636 ETH | ||||
20959192 | 90 days ago | 0.01427636 ETH | ||||
20957849 | 90 days ago | 0.00651368 ETH | ||||
20957849 | 90 days ago | 0.00651368 ETH | ||||
20957664 | 90 days ago | 0.01190848 ETH | ||||
20957622 | 90 days ago | 0.00041198 ETH | ||||
20957622 | 90 days ago | 0.001 ETH | ||||
20940590 | 92 days ago | 0.00039184 ETH | ||||
20936956 | 93 days ago | 0.00582157 ETH | ||||
20936956 | 93 days ago | 0.00582157 ETH | ||||
20936772 | 93 days ago | 0.01174726 ETH | ||||
20935078 | 93 days ago | 0.00882914 ETH | ||||
20931200 | 94 days ago | 0.00039184 ETH | ||||
20913387 | 96 days ago | 0.00039184 ETH |
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.1) (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; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.13; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./interfaces/IWormholeReceiver.sol"; import "./interfaces/IWormholeRelayer.sol"; import "./interfaces/ITokenBridge.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) { SafeERC20.forceApprove(IERC20(USDC), 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); }
// 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} from "@openzeppelin/contracts/token/ERC20/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 "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./interfaces/IWormholeReceiver.sol"; import "./interfaces/IWormholeRelayer.sol"; import "./interfaces/ITokenBridge.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) { SafeERC20.forceApprove(IERC20(token), 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
[{"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
6080346200023257601f620052e438819003918201601f19168301916001600160401b0383118484101762000237578084926101209460405283398101031262000232576200004e816200024d565b6200005c602083016200025d565b6200006a604084016200025d565b9162000079606085016200025d565b9162000088608086016200024d565b906200009760a087016200025d565b93620000a660c088016200025d565b91620000c4610100620000bc60e08b016200025d565b99016200025d565b9233156200021957600198603e54908a8060a01b03199a8b953387851617603e55818060a01b039b8c978880988197823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a381603a54956200014660ff881615620001358162000272565b620001408162000272565b62000272565b16836035541617603555169060365416176036558d339060385416176038556101008360a81b039060081b1690828060a81b0319161717603a551689603b541617603b5516918288603c541617603c551686603d541617603d556000549261ffff938465010000000000600160c81b038760281b1692169062ff0001600160c81b0319161764ffff0000008460181b1617176000556209eb1060015560ff8019600254169115151617600255166000526039602052166040600020556038541660385560405161503e9081620002a68239f35b604051631e4fbdf760e01b815260006004820152602490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b519061ffff821682036200023257565b51906001600160a01b03821682036200023257565b156200027a57565b60405162461bcd60e51b815260206004820152600360248201526257524960e81b6044820152606490fdfe60806040526004361015610018575b361561001657005b005b6000803560e01c80630c59272014613ffc57806314f29f8814613fd1578063180f6cc214613fa25780631a74856b146134ef5780632fd8f7e6146131235780633757e53d146130ee578063378717b7146130bf5780633d3a066f14612e23578063529dca3214611da9578063715018a614611d405780637816976814611d2257806384acd1bb14611cfb57806389a3027114611cd45780638c1577c914611cad5780638cd2e0c71461119d5780638da5cb5b14611176578063929f5840146111525780639a8a059214611131578063a489e28a14611088578063a7b4629e14610e68578063a8cbeb2914610a97578063c1bce0b714610777578063c6328a461461074d578063d8c71a9214610724578063da25b725146106fd578063dd12d68b1461064a578063decfba6114610614578063dfe6f20014610273578063e1cc30ee1461024c578063e8dfd508146102295763f2fde38b14610179575061000e565b346102265760206003193601126102265761019261401f565b61019a6141ee565b6001600160a01b038091169081156101f557603e548273ffffffffffffffffffffffffffffffffffffffff19821617603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b602483604051907f1e4fbdf70000000000000000000000000000000000000000000000000000000082526004820152fd5b80fd5b503461022657806003193601126102265760ff6020915460101c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603c5416604051908152f35b5061027d366141cb565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa918215610608579084926105db575b50169080156105975782603d541682148061058b575b82151580610583575b610443575b808697819796979261043a575b81610431575b6040519460018887015233604087015260608601521515608085015281151560a0850152151560c084015260c08352610327836140f5565b156103fd57505b61037d8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e487019061474c565b93606486015260848501521660a48301523360c4830152039134905af19081156103f1576000916103bc575b5067ffffffffffffffff60405191168152f35b908282813d83116103ea575b6103d2818361412d565b8101031261022657506103e490614771565b386103a9565b503d6103c8565b6040513d6000823e3d90fd5b61042c9061041e60405193849287840152604080840152606083019061474c565b03601f19810183528261412d565b61032e565b600191506102ef565b600092506102e9565b60405163313ce56760e01b81528681600481875afa80156103f157839160009161054c575b5060ff8116906008821190838215610528576104959061048f61048a84614da6565b614de8565b90614e0c565b80926000146104ff57506104b6925061048a6104b091614da6565b90614df9565b146102dc576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610510575b50506104b6565b610521925061048a61048f91614dd7565b3880610509565b6008841015610495575061054761054161048a83614dd7565b85614df9565b610495565b91508782813d831161057c575b610563818361412d565b8101031261022657506105768391614d98565b38610468565b503d610559565b5080156102d7565b5060ff600254166102ce565b6064856040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6105fb9150863d8811610601575b6105f3818361412d565b81019061441a565b386102b8565b503d6105e9565b604051903d90823e3d90fd5b50346102265760606003193601126102265761064761063161401f565b610639614035565b61064161404b565b916142d9565b80f35b50346102265760406003193601126102265760043561ffff81168091036106f8576001600160a01b0360385416330361068f5781526039602052602435604082205580f35b608460405162461bcd60e51b8152602060048201526024808201527f4e6f7420616c6c6f77656420746f20736574207265676973746572656420736560448201527f6e646572000000000000000000000000000000000000000000000000000000006064820152fd5b600080fd5b503461022657806003193601126102265760206001600160a01b0360355416604051908152f35b50346102265780600319360112610226576001600160a01b036020915460281c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603a5460081c16604051908152f35b61078036614061565b909161078d8215156143ac565b8215610a53576001600160a01b038080603d541692169182149384610a46575b849083151580610a3e575b610907575b816108fe575b856108f5575b60405191602096879560018786015233604086015260608501526000608085015281151560a0850152151560c084015260c08352610806836140f5565b156108cf57505b61085c8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e487019061474c565b93606486015260848501521660a48301523360c4830152039134905af19081156103f15760009161089a575067ffffffffffffffff60405191168152f35b908282813d83116108c8575b6108b0818361412d565b8101031261022657506108c290614771565b826103a9565b503d6108a6565b6108f09061041e60405193849287840152604080840152606083019061474c565b61080d565b600195506107c9565b600091506107c3565b905060405163313ce56760e01b8152602081600481875afa80156103f1578291600091610a03575b5060ff81169060088211908382156109e5576109519061048f61048a84614da6565b80926000146109bc575061096c925061048a6104b091614da6565b036109785784906107bd565b606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b91926008116109cd575b505061096c565b6109de925061048a61048f91614dd7565b87806109c6565b600884101561095157506109fe61054161048a83614dd7565b610951565b91506020823d8211610a36575b81610a1d6020938361412d565b810103126102265750610a308291614d98565b8761092f565b3d9150610a10565b5081156107b8565b60025460ff1694506107ad565b606460405162461bcd60e51b815260206004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b50602090816003193601126102265750600435610ab38161445c565b803410610dfe57610ac490346143f7565b8015610dba576001600160a01b039081603d54161580610dae575b8080610da6575b81610d9d575b60405191600387840152336040840152600060608401526000608084015281151560a0840152151560c083015260c08252610b26826140f5565b15610d76575b82603a5460081c16916040516384acd1bb60e01b81528681600481875afa9081156103f15785918891600091610d59575b50600460405180948193631a90a21960e01b8352165afa9081156103f157600091610d2a575b50610b8d9161444f565b928560005494604051948580927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528561ffff998a8160181c16600485015260281c166024830152600060448301526080606483015281610bf2608482018961474c565b03925af19384156103f1578693600095610ced575b50610c10614968565b9160005491610c998184169280603a5460081c1660405190610c31826140bd565b8582528982015267ffffffffffffffff809a166040820152610c52876149a4565b52610c5c866149a4565b50610c668a6145b3565b938160355416966001546040519c8d9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a016149db565b03925af19182156103f157600092610cb6575b5060405191168152f35b90918382813d8311610ce6575b610ccd818361412d565b810103126102265750610cdf90614771565b9038610cac565b503d610cc3565b91929382819692963d8311610d23575b610d07818361412d565b81010312610226575090610d1c869392614771565b9338610c07565b503d610cfd565b908782813d8311610d52575b610d40818361412d565b81010312610226575051610b8d610b83565b503d610d36565b610d709150823d8411610601576105f3818361412d565b38610b5d565b610d9861041e916040519283918589840152604080840152606083019061474c565b610b2c565b60019150610aec565b506000610ae6565b5060ff60025416610adf565b6064836040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084836040519062461bcd60e51b82526004820152602a60248201527f53706f6b653a3a72657061794e61746976653a496e73756666696369656e742060448201527f76616c75652073656e74000000000000000000000000000000000000000000006064820152fd5b50610e72366141cb565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa9182156106085790849261106b575b50169080156105975782603d541682148061105f575b82151580611057575b610f2e575b8086978197969792610f25575b81610f1c575b6040519460028887015233604087015260608601521515608085015281151560a0850152151560c084015260c08352610327836140f5565b60019150610ee4565b60009250610ede565b60405163313ce56760e01b81528681600481875afa80156103f1578391600091611020575b5060ff811690600882119083821561100257610f759061048f61048a84614da6565b8092600014610fd95750610f90925061048a6104b091614da6565b14610ed1576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610fea575b5050610f90565b610ffb925061048a61048f91614dd7565b3880610fe3565b6008841015610f75575061101b61054161048a83614dd7565b610f75565b91508782813d8311611050575b611037818361412d565b81010312610226575061104a8391614d98565b38610f53565b503d61102d565b508015610ecc565b5060ff60025416610ec3565b6110829150863d8811610601576105f3818361412d565b38610ead565b50346102265760c0600319360112610226576110a261401f565b6110aa614035565b6110b261404b565b91606435906001600160a01b03938483168093036106f857608435938585168095036106f85760a4359586168096036106f8576110fd926110f860ff603a541615614232565b6142d9565b73ffffffffffffffffffffffffffffffffffffffff199182603b541617603b5581603c541617603c55603d541617603d5580f35b503461022657806003193601126102265761ffff6020915416604051908152f35b503461022657806003193601126102265761ffff6020915460181c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603e5416604051908152f35b6111a636614061565b906111bb6111b38361445c565b341015614361565b8015610a53576001600160a01b0380603d541693169283149283611ca0575b80151580611c98575b611b6a575b838080611b62575b81611b59575b60405191600360208401523360408401528360608401526000608084015281151560a0840152151560c083015260c08252611230826140f5565b15611b3057935b6000901561176f57505061125a816001600160a01b03603d541630903390614786565b60005491600154906040519161126f836140d9565b6001835260005b602081106117585750611287614bd9565b506112aa846001600160a01b03603d54166001600160a01b03603b541690614a9e565b6001600160a01b03603b5416602063ffffffff60a46112cf61ffff8a60181c16614bf3565b60006001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af19081156103f15760009161171e575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa9081156103f1576000916116bf575b506113ad7fffffffffffffffff00000000000000000000000000000000000000000000000091614bf3565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c81526113f2816140d9565b604051906113ff826140d9565b600282526020820152611411846149a4565b5261141b836149a4565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa9283156103f15760009361169e575b506040805163c23ee3c360e01b815261ffff601885901c16600482015260248101869052604481018390529690876064818b5afa9687156103f157600097611655575b506001600160a01b03926115439997969594926114f161ffff936114e36040519b8c926020840152604080840152606083019061474c565b03601f1981018b528a61412d565b6040519a8b997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b019061474c565b9560648a0152600060848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b8301019401926000915b83831061160e57505050505060209391838092600f61014483015203925af180156103f1576000906115d4575b602091505b67ffffffffffffffff60405191168152f35b6020823d602011611606575b816115ed6020938361412d565b810103126102265750611601602091614771565b6115bd565b3d91506115e0565b9193959650919360208061164283601f1986600196030187526040838b5160ff81511684520151918185820152019061474c565b9701930193019092889695949293611590565b6001600160a01b039493919750916114f161168a61ffff9460403d604011611697575b611682818361412d565b810190614439565b50989294955050916114ab565b503d611678565b6116b891935060203d602011610601576105f3818361412d565b9188611468565b906020823d602011611716575b816116d96020938361412d565b8101031261022657506113ad61170f7fffffffffffffffff00000000000000000000000000000000000000000000000092614a8f565b9150611382565b3d91506116cc565b906020823d602011611750575b816117386020938361412d565b81010312610226575061174a90614771565b8761134f565b3d915061172b565b602090611763614bd9565b82828701015201611276565b9190929361177f82303387614786565b8254936001549061178e614968565b93611797614949565b506004604051926117a784614111565b8784526117b2614949565b506117cc836001600160a01b03603a5460081c1683614a9e565b6001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611b2557908a949392918a93611ae4575b509160209493916118896001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c483019061474c565b03925af18015611ad9578590611a9b575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611a90578793611a45575b509067ffffffffffffffff9161ffff604051946118f8866140bd565b168452602084015216604082015261190f846149a4565b52611919836149a4565b506001600160a01b0360355416916040519663c23ee3c360e01b885260408880611964868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611a3a5786979860209793611a14575b506119bb90604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a016149db565b03925af19081156106085780916119d7575b50602091506115c2565b90506020823d602011611a0c575b816119f26020938361412d565b810103126102265750611a06602091614771565b826119cd565b3d91506119e5565b6119bb919350611a329060403d60401161169757611682818361412d565b50929061197c565b6040513d88823e3d90fd5b9092506020813d602011611a88575b81611a616020938361412d565b81010312611a845790611a7c67ffffffffffffffff92614a8f565b9290916118dc565b8680fd5b3d9150611a54565b6040513d89823e3d90fd5b506020813d602011611ad1575b81611ab56020938361412d565b81010312611acd57611ac8600491614771565b61189a565b8480fd5b3d9150611aa8565b6040513d87823e3d90fd5b9192509293506020813d602011611b1d575b81611b036020938361412d565b81010312611b1957518993929091906020611811565b8880fd5b3d9150611af6565b6040513d8b823e3d90fd5b611b5361041e91604051928391866020840152604080840152606083019061474c565b93611237565b600091506111f6565b5060016111f0565b60405163313ce56760e01b8152602081600481855afa80156103f1578391600091611c5d575b5060ff8116906008821190838215611c3f57611bb29061048f61048a84614da6565b8092600014611c165750611bcd925061048a6104b091614da6565b146111e857606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811611c27575b5050611bcd565b611c38925061048a61048f91614dd7565b8680611c20565b6008841015611bb25750611c5861054161048a83614dd7565b611bb2565b91506020823d8211611c90575b81611c776020938361412d565b810103126102265750611c8a8391614d98565b86611b90565b3d9150611c6a565b5083156111e3565b60025460ff1693506111da565b503461022657806003193601126102265760206001600160a01b03603b5416604051908152f35b503461022657806003193601126102265760206001600160a01b03603d5416604051908152f35b503461022657806003193601126102265760206001600160a01b0360365416604051908152f35b50346102265780600319360112610226576020600154604051908152f35b5034610226578060031936011261022657611d596141ee565b60006001600160a01b03603e5473ffffffffffffffffffffffffffffffffffffffff198116603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5060031960a081360112612e1f5760043567ffffffffffffffff8111612d2657611dd790369060040161416c565b60243567ffffffffffffffff8111612e1b5736602382011215612e1b57806004013590611e03826141b3565b91611e11604051938461412d565b80835260051b810160240160208301368211611a845760248301905b828210612deb57505050506064359261ffff84168403611acd578151611e7f575050611e599150614d62565b50508134611e6657505080f35b8080806106479434905af1611e79614886565b50614c71565b611e8a839293614d62565b91505060001461221f5760018351116121b55784926001815114611fa1575b5050611ebf816020808594518301019101614cfe565b9203611f5d5761ffff92611f44611f52936001600160a01b03958693611eea85603554163314614eac565b1687526039602052611f03604435604089205414614f1d565b6084358088526037602052611f1f60ff60408a20541615614f68565b8752603760205260408720600160ff1982541617905560208082518301019101614d2c565b5050168093603d5416614fb3565b8134611e6657505080f35b606460405162461bcd60e51b815260206004820152601560248201527f57726f6e6720616d6f756e7420726563656976656400000000000000000000006044820152fd5b611fac9193506149a4565b51918251830192604081602086019503126121b157602081015167ffffffffffffffff8111611a8457846020611fe492840101614cbc565b9360408201519167ffffffffffffffff83116121ad576120079201602001614cbc565b906001600160a01b0380603d541692604051956020876024817f70a0823100000000000000000000000000000000000000000000000000000000988982523060048301525afa968715611b25578997612176575b50906020916120ab8a85603c5416926120ba604051988996879586947f57ecfd2800000000000000000000000000000000000000000000000000000000865260406004870152604486019061474c565b9184830301602485015261474c565b03925af18015611a905761213c575b60209150603d54169160246040518094819382523060048301525afa908115611ad9578591612108575b50611ebf92612101916143f7565b9138611ea9565b90506020813d602011612134575b816121236020938361412d565b810103126106f85751611ebf6120f3565b3d9150612116565b6020823d60201161216e575b816121556020938361412d565b81010312611a84576121686020926147e5565b506120c9565b3d9150612148565b919096506020823d6020116121a5575b816121936020938361412d565b810103126106f857905195602061205b565b3d9150612186565b8780fd5b8580fd5b608460405162461bcd60e51b815260206004820152602660248201527f434354503a204174206d6f7374206f6e65204d6573736167652069732073757060448201527f706f7274656400000000000000000000000000000000000000000000000000006064820152fd5b50929190805191601f1961224b612235856141b3565b94612243604051968761412d565b8086526141b3565b01845b818110612db6575050835b8251811015612b6e576122b86001600160a01b03603654168661227c84876149c7565b51604051809481927fa9e1189300000000000000000000000000000000000000000000000000000000835260206004840152602483019061474c565b0381845afa918215611a90578792612929575b50608082015190603a549161ffff60608501511690604051917fad66a5f100000000000000000000000000000000000000000000000000000000835260048301526020826024816001600160a01b038860081c165afa91821561291e578a926128ea575b50036128a6578760e0612379940151604051809581927fea63738d00000000000000000000000000000000000000000000000000000000835260206004840152602483019061474c565b03816001600160a01b038660081c165afa92831561289b578893612787575b5030608084015114908161270f575b50156126a557866124036001600160a01b03926123c486896149c7565b51836040519586809581947fc3f511c100000000000000000000000000000000000000000000000000000000835260206004840152602483019061474c565b039360081c165af18015611a9057612663575b5061ffff606082015116604082015190600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa8015611b25578990612628575b61ffff1682141590506125a857508060a01c612577576001600160a01b03165b8680604051602081019063313ce56760e01b825260048152612497816140d9565b5190845afa506124a5614886565b6020818051810103126121ad5760206124be9101614d98565b602083019182519160ff9060088282161161254f575b50506001600160a01b039061ffff6060604087015196015116935193604051956124fd8761408b565b865260208601521660408401526060830152608082015261251e82866149c7565b5261252981856149c7565b50600019811461253b57600101612259565b602485634e487b7160e01b81526011600452fd5b926104b061256f926125696001600160a01b039596614da6565b16614e3d565b9190386124d4565b602490604051907f33b960d00000000000000000000000000000000000000000000000000000000082526004820152fd5b9060209060446001600160a01b03603a5460081c169360405194859384927f1ff1e286000000000000000000000000000000000000000000000000000000008452600484015260248301525afa908115611a90578791612609575b50612476565b612622915060203d602011610601576105f3818361412d565b38612603565b506020813d60201161265b575b816126426020938361412d565b81010312611b195761265661ffff91614a8f565b612456565b3d9150612635565b3d8088833e612672818361412d565b60208282810103126121ad5781519067ffffffffffffffff8211611b195761269e929081019101614cbc565b5038612416565b608460405162461bcd60e51b815260206004820152602260248201527f546f6b656e20776173206e6f742073656e7420746f207468697320616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b90506004602061ffff60a0860151169260405192838092634d4502c960e11b82525afa8015611b2557899061274c575b61ffff91501614386123a7565b506020813d60201161277f575b816127666020938361412d565b81010312611b195761277a61ffff91614a8f565b61273f565b3d9150612759565b9092503d908189823e61279a828261412d565b6020818381010312611b195780519167ffffffffffffffff831161289757610100838301828401031261289757604051928361010081011067ffffffffffffffff610100860111176128835761010084016040526127f9818401614d98565b8452828101602081810151908601526040808201519086015261281e90606001614a8f565b606085015260808184010151608085015261283d60a082850101614a8f565b60a085015282810160c0818101519086015260e001519167ffffffffffffffff831161287f576128739390810192910101614cbc565b60e08201529138612398565b8b80fd5b60248b634e487b7160e01b81526041600452fd5b8980fd5b6040513d8a823e3d90fd5b606460405162461bcd60e51b815260206004820152601660248201527f4e6f74206120546f6b656e2042726964676520564141000000000000000000006044820152fd5b9091506020813d602011612916575b816129066020938361412d565b810103126128975751903861232f565b3d91506128f9565b6040513d8c823e3d90fd5b9091503d908188823e61293c828261412d565b60208183810103126121ad5780519067ffffffffffffffff8211611b1957610160928383830182840103126128975760405193848181011067ffffffffffffffff8287011117612883578401604052612996838301614d98565b84526129a6602084840101614e2c565b60208501526129b9604084840101614e2c565b60408501526129cc606084840101614a8f565b60608501526080838301015160808501526129eb60a084840101614771565b60a085015260c06129ff8185850101614d98565b9085015260e0838301015167ffffffffffffffff8111612b6a57612a2a908284019085850101614cbc565b60e0850152610100612a3f8185850101614e2c565b90850152610120838301015167ffffffffffffffff8111612b6a57818301601f8286860101011215612b6a57808484010151612a7a816141b3565b92612a88604051948561412d565b818452602084019281860160208460071b838a8a0101010111612b66576020818888010101935b60208460071b838a8a010101018510612ae057505050505061012084015201610140908101519082015290386122cb565b6080858489010312612b625760405180608081011067ffffffffffffffff608083011117612b4d57608091818360209301604052875181528288015183820152612b2c60408901614d98565b6040820152612b3d60608901614d98565b6060820152815201940193612aaf565b5060248f634e487b7160e01b81526041600452fd5b8e80fd5b8d80fd5b8a80fd5b50929391905061ffff6001600160a01b0393612b8f85603554163314614eac565b1684526039602052612ba8604435604086205414614f1d565b6084358085526037602052612bc460ff60408720541615614f68565b8452603760205260408420600160ff198254161790556001815103612d7257612bf7612bf085926149a4565b5192614d62565b5090809491600014612d545750806020600492603a5460081c16604051938480926315ab88c960e31b82525afa8015612d49576060928491612d2a575b5016920191825190803b15612d26576024839260405194859384927f2e1a7d4d00000000000000000000000000000000000000000000000000000000845260048401525af18015612d1b57612ceb575b50828080809351855af1612c96614886565b5015612ca7578134611e6657505080f35b606460405162461bcd60e51b815260206004820152601960248201527f776974686472617720746f206e6174697665206661696c6564000000000000006044820152fd5b67ffffffffffffffff8194929411612d07576040529138612c84565b602482634e487b7160e01b81526041600452fd5b6040513d86823e3d90fd5b8280fd5b612d43915060203d602011610601576105f3818361412d565b38612c34565b6040513d85823e3d90fd5b908392506060906040612d6d9501511692015191614fb3565b611f52565b606460405162461bcd60e51b815260206004820152601660248201527f457870656374696e67206f6e65207472616e73666572000000000000000000006044820152fd5b602090604051612dc58161408b565b87815287838201528760408201528760608201528760808201528282880101520161224e565b813567ffffffffffffffff8111611b1957602091612e1083926024369189010161416c565b815201910190611e2d565b8380fd5b5080fd5b50612e2d36614061565b909291612e3b8215156143ac565b8315610a53576001600160a01b038080603d5416921691821494856130b2575b8590831515806130aa575b612fb6575b81612fae575b86612fa5575b604051916020978895600287860152336040860152606085015287608085015281151560a0850152151560c084015260c08352612eb3836140f5565b15612f7f57505b612f0882603554169186549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e487019061474c565b93606486015260848501521660a48301523360c4830152039134905af1918215610608578092612f47575b505067ffffffffffffffff60405191168152f35b9091508282813d8311612f78575b612f5f818361412d565b810103126102265750612f7190614771565b3880612f33565b503d612f55565b612fa09061041e60405193849287840152604080840152606083019061474c565b612eba565b60019650612e77565b859150612e71565b905060405163313ce56760e01b8152602081600481875afa908115611a3a57908291879161306f575b5060ff8116906008821190838215613051576130019061048f61048a84614da6565b8092600014613028575061301c925061048a6104b091614da6565b03610978578590612e6b565b9192600811613039575b505061301c565b61304a925061048a61048f91614dd7565b3880613032565b6008841015613001575061306a61054161048a83614dd7565b613001565b9150506020813d82116130a2575b8161308a6020938361412d565b810103126121b15761309c8291614d98565b38612fdf565b3d915061307d565b508115612e66565b60025460ff169550612e5b565b5034610226576020600319360112610226576004358015158103612e1f57610647906130e96141ee565b61433a565b50346102265760406003193601126102265760243590811515820361022657602061311b8360043561462b565b604051908152f35b50602090816003193601126102265760043561313e8161445c565b8034106134855761314f90346143f7565b908115613441576001600160a01b0380603d54161580613435575b808061342e575b81613425575b60405191868884015233604084015286606084015286608084015281151560a0840152151560c083015260c082526131ae826140f5565b156133fd57905b80603a5460081c16936040516384acd1bb60e01b81528781600481895afa908115611a90578391899189916133e0575b50600460405180948193631a90a21960e01b8352165afa908115611a905787916133b1575b506132149161444f565b86865491604051968780927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528661ffff96878160181c16600485015260281c1660248301528a60448301526080606483015281613276608482018a61474c565b03925af1948515611a3a5790879392918796613375575b50613296614968565b9187549161331e8184169280603a5460081c16604051906132b6826140bd565b8582528982015267ffffffffffffffff809b1660408201526132d7876149a4565b526132e1866149a4565b506132eb896145b3565b938160355416966001546040519b8c9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a016149db565b03925af192831561060857809361333b575b505060405191168152f35b909192508382813d831161336e575b613354818361412d565b81010312610226575061336690614771565b903880613330565b503d61334a565b84819394959297503d83116133aa575b61338f818361412d565b810103126121b157906133a3879392614771565b943861328d565b503d613385565b90508781813d83116133d9575b6133c8818361412d565b81010312611a84575161321461320a565b503d6133be565b6133f79150823d8411610601576105f3818361412d565b386131e5565b61341f61041e91604051928391878a840152604080840152606083019061474c565b906131b5565b60019150613177565b5084613171565b5060ff6002541661316a565b6064846040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084846040519062461bcd60e51b82526004820152603660248201527f53706f6b653a3a6465706f736974436f6c6c61746572616c4e61746976653a4960448201527f6e73756666696369656e742076616c75652073656e74000000000000000000006064820152fd5b506134f936614061565b906135096111b38395949561445c565b8015610a53576001600160a01b0380603d541694169384149384613f95575b80151580613f8d575b613e5e575b848080613e56575b81613e4e575b6040519186602084015233604084015283606084015286608084015281151560a0840152151560c083015260c0825261357c826140f5565b15613e2557945b15613a9e57506135a2816001600160a01b03603d541630903390614786565b82549160015490604051916135b6836140d9565b60018352855b60208110613a8757506135cd614bd9565b506135f0846001600160a01b03603d54166001600160a01b03603b541690614a9e565b6001600160a01b03603b5416602063ffffffff60a461361561ffff8a60181c16614bf3565b8a6001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af1908115611a90578791613a4d575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa90811561289b5788916139ee575b506136f07fffffffffffffffff00000000000000000000000000000000000000000000000091614bf3565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c8152613735816140d9565b60405190613742826140d9565b600282526020820152613754846149a4565b5261375e836149a4565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa92831561289b5788936139cd575b506040805163c23ee3c360e01b815261ffff601885901c166004820152602481018690526044810183905299908a6064818b5afa968715611b255760449a8a9861398e575b5061387b9a509261ffff9197969594926138296001600160a01b03956114e36040519b8c926020840152604080840152606083019061474c565b6040519b8c997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b019061474c565b9560648a01528a60848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b83010194019288915b83831061394757505050505060209391838092600f61014483015203925af190811561060857809161390a575b506020915067ffffffffffffffff60405191168152f35b90506020823d60201161393f575b816139256020938361412d565b810103126102265750613939602091614771565b386138f3565b3d9150613918565b9193959650919360208061397b83601f1986600196030187526040838b5160ff81511684520151918185820152019061474c565b97019301930190928996959492936138c6565b61ffff939198506001600160a01b039594926138296139be61387b9360403d60401161169757611682818361412d565b509a93955050929495506137ef565b6139e791935060203d602011610601576105f3818361412d565b91386137aa565b90506020813d602011613a45575b81613a096020938361412d565b810103126121ad576136f0613a3e7fffffffffffffffff00000000000000000000000000000000000000000000000092614a8f565b91506136c5565b3d91506139fc565b90506020813d602011613a7f575b81613a686020938361412d565b81010312611a8457613a7990614771565b38613693565b3d9150613a5b565b602090613a92614bd9565b828287010152016135bc565b929193613aad82303387614786565b82549360015490613abc614968565b93613ac5614949565b50600460405192613ad584614111565b878452613ae0614949565b50613afa836001600160a01b03603a5460081c1683614a9e565b6001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611b2557908a949392918a93613de8575b50916020949391613bb76001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c483019061474c565b03925af18015611ad9578590613dae575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611a90578793613d67575b509067ffffffffffffffff9161ffff60405194613c26866140bd565b1684526020840152166040820152613c3d846149a4565b52613c47836149a4565b506001600160a01b0360355416916040519663c23ee3c360e01b885260408880613c92868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611a3a5786979860209793613d41575b50613ce990604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a016149db565b03925af1908115610608578091613d045750602091506115c2565b90506020823d602011613d39575b81613d1f6020938361412d565b810103126102265750613d33602091614771565b386119cd565b3d9150613d12565b613ce9919350613d5f9060403d60401161169757611682818361412d565b509290613caa565b9092506020813d602011613da6575b81613d836020938361412d565b81010312611a845790613d9e67ffffffffffffffff92614a8f565b929091613c0a565b3d9150613d76565b506020813d602011613de0575b81613dc86020938361412d565b81010312611acd57613ddb600491614771565b613bc8565b3d9150613dbb565b9192509293506020813d602011613e1d575b81613e076020938361412d565b81010312611b1957518993929091906020613b3f565b3d9150613dfa565b613e4861041e91604051928391866020840152604080840152606083019061474c565b94613583565b859150613544565b50600161353e565b60405163313ce56760e01b8152602081600481855afa908115611ad9579083918691613f52575b5060ff8116906008821190838215613f3457613ea79061048f61048a84614da6565b8092600014613f0b5750613ec2925061048a6104b091614da6565b1461353657606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811613f1c575b5050613ec2565b613f2d925061048a61048f91614dd7565b3880613f15565b6008841015613ea75750613f4d61054161048a83614dd7565b613ea7565b9150506020813d8211613f85575b81613f6d6020938361412d565b81010312611acd57613f7f8391614d98565b38613e85565b3d9150613f60565b508415613531565b60025460ff169450613528565b50346102265760206003193601126102265760ff60406020926004358152603784522054166040519015158152f35b503461022657604060031936011261022657610647613fee61401f565b613ff6614035565b9061427d565b5034610226576020600319360112610226576140166141ee565b60043560015580f35b600435906001600160a01b03821682036106f857565b602435906001600160a01b03821682036106f857565b604435906001600160a01b03821682036106f857565b60031960609101126106f8576004356001600160a01b03811681036106f857906024359060443590565b60a0810190811067ffffffffffffffff8211176140a757604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176140a757604052565b6040810190811067ffffffffffffffff8211176140a757604052565b60e0810190811067ffffffffffffffff8211176140a757604052565b6020810190811067ffffffffffffffff8211176140a757604052565b90601f601f19910116810190811067ffffffffffffffff8211176140a757604052565b67ffffffffffffffff81116140a757601f01601f191660200190565b81601f820112156106f85780359061418382614150565b92614191604051948561412d565b828452602083830101116106f857816000926020809301838601378301015290565b67ffffffffffffffff81116140a75760051b60200190565b60031960609101126106f857600435906024359060443580151581036106f85790565b6001600160a01b03603e5416330361420257565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b1561423957565b606460405162461bcd60e51b815260206004820152600360248201527f57524900000000000000000000000000000000000000000000000000000000006044820152fd5b90600160ff19603a5461429360ff821615614232565b1617603a556001600160a01b03908173ffffffffffffffffffffffffffffffffffffffff1993168360355416176035551681603654161760365533906038541617603855565b916142f391926142ee60ff603a541615614232565b61427d565b7fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff00603a549260081b16911617603a55565b6001600160a01b03603c54161561435e5760ff60ff19600254169115151617600255565b50565b1561436857565b606460405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e742076616c75652073656e740000000000000000006044820152fd5b156143b357565b606460405162461bcd60e51b815260206004820152601e60248201527f4e6f6e2d7a65726f20636f7374466f7252657475726e44656c697665727900006044820152fd5b9190820391821161440457565b634e487b7160e01b600052601160045260246000fd5b908160209103126106f857516001600160a01b03811681036106f85790565b91908260409103126106f8576020825192015190565b9190820180921161440457565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152909392916001600160a01b039083908390831681806064810103915afa9182156145a9578592614589575b5080603a5460081c16835180916384acd1bb60e01b825281600460209485935afa90811561457f579082918891614562575b506004865180958193631a90a21960e01b8352165afa93841561455957508593614522575b505061451f92935061444f565b90565b9080929350813d8311614552575b61453a818361412d565b81010312612e1b5761451f9293505190839238614512565b503d614530565b513d87823e3d90fd5b6145799150823d8411610601576105f3818361412d565b386144ed565b85513d89823e3d90fd5b6145a1919250833d851161169757611682818361412d565b5090386144bb565b83513d87823e3d90fd5b6035546000546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301949094526044820152919082906001600160a01b031681806064810103915afa9081156103f15760009161460f575090565b614627915060403d811161169757611682818361412d565b5090565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152939092916001600160a01b03919083908690841681806064810103915afa94851561471c5784956146fc575b506146965750505090565b80603a95949293955460081c16835180916384acd1bb60e01b825281600460209485935afa90811561457f57908291889161456257506004865180958193631a90a21960e01b8352165afa9384156145595750859361452257505061451f92935061444f565b614714919550833d851161169757611682818361412d565b50933861468b565b50505051903d90823e3d90fd5b60005b83811061473c5750506000910152565b818101518382015260200161472c565b90601f19601f60209361476a81518092818752878088019101614729565b0116010190565b519067ffffffffffffffff821682036106f857565b90926147e393604051937f23b872dd0000000000000000000000000000000000000000000000000000000060208601526001600160a01b0380921660248601521660448401526064830152606482526147de8261408b565b6147f2565b565b519081151582036106f857565b6000806001600160a01b0361481c93169360208151910182865af1614815614886565b90836148b6565b8051908115159182614863575b50506148325750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b81925090602091810103126106f857602061487e91016147e5565b153880614829565b3d156148b1573d9061489782614150565b916148a5604051938461412d565b82523d6000602084013e565b606090565b906148f557508051156148cb57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580614940575b614906575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156148fe565b60405190614956826140bd565b60006040838281528260208201520152565b60405190614975826140d9565b600182528160005b6020908181101561499f57602091614993614949565b9082850101520161497d565b505050565b8051156149b15760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156149b15760209160051b010190565b9591949896979390929861ffff80941687526001600160a01b0395866020911681890152614a166101009b8c60409d8e8c01528a019061474c565b91606093848a0152608089015287820360a08901528080855193848152019401926000905b838210614a5b57505050505060e09495969750961660c085015216910152565b845180518816875280840151878501528d015167ffffffffffffffff168d8701529485019493820193600190910190614a3b565b519061ffff821682036106f857565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000602082018181526001600160a01b038516602484015260448084019690965294825294939092614af360648561412d565b6001600160a01b0390818416600080809588519082855af190614b14614886565b82614b9f575b5081614b94575b5015614b31575b50505050509050565b60405196602088015216602486015280604486015260448552608085019085821067ffffffffffffffff831117614b805750614b7593946147de91604052826147f2565b803880808080614b28565b80634e487b7160e01b602492526041600452fd5b90503b151538614b21565b80519192508115918215614bb7575b50509038614b1a565b8192509060209181010312611acd576020614bd291016147e5565b3880614bae565b60405190614be6826140d9565b6060602083600081520152565b61ffff1660028103614c055750600090565b60068103614c135750600190565b60178103614c215750600390565b601803614c2d57600290565b606460405162461bcd60e51b815260206004820152601160248201527f57726f6e67204343545020446f6d61696e0000000000000000000000000000006044820152fd5b15614c7857565b606460405162461bcd60e51b815260206004820152600d60248201527f726566756e64206661696c6564000000000000000000000000000000000000006044820152fd5b81601f820112156106f8578051614cd281614150565b92614ce0604051948561412d565b818452602082840101116106f85761451f9160208085019101614729565b9190916040818403126106f857805192602082015167ffffffffffffffff81116106f85761451f9201614cbc565b908160609103126106f85780516001600160a01b03811681036106f8579161451f6040614d5b602085016147e5565b93016147e5565b614d91614d806001600160a01b039260208082518301019101614cfe565b905060208082518301019101614d2c565b9190921692565b519060ff821682036106f857565b60ff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89116019060ff821161440457565b60ff166008039060ff821161440457565b60ff16604d811161440457600a0a90565b8181029291811591840414171561440457565b8115614e16570490565b634e487b7160e01b600052601260045260246000fd5b519063ffffffff821682036106f857565b8015614ea657600190602081108216604e8210831617614e9e578190600a925b808211614e7557505081600019048111614404570290565b90928060001904811161440457818416614e95575b800292811c90614e5d565b80920291614e8a565b9050600a0a90565b50600190565b15614eb357565b608460405162461bcd60e51b815260206004820152602260248201527f4d73672e73656e646572206973206e6f7420576f726d686f6c652052656c617960448201527f65720000000000000000000000000000000000000000000000000000000000006064820152fd5b15614f2457565b606460405162461bcd60e51b815260206004820152601560248201527f4e6f7420726567697374657265642073656e64657200000000000000000000006044820152fd5b15614f6f57565b606460405162461bcd60e51b815260206004820152601960248201527f4d65737361676520616c72656164792070726f636573736564000000000000006044820152fd5b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b039290921660248301526044808301939093529181526147e3916147de60648361412d56fea2646970667358221220c01bb022dcabde12b63571b0b163c202062a26bed1b2436335bad59d81d8542364736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000200000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa58500000000000000000000000027428dd2d3dd32a4d7f7c497eaaa23130d89491100000000000000000000000000000000000000000000000000000000000000170000000000000000000000001e3f1f1ca8c62aabcb3b78d87223e988dfa3780e0000000000000000000000000a992d191deec32afe36203ad87d7d289a738f81000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Deployed Bytecode
0x60806040526004361015610018575b361561001657005b005b6000803560e01c80630c59272014613ffc57806314f29f8814613fd1578063180f6cc214613fa25780631a74856b146134ef5780632fd8f7e6146131235780633757e53d146130ee578063378717b7146130bf5780633d3a066f14612e23578063529dca3214611da9578063715018a614611d405780637816976814611d2257806384acd1bb14611cfb57806389a3027114611cd45780638c1577c914611cad5780638cd2e0c71461119d5780638da5cb5b14611176578063929f5840146111525780639a8a059214611131578063a489e28a14611088578063a7b4629e14610e68578063a8cbeb2914610a97578063c1bce0b714610777578063c6328a461461074d578063d8c71a9214610724578063da25b725146106fd578063dd12d68b1461064a578063decfba6114610614578063dfe6f20014610273578063e1cc30ee1461024c578063e8dfd508146102295763f2fde38b14610179575061000e565b346102265760206003193601126102265761019261401f565b61019a6141ee565b6001600160a01b038091169081156101f557603e548273ffffffffffffffffffffffffffffffffffffffff19821617603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a380f35b602483604051907f1e4fbdf70000000000000000000000000000000000000000000000000000000082526004820152fd5b80fd5b503461022657806003193601126102265760ff6020915460101c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603c5416604051908152f35b5061027d366141cb565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa918215610608579084926105db575b50169080156105975782603d541682148061058b575b82151580610583575b610443575b808697819796979261043a575b81610431575b6040519460018887015233604087015260608601521515608085015281151560a0850152151560c084015260c08352610327836140f5565b156103fd57505b61037d8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e487019061474c565b93606486015260848501521660a48301523360c4830152039134905af19081156103f1576000916103bc575b5067ffffffffffffffff60405191168152f35b908282813d83116103ea575b6103d2818361412d565b8101031261022657506103e490614771565b386103a9565b503d6103c8565b6040513d6000823e3d90fd5b61042c9061041e60405193849287840152604080840152606083019061474c565b03601f19810183528261412d565b61032e565b600191506102ef565b600092506102e9565b60405163313ce56760e01b81528681600481875afa80156103f157839160009161054c575b5060ff8116906008821190838215610528576104959061048f61048a84614da6565b614de8565b90614e0c565b80926000146104ff57506104b6925061048a6104b091614da6565b90614df9565b146102dc576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610510575b50506104b6565b610521925061048a61048f91614dd7565b3880610509565b6008841015610495575061054761054161048a83614dd7565b85614df9565b610495565b91508782813d831161057c575b610563818361412d565b8101031261022657506105768391614d98565b38610468565b503d610559565b5080156102d7565b5060ff600254166102ce565b6064856040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6105fb9150863d8811610601575b6105f3818361412d565b81019061441a565b386102b8565b503d6105e9565b604051903d90823e3d90fd5b50346102265760606003193601126102265761064761063161401f565b610639614035565b61064161404b565b916142d9565b80f35b50346102265760406003193601126102265760043561ffff81168091036106f8576001600160a01b0360385416330361068f5781526039602052602435604082205580f35b608460405162461bcd60e51b8152602060048201526024808201527f4e6f7420616c6c6f77656420746f20736574207265676973746572656420736560448201527f6e646572000000000000000000000000000000000000000000000000000000006064820152fd5b600080fd5b503461022657806003193601126102265760206001600160a01b0360355416604051908152f35b50346102265780600319360112610226576001600160a01b036020915460281c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603a5460081c16604051908152f35b61078036614061565b909161078d8215156143ac565b8215610a53576001600160a01b038080603d541692169182149384610a46575b849083151580610a3e575b610907575b816108fe575b856108f5575b60405191602096879560018786015233604086015260608501526000608085015281151560a0850152151560c084015260c08352610806836140f5565b156108cf57505b61085c8260355416916000549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e487019061474c565b93606486015260848501521660a48301523360c4830152039134905af19081156103f15760009161089a575067ffffffffffffffff60405191168152f35b908282813d83116108c8575b6108b0818361412d565b8101031261022657506108c290614771565b826103a9565b503d6108a6565b6108f09061041e60405193849287840152604080840152606083019061474c565b61080d565b600195506107c9565b600091506107c3565b905060405163313ce56760e01b8152602081600481875afa80156103f1578291600091610a03575b5060ff81169060088211908382156109e5576109519061048f61048a84614da6565b80926000146109bc575061096c925061048a6104b091614da6565b036109785784906107bd565b606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b91926008116109cd575b505061096c565b6109de925061048a61048f91614dd7565b87806109c6565b600884101561095157506109fe61054161048a83614dd7565b610951565b91506020823d8211610a36575b81610a1d6020938361412d565b810103126102265750610a308291614d98565b8761092f565b3d9150610a10565b5081156107b8565b60025460ff1694506107ad565b606460405162461bcd60e51b815260206004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b50602090816003193601126102265750600435610ab38161445c565b803410610dfe57610ac490346143f7565b8015610dba576001600160a01b039081603d54161580610dae575b8080610da6575b81610d9d575b60405191600387840152336040840152600060608401526000608084015281151560a0840152151560c083015260c08252610b26826140f5565b15610d76575b82603a5460081c16916040516384acd1bb60e01b81528681600481875afa9081156103f15785918891600091610d59575b50600460405180948193631a90a21960e01b8352165afa9081156103f157600091610d2a575b50610b8d9161444f565b928560005494604051948580927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528561ffff998a8160181c16600485015260281c166024830152600060448301526080606483015281610bf2608482018961474c565b03925af19384156103f1578693600095610ced575b50610c10614968565b9160005491610c998184169280603a5460081c1660405190610c31826140bd565b8582528982015267ffffffffffffffff809a166040820152610c52876149a4565b52610c5c866149a4565b50610c668a6145b3565b938160355416966001546040519c8d9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a016149db565b03925af19182156103f157600092610cb6575b5060405191168152f35b90918382813d8311610ce6575b610ccd818361412d565b810103126102265750610cdf90614771565b9038610cac565b503d610cc3565b91929382819692963d8311610d23575b610d07818361412d565b81010312610226575090610d1c869392614771565b9338610c07565b503d610cfd565b908782813d8311610d52575b610d40818361412d565b81010312610226575051610b8d610b83565b503d610d36565b610d709150823d8411610601576105f3818361412d565b38610b5d565b610d9861041e916040519283918589840152604080840152606083019061474c565b610b2c565b60019150610aec565b506000610ae6565b5060ff60025416610adf565b6064836040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084836040519062461bcd60e51b82526004820152602a60248201527f53706f6b653a3a72657061794e61746976653a496e73756666696369656e742060448201527f76616c75652073656e74000000000000000000000000000000000000000000006064820152fd5b50610e72366141cb565b9291603a5490604051936315ab88c960e31b855260209485816004816001600160a01b03809860081c165afa9182156106085790849261106b575b50169080156105975782603d541682148061105f575b82151580611057575b610f2e575b8086978197969792610f25575b81610f1c575b6040519460028887015233604087015260608601521515608085015281151560a0850152151560c084015260c08352610327836140f5565b60019150610ee4565b60009250610ede565b60405163313ce56760e01b81528681600481875afa80156103f1578391600091611020575b5060ff811690600882119083821561100257610f759061048f61048a84614da6565b8092600014610fd95750610f90925061048a6104b091614da6565b14610ed1576064866040519062461bcd60e51b82526004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811610fea575b5050610f90565b610ffb925061048a61048f91614dd7565b3880610fe3565b6008841015610f75575061101b61054161048a83614dd7565b610f75565b91508782813d8311611050575b611037818361412d565b81010312610226575061104a8391614d98565b38610f53565b503d61102d565b508015610ecc565b5060ff60025416610ec3565b6110829150863d8811610601576105f3818361412d565b38610ead565b50346102265760c0600319360112610226576110a261401f565b6110aa614035565b6110b261404b565b91606435906001600160a01b03938483168093036106f857608435938585168095036106f85760a4359586168096036106f8576110fd926110f860ff603a541615614232565b6142d9565b73ffffffffffffffffffffffffffffffffffffffff199182603b541617603b5581603c541617603c55603d541617603d5580f35b503461022657806003193601126102265761ffff6020915416604051908152f35b503461022657806003193601126102265761ffff6020915460181c16604051908152f35b503461022657806003193601126102265760206001600160a01b03603e5416604051908152f35b6111a636614061565b906111bb6111b38361445c565b341015614361565b8015610a53576001600160a01b0380603d541693169283149283611ca0575b80151580611c98575b611b6a575b838080611b62575b81611b59575b60405191600360208401523360408401528360608401526000608084015281151560a0840152151560c083015260c08252611230826140f5565b15611b3057935b6000901561176f57505061125a816001600160a01b03603d541630903390614786565b60005491600154906040519161126f836140d9565b6001835260005b602081106117585750611287614bd9565b506112aa846001600160a01b03603d54166001600160a01b03603b541690614a9e565b6001600160a01b03603b5416602063ffffffff60a46112cf61ffff8a60181c16614bf3565b60006001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af19081156103f15760009161171e575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa9081156103f1576000916116bf575b506113ad7fffffffffffffffff00000000000000000000000000000000000000000000000091614bf3565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c81526113f2816140d9565b604051906113ff826140d9565b600282526020820152611411846149a4565b5261141b836149a4565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa9283156103f15760009361169e575b506040805163c23ee3c360e01b815261ffff601885901c16600482015260248101869052604481018390529690876064818b5afa9687156103f157600097611655575b506001600160a01b03926115439997969594926114f161ffff936114e36040519b8c926020840152604080840152606083019061474c565b03601f1981018b528a61412d565b6040519a8b997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b019061474c565b9560648a0152600060848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b8301019401926000915b83831061160e57505050505060209391838092600f61014483015203925af180156103f1576000906115d4575b602091505b67ffffffffffffffff60405191168152f35b6020823d602011611606575b816115ed6020938361412d565b810103126102265750611601602091614771565b6115bd565b3d91506115e0565b9193959650919360208061164283601f1986600196030187526040838b5160ff81511684520151918185820152019061474c565b9701930193019092889695949293611590565b6001600160a01b039493919750916114f161168a61ffff9460403d604011611697575b611682818361412d565b810190614439565b50989294955050916114ab565b503d611678565b6116b891935060203d602011610601576105f3818361412d565b9188611468565b906020823d602011611716575b816116d96020938361412d565b8101031261022657506113ad61170f7fffffffffffffffff00000000000000000000000000000000000000000000000092614a8f565b9150611382565b3d91506116cc565b906020823d602011611750575b816117386020938361412d565b81010312610226575061174a90614771565b8761134f565b3d915061172b565b602090611763614bd9565b82828701015201611276565b9190929361177f82303387614786565b8254936001549061178e614968565b93611797614949565b506004604051926117a784614111565b8784526117b2614949565b506117cc836001600160a01b03603a5460081c1683614a9e565b6001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611b2557908a949392918a93611ae4575b509160209493916118896001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c483019061474c565b03925af18015611ad9578590611a9b575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611a90578793611a45575b509067ffffffffffffffff9161ffff604051946118f8866140bd565b168452602084015216604082015261190f846149a4565b52611919836149a4565b506001600160a01b0360355416916040519663c23ee3c360e01b885260408880611964868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611a3a5786979860209793611a14575b506119bb90604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a016149db565b03925af19081156106085780916119d7575b50602091506115c2565b90506020823d602011611a0c575b816119f26020938361412d565b810103126102265750611a06602091614771565b826119cd565b3d91506119e5565b6119bb919350611a329060403d60401161169757611682818361412d565b50929061197c565b6040513d88823e3d90fd5b9092506020813d602011611a88575b81611a616020938361412d565b81010312611a845790611a7c67ffffffffffffffff92614a8f565b9290916118dc565b8680fd5b3d9150611a54565b6040513d89823e3d90fd5b506020813d602011611ad1575b81611ab56020938361412d565b81010312611acd57611ac8600491614771565b61189a565b8480fd5b3d9150611aa8565b6040513d87823e3d90fd5b9192509293506020813d602011611b1d575b81611b036020938361412d565b81010312611b1957518993929091906020611811565b8880fd5b3d9150611af6565b6040513d8b823e3d90fd5b611b5361041e91604051928391866020840152604080840152606083019061474c565b93611237565b600091506111f6565b5060016111f0565b60405163313ce56760e01b8152602081600481855afa80156103f1578391600091611c5d575b5060ff8116906008821190838215611c3f57611bb29061048f61048a84614da6565b8092600014611c165750611bcd925061048a6104b091614da6565b146111e857606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811611c27575b5050611bcd565b611c38925061048a61048f91614dd7565b8680611c20565b6008841015611bb25750611c5861054161048a83614dd7565b611bb2565b91506020823d8211611c90575b81611c776020938361412d565b810103126102265750611c8a8391614d98565b86611b90565b3d9150611c6a565b5083156111e3565b60025460ff1693506111da565b503461022657806003193601126102265760206001600160a01b03603b5416604051908152f35b503461022657806003193601126102265760206001600160a01b03603d5416604051908152f35b503461022657806003193601126102265760206001600160a01b0360365416604051908152f35b50346102265780600319360112610226576020600154604051908152f35b5034610226578060031936011261022657611d596141ee565b60006001600160a01b03603e5473ffffffffffffffffffffffffffffffffffffffff198116603e55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b5060031960a081360112612e1f5760043567ffffffffffffffff8111612d2657611dd790369060040161416c565b60243567ffffffffffffffff8111612e1b5736602382011215612e1b57806004013590611e03826141b3565b91611e11604051938461412d565b80835260051b810160240160208301368211611a845760248301905b828210612deb57505050506064359261ffff84168403611acd578151611e7f575050611e599150614d62565b50508134611e6657505080f35b8080806106479434905af1611e79614886565b50614c71565b611e8a839293614d62565b91505060001461221f5760018351116121b55784926001815114611fa1575b5050611ebf816020808594518301019101614cfe565b9203611f5d5761ffff92611f44611f52936001600160a01b03958693611eea85603554163314614eac565b1687526039602052611f03604435604089205414614f1d565b6084358088526037602052611f1f60ff60408a20541615614f68565b8752603760205260408720600160ff1982541617905560208082518301019101614d2c565b5050168093603d5416614fb3565b8134611e6657505080f35b606460405162461bcd60e51b815260206004820152601560248201527f57726f6e6720616d6f756e7420726563656976656400000000000000000000006044820152fd5b611fac9193506149a4565b51918251830192604081602086019503126121b157602081015167ffffffffffffffff8111611a8457846020611fe492840101614cbc565b9360408201519167ffffffffffffffff83116121ad576120079201602001614cbc565b906001600160a01b0380603d541692604051956020876024817f70a0823100000000000000000000000000000000000000000000000000000000988982523060048301525afa968715611b25578997612176575b50906020916120ab8a85603c5416926120ba604051988996879586947f57ecfd2800000000000000000000000000000000000000000000000000000000865260406004870152604486019061474c565b9184830301602485015261474c565b03925af18015611a905761213c575b60209150603d54169160246040518094819382523060048301525afa908115611ad9578591612108575b50611ebf92612101916143f7565b9138611ea9565b90506020813d602011612134575b816121236020938361412d565b810103126106f85751611ebf6120f3565b3d9150612116565b6020823d60201161216e575b816121556020938361412d565b81010312611a84576121686020926147e5565b506120c9565b3d9150612148565b919096506020823d6020116121a5575b816121936020938361412d565b810103126106f857905195602061205b565b3d9150612186565b8780fd5b8580fd5b608460405162461bcd60e51b815260206004820152602660248201527f434354503a204174206d6f7374206f6e65204d6573736167652069732073757060448201527f706f7274656400000000000000000000000000000000000000000000000000006064820152fd5b50929190805191601f1961224b612235856141b3565b94612243604051968761412d565b8086526141b3565b01845b818110612db6575050835b8251811015612b6e576122b86001600160a01b03603654168661227c84876149c7565b51604051809481927fa9e1189300000000000000000000000000000000000000000000000000000000835260206004840152602483019061474c565b0381845afa918215611a90578792612929575b50608082015190603a549161ffff60608501511690604051917fad66a5f100000000000000000000000000000000000000000000000000000000835260048301526020826024816001600160a01b038860081c165afa91821561291e578a926128ea575b50036128a6578760e0612379940151604051809581927fea63738d00000000000000000000000000000000000000000000000000000000835260206004840152602483019061474c565b03816001600160a01b038660081c165afa92831561289b578893612787575b5030608084015114908161270f575b50156126a557866124036001600160a01b03926123c486896149c7565b51836040519586809581947fc3f511c100000000000000000000000000000000000000000000000000000000835260206004840152602483019061474c565b039360081c165af18015611a9057612663575b5061ffff606082015116604082015190600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa8015611b25578990612628575b61ffff1682141590506125a857508060a01c612577576001600160a01b03165b8680604051602081019063313ce56760e01b825260048152612497816140d9565b5190845afa506124a5614886565b6020818051810103126121ad5760206124be9101614d98565b602083019182519160ff9060088282161161254f575b50506001600160a01b039061ffff6060604087015196015116935193604051956124fd8761408b565b865260208601521660408401526060830152608082015261251e82866149c7565b5261252981856149c7565b50600019811461253b57600101612259565b602485634e487b7160e01b81526011600452fd5b926104b061256f926125696001600160a01b039596614da6565b16614e3d565b9190386124d4565b602490604051907f33b960d00000000000000000000000000000000000000000000000000000000082526004820152fd5b9060209060446001600160a01b03603a5460081c169360405194859384927f1ff1e286000000000000000000000000000000000000000000000000000000008452600484015260248301525afa908115611a90578791612609575b50612476565b612622915060203d602011610601576105f3818361412d565b38612603565b506020813d60201161265b575b816126426020938361412d565b81010312611b195761265661ffff91614a8f565b612456565b3d9150612635565b3d8088833e612672818361412d565b60208282810103126121ad5781519067ffffffffffffffff8211611b195761269e929081019101614cbc565b5038612416565b608460405162461bcd60e51b815260206004820152602260248201527f546f6b656e20776173206e6f742073656e7420746f207468697320616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b90506004602061ffff60a0860151169260405192838092634d4502c960e11b82525afa8015611b2557899061274c575b61ffff91501614386123a7565b506020813d60201161277f575b816127666020938361412d565b81010312611b195761277a61ffff91614a8f565b61273f565b3d9150612759565b9092503d908189823e61279a828261412d565b6020818381010312611b195780519167ffffffffffffffff831161289757610100838301828401031261289757604051928361010081011067ffffffffffffffff610100860111176128835761010084016040526127f9818401614d98565b8452828101602081810151908601526040808201519086015261281e90606001614a8f565b606085015260808184010151608085015261283d60a082850101614a8f565b60a085015282810160c0818101519086015260e001519167ffffffffffffffff831161287f576128739390810192910101614cbc565b60e08201529138612398565b8b80fd5b60248b634e487b7160e01b81526041600452fd5b8980fd5b6040513d8a823e3d90fd5b606460405162461bcd60e51b815260206004820152601660248201527f4e6f74206120546f6b656e2042726964676520564141000000000000000000006044820152fd5b9091506020813d602011612916575b816129066020938361412d565b810103126128975751903861232f565b3d91506128f9565b6040513d8c823e3d90fd5b9091503d908188823e61293c828261412d565b60208183810103126121ad5780519067ffffffffffffffff8211611b1957610160928383830182840103126128975760405193848181011067ffffffffffffffff8287011117612883578401604052612996838301614d98565b84526129a6602084840101614e2c565b60208501526129b9604084840101614e2c565b60408501526129cc606084840101614a8f565b60608501526080838301015160808501526129eb60a084840101614771565b60a085015260c06129ff8185850101614d98565b9085015260e0838301015167ffffffffffffffff8111612b6a57612a2a908284019085850101614cbc565b60e0850152610100612a3f8185850101614e2c565b90850152610120838301015167ffffffffffffffff8111612b6a57818301601f8286860101011215612b6a57808484010151612a7a816141b3565b92612a88604051948561412d565b818452602084019281860160208460071b838a8a0101010111612b66576020818888010101935b60208460071b838a8a010101018510612ae057505050505061012084015201610140908101519082015290386122cb565b6080858489010312612b625760405180608081011067ffffffffffffffff608083011117612b4d57608091818360209301604052875181528288015183820152612b2c60408901614d98565b6040820152612b3d60608901614d98565b6060820152815201940193612aaf565b5060248f634e487b7160e01b81526041600452fd5b8e80fd5b8d80fd5b8a80fd5b50929391905061ffff6001600160a01b0393612b8f85603554163314614eac565b1684526039602052612ba8604435604086205414614f1d565b6084358085526037602052612bc460ff60408720541615614f68565b8452603760205260408420600160ff198254161790556001815103612d7257612bf7612bf085926149a4565b5192614d62565b5090809491600014612d545750806020600492603a5460081c16604051938480926315ab88c960e31b82525afa8015612d49576060928491612d2a575b5016920191825190803b15612d26576024839260405194859384927f2e1a7d4d00000000000000000000000000000000000000000000000000000000845260048401525af18015612d1b57612ceb575b50828080809351855af1612c96614886565b5015612ca7578134611e6657505080f35b606460405162461bcd60e51b815260206004820152601960248201527f776974686472617720746f206e6174697665206661696c6564000000000000006044820152fd5b67ffffffffffffffff8194929411612d07576040529138612c84565b602482634e487b7160e01b81526041600452fd5b6040513d86823e3d90fd5b8280fd5b612d43915060203d602011610601576105f3818361412d565b38612c34565b6040513d85823e3d90fd5b908392506060906040612d6d9501511692015191614fb3565b611f52565b606460405162461bcd60e51b815260206004820152601660248201527f457870656374696e67206f6e65207472616e73666572000000000000000000006044820152fd5b602090604051612dc58161408b565b87815287838201528760408201528760608201528760808201528282880101520161224e565b813567ffffffffffffffff8111611b1957602091612e1083926024369189010161416c565b815201910190611e2d565b8380fd5b5080fd5b50612e2d36614061565b909291612e3b8215156143ac565b8315610a53576001600160a01b038080603d5416921691821494856130b2575b8590831515806130aa575b612fb6575b81612fae575b86612fa5575b604051916020978895600287860152336040860152606085015287608085015281151560a0850152151560c084015260c08352612eb3836140f5565b15612f7f57505b612f0882603554169186549560015494604051978896879586956312d729bd60e21b875261ffff94858560181c1660048901528460281c16602488015260e0604488015260e487019061474c565b93606486015260848501521660a48301523360c4830152039134905af1918215610608578092612f47575b505067ffffffffffffffff60405191168152f35b9091508282813d8311612f78575b612f5f818361412d565b810103126102265750612f7190614771565b3880612f33565b503d612f55565b612fa09061041e60405193849287840152604080840152606083019061474c565b612eba565b60019650612e77565b859150612e71565b905060405163313ce56760e01b8152602081600481875afa908115611a3a57908291879161306f575b5060ff8116906008821190838215613051576130019061048f61048a84614da6565b8092600014613028575061301c925061048a6104b091614da6565b03610978578590612e6b565b9192600811613039575b505061301c565b61304a925061048a61048f91614dd7565b3880613032565b6008841015613001575061306a61054161048a83614dd7565b613001565b9150506020813d82116130a2575b8161308a6020938361412d565b810103126121b15761309c8291614d98565b38612fdf565b3d915061307d565b508115612e66565b60025460ff169550612e5b565b5034610226576020600319360112610226576004358015158103612e1f57610647906130e96141ee565b61433a565b50346102265760406003193601126102265760243590811515820361022657602061311b8360043561462b565b604051908152f35b50602090816003193601126102265760043561313e8161445c565b8034106134855761314f90346143f7565b908115613441576001600160a01b0380603d54161580613435575b808061342e575b81613425575b60405191868884015233604084015286606084015286608084015281151560a0840152151560c083015260c082526131ae826140f5565b156133fd57905b80603a5460081c16936040516384acd1bb60e01b81528781600481895afa908115611a90578391899189916133e0575b50600460405180948193631a90a21960e01b8352165afa908115611a905787916133b1575b506132149161444f565b86865491604051968780927fbee9cdfc0000000000000000000000000000000000000000000000000000000082528661ffff96878160181c16600485015260281c1660248301528a60448301526080606483015281613276608482018a61474c565b03925af1948515611a3a5790879392918796613375575b50613296614968565b9187549161331e8184169280603a5460081c16604051906132b6826140bd565b8582528982015267ffffffffffffffff809b1660408201526132d7876149a4565b526132e1866149a4565b506132eb896145b3565b938160355416966001546040519b8c9a8b998a986319597e0760e11b8a5233978260281c169160181c1660048a016149db565b03925af192831561060857809361333b575b505060405191168152f35b909192508382813d831161336e575b613354818361412d565b81010312610226575061336690614771565b903880613330565b503d61334a565b84819394959297503d83116133aa575b61338f818361412d565b810103126121b157906133a3879392614771565b943861328d565b503d613385565b90508781813d83116133d9575b6133c8818361412d565b81010312611a84575161321461320a565b503d6133be565b6133f79150823d8411610601576105f3818361412d565b386131e5565b61341f61041e91604051928391878a840152604080840152606083019061474c565b906131b5565b60019150613177565b5084613171565b5060ff6002541661316a565b6064846040519062461bcd60e51b82526004820152601460248201527f4e6f207a65726f20617373657420616d6f756e740000000000000000000000006044820152fd5b6084846040519062461bcd60e51b82526004820152603660248201527f53706f6b653a3a6465706f736974436f6c6c61746572616c4e61746976653a4960448201527f6e73756666696369656e742076616c75652073656e74000000000000000000006064820152fd5b506134f936614061565b906135096111b38395949561445c565b8015610a53576001600160a01b0380603d541694169384149384613f95575b80151580613f8d575b613e5e575b848080613e56575b81613e4e575b6040519186602084015233604084015283606084015286608084015281151560a0840152151560c083015260c0825261357c826140f5565b15613e2557945b15613a9e57506135a2816001600160a01b03603d541630903390614786565b82549160015490604051916135b6836140d9565b60018352855b60208110613a8757506135cd614bd9565b506135f0846001600160a01b03603d54166001600160a01b03603b541690614a9e565b6001600160a01b03603b5416602063ffffffff60a461361561ffff8a60181c16614bf3565b8a6001600160a01b03603d54169560405196879586947ff856ddb60000000000000000000000000000000000000000000000000000000086528d60048701521660248501526001600160a01b038d60281c16604485015260648401526001600160a01b038c60281c1660848401525af1908115611a90578791613a4d575b50600460206001600160a01b036036541660405192838092634d4502c960e11b82525afa90811561289b5788916139ee575b506136f07fffffffffffffffff00000000000000000000000000000000000000000000000091614bf3565b917fffffffff000000000000000000000000000000000000000000000000000000006040519360e01b16602084015260c01b166024820152600c8152613735816140d9565b60405190613742826140d9565b600282526020820152613754846149a4565b5261375e836149a4565b506001600160a01b036035541694604051917f24320c9f0000000000000000000000000000000000000000000000000000000083526020836004818a5afa92831561289b5788936139cd575b506040805163c23ee3c360e01b815261ffff601885901c166004820152602481018690526044810183905299908a6064818b5afa968715611b255760449a8a9861398e575b5061387b9a509261ffff9197969594926138296001600160a01b03956114e36040519b8c926020840152604080840152606083019061474c565b6040519b8c997fc055120e000000000000000000000000000000000000000000000000000000008b52848460181c1660048c0152868460281c1660248c015261016060448c01526101648b019061474c565b9560648a01528a60848a015260a48901521660c48701523360e48701521661010485015260031984820301610124850152815180825260208201916020808360051b83010194019288915b83831061394757505050505060209391838092600f61014483015203925af190811561060857809161390a575b506020915067ffffffffffffffff60405191168152f35b90506020823d60201161393f575b816139256020938361412d565b810103126102265750613939602091614771565b386138f3565b3d9150613918565b9193959650919360208061397b83601f1986600196030187526040838b5160ff81511684520151918185820152019061474c565b97019301930190928996959492936138c6565b61ffff939198506001600160a01b039594926138296139be61387b9360403d60401161169757611682818361412d565b509a93955050929495506137ef565b6139e791935060203d602011610601576105f3818361412d565b91386137aa565b90506020813d602011613a45575b81613a096020938361412d565b810103126121ad576136f0613a3e7fffffffffffffffff00000000000000000000000000000000000000000000000092614a8f565b91506136c5565b3d91506139fc565b90506020813d602011613a7f575b81613a686020938361412d565b81010312611a8457613a7990614771565b38613693565b3d9150613a5b565b602090613a92614bd9565b828287010152016135bc565b929193613aad82303387614786565b82549360015490613abc614968565b93613ac5614949565b50600460405192613ad584614111565b878452613ae0614949565b50613afa836001600160a01b03603a5460081c1683614a9e565b6001600160a01b03603a5460081c169360206001600160a01b036036541660405194858092631a90a21960e01b82525afa928315611b2557908a949392918a93613de8575b50916020949391613bb76001600160a01b039794604051988997889687957fc5a5ebda0000000000000000000000000000000000000000000000000000000087526004870152602486015261ffff8160181c16604486015260281c1660648401528c608484015260c060a484015260c483019061474c565b03925af18015611ad9578590613dae575b600491506001600160a01b03603a5460081c1660206001600160a01b036036541660405194858092634d4502c960e11b82525afa928315611a90578793613d67575b509067ffffffffffffffff9161ffff60405194613c26866140bd565b1684526020840152166040820152613c3d846149a4565b52613c47836149a4565b506001600160a01b0360355416916040519663c23ee3c360e01b885260408880613c92868561ffff8d60181c166004850160409194939261ffff606083019616825260208201520152565b0381875afa918215611a3a5786979860209793613d41575b50613ce990604051998a97889687956319597e0760e11b8752339461ffff85169461ffff6001600160a01b038260281c169160181c1660048a016149db565b03925af1908115610608578091613d045750602091506115c2565b90506020823d602011613d39575b81613d1f6020938361412d565b810103126102265750613d33602091614771565b386119cd565b3d9150613d12565b613ce9919350613d5f9060403d60401161169757611682818361412d565b509290613caa565b9092506020813d602011613da6575b81613d836020938361412d565b81010312611a845790613d9e67ffffffffffffffff92614a8f565b929091613c0a565b3d9150613d76565b506020813d602011613de0575b81613dc86020938361412d565b81010312611acd57613ddb600491614771565b613bc8565b3d9150613dbb565b9192509293506020813d602011613e1d575b81613e076020938361412d565b81010312611b1957518993929091906020613b3f565b3d9150613dfa565b613e4861041e91604051928391866020840152604080840152606083019061474c565b94613583565b859150613544565b50600161353e565b60405163313ce56760e01b8152602081600481855afa908115611ad9579083918691613f52575b5060ff8116906008821190838215613f3457613ea79061048f61048a84614da6565b8092600014613f0b5750613ec2925061048a6104b091614da6565b1461353657606460405162461bcd60e51b815260206004820152601760248201527f546f6f206d616e7920646563696d616c20706c616365730000000000000000006044820152fd5b9192600811613f1c575b5050613ec2565b613f2d925061048a61048f91614dd7565b3880613f15565b6008841015613ea75750613f4d61054161048a83614dd7565b613ea7565b9150506020813d8211613f85575b81613f6d6020938361412d565b81010312611acd57613f7f8391614d98565b38613e85565b3d9150613f60565b508415613531565b60025460ff169450613528565b50346102265760206003193601126102265760ff60406020926004358152603784522054166040519015158152f35b503461022657604060031936011261022657610647613fee61401f565b613ff6614035565b9061427d565b5034610226576020600319360112610226576140166141ee565b60043560015580f35b600435906001600160a01b03821682036106f857565b602435906001600160a01b03821682036106f857565b604435906001600160a01b03821682036106f857565b60031960609101126106f8576004356001600160a01b03811681036106f857906024359060443590565b60a0810190811067ffffffffffffffff8211176140a757604052565b634e487b7160e01b600052604160045260246000fd5b6060810190811067ffffffffffffffff8211176140a757604052565b6040810190811067ffffffffffffffff8211176140a757604052565b60e0810190811067ffffffffffffffff8211176140a757604052565b6020810190811067ffffffffffffffff8211176140a757604052565b90601f601f19910116810190811067ffffffffffffffff8211176140a757604052565b67ffffffffffffffff81116140a757601f01601f191660200190565b81601f820112156106f85780359061418382614150565b92614191604051948561412d565b828452602083830101116106f857816000926020809301838601378301015290565b67ffffffffffffffff81116140a75760051b60200190565b60031960609101126106f857600435906024359060443580151581036106f85790565b6001600160a01b03603e5416330361420257565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b1561423957565b606460405162461bcd60e51b815260206004820152600360248201527f57524900000000000000000000000000000000000000000000000000000000006044820152fd5b90600160ff19603a5461429360ff821615614232565b1617603a556001600160a01b03908173ffffffffffffffffffffffffffffffffffffffff1993168360355416176035551681603654161760365533906038541617603855565b916142f391926142ee60ff603a541615614232565b61427d565b7fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff00603a549260081b16911617603a55565b6001600160a01b03603c54161561435e5760ff60ff19600254169115151617600255565b50565b1561436857565b606460405162461bcd60e51b815260206004820152601760248201527f496e73756666696369656e742076616c75652073656e740000000000000000006044820152fd5b156143b357565b606460405162461bcd60e51b815260206004820152601e60248201527f4e6f6e2d7a65726f20636f7374466f7252657475726e44656c697665727900006044820152fd5b9190820391821161440457565b634e487b7160e01b600052601160045260246000fd5b908160209103126106f857516001600160a01b03811681036106f85790565b91908260409103126106f8576020825192015190565b9190820180921161440457565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152909392916001600160a01b039083908390831681806064810103915afa9182156145a9578592614589575b5080603a5460081c16835180916384acd1bb60e01b825281600460209485935afa90811561457f579082918891614562575b506004865180958193631a90a21960e01b8352165afa93841561455957508593614522575b505061451f92935061444f565b90565b9080929350813d8311614552575b61453a818361412d565b81010312612e1b5761451f9293505190839238614512565b503d614530565b513d87823e3d90fd5b6145799150823d8411610601576105f3818361412d565b386144ed565b85513d89823e3d90fd5b6145a1919250833d851161169757611682818361412d565b5090386144bb565b83513d87823e3d90fd5b6035546000546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301949094526044820152919082906001600160a01b031681806064810103915afa9081156103f15760009161460f575090565b614627915060403d811161169757611682818361412d565b5090565b603554600080546001546040805163c23ee3c360e01b815260189390931c61ffff16600484015260248301959095526044820152939092916001600160a01b03919083908690841681806064810103915afa94851561471c5784956146fc575b506146965750505090565b80603a95949293955460081c16835180916384acd1bb60e01b825281600460209485935afa90811561457f57908291889161456257506004865180958193631a90a21960e01b8352165afa9384156145595750859361452257505061451f92935061444f565b614714919550833d851161169757611682818361412d565b50933861468b565b50505051903d90823e3d90fd5b60005b83811061473c5750506000910152565b818101518382015260200161472c565b90601f19601f60209361476a81518092818752878088019101614729565b0116010190565b519067ffffffffffffffff821682036106f857565b90926147e393604051937f23b872dd0000000000000000000000000000000000000000000000000000000060208601526001600160a01b0380921660248601521660448401526064830152606482526147de8261408b565b6147f2565b565b519081151582036106f857565b6000806001600160a01b0361481c93169360208151910182865af1614815614886565b90836148b6565b8051908115159182614863575b50506148325750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b81925090602091810103126106f857602061487e91016147e5565b153880614829565b3d156148b1573d9061489782614150565b916148a5604051938461412d565b82523d6000602084013e565b606090565b906148f557508051156148cb57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580614940575b614906575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156148fe565b60405190614956826140bd565b60006040838281528260208201520152565b60405190614975826140d9565b600182528160005b6020908181101561499f57602091614993614949565b9082850101520161497d565b505050565b8051156149b15760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156149b15760209160051b010190565b9591949896979390929861ffff80941687526001600160a01b0395866020911681890152614a166101009b8c60409d8e8c01528a019061474c565b91606093848a0152608089015287820360a08901528080855193848152019401926000905b838210614a5b57505050505060e09495969750961660c085015216910152565b845180518816875280840151878501528d015167ffffffffffffffff168d8701529485019493820193600190910190614a3b565b519061ffff821682036106f857565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000602082018181526001600160a01b038516602484015260448084019690965294825294939092614af360648561412d565b6001600160a01b0390818416600080809588519082855af190614b14614886565b82614b9f575b5081614b94575b5015614b31575b50505050509050565b60405196602088015216602486015280604486015260448552608085019085821067ffffffffffffffff831117614b805750614b7593946147de91604052826147f2565b803880808080614b28565b80634e487b7160e01b602492526041600452fd5b90503b151538614b21565b80519192508115918215614bb7575b50509038614b1a565b8192509060209181010312611acd576020614bd291016147e5565b3880614bae565b60405190614be6826140d9565b6060602083600081520152565b61ffff1660028103614c055750600090565b60068103614c135750600190565b60178103614c215750600390565b601803614c2d57600290565b606460405162461bcd60e51b815260206004820152601160248201527f57726f6e67204343545020446f6d61696e0000000000000000000000000000006044820152fd5b15614c7857565b606460405162461bcd60e51b815260206004820152600d60248201527f726566756e64206661696c6564000000000000000000000000000000000000006044820152fd5b81601f820112156106f8578051614cd281614150565b92614ce0604051948561412d565b818452602082840101116106f85761451f9160208085019101614729565b9190916040818403126106f857805192602082015167ffffffffffffffff81116106f85761451f9201614cbc565b908160609103126106f85780516001600160a01b03811681036106f8579161451f6040614d5b602085016147e5565b93016147e5565b614d91614d806001600160a01b039260208082518301019101614cfe565b905060208082518301019101614d2c565b9190921692565b519060ff821682036106f857565b60ff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89116019060ff821161440457565b60ff166008039060ff821161440457565b60ff16604d811161440457600a0a90565b8181029291811591840414171561440457565b8115614e16570490565b634e487b7160e01b600052601260045260246000fd5b519063ffffffff821682036106f857565b8015614ea657600190602081108216604e8210831617614e9e578190600a925b808211614e7557505081600019048111614404570290565b90928060001904811161440457818416614e95575b800292811c90614e5d565b80920291614e8a565b9050600a0a90565b50600190565b15614eb357565b608460405162461bcd60e51b815260206004820152602260248201527f4d73672e73656e646572206973206e6f7420576f726d686f6c652052656c617960448201527f65720000000000000000000000000000000000000000000000000000000000006064820152fd5b15614f2457565b606460405162461bcd60e51b815260206004820152601560248201527f4e6f7420726567697374657265642073656e64657200000000000000000000006044820152fd5b15614f6f57565b606460405162461bcd60e51b815260206004820152601960248201527f4d65737361676520616c72656164792070726f636573736564000000000000006044820152fd5b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b039290921660248301526044808301939093529181526147e3916147de60648361412d56fea2646970667358221220c01bb022dcabde12b63571b0b163c202062a26bed1b2436335bad59d81d8542364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000000200000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa58500000000000000000000000027428dd2d3dd32a4d7f7c497eaaa23130d89491100000000000000000000000000000000000000000000000000000000000000170000000000000000000000001e3f1f1ca8c62aabcb3b78d87223e988dfa3780e0000000000000000000000000a992d191deec32afe36203ad87d7d289a738f81000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
-----Decoded View---------------
Arg [0] : chainId (uint16): 2
Arg [1] : wormhole (address): 0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B
Arg [2] : tokenBridge (address): 0x3ee18B2214AFF97000D974cf647E7C347E8fa585
Arg [3] : relayer (address): 0x27428DD2d3DD32A4D7f7C497eAaa23130d894911
Arg [4] : hubChainId (uint16): 23
Arg [5] : hubContractAddress (address): 0x1e3f1f1cA8C62aABCB3B78D87223E988Dfa3780E
Arg [6] : _circleMessageTransmitter (address): 0x0a992d191DEeC32aFe36203Ad87D7d289a738F81
Arg [7] : _circleTokenMessenger (address): 0xBd3fa81B58Ba92a82136038B25aDec7066af3155
Arg [8] : _USDC (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [1] : 00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b
Arg [2] : 0000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa585
Arg [3] : 00000000000000000000000027428dd2d3dd32a4d7f7c497eaaa23130d894911
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000017
Arg [5] : 0000000000000000000000001e3f1f1ca8c62aabcb3b78d87223e988dfa3780e
Arg [6] : 0000000000000000000000000a992d191deec32afe36203ad87d7d289a738f81
Arg [7] : 000000000000000000000000bd3fa81b58ba92a82136038b25adec7066af3155
Arg [8] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
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.