Overview
GLMR Balance
GLMR Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
10182193 | 1 hr ago | 0.03664969 GLMR | ||||
10182189 | 1 hr ago | 0.03664969 GLMR | ||||
10182186 | 1 hr ago | 0.01606854 GLMR | ||||
10182186 | 1 hr ago | 0.02857431 GLMR | ||||
10182186 | 1 hr ago | 0.02857431 GLMR | ||||
10182186 | 1 hr ago | 0.08711954 GLMR | ||||
10182186 | 1 hr ago | 0.08711954 GLMR | ||||
10182180 | 1 hr ago | 0.01606854 GLMR | ||||
10182180 | 1 hr ago | 0.02857431 GLMR | ||||
10182180 | 1 hr ago | 0.02857431 GLMR | ||||
10182180 | 1 hr ago | 0.08711954 GLMR | ||||
10182180 | 1 hr ago | 0.08711954 GLMR | ||||
10174101 | 15 hrs ago | 0.00892857 GLMR | ||||
10173972 | 15 hrs ago | 10.38796396 GLMR | ||||
10173814 | 15 hrs ago | 0.00892857 GLMR | ||||
10173061 | 17 hrs ago | 0.00892857 GLMR | ||||
10170527 | 21 hrs ago | 10.38796396 GLMR | ||||
10170495 | 21 hrs ago | 0.00184357 GLMR | ||||
10170495 | 21 hrs ago | 0.00172785 GLMR | ||||
10167771 | 26 hrs ago | 10.38796396 GLMR | ||||
10167587 | 26 hrs ago | 0.02905085 GLMR | ||||
10167576 | 26 hrs ago | 0.01239958 GLMR | ||||
10167576 | 26 hrs ago | 0.03224327 GLMR | ||||
10167576 | 26 hrs ago | 0.03224327 GLMR | ||||
10167576 | 26 hrs ago | 0.06912394 GLMR |
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 messages) * to a chain and address of their choice. */ /** * @notice VaaKey identifies a wormhole message * * @custom:member chainId Wormhole chain ID of the chain where this VAA was emitted from * @custom:member emitterAddress Address of the emitter of the VAA, in Wormhole bytes32 format * @custom:member sequence Sequence number of the VAA */ struct VaaKey { uint16 chainId; bytes32 emitterAddress; uint64 sequence; } // 0-127 are reserved for standardized KeyTypes, 128-255 are for custom use uint8 constant VAA_KEY_TYPE = 1; struct MessageKey { uint8 keyType; // 0-127 are reserved for standardized KeyTypes, 128-255 are for custom use bytes encodedKey; } interface IWormholeRelayerBase { event SendEvent( uint64 indexed sequence, LocalNative deliveryQuote, LocalNative paymentForExtraReceiverValue ); function getRegisteredWormholeRelayerContract(uint16 chainId) external view returns (bytes32); /** * @notice Returns true if a delivery has been attempted for the given deliveryHash * Note: invalid deliveries where the tx reverts are not considered attempted */ function deliveryAttempted(bytes32 deliveryHash) external view returns (bool attempted); /** * @notice block number at which a delivery was successfully executed */ function deliverySuccessBlock(bytes32 deliveryHash) external view returns (uint256 blockNumber); /** * @notice block number of the latest attempt to execute a delivery that failed */ function deliveryFailureBlock(bytes32 deliveryHash) external view returns (uint256 blockNumber); } /** * @title IWormholeRelayerSend * @notice The interface to request deliveries */ interface IWormholeRelayerSend is IWormholeRelayerBase { /** * @notice Publishes an instruction for the default delivery provider * to relay a payload to the address `targetAddress` on chain `targetChain` * with gas limit `gasLimit` and `msg.value` equal to `receiverValue` * * `targetAddress` must implement the IWormholeReceiver interface * * This function must be called with `msg.value` equal to `quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit)` * * Any refunds (from leftover gas) will be paid to the delivery provider. In order to receive the refunds, use the `sendPayloadToEvm` function * with `refundChain` and `refundAddress` as parameters * * @param targetChain in Wormhole Chain ID format * @param targetAddress address to call on targetChain (that implements IWormholeReceiver) * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress` * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units) * @param gasLimit gas limit with which to call `targetAddress`. * @return sequence sequence number of published VAA containing delivery instructions */ function sendPayloadToEvm( uint16 targetChain, address targetAddress, bytes memory payload, 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 external messages specified by `messageKeys` to the address `targetAddress` on chain `targetChain` * with gas limit `gasLimit` and `msg.value` equal to * receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei. * * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain` * `targetAddress` must implement the IWormholeReceiver interface * * This function must be called with `msg.value` equal to * quoteEVMDeliveryPrice(targetChain, receiverValue, gasLimit, deliveryProviderAddress) + paymentForExtraReceiverValue * * Note: MessageKeys can specify wormhole messages (VaaKeys) or other types of messages (ex. USDC CCTP attestations). Ensure the selected * DeliveryProvider supports all the MessageKey.keyType values specified or it will not be delivered! * * @param targetChain in Wormhole Chain ID format * @param targetAddress address to call on targetChain (that implements IWormholeReceiver) * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress` * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units) * @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue * (in addition to the `receiverValue` specified) * @param gasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the * `targetChainRefundPerGasUnused` rate quoted by the delivery provider * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format * @param refundAddress The address on `refundChain` to deliver any refund to * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider * @param messageKeys Additional messagess to pass in as parameter in call to `targetAddress` * @param consistencyLevel Consistency level with which to publish the delivery instructions - see * https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels * @return sequence sequence number of published VAA containing delivery instructions */ function sendToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, Gas gasLimit, uint16 refundChain, address refundAddress, address deliveryProviderAddress, MessageKey[] memory messageKeys, uint8 consistencyLevel ) external payable returns (uint64 sequence); /** * @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress` * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain` * with `msg.value` equal to * receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei. * * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain` * `targetAddress` must implement the IWormholeReceiver interface * * This function must be called with `msg.value` equal to * quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress) + paymentForExtraReceiverValue * * @param targetChain in Wormhole Chain ID format * @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress` * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units) * @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue * (in addition to the `receiverValue` specified) * @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing * e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress` * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format * @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider * @param vaaKeys Additional VAAs to pass in as parameter in call to `targetAddress` * @param consistencyLevel Consistency level with which to publish the delivery instructions - see * https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels * @return sequence sequence number of published VAA containing delivery instructions */ function send( uint16 targetChain, bytes32 targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) external payable returns (uint64 sequence); /** * @notice Publishes an instruction for the delivery provider at `deliveryProviderAddress` * to relay a payload and VAAs specified by `vaaKeys` to the address `targetAddress` on chain `targetChain` * with `msg.value` equal to * receiverValue + (arbitrary amount that is paid for by paymentForExtraReceiverValue of this chain's wei) in targetChain wei. * * Any refunds (from leftover gas) will be sent to `refundAddress` on chain `refundChain` * `targetAddress` must implement the IWormholeReceiver interface * * This function must be called with `msg.value` equal to * quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress) + paymentForExtraReceiverValue * * Note: MessageKeys can specify wormhole messages (VaaKeys) or other types of messages (ex. USDC CCTP attestations). Ensure the selected * DeliveryProvider supports all the MessageKey.keyType values specified or it will not be delivered! * * @param targetChain in Wormhole Chain ID format * @param targetAddress address to call on targetChain (that implements IWormholeReceiver), in Wormhole bytes32 format * @param payload arbitrary bytes to pass in as parameter in call to `targetAddress` * @param receiverValue msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units) * @param paymentForExtraReceiverValue amount (in current chain currency units) to spend on extra receiverValue * (in addition to the `receiverValue` specified) * @param encodedExecutionParameters encoded information on how to execute delivery that may impact pricing * e.g. for version EVM_V1, this is a struct that encodes the `gasLimit` with which to call `targetAddress` * @param refundChain The chain to deliver any refund to, in Wormhole Chain ID format * @param refundAddress The address on `refundChain` to deliver any refund to, in Wormhole bytes32 format * @param deliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider * @param messageKeys Additional messagess to pass in as parameter in call to `targetAddress` * @param consistencyLevel Consistency level with which to publish the delivery instructions - see * https://book.wormhole.com/wormhole/3_coreLayerContracts.html?highlight=consistency#consistency-levels * @return sequence sequence number of published VAA containing delivery instructions */ function send( uint16 targetChain, bytes32 targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, MessageKey[] memory messageKeys, uint8 consistencyLevel ) external payable returns (uint64 sequence); /** * @notice Requests a previously published delivery instruction to be redelivered * (e.g. with a different delivery provider) * * This function must be called with `msg.value` equal to * quoteEVMDeliveryPrice(targetChain, newReceiverValue, newGasLimit, newDeliveryProviderAddress) * * @notice *** This will only be able to succeed if the following is true ** * - newGasLimit >= gas limit of the old instruction * - newReceiverValue >= receiver value of the old instruction * - newDeliveryProvider's `targetChainRefundPerGasUnused` >= old relay provider's `targetChainRefundPerGasUnused` * * @param deliveryVaaKey VaaKey identifying the wormhole message containing the * previously published delivery instructions * @param targetChain The target chain that the original delivery targeted. Must match targetChain from original delivery instructions * @param newReceiverValue new msg.value that delivery provider should pass in for call to `targetAddress` (in targetChain currency units) * @param newGasLimit gas limit with which to call `targetAddress`. Any units of gas unused will be refunded according to the * `targetChainRefundPerGasUnused` rate quoted by the delivery provider, to the refund chain and address specified in the original request * @param newDeliveryProviderAddress The address of the desired delivery provider's implementation of IDeliveryProvider * @return sequence sequence number of published VAA containing redelivery instructions * * @notice *** This will only be able to succeed if the following is true ** * - newGasLimit >= gas limit of the old instruction * - newReceiverValue >= receiver value of the old instruction */ 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. * Note: This value can be overridden by the delivery provider on the target chain. The returned value here should be considered to be a * promise by the delivery provider of the amount of refund per gas unused that will be returned to the refundAddress at the target chain. * If a delivery provider decides to override, this will be visible as part of the emitted Delivery event on the target chain. */ 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 * Note: This value can be overridden by the delivery provider on the target chain. The returned value here should be considered to be a * promise by the delivery provider of the amount of refund per gas unused that will be returned to the refundAddress at the target chain. * If a delivery provider decides to override, this will be visible as part of the emitted Delivery event on the target chain. */ 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 } enum RefundStatus { REFUND_SENT, REFUND_FAIL, CROSS_CHAIN_REFUND_SENT, CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED, CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH, NO_REFUND_REQUESTED } /** * @custom:member recipientContract - The target contract address * @custom:member sourceChain - The chain which this delivery was requested from (in wormhole * ChainID format) * @custom:member sequence - The wormhole sequence number of the delivery VAA on the source chain * corresponding to this delivery request * @custom:member deliveryVaaHash - The hash of the delivery VAA corresponding to this delivery * request * @custom:member gasUsed - The amount of gas that was used to call your target contract * @custom:member status: * - RECEIVER_FAILURE, if the target contract reverts * - SUCCESS, if the target contract doesn't revert * @custom:member additionalStatusInfo: * - If status is SUCCESS, then this is empty. * - If status is RECEIVER_FAILURE, this is `RETURNDATA_TRUNCATION_THRESHOLD` bytes of the * return data (i.e. potentially truncated revert reason information). * @custom:member refundStatus - Result of the refund. REFUND_SUCCESS or REFUND_FAIL are for * refunds where targetChain=refundChain; the others are for targetChain!=refundChain, * where a cross chain refund is necessary, or if the default code path is used where no refund is requested (NO_REFUND_REQUESTED) * @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(); error DeliveryProviderDoesNotSupportMessageKeyType(uint8 keyType); //When calling `delivery()` a second time even though a delivery is already in progress error ReentrantDelivery(address msgSender, address lockedBy); error InvalidPayloadId(uint8 parsed, uint8 expected); error InvalidPayloadLength(uint256 received, uint256 expected); error InvalidVaaKeyType(uint8 parsed); error TooManyMessageKeys(uint256 numMessageKeys); error InvalidDeliveryVaa(string reason); //When the delivery VAA (signed wormhole message with delivery instructions) was not emitted by the // registered WormholeRelayer contract error InvalidEmitter(bytes32 emitter, bytes32 registered, uint16 chainId); error MessageKeysLengthDoesNotMatchMessagesLength(uint256 keys, uint256 vaas); error VaaKeysDoNotMatchVaas(uint8 index); //When someone tries to call an external function of the WormholeRelayer that is only intended to be // called by the WormholeRelayer itself (to allow retroactive reverts for atomicity) error RequesterNotWormholeRelayer(); //When trying to relay a `DeliveryInstruction` to any other chain but the one it was specified for error TargetChainIsNotThisChain(uint16 targetChain); //When a `DeliveryOverride` contains a gas limit that's less than the original error InvalidOverrideGasLimit(); //When a `DeliveryOverride` contains a receiver value that's less than the original error InvalidOverrideReceiverValue(); //When a `DeliveryOverride` contains a 'refund per unit of gas unused' that's less than the original error InvalidOverrideRefundPerGasUnused(); //When the delivery provider doesn't pass in sufficient funds (i.e. msg.value does not cover the // maximum possible refund to the user) error InsufficientRelayerFunds(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("DefaultRelayProviderState") - 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("RegisteredCoreRelayersState") - 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 } } // Replay Protection and Indexing struct DeliverySuccessState { mapping(bytes32 => uint256) deliverySuccessBlock; } struct DeliveryFailureState { mapping(bytes32 => uint256) deliveryFailureBlock; } //keccak256("DeliverySuccessState") - 1 bytes32 constant DELIVERY_SUCCESS_STATE_STORAGE_SLOT = 0x1b988580e74603c035f5a7f71f2ae4647578af97cd0657db620836b9955fd8f5; //keccak256("DeliveryFailureState") - 1 bytes32 constant DELIVERY_FAILURE_STATE_STORAGE_SLOT = 0x6c615753402911c4de18a758def0565f37c41834d6eff72b16cb37cfb697f2a5; function getDeliverySuccessState() pure returns (DeliverySuccessState storage state) { assembly ("memory-safe") { state.slot := DELIVERY_SUCCESS_STATE_STORAGE_SLOT } } function getDeliveryFailureState() pure returns (DeliveryFailureState storage state) { assembly ("memory-safe") { state.slot := DELIVERY_FAILURE_STATE_STORAGE_SLOT } } struct ReentrancyGuardState { // if 0 address, no reentrancy guard is active // otherwise, the address of the contract that has locked the reentrancy guard (msg.sender) address lockedBy; } //keccak256("ReentrancyGuardState") - 1 bytes32 constant REENTRANCY_GUARD_STORAGE_SLOT = 0x44dc27ebd67a87ad2af1d98fc4a5f971d9492fe12498e4c413ab5a05b7807a67; function getReentrancyGuardState() pure returns (ReentrancyGuardState storage state) { assembly ("memory-safe") { state.slot := REENTRANCY_GUARD_STORAGE_SLOT } } struct DeliveryTmpState { // the refund chain for the in-progress delivery uint16 refundChain; // the refund address for the in-progress delivery bytes32 refundAddress; } //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 "../../relayer/libraries/Utils.sol"; import {BytesParsing} from "../../relayer/libraries/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, DeliveryProviderDoesNotSupportMessageKeyType, InvalidMsgValue, DeliveryProviderCannotReceivePayment, MessageKey, VaaKey, IWormholeRelayerSend } from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import {IDeliveryProvider} from "../../interfaces/relayer/IDeliveryProviderTyped.sol"; import {toWormholeFormat, fromWormholeFormat} from "../../relayer/libraries/Utils.sol"; import { DeliveryInstruction, RedeliveryInstruction } from "../../relayer/libraries/RelayerInternalStructs.sol"; import {WormholeRelayerSerde} from "./WormholeRelayerSerde.sol"; import {getDefaultDeliveryProviderState} from "./WormholeRelayerStorage.sol"; import {WormholeRelayerBase} from "./WormholeRelayerBase.sol"; import "../../interfaces/relayer/TypedUnits.sol"; import "../../relayer/libraries/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, address(0x0), 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, address(0x0), 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 sendToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, Gas gasLimit, uint16 refundChain, address refundAddress, address deliveryProviderAddress, MessageKey[] memory messageKeys, uint8 consistencyLevel ) public payable returns (uint64 sequence) { sequence = send( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, paymentForExtraReceiverValue, encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), refundChain, toWormholeFormat(refundAddress), deliveryProviderAddress, messageKeys, 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, WormholeRelayerSerde.vaaKeyArrayToMessageKeyArray(vaaKeys), consistencyLevel ) ); } function send( uint16 targetChain, bytes32 targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative paymentForExtraReceiverValue, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, MessageKey[] memory messageKeys, uint8 consistencyLevel ) public payable returns (uint64 sequence) { sequence = send( Send( targetChain, targetAddress, payload, receiverValue, paymentForExtraReceiverValue, encodedExecutionParameters, refundChain, refundAddress, deliveryProviderAddress, messageKeys, consistencyLevel ) ); } /* * Non overload logic */ struct Send { uint16 targetChain; bytes32 targetAddress; bytes payload; TargetNative receiverValue; LocalNative paymentForExtraReceiverValue; bytes encodedExecutionParameters; uint16 refundChain; bytes32 refundAddress; address deliveryProviderAddress; MessageKey[] messageKeys; 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); checkKeyTypesSupported(provider, sendParams.messageKeys); // 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), messageKeys: sendParams.messageKeys }).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 checkKeyTypesSupported( IDeliveryProvider provider, MessageKey[] memory messageKeys ) internal view { uint256 len = messageKeys.length; if (len == 0) { return; } uint256 supportedKeyTypes = provider.getSupportedKeys(); for (uint256 i = 0; i < len;) { uint8 keyType = messageKeys[i].keyType; if ((supportedKeyTypes & (1 << keyType)) == 0) { revert DeliveryProviderDoesNotSupportMessageKeyType(keyType); } unchecked { ++i; } } } 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; } 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 ); } // Forwards function forwardPayloadToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit ) external payable { forward( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, LocalNative.wrap(0), encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), getCurrentRefundChain(), getCurrentRefundAddress(), getDefaultDeliveryProvider(), new VaaKey[](0), CONSISTENCY_LEVEL_FINALIZED ); } function forwardVaasToEvm( uint16 targetChain, address targetAddress, bytes memory payload, TargetNative receiverValue, Gas gasLimit, VaaKey[] memory vaaKeys ) external payable { forward( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, LocalNative.wrap(0), encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), getCurrentRefundChain(), getCurrentRefundAddress(), getDefaultDeliveryProvider(), 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 { forward( targetChain, toWormholeFormat(targetAddress), payload, receiverValue, paymentForExtraReceiverValue, encodeEvmExecutionParamsV1(EvmExecutionParamsV1(gasLimit)), refundChain, toWormholeFormat(refundAddress), deliveryProviderAddress, vaaKeys, consistencyLevel ); } function forward( uint16 targetChain, bytes32 targetAddress, bytes memory payload, TargetNative receiverValue, LocalNative, bytes memory encodedExecutionParameters, uint16 refundChain, bytes32 refundAddress, address deliveryProviderAddress, VaaKey[] memory vaaKeys, uint8 consistencyLevel ) public payable { (LocalNative cost,) = quoteDeliveryPrice(targetChain, receiverValue, encodedExecutionParameters, deliveryProviderAddress); send( targetChain, targetAddress, payload, receiverValue, LocalNative.wrap(msg.value) - cost, // include the extra value that is passed in encodedExecutionParameters, refundChain, refundAddress, deliveryProviderAddress, vaaKeys, consistencyLevel ); } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.19; import {IWormhole} from "../../interfaces/IWormhole.sol"; import { InvalidDeliveryVaa, InvalidEmitter, InsufficientRelayerFunds, TargetChainIsNotThisChain, MessageKeysLengthDoesNotMatchMessagesLength, VaaKeysDoNotMatchVaas, InvalidOverrideGasLimit, InvalidOverrideReceiverValue, RequesterNotWormholeRelayer, DeliveryProviderCannotReceivePayment, MessageKey, VAA_KEY_TYPE, 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, pay, min, toWormholeFormat, fromWormholeFormat, returnLengthBoundedCall, returnLengthBoundedCall} from "../../relayer/libraries/Utils.sol"; import { DeliveryInstruction, DeliveryOverride, EvmDeliveryInstruction } from "../../relayer/libraries/RelayerInternalStructs.sol"; import {BytesParsing} from "../../relayer/libraries/BytesParsing.sol"; import {WormholeRelayerSerde} from "./WormholeRelayerSerde.sol"; import { DeliverySuccessState, DeliveryFailureState, getDeliverySuccessState, getDeliveryFailureState } from "./WormholeRelayerStorage.sol"; import {WormholeRelayerBase} from "./WormholeRelayerBase.sol"; import "../../interfaces/relayer/TypedUnits.sol"; import "../../relayer/libraries/ExecutionParameters.sol"; uint256 constant QUOTE_LENGTH_BYTES = 32; uint256 constant GAS_LIMIT_EXTERNAL_CALL = 100_000; 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 nonReentrant { // 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(); // Record information about the delivery's refund in temporary storage recordRefundInformation( 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 checkMessageKeysWithMessages(instruction.messageKeys, encodedVMs); executeDelivery(deliveryVaaInfo); // Clear temporary storage of refund information clearRefundInformation(); } // ------------------------------------------- PRIVATE ------------------------------------------- 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.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 * - 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 (vaaInfo.deliveryInstruction.targetAddress == 0x0) { handleCrossChainRefund(vaaInfo); return; } DeliveryResults memory results; // Check replay protection - if so, set status to receiver failure if(getDeliverySuccessState().deliverySuccessBlock[vaaInfo.deliveryVaaHash] != 0) { results = DeliveryResults( Gas.wrap(0), DeliveryStatus.RECEIVER_FAILURE, bytes("Delivery already performed") ); } else { results = 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 }) ); setDeliveryBlock(results.status, vaaInfo.deliveryVaaHash); } RefundStatus refundStatus = payRefunds( vaaInfo.deliveryInstruction, vaaInfo.relayerRefundAddress, (vaaInfo.gasLimit - results.gasUsed).toWei(vaaInfo.targetChainRefundPerGasUnused).asLocalNative(), results.status ); emitDeliveryEvent(vaaInfo, results, refundStatus); } function executeInstruction(EvmDeliveryInstruction memory evmInstruction) internal returns (DeliveryResults memory results) { 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, results.additionalStatusInfo) = returnLengthBoundedCall( deliveryTarget, callData, gasLimit.unwrap(), evmInstruction.totalReceiverValue.unwrap(), RETURNDATA_TRUNCATION_THRESHOLD ); Gas postGas = Gas.wrap(gasleft()); unchecked { results.gasUsed = (preGas - postGas).min(gasLimit); } } if (success) { results.additionalStatusInfo = new bytes(0); results.status = DeliveryStatus.SUCCESS; } else { // Call to 'receiveWormholeMessages' on targetAddress reverted results.status = DeliveryStatus.RECEIVER_FAILURE; } } function handleCrossChainRefund(DeliveryVAAInfo memory vaaInfo) internal { RefundStatus refundStatus = payRefunds( vaaInfo.deliveryInstruction, vaaInfo.relayerRefundAddress, LocalNative.wrap(0), DeliveryStatus.RECEIVER_FAILURE ); emitDeliveryEvent( vaaInfo, DeliveryResults( Gas.wrap(0), DeliveryStatus.SUCCESS, bytes("") ), refundStatus ); } function emitDeliveryEvent(DeliveryVAAInfo memory vaaInfo, DeliveryResults memory results, RefundStatus refundStatus) private { emit Delivery( fromWormholeFormat(vaaInfo.deliveryInstruction.targetAddress), vaaInfo.sourceChain, vaaInfo.sourceSequence, vaaInfo.deliveryVaaHash, results.status, results.gasUsed, refundStatus, results.additionalStatusInfo, (vaaInfo.redeliveryHash != 0) ? vaaInfo.encodedOverrides : new bytes(0) ); } 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.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 + transactionFeeRefundAmount; //Refund the user refundStatus = deliveryInstruction.refundAddress == bytes32(0x0) ? RefundStatus.NO_REFUND_REQUESTED : 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 deliveryProvider ) private returns (RefundStatus) { // User requested refund on this chain if (refundChain == getChainId()) { return pay(payable(fromWormholeFormat(refundAddress)), refundAmount, GAS_LIMIT_EXTERNAL_CALL) ? RefundStatus.REFUND_SENT : RefundStatus.REFUND_FAIL; } // User requested refund on a different chain // Determine price of an 'empty' delivery // (Note: assumes refund chain is an EVM chain) (bool success, LocalNative baseDeliveryPrice) = untrustedBaseDeliveryPrice(fromWormholeFormat(deliveryProvider), refundChain); // If the unstrusted call failed, or the refundAmount is not greater than the 'empty delivery price', then the refund does not go through // Note: We first check 'refundAmount <= baseDeliveryPrice', in case an untrusted delivery provider returns a value that overflows once // the wormhole message fee is added to it unchecked { if (!success || (refundAmount <= baseDeliveryPrice) || (refundAmount <= getWormholeMessageFee() + baseDeliveryPrice)) { return RefundStatus.CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH; } } return sendCrossChainRefund(refundChain, refundAddress, refundAmount, refundAmount - getWormholeMessageFee() - baseDeliveryPrice, deliveryProvider); } function untrustedBaseDeliveryPrice(address deliveryProvider, uint16 refundChain) internal returns (bool success, LocalNative baseDeliveryPrice) { (bool externalCallSuccess, bytes memory returnData) = returnLengthBoundedCall( deliveryProvider, abi.encodeCall(IDeliveryProvider.quoteDeliveryPrice, (refundChain, TargetNative.wrap(0), encodeEvmExecutionParamsV1(getEmptyEvmExecutionParamsV1()))), GAS_LIMIT_EXTERNAL_CALL, QUOTE_LENGTH_BYTES ); if(externalCallSuccess && returnData.length == QUOTE_LENGTH_BYTES) { baseDeliveryPrice = abi.decode(returnData, (LocalNative)); success = true; } else { success = false; } } function sendCrossChainRefund(uint16 refundChain, bytes32 refundAddress, LocalNative sendAmount, LocalNative receiveAmount, bytes32 deliveryProvider) internal returns (RefundStatus status) { // Request a 'send' with 'paymentForExtraReceiverValue' equal to the refund minus the 'empty delivery price' // We limit the gas because we are within a delivery, so thus the trust assumptions on the delivery provider are different // Normally, in 'send', a revert is no problem; but here, we want to prevent such reverts in this try-catch try IWormholeRelayerSend(address(this)).send{value: sendAmount.unwrap(), gas: GAS_LIMIT_EXTERNAL_CALL}( refundChain, bytes32(0), bytes(""), TargetNative.wrap(0), receiveAmount, encodeEvmExecutionParamsV1(getEmptyEvmExecutionParamsV1()), refundChain, refundAddress, fromWormholeFormat(deliveryProvider), 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 checkMessageKeysWithMessages( MessageKey[] memory messageKeys, bytes[] memory signedMessages ) private view { if (messageKeys.length != signedMessages.length) { revert MessageKeysLengthDoesNotMatchMessagesLength(messageKeys.length, signedMessages.length); } uint256 len = messageKeys.length; for (uint256 i = 0; i < len;) { if (messageKeys[i].keyType == VAA_KEY_TYPE) { IWormhole.VM memory parsedVaa = getWormhole().parseVM(signedMessages[i]); (VaaKey memory vaaKey,) = WormholeRelayerSerde.decodeVaaKey(messageKeys[i].encodedKey, 0); if ( vaaKey.chainId != parsedVaa.emitterChainId || vaaKey.emitterAddress != parsedVaa.emitterAddress || vaaKey.sequence != parsedVaa.sequence ) { revert VaaKeysDoNotMatchVaas(uint8(i)); } } unchecked { ++i; } } } // Ensures current block number is set to implement replay protection and for indexing purposes function setDeliveryBlock(DeliveryStatus status, bytes32 deliveryHash) private { if (status == DeliveryStatus.SUCCESS) { getDeliverySuccessState().deliverySuccessBlock[deliveryHash] = block.number; // Clear out failure block if it exists from previous delivery failure delete getDeliveryFailureState().deliveryFailureBlock[deliveryHash]; } else { getDeliveryFailureState().deliveryFailureBlock[deliveryHash] = block.number; } } }
// 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 "../../relayer/libraries/Utils.sol"; import { ReentrantDelivery, DeliveryProviderDoesNotSupportTargetChain, VaaKey, InvalidMsgValue, IWormholeRelayerBase } from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import {DeliveryInstruction} from "../../relayer/libraries/RelayerInternalStructs.sol"; import { DeliveryTmpState, getDeliveryTmpState, getDeliverySuccessState, getDeliveryFailureState, getRegisteredWormholeRelayersState, getReentrancyGuardState } 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]; } function deliveryAttempted(bytes32 deliveryHash) public view returns (bool attempted) { return getDeliverySuccessState().deliverySuccessBlock[deliveryHash] != 0 || getDeliveryFailureState().deliveryFailureBlock[deliveryHash] != 0; } function deliverySuccessBlock(bytes32 deliveryHash) public view returns (uint256 blockNumber) { return getDeliverySuccessState().deliverySuccessBlock[deliveryHash]; } function deliveryFailureBlock(bytes32 deliveryHash) public view returns (uint256 blockNumber) { return getDeliveryFailureState().deliveryFailureBlock[deliveryHash]; } //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); } modifier nonReentrant() { // Reentrancy guard if (getReentrancyGuardState().lockedBy != address(0)) { revert ReentrantDelivery(msg.sender, getReentrancyGuardState().lockedBy); } getReentrancyGuardState().lockedBy = msg.sender; _; getReentrancyGuardState().lockedBy = address(0); } // ----------------------- delivery transaction temorary storage functions ----------------------- function recordRefundInformation(uint16 refundChain, bytes32 refundAddress) internal { DeliveryTmpState storage state = getDeliveryTmpState(); state.refundChain = refundChain; state.refundAddress = refundAddress; } function clearRefundInformation() internal { DeliveryTmpState storage state = getDeliveryTmpState(); state.refundChain = 0; state.refundAddress = bytes32(0); } function getCurrentRefundChain() internal view returns (uint16) { return getDeliveryTmpState().refundChain; } function getCurrentRefundAddress() internal view returns (bytes32) { return getDeliveryTmpState().refundAddress; } }
// 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 pay(address payable receiver, LocalNative amount, uint256 gasBound) 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), gasBound, 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; /** * Overload with no 'value' and non-payable address */ function returnLengthBoundedCall( address callee, bytes memory callData, uint256 gasLimit, uint256 dataLengthBound ) returns (bool success, bytes memory returnedData) { return returnLengthBoundedCall(payable(callee), callData, gasLimit, 0, dataLengthBound); } /** * 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 { /** * @notice This function returns * * 1) nativePriceQuote: the price of a delivery (by this delivery provider) to chain * 'targetChain', giving the user's contract 'receiverValue' target chain wei and performing the * relay with the execution parameters (e.g. the gas limit) specified in 'encodedExecutionParameters' * * 2) encodedExecutionInfo: information relating to how this delivery provider * will perform such a delivery (e.g. the gas limit, and the amount it will refund per gas unused) * * encodedExecutionParameters and encodedExecutionInfo both are encodings of versioned structs - * version EVM_V1 of ExecutionParameters specifies the gas limit, * and version EVM_V1 of ExecutionInfo specifies the gas limit and the amount that this delivery provider * will refund per unit of gas unused */ function quoteDeliveryPrice( uint16 targetChain, TargetNative receiverValue, bytes memory encodedExecutionParams ) external view returns (LocalNative nativePriceQuote, bytes memory encodedExecutionInfo); /** * @notice This function returns the amount of extra 'receiverValue' (msg.value on the target chain) * that will be sent to your contract, if you specify 'currentChainAmount' in the * 'paymentForExtraReceiverValue' field on 'send' */ 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 This function determines whether a relay provider supports the given keyType. * * Note: 0-127 are reserved for standardized keyTypes and 128-255 are allowed to be custom per DeliveryProvider * Practically this means that 0-127 must mean the same thing for all DeliveryProviders, * while x within 128-255 may have different meanings between DeliveryProviders * (e.g. 130 for provider A means pyth price quotes while 130 for provider B means tweets, * but 8 must mean the same for both) * * @param keyType - The keyType within MessageKey that specifies what the encodedKey within a MessageKey means */ function isMessageKeyTypeSupported(uint8 keyType) external view returns (bool supported); /** * @notice This function returns a bitmap encoding all the keyTypes this provider supports * * Note: 0-127 are reserved for standardized keyTypes and 128-255 are allowed to be custom per DeliveryProvider * Practically this means that 0-127 must mean the same thing for all DeliveryProviders, * while x within 128-255 may have different meanings between DeliveryProviders * (e.g. 130 for provider A means pyth price quotes while 130 for provider B means tweets, * but 8 must mean the same for both) */ function getSupportedKeys() external view returns (uint256 bitmap); /** * @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; MessageKey[] messageKeys; } // 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, TooManyMessageKeys, MessageKey, VAA_KEY_TYPE, VaaKey } from "../../interfaces/relayer/IWormholeRelayerTyped.sol"; import { DeliveryOverride, DeliveryInstruction, RedeliveryInstruction } from "../../relayer/libraries/RelayerInternalStructs.sol"; import {BytesParsing} from "../../relayer/libraries/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; uint256 constant VAA_KEY_TYPE_LENGTH = 2 + 32 + 8; // ---------------------- "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, encodeMessageKeyArray(strct.messageKeys) ); } 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.messageKeys, offset) = decodeMessageKeyArray(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 = abi.encodePacked(VAA_KEY_TYPE, 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; offset = checkUint8(encoded, offset, VAA_KEY_TYPE); (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); } function vaaKeyArrayToMessageKeyArray(VaaKey[] memory vaaKeys) internal pure returns (MessageKey[] memory msgKeys) { msgKeys = new MessageKey[](vaaKeys.length); uint256 len = vaaKeys.length; for (uint256 i = 0; i < len;) { msgKeys[i] = MessageKey(VAA_KEY_TYPE, encodeVaaKey(vaaKeys[i])); unchecked { ++i; } } } function encodeMessageKey( MessageKey memory msgKey ) internal pure returns (bytes memory encoded) { if (msgKey.keyType == VAA_KEY_TYPE) { // known length encoded = abi.encodePacked(msgKey.keyType, msgKey.encodedKey); } else { encoded = abi.encodePacked(msgKey.keyType, encodeBytes(msgKey.encodedKey)); } } function decodeMessageKey( bytes memory encoded, uint256 startOffset ) internal pure returns (MessageKey memory msgKey, uint256 offset) { (msgKey.keyType, offset) = encoded.asUint8Unchecked(startOffset); if (msgKey.keyType == VAA_KEY_TYPE) { (msgKey.encodedKey, offset) = encoded.sliceUnchecked(offset, VAA_KEY_TYPE_LENGTH); } else { (msgKey.encodedKey, offset) = decodeBytes(encoded, offset); } } function encodeVaaKey(VaaKey memory vaaKey) internal pure returns (bytes memory encoded) { encoded = abi.encodePacked(vaaKey.chainId, vaaKey.emitterAddress, vaaKey.sequence); } function decodeVaaKey( bytes memory encoded, uint256 startOffset ) internal pure returns (VaaKey memory vaaKey, uint256 offset) { offset = startOffset; (vaaKey.chainId, offset) = encoded.asUint16Unchecked(offset); (vaaKey.emitterAddress, offset) = encoded.asBytes32Unchecked(offset); (vaaKey.sequence, offset) = encoded.asUint64Unchecked(offset); } function encodeMessageKeyArray(MessageKey[] memory msgKeys) internal pure returns (bytes memory encoded) { uint256 len = msgKeys.length; if (len > type(uint8).max) { revert TooManyMessageKeys(len); } encoded = abi.encodePacked(uint8(msgKeys.length)); for (uint256 i = 0; i < len;) { encoded = abi.encodePacked(encoded, encodeMessageKey(msgKeys[i])); unchecked { ++i; } } } function decodeMessageKeyArray( bytes memory encoded, uint256 startOffset ) internal pure returns (MessageKey[] memory msgKeys, uint256 offset) { uint8 msgKeysLength; (msgKeysLength, offset) = encoded.asUint8Unchecked(startOffset); msgKeys = new MessageKey[](msgKeysLength); for (uint256 i = 0; i < msgKeysLength;) { (msgKeys[i], offset) = decodeMessageKey(encoded, offset); unchecked { ++i; } } } // ------------------------------------------ private -------------------------------------------- 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 "../../relayer/libraries/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` 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/", "forge-std/=lib/forge-std/src/", "truffle/=node_modules/truffle/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "openzeppelin/=lib/openzeppelin-contracts/contracts/" ], "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":"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":"uint8","name":"keyType","type":"uint8"}],"name":"DeliveryProviderDoesNotSupportMessageKeyType","type":"error"},{"inputs":[{"internalType":"address","name":"relayer","type":"address"},{"internalType":"uint16","name":"chainId","type":"uint16"}],"name":"DeliveryProviderDoesNotSupportTargetChain","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":[{"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":[{"internalType":"uint256","name":"keys","type":"uint256"},{"internalType":"uint256","name":"vaas","type":"uint256"}],"name":"MessageKeysLengthDoesNotMatchMessagesLength","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":[{"internalType":"uint16","name":"targetChain","type":"uint16"}],"name":"TargetChainIsNotThisChain","type":"error"},{"inputs":[{"internalType":"uint256","name":"numMessageKeys","type":"uint256"}],"name":"TooManyMessageKeys","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":"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":[{"internalType":"bytes32","name":"deliveryHash","type":"bytes32"}],"name":"deliveryAttempted","outputs":[{"internalType":"bool","name":"attempted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"deliveryHash","type":"bytes32"}],"name":"deliveryFailureBlock","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"deliveryHash","type":"bytes32"}],"name":"deliverySuccessBlock","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"stateMutability":"view","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":"","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":"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":"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":"uint8","name":"keyType","type":"uint8"},{"internalType":"bytes","name":"encodedKey","type":"bytes"}],"internalType":"struct MessageKey[]","name":"messageKeys","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":"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":"uint8","name":"keyType","type":"uint8"},{"internalType":"bytes","name":"encodedKey","type":"bytes"}],"internalType":"struct MessageKey[]","name":"messageKeys","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
60c080604052346200013f57602081620046d5803803809162000023828562000144565b8339810103126200013f57516001600160a01b038116908190036200013f5760208160049260805260405192838092634d4502c960e11b82525afa9081156200013357600091620000e9575b5060a05260405161455690816200017f82396080518181816106a401528181610aad015281816112040152818161162a0152818161198601528181612d750152818161300f01528181613a19015261400a015260a0518181816109ad0152818161123d0152818161166a015281816119c70152612b860152f35b6020813d82116200012a575b81620001046020938362000144565b810103126200012657519061ffff82168203620001235750386200006f565b80fd5b5080fd5b3d9150620000f5565b6040513d6000823e3d90fd5b600080fd5b601f909101601f19168101906001600160401b038211908210176200016857604052565b634e487b7160e01b600052604160045260246000fdfe60a080604052600436101561001357600080fd5b60003560e01c9081632385904a14611a885750806324320c9f14611a5257806328b1d852146118935780632c75470f14611877578063329a2be71461184557806332b2fc0e146117ab5780633a2c767d1461176c5780633e8267e7146117125780633ed334df146114f957806340984f08146114c05780634533e5ff1461143e5780634b5ca6f4146113aa5780634d48ec60146113535780635a3b92e81461131a5780635cb8cae21461100557806375ea8b5814610fd957806380ebabd014610fa55780638b0301b114610f615780638fecdd0214610f1e578063a60eb4c81461055e578063a79629d8146104ef578063b1eac87514610464578063b686d0891461040d578063c055120e14610354578063c23ee3c314610303578063c4d66de8146102a1578063c81fb7fe14610270578063cee4bda0146101c85763d0625a191461015e57600080fd5b346101c35760203660031901126101c3576004356000526000805160206144e1833981519152602052604060002054158015906101a3575b6020906040519015158152f35b506000805160206145018339815191526020526040600020541515610196565b600080fd5b6101603660031901126101c3576101dd611b29565b6001600160401b036044358181116101c3576101fd903690600401611ca6565b9160a4358281116101c357610216903690600401611ca6565b9261021f611b3a565b93610228611bb4565b61012435938585116101c35760209661024861026796369060040161206f565b93610251611e6c565b9560e435936084359160643591602435906135e6565b60405191168152f35b602061029061027e36611f87565b99989098979197969296959395613522565b6001600160401b0360405191168152f35b346101c35760203660031901126101c3576004356001600160a01b038116908190036101c35760016000546102d960ff82161561212c565b60ff19161760005560008051602061448183398151915280546001600160a01b0319169091179055005b346101c35760603660031901126101c3576040610348610321611b29565b600080516020614481833981519152546001600160a01b03169060443590602435906141f4565b82519182526020820152f35b6101603660031901126101c357610369611b29565b610371611b5c565b906001600160401b03906044358281116101c357610393903690600401611ca6565b9261039c611b3a565b916103a5611b72565b906103ae611bb4565b9061012435948686116101c3576020976103cf61026797369060040161206f565b946103d8611e6c565b966103f26040516103e881611bcb565b60a4358152612e02565b9260018060a01b0380931695608435936064359316906135e6565b60e03660031901126101c35761042236611d04565b61042a611b4b565b60a4356001600160401b03918282116101c357602093610451610267933690600401611ca6565b9061045a611b88565b9260843591613dfa565b6104ed61048d61047336611dfb565b9590604094929394519061048682611bcb565b8152612e02565b9061ffff6000805160206144a18339815191525416926000805160206144c1833981519152549460018060a01b0396876000805160206144818339815191525416976104e56104de8a888786614224565b5034612a00565b941690613469565b005b346101c35760803660031901126101c357610508611b29565b604435906001600160401b0382116101c35761052b61053d923690600401611ca6565b610533611b9e565b9160243590614224565b9061055a604051928392835260406020840152604083019061204a565b0390f35b60803660031901126101c3576004356001600160401b0381116101c357366023820112156101c357806004013561059481611ced565b916105a26040519384611c6a565b8183526024602084019260051b820101903682116101c35760248101925b828410610eef57846024356001600160401b0381116101c3576105e7903690600401611ca6565b906001600160a01b0360443581811681036101c3576064356001600160401b0381116101c35761061b903690600401611ca6565b7f44dc27ebd67a87ad2af1d98fc4a5f971d9492fe12498e4c413ab5a05b7807a675483811680610ed157506001600160a01b03191633177f44dc27ebd67a87ad2af1d98fc4a5f971d9492fe12498e4c413ab5a05b7807a675560405163607ec5ef60e11b81526020600482015293946000908590819061069f90602483019061204a565b0381867f0000000000000000000000000000000000000000000000000000000000000000165afa8015610bdb57600094600090600092610ea6575b5015610e815750606084015161ffff1660009081527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d6020526040902054608085015190808203610e5457505060e0840151836040519361073a85611bfc565b6000855260006020860152606060408601526000606086015260006080860152606060a0860152600060c0860152600060e086015260006101008601526000610120860152600061014086015260606101608601526107ba602261079d8561444d565b858101600281015161ffff1689528201516020890152018461442e565b93906040870152838101906107db604080602085015194015196018261442e565b959060a089015260838087840161ffff60028201511660c08c0152602281015160e08c015260428101516101008c015260628101516101208c015260828101516101408c0152015196019261083260ff88166142df565b6000975b60ff81168910610d9d575061085d959697506101608a015260608901526080880152613283565b61ffff60c08501511660e08501519061ffff196000805160206144a18339815191525416176000805160206144a1833981519152556000805160206144c18339815191525561ffff606087015116956101406001600160401b0360a08301511691015190604051976108ce89611c18565b8852602088015260408701521660608501528460808501528160a0850152600060c0850152600060e0850152600061010085015280610120850152600061014085015260009061092160a0840151612410565b61092a816123f0565b80610d64575061093d60a084015161242d565b908051610c50575b50602081519101519161096160608501516080860151906123e3565b906101408701526101008601528160e08601528060c0860152818102918183041490151715610c3a57610100840151610999916123e3565b803410610c1d575061ffff8151168061ffff7f00000000000000000000000000000000000000000000000000000000000000001603610c05575061016001519081518451808203610be757505081519360005b858110610a57576109fc856124c0565b6000805160206144a1833981519152805461ffff1916905560006000805160206144c1833981519152557f44dc27ebd67a87ad2af1d98fc4a5f971d9492fe12498e4c413ab5a05b7807a6780546001600160a01b0319169055005b600160ff610a658387612fdb565b51511614610a76575b6001016109ec565b610aa86000610a858385612fdb565b516040518093819263a9e1189360e01b835260206004840152602483019061204a565b0381877f0000000000000000000000000000000000000000000000000000000000000000165afa908115610bdb57600091610b9a575b506020610aeb8387612fdb565b51015160405191610afb83611c34565b600083526020830192600084526001600160401b03602a60408301946000865261ffff600282015116809452602281015180975201511680935261ffff6060830151161492831593610b8b575b508215610b73575b505015610a6e57604051633ad7858760e21b815260ff9091166004820152602490fd5b60a001516001600160401b0316141590508780610b50565b60808201511415925089610b48565b3d9150816000823e610bac8282611c6a565b60208183810103126101c35780516001600160401b0381116101c357610bd592820191016121dc565b87610ade565b6040513d6000823e3d90fd5b60449250604051916365c1f7cd60e01b835260048301526024820152fd5b6024906040519063d8215fc960e01b82526004820152fd5b60449060405190620885af60e61b82523460048301526024820152fd5b634e487b7160e01b600052601160045260246000fd5b604051919250610c5f82611c34565b60008252610cab602083019160608352600060408501526020610c818261444d565b610c938280838601015192018461442e565b91908652828285010151604088015286520190613283565b606084015160006020604051610cc081611c4f565b8281520152825110610d5257610cd68151612410565b610cdf816123f0565b80610d275750610cef905161242d565b908051928251905111610d15576040919260608501526000608085015201519086610945565b6040516315fc687d60e31b8152600490fd5b80610d336044926123f0565b60ff6040519163170cd96160e11b835260006004840152166024820152fd5b604051631c6e090160e31b8152600490fd5b80610d71610d99926123f0565b60405163c1f4bdd960e01b815260ff9091166004820152600060248201529081906044820190565b0390fd5b9193959796509193610dad6142c5565b96600191828082019189010151908360ff829316808c5214600014610e39575060405190600a90818301916034840190828c01015b818410610e29575050602a808452601f909201601f191660405260208b019290925260ff939291015b98610e168287612fdb565b5201918b97959391509795939197610836565b8051845260209384019301610de2565b60ff93929150610e49908961442e565b9060208b0152610e0b565b6064925061ffff6060870151169060405192633bb6036760e11b8452600484015260248301526044820152fd5b60405163b72c3b7f60e01b815260206004820152908190610d9990602483019061204a565b915050610ec79194503d806000833e610ebf8183611c6a565b810190612383565b90949194876106da565b604490604051906320b84ced60e01b82523360048301526024820152fd5b83356001600160401b0381116101c357602091610f13839260243691870101611ca6565b8152019301926105c0565b6020610290610f2c36611f2f565b9060018060a0969495961b0360008051602061448183398151915254169360405195610f5787611bcb565b60008752846132ae565b60e03660031901126101c3576020610290610f7b36611d04565b610f83611b4b565b610f8b611b88565b91610f9b6040516103e881611bcb565b9160843591613dfa565b346101c35760803660031901126101c3576040610348610fc3611b29565b610fcb611b9e565b9060443590602435906141f4565b6104ed610fe536611f87565b9993955097610fff6104de83878a889c969c9b979b614224565b93613522565b346101c3576020806003193601126101c3576004356001600160401b0381116101c357611036903690600401611ca6565b60018060a01b03917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc9261106d8185541693613005565b828101516e576f726d686f6c6552656c617965728082036112fc57505060ff602182015116600281036112dd5750602381015161ffff908181168015806112d5575b156111f0575b5050506110ce6110c86043830151612a0d565b91613258565b803b15611195571692836bffffffffffffffffffffffff60a01b825416179055600080604051857fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8380a2632c75470f60e01b818501908152600482529061113581611c4f565b519082305af190611144612e20565b91156111735750507f2e4cc16c100f0b55e2df82ab0b1a7e294aa9cbd01b48fbaf622683fbc0507a49600080a3005b610d9960405192839263135687c760e31b84526004840152602483019061204a565b60405162461bcd60e51b815260048101849052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b60405163380e7c8960e21b815286816004817f00000000000000000000000000000000000000000000000000000000000000008a165afa908115610bdb576000916112a0575b5061128e577f00000000000000000000000000000000000000000000000000000000000000009283160361126a57806110b5565b60405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b60405163ea03b6eb60e01b8152600490fd5b90508681813d83116112ce575b6112b78183611c6a565b810103126101c3576112c890612376565b89611236565b503d6112ad565b5060006110af565b60449060405190633460202560e21b8252600482015260026024820152fd5b6044925060405191633d254c6160e01b835260048301526024820152fd5b346101c35760203660031901126101c3576004356000526000805160206145018339815191526020526020604060002054604051908152f35b6104ed61137d61136236611e7d565b9a919894909995506040979297969396519061048682611bcb565b9361138a88868584614224565b506001600160a01b0397881697906113a29034612a00565b941690613522565b60e03660031901126101c3576113be611b29565b6113c6611b5c565b906001600160401b03906044358281116101c3576113e8903690600401611ca6565b60a4359161ffff831683036101c35760209461026793611406611b88565b9160018060a01b036000805160206144818339815191525416936040519561142d87611bcb565b60008752608435926064359261338d565b6104ed61145f61144d36611f2f565b60409591939295519061048682611bcb565b61ffff6000805160206144a18339815191525416916000805160206144c1833981519152549360018060a01b039687600080516020614481833981519152541696604051986114ad8a611bcb565b60008a526104e56104de8a888786614224565b346101c35760203660031901126101c3576004356000526000805160206144e18339815191526020526020604060002054604051908152f35b346101c3576020806003193601126101c3576004356001600160401b0381116101c35761152d611532913690600401611ca6565b613005565b90808201516e576f726d686f6c6552656c617965728082036112fc57505060ff602183015116600181036116f35750602382015161ffff90818116908115806116eb575b15611616575b5050602583015160458401519351604581036115f757501690816000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d91828252604060002054806115d9575060005252604060002055600080f35b6044925060405191637b5672c560e11b835260048301526024820152fd5b6044906040519063061bc83560e51b8252600482015260456024820152fd5b60405163380e7c8960e21b815284816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610bdb576000916116b6575b5061128e577f0000000000000000000000000000000000000000000000000000000000000000918383161461157c5760405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b90508481813d83116116e4575b6116cd8183611c6a565b810103126101c3576116de90612376565b86611663565b503d6116c3565b506001611576565b60449060405190633460202560e21b8252600482015260016024820152fd5b346101c35760203660031901126101c3576020611764611730611b29565b61ffff166000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d60205260406000205490565b604051908152f35b602061029061179661177d36611e7d565b9a90999198979295946040979497519061048682611bcb565b6001600160a01b039586169790951690613522565b6101003660031901126101c3576117c0611b29565b6117c8611b5c565b6001600160401b03906044358281116101c3576117e9903690600401611ca6565b9060a435918383116101c357602094611809610267943690600401611d50565b92611812611b3a565b9061181b611b72565b600080516020614481833981519152546001600160a01b031694909360843592606435929161338d565b602061029061185336611dfb565b949260018060a09493941b03600080516020614481833981519152541694846132ae565b346101c35760003660031901126101c3576104ed30331461212c565b346101c3576020806003193601126101c3576004356001600160401b0381116101c35761152d6118c7913690600401611ca6565b90808201516e576f726d686f6c6552656c617965728082036112fc57505060ff60218301511660038103611a335750602382015161ffff9182821690811580611a2b575b15611972575b505050506043810151906119276110c883612a0d565b6001600160a01b031690811561195a575060008051602061448183398151915280546001600160a01b0319169091179055005b60249060405190637a8ad12560e01b82526004820152fd5b60405163380e7c8960e21b815281816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215610bdb576000926119f5575b505061128e577f00000000000000000000000000000000000000000000000000000000000000009283160361126a578080611911565b90809250813d8311611a24575b611a0c8183611c6a565b810103126101c357611a1d90612376565b85806119bf565b503d611a02565b50600061190b565b60449060405190633460202560e21b8252600482015260036024820152fd5b346101c35760003660031901126101c357600080516020614481833981519152546040516001600160a01b039091168152602090f35b346101c35760603660031901126101c357611aa1611b29565b6044356001600160a01b03811691908290036101c357630cbcf9e160e21b835261ffff1660048301526024803590830152602090829060449082905afa8015610bdb57600090611af7575b602090604051908152f35b506020813d8211611b21575b81611b1060209383611c6a565b810103126101c35760209051611aec565b3d9150611b03565b6004359061ffff821682036101c357565b60c4359061ffff821682036101c357565b6064359061ffff821682036101c357565b602435906001600160a01b03821682036101c357565b60e435906001600160a01b03821682036101c357565b60c435906001600160a01b03821682036101c357565b606435906001600160a01b03821682036101c357565b61010435906001600160a01b03821682036101c357565b602081019081106001600160401b03821117611be657604052565b634e487b7160e01b600052604160045260246000fd5b61018081019081106001600160401b03821117611be657604052565b61016081019081106001600160401b03821117611be657604052565b606081019081106001600160401b03821117611be657604052565b604081019081106001600160401b03821117611be657604052565b90601f801991011681019081106001600160401b03821117611be657604052565b6001600160401b038111611be657601f01601f191660200190565b81601f820112156101c357803590611cbd82611c8b565b92611ccb6040519485611c6a565b828452602083830101116101c357816000926020809301838601378301015290565b6001600160401b038111611be65760051b60200190565b60609060031901126101c35760405190611d1d82611c34565b8160043561ffff811681036101c35781526024356020820152604435906001600160401b03821682036101c35760400152565b81601f820112156101c357803590611d6782611ced565b92604090611d7782519586611c6a565b83855260209182860191836060809702860101948186116101c3578401925b858410611da7575050505050505090565b86848303126101c357825190611dbc82611c34565b843561ffff811681036101c3578252858501358683015283850135906001600160401b03821682036101c357828792868b950152815201930192611d96565b60c06003198201126101c35760043561ffff811681036101c357916024356001600160a01b03811681036101c357916001600160401b03906044358281116101c35781611e4a91600401611ca6565b92606435926084359260a4359182116101c357611e6991600401611d50565b90565b610144359060ff821682036101c357565b906101606003198301126101c35761ffff9060043582811681036101c357926001600160a01b039160243583811681036101c357936001600160401b036044358181116101c35784611ed191600401611ca6565b94606435946084359460a4359460c43590811681036101c3579360e43584811681036101c357936101043590811681036101c35792610124359182116101c357611f1d91600401611d50565b906101443560ff811681036101c35790565b60a06003198201126101c35760043561ffff811681036101c357916024356001600160a01b03811681036101c35791604435906001600160401b0382116101c357611f7c91600401611ca6565b906064359060843590565b6101606003198201126101c35761ffff9160043583811681036101c35792602435926001600160401b03916044358381116101c35782611fc991600401611ca6565b93606435936084359360a4358281116101c35781611fe991600401611ca6565b9360c43590811681036101c3579260e43592610104356001600160a01b03811681036101c35792610124359182116101c357611f1d91600401611d50565b60005b83811061203a5750506000910152565b818101518382015260200161202a565b9060209161206381518092818552858086019101612027565b601f01601f1916010190565b81601f820112156101c35780359061208682611ced565b9260409261209684519586611c6a565b808552602093848087019260051b850101938385116101c357858101925b8584106120c5575050505050505090565b6001600160401b0384358181116101c35783019184601f1984890301126101c35784516120f181611c4f565b8984013560ff811681036101c3578152858401359283116101c35761211d888b80969581960101611ca6565b838201528152019301926120b4565b1561213357565b634e487b7160e01b600052600160045260246000fd5b519060ff821682036101c357565b519063ffffffff821682036101c357565b519061ffff821682036101c357565b51906001600160401b03821682036101c357565b9092919261219881611c8b565b916121a66040519384611c6a565b8294828452828201116101c35760206121c0930190612027565b565b9080601f830112156101c3578151611e699260200161218b565b9190610160838203126101c3576040928351916121f883611c18565b829461220383612149565b84526020612212818501612157565b81860152612221828501612157565b828601526060612232818601612168565b81870152608091828601518388015261224d60a08701612177565b60a088015261225e60c08701612149565b60c088015260e0860151936001600160401b03948581116101c357866122859189016121c2565b60e0890152610100612298818901612157565b9089015261012094858801518181116101c35788019387601f860112156101c3578451926122c584611ced565b986122d282519a8b611c6a565b848a5285808b019560071b880101968188116101c3578601945b87861061230b5750505050505050505083015261014080910151910152565b88868303126101c3578251908982019082821087831117612361578a9289928652885181528289015183820152612343868a01612149565b86820152612352878a01612149565b878201528152019501946122ec565b60246000634e487b7160e01b81526041600452fd5b519081151582036101c357565b90916060828403126101c3578151926001600160401b03938481116101c357816123ae9185016121dc565b936123bb60208501612376565b9360408101519182116101c357019080601f830112156101c3578151611e699260200161218b565b91908201809211610c3a57565b600111156123fa57565b634e487b7160e01b600052602160045260246000fd5b6020818051810103126101c3576020015160018110156101c35790565b906040519161243b83611c4f565b60008352602083019060008252836060828051810103126101c35761246260208301612149565b926060604084015193015190525260ff811661247b5750565b60405163c1f4bdd960e01b815260ff91909116600482015260006024820152604490fd5b604051906124ac82611c34565b606060408360008152600060208201520152565b60a0810151602081015115612927575060006124da61249f565b5060408281015182526000805160206144e183398151915260205281205415612688575060405161250a81611c4f565b601a81527f44656c697665727920616c726561647920706572666f726d656400000000000060208201526040519061254182611c34565b600082526001602083015260408201525b60a08201519060018060a01b036060840151169161257660c0850151835190612a00565b9260e085015193848102948186041490151715610c3a57602083015160028110156123fa5784600160009214612669575b6125b0916123e3565b9360e0830151801560001461264857506005945b9260068610156123fa576126159361260f928715801561263e575b612635575b6126056125ff846080606061260a96970151910151906123e3565b34612a00565b612a00565b6123e3565b90612b25565b15612623576121c092612a39565b6040516304aeb27d60e51b8152600490fd5b600091506125e4565b50600288146125df565b612663908661ffff60c0870151169161010087015192612b7a565b946125c4565b6125b0915061268160608501516080860151906123e3565b91506125a7565b61ffff8251169060a0830151602081015160408201519160c08601519061010087015161014060e08901519201519260408901519560808a015195604051998a6101208101106001600160401b036101208d01111761291357908a9594939291610120612781999c016040528652602086015260408501526060840152608083015260a082015260c0810191825260e08101938452610100810195865261272d61249f565b9560608201519260018060a01b036127486020850151612a0d565b16956040840151925191519061ffff85511690519160405197889563294ee51960e11b602088015260a0602488015260c487019061204a565b602319868203016044870152845180825260208201916020808360051b8301019701928d915b8383106128dc5750505050509260849285926020956064612823999801528484015260a483015203956127e2601f1997888101835282611c6a565b60805a940151978151906040519985808c0194019189f1943d806084106001146128d4575b80601f9189520116860101604052604087019485525a90612a00565b90808210156128cd57505b8452156128bf576040519061284282611bcb565b8282525260208201525b602081015160028110156123fa5760408301519061289e576000526000805160206144e18339815191526020524360406000205560008051602061450183398151915260205260006040812055612552565b60005260008051602061450183398151915260205243604060002055612552565b50506001602082015261284c565b905061282e565b506084612807565b9193985091939495966020806128fe600193601f198682030187528c5161204a565b9a019301930190928b989796959492936127a7565b634e487b7160e01b8a52604160045260248afd5b606082810151908201805160808401805190936001600160a01b0316929161294f91906123e3565b60e0850151909485826129e1575050506005935b60068510156123fa576129939361260f92861580156129d7575b6129ce575b61260a916125ff91519051906123e3565b15612623576040516121c0926129a882611bcb565b60008252604051916129b983611c34565b60008352600060208401526040830152612a39565b60009250612982565b506002871461297d565b6129fa9261010061ffff60c08501511693015192612b7a565b93612963565b91908203918211610c3a57565b8060a01c612a21576001600160a01b031690565b6024906040519063033b960d60e41b82526004820152fd5b90612a4b602060a08401510151612a0d565b61ffff835116926001600160401b036020820151169460408201519160208501519460028610156123fa578051604090910151610140830151909260009115612b0f5761012091500151955b6040519485526020850152604084015260068210156123fa577fbccc00b713f54173962e7de6098f643d8ebf53d488d71f4b2a5171496d038f9e93612af1612b0a928594606086015260c0608086015260c085019061204a565b83810360a08501526001600160a01b039091169561204a565b0390a4565b5060405190612b1d82611bcb565b815295612a97565b908015612b7357600060209181604051612b3e81611bcb565b5281805a926040519686880194f1913d801515600114612b6b575b808252601f01601f1916010160405290565b506000612b59565b5050600190565b90929161ffff808316907f0000000000000000000000000000000000000000000000000000000000000000168114612ce457612bb584612a0d565b6000918283612bd1604051612bc981611bcb565b828152612e02565b93612c0c6040519586926020978893849384830199635cf3af3360e11b8b52602484015287604484015260606064840152608483019061204a565b0393612c20601f1995868101835282611c6a565b51604051978389019a8b9360018060a01b0316620186a0f1913d808310600114612cdd575b808752601f011685010160405280612cd3575b15612cc85750508051810103126101c357519360015b158015612cbe575b8015612ca9575b612c9f57612c99611e6995612605612c93612d60565b85612a00565b92612e50565b5050505050600490565b50612cb68561260a612d60565b821115612c7d565b5084821115612c76565b925096925050612c6e565b5083835114612c58565b5081612c45565b50926001600160a01b039250612cfa9150612a0d565b168115612d5757600060209181604051612d1381611bcb565b5281806040519585870193620186a0f1913d801515600114612d4f575b808252601f01601f191601016040525b15612d4a57600090565b600190565b506000612d30565b50506001612d40565b604051631a90a21960e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610bdb57600091612db1575090565b906020823d8211612dda575b81612dca60209383611c6a565b81010312612dd757505190565b80fd5b3d9150612dbd565b611e69939261ffff6060931682526020820152816040820152019061204a565b516040519060006020830152604082015260408152611e6981611c34565b3d15612e4b573d90612e3182611c8b565b91612e3f6040519384611c6a565b82523d6000602084013e565b606090565b939192612ef193604092835193612e6685611bcb565b600095868652612e8b612e858351612e7d81611bcb565b898152612e02565b91612a0d565b825193612e9785611bcb565b8885528351998a9763640fdbff60e11b8952612ed261ffff809e169a8b60048c01528c60248c015261016060448c01526101648b019061204a565b908b60648b015260848a015260031994858a83030160a48b015261204a565b60c488019890985260e48701526001600160a01b0316610104860152848603016101248501528151808652602098899687019387019288915b838310612fa057505050505082809160c8610144830152039130620186a0f19283612f6a575b505050612f6557612f5f612e20565b50600390565b600290565b82813d8311612f99575b612f7e8183611c6a565b81010312612dd75750612f9090612177565b50388080612f50565b503d612f74565b8451805182168752898101518a8801528201516001600160401b0316828701528b988b98506060909601959094019360019290920191612f2a565b8051821015612fef5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060018060a01b037f00000000000000000000000000000000000000000000000000000000000000001691604061305e81519263607ec5ef60e11b8452602095600491878387015285806000958693602483019061204a565b0381845afa801561319c57839584908592613236575b50156132135750835163fbe3c2cd60e01b815287818481855afa9081156132095784916131d0575b5061ffff908160608801511691811682036131a65750508682918551928380926358b9591160e11b82525afa90811561319c57839161316b575b506080850151818103613150575050610140840195865183527f970ad24d4754c92e299cabb86552091f5df0a15abc0f1b71f37d3e30031585dc9182825260ff8585205416613139575060e095965183525220600160ff19825416179055015190565b875185516364cbf47160e01b815291820152602490fd5b84516342852f8d60e11b815292830152602482015260449150fd5b90508681813d8311613195575b6131828183611c6a565b810103126131915751386130d6565b8280fd5b503d613178565b84513d85823e3d90fd5b855163c97817ed60e01b815261ffff92831685820190815291909216602082015281900360400190fd5b90508781813d8311613202575b6131e78183611c6a565b810103126131fe576131f890612168565b3861309c565b8380fd5b503d6131dd565b85513d86823e3d90fd5b8451630169d68560e71b8152808401899052908190610d9990602483019061204a565b91505061324e9195503d8085833e610ebf8183611c6a565b9095919538613074565b51604381036132645750565b6044906040519063061bc83560e51b8252600482015260436024820152fd5b5190808203613290575050565b604492506040519163061bc83560e51b835260048301526024820152fd5b9194909395929796976132c960409788519061048682611bcb565b906132d48a516142df565b968a5160005b818110613347575050611e69999a508851986132f58a611c18565b61ffff8096168a5260018060a01b0380981660208b015289015260608801526000608088015260a08701521660c0850152600060e085015216610100830152610120820152600f61014082015261364f565b61335a613354828f612fdb565b5161432f565b908b5161336681611c4f565b600192838252602082015261337b828d612fdb565b52613386818c612fdb565b50016132da565b909295919693949897986133a960409687519061048682611bcb565b6133b38b516142df565b978b5160005b818110613427575050611e699a9b508751996133d48b611c18565b61ffff8095168b5260018060a01b03988980981660208d01528b015260608a0152600060808a015260a08901521660c08701521660e085015216610100830152610120820152600f61014082015261364f565b6134356133548f8390612fdb565b908b613457828d519261344784611c4f565b6001958685526020850152612fdb565b52613462818d612fdb565b50016133b9565b93909795929998999694919661347f8b516142df565b978b5160005b8181106134ef575050611e699a9b50604051996134a18b611c18565b61ffff9687168b5260208b015260408a01526060890152608088015260a08701521660c085015260e08401526001600160a01b0316610100830152610120820152600f61014082015261364f565b6134fd6133548f8390612fdb565b908b613510826040519261344784611c4f565b5261351b818d612fdb565b5001613485565b9390989a99979592969491966135388c516142df565b978c5160005b8181106135b3575050611e699b9c509060ff9998979695949392916040519b6135668d611c18565b61ffff9687168d5260208d015260408c015260608b015260808a015260a08901521660c087015260e08601526001600160a01b03166101008501526101208401521661014082015261364f565b6135c18f8261335491612fdb565b908b6135d4826040519261344784611c4f565b526135df818d612fdb565b500161353e565b9293611e699a979460ff9996929a97936040519b6135668d611c18565b9190916040818403126101c35780519260208201516001600160401b0381116101c357611e6992016121c2565b908160209103126101c357516001600160a01b03811681036101c35790565b60018060a01b03610100820151169161ffff825116604051906328f41de360e01b82526004820152602081602481875afa908115610bdb57600091613cf2575b5015613cbe5761ffff825116926000606084015160a0850151956136c76040519788938493635cf3af3360e11b855260048501612de2565b0381845afa938415610bdb576000908195613c98575b506136e6612d60565b60808501516136f98261260a83866123e3565b3403613c6d575061370f61012086015184613d2c565b845160208087015160408089015160608a015160808b01519251630cbcf9e160e21b815261ffff871660048201526024810193909352929a9093909190816044818b5afa908115610bdb5788928b91600093613c33575b5061ffff60c08301511690602061ffff60e0850151945116602460405180988193633d77cbfd60e01b835260048301525afa938415610bdb578e968e96600096613bed575b509260839895926138a59a989592613842989561012060018060a01b0361010089015116970151976137e260405180608052611bfc565b61ffff8c16608051526020608051015288604060805101526060608051015260808051015260a0608051015260c0608051015260e060805101526101006080510152610120608051015233610140608051015261016060805101526143ec565b6080519060806060830151920151906040519b8c94600160f81b602087015261ffff60f01b9060f01b166021860152602385015261388a815180926020604388019101612027565b83019160438301526063820152036063810189520187611c6a565b6138b460a060805101516143ec565b6080519660c088015160e0890151906101008a0151996101208101516101606101408301519201519b8c519460ff8611613bd4578d5160405160f89190911b6001600160f81b03191660208201526001815261390f81611c4f565b956000905b808210613b1b5750506004999a9b9c9d509160a29593916139a7979593604051998761394a8c995180926020808d019101612027565b880161395f8251809360208085019101612027565b019461ffff60f01b9060f01b16602086015260228501526042840152606283015260828201526139988251809360208785019101612027565b01036082810184520182611c6a565b602060ff61014060808a0151990151169560405194858092632fe4c87f60e21b82525afa928315610bdb57600093613ae3575b50602091613a0d916040518097819482936358cd21bf60e11b84526000600485015260606024850152606484019061204a565b604483019190915203917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1928315610bdb57600093613aa4575b50613a619061260f86846123e3565b9360405191825260208201527fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e48536460406001600160401b03841692a2911561262357565b90926020823d602011613adb575b81613abf60209383611c6a565b81010312612dd75750613ad4613a6191612177565b9290613a52565b3d9150613ab2565b613a0d91935091613b0b602093843d8611613b14575b613b038183611c6a565b810190613630565b939150916139da565b503d613af9565b90968f90613ba46020613b308b600195612fdb565b51805160ff16858103613bac5750613b6a8260ff83511692015191613b5c60405193849286840161429d565b03601f198101835282611c6a565b925b6040519381613b848693518092868087019101612027565b8201613b9882518093868085019101612027565b01038084520182611c6a565b970190613914565b90613bbc83613bce9201516143ec565b91613b5c60405193849286840161429d565b92613b6c565b6040516312d6a5c760e01b815260048101879052602490fd5b92949650965091936020823d602011613c2b575b81613c0e60209383611c6a565b81010312612dd75750518d958d95919491939192906138426137ab565b3d9150613c01565b92915092506020823d602011613c65575b81613c5160209383611c6a565b81010312612dd757505187918a9038613766565b3d9150613c44565b9061260a613c7d926044946123e3565b60405190631f89f67160e01b82523460048301526024820152fd5b90613cb69295503d8091833e613cae8183611c6a565b810190613603565b9390386136dd565b5061010081015190516040516366b69b9d60e01b81526001600160a01b03909216600483015261ffff166024820152604490fd5b906020823d602011613d24575b81613d0c60209383611c6a565b81010312612dd75750613d1e90612376565b3861368f565b3d9150613cff565b8151908115613df55760408051631284653d60e21b8152936004926020908690859082906001600160a01b03165afa948515613dea57600095613db7575b5060005b848110613d7d57505050505050565b60ff613d898284612fdb565b5151169060019182811b881615613da1575001613d6e565b8560249186519163041139f960e51b8352820152fd5b90946020823d8211613de2575b81613dd160209383611c6a565b81010312612dd75750519338613d6a565b3d9150613dc4565b82513d6000823e3d90fd5b505050565b604080516328f41de360e01b815261ffff841660048083018290526020999890979396956001600160a01b039485831693928c86602481885afa80156141e95760009687916141b0575b5015614180575093809392918a613e6f8a98978c519b8c948594635cf3af3360e11b86528501612de2565b0381855afa9586156141765783978497614154575b50613e8d612d60565b9395613e99858a6123e3565b340361412d5789519060c08201986001600160401b03998381108b82111761411957948f948d8f9b9a9895613fa895608395613f639c9a84528483528983019586528383019182526060830190815289613f44613f0360a060808701968d8852019733895261432f565b97613f3a60218851809b613f2a87830197600160f81b895282519283918686019101612027565b810103600181018c52018a611c6a565b51935192516143ec565b9351955194519d8e97600160f91b8d8a015251809260218a0190612027565b86019161ffff60f01b9060f01b1660218301526023820152613f8e825180938b604385019101612027565b019160438301526063820152036063810188520186611c6a565b8951632fe4c87f60e21b815296879182905afa94851561410f57918493918c93613fff9998976140ee575b5089516358cd21bf60e11b8152808c01869052606060248201529889938492918391606483019061204a565b60c8604483015203927f0000000000000000000000000000000000000000000000000000000000000000165af19485156140e25781956140a7575b508061409457507fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e485364916000614070858894612b25565b998351958652850152841692a29315614087575050565b516304aeb27d60e51b8152fd5b634e487b7160e01b815260118752602490fd5b9094508881813d83116140db575b6140bf8183611c6a565b810103126140d7576140d18591612177565b9461403a565b8480fd5b503d6140b5565b508551903d90823e3d90fd5b8591965061410890853d8711613b1457613b038183611c6a565b9590613fd3565b88513d86823e3d90fd5b50634e487b7160e01b895260418d52602489fd5b60448b8b61413b888d6123e3565b905191631f89f67160e01b835234908301526024820152fd5b90965061416c9197503d8085833e613cae8183611c6a565b9690969538613e84565b88513d85823e3d90fd5b89516366b69b9d60e01b81526001600160a01b03909116818c0190815261ffff8416602082015281906040010390fd5b90508d81813d83116141e2575b6141c78183611c6a565b810103126141de576141d890612376565b38613e44565b8680fd5b503d6141bd565b8a513d6000823e3d90fd5b9261421f9493614217926142116020956040519061048682611bcb565b91614224565b93909361242d565b015190565b926000929161424a9460405195869485938493635cf3af3360e11b855260048501612de2565b03916001600160a01b03165afa8015610bdb57600091829161427e575b5061427b9091614275612d60565b906123e3565b91565b614296915061427b923d8091833e613cae8183611c6a565b9091614267565b6001929160ff60f81b9060f81b1681526142c08251809360208685019101612027565b010190565b604051906142d282611c4f565b6060602083600081520152565b906142e982611ced565b6142f66040519182611c6a565b8281528092614307601f1991611ced565b019060005b82811061431857505050565b6020906143236142c5565b8282850101520161430c565b80519060406020820151910151906040519261ffff60f01b9060f01b16602084015260228301526001600160401b0360c01b9060c01b166042820152602a8152611e6981611c34565b929082156143cf578281019260405194601f82169283156143c6575b838701938385019201015b8184106143b65750508452601f01601f1916604052565b805184526020938401930161439f565b60209350614394565b925090506040516143df81611bcb565b6000815260003681379190565b611e696024825160405193849163ffffffff60e01b9060e01b16602083015261441e8151809260208686019101612027565b8101036004810184520182611c6a565b9061444991600463ffffffff81848401015116920190614378565b9091565b600101519060ff600192168281036144625750565b6044908360405191633ce5fedf60e11b835260048301526024820152fdfeebc28a1927f62765bfb7ada566eeab2d31a98c65dbd1e8cad64acae2a3ae45d41a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd221a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd231b988580e74603c035f5a7f71f2ae4647578af97cd0657db620836b9955fd8f56c615753402911c4de18a758def0565f37c41834d6eff72b16cb37cfb697f2a5a264697066735822122099073e38a4175732fdceb8d66f75e714a96fc9d50fe1ad3dd8914c358b5480be64736f6c63430008130033000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3
Deployed Bytecode
0x60a080604052600436101561001357600080fd5b60003560e01c9081632385904a14611a885750806324320c9f14611a5257806328b1d852146118935780632c75470f14611877578063329a2be71461184557806332b2fc0e146117ab5780633a2c767d1461176c5780633e8267e7146117125780633ed334df146114f957806340984f08146114c05780634533e5ff1461143e5780634b5ca6f4146113aa5780634d48ec60146113535780635a3b92e81461131a5780635cb8cae21461100557806375ea8b5814610fd957806380ebabd014610fa55780638b0301b114610f615780638fecdd0214610f1e578063a60eb4c81461055e578063a79629d8146104ef578063b1eac87514610464578063b686d0891461040d578063c055120e14610354578063c23ee3c314610303578063c4d66de8146102a1578063c81fb7fe14610270578063cee4bda0146101c85763d0625a191461015e57600080fd5b346101c35760203660031901126101c3576004356000526000805160206144e1833981519152602052604060002054158015906101a3575b6020906040519015158152f35b506000805160206145018339815191526020526040600020541515610196565b600080fd5b6101603660031901126101c3576101dd611b29565b6001600160401b036044358181116101c3576101fd903690600401611ca6565b9160a4358281116101c357610216903690600401611ca6565b9261021f611b3a565b93610228611bb4565b61012435938585116101c35760209661024861026796369060040161206f565b93610251611e6c565b9560e435936084359160643591602435906135e6565b60405191168152f35b602061029061027e36611f87565b99989098979197969296959395613522565b6001600160401b0360405191168152f35b346101c35760203660031901126101c3576004356001600160a01b038116908190036101c35760016000546102d960ff82161561212c565b60ff19161760005560008051602061448183398151915280546001600160a01b0319169091179055005b346101c35760603660031901126101c3576040610348610321611b29565b600080516020614481833981519152546001600160a01b03169060443590602435906141f4565b82519182526020820152f35b6101603660031901126101c357610369611b29565b610371611b5c565b906001600160401b03906044358281116101c357610393903690600401611ca6565b9261039c611b3a565b916103a5611b72565b906103ae611bb4565b9061012435948686116101c3576020976103cf61026797369060040161206f565b946103d8611e6c565b966103f26040516103e881611bcb565b60a4358152612e02565b9260018060a01b0380931695608435936064359316906135e6565b60e03660031901126101c35761042236611d04565b61042a611b4b565b60a4356001600160401b03918282116101c357602093610451610267933690600401611ca6565b9061045a611b88565b9260843591613dfa565b6104ed61048d61047336611dfb565b9590604094929394519061048682611bcb565b8152612e02565b9061ffff6000805160206144a18339815191525416926000805160206144c1833981519152549460018060a01b0396876000805160206144818339815191525416976104e56104de8a888786614224565b5034612a00565b941690613469565b005b346101c35760803660031901126101c357610508611b29565b604435906001600160401b0382116101c35761052b61053d923690600401611ca6565b610533611b9e565b9160243590614224565b9061055a604051928392835260406020840152604083019061204a565b0390f35b60803660031901126101c3576004356001600160401b0381116101c357366023820112156101c357806004013561059481611ced565b916105a26040519384611c6a565b8183526024602084019260051b820101903682116101c35760248101925b828410610eef57846024356001600160401b0381116101c3576105e7903690600401611ca6565b906001600160a01b0360443581811681036101c3576064356001600160401b0381116101c35761061b903690600401611ca6565b7f44dc27ebd67a87ad2af1d98fc4a5f971d9492fe12498e4c413ab5a05b7807a675483811680610ed157506001600160a01b03191633177f44dc27ebd67a87ad2af1d98fc4a5f971d9492fe12498e4c413ab5a05b7807a675560405163607ec5ef60e11b81526020600482015293946000908590819061069f90602483019061204a565b0381867f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3165afa8015610bdb57600094600090600092610ea6575b5015610e815750606084015161ffff1660009081527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d6020526040902054608085015190808203610e5457505060e0840151836040519361073a85611bfc565b6000855260006020860152606060408601526000606086015260006080860152606060a0860152600060c0860152600060e086015260006101008601526000610120860152600061014086015260606101608601526107ba602261079d8561444d565b858101600281015161ffff1689528201516020890152018461442e565b93906040870152838101906107db604080602085015194015196018261442e565b959060a089015260838087840161ffff60028201511660c08c0152602281015160e08c015260428101516101008c015260628101516101208c015260828101516101408c0152015196019261083260ff88166142df565b6000975b60ff81168910610d9d575061085d959697506101608a015260608901526080880152613283565b61ffff60c08501511660e08501519061ffff196000805160206144a18339815191525416176000805160206144a1833981519152556000805160206144c18339815191525561ffff606087015116956101406001600160401b0360a08301511691015190604051976108ce89611c18565b8852602088015260408701521660608501528460808501528160a0850152600060c0850152600060e0850152600061010085015280610120850152600061014085015260009061092160a0840151612410565b61092a816123f0565b80610d64575061093d60a084015161242d565b908051610c50575b50602081519101519161096160608501516080860151906123e3565b906101408701526101008601528160e08601528060c0860152818102918183041490151715610c3a57610100840151610999916123e3565b803410610c1d575061ffff8151168061ffff7f00000000000000000000000000000000000000000000000000000000000000101603610c05575061016001519081518451808203610be757505081519360005b858110610a57576109fc856124c0565b6000805160206144a1833981519152805461ffff1916905560006000805160206144c1833981519152557f44dc27ebd67a87ad2af1d98fc4a5f971d9492fe12498e4c413ab5a05b7807a6780546001600160a01b0319169055005b600160ff610a658387612fdb565b51511614610a76575b6001016109ec565b610aa86000610a858385612fdb565b516040518093819263a9e1189360e01b835260206004840152602483019061204a565b0381877f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3165afa908115610bdb57600091610b9a575b506020610aeb8387612fdb565b51015160405191610afb83611c34565b600083526020830192600084526001600160401b03602a60408301946000865261ffff600282015116809452602281015180975201511680935261ffff6060830151161492831593610b8b575b508215610b73575b505015610a6e57604051633ad7858760e21b815260ff9091166004820152602490fd5b60a001516001600160401b0316141590508780610b50565b60808201511415925089610b48565b3d9150816000823e610bac8282611c6a565b60208183810103126101c35780516001600160401b0381116101c357610bd592820191016121dc565b87610ade565b6040513d6000823e3d90fd5b60449250604051916365c1f7cd60e01b835260048301526024820152fd5b6024906040519063d8215fc960e01b82526004820152fd5b60449060405190620885af60e61b82523460048301526024820152fd5b634e487b7160e01b600052601160045260246000fd5b604051919250610c5f82611c34565b60008252610cab602083019160608352600060408501526020610c818261444d565b610c938280838601015192018461442e565b91908652828285010151604088015286520190613283565b606084015160006020604051610cc081611c4f565b8281520152825110610d5257610cd68151612410565b610cdf816123f0565b80610d275750610cef905161242d565b908051928251905111610d15576040919260608501526000608085015201519086610945565b6040516315fc687d60e31b8152600490fd5b80610d336044926123f0565b60ff6040519163170cd96160e11b835260006004840152166024820152fd5b604051631c6e090160e31b8152600490fd5b80610d71610d99926123f0565b60405163c1f4bdd960e01b815260ff9091166004820152600060248201529081906044820190565b0390fd5b9193959796509193610dad6142c5565b96600191828082019189010151908360ff829316808c5214600014610e39575060405190600a90818301916034840190828c01015b818410610e29575050602a808452601f909201601f191660405260208b019290925260ff939291015b98610e168287612fdb565b5201918b97959391509795939197610836565b8051845260209384019301610de2565b60ff93929150610e49908961442e565b9060208b0152610e0b565b6064925061ffff6060870151169060405192633bb6036760e11b8452600484015260248301526044820152fd5b60405163b72c3b7f60e01b815260206004820152908190610d9990602483019061204a565b915050610ec79194503d806000833e610ebf8183611c6a565b810190612383565b90949194876106da565b604490604051906320b84ced60e01b82523360048301526024820152fd5b83356001600160401b0381116101c357602091610f13839260243691870101611ca6565b8152019301926105c0565b6020610290610f2c36611f2f565b9060018060a0969495961b0360008051602061448183398151915254169360405195610f5787611bcb565b60008752846132ae565b60e03660031901126101c3576020610290610f7b36611d04565b610f83611b4b565b610f8b611b88565b91610f9b6040516103e881611bcb565b9160843591613dfa565b346101c35760803660031901126101c3576040610348610fc3611b29565b610fcb611b9e565b9060443590602435906141f4565b6104ed610fe536611f87565b9993955097610fff6104de83878a889c969c9b979b614224565b93613522565b346101c3576020806003193601126101c3576004356001600160401b0381116101c357611036903690600401611ca6565b60018060a01b03917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc9261106d8185541693613005565b828101516e576f726d686f6c6552656c617965728082036112fc57505060ff602182015116600281036112dd5750602381015161ffff908181168015806112d5575b156111f0575b5050506110ce6110c86043830151612a0d565b91613258565b803b15611195571692836bffffffffffffffffffffffff60a01b825416179055600080604051857fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8380a2632c75470f60e01b818501908152600482529061113581611c4f565b519082305af190611144612e20565b91156111735750507f2e4cc16c100f0b55e2df82ab0b1a7e294aa9cbd01b48fbaf622683fbc0507a49600080a3005b610d9960405192839263135687c760e31b84526004840152602483019061204a565b60405162461bcd60e51b815260048101849052602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608490fd5b60405163380e7c8960e21b815286816004817f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f38a165afa908115610bdb576000916112a0575b5061128e577f00000000000000000000000000000000000000000000000000000000000000109283160361126a57806110b5565b60405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b60405163ea03b6eb60e01b8152600490fd5b90508681813d83116112ce575b6112b78183611c6a565b810103126101c3576112c890612376565b89611236565b503d6112ad565b5060006110af565b60449060405190633460202560e21b8252600482015260026024820152fd5b6044925060405191633d254c6160e01b835260048301526024820152fd5b346101c35760203660031901126101c3576004356000526000805160206145018339815191526020526020604060002054604051908152f35b6104ed61137d61136236611e7d565b9a919894909995506040979297969396519061048682611bcb565b9361138a88868584614224565b506001600160a01b0397881697906113a29034612a00565b941690613522565b60e03660031901126101c3576113be611b29565b6113c6611b5c565b906001600160401b03906044358281116101c3576113e8903690600401611ca6565b60a4359161ffff831683036101c35760209461026793611406611b88565b9160018060a01b036000805160206144818339815191525416936040519561142d87611bcb565b60008752608435926064359261338d565b6104ed61145f61144d36611f2f565b60409591939295519061048682611bcb565b61ffff6000805160206144a18339815191525416916000805160206144c1833981519152549360018060a01b039687600080516020614481833981519152541696604051986114ad8a611bcb565b60008a526104e56104de8a888786614224565b346101c35760203660031901126101c3576004356000526000805160206144e18339815191526020526020604060002054604051908152f35b346101c3576020806003193601126101c3576004356001600160401b0381116101c35761152d611532913690600401611ca6565b613005565b90808201516e576f726d686f6c6552656c617965728082036112fc57505060ff602183015116600181036116f35750602382015161ffff90818116908115806116eb575b15611616575b5050602583015160458401519351604581036115f757501690816000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d91828252604060002054806115d9575060005252604060002055600080f35b6044925060405191637b5672c560e11b835260048301526024820152fd5b6044906040519063061bc83560e51b8252600482015260456024820152fd5b60405163380e7c8960e21b815284816004817f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f36001600160a01b03165afa908115610bdb576000916116b6575b5061128e577f0000000000000000000000000000000000000000000000000000000000000010918383161461157c5760405163901f6ae360e01b815261ffff918216600482015291166024820152604490fd5b90508481813d83116116e4575b6116cd8183611c6a565b810103126101c3576116de90612376565b86611663565b503d6116c3565b506001611576565b60449060405190633460202560e21b8252600482015260016024820152fd5b346101c35760203660031901126101c3576020611764611730611b29565b61ffff166000527f9e4e57806ba004485cfae8ca22fb13380f01c10b1b0ccf48c20464961643cf6d60205260406000205490565b604051908152f35b602061029061179661177d36611e7d565b9a90999198979295946040979497519061048682611bcb565b6001600160a01b039586169790951690613522565b6101003660031901126101c3576117c0611b29565b6117c8611b5c565b6001600160401b03906044358281116101c3576117e9903690600401611ca6565b9060a435918383116101c357602094611809610267943690600401611d50565b92611812611b3a565b9061181b611b72565b600080516020614481833981519152546001600160a01b031694909360843592606435929161338d565b602061029061185336611dfb565b949260018060a09493941b03600080516020614481833981519152541694846132ae565b346101c35760003660031901126101c3576104ed30331461212c565b346101c3576020806003193601126101c3576004356001600160401b0381116101c35761152d6118c7913690600401611ca6565b90808201516e576f726d686f6c6552656c617965728082036112fc57505060ff60218301511660038103611a335750602382015161ffff9182821690811580611a2b575b15611972575b505050506043810151906119276110c883612a0d565b6001600160a01b031690811561195a575060008051602061448183398151915280546001600160a01b0319169091179055005b60249060405190637a8ad12560e01b82526004820152fd5b60405163380e7c8960e21b815281816004817f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f36001600160a01b03165afa918215610bdb576000926119f5575b505061128e577f00000000000000000000000000000000000000000000000000000000000000109283160361126a578080611911565b90809250813d8311611a24575b611a0c8183611c6a565b810103126101c357611a1d90612376565b85806119bf565b503d611a02565b50600061190b565b60449060405190633460202560e21b8252600482015260036024820152fd5b346101c35760003660031901126101c357600080516020614481833981519152546040516001600160a01b039091168152602090f35b346101c35760603660031901126101c357611aa1611b29565b6044356001600160a01b03811691908290036101c357630cbcf9e160e21b835261ffff1660048301526024803590830152602090829060449082905afa8015610bdb57600090611af7575b602090604051908152f35b506020813d8211611b21575b81611b1060209383611c6a565b810103126101c35760209051611aec565b3d9150611b03565b6004359061ffff821682036101c357565b60c4359061ffff821682036101c357565b6064359061ffff821682036101c357565b602435906001600160a01b03821682036101c357565b60e435906001600160a01b03821682036101c357565b60c435906001600160a01b03821682036101c357565b606435906001600160a01b03821682036101c357565b61010435906001600160a01b03821682036101c357565b602081019081106001600160401b03821117611be657604052565b634e487b7160e01b600052604160045260246000fd5b61018081019081106001600160401b03821117611be657604052565b61016081019081106001600160401b03821117611be657604052565b606081019081106001600160401b03821117611be657604052565b604081019081106001600160401b03821117611be657604052565b90601f801991011681019081106001600160401b03821117611be657604052565b6001600160401b038111611be657601f01601f191660200190565b81601f820112156101c357803590611cbd82611c8b565b92611ccb6040519485611c6a565b828452602083830101116101c357816000926020809301838601378301015290565b6001600160401b038111611be65760051b60200190565b60609060031901126101c35760405190611d1d82611c34565b8160043561ffff811681036101c35781526024356020820152604435906001600160401b03821682036101c35760400152565b81601f820112156101c357803590611d6782611ced565b92604090611d7782519586611c6a565b83855260209182860191836060809702860101948186116101c3578401925b858410611da7575050505050505090565b86848303126101c357825190611dbc82611c34565b843561ffff811681036101c3578252858501358683015283850135906001600160401b03821682036101c357828792868b950152815201930192611d96565b60c06003198201126101c35760043561ffff811681036101c357916024356001600160a01b03811681036101c357916001600160401b03906044358281116101c35781611e4a91600401611ca6565b92606435926084359260a4359182116101c357611e6991600401611d50565b90565b610144359060ff821682036101c357565b906101606003198301126101c35761ffff9060043582811681036101c357926001600160a01b039160243583811681036101c357936001600160401b036044358181116101c35784611ed191600401611ca6565b94606435946084359460a4359460c43590811681036101c3579360e43584811681036101c357936101043590811681036101c35792610124359182116101c357611f1d91600401611d50565b906101443560ff811681036101c35790565b60a06003198201126101c35760043561ffff811681036101c357916024356001600160a01b03811681036101c35791604435906001600160401b0382116101c357611f7c91600401611ca6565b906064359060843590565b6101606003198201126101c35761ffff9160043583811681036101c35792602435926001600160401b03916044358381116101c35782611fc991600401611ca6565b93606435936084359360a4358281116101c35781611fe991600401611ca6565b9360c43590811681036101c3579260e43592610104356001600160a01b03811681036101c35792610124359182116101c357611f1d91600401611d50565b60005b83811061203a5750506000910152565b818101518382015260200161202a565b9060209161206381518092818552858086019101612027565b601f01601f1916010190565b81601f820112156101c35780359061208682611ced565b9260409261209684519586611c6a565b808552602093848087019260051b850101938385116101c357858101925b8584106120c5575050505050505090565b6001600160401b0384358181116101c35783019184601f1984890301126101c35784516120f181611c4f565b8984013560ff811681036101c3578152858401359283116101c35761211d888b80969581960101611ca6565b838201528152019301926120b4565b1561213357565b634e487b7160e01b600052600160045260246000fd5b519060ff821682036101c357565b519063ffffffff821682036101c357565b519061ffff821682036101c357565b51906001600160401b03821682036101c357565b9092919261219881611c8b565b916121a66040519384611c6a565b8294828452828201116101c35760206121c0930190612027565b565b9080601f830112156101c3578151611e699260200161218b565b9190610160838203126101c3576040928351916121f883611c18565b829461220383612149565b84526020612212818501612157565b81860152612221828501612157565b828601526060612232818601612168565b81870152608091828601518388015261224d60a08701612177565b60a088015261225e60c08701612149565b60c088015260e0860151936001600160401b03948581116101c357866122859189016121c2565b60e0890152610100612298818901612157565b9089015261012094858801518181116101c35788019387601f860112156101c3578451926122c584611ced565b986122d282519a8b611c6a565b848a5285808b019560071b880101968188116101c3578601945b87861061230b5750505050505050505083015261014080910151910152565b88868303126101c3578251908982019082821087831117612361578a9289928652885181528289015183820152612343868a01612149565b86820152612352878a01612149565b878201528152019501946122ec565b60246000634e487b7160e01b81526041600452fd5b519081151582036101c357565b90916060828403126101c3578151926001600160401b03938481116101c357816123ae9185016121dc565b936123bb60208501612376565b9360408101519182116101c357019080601f830112156101c3578151611e699260200161218b565b91908201809211610c3a57565b600111156123fa57565b634e487b7160e01b600052602160045260246000fd5b6020818051810103126101c3576020015160018110156101c35790565b906040519161243b83611c4f565b60008352602083019060008252836060828051810103126101c35761246260208301612149565b926060604084015193015190525260ff811661247b5750565b60405163c1f4bdd960e01b815260ff91909116600482015260006024820152604490fd5b604051906124ac82611c34565b606060408360008152600060208201520152565b60a0810151602081015115612927575060006124da61249f565b5060408281015182526000805160206144e183398151915260205281205415612688575060405161250a81611c4f565b601a81527f44656c697665727920616c726561647920706572666f726d656400000000000060208201526040519061254182611c34565b600082526001602083015260408201525b60a08201519060018060a01b036060840151169161257660c0850151835190612a00565b9260e085015193848102948186041490151715610c3a57602083015160028110156123fa5784600160009214612669575b6125b0916123e3565b9360e0830151801560001461264857506005945b9260068610156123fa576126159361260f928715801561263e575b612635575b6126056125ff846080606061260a96970151910151906123e3565b34612a00565b612a00565b6123e3565b90612b25565b15612623576121c092612a39565b6040516304aeb27d60e51b8152600490fd5b600091506125e4565b50600288146125df565b612663908661ffff60c0870151169161010087015192612b7a565b946125c4565b6125b0915061268160608501516080860151906123e3565b91506125a7565b61ffff8251169060a0830151602081015160408201519160c08601519061010087015161014060e08901519201519260408901519560808a015195604051998a6101208101106001600160401b036101208d01111761291357908a9594939291610120612781999c016040528652602086015260408501526060840152608083015260a082015260c0810191825260e08101938452610100810195865261272d61249f565b9560608201519260018060a01b036127486020850151612a0d565b16956040840151925191519061ffff85511690519160405197889563294ee51960e11b602088015260a0602488015260c487019061204a565b602319868203016044870152845180825260208201916020808360051b8301019701928d915b8383106128dc5750505050509260849285926020956064612823999801528484015260a483015203956127e2601f1997888101835282611c6a565b60805a940151978151906040519985808c0194019189f1943d806084106001146128d4575b80601f9189520116860101604052604087019485525a90612a00565b90808210156128cd57505b8452156128bf576040519061284282611bcb565b8282525260208201525b602081015160028110156123fa5760408301519061289e576000526000805160206144e18339815191526020524360406000205560008051602061450183398151915260205260006040812055612552565b60005260008051602061450183398151915260205243604060002055612552565b50506001602082015261284c565b905061282e565b506084612807565b9193985091939495966020806128fe600193601f198682030187528c5161204a565b9a019301930190928b989796959492936127a7565b634e487b7160e01b8a52604160045260248afd5b606082810151908201805160808401805190936001600160a01b0316929161294f91906123e3565b60e0850151909485826129e1575050506005935b60068510156123fa576129939361260f92861580156129d7575b6129ce575b61260a916125ff91519051906123e3565b15612623576040516121c0926129a882611bcb565b60008252604051916129b983611c34565b60008352600060208401526040830152612a39565b60009250612982565b506002871461297d565b6129fa9261010061ffff60c08501511693015192612b7a565b93612963565b91908203918211610c3a57565b8060a01c612a21576001600160a01b031690565b6024906040519063033b960d60e41b82526004820152fd5b90612a4b602060a08401510151612a0d565b61ffff835116926001600160401b036020820151169460408201519160208501519460028610156123fa578051604090910151610140830151909260009115612b0f5761012091500151955b6040519485526020850152604084015260068210156123fa577fbccc00b713f54173962e7de6098f643d8ebf53d488d71f4b2a5171496d038f9e93612af1612b0a928594606086015260c0608086015260c085019061204a565b83810360a08501526001600160a01b039091169561204a565b0390a4565b5060405190612b1d82611bcb565b815295612a97565b908015612b7357600060209181604051612b3e81611bcb565b5281805a926040519686880194f1913d801515600114612b6b575b808252601f01601f1916010160405290565b506000612b59565b5050600190565b90929161ffff808316907f0000000000000000000000000000000000000000000000000000000000000010168114612ce457612bb584612a0d565b6000918283612bd1604051612bc981611bcb565b828152612e02565b93612c0c6040519586926020978893849384830199635cf3af3360e11b8b52602484015287604484015260606064840152608483019061204a565b0393612c20601f1995868101835282611c6a565b51604051978389019a8b9360018060a01b0316620186a0f1913d808310600114612cdd575b808752601f011685010160405280612cd3575b15612cc85750508051810103126101c357519360015b158015612cbe575b8015612ca9575b612c9f57612c99611e6995612605612c93612d60565b85612a00565b92612e50565b5050505050600490565b50612cb68561260a612d60565b821115612c7d565b5084821115612c76565b925096925050612c6e565b5083835114612c58565b5081612c45565b50926001600160a01b039250612cfa9150612a0d565b168115612d5757600060209181604051612d1381611bcb565b5281806040519585870193620186a0f1913d801515600114612d4f575b808252601f01601f191601016040525b15612d4a57600090565b600190565b506000612d30565b50506001612d40565b604051631a90a21960e01b81526020816004817f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f36001600160a01b03165afa908115610bdb57600091612db1575090565b906020823d8211612dda575b81612dca60209383611c6a565b81010312612dd757505190565b80fd5b3d9150612dbd565b611e69939261ffff6060931682526020820152816040820152019061204a565b516040519060006020830152604082015260408152611e6981611c34565b3d15612e4b573d90612e3182611c8b565b91612e3f6040519384611c6a565b82523d6000602084013e565b606090565b939192612ef193604092835193612e6685611bcb565b600095868652612e8b612e858351612e7d81611bcb565b898152612e02565b91612a0d565b825193612e9785611bcb565b8885528351998a9763640fdbff60e11b8952612ed261ffff809e169a8b60048c01528c60248c015261016060448c01526101648b019061204a565b908b60648b015260848a015260031994858a83030160a48b015261204a565b60c488019890985260e48701526001600160a01b0316610104860152848603016101248501528151808652602098899687019387019288915b838310612fa057505050505082809160c8610144830152039130620186a0f19283612f6a575b505050612f6557612f5f612e20565b50600390565b600290565b82813d8311612f99575b612f7e8183611c6a565b81010312612dd75750612f9090612177565b50388080612f50565b503d612f74565b8451805182168752898101518a8801528201516001600160401b0316828701528b988b98506060909601959094019360019290920191612f2a565b8051821015612fef5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b9060018060a01b037f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f31691604061305e81519263607ec5ef60e11b8452602095600491878387015285806000958693602483019061204a565b0381845afa801561319c57839584908592613236575b50156132135750835163fbe3c2cd60e01b815287818481855afa9081156132095784916131d0575b5061ffff908160608801511691811682036131a65750508682918551928380926358b9591160e11b82525afa90811561319c57839161316b575b506080850151818103613150575050610140840195865183527f970ad24d4754c92e299cabb86552091f5df0a15abc0f1b71f37d3e30031585dc9182825260ff8585205416613139575060e095965183525220600160ff19825416179055015190565b875185516364cbf47160e01b815291820152602490fd5b84516342852f8d60e11b815292830152602482015260449150fd5b90508681813d8311613195575b6131828183611c6a565b810103126131915751386130d6565b8280fd5b503d613178565b84513d85823e3d90fd5b855163c97817ed60e01b815261ffff92831685820190815291909216602082015281900360400190fd5b90508781813d8311613202575b6131e78183611c6a565b810103126131fe576131f890612168565b3861309c565b8380fd5b503d6131dd565b85513d86823e3d90fd5b8451630169d68560e71b8152808401899052908190610d9990602483019061204a565b91505061324e9195503d8085833e610ebf8183611c6a565b9095919538613074565b51604381036132645750565b6044906040519063061bc83560e51b8252600482015260436024820152fd5b5190808203613290575050565b604492506040519163061bc83560e51b835260048301526024820152fd5b9194909395929796976132c960409788519061048682611bcb565b906132d48a516142df565b968a5160005b818110613347575050611e69999a508851986132f58a611c18565b61ffff8096168a5260018060a01b0380981660208b015289015260608801526000608088015260a08701521660c0850152600060e085015216610100830152610120820152600f61014082015261364f565b61335a613354828f612fdb565b5161432f565b908b5161336681611c4f565b600192838252602082015261337b828d612fdb565b52613386818c612fdb565b50016132da565b909295919693949897986133a960409687519061048682611bcb565b6133b38b516142df565b978b5160005b818110613427575050611e699a9b508751996133d48b611c18565b61ffff8095168b5260018060a01b03988980981660208d01528b015260608a0152600060808a015260a08901521660c08701521660e085015216610100830152610120820152600f61014082015261364f565b6134356133548f8390612fdb565b908b613457828d519261344784611c4f565b6001958685526020850152612fdb565b52613462818d612fdb565b50016133b9565b93909795929998999694919661347f8b516142df565b978b5160005b8181106134ef575050611e699a9b50604051996134a18b611c18565b61ffff9687168b5260208b015260408a01526060890152608088015260a08701521660c085015260e08401526001600160a01b0316610100830152610120820152600f61014082015261364f565b6134fd6133548f8390612fdb565b908b613510826040519261344784611c4f565b5261351b818d612fdb565b5001613485565b9390989a99979592969491966135388c516142df565b978c5160005b8181106135b3575050611e699b9c509060ff9998979695949392916040519b6135668d611c18565b61ffff9687168d5260208d015260408c015260608b015260808a015260a08901521660c087015260e08601526001600160a01b03166101008501526101208401521661014082015261364f565b6135c18f8261335491612fdb565b908b6135d4826040519261344784611c4f565b526135df818d612fdb565b500161353e565b9293611e699a979460ff9996929a97936040519b6135668d611c18565b9190916040818403126101c35780519260208201516001600160401b0381116101c357611e6992016121c2565b908160209103126101c357516001600160a01b03811681036101c35790565b60018060a01b03610100820151169161ffff825116604051906328f41de360e01b82526004820152602081602481875afa908115610bdb57600091613cf2575b5015613cbe5761ffff825116926000606084015160a0850151956136c76040519788938493635cf3af3360e11b855260048501612de2565b0381845afa938415610bdb576000908195613c98575b506136e6612d60565b60808501516136f98261260a83866123e3565b3403613c6d575061370f61012086015184613d2c565b845160208087015160408089015160608a015160808b01519251630cbcf9e160e21b815261ffff871660048201526024810193909352929a9093909190816044818b5afa908115610bdb5788928b91600093613c33575b5061ffff60c08301511690602061ffff60e0850151945116602460405180988193633d77cbfd60e01b835260048301525afa938415610bdb578e968e96600096613bed575b509260839895926138a59a989592613842989561012060018060a01b0361010089015116970151976137e260405180608052611bfc565b61ffff8c16608051526020608051015288604060805101526060608051015260808051015260a0608051015260c0608051015260e060805101526101006080510152610120608051015233610140608051015261016060805101526143ec565b6080519060806060830151920151906040519b8c94600160f81b602087015261ffff60f01b9060f01b166021860152602385015261388a815180926020604388019101612027565b83019160438301526063820152036063810189520187611c6a565b6138b460a060805101516143ec565b6080519660c088015160e0890151906101008a0151996101208101516101606101408301519201519b8c519460ff8611613bd4578d5160405160f89190911b6001600160f81b03191660208201526001815261390f81611c4f565b956000905b808210613b1b5750506004999a9b9c9d509160a29593916139a7979593604051998761394a8c995180926020808d019101612027565b880161395f8251809360208085019101612027565b019461ffff60f01b9060f01b16602086015260228501526042840152606283015260828201526139988251809360208785019101612027565b01036082810184520182611c6a565b602060ff61014060808a0151990151169560405194858092632fe4c87f60e21b82525afa928315610bdb57600093613ae3575b50602091613a0d916040518097819482936358cd21bf60e11b84526000600485015260606024850152606484019061204a565b604483019190915203917f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f36001600160a01b03165af1928315610bdb57600093613aa4575b50613a619061260f86846123e3565b9360405191825260208201527fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e48536460406001600160401b03841692a2911561262357565b90926020823d602011613adb575b81613abf60209383611c6a565b81010312612dd75750613ad4613a6191612177565b9290613a52565b3d9150613ab2565b613a0d91935091613b0b602093843d8611613b14575b613b038183611c6a565b810190613630565b939150916139da565b503d613af9565b90968f90613ba46020613b308b600195612fdb565b51805160ff16858103613bac5750613b6a8260ff83511692015191613b5c60405193849286840161429d565b03601f198101835282611c6a565b925b6040519381613b848693518092868087019101612027565b8201613b9882518093868085019101612027565b01038084520182611c6a565b970190613914565b90613bbc83613bce9201516143ec565b91613b5c60405193849286840161429d565b92613b6c565b6040516312d6a5c760e01b815260048101879052602490fd5b92949650965091936020823d602011613c2b575b81613c0e60209383611c6a565b81010312612dd75750518d958d95919491939192906138426137ab565b3d9150613c01565b92915092506020823d602011613c65575b81613c5160209383611c6a565b81010312612dd757505187918a9038613766565b3d9150613c44565b9061260a613c7d926044946123e3565b60405190631f89f67160e01b82523460048301526024820152fd5b90613cb69295503d8091833e613cae8183611c6a565b810190613603565b9390386136dd565b5061010081015190516040516366b69b9d60e01b81526001600160a01b03909216600483015261ffff166024820152604490fd5b906020823d602011613d24575b81613d0c60209383611c6a565b81010312612dd75750613d1e90612376565b3861368f565b3d9150613cff565b8151908115613df55760408051631284653d60e21b8152936004926020908690859082906001600160a01b03165afa948515613dea57600095613db7575b5060005b848110613d7d57505050505050565b60ff613d898284612fdb565b5151169060019182811b881615613da1575001613d6e565b8560249186519163041139f960e51b8352820152fd5b90946020823d8211613de2575b81613dd160209383611c6a565b81010312612dd75750519338613d6a565b3d9150613dc4565b82513d6000823e3d90fd5b505050565b604080516328f41de360e01b815261ffff841660048083018290526020999890979396956001600160a01b039485831693928c86602481885afa80156141e95760009687916141b0575b5015614180575093809392918a613e6f8a98978c519b8c948594635cf3af3360e11b86528501612de2565b0381855afa9586156141765783978497614154575b50613e8d612d60565b9395613e99858a6123e3565b340361412d5789519060c08201986001600160401b03998381108b82111761411957948f948d8f9b9a9895613fa895608395613f639c9a84528483528983019586528383019182526060830190815289613f44613f0360a060808701968d8852019733895261432f565b97613f3a60218851809b613f2a87830197600160f81b895282519283918686019101612027565b810103600181018c52018a611c6a565b51935192516143ec565b9351955194519d8e97600160f91b8d8a015251809260218a0190612027565b86019161ffff60f01b9060f01b1660218301526023820152613f8e825180938b604385019101612027565b019160438301526063820152036063810188520186611c6a565b8951632fe4c87f60e21b815296879182905afa94851561410f57918493918c93613fff9998976140ee575b5089516358cd21bf60e11b8152808c01869052606060248201529889938492918391606483019061204a565b60c8604483015203927f000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3165af19485156140e25781956140a7575b508061409457507fda8540426b64ece7b164a9dce95448765f0a7263ef3ff85091c9c7361e485364916000614070858894612b25565b998351958652850152841692a29315614087575050565b516304aeb27d60e51b8152fd5b634e487b7160e01b815260118752602490fd5b9094508881813d83116140db575b6140bf8183611c6a565b810103126140d7576140d18591612177565b9461403a565b8480fd5b503d6140b5565b508551903d90823e3d90fd5b8591965061410890853d8711613b1457613b038183611c6a565b9590613fd3565b88513d86823e3d90fd5b50634e487b7160e01b895260418d52602489fd5b60448b8b61413b888d6123e3565b905191631f89f67160e01b835234908301526024820152fd5b90965061416c9197503d8085833e613cae8183611c6a565b9690969538613e84565b88513d85823e3d90fd5b89516366b69b9d60e01b81526001600160a01b03909116818c0190815261ffff8416602082015281906040010390fd5b90508d81813d83116141e2575b6141c78183611c6a565b810103126141de576141d890612376565b38613e44565b8680fd5b503d6141bd565b8a513d6000823e3d90fd5b9261421f9493614217926142116020956040519061048682611bcb565b91614224565b93909361242d565b015190565b926000929161424a9460405195869485938493635cf3af3360e11b855260048501612de2565b03916001600160a01b03165afa8015610bdb57600091829161427e575b5061427b9091614275612d60565b906123e3565b91565b614296915061427b923d8091833e613cae8183611c6a565b9091614267565b6001929160ff60f81b9060f81b1681526142c08251809360208685019101612027565b010190565b604051906142d282611c4f565b6060602083600081520152565b906142e982611ced565b6142f66040519182611c6a565b8281528092614307601f1991611ced565b019060005b82811061431857505050565b6020906143236142c5565b8282850101520161430c565b80519060406020820151910151906040519261ffff60f01b9060f01b16602084015260228301526001600160401b0360c01b9060c01b166042820152602a8152611e6981611c34565b929082156143cf578281019260405194601f82169283156143c6575b838701938385019201015b8184106143b65750508452601f01601f1916604052565b805184526020938401930161439f565b60209350614394565b925090506040516143df81611bcb565b6000815260003681379190565b611e696024825160405193849163ffffffff60e01b9060e01b16602083015261441e8151809260208686019101612027565b8101036004810184520182611c6a565b9061444991600463ffffffff81848401015116920190614378565b9091565b600101519060ff600192168281036144625750565b6044908360405191633ce5fedf60e11b835260048301526024820152fdfeebc28a1927f62765bfb7ada566eeab2d31a98c65dbd1e8cad64acae2a3ae45d41a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd221a2a8eb52f1d00a1242a3f8cc031e30a32870ff64f69009c4e06f75bd842fd231b988580e74603c035f5a7f71f2ae4647578af97cd0657db620836b9955fd8f56c615753402911c4de18a758def0565f37c41834d6eff72b16cb37cfb697f2a5a264697066735822122099073e38a4175732fdceb8d66f75e714a96fc9d50fe1ad3dd8914c358b5480be64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3
-----Decoded View---------------
Arg [0] : wormhole (address): 0xC8e2b0cD52Cf01b0Ce87d389Daa3d414d4cE29f3
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000c8e2b0cd52cf01b0ce87d389daa3d414d4ce29f3
Loading...
Loading
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.