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