Overview
ETH Balance
0 ETH
ETH Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
Contract Name:
WormholeRelayer
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import {IWormholeRelayer} from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import {getDefaultDeliveryProviderState} from "./WormholeRelayerStorage.sol"; import {WormholeRelayerGovernance} from "./WormholeRelayerGovernance.sol"; import {WormholeRelayerSend} from "./WormholeRelayerSend.sol"; import {WormholeRelayerDelivery} from "./WormholeRelayerDelivery.sol"; import {WormholeRelayerBase} from "./WormholeRelayerBase.sol"; //WormholeRelayerGovernance inherits from ERC1967Upgrade, i.e. this is a proxy contract! contract WormholeRelayer is WormholeRelayerGovernance, WormholeRelayerSend, WormholeRelayerDelivery, IWormholeRelayer { //the only normal storage variable - everything else uses slot pattern //no point doing it for this one since it is entirely one-off and of no interest to the rest // of the contract and it also can't accidentally be moved because we are at the bottom of // the inheritance hierarchy here bool private initialized; constructor(address wormhole) WormholeRelayerBase(wormhole) {} //needs to be called upon construction of the EC1967 proxy function initialize(address defaultDeliveryProvider) public { assert(!initialized); initialized = true; getDefaultDeliveryProviderState().defaultDeliveryProvider = defaultDeliveryProvider; } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import "./TypedUnits.sol"; /** * @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; } interface IWormholeRelayerBase { event SendEvent( uint64 indexed sequence, LocalNative deliveryQuote, LocalNative paymentForExtraReceiverValue ); function getRegisteredWormholeRelayerContract(uint16 chainId) external view returns (bytes32); } /** * @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, TargetNative receiverValue, Gas 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, TargetNative receiverValue, Gas 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, TargetNative receiverValue, Gas 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, TargetNative receiverValue, Gas 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, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, Gas 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 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, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) external payable returns (uint64 sequence); /** * @notice Performs the same function as a `send`, except: * 1) Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`) * 2) Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery) * 3) Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number) * * The refund from the delivery currently in progress will not be sent to the user; it will instead * be paid to the delivery provider to perform the instruction specified here * * Publishes an instruction for the same delivery provider (or default, if the same one doesn't support the new target chain) * to relay a payload to the address `targetAddress` on chain `targetChain` * with gas limit `gasLimit` and with `msg.value` equal to `receiverValue` * * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`): * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f] * >= sum_f [quoteEVMDeliveryPrice(targetChain_f, receiverValue_f, gasLimit_f)] * * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested * * Any refunds (from leftover gas) from this forward will be paid to the same refundChain and refundAddress specified for the current delivery. * * @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 gasLimit gas limit with which to call `targetAddress`. */ function forwardPayloadToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit ) external payable; /** * @notice Performs the same function as a `send`, except: * 1) Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`) * 2) Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery) * 3) Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number) * * The refund from the delivery currently in progress will not be sent to the user; it will instead * be paid to the delivery provider to perform the instruction specified here * * Publishes an instruction for the same delivery provider (or default, if the same one doesn't support the new target chain) * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain` * with gas limit `gasLimit` and with `msg.value` equal to `receiverValue` * * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`): * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f] * >= sum_f [quoteEVMDeliveryPrice(targetChain_f, receiverValue_f, gasLimit_f)] * * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested * * Any refunds (from leftover gas) from this forward will be paid to the same refundChain and refundAddress specified for the current delivery. * * @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 gasLimit gas limit with which to call `targetAddress`. * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress` */ function forwardVaasToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit, VaaKey[] memory vaaKeys ) external payable; /** * @notice Performs the same function as a `send`, except: * 1) Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`) * 2) Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery) * 3) Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number) * * The refund from the delivery currently in progress will not be sent to the user; it will instead * be paid to the delivery provider to perform the instruction specified here * * 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 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 * * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`): * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f] * >= sum_f [quoteEVMDeliveryPrice(targetChain_f, receiverValue_f, gasLimit_f, deliveryProviderAddress_f) + paymentForExtraReceiverValue_f] * * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested * * @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 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, 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 */ function forwardToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, Gas gasLimit, uint16 refundChain, address refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) external payable; /** * @notice Performs the same function as a `send`, except: * 1) Can only be used during a delivery (i.e. in execution of `receiveWormholeMessages`) * 2) Is paid for (along with any other calls to forward) by (any msg.value passed in) + (refund leftover from current delivery) * 3) Only executes after `receiveWormholeMessages` is completed (and thus does not return a sequence number) * * The refund from the delivery currently in progress will not be sent to the user; it will instead * be paid to the delivery provider to perform the instruction specified here * * 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 * * The following equation must be satisfied (sum_f indicates summing over all forwards requested in `receiveWormholeMessages`): * (refund amount from current execution of receiveWormholeMessages) + sum_f [msg.value_f] * >= sum_f [quoteDeliveryPrice(targetChain_f, receiverValue_f, encodedExecutionParameters_f, deliveryProviderAddress_f) + paymentForExtraReceiverValue_f] * * The difference between the two sides of the above inequality will be added to `paymentForExtraReceiverValue` of the first forward requested * * @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 */ function forward( uint16 targetChain, bytes32 targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) external payable; /** * @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, TargetNative newReceiverValue, Gas 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, TargetNative 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, TargetNative receiverValue, Gas gasLimit ) external view returns (LocalNative nativePriceQuote, GasPrice 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, TargetNative receiverValue, Gas gasLimit, address deliveryProviderAddress ) external view returns (LocalNative nativePriceQuote, GasPrice 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, TargetNative receiverValue, bytes memory encodedExecutionParameters, address deliveryProviderAddress ) external view returns (LocalNative 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, LocalNative currentChainAmount, address deliveryProviderAddress ) external view returns (TargetNative 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, FORWARD_REQUEST_FAILURE, FORWARD_REQUEST_SUCCESS } 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 and no forwards were requested * - FORWARD_REQUEST_FAILURE, if the target contract doesn't revert, forwards were requested, * but provided/leftover funds were not sufficient to cover them all * - FORWARD_REQUEST_SUCCESS, if the target contract doesn't revert and all forwards are covered * @custom:member additionalStatusInfo: * - If status is SUCCESS or FORWARD_REQUEST_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). * - If status is FORWARD_REQUEST_FAILURE, this is also the revert data - the reason the forward failed. * This will be either an encoded Cancelled, DeliveryProviderReverted, or DeliveryProviderPaymentFailed error * @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, Gas 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(LocalNative msgValue, LocalNative totalFee); error RequestedGasLimitTooLow(); error DeliveryProviderDoesNotSupportTargetChain(address relayer, uint16 chainId); error DeliveryProviderCannotReceivePayment(); //When calling `forward()` on the WormholeRelayer if no delivery is in progress error NoDeliveryInProgress(); //When calling `delivery()` a second time even though a delivery is already in progress error ReentrantDelivery(address msgSender, address lockedBy); //When any other contract but the delivery target calls `forward()` on the WormholeRelayer while a // delivery is in progress error ForwardRequestFromWrongAddress(address msgSender, address deliveryTarget); error InvalidPayloadId(uint8 parsed, uint8 expected); error InvalidPayloadLength(uint256 received, uint256 expected); error InvalidVaaKeyType(uint8 parsed); 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 VaaKeysLengthDoesNotMatchVaasLength(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); error ForwardNotSufficientlyFunded(LocalNative amountOfFunds, LocalNative amountOfFundsNeeded); //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(LocalNative msgValue, LocalNative 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: Apache 2 pragma solidity ^0.8.19; import "../../interfaces/relayer/TypedUnits.sol"; // -------------------------------------- Persistent Storage --------------------------------------- //We have to hardcode the keccak256 values by hand rather than having them calculated because: // solc: TypeError: Only direct number constants and references to such constants are supported by // inline assembly. //And presumably what they mean by "direct number constants" is number literals... struct GovernanceState { // mapping of IWormhole.VM.hash of previously executed governance VMs mapping(bytes32 => bool) consumedGovernanceActions; } //keccak256("GovernanceState") - 1 bytes32 constant GOVERNANCE_STORAGE_SLOT = 0x970ad24d4754c92e299cabb86552091f5df0a15abc0f1b71f37d3e30031585dc; function getGovernanceState() pure returns (GovernanceState storage state) { assembly ("memory-safe") { state.slot := GOVERNANCE_STORAGE_SLOT } } struct DefaultDeliveryProviderState { // address of the default relay provider on this chain address defaultDeliveryProvider; } //keccak256("DefaultDeliveryProviderState") - 1 bytes32 constant DEFAULT_RELAY_PROVIDER_STORAGE_SLOT = 0xebc28a1927f62765bfb7ada566eeab2d31a98c65dbd1e8cad64acae2a3ae45d4; function getDefaultDeliveryProviderState() pure returns (DefaultDeliveryProviderState storage state) { assembly ("memory-safe") { state.slot := DEFAULT_RELAY_PROVIDER_STORAGE_SLOT } } struct RegisteredWormholeRelayersState { // chainId => wormhole address mapping of relayer contracts on other chains mapping(uint16 => bytes32) registeredWormholeRelayers; } //keccak256("RegisteredWormholeRelayersState") - 1 bytes32 constant REGISTERED_CORE_RELAYERS_STORAGE_SLOT = 0x9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d; function getRegisteredWormholeRelayersState() pure returns (RegisteredWormholeRelayersState storage state) { assembly ("memory-safe") { state.slot := REGISTERED_CORE_RELAYERS_STORAGE_SLOT } } // ---------------------------------- Temporary/Volatile Storage ----------------------------------- //Unlike proper persistent storage, everything below is only used for the lifetime of the current // transaction and is (i.e. must be) reset at the end. struct ForwardInstruction { bytes encodedInstruction; LocalNative msgValue; LocalNative deliveryPrice; LocalNative paymentForExtraReceiverValue; address payable rewardAddress; uint8 consistencyLevel; } struct DeliveryTmpState { bool deliveryInProgress; // the target address that is currently being delivered to (0 for a simple refund) address deliveryTarget; // the target relay provider address for the in-progress delivery address deliveryProvider; // the refund chain for the in-progress delivery uint16 refundChain; // the refund address for the in-progress delivery bytes32 refundAddress; // Requests which will be forwarded from the current delivery. ForwardInstruction[] forwardInstructions; } //keccak256("DeliveryTmpState") - 1 bytes32 constant DELIVERY_TMP_STORAGE_SLOT = 0x1a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd22; function getDeliveryTmpState() pure returns (DeliveryTmpState storage state) { assembly ("memory-safe") { state.slot := DELIVERY_TMP_STORAGE_SLOT } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol"; import {IWormhole} from "../../interfaces/IWormhole.sol"; import {InvalidPayloadLength} from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import {fromWormholeFormat} from "../../libraries/relayer/Utils.sol"; import {BytesParsing} from "../../libraries/relayer/BytesParsing.sol"; import { getGovernanceState, getRegisteredWormholeRelayersState, getDefaultDeliveryProviderState } from "./WormholeRelayerStorage.sol"; import {WormholeRelayerBase} from "./WormholeRelayerBase.sol"; error GovernanceActionAlreadyConsumed(bytes32 hash); error InvalidGovernanceVM(string reason); error InvalidGovernanceChainId(uint16 parsed, uint16 expected); error InvalidGovernanceContract(bytes32 parsed, bytes32 expected); error InvalidPayloadChainId(uint16 parsed, uint16 expected); error InvalidPayloadAction(uint8 parsed, uint8 expected); error InvalidPayloadModule(bytes32 parsed, bytes32 expected); error InvalidFork(); error ContractUpgradeFailed(bytes failure); error ChainAlreadyRegistered(uint16 chainId, bytes32 registeredWormholeRelayerContract); error InvalidDefaultDeliveryProvider(bytes32 defaultDeliveryProvider); abstract contract WormholeRelayerGovernance is WormholeRelayerBase, ERC1967Upgrade { //This constant should actually be defined in IWormhole. Alas, it isn't. uint16 private constant WORMHOLE_CHAINID_UNSET = 0; /** * Governance VMs are encoded in a packed fashion using the general wormhole scheme: * GovernancePacket = <Common Header|Action Parameters> * * For a more detailed explanation see here: * - https://docs.wormhole.com/wormhole/governance * - https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0002_governance_messaging.md */ //Right shifted ascii encoding of "WormholeRelayer" bytes32 private constant module = 0x0000000000000000000000000000000000576f726d686f6c6552656c61796572; /** * The choice of action enumeration and parameters follows the scheme of the core bridge: * - https://github.com/wormhole-foundation/wormhole/blob/main/ethereum/contracts/bridge/BridgeGovernance.sol#L115 */ /** * Registers a wormhole relayer contract that was deployed on another chain with the WormholeRelayer on * this chain. The equivalent to the core bridge's registerChain action. * * Action Parameters: * - uint16 foreignChainId * - bytes32 foreignContractAddress */ uint8 private constant GOVERNANCE_ACTION_REGISTER_WORMHOLE_RELAYER_CONTRACT = 1; /** * Upgrades the WormholeRelayer contract to a new implementation. The equivalent to the core bridge's * upgrade action. * * Action Parameters: * - bytes32 newImplementation */ uint8 private constant GOVERNANCE_ACTION_CONTRACT_UPGRADE = 2; /** * Sets the default relay provider for the WormholeRelayer. Has no equivalent in the core bridge. * * Action Parameters: * - bytes32 newProvider */ uint8 private constant GOVERNANCE_ACTION_UPDATE_DEFAULT_PROVIDER = 3; //By checking that only the contract can call itself, we can enforce that the migration code is // executed upon program upgrade and that it can't be called externally by anyone else. function checkAndExecuteUpgradeMigration() external { assert(msg.sender == address(this)); executeUpgradeMigration(); } function executeUpgradeMigration() internal virtual { //override and implement in WormholeRelayer upon contract upgrade (if required) } function registerWormholeRelayerContract(bytes memory encodedVm) external { (uint16 foreignChainId, bytes32 foreignAddress) = parseAndCheckRegisterWormholeRelayerContractVm(encodedVm); getRegisteredWormholeRelayersState().registeredWormholeRelayers[foreignChainId] = foreignAddress; } event ContractUpgraded(address indexed oldContract, address indexed newContract); function submitContractUpgrade(bytes memory encodedVm) external { address currentImplementation = _getImplementation(); address newImplementation = parseAndCheckContractUpgradeVm(encodedVm); _upgradeTo(newImplementation); (bool success, bytes memory revertData) = address(this).call(abi.encodeCall(this.checkAndExecuteUpgradeMigration, ())); if (!success) { revert ContractUpgradeFailed(revertData); } emit ContractUpgraded(currentImplementation, newImplementation); } function setDefaultDeliveryProvider(bytes memory encodedVm) external { address newProvider = parseAndCheckRegisterDefaultDeliveryProviderVm(encodedVm); getDefaultDeliveryProviderState().defaultDeliveryProvider = newProvider; } // ------------------------------------------- PRIVATE ------------------------------------------- using BytesParsing for bytes; function parseAndCheckRegisterWormholeRelayerContractVm(bytes memory encodedVm) private returns (uint16 foreignChainId, bytes32 foreignAddress) { bytes memory payload = verifyAndConsumeGovernanceVM(encodedVm); uint256 offset = parseAndCheckPayloadHeader( payload, GOVERNANCE_ACTION_REGISTER_WORMHOLE_RELAYER_CONTRACT, true ); (foreignChainId, offset) = payload.asUint16Unchecked(offset); (foreignAddress, offset) = payload.asBytes32Unchecked(offset); checkLength(payload, offset); if(getRegisteredWormholeRelayerContract(foreignChainId) != bytes32(0)) { revert ChainAlreadyRegistered(foreignChainId, getRegisteredWormholeRelayerContract(foreignChainId)); } } function parseAndCheckContractUpgradeVm(bytes memory encodedVm) private returns (address newImplementation) { bytes memory payload = verifyAndConsumeGovernanceVM(encodedVm); uint256 offset = parseAndCheckPayloadHeader(payload, GOVERNANCE_ACTION_CONTRACT_UPGRADE, false); bytes32 newImplementationWhFmt; (newImplementationWhFmt, offset) = payload.asBytes32Unchecked(offset); //fromWormholeFormat reverts if first 12 bytes aren't zero (i.e. if it's not an EVM address) newImplementation = fromWormholeFormat(newImplementationWhFmt); checkLength(payload, offset); } function parseAndCheckRegisterDefaultDeliveryProviderVm(bytes memory encodedVm) private returns (address newProvider) { bytes memory payload = verifyAndConsumeGovernanceVM(encodedVm); uint256 offset = parseAndCheckPayloadHeader(payload, GOVERNANCE_ACTION_UPDATE_DEFAULT_PROVIDER, false); bytes32 newProviderWhFmt; (newProviderWhFmt, offset) = payload.asBytes32Unchecked(offset); //fromWormholeFormat reverts if first 12 bytes aren't zero (i.e. if it's not an EVM address) newProvider = fromWormholeFormat(newProviderWhFmt); checkLength(payload, offset); if(newProvider == address(0)) { revert InvalidDefaultDeliveryProvider(newProviderWhFmt); } } function verifyAndConsumeGovernanceVM(bytes memory encodedVm) private returns (bytes memory payload) { (IWormhole.VM memory vm, bool valid, string memory reason) = getWormhole().parseAndVerifyVM(encodedVm); if (!valid) { revert InvalidGovernanceVM(reason); } uint16 governanceChainId = getWormhole().governanceChainId(); if (vm.emitterChainId != governanceChainId) { revert InvalidGovernanceChainId(vm.emitterChainId, governanceChainId); } bytes32 governanceContract = getWormhole().governanceContract(); if (vm.emitterAddress != governanceContract) { revert InvalidGovernanceContract(vm.emitterAddress, governanceContract); } bool consumed = getGovernanceState().consumedGovernanceActions[vm.hash]; if (consumed) { revert GovernanceActionAlreadyConsumed(vm.hash); } getGovernanceState().consumedGovernanceActions[vm.hash] = true; return vm.payload; } function parseAndCheckPayloadHeader( bytes memory encodedPayload, uint8 expectedAction, bool allowUnset ) private view returns (uint256 offset) { bytes32 parsedModule; (parsedModule, offset) = encodedPayload.asBytes32Unchecked(offset); if (parsedModule != module) { revert InvalidPayloadModule(parsedModule, module); } uint8 parsedAction; (parsedAction, offset) = encodedPayload.asUint8Unchecked(offset); if (parsedAction != expectedAction) { revert InvalidPayloadAction(parsedAction, expectedAction); } uint16 parsedChainId; (parsedChainId, offset) = encodedPayload.asUint16Unchecked(offset); if (!(parsedChainId == WORMHOLE_CHAINID_UNSET && allowUnset)) { if (getWormhole().isFork()) { revert InvalidFork(); } if (parsedChainId != getChainId()) { revert InvalidPayloadChainId(parsedChainId, getChainId()); } } } function checkLength(bytes memory payload, uint256 expected) private pure { if (payload.length != expected) { revert InvalidPayloadLength(payload.length, expected); } } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import { DeliveryProviderDoesNotSupportTargetChain, InvalidMsgValue, DeliveryProviderCannotReceivePayment, VaaKey, IWormholeRelayerSend } from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import {IDeliveryProvider} from "../../interfaces/relayer/IDeliveryProviderTyped.sol"; import {toWormholeFormat, fromWormholeFormat} from "../../libraries/relayer/Utils.sol"; import { DeliveryInstruction, RedeliveryInstruction } from "../../libraries/relayer/RelayerInternalStructs.sol"; import {WormholeRelayerSerde} from "./WormholeRelayerSerde.sol"; import {ForwardInstruction, getDefaultDeliveryProviderState} from "./WormholeRelayerStorage.sol"; import {WormholeRelayerBase} from "./WormholeRelayerBase.sol"; import "../../interfaces/relayer/TypedUnits.sol"; import "../../libraries/relayer/ExecutionParameters.sol"; abstract contract WormholeRelayerSend is WormholeRelayerBase, IWormholeRelayerSend { using WormholeRelayerSerde for *; using WeiLib for Wei; using GasLib for Gas; using TargetNativeLib for TargetNative; using LocalNativeLib for LocalNative; /* * Public convenience overloads */ function sendPayloadToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit ) external payable returns (uint64 sequence) { return sendToEvm( targetChain, targetAddress, payload, receiverValue, LocalNative.wrap(0), gasLimit, targetChain, getDefaultDeliveryProviderOnChain(targetChain), getDefaultDeliveryProvider(), new VaaKey[](0), CONSISTENCY_LEVEL_FINALIZED ); } function sendPayloadToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit, uint16 refundChain, address refundAddress ) external payable returns (uint64 sequence) { return sendToEvm( targetChain, targetAddress, payload, receiverValue, LocalNative.wrap(0), gasLimit, refundChain, refundAddress, getDefaultDeliveryProvider(), new VaaKey[](0), CONSISTENCY_LEVEL_FINALIZED ); } function sendVaasToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit, VaaKey[] memory vaaKeys ) external payable returns (uint64 sequence) { return sendToEvm( targetChain, targetAddress, payload, receiverValue, LocalNative.wrap(0), gasLimit, targetChain, getDefaultDeliveryProviderOnChain(targetChain), getDefaultDeliveryProvider(), vaaKeys, CONSISTENCY_LEVEL_FINALIZED ); } function sendVaasToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit, VaaKey[] memory vaaKeys, uint16 refundChain, address refundAddress ) external payable returns (uint64 sequence) { return sendToEvm( targetChain, targetAddress, payload, receiverValue, LocalNative.wrap(0), gasLimit, refundChain, refundAddress, getDefaultDeliveryProvider(), vaaKeys, CONSISTENCY_LEVEL_FINALIZED ); } function sendToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, Gas gasLimit, uint16 refundChain, address refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) public payable returns (uint64 sequence) { sequence = send( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, paymentForExtraReceiverValue, encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), refundChain, toWormholeFormat(refundAddress), deliveryProviderAddress, vaaKeys, consistencyLevel ); } function forwardPayloadToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit ) external payable { (address deliveryProvider,) = getOriginalOrDefaultDeliveryProvider(targetChain); forward( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, LocalNative.wrap(0), encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), getCurrentRefundChain(), getCurrentRefundAddress(), deliveryProvider, new VaaKey[](0), CONSISTENCY_LEVEL_FINALIZED ); } function forwardVaasToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit, VaaKey[] memory vaaKeys ) external payable { (address deliveryProvider,) = getOriginalOrDefaultDeliveryProvider(targetChain); forward( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, LocalNative.wrap(0), encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), getCurrentRefundChain(), getCurrentRefundAddress(), deliveryProvider, vaaKeys, CONSISTENCY_LEVEL_FINALIZED ); } function forwardToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, Gas gasLimit, uint16 refundChain, address refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) public payable { // provide ability to use original relay provider if (deliveryProviderAddress == address(0)) { deliveryProviderAddress = getOriginalDeliveryProvider(); } forward( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, paymentForExtraReceiverValue, encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), refundChain, toWormholeFormat(refundAddress), deliveryProviderAddress, vaaKeys, consistencyLevel ); } function resendToEvm( VaaKey memory deliveryVaaKey, uint16 targetChain, TargetNative newReceiverValue, Gas newGasLimit, address newDeliveryProviderAddress ) public payable returns (uint64 sequence) { sequence = resend( deliveryVaaKey, targetChain, newReceiverValue, encodeEvmExecutionParamsV1(EvmExecutionParamsV1(newGasLimit)), newDeliveryProviderAddress ); } function send( uint16 targetChain, bytes32 targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) public payable returns (uint64 sequence) { sequence = send( Send( targetChain, targetAddress, payload, receiverValue, paymentForExtraReceiverValue, encodedExecutionParameters, refundChain, refundAddress, deliveryProviderAddress, vaaKeys, consistencyLevel ) ); } function forward( uint16 targetChain, bytes32 targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) public payable { forward( Send( targetChain, targetAddress, payload, receiverValue, paymentForExtraReceiverValue, encodedExecutionParameters, refundChain, refundAddress, deliveryProviderAddress, vaaKeys, consistencyLevel ) ); } /* * Non overload logic */ struct Send { uint16 targetChain; bytes32 targetAddress; bytes payload; TargetNative receiverValue; LocalNative paymentForExtraReceiverValue; bytes encodedExecutionParameters; uint16 refundChain; bytes32 refundAddress; address deliveryProviderAddress; VaaKey[] vaaKeys; uint8 consistencyLevel; } function send(Send memory sendParams) internal returns (uint64 sequence) { IDeliveryProvider provider = IDeliveryProvider(sendParams.deliveryProviderAddress); // Revert if delivery provider does not support the target chain if (!provider.isChainSupported(sendParams.targetChain)) { revert DeliveryProviderDoesNotSupportTargetChain( sendParams.deliveryProviderAddress, sendParams.targetChain ); } // Obtain the delivery provider's fee for this delivery, as well as some encoded info (e.g. refund per unit of gas unused) (LocalNative deliveryPrice, bytes memory encodedExecutionInfo) = provider.quoteDeliveryPrice( sendParams.targetChain, sendParams.receiverValue, sendParams.encodedExecutionParameters ); // Check if user passed in 'one wormhole message fee' + 'delivery provider's fee' LocalNative wormholeMessageFee = getWormholeMessageFee(); checkMsgValue(wormholeMessageFee, deliveryPrice, sendParams.paymentForExtraReceiverValue); // Encode all relevant info the delivery provider needs to perform the delivery as requested bytes memory encodedInstruction = DeliveryInstruction({ targetChain: sendParams.targetChain, targetAddress: sendParams.targetAddress, payload: sendParams.payload, requestedReceiverValue: sendParams.receiverValue, extraReceiverValue: provider.quoteAssetConversion( sendParams.targetChain, sendParams.paymentForExtraReceiverValue ), encodedExecutionInfo: encodedExecutionInfo, refundChain: sendParams.refundChain, refundAddress: sendParams.refundAddress, refundDeliveryProvider: provider.getTargetChainAddress(sendParams.targetChain), sourceDeliveryProvider: toWormholeFormat(sendParams.deliveryProviderAddress), senderAddress: toWormholeFormat(msg.sender), vaaKeys: sendParams.vaaKeys }).encode(); // Publish the encoded delivery instruction as a wormhole message // and pay the delivery provider their fee bool paymentSucceeded; (sequence, paymentSucceeded) = publishAndPay( wormholeMessageFee, deliveryPrice, sendParams.paymentForExtraReceiverValue, encodedInstruction, sendParams.consistencyLevel, provider.getRewardAddress() ); if(!paymentSucceeded) revert DeliveryProviderCannotReceivePayment(); } function forward(Send memory sendParams) internal { // Revert if a delivery with targetAddress == msg.sender is not currently in progress checkMsgSenderInDelivery(); IDeliveryProvider provider = IDeliveryProvider(sendParams.deliveryProviderAddress); // Revert if delivery provider does not support the target chain if (!provider.isChainSupported(sendParams.targetChain)) { revert DeliveryProviderDoesNotSupportTargetChain( sendParams.deliveryProviderAddress, sendParams.targetChain ); } // Obtain the delivery provider's fee for this delivery, as well as some encoded info (e.g. refund per unit of gas unused) (LocalNative deliveryPrice, bytes memory encodedExecutionInfo) = provider.quoteDeliveryPrice( sendParams.targetChain, sendParams.receiverValue, sendParams.encodedExecutionParameters ); // Encode all relevant info the delivery provider needs to perform this delivery as requested bytes memory encodedInstruction = DeliveryInstruction({ targetChain: sendParams.targetChain, targetAddress: sendParams.targetAddress, payload: sendParams.payload, requestedReceiverValue: sendParams.receiverValue, extraReceiverValue: provider.quoteAssetConversion( sendParams.targetChain, sendParams.paymentForExtraReceiverValue ), encodedExecutionInfo: encodedExecutionInfo, refundChain: sendParams.refundChain, refundAddress: sendParams.refundAddress, refundDeliveryProvider: provider.getTargetChainAddress(sendParams.targetChain), sourceDeliveryProvider: toWormholeFormat(sendParams.deliveryProviderAddress), senderAddress: toWormholeFormat(msg.sender), vaaKeys: sendParams.vaaKeys }).encode(); // Store information about this delivery in state // so that it can be performed after the execution of the current delivery, // when the 'refund' available to use for this delivery is known appendForwardInstruction( ForwardInstruction({ encodedInstruction: encodedInstruction, msgValue: LocalNative.wrap(msg.value), deliveryPrice: deliveryPrice, paymentForExtraReceiverValue: sendParams.paymentForExtraReceiverValue, consistencyLevel: sendParams.consistencyLevel, rewardAddress: provider.getRewardAddress() }) ); } function resend( VaaKey memory deliveryVaaKey, uint16 targetChain, TargetNative newReceiverValue, bytes memory newEncodedExecutionParameters, address newDeliveryProviderAddress ) public payable returns (uint64 sequence) { IDeliveryProvider provider = IDeliveryProvider(newDeliveryProviderAddress); // Revert if delivery provider does not support the target chain if (!provider.isChainSupported(targetChain)) { revert DeliveryProviderDoesNotSupportTargetChain( newDeliveryProviderAddress, targetChain ); } // Obtain the delivery provider's fee for this delivery, as well as some encoded info (e.g. refund per unit of gas unused) (LocalNative deliveryPrice, bytes memory encodedExecutionInfo) = provider.quoteDeliveryPrice( targetChain, newReceiverValue, newEncodedExecutionParameters ); // Check if user passed in 'one wormhole message fee' + 'delivery provider's fee' LocalNative wormholeMessageFee = getWormholeMessageFee(); checkMsgValue(wormholeMessageFee, deliveryPrice, LocalNative.wrap(0)); // Encode all relevant info the delivery provider needs to perform this redelivery as requested bytes memory encodedInstruction = RedeliveryInstruction({ deliveryVaaKey: deliveryVaaKey, targetChain: targetChain, newRequestedReceiverValue: newReceiverValue, newEncodedExecutionInfo: encodedExecutionInfo, newSourceDeliveryProvider: toWormholeFormat(newDeliveryProviderAddress), newSenderAddress: toWormholeFormat(msg.sender) }).encode(); // Publish the encoded redelivery instruction as a wormhole message // and pay the delivery provider their fee bool paymentSucceeded; (sequence, paymentSucceeded) = publishAndPay( wormholeMessageFee, deliveryPrice, LocalNative.wrap(0), encodedInstruction, CONSISTENCY_LEVEL_INSTANT, provider.getRewardAddress() ); if (!paymentSucceeded) revert DeliveryProviderCannotReceivePayment(); } function getDefaultDeliveryProvider() public view returns (address deliveryProvider) { deliveryProvider = getDefaultDeliveryProviderState().defaultDeliveryProvider; } // Get the delivery provider's contract address on chain 'targetChain' function getDefaultDeliveryProviderOnChain(uint16 targetChain) public view returns (address deliveryProvider) { deliveryProvider = fromWormholeFormat( IDeliveryProvider(getDefaultDeliveryProviderState().defaultDeliveryProvider) .getTargetChainAddress(targetChain) ); } function getOriginalOrDefaultDeliveryProvider(uint16 targetChain) public view returns (address deliveryProvider, address deliveryProviderOnTarget) { deliveryProvider = getOriginalDeliveryProvider(); if ( deliveryProvider == address(0) || !IDeliveryProvider(deliveryProvider).isChainSupported(targetChain) ) { deliveryProvider = getDefaultDeliveryProvider(); } deliveryProviderOnTarget = fromWormholeFormat( IDeliveryProvider(deliveryProvider).getTargetChainAddress(targetChain) ); } function quoteEVMDeliveryPrice( uint16 targetChain, TargetNative receiverValue, Gas gasLimit, address deliveryProviderAddress ) public view returns (LocalNative nativePriceQuote, GasPrice targetChainRefundPerGasUnused) { (LocalNative quote, bytes memory encodedExecutionInfo) = quoteDeliveryPrice( targetChain, receiverValue, encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), deliveryProviderAddress ); nativePriceQuote = quote; targetChainRefundPerGasUnused = decodeEvmExecutionInfoV1(encodedExecutionInfo).targetChainRefundPerGasUnused; } function quoteEVMDeliveryPrice( uint16 targetChain, TargetNative receiverValue, Gas gasLimit ) public view returns (LocalNative nativePriceQuote, GasPrice targetChainRefundPerGasUnused) { return quoteEVMDeliveryPrice( targetChain, receiverValue, gasLimit, getDefaultDeliveryProvider() ); } function quoteDeliveryPrice( uint16 targetChain, TargetNative receiverValue, bytes memory encodedExecutionParameters, address deliveryProviderAddress ) public view returns (LocalNative nativePriceQuote, bytes memory encodedExecutionInfo) { IDeliveryProvider provider = IDeliveryProvider(deliveryProviderAddress); (LocalNative deliveryPrice, bytes memory _encodedExecutionInfo) = provider .quoteDeliveryPrice( targetChain, receiverValue, encodedExecutionParameters ); encodedExecutionInfo = _encodedExecutionInfo; nativePriceQuote = deliveryPrice + getWormholeMessageFee(); } function quoteNativeForChain( uint16 targetChain, LocalNative currentChainAmount, address deliveryProviderAddress ) public view returns (TargetNative targetChainAmount) { return IDeliveryProvider(deliveryProviderAddress).quoteAssetConversion(targetChain, currentChainAmount); } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import {IWormhole} from "../../interfaces/IWormhole.sol"; import { InvalidDeliveryVaa, InvalidEmitter, InsufficientRelayerFunds, TargetChainIsNotThisChain, ForwardNotSufficientlyFunded, VaaKeysLengthDoesNotMatchVaasLength, VaaKeysDoNotMatchVaas, InvalidOverrideGasLimit, InvalidOverrideReceiverValue, InvalidOverrideRefundPerGasUnused, RequesterNotWormholeRelayer, DeliveryProviderCannotReceivePayment, VaaKey, IWormholeRelayerDelivery, IWormholeRelayerSend, RETURNDATA_TRUNCATION_THRESHOLD } from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import {IWormholeReceiver} from "../../interfaces/relayer/IWormholeReceiver.sol"; import {IDeliveryProvider} from "../../interfaces/relayer/IDeliveryProviderTyped.sol"; import {pay, min, toWormholeFormat, fromWormholeFormat, returnLengthBoundedCall} from "../../libraries/relayer/Utils.sol"; import { DeliveryInstruction, DeliveryOverride, EvmDeliveryInstruction } from "../../libraries/relayer/RelayerInternalStructs.sol"; import {BytesParsing} from "../../libraries/relayer/BytesParsing.sol"; import {WormholeRelayerSerde} from "./WormholeRelayerSerde.sol"; import {ForwardInstruction} from "./WormholeRelayerStorage.sol"; import {WormholeRelayerBase} from "./WormholeRelayerBase.sol"; import "../../interfaces/relayer/TypedUnits.sol"; import "../../libraries/relayer/ExecutionParameters.sol"; abstract contract WormholeRelayerDelivery is WormholeRelayerBase, IWormholeRelayerDelivery { using WormholeRelayerSerde for *; using BytesParsing for bytes; using WeiLib for Wei; using GasLib for Gas; using GasPriceLib for GasPrice; using TargetNativeLib for TargetNative; using LocalNativeLib for LocalNative; function deliver( bytes[] memory encodedVMs, bytes memory encodedDeliveryVAA, address payable relayerRefundAddress, bytes memory deliveryOverrides ) public payable { // Parse and verify VAA containing delivery instructions, revert if invalid (IWormhole.VM memory vm, bool valid, string memory reason) = getWormhole().parseAndVerifyVM(encodedDeliveryVAA); if (!valid) { revert InvalidDeliveryVaa(reason); } // Revert if the emitter of the VAA is not a Wormhole Relayer contract bytes32 registeredWormholeRelayer = getRegisteredWormholeRelayerContract(vm.emitterChainId); if (vm.emitterAddress != registeredWormholeRelayer) { revert InvalidEmitter(vm.emitterAddress, registeredWormholeRelayer, vm.emitterChainId); } DeliveryInstruction memory instruction = vm.payload.decodeDeliveryInstruction(); // Lock the contract (and store some information about the delivery in temporary storage) startDelivery( fromWormholeFormat(instruction.targetAddress), fromWormholeFormat(instruction.refundDeliveryProvider), instruction.refundChain, instruction.refundAddress ); DeliveryVAAInfo memory deliveryVaaInfo = DeliveryVAAInfo({ sourceChain: vm.emitterChainId, sourceSequence: vm.sequence, deliveryVaaHash: vm.hash, relayerRefundAddress: relayerRefundAddress, encodedVMs: encodedVMs, deliveryInstruction: instruction, gasLimit: Gas.wrap(0), targetChainRefundPerGasUnused: GasPrice.wrap(0), totalReceiverValue: TargetNative.wrap(0), encodedOverrides: deliveryOverrides, redeliveryHash: bytes32(0) }); // Decode information from the execution parameters // (overriding them if there was an override requested) // Assumes execution parameters and info are of version EVM_V1 ( deliveryVaaInfo.gasLimit, deliveryVaaInfo.targetChainRefundPerGasUnused, deliveryVaaInfo.totalReceiverValue, deliveryVaaInfo.redeliveryHash ) = getDeliveryParametersEvmV1(instruction, deliveryOverrides); // Revert if msg.value is not enough to fund both the receiver value // as well as the maximum possible refund // Note: instruction's TargetNative is delivery's LocalNative LocalNative requiredFunds = (deliveryVaaInfo.gasLimit.toWei( deliveryVaaInfo.targetChainRefundPerGasUnused ) + deliveryVaaInfo.totalReceiverValue.asNative()).asLocalNative(); if (msgValue() < requiredFunds) { revert InsufficientRelayerFunds(msgValue(), requiredFunds); } // Revert if the instruction's target chain is not this chain if (getChainId() != instruction.targetChain) { revert TargetChainIsNotThisChain(instruction.targetChain); } // Revert if the VAAs delivered do not match the descriptions specified in the instruction checkVaaKeysWithVAAs(instruction.vaaKeys, encodedVMs); executeDelivery(deliveryVaaInfo); // Unlock contract finishDelivery(); } // ------------------------------------------- PRIVATE ------------------------------------------- error Cancelled(Gas gasUsed, LocalNative available, LocalNative required); error DeliveryProviderReverted(Gas gasUsed); error DeliveryProviderPaymentFailed(Gas gasUsed); struct DeliveryVAAInfo { uint16 sourceChain; uint64 sourceSequence; bytes32 deliveryVaaHash; address payable relayerRefundAddress; bytes[] encodedVMs; DeliveryInstruction deliveryInstruction; Gas gasLimit; GasPrice targetChainRefundPerGasUnused; TargetNative totalReceiverValue; bytes encodedOverrides; bytes32 redeliveryHash; //optional (0 if not present) } function getDeliveryParametersEvmV1( DeliveryInstruction memory instruction, bytes memory encodedOverrides ) internal pure returns ( Gas gasLimit, GasPrice targetChainRefundPerGasUnused, TargetNative totalReceiverValue, bytes32 redeliveryHash ) { ExecutionInfoVersion instructionExecutionInfoVersion = decodeExecutionInfoVersion(instruction.encodedExecutionInfo); if (instructionExecutionInfoVersion != ExecutionInfoVersion.EVM_V1) { revert UnexpectedExecutionInfoVersion( uint8(instructionExecutionInfoVersion), uint8(ExecutionInfoVersion.EVM_V1) ); } EvmExecutionInfoV1 memory executionInfo = decodeEvmExecutionInfoV1(instruction.encodedExecutionInfo); // If present, apply redelivery deliveryOverrides to current instruction if (encodedOverrides.length != 0) { DeliveryOverride memory deliveryOverrides = encodedOverrides.decodeDeliveryOverride(); // Check to see if gasLimit >= original gas limit, receiver value >= original receiver value, and refund >= original refund // If so, replace the corresponding variables with the overriden variables // If not, revert (instruction.requestedReceiverValue, executionInfo) = decodeAndCheckOverridesEvmV1( instruction.requestedReceiverValue, executionInfo, deliveryOverrides ); instruction.extraReceiverValue = TargetNative.wrap(0); redeliveryHash = deliveryOverrides.redeliveryHash; } gasLimit = executionInfo.gasLimit; targetChainRefundPerGasUnused = executionInfo.targetChainRefundPerGasUnused; totalReceiverValue = instruction.requestedReceiverValue + instruction.extraReceiverValue; } function decodeAndCheckOverridesEvmV1( TargetNative receiverValue, EvmExecutionInfoV1 memory executionInfo, DeliveryOverride memory deliveryOverrides ) internal pure returns ( TargetNative deliveryOverridesReceiverValue, EvmExecutionInfoV1 memory deliveryOverridesExecutionInfo ) { if (deliveryOverrides.newReceiverValue.unwrap() < receiverValue.unwrap()) { revert InvalidOverrideReceiverValue(); } ExecutionInfoVersion deliveryOverridesExecutionInfoVersion = decodeExecutionInfoVersion(deliveryOverrides.newExecutionInfo); if (ExecutionInfoVersion.EVM_V1 != deliveryOverridesExecutionInfoVersion) { revert VersionMismatchOverride( uint8(ExecutionInfoVersion.EVM_V1), uint8(deliveryOverridesExecutionInfoVersion) ); } deliveryOverridesExecutionInfo = decodeEvmExecutionInfoV1(deliveryOverrides.newExecutionInfo); deliveryOverridesReceiverValue = deliveryOverrides.newReceiverValue; if ( deliveryOverridesExecutionInfo.targetChainRefundPerGasUnused.unwrap() < executionInfo.targetChainRefundPerGasUnused.unwrap() ) { revert InvalidOverrideRefundPerGasUnused(); } if (deliveryOverridesExecutionInfo.gasLimit < executionInfo.gasLimit) { revert InvalidOverrideGasLimit(); } } struct DeliveryResults { Gas gasUsed; DeliveryStatus status; bytes additionalStatusInfo; } /** * Performs the following actions: * - Calls the `receiveWormholeMessages` method on the contract * `vaaInfo.deliveryInstruction.targetAddress` (with the gas limit and value specified in * vaaInfo.gasLimit and vaaInfo.totalReceiverValue, and `encodedVMs` as the input) * * - Calculates how much gas from `vaaInfo.gasLimit` is left * - If the call succeeded and during execution of `receiveWormholeMessages` there were * forward(s), and (gas left from vaaInfo.gasLimit) * (vaaInfo.targetChainRefundPerGasUnused) is enough extra funds * to execute the forward(s), then the forward(s) are executed * - else: * revert the delivery to trigger a receiver failure (or forward request failure if * there were forward(s)) * refund 'vaaInfo.targetChainRefundPerGasUnused'*(amount of vaaInfo.gasLimit unused) to vaaInfo.deliveryInstruction.refundAddress * if the call reverted, refund `vaaInfo.totalReceiverValue` to vaaInfo.deliveryInstruction.refundAddress * - refund anything leftover to the relayer * * @param vaaInfo struct specifying: * - sourceChain chain id that the delivery originated from * - sourceSequence sequence number of the delivery VAA on the source chain * - deliveryVaaHash hash of delivery VAA * - relayerRefundAddress address that should be paid for relayer refunds * - encodedVMs list of signed wormhole messages (VAAs) * - deliveryInstruction the specific instruction which is being executed * - gasLimit the gas limit to call targetAddress with * - targetChainRefundPerGasUnused the amount of (this chain) wei to refund to refundAddress * per unit of gas unused (from gasLimit) * - totalReceiverValue the msg.value to call targetAddress with * - encodedOverrides any (encoded) overrides that were applied * - (optional) redeliveryHash hash of redelivery Vaa */ function executeDelivery(DeliveryVAAInfo memory vaaInfo) private { // If the targetAddress is the 0 address // Then emit event and return // (This is used for cross-chain refunds) if (checkIfCrossChainRefund(vaaInfo)) { return; } DeliveryResults memory results; // Forces external call // In order to catch reverts // (If the user's contract requests a forward // and there ends up not being enough funds, // then we will revert this call) try this.executeInstruction( EvmDeliveryInstruction({ sourceChain: vaaInfo.sourceChain, targetAddress: vaaInfo.deliveryInstruction.targetAddress, payload: vaaInfo.deliveryInstruction.payload, gasLimit: vaaInfo.gasLimit, totalReceiverValue: vaaInfo.totalReceiverValue, targetChainRefundPerGasUnused: vaaInfo.targetChainRefundPerGasUnused, senderAddress: vaaInfo.deliveryInstruction.senderAddress, deliveryHash: vaaInfo.deliveryVaaHash, signedVaas: vaaInfo.encodedVMs } )) returns ( uint8 _status, Gas _gasUsed, bytes memory targetRevertDataTruncated ) { results = DeliveryResults( _gasUsed, DeliveryStatus(_status), targetRevertDataTruncated ); } catch (bytes memory revertData) { // Should only revert if // 1) forward(s) were requested but not enough funds were available from the refund // 2) forward(s) were requested, but the delivery provider requested for the forward reverted during execution // 3) forward(s) were requested, but the payment of the delivery provider for the forward failed // Decode returned error (into one of these three known types) // obtaining the gas usage of targetAddress bool knownError; Gas gasUsed_; (gasUsed_, knownError) = tryDecodeExecuteInstructionError(revertData); results = DeliveryResults( knownError? gasUsed_ : vaaInfo.gasLimit, DeliveryStatus.FORWARD_REQUEST_FAILURE, revertData ); } emit Delivery( fromWormholeFormat(vaaInfo.deliveryInstruction.targetAddress), vaaInfo.sourceChain, vaaInfo.sourceSequence, vaaInfo.deliveryVaaHash, results.status, results.gasUsed, payRefunds( vaaInfo.deliveryInstruction, vaaInfo.relayerRefundAddress, (vaaInfo.gasLimit - results.gasUsed).toWei(vaaInfo.targetChainRefundPerGasUnused).asLocalNative(), results.status ), results.additionalStatusInfo, (vaaInfo.redeliveryHash != 0) ? vaaInfo.encodedOverrides : new bytes(0) ); } function checkIfCrossChainRefund(DeliveryVAAInfo memory vaaInfo) internal returns (bool isCrossChainRefund) { if (vaaInfo.deliveryInstruction.targetAddress == 0x0) { emit Delivery( fromWormholeFormat(vaaInfo.deliveryInstruction.targetAddress), vaaInfo.sourceChain, vaaInfo.sourceSequence, vaaInfo.deliveryVaaHash, DeliveryStatus.SUCCESS, Gas.wrap(0), payRefunds( vaaInfo.deliveryInstruction, vaaInfo.relayerRefundAddress, LocalNative.wrap(0), DeliveryStatus.RECEIVER_FAILURE ), bytes(""), (vaaInfo.redeliveryHash != 0) ? vaaInfo.encodedOverrides : new bytes(0) ); isCrossChainRefund = true; } } function executeInstruction(EvmDeliveryInstruction memory evmInstruction) external returns (uint8 status, Gas gasUsed, bytes memory targetRevertDataTruncated) { // despite being external, we only allow ourselves to call this function (via CALL opcode) // used as a means to retroactively revert the call to the delivery target if the forwards // can't be funded if (msg.sender != address(this)) { revert RequesterNotWormholeRelayer(); } Gas gasLimit = evmInstruction.gasLimit; bool success; { address payable deliveryTarget = payable(fromWormholeFormat(evmInstruction.targetAddress)); bytes memory callData = abi.encodeCall(IWormholeReceiver.receiveWormholeMessages, ( evmInstruction.payload, evmInstruction.signedVaas, evmInstruction.senderAddress, evmInstruction.sourceChain, evmInstruction.deliveryHash )); // Measure gas usage of call Gas preGas = Gas.wrap(gasleft()); // Calls the `receiveWormholeMessages` endpoint on the contract `evmInstruction.targetAddress` // (with the gas limit and value specified in instruction, and `encodedVMs` as the input) // If it reverts, returns the first 132 bytes of the revert message (success, targetRevertDataTruncated) = returnLengthBoundedCall( deliveryTarget, callData, gasLimit.unwrap(), evmInstruction.totalReceiverValue.unwrap(), RETURNDATA_TRUNCATION_THRESHOLD ); Gas postGas = Gas.wrap(gasleft()); unchecked { gasUsed = (preGas - postGas).min(gasLimit); } } if (success) { targetRevertDataTruncated = new bytes(0); status = uint8(DeliveryStatus.SUCCESS); ForwardInstruction[] storage forwardInstructions = getForwardInstructions(); if (forwardInstructions.length > 0) { // forward(s) were requested during execution of the call to targetAddress above // Calculate the amount to refund the user for unused gas LocalNative transactionFeeRefundAmount = (gasLimit - gasUsed).toWei( evmInstruction.targetChainRefundPerGasUnused ).asLocalNative(); // Check if refund amount is enough, and if so, emit forwards emitForward(gasUsed, transactionFeeRefundAmount, forwardInstructions); // If we reach here (i.e. emitForward didn't revert) then the forward succeeded status = uint8(DeliveryStatus.FORWARD_REQUEST_SUCCESS); } } else { // Call to 'receiveWormholeMessages' on targetAddress reverted status = uint8(DeliveryStatus.RECEIVER_FAILURE); } } /** * - Checks if enough funds were passed into a forward (and reverts if not) * - Increases the 'extraReceiverValue' of the first forward in order to use all of the funds * - Publishes the DeliveryInstruction * - Pays the relayer's reward address to deliver the forward * * @param transactionFeeRefundAmount amount of maxTransactionFee that was unused * @param forwardInstructions An array of structs containing information about the user's forward * request(s) */ function emitForward( Gas gasUsed, LocalNative transactionFeeRefundAmount, ForwardInstruction[] storage forwardInstructions ) private { LocalNative wormholeMessageFee = getWormholeMessageFee(); // Decode delivery instructions from each 'forward' request DeliveryInstruction[] memory instructions = new DeliveryInstruction[](forwardInstructions.length); // Calculate total msg.value passed into all 'forward' requests LocalNative totalMsgValue; // Calculate total fee for all 'forward' requests LocalNative totalFee; for (uint256 i = 0; i < forwardInstructions.length;) { unchecked { totalMsgValue = totalMsgValue + forwardInstructions[i].msgValue; } instructions[i] = (forwardInstructions[i].encodedInstruction).decodeDeliveryInstruction(); totalFee = totalFee + forwardInstructions[i].deliveryPrice + forwardInstructions[i].paymentForExtraReceiverValue + wormholeMessageFee; unchecked { ++i; } } // Combine refund amount with any additional funds which were passed in to the forward as // msg.value and check that this value is enough to fund all the forwards, reverting otherwise LocalNative fundsForForward; unchecked { fundsForForward = transactionFeeRefundAmount + totalMsgValue; } if (fundsForForward.unwrap() < totalFee.unwrap()) { revert Cancelled(gasUsed, fundsForForward, totalFee); } // Simulates if the user had increased the 'paymentForExtraReceiverValue' field of their first forward // to the maximum value such that the forward(s) would still have enough funds TargetNative extraReceiverValue; try IDeliveryProvider( fromWormholeFormat(instructions[0].sourceDeliveryProvider) ).quoteAssetConversion(instructions[0].targetChain, fundsForForward - totalFee) returns (TargetNative _extraReceiverValue) { extraReceiverValue = _extraReceiverValue; } catch { revert DeliveryProviderReverted(gasUsed); } unchecked { instructions[0].extraReceiverValue = instructions[0].extraReceiverValue + extraReceiverValue; } //Publishes the DeliveryInstruction(s) for each forward request and pays the associated deliveryProvider for (uint256 i = 0; i < forwardInstructions.length;) { (, bool paymentSucceeded) = publishAndPay( wormholeMessageFee, forwardInstructions[i].deliveryPrice, // We had increased the 'paymentForExtraReceiverValue' of the first forward forwardInstructions[i].paymentForExtraReceiverValue + ((i == 0) ? (fundsForForward - totalFee) : LocalNative.wrap(0)), i == 0 ? instructions[0].encode() : forwardInstructions[i].encodedInstruction, forwardInstructions[i].consistencyLevel, forwardInstructions[i].rewardAddress ); if (!paymentSucceeded) { revert DeliveryProviderPaymentFailed(gasUsed); } unchecked { ++i; } } } function payRefunds( DeliveryInstruction memory deliveryInstruction, address payable relayerRefundAddress, LocalNative transactionFeeRefundAmount, DeliveryStatus status ) private returns (RefundStatus refundStatus) { //Amount of receiverValue that is refunded to the user (0 if the call to // 'receiveWormholeMessages' did not revert, or the full receiverValue otherwise) LocalNative receiverValueRefundAmount = LocalNative.wrap(0); if ( status == DeliveryStatus.FORWARD_REQUEST_FAILURE || status == DeliveryStatus.RECEIVER_FAILURE ) { receiverValueRefundAmount = ( deliveryInstruction.requestedReceiverValue + deliveryInstruction.extraReceiverValue ).asNative().asLocalNative(); // NOTE: instruction's target is delivery's local } // Total refund to the user // (If the forward succeeded, the 'transactionFeeRefundAmount' was used there already) LocalNative refundToRefundAddress = receiverValueRefundAmount + ( status == DeliveryStatus.FORWARD_REQUEST_SUCCESS ? LocalNative.wrap(0) : transactionFeeRefundAmount ); //Refund the user refundStatus = payRefundToRefundAddress( deliveryInstruction.refundChain, deliveryInstruction.refundAddress, refundToRefundAddress, deliveryInstruction.refundDeliveryProvider ); //If sending the user's refund failed, this gets added to the relayer's refund LocalNative leftoverUserRefund = refundToRefundAddress; if ( refundStatus == RefundStatus.REFUND_SENT || refundStatus == RefundStatus.CROSS_CHAIN_REFUND_SENT ) { leftoverUserRefund = LocalNative.wrap(0); } // Refund the relayer all remaining funds LocalNative relayerRefundAmount = calcRelayerRefundAmount(deliveryInstruction, transactionFeeRefundAmount, leftoverUserRefund); bool paymentSucceeded = pay(relayerRefundAddress, relayerRefundAmount); if(!paymentSucceeded) { revert DeliveryProviderCannotReceivePayment(); } } function calcRelayerRefundAmount( DeliveryInstruction memory deliveryInstruction, LocalNative transactionFeeRefundAmount, LocalNative leftoverUserRefund ) private view returns (LocalNative) { return msgValue() // Note: instruction's target is delivery's local - (deliveryInstruction.requestedReceiverValue + deliveryInstruction.extraReceiverValue).asNative().asLocalNative() - transactionFeeRefundAmount + leftoverUserRefund; } function payRefundToRefundAddress( uint16 refundChain, bytes32 refundAddress, LocalNative refundAmount, bytes32 relayerAddress ) private returns (RefundStatus) { // User requested refund on this chain if (refundChain == getChainId()) { return pay(payable(fromWormholeFormat(refundAddress)), refundAmount) ? RefundStatus.REFUND_SENT : RefundStatus.REFUND_FAIL; } // User requested refund on a different chain IDeliveryProvider deliveryProvider = IDeliveryProvider(fromWormholeFormat(relayerAddress)); // Determine price of an 'empty' delivery // (Note: assumes refund chain is an EVM chain) LocalNative baseDeliveryPrice; try deliveryProvider.quoteDeliveryPrice( refundChain, TargetNative.wrap(0), encodeEvmExecutionParamsV1(getEmptyEvmExecutionParamsV1()) ) returns (LocalNative quote, bytes memory) { baseDeliveryPrice = quote; } catch (bytes memory) { return RefundStatus.CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED; } // If the refundAmount is not greater than the 'empty delivery price', the refund does not go through if (refundAmount <= getWormholeMessageFee() + baseDeliveryPrice) { return RefundStatus.CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH; } // Request a 'send' with 'paymentForExtraReceiverValue' equal to the refund minus the 'empty delivery price' try IWormholeRelayerSend(address(this)).send{value: refundAmount.unwrap()}( refundChain, bytes32(0), bytes(""), TargetNative.wrap(0), refundAmount - getWormholeMessageFee() - baseDeliveryPrice, encodeEvmExecutionParamsV1(getEmptyEvmExecutionParamsV1()), refundChain, refundAddress, fromWormholeFormat(relayerAddress), new VaaKey[](0), CONSISTENCY_LEVEL_INSTANT ) returns (uint64) { return RefundStatus.CROSS_CHAIN_REFUND_SENT; } catch (bytes memory) { return RefundStatus.CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED; } } function tryDecodeExecuteInstructionError( bytes memory revertData ) private pure returns (Gas gasUsed, bool knownError) { uint offset = 0; bytes4 selector; // Check to see if the following decode can be performed if(revertData.length < 36) { return (Gas.wrap(0), false); } (selector, offset) = revertData.asBytes4Unchecked(offset); if((selector == Cancelled.selector) || (selector == DeliveryProviderReverted.selector) || (selector == DeliveryProviderPaymentFailed.selector)) { knownError = true; uint256 _gasUsed; (_gasUsed, offset) = revertData.asUint256Unchecked(offset); gasUsed = Gas.wrap(_gasUsed); } } function checkVaaKeysWithVAAs( VaaKey[] memory vaaKeys, bytes[] memory signedVaas ) private view { if (vaaKeys.length != signedVaas.length) { revert VaaKeysLengthDoesNotMatchVaasLength(vaaKeys.length, signedVaas.length); } for (uint256 i = 0; i < vaaKeys.length;) { IWormhole.VM memory parsedVaa = getWormhole().parseVM(signedVaas[i]); VaaKey memory vaaKey = vaaKeys[i]; if ( vaaKey.chainId != parsedVaa.emitterChainId || vaaKey.emitterAddress != parsedVaa.emitterAddress || vaaKey.sequence != parsedVaa.sequence ) { revert VaaKeysDoNotMatchVaas(uint8(i)); } unchecked { ++i; } } } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import {IWormhole} from "../../interfaces/IWormhole.sol"; import {IDeliveryProvider} from "../../interfaces/relayer/IDeliveryProviderTyped.sol"; import {toWormholeFormat, min, pay} from "../../libraries/relayer/Utils.sol"; import { NoDeliveryInProgress, ReentrantDelivery, ForwardRequestFromWrongAddress, DeliveryProviderDoesNotSupportTargetChain, VaaKey, InvalidMsgValue, IWormholeRelayerBase } from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import {DeliveryInstruction} from "../../libraries/relayer/RelayerInternalStructs.sol"; import { ForwardInstruction, DeliveryTmpState, getDeliveryTmpState, getRegisteredWormholeRelayersState } from "./WormholeRelayerStorage.sol"; import "../../interfaces/relayer/TypedUnits.sol"; abstract contract WormholeRelayerBase is IWormholeRelayerBase { using WeiLib for Wei; using GasLib for Gas; using WeiPriceLib for WeiPrice; using GasPriceLib for GasPrice; using LocalNativeLib for LocalNative; //see https://book.wormhole.com/wormhole/3_coreLayerContracts.html#consistency-levels // 15 is valid choice for now but ultimately we want something more canonical (202?) // Also, these values should definitely not be defined here but should be provided by IWormhole! uint8 internal constant CONSISTENCY_LEVEL_FINALIZED = 15; uint8 internal constant CONSISTENCY_LEVEL_INSTANT = 200; IWormhole private immutable wormhole_; uint16 private immutable chainId_; constructor(address _wormhole) { wormhole_ = IWormhole(_wormhole); chainId_ = uint16(wormhole_.chainId()); } function getRegisteredWormholeRelayerContract(uint16 chainId) public view returns (bytes32) { return getRegisteredWormholeRelayersState().registeredWormholeRelayers[chainId]; } //Our get functions require view instead of pure (despite not actually reading storage) because // they can't be evaluated at compile time. (https://ethereum.stackexchange.com/a/120630/103366) function getWormhole() internal view returns (IWormhole) { return wormhole_; } function getChainId() internal view returns (uint16) { return chainId_; } function getWormholeMessageFee() internal view returns (LocalNative) { return LocalNative.wrap(getWormhole().messageFee()); } function msgValue() internal view returns (LocalNative) { return LocalNative.wrap(msg.value); } function checkMsgValue( LocalNative wormholeMessageFee, LocalNative deliveryPrice, LocalNative paymentForExtraReceiverValue ) internal view { if (msgValue() != deliveryPrice + paymentForExtraReceiverValue + wormholeMessageFee) { revert InvalidMsgValue( msgValue(), deliveryPrice + paymentForExtraReceiverValue + wormholeMessageFee ); } } function publishAndPay( LocalNative wormholeMessageFee, LocalNative deliveryQuote, LocalNative paymentForExtraReceiverValue, bytes memory encodedInstruction, uint8 consistencyLevel, address payable rewardAddress ) internal returns (uint64 sequence, bool paymentSucceeded) { sequence = getWormhole().publishMessage{value: wormholeMessageFee.unwrap()}( 0, encodedInstruction, consistencyLevel ); paymentSucceeded = pay( rewardAddress, deliveryQuote + paymentForExtraReceiverValue ); emit SendEvent(sequence, deliveryQuote, paymentForExtraReceiverValue); } // ----------------------- delivery transaction temorary storage functions ----------------------- function startDelivery(address targetAddress, address deliveryProvider, uint16 refundChain, bytes32 refundAddress) internal { DeliveryTmpState storage state = getDeliveryTmpState(); if (state.deliveryInProgress) { revert ReentrantDelivery(msg.sender, state.deliveryTarget); } state.deliveryInProgress = true; state.deliveryTarget = targetAddress; state.deliveryProvider = deliveryProvider; state.refundChain = refundChain; state.refundAddress = refundAddress; } function finishDelivery() internal { DeliveryTmpState storage state = getDeliveryTmpState(); state.deliveryInProgress = false; state.deliveryTarget = address(0); state.deliveryProvider = address(0); state.refundChain = 0; state.refundAddress = bytes32(0); delete state.forwardInstructions; } function appendForwardInstruction(ForwardInstruction memory forwardInstruction) internal { getDeliveryTmpState().forwardInstructions.push(forwardInstruction); } function getForwardInstructions() internal view returns (ForwardInstruction[] storage) { return getDeliveryTmpState().forwardInstructions; } function getOriginalDeliveryProvider() internal view returns (address) { return getDeliveryTmpState().deliveryProvider; } function getCurrentRefundChain() internal view returns (uint16) { return getDeliveryTmpState().refundChain; } function getCurrentRefundAddress() internal view returns (bytes32) { return getDeliveryTmpState().refundAddress; } function checkMsgSenderInDelivery() internal view { DeliveryTmpState storage state = getDeliveryTmpState(); if (!state.deliveryInProgress) { revert NoDeliveryInProgress(); } if (msg.sender != state.deliveryTarget) { revert ForwardRequestFromWrongAddress(msg.sender, state.deliveryTarget); } } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; type WeiPrice is uint256; type GasPrice is uint256; type Gas is uint256; type Dollar is uint256; type Wei is uint256; type LocalNative is uint256; type TargetNative is uint256; using { addWei as +, subWei as -, lteWei as <=, ltWei as <, gtWei as >, eqWei as ==, neqWei as != } for Wei global; using {addTargetNative as +, subTargetNative as -} for TargetNative global; using { leLocalNative as <, leqLocalNative as <=, neqLocalNative as !=, addLocalNative as +, subLocalNative as - } for LocalNative global; using { ltGas as <, lteGas as <=, subGas as - } for Gas global; using WeiLib for Wei; using GasLib for Gas; using DollarLib for Dollar; using WeiPriceLib for WeiPrice; using GasPriceLib for GasPrice; function ltWei(Wei a, Wei b) pure returns (bool) { return Wei.unwrap(a) < Wei.unwrap(b); } function eqWei(Wei a, Wei b) pure returns (bool) { return Wei.unwrap(a) == Wei.unwrap(b); } function gtWei(Wei a, Wei b) pure returns (bool) { return Wei.unwrap(a) > Wei.unwrap(b); } function lteWei(Wei a, Wei b) pure returns (bool) { return Wei.unwrap(a) <= Wei.unwrap(b); } function subWei(Wei a, Wei b) pure returns (Wei) { return Wei.wrap(Wei.unwrap(a) - Wei.unwrap(b)); } function addWei(Wei a, Wei b) pure returns (Wei) { return Wei.wrap(Wei.unwrap(a) + Wei.unwrap(b)); } function neqWei(Wei a, Wei b) pure returns (bool) { return Wei.unwrap(a) != Wei.unwrap(b); } function ltGas(Gas a, Gas b) pure returns (bool) { return Gas.unwrap(a) < Gas.unwrap(b); } function lteGas(Gas a, Gas b) pure returns (bool) { return Gas.unwrap(a) <= Gas.unwrap(b); } function subGas(Gas a, Gas b) pure returns (Gas) { return Gas.wrap(Gas.unwrap(a) - Gas.unwrap(b)); } function addTargetNative(TargetNative a, TargetNative b) pure returns (TargetNative) { return TargetNative.wrap(TargetNative.unwrap(a) + TargetNative.unwrap(b)); } function subTargetNative(TargetNative a, TargetNative b) pure returns (TargetNative) { return TargetNative.wrap(TargetNative.unwrap(a) - TargetNative.unwrap(b)); } function addLocalNative(LocalNative a, LocalNative b) pure returns (LocalNative) { return LocalNative.wrap(LocalNative.unwrap(a) + LocalNative.unwrap(b)); } function subLocalNative(LocalNative a, LocalNative b) pure returns (LocalNative) { return LocalNative.wrap(LocalNative.unwrap(a) - LocalNative.unwrap(b)); } function neqLocalNative(LocalNative a, LocalNative b) pure returns (bool) { return LocalNative.unwrap(a) != LocalNative.unwrap(b); } function leLocalNative(LocalNative a, LocalNative b) pure returns (bool) { return LocalNative.unwrap(a) < LocalNative.unwrap(b); } function leqLocalNative(LocalNative a, LocalNative b) pure returns (bool) { return LocalNative.unwrap(a) <= LocalNative.unwrap(b); } library WeiLib { using { toDollars, toGas, convertAsset, min, max, scale, unwrap, asGasPrice, asTargetNative, asLocalNative } for Wei; function min(Wei x, Wei maxVal) internal pure returns (Wei) { return x > maxVal ? maxVal : x; } function max(Wei x, Wei maxVal) internal pure returns (Wei) { return x < maxVal ? maxVal : x; } function asTargetNative(Wei w) internal pure returns (TargetNative) { return TargetNative.wrap(Wei.unwrap(w)); } function asLocalNative(Wei w) internal pure returns (LocalNative) { return LocalNative.wrap(Wei.unwrap(w)); } function toDollars(Wei w, WeiPrice price) internal pure returns (Dollar) { return Dollar.wrap(Wei.unwrap(w) * WeiPrice.unwrap(price)); } function toGas(Wei w, GasPrice price) internal pure returns (Gas) { return Gas.wrap(Wei.unwrap(w) / GasPrice.unwrap(price)); } function scale(Wei w, Gas num, Gas denom) internal pure returns (Wei) { return Wei.wrap(Wei.unwrap(w) * Gas.unwrap(num) / Gas.unwrap(denom)); } function unwrap(Wei w) internal pure returns (uint256) { return Wei.unwrap(w); } function asGasPrice(Wei w) internal pure returns (GasPrice) { return GasPrice.wrap(Wei.unwrap(w)); } function convertAsset( Wei w, WeiPrice fromPrice, WeiPrice toPrice, uint32 multiplierNum, uint32 multiplierDenom, bool roundUp ) internal pure returns (Wei) { Dollar numerator = w.toDollars(fromPrice).mul(multiplierNum); WeiPrice denom = toPrice.mul(multiplierDenom); Wei res = numerator.toWei(denom, roundUp); return res; } } library GasLib { using {toWei, unwrap} for Gas; function min(Gas x, Gas maxVal) internal pure returns (Gas) { return x < maxVal ? x : maxVal; } function toWei(Gas w, GasPrice price) internal pure returns (Wei) { return Wei.wrap(w.unwrap() * price.unwrap()); } function unwrap(Gas w) internal pure returns (uint256) { return Gas.unwrap(w); } } library DollarLib { using {toWei, mul, unwrap} for Dollar; function mul(Dollar a, uint256 b) internal pure returns (Dollar) { return Dollar.wrap(a.unwrap() * b); } function toWei(Dollar w, WeiPrice price, bool roundUp) internal pure returns (Wei) { return Wei.wrap((w.unwrap() + (roundUp ? price.unwrap() - 1 : 0)) / price.unwrap()); } function toGas(Dollar w, GasPrice price, WeiPrice weiPrice) internal pure returns (Gas) { return w.toWei(weiPrice, false).toGas(price); } function unwrap(Dollar w) internal pure returns (uint256) { return Dollar.unwrap(w); } } library WeiPriceLib { using {mul, unwrap} for WeiPrice; function mul(WeiPrice a, uint256 b) internal pure returns (WeiPrice) { return WeiPrice.wrap(a.unwrap() * b); } function unwrap(WeiPrice w) internal pure returns (uint256) { return WeiPrice.unwrap(w); } } library GasPriceLib { using {unwrap, priceAsWei} for GasPrice; function priceAsWei(GasPrice w) internal pure returns (Wei) { return Wei.wrap(w.unwrap()); } function unwrap(GasPrice w) internal pure returns (uint256) { return GasPrice.unwrap(w); } } library TargetNativeLib { using {unwrap, asNative} for TargetNative; function unwrap(TargetNative w) internal pure returns (uint256) { return TargetNative.unwrap(w); } function asNative(TargetNative w) internal pure returns (Wei) { return Wei.wrap(TargetNative.unwrap(w)); } } library LocalNativeLib { using {unwrap, asNative} for LocalNative; function unwrap(LocalNative w) internal pure returns (uint256) { return LocalNative.unwrap(w); } function asNative(LocalNative w) internal pure returns (Wei) { return Wei.wrap(LocalNative.unwrap(w)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.2; import "../beacon/IBeacon.sol"; import "../../utils/Address.sol"; import "../../utils/StorageSlot.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. * * _Available since v4.1._ * * @custom:oz-upgrades-unsafe-allow delegatecall */ abstract contract ERC1967Upgrade { // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Returns the current implementation address. */ function _getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Perform implementation upgrade * * Emits an {Upgraded} event. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Perform implementation upgrade with additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCall( address newImplementation, bytes memory data, bool forceCall ) internal { _upgradeTo(newImplementation); if (data.length > 0 || forceCall) { Address.functionDelegateCall(newImplementation, data); } } /** * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCallSecure( address newImplementation, bytes memory data, bool forceCall ) internal { address oldImplementation = _getImplementation(); // Initial upgrade and setup call _setImplementation(newImplementation); if (data.length > 0 || forceCall) { Address.functionDelegateCall(newImplementation, data); } // Perform rollback test if not already in progress StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); if (!rollbackTesting.value) { // Trigger rollback using upgradeTo from the new implementation rollbackTesting.value = true; Address.functionDelegateCall( newImplementation, abi.encodeWithSignature("upgradeTo(address)", oldImplementation) ); rollbackTesting.value = false; // Check rollback was effective require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); // Finally reset to the new implementation and log the upgrade _upgradeTo(newImplementation); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Returns the current admin. */ function _getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; } /** * @dev Stores a new address in the EIP1967 admin slot. */ function _setAdmin(address newAdmin) private { require(newAdmin != address(0), "ERC1967: new admin is the zero address"); StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. */ function _changeAdmin(address newAdmin) internal { emit AdminChanged(_getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. */ bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Emitted when the beacon is upgraded. */ event BeaconUpgraded(address indexed beacon); /** * @dev Returns the current beacon. */ function _getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(_BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); require( Address.isContract(IBeacon(newBeacon).implementation()), "ERC1967: beacon implementation is not a contract" ); StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; } /** * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). * * Emits a {BeaconUpgraded} event. */ function _upgradeBeaconToAndCall( address newBeacon, bytes memory data, bool forceCall ) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0 || forceCall) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } } }
// 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(uint numGuardians) external pure returns (uint 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; import "../../interfaces/relayer/TypedUnits.sol"; error NotAnEvmAddress(bytes32); function pay(address payable receiver, LocalNative amount) returns (bool success) { uint256 amount_ = LocalNative.unwrap(amount); if (amount_ != 0) // TODO: we currently ignore the return data. Some users of this function might want to bubble up the return value though. // Specifying a higher limit than 63/64 of the remaining gas caps it at that amount without throwing an exception. (success,) = returnLengthBoundedCall(receiver, new bytes(0), gasleft(), amount_, 0); else success = true; } function min(uint256 a, uint256 b) pure returns (uint256) { return a < b ? a : b; } function min(uint64 a, uint64 b) pure returns (uint64) { return a < b ? a : b; } function max(uint256 a, uint256 b) pure returns (uint256) { return a > b ? a : b; } 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))); } function fromWormholeFormatUnchecked(bytes32 whFormatAddress) pure returns (address) { return address(uint160(uint256(whFormatAddress))); } uint256 constant freeMemoryPtr = 0x40; uint256 constant memoryWord = 32; uint256 constant maskModulo32 = 0x1f; /** * Implements call that truncates return data to a specific size to avoid excessive gas consumption for relayers * when a revert or unexpectedly large return value is produced by the call. * * @param returnedData Buffer of returned data truncated to the first `dataLengthBound` bytes. */ function returnLengthBoundedCall( address payable callee, bytes memory callData, uint256 gasLimit, uint256 value, uint256 dataLengthBound ) returns (bool success, bytes memory returnedData) { uint256 callDataLength = callData.length; assembly ("memory-safe") { returnedData := mload(freeMemoryPtr) let returnedDataBuffer := add(returnedData, memoryWord) let callDataBuffer := add(callData, memoryWord) success := call(gasLimit, callee, value, callDataBuffer, callDataLength, returnedDataBuffer, dataLengthBound) let returnedDataSize := returndatasize() switch lt(dataLengthBound, returnedDataSize) case 1 { returnedDataSize := dataLengthBound } default {} mstore(returnedData, returnedDataSize) // Here we update the free memory pointer. // We want to pad `returnedData` to memory word size, i.e. 32 bytes. // Note that negating bitwise `maskModulo32` produces a mask that aligns addressing to 32 bytes. // This allows us to pad the entire `bytes` structure (length + buffer) to 32 bytes at the end. // We add `maskModulo32` to get the next free memory "slot" in case the `returnedDataSize` is not a multiple of the memory word size. // // Rationale: // We do not care about the alignment of the free memory pointer. The solidity compiler documentation does not promise nor require alignment on it. // It does however lightly suggest to pad `bytes` structures to 32 bytes: https://docs.soliditylang.org/en/v0.8.20/assembly.html#example // Searching for "alignment" and "padding" in https://gitter.im/ethereum/solidity-dev // yielded the following at the time of writing – paraphrased: // > It's possible that the compiler cleans that padding in some cases. Users should not rely on the compiler never doing that. // This means that we want to ensure that the free memory pointer points to memory just after this padding for our `returnedData` `bytes` structure. let paddedPastTheEndOffset := and(add(returnedDataSize, maskModulo32), not(maskModulo32)) let newFreeMemoryPtr := add(returnedDataBuffer, paddedPastTheEndOffset) mstore(freeMemoryPtr, newFreeMemoryPtr) } }
pragma solidity ^0.8.19; library BytesParsing { uint256 private constant freeMemoryPtr = 0x40; uint256 private constant wordSize = 32; error OutOfBounds(uint256 offset, uint256 length); function checkBound(uint offset, uint length) internal pure { if (offset > length) revert OutOfBounds(offset, length); } function sliceUnchecked( bytes memory encoded, uint offset, uint length ) internal pure returns (bytes memory ret, uint nextOffset) { //bail early for degenerate case if (length == 0) return (new bytes(0), offset); assembly ("memory-safe") { nextOffset := add(offset, length) ret := mload(freeMemoryPtr) //Explanation on how we copy data here: // The bytes type has the following layout in memory: // [length: 32 bytes, data: length bytes] // So if we allocate `bytes memory foo = new bytes(1);` then `foo` will be a pointer to 33 // bytes where the first 32 bytes contain the length and the last byte is the actual data. // Since mload always loads 32 bytes of memory at once, we use our shift variable to align // our reads so that our last read lines up exactly with the last 32 bytes of `encoded`. // However this also means that if the length of `encoded` is not a multiple of 32 bytes, our // first read will necessarily partly contain bytes from `encoded`'s 32 length bytes that // will be written into the length part of our `ret` slice. // We remedy this issue by writing the length of our `ret` slice at the end, thus // overwritting those garbage bytes. let shift := and(length, 31) //equivalent to `mod(length, 32)` but 2 gas cheaper if iszero(shift) { shift := wordSize } let dest := add(ret, shift) let end := add(dest, length) for { let src := add(add(encoded, shift), offset) } lt(dest, end) { src := add(src, wordSize) dest := add(dest, wordSize) } { mstore(dest, mload(src)) } mstore(ret, length) //When compiling with --via-ir then normally allocated memory (i.e. via new) will have 32 byte // memory alignment and so we enforce the same memory alignment here. mstore(freeMemoryPtr, and(add(dest, 31), not(31))) } } function slice( bytes memory encoded, uint offset, uint length ) internal pure returns (bytes memory ret, uint nextOffset) { (ret, nextOffset) = sliceUnchecked(encoded, offset, length); checkBound(nextOffset, encoded.length); } function asAddressUnchecked( bytes memory encoded, uint offset ) internal pure returns (address, uint) { (uint160 ret, uint nextOffset) = asUint160(encoded, offset); return (address(ret), nextOffset); } function asAddress( bytes memory encoded, uint offset ) internal pure returns (address ret, uint nextOffset) { (ret, nextOffset) = asAddressUnchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBoolUnckecked( bytes memory encoded, uint offset ) internal pure returns (bool, uint) { (uint8 ret, uint nextOffset) = asUint8(encoded, offset); return (ret != 0, nextOffset); } function asBool( bytes memory encoded, uint offset ) internal pure returns (bool ret, uint nextOffset) { (ret, nextOffset) = asBoolUnckecked(encoded, offset); checkBound(nextOffset, encoded.length); } /* ------------------------------------------------------------------------------------------------- Remaining library code below was auto-generated by via the following js/node code: for (let bytes = 1; bytes <= 32; ++bytes) { const bits = bytes*8; console.log( `function asUint${bits}Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint${bits} ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, ${bytes}) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint${bits}( bytes memory encoded, uint offset ) internal pure returns (uint${bits} ret, uint nextOffset) { (ret, nextOffset) = asUint${bits}Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes${bytes}Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes${bytes}, uint) { (uint${bits} ret, uint nextOffset) = asUint${bits}Unchecked(encoded, offset); return (bytes${bytes}(ret), nextOffset); } function asBytes${bytes}( bytes memory encoded, uint offset ) internal pure returns (bytes${bytes}, uint) { (uint${bits} ret, uint nextOffset) = asUint${bits}(encoded, offset); return (bytes${bytes}(ret), nextOffset); } ` ); } ------------------------------------------------------------------------------------------------- */ function asUint8Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint8 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 1) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint8( bytes memory encoded, uint offset ) internal pure returns (uint8 ret, uint nextOffset) { (ret, nextOffset) = asUint8Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes1Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes1, uint) { (uint8 ret, uint nextOffset) = asUint8Unchecked(encoded, offset); return (bytes1(ret), nextOffset); } function asBytes1( bytes memory encoded, uint offset ) internal pure returns (bytes1, uint) { (uint8 ret, uint nextOffset) = asUint8(encoded, offset); return (bytes1(ret), nextOffset); } function asUint16Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint16 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 2) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint16( bytes memory encoded, uint offset ) internal pure returns (uint16 ret, uint nextOffset) { (ret, nextOffset) = asUint16Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes2Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes2, uint) { (uint16 ret, uint nextOffset) = asUint16Unchecked(encoded, offset); return (bytes2(ret), nextOffset); } function asBytes2( bytes memory encoded, uint offset ) internal pure returns (bytes2, uint) { (uint16 ret, uint nextOffset) = asUint16(encoded, offset); return (bytes2(ret), nextOffset); } function asUint24Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint24 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 3) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint24( bytes memory encoded, uint offset ) internal pure returns (uint24 ret, uint nextOffset) { (ret, nextOffset) = asUint24Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes3Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes3, uint) { (uint24 ret, uint nextOffset) = asUint24Unchecked(encoded, offset); return (bytes3(ret), nextOffset); } function asBytes3( bytes memory encoded, uint offset ) internal pure returns (bytes3, uint) { (uint24 ret, uint nextOffset) = asUint24(encoded, offset); return (bytes3(ret), nextOffset); } function asUint32Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint32 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 4) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint32( bytes memory encoded, uint offset ) internal pure returns (uint32 ret, uint nextOffset) { (ret, nextOffset) = asUint32Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes4Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes4, uint) { (uint32 ret, uint nextOffset) = asUint32Unchecked(encoded, offset); return (bytes4(ret), nextOffset); } function asBytes4( bytes memory encoded, uint offset ) internal pure returns (bytes4, uint) { (uint32 ret, uint nextOffset) = asUint32(encoded, offset); return (bytes4(ret), nextOffset); } function asUint40Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint40 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 5) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint40( bytes memory encoded, uint offset ) internal pure returns (uint40 ret, uint nextOffset) { (ret, nextOffset) = asUint40Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes5Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes5, uint) { (uint40 ret, uint nextOffset) = asUint40Unchecked(encoded, offset); return (bytes5(ret), nextOffset); } function asBytes5( bytes memory encoded, uint offset ) internal pure returns (bytes5, uint) { (uint40 ret, uint nextOffset) = asUint40(encoded, offset); return (bytes5(ret), nextOffset); } function asUint48Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint48 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 6) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint48( bytes memory encoded, uint offset ) internal pure returns (uint48 ret, uint nextOffset) { (ret, nextOffset) = asUint48Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes6Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes6, uint) { (uint48 ret, uint nextOffset) = asUint48Unchecked(encoded, offset); return (bytes6(ret), nextOffset); } function asBytes6( bytes memory encoded, uint offset ) internal pure returns (bytes6, uint) { (uint48 ret, uint nextOffset) = asUint48(encoded, offset); return (bytes6(ret), nextOffset); } function asUint56Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint56 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 7) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint56( bytes memory encoded, uint offset ) internal pure returns (uint56 ret, uint nextOffset) { (ret, nextOffset) = asUint56Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes7Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes7, uint) { (uint56 ret, uint nextOffset) = asUint56Unchecked(encoded, offset); return (bytes7(ret), nextOffset); } function asBytes7( bytes memory encoded, uint offset ) internal pure returns (bytes7, uint) { (uint56 ret, uint nextOffset) = asUint56(encoded, offset); return (bytes7(ret), nextOffset); } function asUint64Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint64 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 8) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint64( bytes memory encoded, uint offset ) internal pure returns (uint64 ret, uint nextOffset) { (ret, nextOffset) = asUint64Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes8Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes8, uint) { (uint64 ret, uint nextOffset) = asUint64Unchecked(encoded, offset); return (bytes8(ret), nextOffset); } function asBytes8( bytes memory encoded, uint offset ) internal pure returns (bytes8, uint) { (uint64 ret, uint nextOffset) = asUint64(encoded, offset); return (bytes8(ret), nextOffset); } function asUint72Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint72 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 9) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint72( bytes memory encoded, uint offset ) internal pure returns (uint72 ret, uint nextOffset) { (ret, nextOffset) = asUint72Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes9Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes9, uint) { (uint72 ret, uint nextOffset) = asUint72Unchecked(encoded, offset); return (bytes9(ret), nextOffset); } function asBytes9( bytes memory encoded, uint offset ) internal pure returns (bytes9, uint) { (uint72 ret, uint nextOffset) = asUint72(encoded, offset); return (bytes9(ret), nextOffset); } function asUint80Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint80 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 10) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint80( bytes memory encoded, uint offset ) internal pure returns (uint80 ret, uint nextOffset) { (ret, nextOffset) = asUint80Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes10Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes10, uint) { (uint80 ret, uint nextOffset) = asUint80Unchecked(encoded, offset); return (bytes10(ret), nextOffset); } function asBytes10( bytes memory encoded, uint offset ) internal pure returns (bytes10, uint) { (uint80 ret, uint nextOffset) = asUint80(encoded, offset); return (bytes10(ret), nextOffset); } function asUint88Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint88 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 11) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint88( bytes memory encoded, uint offset ) internal pure returns (uint88 ret, uint nextOffset) { (ret, nextOffset) = asUint88Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes11Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes11, uint) { (uint88 ret, uint nextOffset) = asUint88Unchecked(encoded, offset); return (bytes11(ret), nextOffset); } function asBytes11( bytes memory encoded, uint offset ) internal pure returns (bytes11, uint) { (uint88 ret, uint nextOffset) = asUint88(encoded, offset); return (bytes11(ret), nextOffset); } function asUint96Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint96 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 12) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint96( bytes memory encoded, uint offset ) internal pure returns (uint96 ret, uint nextOffset) { (ret, nextOffset) = asUint96Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes12Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes12, uint) { (uint96 ret, uint nextOffset) = asUint96Unchecked(encoded, offset); return (bytes12(ret), nextOffset); } function asBytes12( bytes memory encoded, uint offset ) internal pure returns (bytes12, uint) { (uint96 ret, uint nextOffset) = asUint96(encoded, offset); return (bytes12(ret), nextOffset); } function asUint104Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint104 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 13) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint104( bytes memory encoded, uint offset ) internal pure returns (uint104 ret, uint nextOffset) { (ret, nextOffset) = asUint104Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes13Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes13, uint) { (uint104 ret, uint nextOffset) = asUint104Unchecked(encoded, offset); return (bytes13(ret), nextOffset); } function asBytes13( bytes memory encoded, uint offset ) internal pure returns (bytes13, uint) { (uint104 ret, uint nextOffset) = asUint104(encoded, offset); return (bytes13(ret), nextOffset); } function asUint112Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint112 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 14) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint112( bytes memory encoded, uint offset ) internal pure returns (uint112 ret, uint nextOffset) { (ret, nextOffset) = asUint112Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes14Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes14, uint) { (uint112 ret, uint nextOffset) = asUint112Unchecked(encoded, offset); return (bytes14(ret), nextOffset); } function asBytes14( bytes memory encoded, uint offset ) internal pure returns (bytes14, uint) { (uint112 ret, uint nextOffset) = asUint112(encoded, offset); return (bytes14(ret), nextOffset); } function asUint120Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint120 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 15) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint120( bytes memory encoded, uint offset ) internal pure returns (uint120 ret, uint nextOffset) { (ret, nextOffset) = asUint120Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes15Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes15, uint) { (uint120 ret, uint nextOffset) = asUint120Unchecked(encoded, offset); return (bytes15(ret), nextOffset); } function asBytes15( bytes memory encoded, uint offset ) internal pure returns (bytes15, uint) { (uint120 ret, uint nextOffset) = asUint120(encoded, offset); return (bytes15(ret), nextOffset); } function asUint128Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint128 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 16) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint128( bytes memory encoded, uint offset ) internal pure returns (uint128 ret, uint nextOffset) { (ret, nextOffset) = asUint128Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes16Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes16, uint) { (uint128 ret, uint nextOffset) = asUint128Unchecked(encoded, offset); return (bytes16(ret), nextOffset); } function asBytes16( bytes memory encoded, uint offset ) internal pure returns (bytes16, uint) { (uint128 ret, uint nextOffset) = asUint128(encoded, offset); return (bytes16(ret), nextOffset); } function asUint136Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint136 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 17) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint136( bytes memory encoded, uint offset ) internal pure returns (uint136 ret, uint nextOffset) { (ret, nextOffset) = asUint136Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes17Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes17, uint) { (uint136 ret, uint nextOffset) = asUint136Unchecked(encoded, offset); return (bytes17(ret), nextOffset); } function asBytes17( bytes memory encoded, uint offset ) internal pure returns (bytes17, uint) { (uint136 ret, uint nextOffset) = asUint136(encoded, offset); return (bytes17(ret), nextOffset); } function asUint144Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint144 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 18) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint144( bytes memory encoded, uint offset ) internal pure returns (uint144 ret, uint nextOffset) { (ret, nextOffset) = asUint144Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes18Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes18, uint) { (uint144 ret, uint nextOffset) = asUint144Unchecked(encoded, offset); return (bytes18(ret), nextOffset); } function asBytes18( bytes memory encoded, uint offset ) internal pure returns (bytes18, uint) { (uint144 ret, uint nextOffset) = asUint144(encoded, offset); return (bytes18(ret), nextOffset); } function asUint152Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint152 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 19) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint152( bytes memory encoded, uint offset ) internal pure returns (uint152 ret, uint nextOffset) { (ret, nextOffset) = asUint152Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes19Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes19, uint) { (uint152 ret, uint nextOffset) = asUint152Unchecked(encoded, offset); return (bytes19(ret), nextOffset); } function asBytes19( bytes memory encoded, uint offset ) internal pure returns (bytes19, uint) { (uint152 ret, uint nextOffset) = asUint152(encoded, offset); return (bytes19(ret), nextOffset); } function asUint160Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint160 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 20) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint160( bytes memory encoded, uint offset ) internal pure returns (uint160 ret, uint nextOffset) { (ret, nextOffset) = asUint160Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes20Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes20, uint) { (uint160 ret, uint nextOffset) = asUint160Unchecked(encoded, offset); return (bytes20(ret), nextOffset); } function asBytes20( bytes memory encoded, uint offset ) internal pure returns (bytes20, uint) { (uint160 ret, uint nextOffset) = asUint160(encoded, offset); return (bytes20(ret), nextOffset); } function asUint168Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint168 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 21) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint168( bytes memory encoded, uint offset ) internal pure returns (uint168 ret, uint nextOffset) { (ret, nextOffset) = asUint168Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes21Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes21, uint) { (uint168 ret, uint nextOffset) = asUint168Unchecked(encoded, offset); return (bytes21(ret), nextOffset); } function asBytes21( bytes memory encoded, uint offset ) internal pure returns (bytes21, uint) { (uint168 ret, uint nextOffset) = asUint168(encoded, offset); return (bytes21(ret), nextOffset); } function asUint176Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint176 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 22) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint176( bytes memory encoded, uint offset ) internal pure returns (uint176 ret, uint nextOffset) { (ret, nextOffset) = asUint176Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes22Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes22, uint) { (uint176 ret, uint nextOffset) = asUint176Unchecked(encoded, offset); return (bytes22(ret), nextOffset); } function asBytes22( bytes memory encoded, uint offset ) internal pure returns (bytes22, uint) { (uint176 ret, uint nextOffset) = asUint176(encoded, offset); return (bytes22(ret), nextOffset); } function asUint184Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint184 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 23) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint184( bytes memory encoded, uint offset ) internal pure returns (uint184 ret, uint nextOffset) { (ret, nextOffset) = asUint184Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes23Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes23, uint) { (uint184 ret, uint nextOffset) = asUint184Unchecked(encoded, offset); return (bytes23(ret), nextOffset); } function asBytes23( bytes memory encoded, uint offset ) internal pure returns (bytes23, uint) { (uint184 ret, uint nextOffset) = asUint184(encoded, offset); return (bytes23(ret), nextOffset); } function asUint192Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint192 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 24) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint192( bytes memory encoded, uint offset ) internal pure returns (uint192 ret, uint nextOffset) { (ret, nextOffset) = asUint192Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes24Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes24, uint) { (uint192 ret, uint nextOffset) = asUint192Unchecked(encoded, offset); return (bytes24(ret), nextOffset); } function asBytes24( bytes memory encoded, uint offset ) internal pure returns (bytes24, uint) { (uint192 ret, uint nextOffset) = asUint192(encoded, offset); return (bytes24(ret), nextOffset); } function asUint200Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint200 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 25) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint200( bytes memory encoded, uint offset ) internal pure returns (uint200 ret, uint nextOffset) { (ret, nextOffset) = asUint200Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes25Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes25, uint) { (uint200 ret, uint nextOffset) = asUint200Unchecked(encoded, offset); return (bytes25(ret), nextOffset); } function asBytes25( bytes memory encoded, uint offset ) internal pure returns (bytes25, uint) { (uint200 ret, uint nextOffset) = asUint200(encoded, offset); return (bytes25(ret), nextOffset); } function asUint208Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint208 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 26) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint208( bytes memory encoded, uint offset ) internal pure returns (uint208 ret, uint nextOffset) { (ret, nextOffset) = asUint208Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes26Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes26, uint) { (uint208 ret, uint nextOffset) = asUint208Unchecked(encoded, offset); return (bytes26(ret), nextOffset); } function asBytes26( bytes memory encoded, uint offset ) internal pure returns (bytes26, uint) { (uint208 ret, uint nextOffset) = asUint208(encoded, offset); return (bytes26(ret), nextOffset); } function asUint216Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint216 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 27) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint216( bytes memory encoded, uint offset ) internal pure returns (uint216 ret, uint nextOffset) { (ret, nextOffset) = asUint216Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes27Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes27, uint) { (uint216 ret, uint nextOffset) = asUint216Unchecked(encoded, offset); return (bytes27(ret), nextOffset); } function asBytes27( bytes memory encoded, uint offset ) internal pure returns (bytes27, uint) { (uint216 ret, uint nextOffset) = asUint216(encoded, offset); return (bytes27(ret), nextOffset); } function asUint224Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint224 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 28) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint224( bytes memory encoded, uint offset ) internal pure returns (uint224 ret, uint nextOffset) { (ret, nextOffset) = asUint224Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes28Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes28, uint) { (uint224 ret, uint nextOffset) = asUint224Unchecked(encoded, offset); return (bytes28(ret), nextOffset); } function asBytes28( bytes memory encoded, uint offset ) internal pure returns (bytes28, uint) { (uint224 ret, uint nextOffset) = asUint224(encoded, offset); return (bytes28(ret), nextOffset); } function asUint232Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint232 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 29) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint232( bytes memory encoded, uint offset ) internal pure returns (uint232 ret, uint nextOffset) { (ret, nextOffset) = asUint232Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes29Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes29, uint) { (uint232 ret, uint nextOffset) = asUint232Unchecked(encoded, offset); return (bytes29(ret), nextOffset); } function asBytes29( bytes memory encoded, uint offset ) internal pure returns (bytes29, uint) { (uint232 ret, uint nextOffset) = asUint232(encoded, offset); return (bytes29(ret), nextOffset); } function asUint240Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint240 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 30) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint240( bytes memory encoded, uint offset ) internal pure returns (uint240 ret, uint nextOffset) { (ret, nextOffset) = asUint240Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes30Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes30, uint) { (uint240 ret, uint nextOffset) = asUint240Unchecked(encoded, offset); return (bytes30(ret), nextOffset); } function asBytes30( bytes memory encoded, uint offset ) internal pure returns (bytes30, uint) { (uint240 ret, uint nextOffset) = asUint240(encoded, offset); return (bytes30(ret), nextOffset); } function asUint248Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint248 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 31) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint248( bytes memory encoded, uint offset ) internal pure returns (uint248 ret, uint nextOffset) { (ret, nextOffset) = asUint248Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes31Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes31, uint) { (uint248 ret, uint nextOffset) = asUint248Unchecked(encoded, offset); return (bytes31(ret), nextOffset); } function asBytes31( bytes memory encoded, uint offset ) internal pure returns (bytes31, uint) { (uint248 ret, uint nextOffset) = asUint248(encoded, offset); return (bytes31(ret), nextOffset); } function asUint256Unchecked( bytes memory encoded, uint offset ) internal pure returns (uint256 ret, uint nextOffset) { assembly ("memory-safe") { nextOffset := add(offset, 32) ret := mload(add(encoded, nextOffset)) } return (ret, nextOffset); } function asUint256( bytes memory encoded, uint offset ) internal pure returns (uint256 ret, uint nextOffset) { (ret, nextOffset) = asUint256Unchecked(encoded, offset); checkBound(nextOffset, encoded.length); } function asBytes32Unchecked( bytes memory encoded, uint offset ) internal pure returns (bytes32, uint) { (uint256 ret, uint nextOffset) = asUint256Unchecked(encoded, offset); return (bytes32(ret), nextOffset); } function asBytes32( bytes memory encoded, uint offset ) internal pure returns (bytes32, uint) { (uint256 ret, uint nextOffset) = asUint256(encoded, offset); return (bytes32(ret), nextOffset); } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "./TypedUnits.sol"; interface IDeliveryProvider { function quoteDeliveryPrice( uint16 targetChain, TargetNative receiverValue, bytes memory encodedExecutionParams ) external view returns (LocalNative nativePriceQuote, bytes memory encodedExecutionInfo); function quoteAssetConversion( uint16 targetChain, LocalNative currentChainAmount ) external view returns (TargetNative targetChainAmount); /** * @notice This function should return a payable address on this (source) chain where all awards * should be sent for the relay provider. * */ function getRewardAddress() external view returns (address payable rewardAddress); /** * @notice This function determines whether a relay provider supports deliveries to a given chain * or not. * * @param targetChain - The chain which is being delivered to. */ function isChainSupported(uint16 targetChain) external view returns (bool supported); /** * @notice If a DeliveryProvider supports a given chain, this function should provide the contract * address (in wormhole format) of the relay provider on that chain. * * @param targetChain - The chain which is being delivered to. */ function getTargetChainAddress(uint16 targetChain) external view returns (bytes32 deliveryProviderAddress); }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import "../../interfaces/relayer/TypedUnits.sol"; import "../../interfaces/relayer/IWormholeRelayerTyped.sol"; struct DeliveryInstruction { uint16 targetChain; bytes32 targetAddress; bytes payload; TargetNative requestedReceiverValue; TargetNative extraReceiverValue; bytes encodedExecutionInfo; uint16 refundChain; bytes32 refundAddress; bytes32 refundDeliveryProvider; bytes32 sourceDeliveryProvider; bytes32 senderAddress; VaaKey[] vaaKeys; } // Meant to hold all necessary values for `CoreRelayerDelivery::executeInstruction` // Nothing more and nothing less. struct EvmDeliveryInstruction { uint16 sourceChain; bytes32 targetAddress; bytes payload; Gas gasLimit; TargetNative totalReceiverValue; GasPrice targetChainRefundPerGasUnused; bytes32 senderAddress; bytes32 deliveryHash; bytes[] signedVaas; } struct RedeliveryInstruction { VaaKey deliveryVaaKey; uint16 targetChain; TargetNative newRequestedReceiverValue; bytes newEncodedExecutionInfo; bytes32 newSourceDeliveryProvider; bytes32 newSenderAddress; } /** * @notice When a user requests a `resend()`, a `RedeliveryInstruction` is emitted by the * WormholeRelayer and in turn converted by the relay provider into an encoded (=serialized) * `DeliveryOverride` struct which is then passed to `delivery()` to override the parameters of * a previously failed delivery attempt. * * @custom:member newReceiverValue - must >= than the `receiverValue` specified in the original * `DeliveryInstruction` * @custom:member newExecutionInfo - for EVM_V1, must contain a gasLimit and targetChainRefundPerGasUnused * such that * - gasLimit is >= the `gasLimit` specified in the `executionParameters` * of the original `DeliveryInstruction` * - targetChainRefundPerGasUnused is >= the `targetChainRefundPerGasUnused` specified in the original * `DeliveryInstruction` * @custom:member redeliveryHash - the hash of the redelivery which is being performed */ struct DeliveryOverride { TargetNative newReceiverValue; bytes newExecutionInfo; bytes32 redeliveryHash; }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import { InvalidPayloadId, InvalidPayloadLength, InvalidVaaKeyType, VaaKey } from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import { DeliveryOverride, DeliveryInstruction, RedeliveryInstruction } from "../../libraries/relayer/RelayerInternalStructs.sol"; import {BytesParsing} from "../../libraries/relayer/BytesParsing.sol"; import "../../interfaces/relayer/TypedUnits.sol"; library WormholeRelayerSerde { using BytesParsing for bytes; using WeiLib for Wei; using GasLib for Gas; //The slightly subtle difference between `PAYLOAD_ID`s and `VERSION`s is that payload ids carry // both type information _and_ version information, while `VERSION`s only carry the latter. //That is, when deserialing a "version struct" we already know the expected type, but since we // publish both Delivery _and_ Redelivery instructions as serialized messages, we need a robust // way to distinguish both their type and their version during deserialization. uint8 private constant VERSION_VAAKEY = 1; uint8 private constant VERSION_DELIVERY_OVERRIDE = 1; uint8 private constant PAYLOAD_ID_DELIVERY_INSTRUCTION = 1; uint8 private constant PAYLOAD_ID_REDELIVERY_INSTRUCTION = 2; // ---------------------- "public" (i.e implicitly internal) encode/decode ----------------------- //TODO GAS OPTIMIZATION: All the recursive abi.encodePacked calls in here are _insanely_ gas // inefficient (unless the optimizer is smart enough to just concatenate them tail-recursion // style which seems highly unlikely) function encode(DeliveryInstruction memory strct) internal pure returns (bytes memory encoded) { encoded = abi.encodePacked( PAYLOAD_ID_DELIVERY_INSTRUCTION, strct.targetChain, strct.targetAddress, encodeBytes(strct.payload), strct.requestedReceiverValue, strct.extraReceiverValue ); encoded = abi.encodePacked( encoded, encodeBytes(strct.encodedExecutionInfo), strct.refundChain, strct.refundAddress, strct.refundDeliveryProvider, strct.sourceDeliveryProvider, strct.senderAddress, encodeVaaKeyArray(strct.vaaKeys) ); } function decodeDeliveryInstruction(bytes memory encoded) internal pure returns (DeliveryInstruction memory strct) { uint256 offset = checkUint8(encoded, 0, PAYLOAD_ID_DELIVERY_INSTRUCTION); uint256 requestedReceiverValue; uint256 extraReceiverValue; (strct.targetChain, offset) = encoded.asUint16Unchecked(offset); (strct.targetAddress, offset) = encoded.asBytes32Unchecked(offset); (strct.payload, offset) = decodeBytes(encoded, offset); (requestedReceiverValue, offset) = encoded.asUint256Unchecked(offset); (extraReceiverValue, offset) = encoded.asUint256Unchecked(offset); (strct.encodedExecutionInfo, offset) = decodeBytes(encoded, offset); (strct.refundChain, offset) = encoded.asUint16Unchecked(offset); (strct.refundAddress, offset) = encoded.asBytes32Unchecked(offset); (strct.refundDeliveryProvider, offset) = encoded.asBytes32Unchecked(offset); (strct.sourceDeliveryProvider, offset) = encoded.asBytes32Unchecked(offset); (strct.senderAddress, offset) = encoded.asBytes32Unchecked(offset); (strct.vaaKeys, offset) = decodeVaaKeyArray(encoded, offset); strct.requestedReceiverValue = TargetNative.wrap(requestedReceiverValue); strct.extraReceiverValue = TargetNative.wrap(extraReceiverValue); checkLength(encoded, offset); } function encode(RedeliveryInstruction memory strct) internal pure returns (bytes memory encoded) { bytes memory vaaKey = encodeVaaKey(strct.deliveryVaaKey); encoded = abi.encodePacked( PAYLOAD_ID_REDELIVERY_INSTRUCTION, vaaKey, strct.targetChain, strct.newRequestedReceiverValue, encodeBytes(strct.newEncodedExecutionInfo), strct.newSourceDeliveryProvider, strct.newSenderAddress ); } function decodeRedeliveryInstruction(bytes memory encoded) internal pure returns (RedeliveryInstruction memory strct) { uint256 offset = checkUint8(encoded, 0, PAYLOAD_ID_REDELIVERY_INSTRUCTION); uint256 newRequestedReceiverValue; (strct.deliveryVaaKey, offset) = decodeVaaKey(encoded, offset); (strct.targetChain, offset) = encoded.asUint16Unchecked(offset); (newRequestedReceiverValue, offset) = encoded.asUint256Unchecked(offset); (strct.newEncodedExecutionInfo, offset) = decodeBytes(encoded, offset); (strct.newSourceDeliveryProvider, offset) = encoded.asBytes32Unchecked(offset); (strct.newSenderAddress, offset) = encoded.asBytes32Unchecked(offset); strct.newRequestedReceiverValue = TargetNative.wrap(newRequestedReceiverValue); checkLength(encoded, offset); } function encode(DeliveryOverride memory strct) internal pure returns (bytes memory encoded) { encoded = abi.encodePacked( VERSION_DELIVERY_OVERRIDE, strct.newReceiverValue, encodeBytes(strct.newExecutionInfo), strct.redeliveryHash ); } function decodeDeliveryOverride(bytes memory encoded) internal pure returns (DeliveryOverride memory strct) { uint256 offset = checkUint8(encoded, 0, VERSION_DELIVERY_OVERRIDE); uint256 receiverValue; (receiverValue, offset) = encoded.asUint256Unchecked(offset); (strct.newExecutionInfo, offset) = decodeBytes(encoded, offset); (strct.redeliveryHash, offset) = encoded.asBytes32Unchecked(offset); strct.newReceiverValue = TargetNative.wrap(receiverValue); checkLength(encoded, offset); } // ------------------------------------------ private -------------------------------------------- function encodeVaaKeyArray(VaaKey[] memory vaaKeys) private pure returns (bytes memory encoded) { assert(vaaKeys.length < type(uint8).max); encoded = abi.encodePacked(uint8(vaaKeys.length)); for (uint256 i = 0; i < vaaKeys.length;) { encoded = abi.encodePacked(encoded, encodeVaaKey(vaaKeys[i])); unchecked { ++i; } } } function decodeVaaKeyArray( bytes memory encoded, uint256 startOffset ) private pure returns (VaaKey[] memory vaaKeys, uint256 offset) { uint8 vaaKeysLength; (vaaKeysLength, offset) = encoded.asUint8Unchecked(startOffset); vaaKeys = new VaaKey[](vaaKeysLength); for (uint256 i = 0; i < vaaKeys.length;) { (vaaKeys[i], offset) = decodeVaaKey(encoded, offset); unchecked { ++i; } } } function encodeVaaKey(VaaKey memory vaaKey) private pure returns (bytes memory encoded) { encoded = abi.encodePacked( encoded, VERSION_VAAKEY, vaaKey.chainId, vaaKey.emitterAddress, vaaKey.sequence ); } function decodeVaaKey( bytes memory encoded, uint256 startOffset ) private pure returns (VaaKey memory vaaKey, uint256 offset) { offset = checkUint8(encoded, startOffset, VERSION_VAAKEY); (vaaKey.chainId, offset) = encoded.asUint16Unchecked(offset); (vaaKey.emitterAddress, offset) = encoded.asBytes32Unchecked(offset); (vaaKey.sequence, offset) = encoded.asUint64Unchecked(offset); } function encodeBytes(bytes memory payload) private pure returns (bytes memory encoded) { //casting payload.length to uint32 is safe because you'll be hard-pressed to allocate 4 GB of // EVM memory in a single transaction encoded = abi.encodePacked(uint32(payload.length), payload); } function decodeBytes( bytes memory encoded, uint256 startOffset ) private pure returns (bytes memory payload, uint256 offset) { uint32 payloadLength; (payloadLength, offset) = encoded.asUint32Unchecked(startOffset); (payload, offset) = encoded.sliceUnchecked(offset, payloadLength); } function checkUint8( bytes memory encoded, uint256 startOffset, uint8 expectedPayloadId ) private pure returns (uint256 offset) { uint8 parsedPayloadId; (parsedPayloadId, offset) = encoded.asUint8Unchecked(startOffset); if (parsedPayloadId != expectedPayloadId) { revert InvalidPayloadId(parsedPayloadId, expectedPayloadId); } } function checkLength(bytes memory encoded, uint256 expected) private pure { if (encoded.length != expected) { revert InvalidPayloadLength(encoded.length, expected); } } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import "../../interfaces/relayer/TypedUnits.sol"; import {BytesParsing} from "../../libraries/relayer/BytesParsing.sol"; error UnexpectedExecutionParamsVersion(uint8 version, uint8 expectedVersion); error UnsupportedExecutionParamsVersion(uint8 version); error TargetChainAndExecutionParamsVersionMismatch(uint16 targetChain, uint8 version); error UnexpectedExecutionInfoVersion(uint8 version, uint8 expectedVersion); error UnsupportedExecutionInfoVersion(uint8 version); error TargetChainAndExecutionInfoVersionMismatch(uint16 targetChain, uint8 version); error VersionMismatchOverride(uint8 instructionVersion, uint8 overrideVersion); using BytesParsing for bytes; enum ExecutionParamsVersion {EVM_V1} struct EvmExecutionParamsV1 { Gas gasLimit; } enum ExecutionInfoVersion {EVM_V1} struct EvmExecutionInfoV1 { Gas gasLimit; GasPrice targetChainRefundPerGasUnused; } function decodeExecutionParamsVersion(bytes memory data) pure returns (ExecutionParamsVersion version) { (version) = abi.decode(data, (ExecutionParamsVersion)); } function decodeExecutionInfoVersion(bytes memory data) pure returns (ExecutionInfoVersion version) { (version) = abi.decode(data, (ExecutionInfoVersion)); } function encodeEvmExecutionParamsV1(EvmExecutionParamsV1 memory executionParams) pure returns (bytes memory) { return abi.encode(uint8(ExecutionParamsVersion.EVM_V1), executionParams.gasLimit); } function decodeEvmExecutionParamsV1(bytes memory data) pure returns (EvmExecutionParamsV1 memory executionParams) { uint8 version; (version, executionParams.gasLimit) = abi.decode(data, (uint8, Gas)); if (version != uint8(ExecutionParamsVersion.EVM_V1)) { revert UnexpectedExecutionParamsVersion(version, uint8(ExecutionParamsVersion.EVM_V1)); } } function encodeEvmExecutionInfoV1(EvmExecutionInfoV1 memory executionInfo) pure returns (bytes memory) { return abi.encode( uint8(ExecutionInfoVersion.EVM_V1), executionInfo.gasLimit, executionInfo.targetChainRefundPerGasUnused ); } function decodeEvmExecutionInfoV1(bytes memory data) pure returns (EvmExecutionInfoV1 memory executionInfo) { uint8 version; (version, executionInfo.gasLimit, executionInfo.targetChainRefundPerGasUnused) = abi.decode(data, (uint8, Gas, GasPrice)); if (version != uint8(ExecutionInfoVersion.EVM_V1)) { revert UnexpectedExecutionInfoVersion(version, uint8(ExecutionInfoVersion.EVM_V1)); } } function getEmptyEvmExecutionParamsV1() pure returns (EvmExecutionParamsV1 memory executionParams) { executionParams.gasLimit = Gas.wrap(uint256(0)); }
// 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` or `forward` 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: MIT pragma solidity ^0.8.0; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {BeaconProxy} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC1967 implementation slot: * ``` * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly { r.slot := slot } } }
{ "remappings": [ "@openzeppelin/=node_modules/@openzeppelin/", "@solidity-parser/=node_modules/@solidity-parser/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "truffle/=node_modules/truffle/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"wormhole","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"Gas","name":"gasUsed","type":"uint256"},{"internalType":"LocalNative","name":"available","type":"uint256"},{"internalType":"LocalNative","name":"required","type":"uint256"}],"name":"Cancelled","type":"error"},{"inputs":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"registeredWormholeRelayerContract","type":"bytes32"}],"name":"ChainAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"bytes","name":"failure","type":"bytes"}],"name":"ContractUpgradeFailed","type":"error"},{"inputs":[],"name":"DeliveryProviderCannotReceivePayment","type":"error"},{"inputs":[{"internalType":"address","name":"relayer","type":"address"},{"internalType":"uint16","name":"chainId","type":"uint16"}],"name":"DeliveryProviderDoesNotSupportTargetChain","type":"error"},{"inputs":[{"internalType":"Gas","name":"gasUsed","type":"uint256"}],"name":"DeliveryProviderPaymentFailed","type":"error"},{"inputs":[{"internalType":"Gas","name":"gasUsed","type":"uint256"}],"name":"DeliveryProviderReverted","type":"error"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"address","name":"deliveryTarget","type":"address"}],"name":"ForwardRequestFromWrongAddress","type":"error"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"GovernanceActionAlreadyConsumed","type":"error"},{"inputs":[{"internalType":"LocalNative","name":"msgValue","type":"uint256"},{"internalType":"LocalNative","name":"minimum","type":"uint256"}],"name":"InsufficientRelayerFunds","type":"error"},{"inputs":[{"internalType":"bytes32","name":"defaultDeliveryProvider","type":"bytes32"}],"name":"InvalidDefaultDeliveryProvider","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"InvalidDeliveryVaa","type":"error"},{"inputs":[{"internalType":"bytes32","name":"emitter","type":"bytes32"},{"internalType":"bytes32","name":"registered","type":"bytes32"},{"internalType":"uint16","name":"chainId","type":"uint16"}],"name":"InvalidEmitter","type":"error"},{"inputs":[],"name":"InvalidFork","type":"error"},{"inputs":[{"internalType":"uint16","name":"parsed","type":"uint16"},{"internalType":"uint16","name":"expected","type":"uint16"}],"name":"InvalidGovernanceChainId","type":"error"},{"inputs":[{"internalType":"bytes32","name":"parsed","type":"bytes32"},{"internalType":"bytes32","name":"expected","type":"bytes32"}],"name":"InvalidGovernanceContract","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"InvalidGovernanceVM","type":"error"},{"inputs":[{"internalType":"LocalNative","name":"msgValue","type":"uint256"},{"internalType":"LocalNative","name":"totalFee","type":"uint256"}],"name":"InvalidMsgValue","type":"error"},{"inputs":[],"name":"InvalidOverrideGasLimit","type":"error"},{"inputs":[],"name":"InvalidOverrideReceiverValue","type":"error"},{"inputs":[],"name":"InvalidOverrideRefundPerGasUnused","type":"error"},{"inputs":[{"internalType":"uint8","name":"parsed","type":"uint8"},{"internalType":"uint8","name":"expected","type":"uint8"}],"name":"InvalidPayloadAction","type":"error"},{"inputs":[{"internalType":"uint16","name":"parsed","type":"uint16"},{"internalType":"uint16","name":"expected","type":"uint16"}],"name":"InvalidPayloadChainId","type":"error"},{"inputs":[{"internalType":"uint8","name":"parsed","type":"uint8"},{"internalType":"uint8","name":"expected","type":"uint8"}],"name":"InvalidPayloadId","type":"error"},{"inputs":[{"internalType":"uint256","name":"received","type":"uint256"},{"internalType":"uint256","name":"expected","type":"uint256"}],"name":"InvalidPayloadLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"parsed","type":"bytes32"},{"internalType":"bytes32","name":"expected","type":"bytes32"}],"name":"InvalidPayloadModule","type":"error"},{"inputs":[],"name":"NoDeliveryInProgress","type":"error"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"NotAnEvmAddress","type":"error"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"address","name":"lockedBy","type":"address"}],"name":"ReentrantDelivery","type":"error"},{"inputs":[],"name":"RequesterNotWormholeRelayer","type":"error"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"}],"name":"TargetChainIsNotThisChain","type":"error"},{"inputs":[{"internalType":"uint8","name":"version","type":"uint8"},{"internalType":"uint8","name":"expectedVersion","type":"uint8"}],"name":"UnexpectedExecutionInfoVersion","type":"error"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"VaaKeysDoNotMatchVaas","type":"error"},{"inputs":[{"internalType":"uint256","name":"keys","type":"uint256"},{"internalType":"uint256","name":"vaas","type":"uint256"}],"name":"VaaKeysLengthDoesNotMatchVaasLength","type":"error"},{"inputs":[{"internalType":"uint8","name":"instructionVersion","type":"uint8"},{"internalType":"uint8","name":"overrideVersion","type":"uint8"}],"name":"VersionMismatchOverride","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldContract","type":"address"},{"indexed":true,"internalType":"address","name":"newContract","type":"address"}],"name":"ContractUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipientContract","type":"address"},{"indexed":true,"internalType":"uint16","name":"sourceChain","type":"uint16"},{"indexed":true,"internalType":"uint64","name":"sequence","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"deliveryVaaHash","type":"bytes32"},{"indexed":false,"internalType":"enum IWormholeRelayerDelivery.DeliveryStatus","name":"status","type":"uint8"},{"indexed":false,"internalType":"Gas","name":"gasUsed","type":"uint256"},{"indexed":false,"internalType":"enum IWormholeRelayerDelivery.RefundStatus","name":"refundStatus","type":"uint8"},{"indexed":false,"internalType":"bytes","name":"additionalStatusInfo","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"overridesInfo","type":"bytes"}],"name":"Delivery","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"sequence","type":"uint64"},{"indexed":false,"internalType":"LocalNative","name":"deliveryQuote","type":"uint256"},{"indexed":false,"internalType":"LocalNative","name":"paymentForExtraReceiverValue","type":"uint256"}],"name":"SendEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"checkAndExecuteUpgradeMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"encodedVMs","type":"bytes[]"},{"internalType":"bytes","name":"encodedDeliveryVAA","type":"bytes"},{"internalType":"address payable","name":"relayerRefundAddress","type":"address"},{"internalType":"bytes","name":"deliveryOverrides","type":"bytes"}],"name":"deliver","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"sourceChain","type":"uint16"},{"internalType":"bytes32","name":"targetAddress","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"internalType":"TargetNative","name":"totalReceiverValue","type":"uint256"},{"internalType":"GasPrice","name":"targetChainRefundPerGasUnused","type":"uint256"},{"internalType":"bytes32","name":"senderAddress","type":"bytes32"},{"internalType":"bytes32","name":"deliveryHash","type":"bytes32"},{"internalType":"bytes[]","name":"signedVaas","type":"bytes[]"}],"internalType":"struct EvmDeliveryInstruction","name":"evmInstruction","type":"tuple"}],"name":"executeInstruction","outputs":[{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"Gas","name":"gasUsed","type":"uint256"},{"internalType":"bytes","name":"targetRevertDataTruncated","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"bytes32","name":"targetAddress","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"LocalNative","name":"paymentForExtraReceiverValue","type":"uint256"},{"internalType":"bytes","name":"encodedExecutionParameters","type":"bytes"},{"internalType":"uint16","name":"refundChain","type":"uint16"},{"internalType":"bytes32","name":"refundAddress","type":"bytes32"},{"internalType":"address","name":"deliveryProviderAddress","type":"address"},{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey[]","name":"vaaKeys","type":"tuple[]"},{"internalType":"uint8","name":"consistencyLevel","type":"uint8"}],"name":"forward","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"}],"name":"forwardPayloadToEvm","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"LocalNative","name":"paymentForExtraReceiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"internalType":"uint16","name":"refundChain","type":"uint16"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"address","name":"deliveryProviderAddress","type":"address"},{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey[]","name":"vaaKeys","type":"tuple[]"},{"internalType":"uint8","name":"consistencyLevel","type":"uint8"}],"name":"forwardToEvm","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey[]","name":"vaaKeys","type":"tuple[]"}],"name":"forwardVaasToEvm","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getDefaultDeliveryProvider","outputs":[{"internalType":"address","name":"deliveryProvider","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"}],"name":"getDefaultDeliveryProviderOnChain","outputs":[{"internalType":"address","name":"deliveryProvider","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"}],"name":"getOriginalOrDefaultDeliveryProvider","outputs":[{"internalType":"address","name":"deliveryProvider","type":"address"},{"internalType":"address","name":"deliveryProviderOnTarget","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"chainId","type":"uint16"}],"name":"getRegisteredWormholeRelayerContract","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"defaultDeliveryProvider","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"bytes","name":"encodedExecutionParameters","type":"bytes"},{"internalType":"address","name":"deliveryProviderAddress","type":"address"}],"name":"quoteDeliveryPrice","outputs":[{"internalType":"LocalNative","name":"nativePriceQuote","type":"uint256"},{"internalType":"bytes","name":"encodedExecutionInfo","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"internalType":"address","name":"deliveryProviderAddress","type":"address"}],"name":"quoteEVMDeliveryPrice","outputs":[{"internalType":"LocalNative","name":"nativePriceQuote","type":"uint256"},{"internalType":"GasPrice","name":"targetChainRefundPerGasUnused","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"}],"name":"quoteEVMDeliveryPrice","outputs":[{"internalType":"LocalNative","name":"nativePriceQuote","type":"uint256"},{"internalType":"GasPrice","name":"targetChainRefundPerGasUnused","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"LocalNative","name":"currentChainAmount","type":"uint256"},{"internalType":"address","name":"deliveryProviderAddress","type":"address"}],"name":"quoteNativeForChain","outputs":[{"internalType":"TargetNative","name":"targetChainAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm","type":"bytes"}],"name":"registerWormholeRelayerContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey","name":"deliveryVaaKey","type":"tuple"},{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"TargetNative","name":"newReceiverValue","type":"uint256"},{"internalType":"bytes","name":"newEncodedExecutionParameters","type":"bytes"},{"internalType":"address","name":"newDeliveryProviderAddress","type":"address"}],"name":"resend","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey","name":"deliveryVaaKey","type":"tuple"},{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"TargetNative","name":"newReceiverValue","type":"uint256"},{"internalType":"Gas","name":"newGasLimit","type":"uint256"},{"internalType":"address","name":"newDeliveryProviderAddress","type":"address"}],"name":"resendToEvm","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"bytes32","name":"targetAddress","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"LocalNative","name":"paymentForExtraReceiverValue","type":"uint256"},{"internalType":"bytes","name":"encodedExecutionParameters","type":"bytes"},{"internalType":"uint16","name":"refundChain","type":"uint16"},{"internalType":"bytes32","name":"refundAddress","type":"bytes32"},{"internalType":"address","name":"deliveryProviderAddress","type":"address"},{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey[]","name":"vaaKeys","type":"tuple[]"},{"internalType":"uint8","name":"consistencyLevel","type":"uint8"}],"name":"send","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"internalType":"uint16","name":"refundChain","type":"uint16"},{"internalType":"address","name":"refundAddress","type":"address"}],"name":"sendPayloadToEvm","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"}],"name":"sendPayloadToEvm","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"LocalNative","name":"paymentForExtraReceiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"internalType":"uint16","name":"refundChain","type":"uint16"},{"internalType":"address","name":"refundAddress","type":"address"},{"internalType":"address","name":"deliveryProviderAddress","type":"address"},{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey[]","name":"vaaKeys","type":"tuple[]"},{"internalType":"uint8","name":"consistencyLevel","type":"uint8"}],"name":"sendToEvm","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey[]","name":"vaaKeys","type":"tuple[]"}],"name":"sendVaasToEvm","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint16","name":"targetChain","type":"uint16"},{"internalType":"address","name":"targetAddress","type":"address"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"TargetNative","name":"receiverValue","type":"uint256"},{"internalType":"Gas","name":"gasLimit","type":"uint256"},{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint64","name":"sequence","type":"uint64"}],"internalType":"struct VaaKey[]","name":"vaaKeys","type":"tuple[]"},{"internalType":"uint16","name":"refundChain","type":"uint16"},{"internalType":"address","name":"refundAddress","type":"address"}],"name":"sendVaasToEvm","outputs":[{"internalType":"uint64","name":"sequence","type":"uint64"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm","type":"bytes"}],"name":"setDefaultDeliveryProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVm","type":"bytes"}],"name":"submitContractUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c080604052346200013f5760208162005614803803809162000023828562000144565b8339810103126200013f57516001600160a01b038116908190036200013f5760208160049260805260405192838092634d4502c960e11b82525afa9081156200013357600091620000e9575b5060a05260405161549590816200017f8239608051818181610415015281816106fa015281816114740152818161182501528181611bc801528181612f9a0152818161304d0152818161356a0152614ad8015260a05181818161067f015281816114ad0152818161186501528181611c0901526132060152f35b6020813d82116200012a575b81620001046020938362000144565b810103126200012657519061ffff82168203620001235750386200006f565b80fd5b5080fd5b3d9150620000f5565b6040513d6000823e3d90fd5b600080fd5b601f909101601f19168101906001600160401b038211908210176200016857604052565b634e487b7160e01b600052604160045260246000fdfe6080604052600436101561001257600080fd5b60003560e01c80631d6bd5aa14611d705780632385904a14611cca57806324320c9f14611c9457806328b1d85214611ad55780632936558f14611aa05780632c75470f14611a84578063329a2be714611a4857806332b2fc0e146119a65780633a2c767d146119675780633e8267e71461190d5780633ed334df146116f45780634533e5ff1461167f5780634b5ca6f4146115eb5780634d48ec601461158a5780635cb8cae2146112755780636000415714610cb657806375ea8b5814610c9857806380ebabd014610c645780638b0301b114610c165780638fecdd0214610bca578063a60eb4c81461036b578063a79629d8146102fc578063b1eac8751461028e578063b686d0891461022e578063c23ee3c3146101dd578063c4d66de8146101765763c81fb7fe1461014557600080fd5b6020610165610153366122c6565b99989098979197969296959395613c23565b6001600160401b0360405191168152f35b346101d85760203660031901126101d8576004356001600160a01b038116908190036101d85760016000546101ae60ff821615612366565b60ff1916176000556000805160206153c083398151915280546001600160a01b0319169091179055005b600080fd5b346101d85760603660031901126101d85760406102226101fb611dad565b6000805160206153c0833981519152546001600160a01b0316906044359060243590614e29565b82519182526020820152f35b60e03660031901126101d85761024336611f90565b61024b611dbe565b60a4356001600160401b03918282116101d857602093610272610285933690600401611f32565b9061027b611df4565b9260843591614913565b60405191168152f35b6102fa61029a36612083565b94916102c26102ac8496959396614d3a565b5095604051906102bb82611e20565b81526134c9565b9161ffff6000805160206154008339815191525460a01c1693600080516020615440833981519152549560018060a01b031690613efb565b005b346101d85760803660031901126101d857610315611dad565b604435906001600160401b0382116101d85761033861034a923690600401611f32565b610340611e0a565b9160243590614e59565b9061036760405192839283526040602084015260408301906122a1565b0390f35b60803660031901126101d8576004356001600160401b0381116101d8576103969036906004016121fe565b6024356001600160401b0381116101d8576103b5903690600401611f32565b906044356001600160a01b03811681036101d857606435926001600160401b0384116101d85760006103ee610411953690600401611f32565b916040518096819263607ec5ef60e11b83526020600484015260248301906122a1565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa80156107ff57600094600090600092610b9f575b5015610b7a5750606084015161ffff1660009081527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d6020526040902054608085015190808203610b4d5750506104ad60e085015161509b565b916104bb602084015161263d565b6104c961010085015161263d565b60c085015160e0860151926000805160206153e0833981519152549060ff8216610b2257906001916101008360a81b039060081b16906001600160581b0360a81b1617176000805160206153e083398151915255600080516020615400833981519152549061ffff60a01b9060a01b169160018060a01b03169069ffffffffffffffffffff60b01b161717600080516020615400833981519152556000805160206154408339815191525561ffff606086015116946101406001600160401b0360a08301511691015190604051966105a088611e6d565b87526020870152604086015260018060a01b031660608501528260808501528160a0850152600060c0850152600060e0850152600061010085015280610120850152600061014085015260006105f960a08401516126da565b610602816126ba565b80610ae9575061061560a0840151612720565b9180516109bb575b5061065f9082602061066b945191015191610641606087015160808801519061261d565b906101408901526101008801528160e08801528060c088015261262a565b6101008501519061261d565b80341061099e575061ffff8151168061ffff7f0000000000000000000000000000000000000000000000000000000000000000160361098657506101600151908151815180820361096857505060005b825181101561080b576106f660006106d38385612f71565b516040518093819263a9e1189360e01b83526020600484015260248301906122a1565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156107ff576000916107be575b5061073e8285612f71565b519061ffff82511661ffff60608301511614918215926107ab575b821561078a575b505061076e576001016106bb565b604051633ad7858760e21b815260ff9091166004820152602490fd5b6001600160401b0380929350604060a0920151169201511614158580610760565b6020810151608083015114159250610759565b3d9150816000823e6107d08282611ef6565b60208183810103126101d85780516001600160401b0381116101d8576107f99282019101612416565b85610733565b6040513d6000823e3d90fd5b610814846127b4565b6001600160581b0360a81b6000805160206153e083398151915254166000805160206153e08339815191525569ffffffffffffffffffff60b01b6000805160206154008339815191525416600080516020615400833981519152556000600080516020615440833981519152556000805160206154208339815191528054600082558061089d57005b6005918183029183830403610952576000527f3956f3466fff97ca672672c3102a70900b3589f53a1cac769d9a51ee18555a4f908101905b8181106108de57005b806108ea849254612669565b80610913575b5060006001820155600060028201556000600382015560006004820155016108d5565b601f9081811160011461092d575050600081555b846108f0565b610949600092848452602084209201851c8201600183016126a3565b81835555610927565b634e487b7160e01b600052601160045260246000fd5b60449250604051916316bde1ed60e31b835260048301526024820152fd5b6024906040519063d8215fc960e01b82526004820152fd5b60449060405190620885af60e61b82523460048301526024820152fd5b9050610a16604051916109cd83611e89565b60008352606060208401526000604084015260206109ea8261538c565b6109fc828083860101519201846152f9565b9190838701528282850101516040870152855201906137de565b6060830151610a236126f7565b50815110610ad757610a3860208201516126da565b610a41816126ba565b80610aac5750610a546020820151612720565b918151906020840151602082015111610a9a578351905111610a88576060840152600060808401526040015161066b61061d565b6040516315fc687d60e31b8152600490fd5b60405163067dbecf60e11b8152600490fd5b80610ab86044926126ba565b60ff6040519163170cd96160e11b835260006004840152166024820152fd5b604051631c6e090160e31b8152600490fd5b80610af6610b1e926126ba565b60405163c1f4bdd960e01b815260ff9091166004820152600060248201529081906044820190565b0390fd5b6040516320b84ced60e01b81523360048201526001600160a01b03600884901c166024820152604490fd5b6064925061ffff6060870151169060405192633bb6036760e11b8452600484015260248301526044820152fd5b60405163b72c3b7f60e01b815260206004820152908190610b1e9060248301906122a1565b915050610bc09194503d806000833e610bb88183611ef6565b8101906125bd565b9094919486610453565b6020610165610bd8366121a6565b610be58592939495614cb1565b9260018060a01b036000805160206153c083398151915254169460405196610c0c88611e20565b6000885284613809565b60e03660031901126101d8576020610165610c3036611f90565b610c38611dbe565b610c40611df4565b91610c5a604051610c5081611e20565b60a43581526134c9565b9160843591614913565b346101d85760803660031901126101d8576040610222610c82611dad565b610c8a611e0a565b906044359060243590614e29565b6102fa610ca4366122c6565b99989098979197969296959395614459565b346101d8576003196020368201126101d857600435906001600160401b03908183116101d8576101209083360301126101d85760405190610cf682611e51565b610d0283600401611dcf565b825260208201926024810135845260448101358281116101d857610d2c9060043691840101611f32565b91604084019283526060840191606481013583526084810135608086015260a481013560a086015260c085019160c4820135835260e086019160e481013583526101048101359182116101d8576004610d8892369201016121fe565b92610100860193845230330361126357519551610de9939192906001600160a01b0390610db49061263d565b169451915190519261ffff87511690519060405195869463294ee51960e11b602087015260a0602487015260c48601906122a1565b94602319858703016044860152835180875260208701966020808360051b8301019601976000915b83831061122c57505050506064850152608484015260a483015203601f1981018352610e3e915082611ef6565b610e8460845a926080860151948151906040519660208089019401918af1923d80608410600114611224575b808252601f01601f19168101602001604052915a90612dfc565b8481101561121c57915b1561120b5750604051610ea081611e20565b6000815292600092600080516020615420833981519152549182610ee9575b50505060ff92610367915b60405194859416845260208401526060604084015260608301906122a1565b839450610efc610f05939460a092612dfc565b9101519061262a565b90610f0e612f85565b90610f1881611f79565b92610f266040519485611ef6565b818452601f19610f3583611f79565b0160005b8181106111f4575050600080916000935b8581861061117d575050610f5f92935061261d565b9080821061115957610fd360206001600160a01b03610f8b610120610f8389612f64565b51015161263d565b1661ffff610f9888612f64565b51511690610fa68587612dfc565b604051630cbcf9e160e21b815261ffff909316600484015260248301529092839190829081906044820190565b03915afa60009181611125575b50610ffe57604051632baa6b8960e11b815260048101879052602490fd5b61101890959495608061101088612f64565b51015161261d565b608061102387612f64565b51015260005b6000805160206154208339815191525481101561110f576110c8600261104e83612e62565b500154600361105c84612e62565b50015461107d84159182600014611107576110778789612dfc565b9061261d565b90156110f05761109561108f8a612f64565b51614ecc565b60ff60046110a287612e62565b50015460a01c16916110b386612e62565b50600401546001600160a01b03169389613007565b9050156110d757600101611029565b6040516336e7e91f60e11b815260048101869052602490fd5b6111026110fc85612e62565b50612ec0565b611095565b60009061261d565b5060039450859250610367915060ff9050610ebf565b9091506020813d602011611151575b8161114160209383611ef6565b810103126101d857519088610fe0565b3d9150611134565b8490606492604051926385880e2960e01b8452600484015260248301526044820152fd5b6111e9906111e46111d96111a26001959697866111998c612e62565b5001549061261d565b978b6111c28b6111bc6111b76110fc83612e62565b61509b565b92612f71565b526111cd8a8d612f71565b5060026111998b612e62565b60036111998a612e62565b61261d565b940193929190610f4a565b6020906111ff612e09565b82828901015201610f39565b60ff93506001925061036791610eca565b508391610e8e565b506084610e6a565b919395975091939560208061124d600193601f198682030187528c516122a1565b9a01930193019092899795939896949298610e11565b60405163390996ad60e11b8152600490fd5b346101d8576020806003193601126101d8576004356001600160401b0381116101d8576112a6903690600401611f32565b60018060a01b03917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc926112dd8185541693613560565b828101516e576f726d686f6c6552656c6179657280820361156c57505060ff6021820151166002810361154d5750602381015161ffff90818116801580611545575b15611460575b50505061133e611338604383015161263d565b916137b3565b803b15611405571692836bffffffffffffffffffffffff60a01b825416179055600080604051857fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8380a2632c75470f60e01b81850190815260048252906113a581611edb565b519082305af1906113b4612784565b91156113e35750507f2e4cc16c100f0b55e2df82ab0b1a7e294aa9cbd01b48fbaf622683fbc0507a49600080a3005b610b1e60405192839263135687c760e31b8452600484015260248301906122a1565b60405162461bcd60e51b815260048101849052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b60405163380e7c8960e21b815286816004817f00000000000000000000000000000000000000000000000000000000000000008a165afa9081156107ff57600091611510575b506114fe577f0000000000000000000000000000000000000000000000000000000000000000928316036114da5780611325565b60405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b60405163ea03b6eb60e01b8152600490fd5b90508681813d831161153e575b6115278183611ef6565b810103126101d857611538906125b0565b896114a6565b503d61151d565b50600061131f565b60449060405190633460202560e21b8252600482015260026024820152fd5b6044925060405191633d254c6160e01b835260048301526024820152fd5b6102fa611596366120f4565b99909891979196939592949293926001600160a01b0390818a16156115d2575b6115c88291604051906102bb82611e20565b9616971690614459565b60008051602061540083398151915254821699506115b6565b60e03660031901126101d8576115ff611dad565b611607611dde565b906001600160401b03906044358281116101d857611629903690600401611f32565b60a4359161ffff831683036101d85760209461028593611647611df4565b9160018060a01b036000805160206153c08339815191525416936040519561166e87611e20565b600087526084359260643592613809565b6102fa61168b366121a6565b929391906116aa61169b84614d3a565b5094604051906102bb82611e20565b9061ffff6000805160206154008339815191525460a01c16926000805160206154408339815191525494604051976116e189611e20565b600089526001600160a01b031690613efb565b346101d8576020806003193601126101d8576004356001600160401b0381116101d85761172861172d913690600401611f32565b613560565b90808201516e576f726d686f6c6552656c6179657280820361156c57505060ff602183015116600181036118ee5750602382015161ffff90818116908115806118e6575b15611811575b5050602583015160458401519351604581036117f257501690816000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d91828252604060002054806117d4575060005252604060002055600080f35b6044925060405191637b5672c560e11b835260048301526024820152fd5b6044906040519063061bc83560e51b8252600482015260456024820152fd5b60405163380e7c8960e21b815284816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156107ff576000916118b1575b506114fe577f000000000000000000000000000000000000000000000000000000000000000091838316146117775760405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b90508481813d83116118df575b6118c88183611ef6565b810103126101d8576118d9906125b0565b8661185e565b503d6118be565b506001611771565b60449060405190633460202560e21b8252600482015260016024820152fd5b346101d85760203660031901126101d857602061195f61192b611dad565b61ffff166000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d60205260406000205490565b604051908152f35b6020610165611991611978366120f4565b9a9099919897929594604097949751906102bb82611e20565b6001600160a01b039586169790951690613c23565b6101003660031901126101d8576119bb611dad565b6119c3611dde565b6001600160401b03906044358281116101d8576119e4903690600401611f32565b60a4358381116101d8576119fc903690600401611fdc565b9060c43561ffff811681036101d8576001600160a01b039360e435919085831683036101d857602097610285966000805160206153c08339815191525416946084359260643592613809565b6020610165611a5636612083565b9491611a6183614cb1565b6000805160206153c0833981519152546001600160a01b03169590949084613809565b346101d85760003660031901126101d8576102fa303314612366565b346101d85760203660031901126101d8576020611ac3611abe611dad565b614cb1565b6040516001600160a01b039091168152f35b346101d8576020806003193601126101d8576004356001600160401b0381116101d857611728611b09913690600401611f32565b90808201516e576f726d686f6c6552656c6179657280820361156c57505060ff60218301511660038103611c755750602382015161ffff9182821690811580611c6d575b15611bb4575b50505050604381015190611b696113388361263d565b6001600160a01b0316908115611b9c57506000805160206153c083398151915280546001600160a01b0319169091179055005b60249060405190637a8ad12560e01b82526004820152fd5b60405163380e7c8960e21b815281816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9182156107ff57600092611c37575b50506114fe577f0000000000000000000000000000000000000000000000000000000000000000928316036114da578080611b53565b90809250813d8311611c66575b611c4e8183611ef6565b810103126101d857611c5f906125b0565b8580611c01565b503d611c44565b506000611b4d565b60449060405190633460202560e21b8252600482015260036024820152fd5b346101d85760003660031901126101d8576000805160206153c0833981519152546040516001600160a01b039091168152602090f35b346101d85760603660031901126101d857611ce3611dad565b6044356001600160a01b038116908190036101d857604051630cbcf9e160e21b815261ffff9290921660048301526024803590830152602090829060449082905afa80156107ff57600090611d3e575b602090604051908152f35b506020813d8211611d68575b81611d5760209383611ef6565b810103126101d85760209051611d33565b3d9150611d4a565b346101d85760203660031901126101d857611d91611d8c611dad565b614d3a565b604080516001600160a01b039384168152919092166020820152f35b6004359061ffff821682036101d857565b6064359061ffff821682036101d857565b359061ffff821682036101d857565b602435906001600160a01b03821682036101d857565b60c435906001600160a01b03821682036101d857565b606435906001600160a01b03821682036101d857565b602081019081106001600160401b03821117611e3b57604052565b634e487b7160e01b600052604160045260246000fd5b61012081019081106001600160401b03821117611e3b57604052565b61016081019081106001600160401b03821117611e3b57604052565b606081019081106001600160401b03821117611e3b57604052565b61018081019081106001600160401b03821117611e3b57604052565b60c081019081106001600160401b03821117611e3b57604052565b604081019081106001600160401b03821117611e3b57604052565b90601f801991011681019081106001600160401b03821117611e3b57604052565b6001600160401b038111611e3b57601f01601f191660200190565b81601f820112156101d857803590611f4982611f17565b92611f576040519485611ef6565b828452602083830101116101d857816000926020809301838601378301015290565b6001600160401b038111611e3b5760051b60200190565b60609060031901126101d85760405190611fa982611e89565b8160043561ffff811681036101d85781526024356020820152604435906001600160401b03821682036101d85760400152565b81601f820112156101d857803590611ff382611f79565b9260409061200382519586611ef6565b83855260209182860191836060809702860101948186116101d8578401925b858410612033575050505050505090565b86848303126101d85782519061204882611e89565b61205185611dcf565b8252858501358683015283850135906001600160401b03821682036101d857828792868b950152815201930192612022565b60c06003198201126101d85760043561ffff811681036101d857916024356001600160a01b03811681036101d857916001600160401b03906044358281116101d857816120d291600401611f32565b92606435926084359260a4359182116101d8576120f191600401611fdc565b90565b906101606003198301126101d85761ffff9060043582811681036101d857926001600160a01b039160243583811681036101d857936001600160401b036044358181116101d8578461214891600401611f32565b94606435946084359460a4359460c43590811681036101d8579360e43584811681036101d857936101043590811681036101d85792610124359182116101d85761219491600401611fdc565b906101443560ff811681036101d85790565b60a06003198201126101d85760043561ffff811681036101d857916024356001600160a01b03811681036101d85791604435906001600160401b0382116101d8576121f391600401611f32565b906064359060843590565b9080601f830112156101d85781359061221682611f79565b926122246040519485611ef6565b828452602092838086019160051b830101928084116101d857848301915b8483106122525750505050505090565b82356001600160401b0381116101d857869161227384848094890101611f32565b815201920191612242565b60005b8381106122915750506000910152565b8181015183820152602001612281565b906020916122ba8151809281855285808601910161227e565b601f01601f1916010190565b6101606003198201126101d85761ffff9160043583811681036101d85792602435926001600160401b03916044358381116101d8578261230891600401611f32565b93606435936084359360a4358281116101d8578161232891600401611f32565b9360c43590811681036101d8579260e43592610104356001600160a01b03811681036101d85792610124359182116101d85761219491600401611fdc565b1561236d57565b634e487b7160e01b600052600160045260246000fd5b519060ff821682036101d857565b519063ffffffff821682036101d857565b519061ffff821682036101d857565b51906001600160401b03821682036101d857565b909291926123d281611f17565b916123e06040519384611ef6565b8294828452828201116101d85760206123fa93019061227e565b565b9080601f830112156101d85781516120f1926020016123c5565b9190610160838203126101d85760409283519161243283611e6d565b829461243d83612383565b8452602061244c818501612391565b8186015261245b828501612391565b82860152606061246c8186016123a2565b81870152608091828601518388015261248760a087016123b1565b60a088015261249860c08701612383565b60c088015260e0860151936001600160401b03948581116101d857866124bf9189016123fc565b60e08901526101006124d2818901612391565b9089015261012094858801518181116101d85788019387601f860112156101d8578451926124ff84611f79565b9861250c82519a8b611ef6565b848a5285808b019560071b880101968188116101d8578601945b8786106125455750505050505050505083015261014080910151910152565b88868303126101d857825190898201908282108783111761259b578a928992865288518152828901518382015261257d868a01612383565b8682015261258c878a01612383565b87820152815201950194612526565b60246000634e487b7160e01b81526041600452fd5b519081151582036101d857565b90916060828403126101d8578151926001600160401b03938481116101d857816125e8918501612416565b936125f5602085016125b0565b9360408101519182116101d857019080601f830112156101d85781516120f1926020016123c5565b9190820180921161095257565b8181029291811591840414171561095257565b8060a01c612651576001600160a01b031690565b6024906040519063033b960d60e41b82526004820152fd5b90600182811c92168015612699575b602083101461268357565b634e487b7160e01b600052602260045260246000fd5b91607f1691612678565b8181106126ae575050565b600081556001016126a3565b600111156126c457565b634e487b7160e01b600052602160045260246000fd5b6020818051810103126101d8576020015160018110156101d85790565b60405190604082018281106001600160401b03821117611e3b5760405260006020838281520152565b906127296126f7565b916060818051810103126101d85761274360208201612383565b90606060408201519101516020850152835260ff81166127605750565b60405163c1f4bdd960e01b815260ff91909116600482015260006024820152604490fd5b3d156127af573d9061279582611f17565b916127a36040519384611ef6565b82523d6000602084013e565b606090565b60009081602060a083015101518015612c91575b50612c8d576060604080516127dc81611e89565b848152846020820152015261ffff81511660a082015160208101519060408101519060c08501519061010086015161014060e0880151920151926040880151946080890151966040519861282f8a611e51565b8952602089015260408801526060870152608086015260a085015260c084015260e0830152610100820152826040518092636000415760e01b82526020600483015261ffff8151166024830152602081015160448301526101006128a4604083015161012060648601526101448501906122a1565b9160608101516084850152608081015160a485015260a081015160c485015260c081015160e485015260e081015161010485015201519060231983820301610124840152815180825260208201916020808360051b83010194019286915b838310612c5c5750505050508190038183305af1838185938693612bf8575b50612bb657505050612931612784565b61293a816134e7565b15612baa57905b6040519161294e83611e89565b82526002602083015260408201525b61296e602060a0840151015161263d565b9161ffff815116926001600160401b03602083015116946040830151906020850151926004841015612b965785519560a08601519560018060a01b036060820151166129cc6129c18a60c0850151612dfc565b60e08401519061262a565b9760208401516004811015612b825786906000600282148015612b76575b612b5b575b612b4057612a069190600303612b5457879061261d565b98612a2561ffff60c0840151168b60e0850151610100860151926131f9565b999060058b1015612b405791612a6e91612a7494938c158015612b36575b612b2e575b612a69612a6384608060606111e4969701519101519061261d565b34612dfc565b612dfc565b90613138565b15612b1c577fbccc00b713f54173962e7de6098f643d8ebf53d488d71f4b2a5171496d038f9e96612b01946040612ae8940151926101408101511515600014612b065761012091500151985b604051978897885260208801526040870152606086015260c0608086015260c08501906122a1565b83810360a08501526001600160a01b03909116956122a1565b0390a4565b5060405190612b1482611e20565b815298612ac0565b6040516304aeb27d60e51b8152600490fd5b899150612a48565b5060028d14612a43565b634e487b7160e01b88526021600452602488fd5b8a9061261d565b9150612b70606084015160808501519061261d565b916129ef565b505087600182146129ea565b634e487b7160e01b87526021600452602487fd5b634e487b7160e01b82526021600452602482fd5b5060c082015190612941565b600460ff82161015612be45760ff9060405193612bd285611e89565b8452166020830152604082015261295d565b634e487b7160e01b85526021600452602485fd5b93509150503d8085843e612c0c8184611ef6565b6060838281010312612c5857612c2183612383565b926020810151916040820151906001600160401b038211612c5457612c4a9290810191016123fc565b9290929138612921565b8780fd5b8480fd5b92955092955092602080612c7c600193601f1986820301875289516122a1565b970193019301889593879592612902565b5050565b612c9b915061263d565b61ffff9081835116906001600160401b036020850151169260408501519060a086015160018060a01b0360608801511690612cfb612ce2606083015160808401519061261d565b9360c0830151168460e0840151610100850151926131f9565b92906005841015612de857612d349291612a6e9185158015612dde575b612dd6575b612a6381608060606111e49401519101519061261d565b15612b1c577fbccc00b713f54173962e7de6098f643d8ebf53d488d71f4b2a5171496d038f9e91612db360405194612d6b86611e20565b60008087526101408a015115612dbe5750612ae86101208a0151965b60405195869586526000602087015260006040870152606086015260c0608086015260c08501906122a1565b0390a46001386127c8565b604051612ae891612dce82611e20565b815296612d87565b8b9150612d1d565b5060028614612d18565b634e487b7160e01b8a52602160045260248afd5b9190820391821161095257565b60405190612e1682611ea4565b606061016083600080825280602083015283604083015280848301528060808301528360a08301528060c08301528060e083015280610100830152806101208301526101408201520152565b600080516020615420833981519152908154811015612eaa57600591600052027f3956f3466fff97ca672672c3102a70900b3589f53a1cac769d9a51ee18555a4f0190600090565b634e487b7160e01b600052603260045260246000fd5b9060405191826000825492612ed484612669565b908184526001948581169081600014612f415750600114612efe575b50506123fa92500383611ef6565b9093915060005260209081600020936000915b818310612f295750506123fa93508201013880612ef0565b85548884018501529485019487945091830191612f11565b9150506123fa94506020925060ff191682840152151560051b8201013880612ef0565b805115612eaa5760200190565b8051821015612eaa5760209160051b010190565b604051631a90a21960e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156107ff57600091612fd6575090565b906020823d8211612fff575b81612fef60209383611ef6565b81010312612ffc57505190565b80fd5b3d9150612fe2565b60ff90613042602093959896949798604051958694859384936358cd21bf60e11b8552600060048601526060602486015260648501906122a1565b9116604483015203917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19081156107ff576000916130d2575b5060406001600160401b03826130c27fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e4853649498612a6e898861261d565b96835195865260208601521692a2565b906020823d8211613130575b816130eb60209383611ef6565b81010312612ffc575060406001600160401b036131287fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e485364936123b1565b925050613086565b3d91506130de565b9080156131865760006020918160405161315181611e20565b5281805a926040519686880194f1913d80151560011461317e575b808252601f01601f1916010160405290565b50600061316c565b5050600190565b9190916040818403126101d85780519260208201516001600160401b0381116101d8576120f192016123fc565b6120f1939261ffff606093168252602082015281604082015201906122a1565b604051906131e782611e89565b60006040838281528260208201520152565b91909261ffff80931693837f0000000000000000000000000000000000000000000000000000000000000000168514613499576001600160a01b03938461323f8461263d565b169160009060409361325d855161325581611e20565b8481526134c9565b928086518093635cf3af3360e11b82528c600483015282602483015281806132916060998a604484015260648301906122a1565b03915afa90918282613473575b50506132bb575050505050505050506132b5612784565b50600390565b966132c8886111e4612f85565b87111561346557849298969497959793919351956132e587611e20565b600087526132f1612f85565b6132fb9087612dfc565b9061330591612dfc565b90835161331181611e20565b6000815261331e906134c9565b986133289061263d565b84519561333487611e20565b6000875285519a8b9963640fdbff60e11b8b528b60048c015260248b016000905260448b0161016090526101648b0161336c916122a1565b9460648b016000905260848b015260031994858b82030160a48c0152613391916122a1565b9960c48a015260e4890152166101048701528587030161012486015282519081875260209889808099019501936000925b84841061342d5750505050505082809160c86101448301520391305af191826133f9575b50506133f4576132b5612784565b600290565b81813d8311613426575b61340d8183611ef6565b810103126101d85761341e906123b1565b5038806133e6565b503d613403565b8551805182168852808b01518b8901528201516001600160401b0316828801528b998b995096830196909501946001909301926133c2565b505050505050505050600490565b6134909293503d8091833e6134888183611ef6565b81019061318d565b5090388061329e565b6134ba94509192506001600160a01b03916134b4915061263d565b16613138565b156134c457600090565b600190565b5160405190600060208301526040820152604081526120f181611e89565b60009160009160248151106135565763ffffffff60e01b600482015160e01b166385880e2960e01b8114908115613545575b8115613534575b506135285750565b60240151925060019150565b6336e7e91f60e11b14905038613520565b632baa6b8960e11b81149150613519565b5060009250829150565b9060018060a01b037f0000000000000000000000000000000000000000000000000000000000000000169160406135b981519263607ec5ef60e11b845260209560049187838701528580600095869360248301906122a1565b0381845afa80156136f757839584908592613791575b501561376e5750835163fbe3c2cd60e01b815287818481855afa90811561376457849161372b575b5061ffff908160608801511691811682036137015750508682918551928380926358b9591160e11b82525afa9081156136f75783916136c6575b5060808501518181036136ab575050610140840195865183527f970ad24d4754c92e299cabb86552091f5df0a15abc0f1b71f37d3e30031585dc9182825260ff8585205416613694575060e095965183525220600160ff19825416179055015190565b875185516364cbf47160e01b815291820152602490fd5b84516342852f8d60e11b815292830152602482015260449150fd5b90508681813d83116136f0575b6136dd8183611ef6565b810103126136ec575138613631565b8280fd5b503d6136d3565b84513d85823e3d90fd5b855163c97817ed60e01b815261ffff92831685820190815291909216602082015281900360400190fd5b90508781813d831161375d575b6137428183611ef6565b8101031261375957613753906123a2565b386135f7565b8380fd5b503d613738565b85513d86823e3d90fd5b8451630169d68560e71b8152808401899052908190610b1e9060248301906122a1565b9150506137a99195503d8085833e610bb88183611ef6565b90959195386135cf565b51604381036137bf5750565b6044906040519063061bc83560e51b8252600482015260436024820152fd5b51908082036137eb575050565b604492506040519163061bc83560e51b835260048301526024820152fd5b95939261382761ffff949a99969392989a604051906102bb82611e20565b91604051986138358a611e6d565b8589168a5260018060a01b031660208a0152604089015260608801526000608088015260a08701521660c085015260018060a01b031660e084015260018060a01b038516610100840152610120830152600f61014083015261ffff604051916328f41de360e01b835216600482015260208160248160018060a01b0388165afa9081156107ff57600091613be9575b5015613bb65761ffff815116926000606083015160a0840151956138fc6040519788938493635cf3af3360e11b8552600485016131ba565b03816001600160a01b0385165afa9384156107ff57600090600095613b96575b50613925612f85565b9260808101519561393a856111e4898661261d565b3403613b6b5761399295965061ffff8251169060208301519060408401519060608501519060206080870151604051809c8192630cbcf9e160e21b835289600484016020909392919361ffff60408201951681520152565b03816001600160a01b038c165afa998a156107ff5760009a613b35575b5060c086015160e08701518751604051633d77cbfd60e01b815261ffff9182166004820152949193921691906020856024816001600160a01b038f165afa9485156107ff57600095613af0575b509060049c613a719897969594939260018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b8b5260208b015260408a01526060890152608088015260a087015260c086015260e085015261010084015261012083015233610140830152610160820152614ecc565b91602060ff6101406080850151940151169460405197888092632fe4c87f60e21b825260018060a01b03165afa9485156107ff57613ab796600096613abf575b50613007565b15612b1c5790565b613ae291965060203d602011613ae9575b613ada8183611ef6565b8101906148f4565b9438613ab1565b503d613ad0565b97969594509291906020883d602011613b2d575b81613b1160209383611ef6565b810103126101d8579651959694959394919290919060046139fc565b3d9150613b04565b919099506020823d602011613b63575b81613b5260209383611ef6565b810103126101d857905198876139af565b3d9150613b45565b6044613b7b866111e48a8761261d565b60405190631f89f67160e01b82523460048301526024820152fd5b9050613bae9194503d806000833e6134888183611ef6565b93903861391c565b61010081015190516040516366b69b9d60e01b81526001600160a01b03909216600483015261ffff166024820152604490fd5b90506020813d602011613c1b575b81613c0460209383611ef6565b810103126101d857613c15906125b0565b386138c4565b3d9150613bf7565b979a9995919361ffff9360ff98959a96929a6040519b613c428d611e6d565b868c168d5260208d015260408c015260608b015260808a015260a08901521660c087015260e086015260018060a01b0387166101008601526101208501521661014083015261ffff604051916328f41de360e01b835216600482015260208160248160018060a01b0388165afa9081156107ff57600091613ec1575b5015613bb65761ffff815116926000606083015160a084015195613cf66040519788938493635cf3af3360e11b8552600485016131ba565b03816001600160a01b0385165afa9384156107ff576000908195613ea3575b50613d1e612f85565b926080810151613d32856111e4838661261d565b3403613e93575061ffff81511695602082015190613d89604084015191606085015190602060808701518c6040519586928392630cbcf9e160e21b8452600484016020909392919361ffff60408201951681520152565b03816001600160a01b038c165afa9081156107ff578893600092613e5a575b5061ffff60c0880151169160e088015193602061ffff8a5116602460405180998193633d77cbfd60e01b8352600483015260018060a01b03165afa9586156107ff57600096613e25575b5091613a719795939160049d9e97959360018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b90956020823d602011613e52575b81613e4060209383611ef6565b81010312612ffc575051946004613df2565b3d9150613e33565b91909293506020823d602011613e8b575b81613e7860209383611ef6565b81010312612ffc57505187929138613da8565b3d9150613e6b565b613b7b856111e46044938661261d565b90613eb99295503d8091833e6134888183611ef6565b939038613d15565b906020823d602011613ef3575b81613edb60209383611ef6565b81010312612ffc5750613eed906125b0565b38613cbe565b3d9150613ece565b989796959492909161ffff9492856040519b613f168d611e6d565b168b5260208b015260408a015260608901526000608089015260a08801521660c086015260e08501526001600160a01b0316610100840152610120830152600f6101408301526000805160206153e08339815191525460ff8116156144485760081c6001600160a01b03163381900361441f575060018060a01b036101008301511661ffff835116604051906328f41de360e01b82526004820152602081602481855afa9081156107ff576000916143e5575b50156143b25761ffff835116926000606082015160a0830151956140016040519788938493635cf3af3360e11b8552600485016131ba565b0381855afa80156107ff5760009460009161438d575b509061406a929161ffff8251169060208301519060408401519060608501519060206080870151604051809a8192630cbcf9e160e21b835289600484016020909392919361ffff60408201951681520152565b03818a5afa9788156107ff57600098614359575b5061ffff60c08701511660e08701519161ffff8851169360405194633d77cbfd60e01b865260048601526020856024818d5afa9485156107ff57600095614314575b509060049a6140f29897969594939260018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b90602060ff6101406080840151930151169360405195868092632fe4c87f60e21b82525afa9384156107ff576000946142f3575b506040519161413483611ec0565b825260208201953487526040830190815260608301918252608083019460018060a01b0316855260a083019384526000805160206154208339815191528054600160401b811015611e3b57614190906001928382019055612e62565b9490946142dd57519788516001600160401b038111611e3b576141b38654612669565b601f81116142a0575b506020601f8211600114614237578190600498999a9b60009261422c575b5050600019600383901b1c191690831b1785555b5190840155516002830155516003820155019160018060a01b0390511682549160ff60a01b905160a01b16916001600160581b0360a81b1617179055565b0151905038806141da565b601f1982169a8760005260206000209b60005b81811061428a5750916004999a9b9c918487959410614271575b505050811b0185556141ee565b015160001960f88460031b161c19169055388080614264565b838301518e559c86019c6020938401930161424a565b6142cd90876000526020600020601f840160051c810191602085106142d3575b601f0160051c01906126a3565b386141bc565b90915081906142c0565b634e487b7160e01b600052600060045260246000fd5b61430d91945060203d602011613ae957613ada8183611ef6565b9238614126565b97969594509291906020883d602011614351575b8161433560209383611ef6565b810103126101d8579651959694959394919290919060046140c0565b3d9150614328565b9097506020813d602011614385575b8161437560209383611ef6565b810103126101d85751963861407e565b3d9150614368565b61406a939295506143a991503d806000833e6134888183611ef6565b90949192614017565b61010083015183516040516366b69b9d60e01b81526001600160a01b03909216600483015261ffff166024820152604490fd5b90506020813d602011614417575b8161440060209383611ef6565b810103126101d857614411906125b0565b38613fc9565b3d91506143f3565b6040516313f32dd760e31b81523360048201526001600160a01b03919091166024820152604490fd5b60405162f1e13160e51b8152600490fd5b9a999897969594939291906040519b6144718d611e6d565b61ffff168c5260208c015260408b015260608a0152608089015260a088015261ffff1660c087015260e0860152600160a01b600190031661010085015261012084015260ff166101408301526000805160206153e08339815191525460ff8116156144485760081c6001600160a01b03163381900361441f575060018060a01b036101008301511661ffff835116604051906328f41de360e01b82526004820152602081602481855afa9081156107ff576000916148ba575b50156143b25761ffff835116926000606082015160a0830151956145626040519788938493635cf3af3360e11b8552600485016131ba565b0381855afa80156107ff57600094859161489b575b5061ffff825116906020830151906145c76040850151916060860151906020608088015160405180958192630cbcf9e160e21b83528a600484016020909392919361ffff60408201951681520152565b03818b5afa9081156107ff578893600092614862575b5061ffff60c0890151169160e089015193602061ffff8b5116602460405180998193633d77cbfd60e01b835260048301525afa9586156107ff5760009661482d575b50916146539795939160049b9a9997959360018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b90602060ff6101406080840151930151169360405195868092632fe4c87f60e21b82525afa9384156107ff5760009461480c575b506040519161469583611ec0565b825260208201953487526040830190815260608301918252608083019460018060a01b0316855260a083019384526000805160206154208339815191528054600160401b811015611e3b576146f1906001928382019055612e62565b9490946142dd57519788516001600160401b038111611e3b576147148654612669565b601f81116147da575b506020601f821160011461478b578190600498999a9b60009261422c575050600019600383901b1c191690831b1785555190840155516002830155516003820155019160018060a01b0390511682549160ff60a01b905160a01b16916001600160581b0360a81b1617179055565b601f1982169a8760005260206000209b60005b8181106147c45750916004999a9b9c91848795941061427157505050811b0185556141ee565b838301518e559c86019c6020938401930161479e565b61480690876000526020600020601f840160051c810191602085106142d357601f0160051c01906126a3565b3861471d565b61482691945060203d602011613ae957613ada8183611ef6565b9238614687565b90956020823d60201161485a575b8161484860209383611ef6565b81010312612ffc57505194600461461f565b3d915061483b565b91909293506020823d602011614893575b8161488060209383611ef6565b81010312612ffc575051879291386145dd565b3d9150614873565b90506148b191943d8091833e6134888183611ef6565b93909338614577565b906020823d6020116148ec575b816148d460209383611ef6565b81010312612ffc57506148e6906125b0565b3861452a565b3d91506148c7565b908160209103126101d857516001600160a01b03811681036101d85790565b9094929160018060a01b0391828616926040938451906328f41de360e01b825261ffff8a16600497818985015260209a8b85602481875afa8015614ca6576000958691614c6d575b5015614c3d575088999a9b889961498686938a9b519b8c948594635cf3af3360e11b865285016131ba565b0381855afa948515614c335783978496614c11575b506149a4612f85565b93966149b0858a61261d565b3403614bea5760838d938c9998979695938c614a77948151906149d282611ec0565b838252888201948552828201908152606082019b8c52614a0d614a0260a060808501948b86520195338752615235565b955191519c516152b7565b9151935192519b8c95600160f91b8b880152614a32815180928d60218b01910161227e565b86019161ffff60f01b9060f01b1660218301526023820152614a5d825180938b60438501910161227e565b019160438301526063820152036063810188520186611ef6565b8851632fe4c87f60e21b815296879182905afa948515614be057918493918b93614acd9897614bbf575b5088516358cd21bf60e11b8152808b0186905260606024820152978893849291839160648301906122a1565b60c8604483015203927f0000000000000000000000000000000000000000000000000000000000000000165af1938415614bb3578194614b7c575b5080614b695750614b1b82600092613138565b9684519283528201527fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e485364836001600160401b03841692a29315614b5c575050565b516304aeb27d60e51b8152fd5b634e487b7160e01b815260118652602490fd5b9093508781813d8311614bac575b614b948183611ef6565b8101031261375957614ba684916123b1565b93614b08565b503d614b8a565b508451903d90823e3d90fd5b85919650614bd990853d8711613ae957613ada8183611ef6565b9590614aa1565b87513d86823e3d90fd5b60448b8b614bf8888d61261d565b905191631f89f67160e01b835234908301526024820152fd5b909550614c299197503d8085833e6134888183611ef6565b969096943861499b565b88513d85823e3d90fd5b88516366b69b9d60e01b81526001600160a01b03909116818b0190815261ffff8e16602082015281906040010390fd5b90508c81813d8311614c9f575b614c848183611ef6565b81010312614c9b57614c95906125b0565b3861495b565b8580fd5b503d614c7a565b89513d6000823e3d90fd5b6000805160206153c083398151915254604051633d77cbfd60e01b815261ffff929092166004830152602090829060249082906001600160a01b03165afa80156107ff57600090614d07575b6120f1915061263d565b6020823d8211614d32575b81614d1f60209383611ef6565b81010312612ffc57506120f19051614cfd565b3d9150614d12565b600080516020615400833981519152546001600160a01b03908116929183158015614db9575b614da0575b61ffff60246020926040519485938492633d77cbfd60e01b845216600483015287165afa80156107ff57600090614d07576120f1915061263d565b6000805160206153c08339815191525482169350614d65565b506040516328f41de360e01b815261ffff82166004820152602081602481885afa9081156107ff57600091614df0575b5015614d60565b906020823d8211614e21575b81614e0960209383611ef6565b81010312612ffc5750614e1b906125b0565b38614de9565b3d9150614dfc565b92614e549493614e4c92614e46602095604051906102bb82611e20565b91614e59565b939093612720565b015190565b9260009291614e7f9460405195869485938493635cf3af3360e11b8552600485016131ba565b03916001600160a01b03165afa80156107ff576000918291614ead575b50614eaa9091611077612f85565b91565b614ec59150614eaa923d8091833e6134888183611ef6565b9091614e9c565b805190602080820151926040614ee4818501516152b7565b93614f4a608360608301519660808401519785519889928984019b600160f81b8d5261ffff60f01b809a60f01b1660218601526023850152614f2f815180928c60438801910161227e565b83019160438301526063820152036063810188520186611ef6565b614f5760a08201516152b7565b60c08201519360e0830151610100840151916101208501519361016061014087015196015196614f8a60ff895110612366565b875181519060ff60f81b9060f81b168b82015260018181809352614fad81611edb565b996000925b61502a575b505050519a8b9a5190818b8d01614fcd9261227e565b8a01815191828b808401920191614fe39261227e565b019660f01b168787015260228601526042850152606284015260828301528051809360a284019201916150159261227e565b010360828101825260a2016120f19082611ef6565b9091998c82518c101561509457829161508c8594926150548f61504e908896612f71565b51615235565b928851938161506c869351809286808701910161227e565b82016150808251809386808501910161227e565b01038084520182611ef6565b9b0192614fb2565b5099614fb7565b906150a4612e09565b916150ae8161538c565b918282016150d660228061ffff938460028201511689520151956020968789015201846152f9565b929060409081880152838501956150f78280838a01519901519601876152f9565b989060a08201526083808a89018760028201511660c0850152602281015160e0850152604281015161010085015260628101516101208501526082810151610140850152015199019360ff809a169661514f88611f79565b9761515c8651998a611ef6565b80895261516b601f1991611f79565b018460005b82811061521f5750505060005b88518110156151fa578b906151906131da565b97808c019060019384830151168481036151dd57508a6003830151168a526023820151888b01526001600160401b03602b8093015116898b015201976151d6828c612f71565b520161517d565b604490858b5191633ce5fedf60e11b835260048301526024820152fd5b509496925097925097506123fa959350610160880152606087015260808601526137de565b6152276131da565b82828d010152018590615170565b805160209060408284015193015160405193849260605160005b8181106152a25750600160f81b9085019586015260f01b6001600160f01b0319166021850152602384015260c01b6001600160c01b0319166043830152602b910390810182526120f190604b0182611ef6565b6080810151888201880152879550860161524f565b6120f16024825160405193849163ffffffff60e01b9060e01b1660208301526152e9815180926020868601910161227e565b8101036004810184520182611ef6565b9061531491600463ffffffff81848401015116920190615318565b9091565b9290821561536f578281019260405194601f8216928315615366575b838701938385019201015b8184106153565750508452601f01601f1916604052565b805184526020938401930161533f565b60209350615334565b9250905060405161537f81611e20565b6000815260003681379190565b600101519060ff600192168281036153a15750565b6044908360405191633ce5fedf60e11b835260048301526024820152fdfeebc28a1927f62765bfb7ada566eeab2d31a98c65dbd1e8cad64acae2a3ae45d41a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd221a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd231a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd251a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd24a2646970667358221220c7763b1cff8a3578b7a6011a42750ec671ba860b95cb4e3e8f2a1d141ea4a25164736f6c63430008130033000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca46
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c80631d6bd5aa14611d705780632385904a14611cca57806324320c9f14611c9457806328b1d85214611ad55780632936558f14611aa05780632c75470f14611a84578063329a2be714611a4857806332b2fc0e146119a65780633a2c767d146119675780633e8267e71461190d5780633ed334df146116f45780634533e5ff1461167f5780634b5ca6f4146115eb5780634d48ec601461158a5780635cb8cae2146112755780636000415714610cb657806375ea8b5814610c9857806380ebabd014610c645780638b0301b114610c165780638fecdd0214610bca578063a60eb4c81461036b578063a79629d8146102fc578063b1eac8751461028e578063b686d0891461022e578063c23ee3c3146101dd578063c4d66de8146101765763c81fb7fe1461014557600080fd5b6020610165610153366122c6565b99989098979197969296959395613c23565b6001600160401b0360405191168152f35b346101d85760203660031901126101d8576004356001600160a01b038116908190036101d85760016000546101ae60ff821615612366565b60ff1916176000556000805160206153c083398151915280546001600160a01b0319169091179055005b600080fd5b346101d85760603660031901126101d85760406102226101fb611dad565b6000805160206153c0833981519152546001600160a01b0316906044359060243590614e29565b82519182526020820152f35b60e03660031901126101d85761024336611f90565b61024b611dbe565b60a4356001600160401b03918282116101d857602093610272610285933690600401611f32565b9061027b611df4565b9260843591614913565b60405191168152f35b6102fa61029a36612083565b94916102c26102ac8496959396614d3a565b5095604051906102bb82611e20565b81526134c9565b9161ffff6000805160206154008339815191525460a01c1693600080516020615440833981519152549560018060a01b031690613efb565b005b346101d85760803660031901126101d857610315611dad565b604435906001600160401b0382116101d85761033861034a923690600401611f32565b610340611e0a565b9160243590614e59565b9061036760405192839283526040602084015260408301906122a1565b0390f35b60803660031901126101d8576004356001600160401b0381116101d8576103969036906004016121fe565b6024356001600160401b0381116101d8576103b5903690600401611f32565b906044356001600160a01b03811681036101d857606435926001600160401b0384116101d85760006103ee610411953690600401611f32565b916040518096819263607ec5ef60e11b83526020600484015260248301906122a1565b03817f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca466001600160a01b03165afa80156107ff57600094600090600092610b9f575b5015610b7a5750606084015161ffff1660009081527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d6020526040902054608085015190808203610b4d5750506104ad60e085015161509b565b916104bb602084015161263d565b6104c961010085015161263d565b60c085015160e0860151926000805160206153e0833981519152549060ff8216610b2257906001916101008360a81b039060081b16906001600160581b0360a81b1617176000805160206153e083398151915255600080516020615400833981519152549061ffff60a01b9060a01b169160018060a01b03169069ffffffffffffffffffff60b01b161717600080516020615400833981519152556000805160206154408339815191525561ffff606086015116946101406001600160401b0360a08301511691015190604051966105a088611e6d565b87526020870152604086015260018060a01b031660608501528260808501528160a0850152600060c0850152600060e0850152600061010085015280610120850152600061014085015260006105f960a08401516126da565b610602816126ba565b80610ae9575061061560a0840151612720565b9180516109bb575b5061065f9082602061066b945191015191610641606087015160808801519061261d565b906101408901526101008801528160e08801528060c088015261262a565b6101008501519061261d565b80341061099e575061ffff8151168061ffff7f0000000000000000000000000000000000000000000000000000000000000017160361098657506101600151908151815180820361096857505060005b825181101561080b576106f660006106d38385612f71565b516040518093819263a9e1189360e01b83526020600484015260248301906122a1565b03817f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca466001600160a01b03165afa9081156107ff576000916107be575b5061073e8285612f71565b519061ffff82511661ffff60608301511614918215926107ab575b821561078a575b505061076e576001016106bb565b604051633ad7858760e21b815260ff9091166004820152602490fd5b6001600160401b0380929350604060a0920151169201511614158580610760565b6020810151608083015114159250610759565b3d9150816000823e6107d08282611ef6565b60208183810103126101d85780516001600160401b0381116101d8576107f99282019101612416565b85610733565b6040513d6000823e3d90fd5b610814846127b4565b6001600160581b0360a81b6000805160206153e083398151915254166000805160206153e08339815191525569ffffffffffffffffffff60b01b6000805160206154008339815191525416600080516020615400833981519152556000600080516020615440833981519152556000805160206154208339815191528054600082558061089d57005b6005918183029183830403610952576000527f3956f3466fff97ca672672c3102a70900b3589f53a1cac769d9a51ee18555a4f908101905b8181106108de57005b806108ea849254612669565b80610913575b5060006001820155600060028201556000600382015560006004820155016108d5565b601f9081811160011461092d575050600081555b846108f0565b610949600092848452602084209201851c8201600183016126a3565b81835555610927565b634e487b7160e01b600052601160045260246000fd5b60449250604051916316bde1ed60e31b835260048301526024820152fd5b6024906040519063d8215fc960e01b82526004820152fd5b60449060405190620885af60e61b82523460048301526024820152fd5b9050610a16604051916109cd83611e89565b60008352606060208401526000604084015260206109ea8261538c565b6109fc828083860101519201846152f9565b9190838701528282850101516040870152855201906137de565b6060830151610a236126f7565b50815110610ad757610a3860208201516126da565b610a41816126ba565b80610aac5750610a546020820151612720565b918151906020840151602082015111610a9a578351905111610a88576060840152600060808401526040015161066b61061d565b6040516315fc687d60e31b8152600490fd5b60405163067dbecf60e11b8152600490fd5b80610ab86044926126ba565b60ff6040519163170cd96160e11b835260006004840152166024820152fd5b604051631c6e090160e31b8152600490fd5b80610af6610b1e926126ba565b60405163c1f4bdd960e01b815260ff9091166004820152600060248201529081906044820190565b0390fd5b6040516320b84ced60e01b81523360048201526001600160a01b03600884901c166024820152604490fd5b6064925061ffff6060870151169060405192633bb6036760e11b8452600484015260248301526044820152fd5b60405163b72c3b7f60e01b815260206004820152908190610b1e9060248301906122a1565b915050610bc09194503d806000833e610bb88183611ef6565b8101906125bd565b9094919486610453565b6020610165610bd8366121a6565b610be58592939495614cb1565b9260018060a01b036000805160206153c083398151915254169460405196610c0c88611e20565b6000885284613809565b60e03660031901126101d8576020610165610c3036611f90565b610c38611dbe565b610c40611df4565b91610c5a604051610c5081611e20565b60a43581526134c9565b9160843591614913565b346101d85760803660031901126101d8576040610222610c82611dad565b610c8a611e0a565b906044359060243590614e29565b6102fa610ca4366122c6565b99989098979197969296959395614459565b346101d8576003196020368201126101d857600435906001600160401b03908183116101d8576101209083360301126101d85760405190610cf682611e51565b610d0283600401611dcf565b825260208201926024810135845260448101358281116101d857610d2c9060043691840101611f32565b91604084019283526060840191606481013583526084810135608086015260a481013560a086015260c085019160c4820135835260e086019160e481013583526101048101359182116101d8576004610d8892369201016121fe565b92610100860193845230330361126357519551610de9939192906001600160a01b0390610db49061263d565b169451915190519261ffff87511690519060405195869463294ee51960e11b602087015260a0602487015260c48601906122a1565b94602319858703016044860152835180875260208701966020808360051b8301019601976000915b83831061122c57505050506064850152608484015260a483015203601f1981018352610e3e915082611ef6565b610e8460845a926080860151948151906040519660208089019401918af1923d80608410600114611224575b808252601f01601f19168101602001604052915a90612dfc565b8481101561121c57915b1561120b5750604051610ea081611e20565b6000815292600092600080516020615420833981519152549182610ee9575b50505060ff92610367915b60405194859416845260208401526060604084015260608301906122a1565b839450610efc610f05939460a092612dfc565b9101519061262a565b90610f0e612f85565b90610f1881611f79565b92610f266040519485611ef6565b818452601f19610f3583611f79565b0160005b8181106111f4575050600080916000935b8581861061117d575050610f5f92935061261d565b9080821061115957610fd360206001600160a01b03610f8b610120610f8389612f64565b51015161263d565b1661ffff610f9888612f64565b51511690610fa68587612dfc565b604051630cbcf9e160e21b815261ffff909316600484015260248301529092839190829081906044820190565b03915afa60009181611125575b50610ffe57604051632baa6b8960e11b815260048101879052602490fd5b61101890959495608061101088612f64565b51015161261d565b608061102387612f64565b51015260005b6000805160206154208339815191525481101561110f576110c8600261104e83612e62565b500154600361105c84612e62565b50015461107d84159182600014611107576110778789612dfc565b9061261d565b90156110f05761109561108f8a612f64565b51614ecc565b60ff60046110a287612e62565b50015460a01c16916110b386612e62565b50600401546001600160a01b03169389613007565b9050156110d757600101611029565b6040516336e7e91f60e11b815260048101869052602490fd5b6111026110fc85612e62565b50612ec0565b611095565b60009061261d565b5060039450859250610367915060ff9050610ebf565b9091506020813d602011611151575b8161114160209383611ef6565b810103126101d857519088610fe0565b3d9150611134565b8490606492604051926385880e2960e01b8452600484015260248301526044820152fd5b6111e9906111e46111d96111a26001959697866111998c612e62565b5001549061261d565b978b6111c28b6111bc6111b76110fc83612e62565b61509b565b92612f71565b526111cd8a8d612f71565b5060026111998b612e62565b60036111998a612e62565b61261d565b940193929190610f4a565b6020906111ff612e09565b82828901015201610f39565b60ff93506001925061036791610eca565b508391610e8e565b506084610e6a565b919395975091939560208061124d600193601f198682030187528c516122a1565b9a01930193019092899795939896949298610e11565b60405163390996ad60e11b8152600490fd5b346101d8576020806003193601126101d8576004356001600160401b0381116101d8576112a6903690600401611f32565b60018060a01b03917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc926112dd8185541693613560565b828101516e576f726d686f6c6552656c6179657280820361156c57505060ff6021820151166002810361154d5750602381015161ffff90818116801580611545575b15611460575b50505061133e611338604383015161263d565b916137b3565b803b15611405571692836bffffffffffffffffffffffff60a01b825416179055600080604051857fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8380a2632c75470f60e01b81850190815260048252906113a581611edb565b519082305af1906113b4612784565b91156113e35750507f2e4cc16c100f0b55e2df82ab0b1a7e294aa9cbd01b48fbaf622683fbc0507a49600080a3005b610b1e60405192839263135687c760e31b8452600484015260248301906122a1565b60405162461bcd60e51b815260048101849052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b60405163380e7c8960e21b815286816004817f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca468a165afa9081156107ff57600091611510575b506114fe577f0000000000000000000000000000000000000000000000000000000000000017928316036114da5780611325565b60405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b60405163ea03b6eb60e01b8152600490fd5b90508681813d831161153e575b6115278183611ef6565b810103126101d857611538906125b0565b896114a6565b503d61151d565b50600061131f565b60449060405190633460202560e21b8252600482015260026024820152fd5b6044925060405191633d254c6160e01b835260048301526024820152fd5b6102fa611596366120f4565b99909891979196939592949293926001600160a01b0390818a16156115d2575b6115c88291604051906102bb82611e20565b9616971690614459565b60008051602061540083398151915254821699506115b6565b60e03660031901126101d8576115ff611dad565b611607611dde565b906001600160401b03906044358281116101d857611629903690600401611f32565b60a4359161ffff831683036101d85760209461028593611647611df4565b9160018060a01b036000805160206153c08339815191525416936040519561166e87611e20565b600087526084359260643592613809565b6102fa61168b366121a6565b929391906116aa61169b84614d3a565b5094604051906102bb82611e20565b9061ffff6000805160206154008339815191525460a01c16926000805160206154408339815191525494604051976116e189611e20565b600089526001600160a01b031690613efb565b346101d8576020806003193601126101d8576004356001600160401b0381116101d85761172861172d913690600401611f32565b613560565b90808201516e576f726d686f6c6552656c6179657280820361156c57505060ff602183015116600181036118ee5750602382015161ffff90818116908115806118e6575b15611811575b5050602583015160458401519351604581036117f257501690816000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d91828252604060002054806117d4575060005252604060002055600080f35b6044925060405191637b5672c560e11b835260048301526024820152fd5b6044906040519063061bc83560e51b8252600482015260456024820152fd5b60405163380e7c8960e21b815284816004817f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca466001600160a01b03165afa9081156107ff576000916118b1575b506114fe577f000000000000000000000000000000000000000000000000000000000000001791838316146117775760405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b90508481813d83116118df575b6118c88183611ef6565b810103126101d8576118d9906125b0565b8661185e565b503d6118be565b506001611771565b60449060405190633460202560e21b8252600482015260016024820152fd5b346101d85760203660031901126101d857602061195f61192b611dad565b61ffff166000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d60205260406000205490565b604051908152f35b6020610165611991611978366120f4565b9a9099919897929594604097949751906102bb82611e20565b6001600160a01b039586169790951690613c23565b6101003660031901126101d8576119bb611dad565b6119c3611dde565b6001600160401b03906044358281116101d8576119e4903690600401611f32565b60a4358381116101d8576119fc903690600401611fdc565b9060c43561ffff811681036101d8576001600160a01b039360e435919085831683036101d857602097610285966000805160206153c08339815191525416946084359260643592613809565b6020610165611a5636612083565b9491611a6183614cb1565b6000805160206153c0833981519152546001600160a01b03169590949084613809565b346101d85760003660031901126101d8576102fa303314612366565b346101d85760203660031901126101d8576020611ac3611abe611dad565b614cb1565b6040516001600160a01b039091168152f35b346101d8576020806003193601126101d8576004356001600160401b0381116101d857611728611b09913690600401611f32565b90808201516e576f726d686f6c6552656c6179657280820361156c57505060ff60218301511660038103611c755750602382015161ffff9182821690811580611c6d575b15611bb4575b50505050604381015190611b696113388361263d565b6001600160a01b0316908115611b9c57506000805160206153c083398151915280546001600160a01b0319169091179055005b60249060405190637a8ad12560e01b82526004820152fd5b60405163380e7c8960e21b815281816004817f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca466001600160a01b03165afa9182156107ff57600092611c37575b50506114fe577f0000000000000000000000000000000000000000000000000000000000000017928316036114da578080611b53565b90809250813d8311611c66575b611c4e8183611ef6565b810103126101d857611c5f906125b0565b8580611c01565b503d611c44565b506000611b4d565b60449060405190633460202560e21b8252600482015260036024820152fd5b346101d85760003660031901126101d8576000805160206153c0833981519152546040516001600160a01b039091168152602090f35b346101d85760603660031901126101d857611ce3611dad565b6044356001600160a01b038116908190036101d857604051630cbcf9e160e21b815261ffff9290921660048301526024803590830152602090829060449082905afa80156107ff57600090611d3e575b602090604051908152f35b506020813d8211611d68575b81611d5760209383611ef6565b810103126101d85760209051611d33565b3d9150611d4a565b346101d85760203660031901126101d857611d91611d8c611dad565b614d3a565b604080516001600160a01b039384168152919092166020820152f35b6004359061ffff821682036101d857565b6064359061ffff821682036101d857565b359061ffff821682036101d857565b602435906001600160a01b03821682036101d857565b60c435906001600160a01b03821682036101d857565b606435906001600160a01b03821682036101d857565b602081019081106001600160401b03821117611e3b57604052565b634e487b7160e01b600052604160045260246000fd5b61012081019081106001600160401b03821117611e3b57604052565b61016081019081106001600160401b03821117611e3b57604052565b606081019081106001600160401b03821117611e3b57604052565b61018081019081106001600160401b03821117611e3b57604052565b60c081019081106001600160401b03821117611e3b57604052565b604081019081106001600160401b03821117611e3b57604052565b90601f801991011681019081106001600160401b03821117611e3b57604052565b6001600160401b038111611e3b57601f01601f191660200190565b81601f820112156101d857803590611f4982611f17565b92611f576040519485611ef6565b828452602083830101116101d857816000926020809301838601378301015290565b6001600160401b038111611e3b5760051b60200190565b60609060031901126101d85760405190611fa982611e89565b8160043561ffff811681036101d85781526024356020820152604435906001600160401b03821682036101d85760400152565b81601f820112156101d857803590611ff382611f79565b9260409061200382519586611ef6565b83855260209182860191836060809702860101948186116101d8578401925b858410612033575050505050505090565b86848303126101d85782519061204882611e89565b61205185611dcf565b8252858501358683015283850135906001600160401b03821682036101d857828792868b950152815201930192612022565b60c06003198201126101d85760043561ffff811681036101d857916024356001600160a01b03811681036101d857916001600160401b03906044358281116101d857816120d291600401611f32565b92606435926084359260a4359182116101d8576120f191600401611fdc565b90565b906101606003198301126101d85761ffff9060043582811681036101d857926001600160a01b039160243583811681036101d857936001600160401b036044358181116101d8578461214891600401611f32565b94606435946084359460a4359460c43590811681036101d8579360e43584811681036101d857936101043590811681036101d85792610124359182116101d85761219491600401611fdc565b906101443560ff811681036101d85790565b60a06003198201126101d85760043561ffff811681036101d857916024356001600160a01b03811681036101d85791604435906001600160401b0382116101d8576121f391600401611f32565b906064359060843590565b9080601f830112156101d85781359061221682611f79565b926122246040519485611ef6565b828452602092838086019160051b830101928084116101d857848301915b8483106122525750505050505090565b82356001600160401b0381116101d857869161227384848094890101611f32565b815201920191612242565b60005b8381106122915750506000910152565b8181015183820152602001612281565b906020916122ba8151809281855285808601910161227e565b601f01601f1916010190565b6101606003198201126101d85761ffff9160043583811681036101d85792602435926001600160401b03916044358381116101d8578261230891600401611f32565b93606435936084359360a4358281116101d8578161232891600401611f32565b9360c43590811681036101d8579260e43592610104356001600160a01b03811681036101d85792610124359182116101d85761219491600401611fdc565b1561236d57565b634e487b7160e01b600052600160045260246000fd5b519060ff821682036101d857565b519063ffffffff821682036101d857565b519061ffff821682036101d857565b51906001600160401b03821682036101d857565b909291926123d281611f17565b916123e06040519384611ef6565b8294828452828201116101d85760206123fa93019061227e565b565b9080601f830112156101d85781516120f1926020016123c5565b9190610160838203126101d85760409283519161243283611e6d565b829461243d83612383565b8452602061244c818501612391565b8186015261245b828501612391565b82860152606061246c8186016123a2565b81870152608091828601518388015261248760a087016123b1565b60a088015261249860c08701612383565b60c088015260e0860151936001600160401b03948581116101d857866124bf9189016123fc565b60e08901526101006124d2818901612391565b9089015261012094858801518181116101d85788019387601f860112156101d8578451926124ff84611f79565b9861250c82519a8b611ef6565b848a5285808b019560071b880101968188116101d8578601945b8786106125455750505050505050505083015261014080910151910152565b88868303126101d857825190898201908282108783111761259b578a928992865288518152828901518382015261257d868a01612383565b8682015261258c878a01612383565b87820152815201950194612526565b60246000634e487b7160e01b81526041600452fd5b519081151582036101d857565b90916060828403126101d8578151926001600160401b03938481116101d857816125e8918501612416565b936125f5602085016125b0565b9360408101519182116101d857019080601f830112156101d85781516120f1926020016123c5565b9190820180921161095257565b8181029291811591840414171561095257565b8060a01c612651576001600160a01b031690565b6024906040519063033b960d60e41b82526004820152fd5b90600182811c92168015612699575b602083101461268357565b634e487b7160e01b600052602260045260246000fd5b91607f1691612678565b8181106126ae575050565b600081556001016126a3565b600111156126c457565b634e487b7160e01b600052602160045260246000fd5b6020818051810103126101d8576020015160018110156101d85790565b60405190604082018281106001600160401b03821117611e3b5760405260006020838281520152565b906127296126f7565b916060818051810103126101d85761274360208201612383565b90606060408201519101516020850152835260ff81166127605750565b60405163c1f4bdd960e01b815260ff91909116600482015260006024820152604490fd5b3d156127af573d9061279582611f17565b916127a36040519384611ef6565b82523d6000602084013e565b606090565b60009081602060a083015101518015612c91575b50612c8d576060604080516127dc81611e89565b848152846020820152015261ffff81511660a082015160208101519060408101519060c08501519061010086015161014060e0880151920151926040880151946080890151966040519861282f8a611e51565b8952602089015260408801526060870152608086015260a085015260c084015260e0830152610100820152826040518092636000415760e01b82526020600483015261ffff8151166024830152602081015160448301526101006128a4604083015161012060648601526101448501906122a1565b9160608101516084850152608081015160a485015260a081015160c485015260c081015160e485015260e081015161010485015201519060231983820301610124840152815180825260208201916020808360051b83010194019286915b838310612c5c5750505050508190038183305af1838185938693612bf8575b50612bb657505050612931612784565b61293a816134e7565b15612baa57905b6040519161294e83611e89565b82526002602083015260408201525b61296e602060a0840151015161263d565b9161ffff815116926001600160401b03602083015116946040830151906020850151926004841015612b965785519560a08601519560018060a01b036060820151166129cc6129c18a60c0850151612dfc565b60e08401519061262a565b9760208401516004811015612b825786906000600282148015612b76575b612b5b575b612b4057612a069190600303612b5457879061261d565b98612a2561ffff60c0840151168b60e0850151610100860151926131f9565b999060058b1015612b405791612a6e91612a7494938c158015612b36575b612b2e575b612a69612a6384608060606111e4969701519101519061261d565b34612dfc565b612dfc565b90613138565b15612b1c577fbccc00b713f54173962e7de6098f643d8ebf53d488d71f4b2a5171496d038f9e96612b01946040612ae8940151926101408101511515600014612b065761012091500151985b604051978897885260208801526040870152606086015260c0608086015260c08501906122a1565b83810360a08501526001600160a01b03909116956122a1565b0390a4565b5060405190612b1482611e20565b815298612ac0565b6040516304aeb27d60e51b8152600490fd5b899150612a48565b5060028d14612a43565b634e487b7160e01b88526021600452602488fd5b8a9061261d565b9150612b70606084015160808501519061261d565b916129ef565b505087600182146129ea565b634e487b7160e01b87526021600452602487fd5b634e487b7160e01b82526021600452602482fd5b5060c082015190612941565b600460ff82161015612be45760ff9060405193612bd285611e89565b8452166020830152604082015261295d565b634e487b7160e01b85526021600452602485fd5b93509150503d8085843e612c0c8184611ef6565b6060838281010312612c5857612c2183612383565b926020810151916040820151906001600160401b038211612c5457612c4a9290810191016123fc565b9290929138612921565b8780fd5b8480fd5b92955092955092602080612c7c600193601f1986820301875289516122a1565b970193019301889593879592612902565b5050565b612c9b915061263d565b61ffff9081835116906001600160401b036020850151169260408501519060a086015160018060a01b0360608801511690612cfb612ce2606083015160808401519061261d565b9360c0830151168460e0840151610100850151926131f9565b92906005841015612de857612d349291612a6e9185158015612dde575b612dd6575b612a6381608060606111e49401519101519061261d565b15612b1c577fbccc00b713f54173962e7de6098f643d8ebf53d488d71f4b2a5171496d038f9e91612db360405194612d6b86611e20565b60008087526101408a015115612dbe5750612ae86101208a0151965b60405195869586526000602087015260006040870152606086015260c0608086015260c08501906122a1565b0390a46001386127c8565b604051612ae891612dce82611e20565b815296612d87565b8b9150612d1d565b5060028614612d18565b634e487b7160e01b8a52602160045260248afd5b9190820391821161095257565b60405190612e1682611ea4565b606061016083600080825280602083015283604083015280848301528060808301528360a08301528060c08301528060e083015280610100830152806101208301526101408201520152565b600080516020615420833981519152908154811015612eaa57600591600052027f3956f3466fff97ca672672c3102a70900b3589f53a1cac769d9a51ee18555a4f0190600090565b634e487b7160e01b600052603260045260246000fd5b9060405191826000825492612ed484612669565b908184526001948581169081600014612f415750600114612efe575b50506123fa92500383611ef6565b9093915060005260209081600020936000915b818310612f295750506123fa93508201013880612ef0565b85548884018501529485019487945091830191612f11565b9150506123fa94506020925060ff191682840152151560051b8201013880612ef0565b805115612eaa5760200190565b8051821015612eaa5760209160051b010190565b604051631a90a21960e01b81526020816004817f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca466001600160a01b03165afa9081156107ff57600091612fd6575090565b906020823d8211612fff575b81612fef60209383611ef6565b81010312612ffc57505190565b80fd5b3d9150612fe2565b60ff90613042602093959896949798604051958694859384936358cd21bf60e11b8552600060048601526060602486015260648501906122a1565b9116604483015203917f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca466001600160a01b03165af19081156107ff576000916130d2575b5060406001600160401b03826130c27fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e4853649498612a6e898861261d565b96835195865260208601521692a2565b906020823d8211613130575b816130eb60209383611ef6565b81010312612ffc575060406001600160401b036131287fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e485364936123b1565b925050613086565b3d91506130de565b9080156131865760006020918160405161315181611e20565b5281805a926040519686880194f1913d80151560011461317e575b808252601f01601f1916010160405290565b50600061316c565b5050600190565b9190916040818403126101d85780519260208201516001600160401b0381116101d8576120f192016123fc565b6120f1939261ffff606093168252602082015281604082015201906122a1565b604051906131e782611e89565b60006040838281528260208201520152565b91909261ffff80931693837f0000000000000000000000000000000000000000000000000000000000000017168514613499576001600160a01b03938461323f8461263d565b169160009060409361325d855161325581611e20565b8481526134c9565b928086518093635cf3af3360e11b82528c600483015282602483015281806132916060998a604484015260648301906122a1565b03915afa90918282613473575b50506132bb575050505050505050506132b5612784565b50600390565b966132c8886111e4612f85565b87111561346557849298969497959793919351956132e587611e20565b600087526132f1612f85565b6132fb9087612dfc565b9061330591612dfc565b90835161331181611e20565b6000815261331e906134c9565b986133289061263d565b84519561333487611e20565b6000875285519a8b9963640fdbff60e11b8b528b60048c015260248b016000905260448b0161016090526101648b0161336c916122a1565b9460648b016000905260848b015260031994858b82030160a48c0152613391916122a1565b9960c48a015260e4890152166101048701528587030161012486015282519081875260209889808099019501936000925b84841061342d5750505050505082809160c86101448301520391305af191826133f9575b50506133f4576132b5612784565b600290565b81813d8311613426575b61340d8183611ef6565b810103126101d85761341e906123b1565b5038806133e6565b503d613403565b8551805182168852808b01518b8901528201516001600160401b0316828801528b998b995096830196909501946001909301926133c2565b505050505050505050600490565b6134909293503d8091833e6134888183611ef6565b81019061318d565b5090388061329e565b6134ba94509192506001600160a01b03916134b4915061263d565b16613138565b156134c457600090565b600190565b5160405190600060208301526040820152604081526120f181611e89565b60009160009160248151106135565763ffffffff60e01b600482015160e01b166385880e2960e01b8114908115613545575b8115613534575b506135285750565b60240151925060019150565b6336e7e91f60e11b14905038613520565b632baa6b8960e11b81149150613519565b5060009250829150565b9060018060a01b037f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca46169160406135b981519263607ec5ef60e11b845260209560049187838701528580600095869360248301906122a1565b0381845afa80156136f757839584908592613791575b501561376e5750835163fbe3c2cd60e01b815287818481855afa90811561376457849161372b575b5061ffff908160608801511691811682036137015750508682918551928380926358b9591160e11b82525afa9081156136f75783916136c6575b5060808501518181036136ab575050610140840195865183527f970ad24d4754c92e299cabb86552091f5df0a15abc0f1b71f37d3e30031585dc9182825260ff8585205416613694575060e095965183525220600160ff19825416179055015190565b875185516364cbf47160e01b815291820152602490fd5b84516342852f8d60e11b815292830152602482015260449150fd5b90508681813d83116136f0575b6136dd8183611ef6565b810103126136ec575138613631565b8280fd5b503d6136d3565b84513d85823e3d90fd5b855163c97817ed60e01b815261ffff92831685820190815291909216602082015281900360400190fd5b90508781813d831161375d575b6137428183611ef6565b8101031261375957613753906123a2565b386135f7565b8380fd5b503d613738565b85513d86823e3d90fd5b8451630169d68560e71b8152808401899052908190610b1e9060248301906122a1565b9150506137a99195503d8085833e610bb88183611ef6565b90959195386135cf565b51604381036137bf5750565b6044906040519063061bc83560e51b8252600482015260436024820152fd5b51908082036137eb575050565b604492506040519163061bc83560e51b835260048301526024820152fd5b95939261382761ffff949a99969392989a604051906102bb82611e20565b91604051986138358a611e6d565b8589168a5260018060a01b031660208a0152604089015260608801526000608088015260a08701521660c085015260018060a01b031660e084015260018060a01b038516610100840152610120830152600f61014083015261ffff604051916328f41de360e01b835216600482015260208160248160018060a01b0388165afa9081156107ff57600091613be9575b5015613bb65761ffff815116926000606083015160a0840151956138fc6040519788938493635cf3af3360e11b8552600485016131ba565b03816001600160a01b0385165afa9384156107ff57600090600095613b96575b50613925612f85565b9260808101519561393a856111e4898661261d565b3403613b6b5761399295965061ffff8251169060208301519060408401519060608501519060206080870151604051809c8192630cbcf9e160e21b835289600484016020909392919361ffff60408201951681520152565b03816001600160a01b038c165afa998a156107ff5760009a613b35575b5060c086015160e08701518751604051633d77cbfd60e01b815261ffff9182166004820152949193921691906020856024816001600160a01b038f165afa9485156107ff57600095613af0575b509060049c613a719897969594939260018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b8b5260208b015260408a01526060890152608088015260a087015260c086015260e085015261010084015261012083015233610140830152610160820152614ecc565b91602060ff6101406080850151940151169460405197888092632fe4c87f60e21b825260018060a01b03165afa9485156107ff57613ab796600096613abf575b50613007565b15612b1c5790565b613ae291965060203d602011613ae9575b613ada8183611ef6565b8101906148f4565b9438613ab1565b503d613ad0565b97969594509291906020883d602011613b2d575b81613b1160209383611ef6565b810103126101d8579651959694959394919290919060046139fc565b3d9150613b04565b919099506020823d602011613b63575b81613b5260209383611ef6565b810103126101d857905198876139af565b3d9150613b45565b6044613b7b866111e48a8761261d565b60405190631f89f67160e01b82523460048301526024820152fd5b9050613bae9194503d806000833e6134888183611ef6565b93903861391c565b61010081015190516040516366b69b9d60e01b81526001600160a01b03909216600483015261ffff166024820152604490fd5b90506020813d602011613c1b575b81613c0460209383611ef6565b810103126101d857613c15906125b0565b386138c4565b3d9150613bf7565b979a9995919361ffff9360ff98959a96929a6040519b613c428d611e6d565b868c168d5260208d015260408c015260608b015260808a015260a08901521660c087015260e086015260018060a01b0387166101008601526101208501521661014083015261ffff604051916328f41de360e01b835216600482015260208160248160018060a01b0388165afa9081156107ff57600091613ec1575b5015613bb65761ffff815116926000606083015160a084015195613cf66040519788938493635cf3af3360e11b8552600485016131ba565b03816001600160a01b0385165afa9384156107ff576000908195613ea3575b50613d1e612f85565b926080810151613d32856111e4838661261d565b3403613e93575061ffff81511695602082015190613d89604084015191606085015190602060808701518c6040519586928392630cbcf9e160e21b8452600484016020909392919361ffff60408201951681520152565b03816001600160a01b038c165afa9081156107ff578893600092613e5a575b5061ffff60c0880151169160e088015193602061ffff8a5116602460405180998193633d77cbfd60e01b8352600483015260018060a01b03165afa9586156107ff57600096613e25575b5091613a719795939160049d9e97959360018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b90956020823d602011613e52575b81613e4060209383611ef6565b81010312612ffc575051946004613df2565b3d9150613e33565b91909293506020823d602011613e8b575b81613e7860209383611ef6565b81010312612ffc57505187929138613da8565b3d9150613e6b565b613b7b856111e46044938661261d565b90613eb99295503d8091833e6134888183611ef6565b939038613d15565b906020823d602011613ef3575b81613edb60209383611ef6565b81010312612ffc5750613eed906125b0565b38613cbe565b3d9150613ece565b989796959492909161ffff9492856040519b613f168d611e6d565b168b5260208b015260408a015260608901526000608089015260a08801521660c086015260e08501526001600160a01b0316610100840152610120830152600f6101408301526000805160206153e08339815191525460ff8116156144485760081c6001600160a01b03163381900361441f575060018060a01b036101008301511661ffff835116604051906328f41de360e01b82526004820152602081602481855afa9081156107ff576000916143e5575b50156143b25761ffff835116926000606082015160a0830151956140016040519788938493635cf3af3360e11b8552600485016131ba565b0381855afa80156107ff5760009460009161438d575b509061406a929161ffff8251169060208301519060408401519060608501519060206080870151604051809a8192630cbcf9e160e21b835289600484016020909392919361ffff60408201951681520152565b03818a5afa9788156107ff57600098614359575b5061ffff60c08701511660e08701519161ffff8851169360405194633d77cbfd60e01b865260048601526020856024818d5afa9485156107ff57600095614314575b509060049a6140f29897969594939260018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b90602060ff6101406080840151930151169360405195868092632fe4c87f60e21b82525afa9384156107ff576000946142f3575b506040519161413483611ec0565b825260208201953487526040830190815260608301918252608083019460018060a01b0316855260a083019384526000805160206154208339815191528054600160401b811015611e3b57614190906001928382019055612e62565b9490946142dd57519788516001600160401b038111611e3b576141b38654612669565b601f81116142a0575b506020601f8211600114614237578190600498999a9b60009261422c575b5050600019600383901b1c191690831b1785555b5190840155516002830155516003820155019160018060a01b0390511682549160ff60a01b905160a01b16916001600160581b0360a81b1617179055565b0151905038806141da565b601f1982169a8760005260206000209b60005b81811061428a5750916004999a9b9c918487959410614271575b505050811b0185556141ee565b015160001960f88460031b161c19169055388080614264565b838301518e559c86019c6020938401930161424a565b6142cd90876000526020600020601f840160051c810191602085106142d3575b601f0160051c01906126a3565b386141bc565b90915081906142c0565b634e487b7160e01b600052600060045260246000fd5b61430d91945060203d602011613ae957613ada8183611ef6565b9238614126565b97969594509291906020883d602011614351575b8161433560209383611ef6565b810103126101d8579651959694959394919290919060046140c0565b3d9150614328565b9097506020813d602011614385575b8161437560209383611ef6565b810103126101d85751963861407e565b3d9150614368565b61406a939295506143a991503d806000833e6134888183611ef6565b90949192614017565b61010083015183516040516366b69b9d60e01b81526001600160a01b03909216600483015261ffff166024820152604490fd5b90506020813d602011614417575b8161440060209383611ef6565b810103126101d857614411906125b0565b38613fc9565b3d91506143f3565b6040516313f32dd760e31b81523360048201526001600160a01b03919091166024820152604490fd5b60405162f1e13160e51b8152600490fd5b9a999897969594939291906040519b6144718d611e6d565b61ffff168c5260208c015260408b015260608a0152608089015260a088015261ffff1660c087015260e0860152600160a01b600190031661010085015261012084015260ff166101408301526000805160206153e08339815191525460ff8116156144485760081c6001600160a01b03163381900361441f575060018060a01b036101008301511661ffff835116604051906328f41de360e01b82526004820152602081602481855afa9081156107ff576000916148ba575b50156143b25761ffff835116926000606082015160a0830151956145626040519788938493635cf3af3360e11b8552600485016131ba565b0381855afa80156107ff57600094859161489b575b5061ffff825116906020830151906145c76040850151916060860151906020608088015160405180958192630cbcf9e160e21b83528a600484016020909392919361ffff60408201951681520152565b03818b5afa9081156107ff578893600092614862575b5061ffff60c0890151169160e089015193602061ffff8b5116602460405180998193633d77cbfd60e01b835260048301525afa9586156107ff5760009661482d575b50916146539795939160049b9a9997959360018060a01b036101008c015116966101208c0151986040519a613a2e8c611ea4565b90602060ff6101406080840151930151169360405195868092632fe4c87f60e21b82525afa9384156107ff5760009461480c575b506040519161469583611ec0565b825260208201953487526040830190815260608301918252608083019460018060a01b0316855260a083019384526000805160206154208339815191528054600160401b811015611e3b576146f1906001928382019055612e62565b9490946142dd57519788516001600160401b038111611e3b576147148654612669565b601f81116147da575b506020601f821160011461478b578190600498999a9b60009261422c575050600019600383901b1c191690831b1785555190840155516002830155516003820155019160018060a01b0390511682549160ff60a01b905160a01b16916001600160581b0360a81b1617179055565b601f1982169a8760005260206000209b60005b8181106147c45750916004999a9b9c91848795941061427157505050811b0185556141ee565b838301518e559c86019c6020938401930161479e565b61480690876000526020600020601f840160051c810191602085106142d357601f0160051c01906126a3565b3861471d565b61482691945060203d602011613ae957613ada8183611ef6565b9238614687565b90956020823d60201161485a575b8161484860209383611ef6565b81010312612ffc57505194600461461f565b3d915061483b565b91909293506020823d602011614893575b8161488060209383611ef6565b81010312612ffc575051879291386145dd565b3d9150614873565b90506148b191943d8091833e6134888183611ef6565b93909338614577565b906020823d6020116148ec575b816148d460209383611ef6565b81010312612ffc57506148e6906125b0565b3861452a565b3d91506148c7565b908160209103126101d857516001600160a01b03811681036101d85790565b9094929160018060a01b0391828616926040938451906328f41de360e01b825261ffff8a16600497818985015260209a8b85602481875afa8015614ca6576000958691614c6d575b5015614c3d575088999a9b889961498686938a9b519b8c948594635cf3af3360e11b865285016131ba565b0381855afa948515614c335783978496614c11575b506149a4612f85565b93966149b0858a61261d565b3403614bea5760838d938c9998979695938c614a77948151906149d282611ec0565b838252888201948552828201908152606082019b8c52614a0d614a0260a060808501948b86520195338752615235565b955191519c516152b7565b9151935192519b8c95600160f91b8b880152614a32815180928d60218b01910161227e565b86019161ffff60f01b9060f01b1660218301526023820152614a5d825180938b60438501910161227e565b019160438301526063820152036063810188520186611ef6565b8851632fe4c87f60e21b815296879182905afa948515614be057918493918b93614acd9897614bbf575b5088516358cd21bf60e11b8152808b0186905260606024820152978893849291839160648301906122a1565b60c8604483015203927f000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca46165af1938415614bb3578194614b7c575b5080614b695750614b1b82600092613138565b9684519283528201527fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e485364836001600160401b03841692a29315614b5c575050565b516304aeb27d60e51b8152fd5b634e487b7160e01b815260118652602490fd5b9093508781813d8311614bac575b614b948183611ef6565b8101031261375957614ba684916123b1565b93614b08565b503d614b8a565b508451903d90823e3d90fd5b85919650614bd990853d8711613ae957613ada8183611ef6565b9590614aa1565b87513d86823e3d90fd5b60448b8b614bf8888d61261d565b905191631f89f67160e01b835234908301526024820152fd5b909550614c299197503d8085833e6134888183611ef6565b969096943861499b565b88513d85823e3d90fd5b88516366b69b9d60e01b81526001600160a01b03909116818b0190815261ffff8e16602082015281906040010390fd5b90508c81813d8311614c9f575b614c848183611ef6565b81010312614c9b57614c95906125b0565b3861495b565b8580fd5b503d614c7a565b89513d6000823e3d90fd5b6000805160206153c083398151915254604051633d77cbfd60e01b815261ffff929092166004830152602090829060249082906001600160a01b03165afa80156107ff57600090614d07575b6120f1915061263d565b6020823d8211614d32575b81614d1f60209383611ef6565b81010312612ffc57506120f19051614cfd565b3d9150614d12565b600080516020615400833981519152546001600160a01b03908116929183158015614db9575b614da0575b61ffff60246020926040519485938492633d77cbfd60e01b845216600483015287165afa80156107ff57600090614d07576120f1915061263d565b6000805160206153c08339815191525482169350614d65565b506040516328f41de360e01b815261ffff82166004820152602081602481885afa9081156107ff57600091614df0575b5015614d60565b906020823d8211614e21575b81614e0960209383611ef6565b81010312612ffc5750614e1b906125b0565b38614de9565b3d9150614dfc565b92614e549493614e4c92614e46602095604051906102bb82611e20565b91614e59565b939093612720565b015190565b9260009291614e7f9460405195869485938493635cf3af3360e11b8552600485016131ba565b03916001600160a01b03165afa80156107ff576000918291614ead575b50614eaa9091611077612f85565b91565b614ec59150614eaa923d8091833e6134888183611ef6565b9091614e9c565b805190602080820151926040614ee4818501516152b7565b93614f4a608360608301519660808401519785519889928984019b600160f81b8d5261ffff60f01b809a60f01b1660218601526023850152614f2f815180928c60438801910161227e565b83019160438301526063820152036063810188520186611ef6565b614f5760a08201516152b7565b60c08201519360e0830151610100840151916101208501519361016061014087015196015196614f8a60ff895110612366565b875181519060ff60f81b9060f81b168b82015260018181809352614fad81611edb565b996000925b61502a575b505050519a8b9a5190818b8d01614fcd9261227e565b8a01815191828b808401920191614fe39261227e565b019660f01b168787015260228601526042850152606284015260828301528051809360a284019201916150159261227e565b010360828101825260a2016120f19082611ef6565b9091998c82518c101561509457829161508c8594926150548f61504e908896612f71565b51615235565b928851938161506c869351809286808701910161227e565b82016150808251809386808501910161227e565b01038084520182611ef6565b9b0192614fb2565b5099614fb7565b906150a4612e09565b916150ae8161538c565b918282016150d660228061ffff938460028201511689520151956020968789015201846152f9565b929060409081880152838501956150f78280838a01519901519601876152f9565b989060a08201526083808a89018760028201511660c0850152602281015160e0850152604281015161010085015260628101516101208501526082810151610140850152015199019360ff809a169661514f88611f79565b9761515c8651998a611ef6565b80895261516b601f1991611f79565b018460005b82811061521f5750505060005b88518110156151fa578b906151906131da565b97808c019060019384830151168481036151dd57508a6003830151168a526023820151888b01526001600160401b03602b8093015116898b015201976151d6828c612f71565b520161517d565b604490858b5191633ce5fedf60e11b835260048301526024820152fd5b509496925097925097506123fa959350610160880152606087015260808601526137de565b6152276131da565b82828d010152018590615170565b805160209060408284015193015160405193849260605160005b8181106152a25750600160f81b9085019586015260f01b6001600160f01b0319166021850152602384015260c01b6001600160c01b0319166043830152602b910390810182526120f190604b0182611ef6565b6080810151888201880152879550860161524f565b6120f16024825160405193849163ffffffff60e01b9060e01b1660208301526152e9815180926020868601910161227e565b8101036004810184520182611ef6565b9061531491600463ffffffff81848401015116920190615318565b9091565b9290821561536f578281019260405194601f8216928315615366575b838701938385019201015b8184106153565750508452601f01601f1916604052565b805184526020938401930161533f565b60209350615334565b9250905060405161537f81611e20565b6000815260003681379190565b600101519060ff600192168281036153a15750565b6044908360405191633ce5fedf60e11b835260048301526024820152fdfeebc28a1927f62765bfb7ada566eeab2d31a98c65dbd1e8cad64acae2a3ae45d41a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd221a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd231a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd251a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd24a2646970667358221220c7763b1cff8a3578b7a6011a42750ec671ba860b95cb4e3e8f2a1d141ea4a25164736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000a5f208e072434bC67592E4C49C1B991BA79BCA46
-----Decoded View---------------
Arg [0] : wormhole (address): 0xa5f208e072434bC67592E4C49C1B991BA79BCA46
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000a5f208e072434bC67592E4C49C1B991BA79BCA46
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
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.