Overview
ETH Balance
0.000028822643561123 ETH
ETH Value
$0.12 (@ $4,002.78/ETH)Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 34,830 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Process Packet | 128968740 | 2 hrs ago | IN | 0 ETH | 0.000015230927 | ||||
Process Packet | 128965864 | 4 hrs ago | IN | 0 ETH | 0.000002639157 | ||||
Process Packet | 128965672 | 4 hrs ago | IN | 0 ETH | 0.000002415494 | ||||
Process Packet | 128965258 | 4 hrs ago | IN | 0 ETH | 0.00000248055 | ||||
Process Packet | 128964878 | 5 hrs ago | IN | 0 ETH | 0.000003231245 | ||||
Process Packet | 128963909 | 5 hrs ago | IN | 0 ETH | 0.000006565836 | ||||
Process Packet | 128950120 | 13 hrs ago | IN | 0 ETH | 0.000066224587 | ||||
Process Packet | 128946062 | 15 hrs ago | IN | 0 ETH | 0.000036688889 | ||||
Process Packet | 128944528 | 16 hrs ago | IN | 0 ETH | 0.000013426229 | ||||
Process Packet | 128934750 | 21 hrs ago | IN | 0 ETH | 0.000010822153 | ||||
Process Packet | 128934542 | 21 hrs ago | IN | 0 ETH | 0.000021497606 | ||||
Process Packet | 128929575 | 24 hrs ago | IN | 0 ETH | 0.000014019722 | ||||
Process Packet | 128929167 | 24 hrs ago | IN | 0 ETH | 0.000036163606 | ||||
Process Packet | 128928398 | 25 hrs ago | IN | 0 ETH | 0.000023472559 | ||||
Process Packet | 128920922 | 29 hrs ago | IN | 0 ETH | 0.000004445386 | ||||
Process Packet | 128920543 | 29 hrs ago | IN | 0 ETH | 0.000005883057 | ||||
Process Packet | 128917864 | 31 hrs ago | IN | 0 ETH | 0.000019116106 | ||||
Process Packet | 128913054 | 33 hrs ago | IN | 0 ETH | 0.000006650419 | ||||
Process Packet | 128912270 | 34 hrs ago | IN | 0 ETH | 0.0000049214 | ||||
Process Packet | 128910542 | 35 hrs ago | IN | 0 ETH | 0.000012327819 | ||||
Process Packet | 128910350 | 35 hrs ago | IN | 0 ETH | 0.000016214134 | ||||
Process Packet | 128910158 | 35 hrs ago | IN | 0 ETH | 0.000015414375 | ||||
Process Packet | 128905375 | 38 hrs ago | IN | 0 ETH | 0.000014339373 | ||||
Process Packet | 128902303 | 39 hrs ago | IN | 0 ETH | 0.000034092992 | ||||
Process Packet | 128891534 | 45 hrs ago | IN | 0 ETH | 0.000014665228 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
128968740 | 2 hrs ago | 0.000000910171115 ETH | ||||
128968740 | 2 hrs ago | 0.000000213956274 ETH | ||||
128966867 | 3 hrs ago | 0.000000224825478 ETH | ||||
128966867 | 3 hrs ago | 0.000001348952868 ETH | ||||
128965864 | 4 hrs ago | 0.000000042236572 ETH | ||||
128965864 | 4 hrs ago | 0.000005161566398 ETH | ||||
128965864 | 4 hrs ago | 0.000005222269635 ETH | ||||
128965672 | 4 hrs ago | 0.00000004218053 ETH | ||||
128965672 | 4 hrs ago | 0.000005313850221 ETH | ||||
128965672 | 4 hrs ago | 0.000005375248609 ETH | ||||
128964522 | 5 hrs ago | 0.000002085214521 ETH | ||||
128964522 | 5 hrs ago | 0.000012511287127 ETH | ||||
128964296 | 5 hrs ago | 0.000002146256211 ETH | ||||
128964296 | 5 hrs ago | 0.000012877535572 ETH | ||||
128950120 | 13 hrs ago | 0.000003523283604 ETH | ||||
128950120 | 13 hrs ago | 0.000001810481069 ETH | ||||
128948367 | 14 hrs ago | 0.000001066753613 ETH | ||||
128948367 | 14 hrs ago | 0.000006400518287 ETH | ||||
128917864 | 31 hrs ago | 0.000000041817626 ETH | ||||
128917864 | 31 hrs ago | 0.000011366240954 ETH | ||||
128917864 | 31 hrs ago | 0.000011456340534 ETH | ||||
128914198 | 33 hrs ago | 0.000004572879823 ETH | ||||
128914198 | 33 hrs ago | 0.000027437278938 ETH | ||||
128887527 | 2 days ago | 0.000000041085938 ETH | ||||
128887527 | 2 days ago | 0.000010584115225 ETH |
Loading...
Loading
Contract Name:
IncentivizedWormholeEscrow
Compiler Version
v0.8.22+commit.4fc1097e
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { IncentivizedMessageEscrow } from "../../IncentivizedMessageEscrow.sol"; import { SmallStructs } from "./external/callworm/SmallStructs.sol"; import { WormholeVerifier } from "./external/callworm/WormholeVerifier.sol"; import { IWormhole } from "./interfaces/IWormhole.sol"; /** * @title Incentivized Wormhole Message Escrow * @notice Incentivizes Wormhole messages through Generalised Incentives. * Wormhole does not have any native way of relaying messages, this implemention adds one. * * When using Wormhole with Generalised Incentives and you don't want to lose message, be very careful regarding * emitting messages to destinationChainIdentifiers that does not exist. Wormhole has no way to verify if a * chain identifier exists or not. If the chain identifier does not exist, it is not possible to get a timeout or ack back. * * @dev This implementation only uses the Wormhole contracts to emit messages, it does not use the Wormhole contracts * to verify if messages are authentic. A custom verification library is used that is more gas efficient and skips * parts of the VAA that is not relevant to us. This provides significant gas savings compared to verifying packages * against the Wormhole implementation. */ contract IncentivizedWormholeEscrow is IncentivizedMessageEscrow, WormholeVerifier { error BadChainIdentifier(); IWormhole public immutable WORMHOLE; // Wormhole's chain identifier can be changed. However, we generally expect to redeploy // in cases where they would change it. bytes32 public immutable UNIQUE_SOURCE_IDENTIFIER; // For EVM it is generally set that 15 => Finality uint8 constant WORMHOLE_CONSISTENCY = 15; constructor(address sendLostGasTo, address wormhole_) payable IncentivizedMessageEscrow(sendLostGasTo) WormholeVerifier(wormhole_) { WORMHOLE = IWormhole(wormhole_); // Collect chainId from Wormhole. UNIQUE_SOURCE_IDENTIFIER = bytes32(uint256(WORMHOLE.chainId())); } function estimateAdditionalCost() external view returns(address asset, uint256 amount) { asset = address(0); amount = WORMHOLE.messageFee(); } /** @notice Wormhole proofs are valid until the guardian set is changed. The new guradian set may sign a new VAA */ function _proofValidPeriod(bytes32 /* destinationIdentifier */) override internal pure returns(uint64) { return 0; } function _uniqueSourceIdentifier() override internal view returns(bytes32 sourceIdentifier) { return sourceIdentifier = UNIQUE_SOURCE_IDENTIFIER; } /** @dev _message is the entire Wormhole VAA. It contains both the proof & the message as a slice. */ function _verifyPacket(bytes calldata /* _metadata */, bytes calldata _message) internal view override returns(bytes32 sourceIdentifier, bytes memory implementationIdentifier, bytes calldata message_) { // Decode & verify the VAA. // This uses the custom verification logic found in ./external/callworm/WormholeVerifier.sol. ( SmallStructs.SmallVM memory vm, bytes calldata payload, bool valid, string memory reason ) = parseAndVerifyVM(_message); // This is the preferred flow used by Wormhole. require(valid, reason); // We added the destination chain to the payload since Wormhole messages are broadcast. // Get the chain identifier for the destination chain according to the payload. bytes32 destinationChainIdentifier = bytes32(payload[0:32]); // Check that the message is intended for this chain. if (destinationChainIdentifier != UNIQUE_SOURCE_IDENTIFIER) revert BadChainIdentifier(); // Get the identifier for the source chain. sourceIdentifier = bytes32(uint256(vm.emitterChainId)); // Load the identifier for the calling contract. implementationIdentifier = bytes.concat(vm.emitterAddress); // Get the application message. message_ = payload[32:]; } /** * @dev Wormhole messages are broadcast, as a result we set destinationChainIdentifier in the message. */ function _sendPacket(bytes32 destinationChainIdentifier, bytes memory /* destinationImplementation */, bytes memory message, uint64 /* deadline */) internal override returns(uint128 costOfsendPacketInNativeToken) { // Get the cost of sending wormhole messages. costOfsendPacketInNativeToken = uint128(WORMHOLE.messageFee()); // Relayers can collect the destination chain from the payload and destinationImplementation from storage / their whitelist. // Handoff the message to wormhole. WORMHOLE.publishMessage{value: costOfsendPacketInNativeToken}( 0, bytes.concat( destinationChainIdentifier, message ), WORMHOLE_CONSISTENCY ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Address } from "openzeppelin/utils/Address.sol"; import { IIncentivizedMessageEscrow } from "./interfaces/IIncentivizedMessageEscrow.sol"; import { ICrossChainReceiver } from "./interfaces/ICrossChainReceiver.sol"; import { Bytes65 } from "./utils/Bytes65.sol"; import { CTX_SOURCE_TO_DESTINATION, CTX_DESTINATION_TO_SOURCE, CTX_TIMEDOUT_ON_DESTINATION } from "./MessagePayload.sol"; import "./MessagePayload.sol"; /** * @title Generalised Incentive Escrow * @author Cata labs Inc. * @notice Main logic for placing transparent incentives on message relaying. * This contract is intended to sit between an application and a cross-chain message protocol. * The goal is to overload the existing incentive scheme with one that is open to anyone. * * Each messaging protocol will have a respective implementation that understands how to send * and verify messages. There are 4 functions that an integration has to implement. * Any implementation of this contract, allows applications to deliver a message to ::submitMessage * along with the respective incentives. * The integration (this contract) will handle transfering the message to the destination and * returning an ack from the destination to the integrating application. * * The incentive is released when an ack from the destination chain is delivered to this contract. * * Beyond making relayer incentives stronger, this contract also implements several quality of life features: * - Refund unused gas. * - Seperate gas payments for call and ack. * - Simple implementation of new messaging protocols. * * Applications integration with Generalised Incentives have to be aware that Acks are replayable. */ abstract contract IncentivizedMessageEscrow is IIncentivizedMessageEscrow, Bytes65 { //--- Constants ---// /** * @notice If the message reverts on the destination chain, * 1 byte is prepended to the original message on ack. This is the byte. */ bytes1 constant MESSAGE_REVERTED = 0xff; /** * @notice If the original sender is not authorised on the application on the destination chain, * 1 byte is prepended to the original message on ack. This is the byte. */ bytes1 constant NO_AUTHENTICATION = 0xfe; /** * @notice If the message timed out on destination chain, * 1 byte is prepended to the original message on ack. This is the byte. */ bytes1 constant MESSAGE_TIMED_OUT = 0xfd; /** * @notice If setRemoteImplementation is called with this as the destination implementation * (abi.encodePacked(DISABLE_ROUTE_IMPLEMENTATION)), then the route is permanently disabled * This is treated as a magic value so that incase an implementations treats abi.encodePacked(0x00) * as a valid destination (say address(0)). */ bytes1 constant DISABLE_ROUTE_IMPLEMENTATION = 0x00; /** * @notice If a relayer or application provides an address that cannot accept gas and * the transfer fails the gas is sent here instead. * @dev This may not invoke any logic on receive() or be a proxy. */ address immutable public SEND_LOST_GAS_TO; //--- Storage ---// /** * @notice Get incentive description based on message context: messageIdentifier, fromApplication, and destChain * @dev fromApplication and destChain are required to fetch the bounty. Together they * match exactly 1 remote escrow implementation. As a result, this restricts the storage * slot's security to the security of the specified remote escrow implementation. */ mapping(address fromApplication => mapping(bytes32 destChain => mapping(bytes32 messageIdentifier => IncentiveDescription))) _bounty; /** @notice A hash of the emitted message on receive such that we can emit a similar one. */ mapping(bytes32 => mapping(bytes => mapping(bytes32 => bytes32))) _messageDelivered; // Maps applications to their escrow implementations. mapping(address => mapping(bytes32 => bytes)) public implementationAddress; mapping(address => mapping(bytes32 => bytes32)) public implementationAddressHash; //--- Virtual Functions ---// // To integrate a messaging protocol, a contract has to inherit this contract and implement 4 functions below. /** * @notice Verifies the authenticity of a message. * @dev Should be overwritten by the specific messaging protocol verification structure. * onRecv. implementations should collect acks so _verifyPacket returns true after acks have been executed once. * @param messagingProtocolContext Some context that is useful for verifing the message. * It should not contain the message but instead verification context like signatures, header, etc. * Context may not be needed for verifying the message and can be prepended to rawMessage. * @param rawMessage Some kind of package, initially untrusted. Should contain the message as a slice * It may contain more than just the message, like signatures, headers, etc. * * @return sourceIdentifier The source chain identifier. A chainID, a channel ID, or similarly. * @return implementationIdentifier An identifier for the address that emitted the message. * @return message The emitted message as a calldata slice. Should not contain anything AMB specific * and should be the exact message as delivered to _sendPacket */ function _verifyPacket( bytes calldata messagingProtocolContext, bytes calldata rawMessage ) virtual internal returns( bytes32 sourceIdentifier, bytes memory implementationIdentifier, bytes calldata message ); /** * @notice Deliver the message to the messaging protocol to generate a proof. * @dev Should be overwritten to send a message using the specific messaging protocol. * The function is allowed to claim native tokens (set costOfsendPacketInNativeToken). * The function is allowed to take ERC20 tokens (transferFrom(msg.sender,...)) * in which case set costOfsendPacketInNativeToken to 0. * @param destinationIdentifier The destination chain for the message. * @param destinationImplementation The destination escrow contract. * @param message The message. Contains relevant escrow context. * @param deadline A timestamp that the message should be delivered before. If the AMB does not nativly * support a timeout on their messages this parameter should be ignored. If 0 is provided, parse it as MAX. * @return costOfsendPacketInNativeToken An additional cost to emitting messages in NATIVE tokens. */ function _sendPacket( bytes32 destinationIdentifier, bytes memory destinationImplementation, bytes memory message, uint64 deadline ) virtual internal returns(uint128 costOfsendPacketInNativeToken); /** * @notice A unique source identifier used to generate the message identifier. * @dev Should generally be the same as the one set by the AMB such that we can verify messages with this identifier */ function _uniqueSourceIdentifier() virtual internal view returns(bytes32 sourceIdentifier); /** * @notice The duration for which a proof is valid for. It may vary by destination. * @dev On checks, block.timestamp is added to the return of this function such that * block.timestamp + _proofValidPeriod > deadline. * If any deadline is valid, set to **0** instead of ~~type(uint64).max~~. * @param destinationIdentifier The destination chain identifier. * @return duration The maximum proof duration. Includes the time from message emitted (proof gen) * to message finalised. */ function _proofValidPeriod(bytes32 destinationIdentifier) virtual internal view returns(uint64 duration); /** * @notice The duration for which a proof is valid for. * @dev On checks, block.timestamp is added to the return of this function such that * block.timestamp + _proofValidPeriod > deadline. * If 0, implies that any deadline is valid. * @param destinationIdentifier The destination chain identifier. * @return duration The maximum proof duration. Includes the time from message emitted (proof gen) * to message finalised. */ function proofValidPeriod(bytes32 destinationIdentifier) external view returns(uint64 duration) { return duration = _proofValidPeriod(destinationIdentifier); } /** * @param sendLostGasTo It should only be set to an EOA or a contract that has no logic on receive nor be a proxy. * If no-one wants to take responsibility of the lost Ether, use a burn address (0xdead) instead of address(0). */ constructor(address sendLostGasTo) { if (sendLostGasTo == address(0)) revert SendLostGasToIsZero(); SEND_LOST_GAS_TO = sendLostGasTo; } /** * @notice Generates a unique message identifier for a message * @dev The identifier should: * - Be unique within a trusted ecosystem. * - Be unique based on the sender such that applications can't be DoS'ed. * - Contain the deadline for timeout validation. * - Be unique over time: Use blocknumber or blockhash. * - Be unique on the source chain: Use a unique destinationIdentifier. * - Be unique on destination chain: Use a unique source identifier. * - Depend on the message, for uniqueness & for timeouts. * Point 2 implies that applications are allowed to DoS themselves. To protect against this, applications * should include some user unique information, say include a user address. */ function _getMessageIdentifier( address messageSender, uint64 deadline, uint256 blockNumber, bytes32 sourceIdentifier, bytes32 destinationIdentifier, bytes memory message ) view internal virtual returns(bytes32) { return keccak256( bytes.concat( bytes20(address(this)), bytes20(messageSender), bytes8(deadline), bytes32(blockNumber), sourceIdentifier, destinationIdentifier, message ) ); } /** * @notice Generates a unique message identifier for a message * @dev Simplifies using _getMessageIdentifier raw by assuming what the inputs * to the other parameters should be. */ function _getMessageIdentifier( uint64 deadline, bytes32 destinationIdentifier, bytes calldata message ) view internal virtual returns(bytes32) { return _getMessageIdentifier( msg.sender, deadline, block.number, _uniqueSourceIdentifier(), destinationIdentifier, message ); } //--- Getter Functions ---// /** * @notice Returns the bounty associated with a specific messageIdentifier. * @param fromApplication The application that submitted the message. * @param destinationIdentifier The destination chain for the message. * @param messageIdentifier The message identifier for a specific bounty. * @return incentive The message incentive / bounty as read from memory. If refundGasTo is address(0), it has been claimed. */ function bounty(address fromApplication, bytes32 destinationIdentifier, bytes32 messageIdentifier) external view returns(IncentiveDescription memory incentive) { return _bounty[fromApplication][destinationIdentifier][messageIdentifier]; } /** * @notice Get the message statue through the an ack hash. * If the message hasn't been delivered yet it returns bytes32(0) * @param sourceIdentifier The source chain the message was emitted from. * @param sourceImplementationIdentifier The source escrow implementation that emitted the message. * @param messageIdentifier The message identifier of the message. * @return hasMessageBeenExecuted Hash of the ack message. If not ack, then bytes32(uint256(1)). If not * executed then bytes32(0). */ function messageDelivered( bytes32 sourceIdentifier, bytes calldata sourceImplementationIdentifier, bytes32 messageIdentifier ) external view returns(bytes32 hasMessageBeenExecuted) { return _messageDelivered[sourceIdentifier][sourceImplementationIdentifier][messageIdentifier]; } /** * @notice Sets the escrow implementation for a specific chain * @dev This can only be set once. When set, it cannot be changed. * This is to protect relayers as this could be used to fail acks. * @param destinationIdentifier An identifier for the destination chain. Varies from AMB. * @param implementation Implementation address. Encoding varies between AMBs. * You are not allowed to set a 0 length implementation address. Beaware that while some implementations * are valid for sending (say hex"0x01" which may be read as address(uint160(1))), they break acks / delivery. * If you want to disable a specific route, set implementation to hex"00" (DISABLE_ROUTE_IMPLEMENTATION). */ function setRemoteImplementation(bytes32 destinationIdentifier, bytes calldata implementation) external virtual { if (implementationAddressHash[msg.sender][destinationIdentifier] != bytes32(0)) revert ImplementationAddressAlreadySet( implementationAddress[msg.sender][destinationIdentifier] ); if (implementation.length == 0) revert NoImplementationAddressSet(); implementationAddress[msg.sender][destinationIdentifier] = implementation; bytes32 _implementationHash = keccak256(implementation); implementationAddressHash[msg.sender][destinationIdentifier] = _implementationHash; emit RemoteImplementationSet(msg.sender, destinationIdentifier, _implementationHash, implementation); } //--- Public Endpoints ---// /** * @notice Increases the bounty for relaying messages * @dev It is not possible to increase the gas budget for a message. * The increase should be paid and is generally: * incentive.maxGasDelivery * deliveryGasPriceIncrease + incentive.maxGasAck * ackGasPriceIncrease * Value has to be provided exact. * * @param messageIdentifier The message identifier of the message to increase the bounty price for. * @param deliveryGasPriceIncrease The INCREASE in the gas price of the delivery gas * @param ackGasPriceIncrease The INCREASE in the gas price of ack gas. */ function increaseBounty( address fromApplication, bytes32 destinationIdentifier, bytes32 messageIdentifier, uint96 deliveryGasPriceIncrease, uint96 ackGasPriceIncrease ) external payable { // Find incentive scheme. IncentiveDescription storage incentive = _bounty[fromApplication][destinationIdentifier][messageIdentifier]; if (incentive.refundGasTo == address(0)) revert MessageDoesNotExist(); // Compute incentive metrics. uint128 maxDeliveryFee = incentive.maxGasDelivery * deliveryGasPriceIncrease; uint128 maxAckFee = incentive.maxGasAck * ackGasPriceIncrease; uint128 sum = maxDeliveryFee + maxAckFee; // Check that the provided gas is exact if (msg.value != sum) revert IncorrectValueProvided(sum, uint128(msg.value)); uint96 newPriceOfDeliveryGas = incentive.priceOfDeliveryGas + deliveryGasPriceIncrease; uint96 newPriceOfAckGas = incentive.priceOfAckGas + ackGasPriceIncrease; // Update storage. incentive.priceOfDeliveryGas = newPriceOfDeliveryGas; incentive.priceOfAckGas = newPriceOfAckGas; // Emit the event with the increased values. emit BountyIncreased( messageIdentifier, newPriceOfDeliveryGas, newPriceOfAckGas ); } /** * @notice Set a bounty on a message and transfer the message to the messaging protocol. * @dev Called by applications. These application should ensure: * 1. incentive.maxGasAck is sufficient! Otherwise, an off-chain agent needs to re-submit the ack. * 2. incentive.maxGasDelivery is sufficient. Otherwise, the call will fail within the try - catch. * 3. The relay incentive is enough to get the message relayed within the expected time. If that is never, this check is not needed. * Furthermore, if the package times out there is no gas refund. * Sending too much value results in the excess being refunded to refundGasTo via a call. This may allow refundGasTo to throw the call. * @param destinationIdentifier 32 bytes that identifies the destination chain. * @param destinationAddress The destination application encoded in 65 bytes: First byte is the length and last 64 is the destination application. * @param message The message to be sent to the destination. Please ensure the message is block-unique. * This means that you don't send the same message twice in a single block. If you need to do that, add a nonce or noice. * @param incentive The incentive to attatch to the bounty. The price of this incentive has to be paid, * any excess is refunded to refundGasTo. (not msg.sender) * @param deadline After this date, do not allow relayers to execute the message on the destination chain. If set to 0, disable timeouts. * Not all AMBs may support disabling the deadline. If acks are required it is recommended to set the deadline sometime in the future. * Note, it may still take a significant amount of time to bring back the timeout. * @return gasRefund The amount of excess gas that was paid to this call. The app should handle the excess. * @return messageIdentifier An unique identifier for a message. */ function submitMessage( bytes32 destinationIdentifier, bytes calldata destinationAddress, bytes calldata message, IncentiveDescription calldata incentive, uint64 deadline ) checkBytes65Address(destinationAddress) external payable returns(uint256 gasRefund, bytes32 messageIdentifier) { // Valid refund to. if (incentive.refundGasTo == address(0)) revert RefundGasToIsZero(); // Check that the application has set a destination implementation by checking if the length of the destinationImplementation entry is not 0. bytes memory destinationImplementation = implementationAddress[msg.sender][destinationIdentifier]; if (destinationImplementation.length == 0) revert NoImplementationAddressSet(); if (destinationImplementation.length == 1 && destinationImplementation[0] == DISABLE_ROUTE_IMPLEMENTATION) revert RouteDisabled(); // Check that the deadline is lower than the AMB specification. unchecked { // Timestamps do not overflow in uint64 within reason. uint64 ambMaxDeadline = _proofValidPeriod(destinationIdentifier); if (ambMaxDeadline != 0 && deadline == 0) revert DeadlineTooLong(ambMaxDeadline, 0); if (ambMaxDeadline != 0 && deadline > uint64(block.timestamp) + ambMaxDeadline) revert DeadlineTooLong(uint64(block.timestamp) + ambMaxDeadline, deadline); // Check that the deadline is in the future = not (block.timestamp < deadline) = block.timestamp >= deadline. if (deadline != 0 && block.timestamp >= deadline) revert DeadlineInPast(uint64(block.timestamp), deadline); } // Prepare to store incentive messageIdentifier = _getMessageIdentifier( deadline, destinationIdentifier, message ); // Store the bounty, get the sum to refunding excess later. uint128 sum = _setBounty(msg.sender, destinationIdentifier, messageIdentifier, incentive); // Add escrow context to the message. bytes memory messageWithContext = bytes.concat( bytes1(CTX_SOURCE_TO_DESTINATION), // This is a sendPacket intended for the _destination_ bytes32(messageIdentifier), // A unique message identifier. convertEVMTo65(msg.sender), // Original sender / application. destinationAddress, // The address to deliver the provided message to. bytes8(uint64(deadline)), bytes6(incentive.maxGasDelivery), // The delivery gas limit, to enforce on the destination. message // The message to deliver to the destination. ); // Emit the event for off-chain relayers. emit BountyPlaced( destinationImplementation, destinationIdentifier, messageIdentifier, incentive ); // Bounty is emitted before event to standardized with the other event before sending message scheme. // Send message to messaging protocol // This call will collect payments for sending the message. It can be in any token but if it is in // native gas, it should return the amount it took. uint128 costOfsendPacketInNativeToken = _sendPacket( destinationIdentifier, destinationImplementation, messageWithContext, deadline ); // Add the cost of the send message. sum += costOfsendPacketInNativeToken; // Check that the provided gas is sufficient. The refund will be sent later. if (msg.value < sum) revert NotEnoughGasProvided(sum, uint128(msg.value)); // Return excess incentives to the user (found from incentive.refundGasTo). unchecked { if (msg.value > sum) { // We know: msg.value > sum, thus msg.value - sum > 0. gasRefund = msg.value - sum; Address.sendValue(payable(incentive.refundGasTo), uint256(gasRefund)); return (gasRefund, messageIdentifier); } } return (0, messageIdentifier); } /** * @notice Deliver a message & proof of validity. * @dev This function is intended to be called by off-chain agents. * Please ensure that feeRecipient can receive gas token: EOA or implements fallback() / receive() with no logic. * Likewise for any non-evm chains. Otherwise the message fails (ack) or the relay payment is lost (deliver). * You need to pass in incentive.maxGas(Delivery|Ack) + messaging protocol dependent buffer, otherwise this call might fail. * * OnReceive implementations should make _verifyPacket revert. The result is that this function is disabled. * Though this function is _virtual_ so feel free to override. * @param messagingProtocolContext Additional context required to verify the message by the messaging protocol. * @param rawMessage A messaging protocol message, including the message as it was sent as a slice. * @param feeRecipient An identifier for the the fee recipient. The identifier should identify the relayer on the source chain. * For EVM (and this contract as a source), use the bytes32 encoded address. For other VMs you might have to register your address. */ function processPacket( bytes calldata messagingProtocolContext, bytes calldata rawMessage, bytes32 feeRecipient ) external virtual payable { uint256 gasLimit = gasleft(); // uint256 is used here instead of uint48, since there is no advantage to uint48 until after we calculate the difference. if (feeRecipient == bytes32(0)) revert FeeRecipientIsZero(); // Verify that the message is authentic and remove potential context that the messaging protocol added to the message. (bytes32 chainIdentifier, bytes memory implementationIdentifier, bytes calldata message) = _verifyPacket(messagingProtocolContext, rawMessage); // Figure out if this is deliver or ack. uint128 cost = 0; bytes1 context = bytes1(message[0]); if (context == CTX_SOURCE_TO_DESTINATION) { bytes memory receiveAckWithContext = _handleMessage(chainIdentifier, implementationIdentifier, message, feeRecipient, gasLimit); // The cost management is made by _sendPacket so we don't have to check if enough gas has been provided. cost = _sendPacket(chainIdentifier, implementationIdentifier, receiveAckWithContext, 0); } else if (context == CTX_DESTINATION_TO_SOURCE) { // Notice that sometimes ack actually handles deadlines which have been passed. // However, these are much different from "timeouts". _handleAck(chainIdentifier, implementationIdentifier, message, feeRecipient, gasLimit); } else if (context == CTX_TIMEDOUT_ON_DESTINATION) { // Verify that the timeout is valid. // Anyone is able to not only get here but also control the inputs to this code section // This logic is not protected by us controlling the logic for the roundtrip. // Instead, we need to authenticate the whole message: (bytes32 messageIdentifier, address fromApplication, bytes calldata applicationMessage) = _verifyTimeout(chainIdentifier, implementationIdentifier, message); // Now that we have the verified the inputs, we can actually use them. Execute the timeout: _handleTimeout(chainIdentifier, implementationIdentifier, messageIdentifier, fromApplication, applicationMessage, feeRecipient, gasLimit); } else { revert NotImplementedError(); } // Check if there is a mis-match between the cost and the value of the message. if (uint128(msg.value) != cost) { if (uint128(msg.value) > cost) { // Send the unused gas back to the the user. Address.sendValue(payable(msg.sender), msg.value - uint256(cost)); return; } // => uint128(msg.value) < cost, so revert. revert NotEnoughGasProvided(uint128(msg.value), cost); } } //--- Internal Functions ---// /** * @notice Handles messages deliveries (SOURCE_TO_DESTINATION) */ function _handleMessage( bytes32 sourceIdentifier, bytes memory sourceImplementationIdentifier, bytes calldata message, bytes32 feeRecipient, uint256 gasLimit ) internal returns(bytes memory receiveAckWithContext) { // Ensure message is unique and can only be execyted once bytes32 messageIdentifier = bytes32(message[MESSAGE_IDENTIFIER_START:MESSAGE_IDENTIFIER_END]); // The 3 next lines act as a reentry guard, so this call doesn't have to be protected by reentry. // We will re-set _messageDelivered[messageIdentifier] again later as the hash of the ack, however, we need re-entry protection // so applications don't try to claim incentives multiple times. bytes32 messageState = _messageDelivered[sourceIdentifier][sourceImplementationIdentifier][messageIdentifier]; if (messageState != bytes32(0)) revert MessageAlreadySpent(); // This is where the "magic-byte" of bytes32(uint256(1)) comes from. Generally, it should always be overwritten by // an ack hash. _messageDelivered[sourceIdentifier][sourceImplementationIdentifier][messageIdentifier] = bytes32(uint256(1)); // Prepare to deliver the message to application. // We need toApplication to check if the source implementation is valid // and we opportunistic decode fromApplication since it is needed in all cases. address toApplication = address(bytes20(message[CTX0_TO_APPLICATION_START_EVM:CTX0_TO_APPLICATION_END])); bytes calldata fromApplication = message[FROM_APPLICATION_LENGTH_POS:FROM_APPLICATION_END]; // Check if the message is valid. This includes: // - Checking if the sender is valid. // - Checking if the message has expired. bytes memory acknowledgement; bytes32 expectedSourceImplementationHash = implementationAddressHash[toApplication][sourceIdentifier]; // Check that the application allows the source implementation. // This is not the case when another implementation calls this contract from the source chain. // This could be a mistake, send back an ack with the relevant information. if (expectedSourceImplementationHash != keccak256(sourceImplementationIdentifier)) { // If they are different, return send a failed message back with NO_AUTHENTICATION. acknowledgement = bytes.concat( NO_AUTHENTICATION, message[CTX0_MESSAGE_START: ] ); // Encode a new message to send back. This lets the relayer claim their payment. receiveAckWithContext = bytes.concat( bytes1(CTX_DESTINATION_TO_SOURCE), // Context messageIdentifier, // message identifier fromApplication, feeRecipient, bytes6(uint48(gasLimit - gasleft())), // Delay the gas limit computation until as late as possible. This should include the majority of gas spent. bytes8(uint64(block.timestamp)), // If this overflows, it is fine. It is used in conjunction with a delta. acknowledgement ); // Store a hash of the acknowledgement so we can later retry the ack if the ack proofs expires / becomes invalid. _messageDelivered[sourceIdentifier][sourceImplementationIdentifier][messageIdentifier] = keccak256(receiveAckWithContext); // Message has been delivered and shouldn't be executed again. emit MessageDelivered(sourceImplementationIdentifier, sourceIdentifier, messageIdentifier); return receiveAckWithContext; } // Check that if the deadline has been set (deadline != 0). If the deadline has been set, // check if the current timestamp is beyond the deadline and return MESSAGE_TIMED_OUT if it is. uint64 deadline = uint64(bytes8(message[CTX0_DEADLINE_START:CTX0_DEADLINE_END])); if (deadline != 0 && deadline < block.timestamp) { acknowledgement = bytes.concat( MESSAGE_TIMED_OUT, message[CTX0_MESSAGE_START: ] ); // Encode a new message to send back. This lets the relayer claim their payment. // Incase of timeouts, we give all of the gas. receiveAckWithContext = bytes.concat( bytes1(CTX_DESTINATION_TO_SOURCE), // Context messageIdentifier, // message identifier fromApplication, feeRecipient, bytes6(uint48(2**47)), // We set the gas spent as max. Why 2**47 instead of maxGasDelivery? 2**47 is only 1 high bit and it saves gas. Furthermore, min(2**47, maxGasDelivery) is checked on the source. bytes8(uint64(block.timestamp)), // If this overflows, it is fine. It is used in conjunction with a delta. acknowledgement ); // Store a hash of the acknowledgement so we can later retry a potentially invalid ack proof. _messageDelivered[sourceIdentifier][sourceImplementationIdentifier][messageIdentifier] = keccak256(receiveAckWithContext); // Message has been delivered and shouldn't be executed again. emit MessageDelivered(sourceImplementationIdentifier, sourceIdentifier, messageIdentifier); return receiveAckWithContext; } // Load the max gas. uint48 maxGas = uint48(bytes6(message[CTX0_MAX_GAS_LIMIT_START:CTX0_MAX_GAS_LIMIT_END])); // Execute call to application. Gas limit is set explicitly to ensure enough gas has been sent. // This call might fail because the abi.decode of the return value can fail. It is too gas costly to check co full correctness // of the returned value and then error if decoding is not possible. // As a result, relayers needs to simulate the tx. If the call fails, then they should blacklist the message. // The call will only fall if the application doesn't expose receiveMessage or captures the message via a fallback. // As a result, if message delivery once executed, then it will always execute. try ICrossChainReceiver(toApplication).receiveMessage{gas: maxGas}(sourceIdentifier, messageIdentifier, fromApplication, message[CTX0_MESSAGE_START: ]) returns (bytes memory ack) { acknowledgement = ack; } catch (bytes memory /* err */) { // Check that enough gas was provided to the application. For further documentation of this statement, check // the long description on ack. TLDR: The relayer can cheat the application by providing less gas // but this statement ensures that if they try to do that, then it will fail (assuming the application reverts). if(gasleft() < maxGas * 1 / 63) revert NotEnoughGasExecution(); // Send the message back if the execution failed. // This lets you store information in the message that you can trust gets returned. // (You just have to understand that the status is appended as the first byte.) acknowledgement = bytes.concat( MESSAGE_REVERTED, message[CTX0_MESSAGE_START: ] ); } // Encode a new message to send back. This lets the relayer claim their payment. receiveAckWithContext = bytes.concat( bytes1(CTX_DESTINATION_TO_SOURCE), // Context messageIdentifier, // message identifier fromApplication, feeRecipient, bytes6(uint48(gasLimit - gasleft())), // Delay the gas limit computation until as late as possible. This should include the majority of gas spent. bytes8(uint64(block.timestamp)), // If this overflows, it is fine. It is used in conjunction with a delta. acknowledgement ); // Store a hash of the acknowledgement so we can later retry a potentially invalid ack proof. _messageDelivered[sourceIdentifier][sourceImplementationIdentifier][messageIdentifier] = keccak256(receiveAckWithContext); // Why is the messageDelivered event emitted before _sendPacket? // Because it lets us pop messageIdentifier from the stack. This avoid a stack limit reached error. // Not optimal but okay-ish. // Emit event to inform relayers that the message has been delivered. emit MessageDelivered(sourceImplementationIdentifier, sourceIdentifier, messageIdentifier); // Send message to messaging protocol in processMessage. return receiveAckWithContext; } /** * @notice Handles ack messages (DESTINATION_TO_SOURCE). */ function _handleAck( bytes32 destinationIdentifier, bytes memory destinationImplementationIdentifier, bytes calldata message, bytes32 feeRecipient, uint256 gasLimit ) internal { // Ensure the bounty can only be claimed once. bytes32 messageIdentifier = bytes32(message[MESSAGE_IDENTIFIER_START:MESSAGE_IDENTIFIER_END]); address fromApplication = address(bytes20(message[FROM_APPLICATION_START_EVM:FROM_APPLICATION_END])); // The 3 (9, loading the variables out of storage fills a bit.) next lines act as a reentry guard, // so this call doesn't have to be protected by reentry. IncentiveDescription storage incentive = _bounty[fromApplication][destinationIdentifier][messageIdentifier]; // Load all variables from storage onto the stack. uint48 maxGasDelivery = incentive.maxGasDelivery; uint48 maxGasAck = incentive.maxGasAck; address refundGasTo = incentive.refundGasTo; uint96 priceOfDeliveryGas = incentive.priceOfDeliveryGas; uint96 priceOfAckGas = incentive.priceOfAckGas; uint64 targetDelta = incentive.targetDelta; // Ensure the bounty can only be claimed once. This call is matched on the timeout side, // so it also ensures that an ack cannot be delivered if a timeout has been seen. if (refundGasTo == address(0)) revert MessageAlreadyAcked(); delete _bounty[fromApplication][destinationIdentifier][messageIdentifier]; // The bounty cannot be accessed anymore. // First check if the application trusts the implementation on the destination chain. bytes32 expectedDestinationImplementationHash = implementationAddressHash[fromApplication][destinationIdentifier]; // Check that the application approves the source implementation // For acks, this should always be the case except when a fradulent applications sends a message to this contract. if (expectedDestinationImplementationHash != keccak256(destinationImplementationIdentifier)) revert InvalidImplementationAddress(); // Deliver the ack to the application. // Ensure that if the call reverts it doesn't boil up. // We don't need any return values and don't care if the call reverts. // This call implies we need reentry protection. bytes memory payload = abi.encodeWithSignature("receiveAck(bytes32,bytes32,bytes)", destinationIdentifier, messageIdentifier, message[CTX1_MESSAGE_START: ]); bool success; assembly ("memory-safe") { // Because Solidity always create RETURNDATACOPY for external calls, even low-level calls where no variables are assigned, // the contract can be attacked by a so called return bomb. This incur additional cost to the relayer they aren't paid for. // To protect the relayer, the call is made in inline assembly. success := call(maxGasAck, fromApplication, 0, add(payload, 0x20), mload(payload), 0, 0) // This is what the call would look like non-assembly. // fromApplication.call{gas: maxGasAck}( // abi.encodeWithSignature("receiveAck(bytes32,bytes32,bytes)", destinationIdentifier, messageIdentifier, message[CTX1_MESSAGE_START: ]) // ); } // External calls are allocated gas according roughly the following: min( gasleft * 63/64, gasArg ). // If there is no check against gasleft, then a relayer could potentially cheat by providing less gas. // Without a check, they only have to provide enough gas such that any further logic executees on 1/64 of gasleft // To ensure maximum compatibility with external tx simulation and gas estimation tools we will check a more complex // but more forgiving expression. // Before the call, there needs to be at least maxGasAck * 64/63 gas available. With that available, then // the call is allocated exactly min(+(maxGasAck * 64/63 * 63/64), maxGasAck) = maxGasAck. // If the call uses up all of the gas, then there must be maxGasAck * 64/63 - maxGasAck = maxGasAck * 1/63 // gas left. It is sufficient to check that smaller limit rather than the larger limit. // Furthermore, if we only check when the call failed we don't have to read gasleft if it is not needed. unchecked { if (!success) if(gasleft() < maxGasAck * 1 / 63) revert NotEnoughGasExecution(); } // Why is this better (than checking before)? // 1. We only have to check when the call failed. The vast majority of acks should not revert so it won't be checked. // 2. For the majority of applications it is going to be hold that: gasleft > rest of logic > maxGasAck * 1 / 63 // and as such won't impact and execution/gas simuatlion/estimation libs. // Why is this worse? // 1. What if the application expected us to check that it got maxGasAck? It might assume that it gets // maxGasAck, when it turns out it got less it silently reverts (say by a low level call ala ours). // Get the gas used by the destination call. uint48 gasSpentOnDestination = uint48(bytes6(message[CTX1_GAS_SPENT_START:CTX1_GAS_SPENT_END])); (uint256 gasSpentOnSource, uint256 deliveryFee, uint256 ackFee) = _payoutIncentive( gasLimit, gasSpentOnDestination, maxGasDelivery, priceOfDeliveryGas, maxGasAck, priceOfAckGas, refundGasTo, address(uint160(uint256(bytes32(message[CTX1_RELAYER_RECIPIENT_START:CTX1_RELAYER_RECIPIENT_END])))), address(uint160(uint256(feeRecipient))), targetDelta, uint64(bytes8(message[CTX1_EXECUTION_TIME_START:CTX1_EXECUTION_TIME_END])) ); emit MessageAcked(destinationImplementationIdentifier, destinationIdentifier, messageIdentifier); emit BountyClaimed( destinationImplementationIdentifier, destinationIdentifier, messageIdentifier, uint64(gasSpentOnDestination), uint64(gasSpentOnSource), uint128(deliveryFee), uint128(ackFee) ); } /** * @notice Handles timeout messages. * @dev This function is very light. That is because it is intended to be used for both: * 1. Handling authentic timeouts from some AMBs. * 2. Handling returning timedout functions. * Before calling, check if the destination implementation is correct. */ function _handleTimeout( bytes32 destinationIdentifier, bytes memory destinationImplementationIdentifier, bytes32 messageIdentifier, address fromApplication, bytes calldata applicationMessage, bytes32 feeRecipient, uint256 gasLimit ) internal { // The 3 (9, loading the variables out of storage fills a bit.) next lines act as a reentry guard, // so this call doesn't have to be protected by reentry. IncentiveDescription storage incentive = _bounty[fromApplication][destinationIdentifier][messageIdentifier]; // Load all variables from storage onto the stack. uint48 maxGasDelivery = incentive.maxGasDelivery; uint48 maxGasAck = incentive.maxGasAck; address refundGasTo = incentive.refundGasTo; uint96 priceOfDeliveryGas = incentive.priceOfDeliveryGas; uint96 priceOfAckGas = incentive.priceOfAckGas; // Ensure the bounty can only be claimed once. This call is matched on the ack side, // so it also ensures that an ack cannot be delivered if a timeout has been seen. if (refundGasTo == address(0)) revert MessageAlreadyAcked(); delete _bounty[fromApplication][destinationIdentifier][messageIdentifier]; // The bounty cannot be accessed anymore. // We delegate checking the if the destination implementation is correct to outside this contract. // This is done such that this function can be as light as possible. // Deliver the ack to the application. // Ensure that if the call reverts it doesn't boil up. // We don't need any return values and don't care if the call reverts. // This call implies we need reentry protection. bytes memory payload = abi.encodeWithSignature("receiveAck(bytes32,bytes32,bytes)", destinationIdentifier, messageIdentifier, bytes.concat(MESSAGE_TIMED_OUT, applicationMessage)); bool success; assembly ("memory-safe") { // Because Solidity always create RETURNDATACOPY for external calls, even low-level calls where no variables are assigned, // the contract can be attacked by a so called return bomb. This incur additional cost to the relayer they aren't paid for. // To protect the relayer, the call is made in inline assembly. success := call(maxGasAck, fromApplication, 0, add(payload, 0x20), mload(payload), 0, 0) // This is what the call would look like non-assembly. // fromApplication.call{gas: maxGasAck}( // abi.encodeWithSignature("receiveAck(bytes32,bytes32,bytes)", destinationIdentifier, messageIdentifier, abi.encodePacked(bytes1(0xfd), message[CTX0_MESSAGE_START: ])) // ); } // Check that enough gas was provided to the application. For further documentation of this statement, check // the long description on ack. TLDR: The relayer can cheat the application by providing less gas // but this statement ensures that if they try to do that, then it will fail (assuming the application reverts). unchecked { if (!success) if(gasleft() < maxGasAck * 1 / 63) revert NotEnoughGasExecution(); } (uint256 gasSpentOnSource, uint256 deliveryFee, uint256 ackFee) = _payoutIncentive( gasLimit, maxGasDelivery, // We set gas spent on destination as the entire allowance. maxGasDelivery, priceOfDeliveryGas, maxGasAck, priceOfAckGas, refundGasTo, address(uint160(uint256(feeRecipient))), address(uint160(uint256(feeRecipient))), 0, // Disable target delta, since there is only 1 relayer. 0 ); emit MessageTimedOut(destinationImplementationIdentifier, destinationIdentifier, messageIdentifier); emit BountyClaimed( destinationImplementationIdentifier, destinationIdentifier, messageIdentifier, uint64(maxGasDelivery), uint64(gasSpentOnSource), uint128(deliveryFee), uint128(ackFee) ); } /** * @notice Verifies the input parameters are contained messageIdentfier and that the other arguments are valid. * The usage of this function is intended when no parameters of a message can be trusted and we have to verify them. * This is the case when we receive a timeout, as the timeout had to be emitted without any verification * on the remote chain, for us to then verify since we know when a message identifier is good AND how to compute it. * * @dev This function uses the fact that hash(a) == hash(b) IFF a == b. So if someone proposes b, we have hash(a) * then we can check if b == a by hashing b and comparing to a. * a is the initial state when the message was initiated and b is the proposed state from the timeout. * * When hash(a) is the message identifier, this allows us to verify authenticity by: * 1. The package is correctly formatted. The data within matches the message identifier. * 2. The remote implementation can verify that no package has previously been executed with that message identifier. * 3. If we check if the message identifier has a bounty, we must have emitted that message. (in _handleTimeout) */ function _verifyTimeout( bytes32 destinationIdentifier, bytes memory implementationIdentifier, bytes calldata message ) internal view returns(bytes32 messageIdentifier, address fromApplication, bytes calldata applicationMessage) { // First check if the application trusts the implementation on the destination chain. This is very important // since the remote implementation NEEDS to check that the message hasn't been executed before the deadline // and if the message get relayed post deadline, then it should never arrive at the application. // Without those checks the whole concept of timeouts doesn't matter fromApplication = address(uint160(bytes20(message[FROM_APPLICATION_START_EVM:FROM_APPLICATION_END]))); bytes32 expectedDestinationImplementationHash = implementationAddressHash[fromApplication][destinationIdentifier]; // Check that the application approves of the remote implementation. // For timeouts, this could fail because of fradulent sender or bad data. if (expectedDestinationImplementationHash != keccak256(implementationIdentifier)) revert InvalidImplementationAddress(); // Do we need to check deadline again? // Considering the cost: cheap, we will do it. In most instances it is not needed. // This is because we must expect the remote implementation to also do the check to save gas // since it is an obvious and valid check on the remote. uint64 deadline = uint64(bytes8(message[CTX2_DEADLINE_START:CTX2_DEADLINE_END])); if (deadline >= block.timestamp || deadline == 0) revert DeadlineNotPassed(deadline, uint64(block.timestamp)); // The entirety of the incoming message is untrusted. So far we havn't done any verification of // the message but rather of the origin of the message. // As a result, we need to verify the rest of the message, specifically: // - MESSAGE_IDENTIFIER // - FROM_APPLICATION \ // - DEADLINE \ // - ORIGIN_BLOCK_NUMBER > Message Identifier // - SOURCE_IDENTIFIER / // - MESSAGE / // We need to verify that the message identifier sent is correct because the messageIdentifier // should have been used to check if the message would have been executed before. // We can check if the message identifier is correct by checking the bounty. (See _handleTimeout) // However, we still need to check the rest of the information. The message identifier has been crafted // so it is dependent on the rest of the information we use. applicationMessage = message[CTX2_MESSAGE_START: ]; // Lets compute the messageIdentifier based on the message. bytes32 computedMessageIdentifier = _getMessageIdentifier( fromApplication, deadline, uint256(bytes32(message[CTX2_ORIGIN_BLOCK_NUMBER_START:CTX2_ORIGIN_BLOCK_NUMBER_END])), _uniqueSourceIdentifier(), destinationIdentifier, applicationMessage ); // Get the reference message identifier from the package. We need to verify this since it was used on the sending chain. messageIdentifier = bytes32(message[MESSAGE_IDENTIFIER_START:MESSAGE_IDENTIFIER_END]); if (computedMessageIdentifier != messageIdentifier) revert InvalidTimeoutPackage(messageIdentifier, computedMessageIdentifier); } /** * @notice Payout incentives to the relayers. * @dev Timeouts needs to set targetDelta == 0 to cut off logic. */ function _payoutIncentive( uint256 gasLimit, uint48 gasSpentOnDestination, uint48 maxGasDelivery, uint96 priceOfDeliveryGas, uint48 maxGasAck, uint96 priceOfAckGas, address refundGasTo, address destinationFeeRecipient, address sourceFeeRecipient, uint64 targetDelta, uint64 messageExecutionTimestamp ) internal returns(uint256 gasSpentOnSource, uint256 deliveryFee, uint256 ackFee) { // Find the respective rewards for delivery and ack. uint256 actualFee; uint256 refund; unchecked { // gasSpentOnDestination * priceOfDeliveryGas < 2**48 * 2**96 = 2**144 if (maxGasDelivery <= gasSpentOnDestination) gasSpentOnDestination = maxGasDelivery; // If more gas was spent then allocated, then only use the allocation. deliveryFee = gasSpentOnDestination * priceOfDeliveryGas; // Delay the gas limit computation until as late as possible. This should include the majority of gas spent. // gasLimit = gasleft() when less gas was spent, thus it is always larger than gasleft(). gasSpentOnSource = gasLimit - gasleft(); if (maxGasAck <= gasSpentOnSource) gasSpentOnSource = maxGasAck; // If more gas was spent then allocated, then only use the allocation. // gasSpentOnSource * priceOfAckGas < 2**48 * 2**96 = 2**144 ackFee = gasSpentOnSource * priceOfAckGas; // deliveryFee + ackFee < 2**144 + 2**144 = 2**145 actualFee = deliveryFee + ackFee; // (priceOfDeliveryGas * maxGasDelivery + priceOfDeliveryGas * maxGasAck) has been caculated before (escrowBounty) < (2**48 * 2**96) + (2**48 * 2**96) = 2**144 + 2**144 = 2**145 uint256 maxDeliveryFee = maxGasDelivery * priceOfDeliveryGas; uint256 maxAckFee = maxGasAck * priceOfAckGas; uint256 maxFee = maxDeliveryFee + maxAckFee; refund = maxFee - actualFee; } // send is used to ensure this doesn't revert. ".transfer" could revert and block the ack from ever being delivered. if(!payable(refundGasTo).send(refund)) { payable(SEND_LOST_GAS_TO).transfer(refund); // If we don't send the gas somewhere, the gas is lost forever. } // If both the destination relayer and source relayer are the same then we don't have to figure out which fraction goes to who. For timeouts, logic should end here. if (destinationFeeRecipient == sourceFeeRecipient) { payable(sourceFeeRecipient).transfer(actualFee); // If this reverts, then the relayer that is executing this tx provided a bad input. return (gasSpentOnSource, deliveryFee, ackFee); } // If targetDelta is 0, then distribute exactly the rewards. if (targetDelta == 0) { // ".send" is used to ensure this doesn't revert. ".transfer" could revert and block the ack from ever being delivered. if(!payable(destinationFeeRecipient).send(deliveryFee)) { // If this returns false, it implies that the transfer failed. // The result is that this contract still has deliveryFee. As a result, send it somewhere else. payable(SEND_LOST_GAS_TO).transfer(deliveryFee); // If we don't send the gas somewhere, the gas is lost forever. } Address.sendValue(payable(sourceFeeRecipient), ackFee); // If this reverts, then the relayer that is executing this tx provided a bad input. return (gasSpentOnSource, deliveryFee, ackFee); } // Compute the reward distribution. We need the time it took to deliver the ack back. uint64 executionTime; unchecked { // Overflow is desired in this code chuck. It ensures that the code piece continues working // past the time when uint64 stops working. *As long as any timedelta is less than uint64. executionTime = uint64(block.timestamp) - messageExecutionTimestamp; // Check if the overflow (/underflow) was because block.timestamp < messageExecutionTimestamp rather // than because block.timestamp has overflowed and messageExecutionTimestamp has now. // We do this by checking if executionTime is greater than an unrealistic period of time. // 32768 days is chosen since that is the neatest value close to the uint32 limit: 49710 days. // If this is the cause, we must assume that block.timestamp was slightly less than messageExecutionTimestamp // and an overflow happened and the execution time was set significantly too large as a result. // If this is true, then the delivery was quick (based on all available information) and the source to destination // should get everything. if (executionTime > 32768 days) executionTime = 0; } // The incentive scheme is as follows: When executionTime = targetDelta then // the rewards are distributed as per the incentive spec. If the time is less, then // more incentives are given to the destination relayer while if the time is more, // then more incentives are given to the sourceRelayer. uint256 forDestinationRelayer = deliveryFee; unchecked { // |executionTime - targetDelta| < |2**64 + 2**64| = 2**65 int256 timeBetweenTargetAndExecution = int256(uint256(executionTime)) - int256(uint256(targetDelta)); if (timeBetweenTargetAndExecution <= 0) { // Less time than target passed and the destination relayer should get a larger chunk. // targetDelta != 0, we checked for that. // max abs timeBetweenTargetAndExecution = | - targetDelta| = targetDelta // => ackFee * targetDelta < actualFee * targetDelta < 2**127 * 2**64 = 2**191 forDestinationRelayer += ackFee * uint256(- timeBetweenTargetAndExecution) / targetDelta; } else { // timeBetweenTargetAndExecution > 0 // More time than target passed and the ack relayer should get a larger chunk. // If more time than double the target passed, the ack relayer should get everything if (uint256(timeBetweenTargetAndExecution) < targetDelta) { // targetDelta != 0, we checked for that. // max abs timeBetweenTargetAndExecution = targetDelta from previous // => deliveryFee * targetDelta < actualFee * targetDelta < 2**127 * 2**64 = 2**191 forDestinationRelayer -= deliveryFee * uint256(timeBetweenTargetAndExecution) / targetDelta; } else { // timeBetweenTargetAndExecution > targetDelta === executionTime - targetDelta > targetDelta === executionTime > 2 * targetDelta // This doesn't discourage relaying, since executionTime first begins counting once the destination call has been executed. // As a result, this only encourages delivery of the ack. forDestinationRelayer = 0; } } } // send is used to ensure this doesn't revert. ".transfer" could revert and block the ack from ever being delivered. if(!payable(destinationFeeRecipient).send(forDestinationRelayer)) { payable(SEND_LOST_GAS_TO).transfer(forDestinationRelayer); // If we don't send the gas somewhere, the gas is lost forever. } uint256 forSourceRelayer; unchecked { // max forDestinationRelayer is deliveryFee + ackFee = actualFee => actualFee - forDestinationRelayer == 0 // min forDestinationRelayer = 0 => actualFee - 0 = actualFee forSourceRelayer = actualFee - forDestinationRelayer; } Address.sendValue(payable(sourceFeeRecipient), forSourceRelayer); // If this reverts, then the relayer that is executing this tx provided a bad input. return (gasSpentOnSource, forDestinationRelayer, forSourceRelayer); } /** * @notice Sets a bounty for a message * @dev Does not check if enough incentives have been provided, this is delegated as responsiblity * of the caller of this function. * @param fromApplication The application that called the contract. Should generally be msg.sender. Is used to separate storage between applications. * @param destinationIdentifier The destination chain. Combined with fromApplication, this specifics a unique remote escrow implementation. * @param messageIdentifier A unique identifier for the message. Is used to check if bounties have been paid, on ack and timeout. * @param incentive The incentive structure. ".refundGasTo" is not allowed to be address(0). * * @return sum The total cost of the bounty. It not checked against msg.value in this contract. */ function _setBounty( address fromApplication, bytes32 destinationIdentifier, bytes32 messageIdentifier, IncentiveDescription calldata incentive ) internal returns(uint128 sum) { if (_bounty[fromApplication][destinationIdentifier][messageIdentifier].refundGasTo != address(0)) revert MessageAlreadyBountied(); // Compute incentive metrics. uint128 maxDeliveryFee = incentive.maxGasDelivery * incentive.priceOfDeliveryGas; uint128 maxAckFee = incentive.maxGasAck * incentive.priceOfAckGas; sum = maxDeliveryFee + maxAckFee; _bounty[fromApplication][destinationIdentifier][messageIdentifier] = incentive; } /** * @notice Allows anyone to re-execute an ack which didn't properly execute. * @dev No applciation should rely on this function. It should only be used incase an application has faulty logic. * Example: Faulty logic results in wrong enforcement on gas limit => out of gas? * * This function allows replaying acks. * * For further parameter documentation, read processPacket. * @param messagingProtocolContext Argument that once made _verifyPacket pass on processPacket * @param rawMessage Argument that once made _verifyPacket pass on processPacket. */ function recoverAck( bytes calldata messagingProtocolContext, bytes calldata rawMessage ) external { // onRecv. implementations should collect acks so _verifyPacket returns true after first execution of the ack. (bytes32 chainIdentifier, bytes memory implementationIdentifier, bytes calldata message) = _verifyPacket(messagingProtocolContext, rawMessage); bytes1 context = bytes1(message[0]); // Only allow acks to do this. Normal messages are invalid after first execution. if (context == CTX_DESTINATION_TO_SOURCE) { bytes32 messageIdentifier = bytes32(message[MESSAGE_IDENTIFIER_START:MESSAGE_IDENTIFIER_END]); address fromApplication = address(bytes20(message[FROM_APPLICATION_START_EVM:FROM_APPLICATION_END])); if(_bounty[fromApplication][chainIdentifier][messageIdentifier].refundGasTo != address(0)) revert AckHasNotBeenExecuted(); // check if the application trusts the implementation on the destination chain. bytes32 expectedDestinationImplementationHash = implementationAddressHash[fromApplication][chainIdentifier]; if (expectedDestinationImplementationHash != keccak256(implementationIdentifier)) revert InvalidImplementationAddress(); ICrossChainReceiver(fromApplication).receiveAck(chainIdentifier, messageIdentifier, message[CTX1_MESSAGE_START: ]); emit MessageAcked(implementationIdentifier, chainIdentifier, messageIdentifier); } else { revert NotImplementedError(); } } /** * @notice Emit a new ack message incase the proof for the old got lost. * This function is intended for manual usage in case the ack was critical to the application * and the ack proof expired. * @dev If an AMB controls the entire flow of the message, disable this function. * @param sourceIdentifier Which chain to send the ack to? Is checked against stored ack hash to ensure * the message can only be sent to the correct source chain. * @param implementationIdentifier Which escrow contract to send the ack to? Is checked against stored ack hash * to ensure the message can only be sent to the original implementation. * @param receiveAckWithContext The message as this contract delivered to _sendMessage via processMessage. */ function reemitAckMessage( bytes32 sourceIdentifier, bytes calldata implementationIdentifier, bytes calldata receiveAckWithContext ) external payable virtual { // Has the package previously been executed? (otherwise timeout might be more appropiate) // Load the messageIdentifier from receiveAckWithContext. // This makes it slighly easier to retry messages. bytes32 messageIdentifier = bytes32(receiveAckWithContext[MESSAGE_IDENTIFIER_START:MESSAGE_IDENTIFIER_END]); bytes32 storedAckHash = _messageDelivered[sourceIdentifier][implementationIdentifier][messageIdentifier]; // First, check if there is actually an appropiate hash at the message identifier. // Then, check if the storedAckHash & the source target (sourceIdentifier & implementationIdentifier) matches the executed one. if (storedAckHash == bytes32(0) || storedAckHash != keccak256(receiveAckWithContext)) revert CannotRetryWrongMessage(storedAckHash, keccak256(receiveAckWithContext)); // Send the package again. uint128 cost = _sendPacket(sourceIdentifier, implementationIdentifier, receiveAckWithContext, 0); // Check if there is a mis-match between the cost and the value of the message. if (uint128(msg.value) != cost) { if (uint128(msg.value) > cost) { // Send the unused gas back to the the user. Address.sendValue(payable(msg.sender), msg.value - uint256(cost)); return; } revert NotEnoughGasProvided(uint128(msg.value), cost); } } /** * @notice This function will timeout a message. * If a message has been executed but the proof for the ack might have been lost * then the message can be retried. * If a message has not been executed and the message is beyond timeout, then it can be * timed out. The intended usecase for this function is the latter case AND when the proof is lost. * If the proof is intact, delivering the proof normally is safer. * @dev If an AMB has a native way to timeout messages, disable this function. * The reason why we don't verify that the message is contained within the message identifier * is because we don't know where the messageIdentifier was computed and don't know what hashing function was used * As a result, it is expected that the sender of this function checks for inclusion manually, otherwise they could * waste a lot of gas. * There is no reliable way to block this function such that it can't be called twice after a message has been timed out * since the content could we wrong or the proof may still exist. * @param sourceIdentifier The identifier for the source chain (where to send the message) * @param implementationIdentifier The address of the source generalisedIncentives that emitted the original message * @param originBlockNumber The block number when the message was originally emitted. * Note that for some L2 this could be the block number of the underlying chain. * Regardless: It is the same block number that originally generated themessage identifier. * @param message Original Generalised Incentives message */ function timeoutMessage( bytes32 sourceIdentifier, bytes calldata implementationIdentifier, uint256 originBlockNumber, bytes calldata message ) external payable virtual { //! When reading this function, it is important to remember that 'message' is // entirely untrusted. We do no verification on it. As a result, we shouldn't // trust any data within it. It is first when this message hits the source chain we can begin to verify data. // Check that at least the context is set correctly. if (message[CONTEXT_POS] != CTX_SOURCE_TO_DESTINATION) revert MessageHasInvalidContext(); // Get the message identifier from the message. bytes32 messageIdentifier = bytes32(message[MESSAGE_IDENTIFIER_START:MESSAGE_IDENTIFIER_END]); // Read the status of the package at MessageIdentifier. bytes32 storedAckHash = _messageDelivered[sourceIdentifier][implementationIdentifier][messageIdentifier]; // If has already been processed, then don't allow timeouting the message. Instead, it should be retried. if (storedAckHash != bytes32(0)) revert MessageAlreadyProcessed(); // This also protects a relayer that delivered a timedout message. // ! It could still be that someone delivers the "true" message along with the proof after this has been emitted! // As a result, the above check only ensures that if the message properly arrives, then this cannot be called afterwards. // ensuring that the original relayer isn't cheated. // When the message arrives, the usual incentive check ensures only 1 message can arrive. Since the incentive check is based on // messageIdentifier, we need to verify it. // Remember, the messageIdentifier is actually untrusted. So it is trivial to pass the above check. However, any way to pass // the above check fradulently would result in messageIdentifier being wrong and unable to be reproduced on the source chain. // Load the deadline from the message. uint64 deadline = uint64(bytes8(message[CTX0_DEADLINE_START:CTX0_DEADLINE_END])); // Check that the deadline has passed AND that there is no opt out. // This isn't a strong check but if a relayer is honest, then it can be used as a sanity check. // This protects against emitting rouge messages of timeout before the message has had a time to execute IF deadline belong to messageIdentifier. if (deadline == 0 || deadline >= block.timestamp) revert DeadlineNotPassed(deadline, uint64(block.timestamp)); // Reconstruct message bytes memory receiveAckWithContext = bytes.concat( CTX_TIMEDOUT_ON_DESTINATION, messageIdentifier, message[FROM_APPLICATION_LENGTH_POS:FROM_APPLICATION_END], bytes8(deadline), bytes32(originBlockNumber), message[CTX0_MESSAGE_START: ] ); // To maintain a common implementation language, emit our event before message. emit TimeoutInitiated(implementationIdentifier, sourceIdentifier, messageIdentifier); // Send the message uint128 cost = _sendPacket( sourceIdentifier, implementationIdentifier, receiveAckWithContext, 0 ); // Check if there is a mis-match between the cost and the value of the message. if (uint128(msg.value) != cost) { if (uint128(msg.value) > cost) { // Send the unused gas back to the the user. Address.sendValue(payable(msg.sender), msg.value - uint256(cost)); return; } revert NotEnoughGasProvided(uint128(msg.value), cost); } } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; interface SmallStructs { struct SmallVM { // uint8 version; // uint32 timestamp; // uint32 nonce; uint16 emitterChainId; bytes32 emitterAddress; // uint64 sequence; // uint8 consistencyLevel; uint32 guardianSetIndex; } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "./GettersGetter.sol"; import "../wormhole/Structs.sol"; import "./SmallStructs.sol"; import "../wormhole/libraries/external/BytesLib.sol"; /** * @notice Optimised message verifier for Wormhole * @dev Is based on the Wormhole verification library. */ contract WormholeVerifier is GettersGetter { using BytesLib for bytes; error InvalidSignatory(); error SignatureIndicesNotAscending(); error GuardianIndexOutOfBounds(); error VMVersionIncompatible(); error TooManyGuardians(); constructor(address wormholeState) payable GettersGetter(wormholeState) {} /// @dev parseAndVerifyVM serves to parse an encodedVM and wholy validate it for consumption function parseAndVerifyVM(bytes calldata encodedVM) public view returns ( SmallStructs.SmallVM memory vm, bytes calldata payload, bool valid, string memory reason ) { bytes calldata signatures; bytes32 bodyHash; (vm, signatures, bodyHash, payload) = parseVM(encodedVM); /// setting checkHash to false as we can trust the hash field in this case given that parseVM computes and then sets the hash field above (valid, reason) = verifyVMInternal(vm, signatures, bodyHash); } /** * @dev `verifyVMInternal` serves to validate an arbitrary vm against a valid Guardian set * if checkHash is set then the hash field of the vm is verified against the hash of its contents * in the case that the vm is securely parsed and the hash field can be trusted, checkHash can be set to false * as the check would be redundant */ function verifyVMInternal( SmallStructs.SmallVM memory vm, bytes calldata signatures, bytes32 bodyHash ) internal view returns (bool valid, string memory reason) { /// @dev Obtain the current guardianSet for the guardianSetIndex provided Structs.GuardianSet memory guardianSet = getGuardianSet(vm.guardianSetIndex); /** * @dev Checks whether the guardianSet has zero keys * WARNING: This keys check is critical to ensure the guardianSet has keys present AND to ensure * that guardianSet key size doesn't fall to zero and negatively impact quorum assessment. If guardianSet * key length is 0 and vm.signatures length is 0, this could compromise the integrity of both vm and * signature verification. */ if(guardianSet.keys.length == 0){ return (false, "invalid guardian set"); } /// @dev Checks if VM guardian set index matches the current index (unless the current set is expired). if (guardianSet.expirationTime < block.timestamp) { if (vm.guardianSetIndex != getCurrentGuardianSetIndex()) { return (false, "guardian set has expired"); } } /** * @dev We're using a fixed point number transformation with 1 decimal to deal with rounding. * WARNING: This quorum check is critical to assessing whether we have enough Guardian signatures to validate a VM * if making any changes to this, obtain additional peer review. If guardianSet key length is 0 and * vm.signatures length is 0, this could compromise the integrity of both vm and signature verification. */ if (signatures.length < quorum(guardianSet.keys.length)){ return (false, "no quorum"); } /// @dev Verify the proposed vm.signatures against the guardianSet (bool signaturesValid, string memory invalidReason) = verifySignatures(bodyHash, signatures, guardianSet); if(!signaturesValid){ return (false, invalidReason); } /// If we are here, we've validated the VM is a valid multi-sig that matches the guardianSet. return (true, ""); } /** * @dev verifySignatures serves to validate arbitrary sigatures against an arbitrary guardianSet * - it intentionally does not solve for expectations within guardianSet (you should use verifyVM if you need these protections) * - it intentioanlly does not solve for quorum (you should use verifyVM if you need these protections) * - it intentionally returns true when signatures is an empty set (you should use verifyVM if you need these protections) */ function verifySignatures(bytes32 hash, bytes calldata signatures, Structs.GuardianSet memory guardianSet) public pure returns (bool valid, string memory reason) { uint8 lastIndex = 0; uint256 guardianCount = guardianSet.keys.length; uint256 signersLen = uint8(bytes1(signatures[0])); uint256 index = 1; unchecked { for (uint i = 0; i < signersLen; ++i) { uint8 guardianIndex = uint8(bytes1(signatures[index])); index += 1; bytes32 r = bytes32(signatures[index: index + 32]); index += 32; bytes32 s = bytes32(signatures[index: index + 32]); index += 32; uint8 v = uint8(bytes1(signatures[index: index + 1])) + 27; index += 1; address signatory = ecrecover(hash, v, r, s); // ecrecover returns 0 for invalid signatures. We explicitly require valid signatures to avoid unexpected // behaviour due to the default storage slot value also being 0. if (signatory == address(0)) revert InvalidSignatory(); /// Ensure that provided signature indices are ascending only if(!(i == 0 || guardianIndex > lastIndex)) revert SignatureIndicesNotAscending(); lastIndex = guardianIndex; /// @dev Ensure that the provided signature index is within the /// bounds of the guardianSet. This is implicitly checked by the array /// index operation below, so this check is technically redundant. /// However, reverting explicitly here ensures that a bug is not /// introduced accidentally later due to the nontrivial storage /// semantics of solidity. if (guardianIndex >= guardianCount) revert GuardianIndexOutOfBounds(); /// Check to see if the signer of the signature does not match a specific Guardian key at the provided index if(signatory != guardianSet.keys[guardianIndex]){ return (false, "VM signature invalid"); } } /// If we are here, we've validated that the provided signatures are valid for the provided guardianSet return (true, ""); } } /** * @dev parseVM serves to parse an encodedVM into a vm struct * - it intentionally performs no validation functions, it simply parses raw into a struct */ function parseVM(bytes calldata encodedVM) public view virtual returns (SmallStructs.SmallVM memory vm, bytes calldata signatures, bytes32 bodyHash, bytes calldata payload) { unchecked { uint index = 0; uint8 version = uint8(bytes1(encodedVM[0:1])); index += 1; // SECURITY: Note that currently the VM.version is not part of the hash // and for reasons described below it cannot be made part of the hash. // This means that this field's integrity is not protected and cannot be trusted. // This is not a problem today since there is only one accepted version, but it // could be a problem if we wanted to allow other versions in the future. if(version != 1) revert VMVersionIncompatible(); vm.guardianSetIndex = uint32(bytes4(encodedVM[1:4+1])); index += 4; // Parse Signatures uint256 signersLen = uint8(bytes1(encodedVM[5:5+1])); signatures = encodedVM[5:5 + 1 + signersLen*(1+32+32+1)]; index += 1 + signersLen*(1+32+32+1); // signatures = new Structs.Signature[](signersLen); // for (uint i = 0; i < signersLen; ++i) { // signatures[i].guardianIndex = uint8(bytes1(encodedVM[index:index+1])); // index += 1; // signatures[i].r = bytes32(encodedVM[index:index+32]); // index += 32; // signatures[i].s = bytes32(encodedVM[index:index+32]); // index += 32; // signatures[i].v = uint8(bytes1(encodedVM[index:index+1])) + 27; // index += 1; // } /* Hash the body SECURITY: Do not change the way the hash of a VM is computed! Changing it could result into two different hashes for the same observation. But xDapps rely on the hash of an observation for replay protection. */ bytes calldata body = encodedVM[index:]; bodyHash = keccak256(bytes.concat(keccak256(body))); // Parse the body // vm.timestamp = uint32(bytes4(encodedVM[index:index+4])); index += 4; // vm.nonce = uint32(bytes4(encodedVM[index:index+4])); index += 4; vm.emitterChainId = uint16(bytes2(encodedVM[index:index+2])); index += 2; vm.emitterAddress = bytes32(encodedVM[index:index+32]); index += 32; // vm.sequence = uint64(bytes8(encodedVM[index:index+8])); index += 8; // vm.consistencyLevel = uint8(bytes1(encodedVM[index:index+1])); index += 1; payload = encodedVM[index:]; } } /** * @dev quorum serves solely to determine the number of signatures required to acheive quorum */ function quorum(uint numGuardians) public pure virtual returns (uint numSignaturesRequiredForQuorum) { unchecked { // The max number of guardians is 255 if (numGuardians >= 256) revert TooManyGuardians(); return ((numGuardians * 2) / 3) + 1; } } }
// 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: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/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 functionCallWithValue(target, data, 0, "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"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, 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) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or 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 { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import { IMessageEscrowStructs } from "./IMessageEscrowStructs.sol"; import { IMessageEscrowErrors } from "./IMessageEscrowErrors.sol"; import { IMessageEscrowEvents } from "./IMessageEscrowEvents.sol"; interface IIncentivizedMessageEscrow is IMessageEscrowStructs, IMessageEscrowErrors, IMessageEscrowEvents { function bounty(address fromApplication, bytes32 destinationIdentifier, bytes32 messageIdentifier) external view returns(IncentiveDescription memory incentive); function messageDelivered(bytes32 sourceIdentifier, bytes calldata sourceImplementationIdentifier, bytes32 messageIdentifier) external view returns(bytes32 hasMessageBeenExecuted); function increaseBounty( address fromApplication, bytes32 destinationIdentifier, bytes32 messageIdentifier, uint96 priceOfDeliveryGas, uint96 priceOfAckGas ) external payable; function submitMessage( bytes32 destinationIdentifier, bytes calldata destinationAddress, bytes calldata message, IncentiveDescription calldata incentive, uint64 deadline ) external payable returns(uint256 gasRefund, bytes32 messageIdentifier); function processPacket(bytes calldata messagingProtocolContext, bytes calldata message, bytes32 feeRecipient) payable external; function setRemoteImplementation(bytes32 chainIdentifier, bytes calldata implementation) external; /** * @notice Estimates the additional cost to the messaging router to validate the message * @return asset The asset the token is in. If native token, returns address(0); * @return amount The number of assets to pay. */ function estimateAdditionalCost() external view returns(address asset, uint256 amount); function timeoutMessage( bytes32 sourceIdentifier, bytes calldata implementationIdentifier, uint256 originBlockNumber, bytes calldata message ) external payable; function reemitAckMessage( bytes32 sourceIdentifier, bytes calldata implementationIdentifier, bytes calldata receiveAckWithContext ) external payable; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface ICrossChainReceiver { /** * @notice Handles the acknowledgement from the destination * @dev acknowledgement is exactly the output of receiveMessage except if receiveMessage failed, then it is error code (0xff or 0xfe) + original message. * If an acknowledgement isn't needed, this can be implemented as {}. * - This function can be called by someone else again! Ensure that if this endpoint is called twice with the same message nothing bad happens. * - If the application expects that the maxGasAck will be provided, then it should check that it got enough and revert if it didn't. * Otherwise, it is assumed that you didn't need the extra gas. * @param destinationIdentifier An identifier for the destination chain. * @param messageIdentifier A unique identifier for the message. The identifier matches the identifier returned when escrowed the message. * This identifier can be mismanaged by the messaging protocol. * @param acknowledgement The acknowledgement sent back by receiveMessage. Is 0xff if receiveMessage reverted. */ function receiveAck(bytes32 destinationIdentifier, bytes32 messageIdentifier, bytes calldata acknowledgement) external; /** * @notice receiveMessage from a cross-chain call. * @dev The application needs to check the fromApplication combined with sourceIdentifierbytes to figure out if the call is authenticated. * - If the application expects that the maxGasDelivery will be provided, then it should check that it got enough and revert if it didn't. * Otherwise, it is assumed that you didn't need the extra gas. * @return acknowledgement Information which is passed to receiveAck. * If you return 0xff, you cannot know the difference between Executed but "failed" and outright failed. */ function receiveMessage(bytes32 sourceIdentifierbytes, bytes32 messageIdentifier, bytes calldata fromApplication, bytes calldata message) external returns(bytes memory acknowledgement); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; contract Bytes65 { error InvalidBytes65Address(); function _checkBytes65(bytes calldata supposedlyBytes65) internal pure returns(bool) { return supposedlyBytes65.length == 65; } modifier checkBytes65Address(bytes calldata supposedlyBytes65) { if (!_checkBytes65(supposedlyBytes65)) revert InvalidBytes65Address(); _; } function convertEVMTo65(address evmAddress) public pure returns(bytes memory) { return bytes.concat( bytes1(uint8(20)), // Size of address. Is always 20 for EVM bytes32(0), // First 32 bytes on EVM are 0 bytes32(uint256(uint160((evmAddress)))) // Encode the address in bytes32. ); } function thisBytes65() public view returns(bytes memory) { return convertEVMTo65(address(this)); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.22; // IncentivizedMessageEscrow Payload*********************************************************************************************** // // Common Payload (beginning) // CONTEXT 0 (1 byte) // + MESSAGE_IDENTIFIER 1 (32 bytes) // + FROM_APPLICATION_LENGTH 33 (1 byte) // + FROM_APPLICATION 34 (64 bytes) // // Context-depending Payload // CTX0 - 0x00 - Source to Destination // + TO_APPLICATION_LENGTH 98 (1 byte) // + TO_APPLICATION 99 (64 bytes) // + DEADLINE 163 (8 bytes) // + MAX_GAS 171 (6 bytes) // => MESSAGE_START 177 (remainder) // // CTX1 - 0x01 - Destination to Source // + RELAYER_RECIPIENT 98 (32 bytes) // + GAS_SPENT 130 (6 bytes) // + EXECUTION_TIME 136 (8 bytes) // => MESSAGE_START 144 (remainder) // // CTX2 - 0x02 - Timed Out on Destination // + DEADLINE 98 (8 bytes) // + ORIGIN_BLOCKNUMER 106 (32 bytes) // => MESSAGE_START 138 (remainder) // Contexts ********************************************************************************************************************* bytes1 constant CTX_SOURCE_TO_DESTINATION = 0x00; bytes1 constant CTX_DESTINATION_TO_SOURCE = 0x01; bytes1 constant CTX_TIMEDOUT_ON_DESTINATION = 0x02; // Common Payload *************************************************************************************************************** uint constant CONTEXT_POS = 0; uint constant MESSAGE_IDENTIFIER_START = 1; uint constant MESSAGE_IDENTIFIER_END = 33; uint constant FROM_APPLICATION_LENGTH_POS = 33; uint constant FROM_APPLICATION_START = 34; uint constant FROM_APPLICATION_START_EVM = 78; // If the address is an EVM address, this is the start uint constant FROM_APPLICATION_END = 98; // CTX0 Source to Destination ****************************************************************************************************** uint constant CTX0_TO_APPLICATION_LENGTH_POS = 98; uint constant CTX0_TO_APPLICATION_START = 99; uint constant CTX0_TO_APPLICATION_START_EVM = 143; // If the address is an EVM address, this is the start uint constant CTX0_TO_APPLICATION_END = 163; uint constant CTX0_DEADLINE_START = 163; uint constant CTX0_DEADLINE_END = 171; uint constant CTX0_MAX_GAS_LIMIT_START = 171; uint constant CTX0_MAX_GAS_LIMIT_END = 177; uint constant CTX0_MESSAGE_START = 177; // CTX1 Destination to Source ************************************************************************************************** uint constant CTX1_RELAYER_RECIPIENT_START = 98; uint constant CTX1_RELAYER_RECIPIENT_END = 130; uint constant CTX1_GAS_SPENT_START = 130; uint constant CTX1_GAS_SPENT_END = 136; uint constant CTX1_EXECUTION_TIME_START = 136; uint constant CTX1_EXECUTION_TIME_END = 144; uint constant CTX1_MESSAGE_START = 144; // CTX2 Message Timed out ************************************************************************************************** uint constant CTX2_DEADLINE_START = 98; uint constant CTX2_DEADLINE_END = 106; uint constant CTX2_ORIGIN_BLOCK_NUMBER_START = 106; uint constant CTX2_ORIGIN_BLOCK_NUMBER_END = 138; uint constant CTX2_MESSAGE_START = 138;
// contracts/Getters.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "../wormhole/Getters.sol"; contract GettersGetter { error WormholeStateAddressZero(); Getters immutable internal WORMHOLE_STATE; constructor(address wormholeState) payable { if (wormholeState == address(0)) revert WormholeStateAddressZero(); WORMHOLE_STATE = Getters(wormholeState); } function getGuardianSet(uint32 index) public view returns (Structs.GuardianSet memory) { return WORMHOLE_STATE.getGuardianSet(index); } function getCurrentGuardianSetIndex() public view returns (uint32) { return WORMHOLE_STATE.getCurrentGuardianSetIndex(); } function getGuardianSetExpiry() public view returns (uint32) { return WORMHOLE_STATE.getGuardianSetExpiry(); } function governanceActionIsConsumed(bytes32 hash) public view returns (bool) { return WORMHOLE_STATE.governanceActionIsConsumed(hash); } function isInitialized(address impl) public view returns (bool) { return WORMHOLE_STATE.isInitialized(impl); } function chainId() public view returns (uint16) { return WORMHOLE_STATE.chainId(); } function evmChainId() public view returns (uint256) { return WORMHOLE_STATE.evmChainId(); } function isFork() public view returns (bool) { return WORMHOLE_STATE.isFork(); } function governanceChainId() public view returns (uint16){ return WORMHOLE_STATE.governanceChainId(); } function governanceContract() public view returns (bytes32){ return WORMHOLE_STATE.governanceContract(); } function messageFee() public view returns (uint256) { return WORMHOLE_STATE.messageFee(); } function nextSequence(address emitter) public view returns (uint64) { return WORMHOLE_STATE.nextSequence(emitter); } }
// contracts/Structs.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; interface Structs { struct Provider { uint16 chainId; uint16 governanceChainId; bytes32 governanceContract; } 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; } }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.8.0 <0.9.0; library BytesLib { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IMessageEscrowStructs { struct IncentiveDescription { uint48 maxGasDelivery; // 0: 6/32 bytes uint48 maxGasAck; // 0: 12/32 bytes address refundGasTo; // 0: 32/32 bytes uint96 priceOfDeliveryGas; // 1: 12/32 bytes uint96 priceOfAckGas; // 1: 24/32 bytes uint64 targetDelta; // 1: 32/32 bytes } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IMessageEscrowErrors { error AckHasNotBeenExecuted(); // 0x3d1553f8 error CannotRetryWrongMessage(bytes32,bytes32); // 0x48ce7fac error DeadlineInPast(uint64 blocktimestamp, uint64 actual); // 0x2d098d59 error DeadlineNotPassed(uint64 expected, uint64 actual); // 0x862c57f4 error DeadlineTooLong(uint64 maxAllowed, uint64 actual); // 0x3c06f369 error FeeRecipientIsZero(); // 0xfc53a835 error ImplementationAddressAlreadySet(bytes currentImplementation); // 0xdba47850 error IncorrectValueProvided(uint128 expected, uint128 actual); // 0x0b52a60b error InvalidImplementationAddress(); // 0xc970156c error InvalidTimeoutPackage(bytes32 expected, bytes32 actual); // 0xe020885d error MessageAlreadyAcked(); // 0x8af35858 error MessageAlreadyBountied(); // 0x068a62ee error MessageAlreadyProcessed(); // 0x7b042609 error MessageAlreadySpent(); // 0xe954aba2 error MessageDoesNotExist(); // 0x970e41ec error MessageHasInvalidContext(); // 0x3fcdbaba error NoImplementationAddressSet(); // 0x9f994b4b error NotEnoughGasExecution(); // 0x6bc33587 error NotEnoughGasProvided(uint128 expected, uint128 actual); // 0x030748b5 error NotImplementedError(); // 0xd41c17e7 error RefundGasToIsZero(); // 0x6a1a6afe error RouteDisabled(); // 0x17d0b6db error SendLostGasToIsZero(); // 0x9c76a9df }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import { IMessageEscrowStructs } from "./IMessageEscrowStructs.sol"; interface IMessageEscrowEvents { // Important notice for relayers. The implementations (sourceImplementation and destinationImplementation), // when indexed in events, the hash is in the topic not the actual implementation. event BountyPlaced( bytes indexed destinationImplementation, bytes32 chainIdentifier, bytes32 indexed messageIdentifier, IMessageEscrowStructs.IncentiveDescription incentive ); event MessageDelivered(bytes indexed sourceImplementation, bytes32 chainIdentifier, bytes32 indexed messageIdentifier); event MessageAcked(bytes destinationImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier); // Not indexed since relayers can sort by BountyClaimed. event TimeoutInitiated(bytes sourceImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier); event MessageTimedOut(bytes destinationImplementation, bytes32 chainIdentifier, bytes32 messageIdentifier); // Not indexed since relayers can sort by BountyClaimed. event BountyClaimed( bytes indexed destinationImplementation, bytes32 chainIdentifier, bytes32 indexed messageIdentifier, uint64 gasSpentOnDestination, uint64 gasSpentOnSource, uint128 destinationRelayerReward, uint128 sourceRelayerReward ); // To save gas, this event does not emit the full incentive scheme. // Instead, the new gas prices are emitted. As a result, the relayer can collect all bountyIncreased // and then use the maximum. (since the maxmimum is enforced in the smart contract) event BountyIncreased( bytes32 indexed messageIdentifier, uint96 newDeliveryGasPrice, uint96 newAckGasPrice ); event RemoteImplementationSet(address application, bytes32 chainIdentifier, bytes32 implementationAddressHash, bytes implementationAddress); }
// contracts/Getters.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "./State.sol"; contract Getters is State { function getGuardianSet(uint32 index) public view returns (Structs.GuardianSet memory) { return _state.guardianSets[index]; } function getCurrentGuardianSetIndex() public view returns (uint32) { return _state.guardianSetIndex; } function getGuardianSetExpiry() public view returns (uint32) { return _state.guardianSetExpiry; } function governanceActionIsConsumed(bytes32 hash) public view returns (bool) { return _state.consumedGovernanceActions[hash]; } function isInitialized(address impl) public view returns (bool) { return _state.initializedImplementations[impl]; } function chainId() public view returns (uint16) { return _state.provider.chainId; } function evmChainId() public view returns (uint256) { return _state.evmChainId; } function isFork() public view returns (bool) { return evmChainId() != block.chainid; } function governanceChainId() public view returns (uint16){ return _state.provider.governanceChainId; } function governanceContract() public view returns (bytes32){ return _state.provider.governanceContract; } function messageFee() public view returns (uint256) { return _state.messageFee; } function nextSequence(address emitter) public view returns (uint64) { return _state.sequences[emitter]; } }
// contracts/State.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "./Structs.sol"; contract Events { event LogGuardianSetChanged( uint32 oldGuardianIndex, uint32 newGuardianIndex ); event LogMessagePublished( address emitter_address, uint32 nonce, bytes payload ); } contract Storage { struct WormholeState { Structs.Provider provider; // Mapping of guardian_set_index => guardian set mapping(uint32 => Structs.GuardianSet) guardianSets; // Current active guardian set index uint32 guardianSetIndex; // Period for which a guardian set stays active after it has been replaced uint32 guardianSetExpiry; // Sequence numbers per emitter mapping(address => uint64) sequences; // Mapping of consumed governance actions mapping(bytes32 => bool) consumedGovernanceActions; // Mapping of initialized implementations mapping(address => bool) initializedImplementations; uint256 messageFee; // EIP-155 Chain ID uint256 evmChainId; } } contract State { Storage.WormholeState _state; }
{ "remappings": [ "forge-std/=lib/forge-std/src/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "vibc-core-smart-contracts/=lib/vibc-core-smart-contracts/contracts/", "@lazyledger/protobuf3-solidity-lib/=lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/", "@openzeppelin-upgradeable/=lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/", "@openzeppelin/=lib/vibc-core-smart-contracts/lib/openzeppelin-contracts/", "base64/=lib/vibc-core-smart-contracts/lib/base64/", "clones-with-immutable-args/=lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/clones-with-immutable-args/src/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts-upgradeable/=lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "optimism/=lib/vibc-core-smart-contracts/lib/", "proto/=lib/vibc-core-smart-contracts/lib/proto/", "protobuf3-solidity-lib/=lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/contracts/", "safe-contracts/=lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/", "solmate/=lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 100000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"sendLostGasTo","type":"address"},{"internalType":"address","name":"wormhole_","type":"address"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AckHasNotBeenExecuted","type":"error"},{"inputs":[],"name":"BadChainIdentifier","type":"error"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"CannotRetryWrongMessage","type":"error"},{"inputs":[{"internalType":"uint64","name":"blocktimestamp","type":"uint64"},{"internalType":"uint64","name":"actual","type":"uint64"}],"name":"DeadlineInPast","type":"error"},{"inputs":[{"internalType":"uint64","name":"expected","type":"uint64"},{"internalType":"uint64","name":"actual","type":"uint64"}],"name":"DeadlineNotPassed","type":"error"},{"inputs":[{"internalType":"uint64","name":"maxAllowed","type":"uint64"},{"internalType":"uint64","name":"actual","type":"uint64"}],"name":"DeadlineTooLong","type":"error"},{"inputs":[],"name":"FeeRecipientIsZero","type":"error"},{"inputs":[],"name":"GuardianIndexOutOfBounds","type":"error"},{"inputs":[{"internalType":"bytes","name":"currentImplementation","type":"bytes"}],"name":"ImplementationAddressAlreadySet","type":"error"},{"inputs":[{"internalType":"uint128","name":"expected","type":"uint128"},{"internalType":"uint128","name":"actual","type":"uint128"}],"name":"IncorrectValueProvided","type":"error"},{"inputs":[],"name":"InvalidBytes65Address","type":"error"},{"inputs":[],"name":"InvalidImplementationAddress","type":"error"},{"inputs":[],"name":"InvalidSignatory","type":"error"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"name":"InvalidTimeoutPackage","type":"error"},{"inputs":[],"name":"MessageAlreadyAcked","type":"error"},{"inputs":[],"name":"MessageAlreadyBountied","type":"error"},{"inputs":[],"name":"MessageAlreadyProcessed","type":"error"},{"inputs":[],"name":"MessageAlreadySpent","type":"error"},{"inputs":[],"name":"MessageDoesNotExist","type":"error"},{"inputs":[],"name":"MessageHasInvalidContext","type":"error"},{"inputs":[],"name":"NoImplementationAddressSet","type":"error"},{"inputs":[],"name":"NotEnoughGasExecution","type":"error"},{"inputs":[{"internalType":"uint128","name":"expected","type":"uint128"},{"internalType":"uint128","name":"actual","type":"uint128"}],"name":"NotEnoughGasProvided","type":"error"},{"inputs":[],"name":"NotImplementedError","type":"error"},{"inputs":[],"name":"RefundGasToIsZero","type":"error"},{"inputs":[],"name":"RouteDisabled","type":"error"},{"inputs":[],"name":"SendLostGasToIsZero","type":"error"},{"inputs":[],"name":"SignatureIndicesNotAscending","type":"error"},{"inputs":[],"name":"TooManyGuardians","type":"error"},{"inputs":[],"name":"VMVersionIncompatible","type":"error"},{"inputs":[],"name":"WormholeStateAddressZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes","name":"destinationImplementation","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"gasSpentOnDestination","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"gasSpentOnSource","type":"uint64"},{"indexed":false,"internalType":"uint128","name":"destinationRelayerReward","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"sourceRelayerReward","type":"uint128"}],"name":"BountyClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"},{"indexed":false,"internalType":"uint96","name":"newDeliveryGasPrice","type":"uint96"},{"indexed":false,"internalType":"uint96","name":"newAckGasPrice","type":"uint96"}],"name":"BountyIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes","name":"destinationImplementation","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"indexed":false,"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"}],"name":"BountyPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"destinationImplementation","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"}],"name":"MessageAcked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes","name":"sourceImplementation","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"}],"name":"MessageDelivered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"destinationImplementation","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"}],"name":"MessageTimedOut","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"application","type":"address"},{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"implementationAddressHash","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"implementationAddress","type":"bytes"}],"name":"RemoteImplementationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"sourceImplementation","type":"bytes"},{"indexed":false,"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"}],"name":"TimeoutInitiated","type":"event"},{"inputs":[],"name":"SEND_LOST_GAS_TO","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNIQUE_SOURCE_IDENTIFIER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WORMHOLE","outputs":[{"internalType":"contract IWormhole","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromApplication","type":"address"},{"internalType":"bytes32","name":"destinationIdentifier","type":"bytes32"},{"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"}],"name":"bounty","outputs":[{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"evmAddress","type":"address"}],"name":"convertEVMTo65","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"estimateAdditionalCost","outputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"evmChainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentGuardianSetIndex","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"index","type":"uint32"}],"name":"getGuardianSet","outputs":[{"components":[{"internalType":"address[]","name":"keys","type":"address[]"},{"internalType":"uint32","name":"expirationTime","type":"uint32"}],"internalType":"struct Structs.GuardianSet","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGuardianSetExpiry","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"governanceActionIsConsumed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceChainId","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceContract","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"implementationAddress","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"implementationAddressHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromApplication","type":"address"},{"internalType":"bytes32","name":"destinationIdentifier","type":"bytes32"},{"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"},{"internalType":"uint96","name":"deliveryGasPriceIncrease","type":"uint96"},{"internalType":"uint96","name":"ackGasPriceIncrease","type":"uint96"}],"name":"increaseBounty","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"isFork","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"impl","type":"address"}],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes","name":"sourceImplementationIdentifier","type":"bytes"},{"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"}],"name":"messageDelivered","outputs":[{"internalType":"bytes32","name":"hasMessageBeenExecuted","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messageFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"emitter","type":"address"}],"name":"nextSequence","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVM","type":"bytes"}],"name":"parseAndVerifyVM","outputs":[{"components":[{"internalType":"uint16","name":"emitterChainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint32","name":"guardianSetIndex","type":"uint32"}],"internalType":"struct SmallStructs.SmallVM","name":"vm","type":"tuple"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"bool","name":"valid","type":"bool"},{"internalType":"string","name":"reason","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"encodedVM","type":"bytes"}],"name":"parseVM","outputs":[{"components":[{"internalType":"uint16","name":"emitterChainId","type":"uint16"},{"internalType":"bytes32","name":"emitterAddress","type":"bytes32"},{"internalType":"uint32","name":"guardianSetIndex","type":"uint32"}],"internalType":"struct SmallStructs.SmallVM","name":"vm","type":"tuple"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"bytes32","name":"bodyHash","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"messagingProtocolContext","type":"bytes"},{"internalType":"bytes","name":"rawMessage","type":"bytes"},{"internalType":"bytes32","name":"feeRecipient","type":"bytes32"}],"name":"processPacket","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"destinationIdentifier","type":"bytes32"}],"name":"proofValidPeriod","outputs":[{"internalType":"uint64","name":"duration","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numGuardians","type":"uint256"}],"name":"quorum","outputs":[{"internalType":"uint256","name":"numSignaturesRequiredForQuorum","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"messagingProtocolContext","type":"bytes"},{"internalType":"bytes","name":"rawMessage","type":"bytes"}],"name":"recoverAck","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes","name":"implementationIdentifier","type":"bytes"},{"internalType":"bytes","name":"receiveAckWithContext","type":"bytes"}],"name":"reemitAckMessage","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"destinationIdentifier","type":"bytes32"},{"internalType":"bytes","name":"implementation","type":"bytes"}],"name":"setRemoteImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"destinationIdentifier","type":"bytes32"},{"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"internalType":"bytes","name":"message","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"name":"submitMessage","outputs":[{"internalType":"uint256","name":"gasRefund","type":"uint256"},{"internalType":"bytes32","name":"messageIdentifier","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"thisBytes65","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes","name":"implementationIdentifier","type":"bytes"},{"internalType":"uint256","name":"originBlockNumber","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"timeoutMessage","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signatures","type":"bytes"},{"components":[{"internalType":"address[]","name":"keys","type":"address[]"},{"internalType":"uint32","name":"expirationTime","type":"uint32"}],"internalType":"struct Structs.GuardianSet","name":"guardianSet","type":"tuple"}],"name":"verifySignatures","outputs":[{"internalType":"bool","name":"valid","type":"bool"},{"internalType":"string","name":"reason","type":"string"}],"stateMutability":"pure","type":"function"}]
Contract Creation Code
6101006040908082528181620053c480380380916200001f8285620001d5565b833981010312620001d0576200004360206200003b836200020f565b92016200020f565b6001600160a01b039180831615620001bf57608052168015620001ae5760208160049260a0528060c052835192838092634d4502c960e11b82525afa908115620001a35760009162000156575b5061ffff1660e0525161519f90816200022582396080518181816113a901528181614e4201528181614ec60152818161503d0152615116015260a0518181816106610152818161084e015281816109a901528181610f39015281816110a0015281816112380152818161159e0152818161194301528181611dd101528181611ec40152818161222a0152613fae015260c0518181816112e501528181611d3301526140f4015260e051818181610ab101528181611c42015281816129bb015261360d0152f35b6020813d6020116200019a575b816200017260209383620001d5565b810103126200019657519061ffff8216820362000193575061ffff62000090565b80fd5b5080fd5b3d915062000163565b82513d6000823e3d90fd5b81516323205fe760e01b8152600490fd5b8351639c76a9df60e01b8152600490fd5b600080fd5b601f909101601f19168101906001600160401b03821190821017620001f957604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001d05756fe608060409080825260048036101561001657600080fd5b600091823560e01c9081631a90a21914611e53575080631cfe795114611e0b5780632c3c02a414611d5757806335e78cfe14611ce85780633a24ef3514611c655780633ccb217e14611c0c5780633fccfa0614611b985780634355866714611a515780634993ef33146119c05780634cf842b5146118be57806353391d3f1461184f5780635a640514146118155780635d758e22146115d757806364d42b171461152d57806365a686de146114425780638caa89ec146113cd5780638dedc1791461135e57806392006e73146112755780639a8a0592146111c7578063a9e1189314611112578063b172b2221461102f578063c0fd8bde14610f73578063d60b347f14610eb4578063daf1e16714610e77578063dd6487fc14610e0c578063dfe1aef914610a13578063e039f22414610938578063ea901f1a146108dc578063eb8d3f12146107dc578063f8ce560a14610796578063f951975a146106dc578063fbe3c2cd146105f0578063fd4716c8146104b65763ffbeacc71461019a57600080fd5b346104b257827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25780359067ffffffffffffffff6024358181116104ae576101eb9036908401611efd565b9590913386526020936003855282872086885285528287205461045d5787156104365733875260028552828720868852855282872091881161040a57506102328154612090565b601f81116103c7575b5085601f88116001146103025791879187986102f195947f84cad7de2c9074e70f8d697042ecb3c00ae9e6622320cb8c48bdd4d5d709e79699916102f7575b508360011b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8560031b1c19161790555b6102b7368385612346565b8581519101209033895260038652808920878a52865281818a20558051968796338852870152850152608060608501526080840191612197565b0390a180f35b90508401353861027a565b818752848720907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08916885b8181106103b05750918993917f84cad7de2c9074e70f8d697042ecb3c00ae9e6622320cb8c48bdd4d5d709e796999a6102f197969410610378575b5050600183811b0190556102ac565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88660031b161c19908601351690553880610369565b91928760018192868a01358155019401920161032e565b818752848720601f890160051c810191868a10610400575b601f0160051c01905b8181106103f5575061023b565b8781556001016103e8565b90915081906103df565b8660416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b82517f9f994b4b000000000000000000000000000000000000000000000000000000008152fd5b33875260028552828720868852855282872083517fdba4785000000000000000000000000000000000000000000000000000000000815291820186905281906104aa9060248301906120e3565b0390fd5b8480fd5b5080fd5b8284346104b257606090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5760c0929167ffffffffffffffff916104ff612007565b938180519161050d83611f76565b8083528060a0602094828682015282858201528288820152826080820152015273ffffffffffffffffffffffffffffffffffffffff80971681528083528181206024358252835281812060443582528352209282519361056c85611f76565b80549465ffffffffffff9485871698898352600186840194888a60301c16865283850199871c8a5201549788966bffffffffffffffffffffffff98899660a089880197898c16895289608082019c8c1c168c52019b8f1c8c5285519d8e525116908c015251169089015251169086015251166080840152511660a0820152f35b8280fd5b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937ffbe3c2cd0000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d2576020939261069f575b5061ffff905191168152f35b61ffff9192506106c490843d86116106cb575b6106bc8183611fae565b810190613c85565b9190610693565b503d6106b2565b81513d85823e3d90fd5b5091903461079357602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b257359163ffffffff92838116810361078e5761072a90613f43565b9281519385855260608501938151948488880152855180915287608088019601915b81811061076457505050948495015116908301520390f35b825173ffffffffffffffffffffffffffffffffffffffff168752958801959188019160010161074c565b600080fd5b80fd5b509190346107935760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261079357506107d560209235613f03565b9051908152f35b508290346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020825180927feb8d3f120000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9283156108d157809361088e575b60208463ffffffff855191168152f35b9092506020833d6020116108c9575b816108aa60209383611fae565b81010312610793575063ffffffff6108c36020936121d6565b9261087e565b3d915061089d565b8251903d90823e3d90fd5b8284346104b25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576109349061092161091c612007565b613e5d565b905191829160208352602083019061204d565b0390f35b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937fe039f2240000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d257602093926109e4575b50519015158152f35b610a05919250833d8511610a0c575b6109fd8183611fae565b8101906122a1565b90836109db565b503d6109f3565b50346104b257827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff908035828111610e0857610a629036908301611efd565b5050602435828111610e085790610a91610a82610a9f9336908401611efd565b90610a8b613c9f565b50613ccf565b959497938897939197614b77565b9015610dcc57506020838111610dc8577f0000000000000000000000000000000000000000000000000000000000000000853503610da0578061ffff87511696015193885195828701958652828752610af787611f2b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018015610d7457818401357fff00000000000000000000000000000000000000000000000000000000000000167f010000000000000000000000000000000000000000000000000000000000000003610d4c5780602111610d485760218201359681606211610d4457606e83013560601c94858c528b81528c8c208b8d5281528c8c20898d5281528c8c205460601c610d1c57858c52600381528c8c20908b8d52528b8b2054908951902003610cf457609011610cf057823b15610cf0578593927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff508a8094610c3e60b0958f8e90519a8b98899788967f11cbf805000000000000000000000000000000000000000000000000000000008852019301918d8601613e1f565b03925af18015610ce657610c81575b5050506102f1907ff865f210bf9219c3ca273416db27a66557c44a930cfbc569e325afc1e809a30794955193849384613e3b565b819796929711610cba578552939450807ff865f210bf9219c3ca273416db27a66557c44a930cfbc569e325afc1e809a3076102f1610c4d565b6024826041897f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b88513d89823e3d90fd5b8880fd5b858b517fc970156c000000000000000000000000000000000000000000000000000000008152fd5b878d517f3d1553f8000000000000000000000000000000000000000000000000000000008152fd5b8a80fd5b8980fd5b858b517fd41c17e7000000000000000000000000000000000000000000000000000000008152fd5b60248a6032887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8288517f3c1e02c0000000000000000000000000000000000000000000000000000000008152fd5b8680fd5b826104aa6020928a519384937f08c379a0000000000000000000000000000000000000000000000000000000008552840152602483019061204d565b8380fd5b8284346104b257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2578060209273ffffffffffffffffffffffffffffffffffffffff610e5d612007565b168152600384528181206024358252845220549051908152f35b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576109349061092130613e5d565b5082346105ec5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020610eef612007565b602473ffffffffffffffffffffffffffffffffffffffff9182855196879485937fd60b347f00000000000000000000000000000000000000000000000000000000855216908301527f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d257602093926109e45750519015158152f35b5090346107935760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107935781359067ffffffffffffffff821161079357611009610fde6109348661101a610fd2610a8236898b01611efd565b95949892909389614b77565b939094519788809863ffffffff6040809261ffff815116855260208101516020860152015116910152565b60c0606088015260c0870191612197565b911515608085015283820360a085015261204d565b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937fb172b2220000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d25783926110da575b6020838351908152f35b9091506020813d60201161110a575b816110f660209383611fae565b810103126105ec57602092505190836110d0565b3d91506110e9565b5090346107935760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107935781359067ffffffffffffffff8211610793576111a36111b46109348661117461116e36888a01611efd565b90613ccf565b94979290939195519889809963ffffffff6040809261ffff815116855260208101516020860152015116910152565b60c0606089015260c0880191612197565b92608086015284830360a0860152612197565b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937f9a8a05920000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d2576020939261069f575061ffff905191168152f35b50346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576020835180927f1a90a2190000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115611354578291611322575b5082519182526020820152f35b90506020813d60201161134c575b8161133d60209383611fae565b810103126104b2575138611315565b3d9150611330565b83513d84823e3d90fd5b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff90602435828111610e08576114199036908301611efd565b6044929192359384116104ae5761143661143f9436908401611efd565b93909235613bb6565b80f35b5091906101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107935767ffffffffffffffff906024358281116104b2576114919036908601611efd565b6044929192358481116105ec576114ab9036908801611efd565b93909260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c36011261079357610124359586168603610793575060418203611505576114f9959635613552565b82519182526020820152f35b8686517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937f64d42b170000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d25783926110da576020838351908152f35b508260a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5761160b612007565b91604435926064356bffffffffffffffffffffffff928382168203610dc8576084359284841684036118115773ffffffffffffffffffffffffffffffffffffffff1687528660205284872060243588526020528487208688526020528487209081548060601c156117e9576116a5908661169e8765ffffffffffff836116938a8388166134cb565b169460301c166134cb565b16906134ee565b906fffffffffffffffffffffffffffffffff90818316340361179357505050926117847fffffffffffffffffffffffffffffffffffffffff00000000000000000000000093829360017f0f9ca48bcb4f4aea18e50df9f1df3c02df5c9c48f05e389a349c8ceb242da58198970190611730611724835492878416613512565b93868360601c16613512565b9485931696879116178155907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b8351928352166020820152a280f35b87517f0b52a60b0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff938416918101918252349092169290921660208301529081906040010390fd5b5085517f970e41ec000000000000000000000000000000000000000000000000000000008152fd5b8780fd5b8284346104b25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25751908152602090f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff8135818111610e08576118999036908401611efd565b50506024359081116105ec5761143f916118b591369101611efd565b60443591612986565b5082346105ec5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5760206118f9612007565b602473ffffffffffffffffffffffffffffffffffffffff9182855196879485937f4cf842b500000000000000000000000000000000000000000000000000000000855216908301527f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d25760209392611987575b5067ffffffffffffffff905191168152f35b67ffffffffffffffff9192506119b290843d86116119b9575b6119aa8183611fae565b810190612966565b9190611975565b503d6119a0565b8284346104b257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25780611a3f611a38926109349473ffffffffffffffffffffffffffffffffffffffff611a19612007565b16815260026020528181206024358252602052208251938480926120e3565b0383611fae565b5191829160208352602083019061204d565b509034610793577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6060813601126104b25767ffffffffffffffff92602435848111610e0857611aa49036908301611efd565b939092604435908682116105ec57879082360301126104b257865195611ac987611f2b565b818401359081116105ec578101366023820112156105ec5783810135611aee81611fef565b91611afb8a519384611fae565b8183526020916024602085019160051b83010191368311610dc857602401905b828210611b6c5750505090875250602401359063ffffffff82168203610793575091611b5293918593602061093497015235612710565b83929192519384931515845280602085015283019061204d565b813573ffffffffffffffffffffffffffffffffffffffff81168103611811578152908301908301611b1b565b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff90602435828111610e0857611be49036908301611efd565b916064359384116104ae57611bff61143f9436908301611efd565b93909260443592356123b9565b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b257602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b508290346105ec5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5760243567ffffffffffffffff8111610e08579282916020611cbc819636908401611efd565b923584526001825284842083865194859384378201908152030190206044358252845220549051908152f35b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5082346105ec5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec578051917f2c3c02a400000000000000000000000000000000000000000000000000000000835280359083015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d257602093926109e45750519015158152f35b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25760209063ffffffff611e4b6121e7565b915191168152f35b9184915034610e0857837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e0857826020917f1a90a2190000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156106d25783926110da576020838351908152f35b9181601f8401121561078e5782359167ffffffffffffffff831161078e576020838186019501011161078e57565b6040810190811067ffffffffffffffff821117611f4757604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60c0810190811067ffffffffffffffff821117611f4757604052565b6020810190811067ffffffffffffffff821117611f4757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611f4757604052565b67ffffffffffffffff8111611f475760051b60200190565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361078e57565b60005b83811061203d5750506000910152565b818101518382015260200161202d565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f6020936120898151809281875287808801910161202a565b0116010190565b90600182811c921680156120d9575b60208310146120aa57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f169161209f565b8054600093926120f282612090565b9182825260209360019160018116908160001461215a5750600114612119575b5050505050565b90939495506000929192528360002092846000945b83861061214657505050500101903880808080612112565b80548587018301529401938590820161212e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168685015250505090151560051b010191503880808080612112565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b519063ffffffff8216820361078e57565b6040517f1cfe795100000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156122955760009161225e575090565b90506020813d60201161228d575b8161227960209383611fae565b8101031261078e5761228a906121d6565b90565b3d915061226c565b6040513d6000823e3d90fd5b9081602091031261078e5751801515810361078e5790565b9093929384831161078e57841161078e578101920390565b3590602081106122df575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b67ffffffffffffffff8111611f4757601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926123528261230c565b916123606040519384611fae565b82948184528183011161078e578281602093846000960137010152565b9190820391821161238a57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b939492909280156126e1577fff000000000000000000000000000000000000000000000000000000000000008635166126b7578060211161078e57600186013591600091868352600160205260409788842060208a518092898b8337898201908152030190208585526020528884205461268e578160ab11610e085760a3810135928360c01c80158015612684575b6126405750826062116104ae578260b1116104ae577fffffffffffffffff00000000000000000000000000000000000000000000000097946125a99997947f4b43f0920d04b8fb5f32167b55abce15859e8d8bb1bfe84ba2909268d77de8d2946125a39894612578948e519c8d947f0200000000000000000000000000000000000000000000000000000000000000602087015288602187015260416021850181880137166082850152608a84015260b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8301910160aa84013781017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99283820152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd981018a520188611fae565b885180916060825261258e606083018688612197565b908860208401528b8301520390a13691612346565b506140db565b906fffffffffffffffffffffffffffffffff908134169183168083036125cf5750505050565b80839495929311612625575050517f030748b50000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b90935061263e925061263891503461237d565b3361428b565b565b8a517f862c57f400000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9182166004820152429091166024820152604490fd5b5042811015612448565b600489517f7b042609000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3fcdbaba000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90929160008351519282156126e15760f890863560f81c9560019560005b888110612755575050505050505050505060019060405161274e81611f92565b6000815290565b868810156126e15760218801978a81013580871c9789906127a88e6127886127828f6001890187856122b9565b906122d1565b9d604261279e61278260418a01809489876122b9565b97019485926122b9565b9390357fff00000000000000000000000000000000000000000000000000000000000000908181169560018110612951575b5050508960ff939d6040968751918c83528660209889951c601b011684840152888301526060820152600080525a6000916001608092fa156129465773ffffffffffffffffffffffffffffffffffffffff91826000511693841561291d578715918215612911575b5050156128e8578a878110156128bf57885190815111156126e157611fe0859260f31c16010151160361287957505060010161272e565b99509a995050505050505050507f564d207369676e617475726520696e76616c696400000000000000000000000060009351916128b583611f2b565b6014835282015290565b600486517fba5b4315000000000000000000000000000000000000000000000000000000008152fd5b600485517f4fe17a6f000000000000000000000000000000000000000000000000000000008152fd5b168c1190503880612842565b600487517fba0e0fd5000000000000000000000000000000000000000000000000000000008152fd5b84513d6000823e3d90fd5b60010360031b82901b161693503880806127da565b9081602091031261078e575167ffffffffffffffff8116810361078e5790565b915a9281156134a1576129aa9261299f91610a8b613c9f565b949692909387614b77565b901561346357508160201161078e577f0000000000000000000000000000000000000000000000000000000000000000918282350361343957602061ffff8651169501519260405193602085015260208452612a0584611f2b565b60208301926000917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084011561340c5784357fff000000000000000000000000000000000000000000000000000000000000001680612b27575050505091612a9693917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0612a9c97940191866144ad565b906140db565b6fffffffffffffffffffffffffffffffff803416908216808203612abf57505050565b808293949211612b165750506040517f030748b50000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b61263891935061263e92503461237d565b9091959692989394507f01000000000000000000000000000000000000000000000000000000000000008114600014612f745750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301602111610dc8577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301606211610dc857606e84013560601c87528660205260408720868852602052604087206021850135885260205260408720600181549101548160601c15612f4a57606e86013560601c89528860205260408920888a526020526040892060218701358a52602052886001604082208281550155606e86013560601c8952600360205260408920888a5260205260408920548751602089012003612f20577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08501609011610cf057888086612cdf612d0b8c6040519283918d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5060208501977f11cbf8050000000000000000000000000000000000000000000000000000000089520191602160b083019201359060248601613e1f565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611fae565b519082606e8b013560601c65ffffffffffff8860301c16f115612edd575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08501608811610cf0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060a287013560d01c9501608211610cf0577f9f9aef021b9656e38cd2086f7d7ee0f6b6062c8078a049bfecd6356fdbeb1b3c9593612ed59360219693612e1f9373ffffffffffffffffffffffffffffffffffffffff9160a889013560c01c94838360c01c95169360828b013516928260601c926bffffffffffffffffffffffff8260601c16928a65ffffffffffff6bffffffffffffffffffffffff818560301c169516931691614f1c565b9890927ff865f210bf9219c3ca273416db27a66557c44a930cfbc569e325afc1e809a30760405180612e578c868c8c01359184613e3b565b0390a167ffffffffffffffff612e7d6fffffffffffffffffffffffffffffffff9a613532565b9980604051998a9901359c1695169316918693916080939695919660a0860197865267ffffffffffffffff80921660208701521660408501526fffffffffffffffffffffffffffffffff809216606085015216910152565b0390a3612a9c565b5a65ffffffffffff603f818560301c1604161115612d29575b60046040517f6bc33587000000000000000000000000000000000000000000000000000000008152fd5b60046040517fc970156c000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8af35858000000000000000000000000000000000000000000000000000000008152fd5b91969392917f0200000000000000000000000000000000000000000000000000000000000000036133e2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0830160621161181157606e85013560601c885260036020526040882084895260205260408820548651602088012003612f20577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301606a1161181157608285013560c01c904282108015906133da575b613397577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08401608a11610cf05790846130a992613095367fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff56880160aa8b01612346565b92608a89013590606e8a013560601c614cb9565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301602111611811576021850135810361335c5750606e84013560601c875286602052604087208388526020526040872060218501358852602052604087209560018754970154928760601c15612f4a578861317e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff568293606e8a013560601c845283602052604084208985526020526040842060218b013585526020528360016040822082815501550160aa89016143a3565b6040516131d181612cdf60208201947f11cbf8050000000000000000000000000000000000000000000000000000000086528b602484015260218d0135604484015260606064840152608483019061204d565b519082606e8a013560601c65ffffffffffff8d60301c16f11561333a575b9261327d7f9f9aef021b9656e38cd2086f7d7ee0f6b6062c8078a049bfecd6356fdbeb1b3c9593612ed59373ffffffffffffffffffffffffffffffffffffffff602197169182918b60601c916bffffffffffffffffffffffff8160601c16918d6bffffffffffffffffffffffff65ffffffffffff8260301c1693169165ffffffffffff808316921690614d63565b9890927fb40655ff3c2bb146ef446b11cb0b22131f89ccd00240340eda5ef4e010794ea1604051806132b58c858c8c01359184613e3b565b0390a165ffffffffffff67ffffffffffffffff6132e26fffffffffffffffffffffffffffffffff9b613532565b6040805194855267ffffffffffffffff9390951683166020850152941616918101919091526fffffffffffffffffffffffffffffffff92881683166060820152979096161660808701529101359390819060a0820190565b9391925a65ffffffffffff603f818a60301c16041611612ef6579291936131ef565b846044916021604051927fe020885d000000000000000000000000000000000000000000000000000000008452013560048301526024820152fd5b6040517f862c57f400000000000000000000000000000000000000000000000000000000815267ffffffffffffffff838116600483015242166024820152604490fd5b508115613031565b60046040517fd41c17e7000000000000000000000000000000000000000000000000000000008152fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b60046040517f3c1e02c0000000000000000000000000000000000000000000000000000000008152fd5b6104aa906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260206004840152602483019061204d565b60046040517ffc53a835000000000000000000000000000000000000000000000000000000008152fd5b9190916bffffffffffffffffffffffff8080941691160291821691820361238a57565b9190916fffffffffffffffffffffffffffffffff8080941691160191821161238a57565b9190916bffffffffffffffffffffffff8080941691160191821161238a57565b61354a9060206040519282848094519384920161202a565b810103902090565b929095949373ffffffffffffffffffffffffffffffffffffffff60a4351660a4350361078e5773ffffffffffffffffffffffffffffffffffffffff60a4351615613b8c573360005260026020526040600020846000526020526135c26135c96040600020604051928380926120e3565b0382611fae565b805115613b62578051600181149081613b2d575b50613b035767ffffffffffffffff86168015159081613af8575b50613ab55761363461360a368486612346565b867f0000000000000000000000000000000000000000000000000000000000000000438a33614cb9565b9733600052600060205260406000208660005260205260406000208960005260205260406000205460601c613a8b5765ffffffffffff606435166064350361078e5760c435926bffffffffffffffffffffffff8416840361078e576bffffffffffffffffffffffff6136b08565ffffffffffff606435166134cb565b1665ffffffffffff608435166084350361078e576bffffffffffffffffffffffff60e4351660e4350361078e57613704906bffffffffffffffffffffffff61169e60e43565ffffffffffff608435166134cb565b9733600052600060205260406000208860005260205260406000208b60005260205260406000207fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060a43560601b1665ffffffffffff606435166bffffffffffff00000000000060843560301b16171781556bffffffffffffffffffffffff86167fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060018301541617600182015561380560e43560018301907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b67ffffffffffffffff6101043516610104350361078e57600101805477ffffffffffffffffffffffffffffffffffffffffffffffff167fffffffffffffffff0000000000000000000000000000000000000000000000006101043560c01b161790558a9582908861387533613e5d565b956040519a8b9760208901600090528b60218a01528051908160418b0191602001916138a09261202a565b8801916041830137019260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016604184015260643560d01b7fffffffffffff0000000000000000000000000000000000000000000000000000166049840152604f83013701604f81016000905203602f81018552604f016139239085611fae565b61392c90613532565b906040519085825260643565ffffffffffff16602083015260843565ffffffffffff16604083015260a43573ffffffffffffffffffffffffffffffffffffffff1660608301526bffffffffffffffffffffffff16608082015260e4356bffffffffffffffffffffffff1660a08201526101043567ffffffffffffffff1660c082015260e07f1b7379b49b96ce33cfd9daf95718231650c40a2c92da579412e588601186772791a36139dc916140db565b6139e5916134ee565b6fffffffffffffffffffffffffffffffff9081811691823410613a3a575050803411613a12575060009190565b3403613a368173ffffffffffffffffffffffffffffffffffffffff60a4351661428b565b9190565b6040517f030748b50000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff928316600482015234919091169091166024820152604490fd5b60046040517f068a62ee000000000000000000000000000000000000000000000000000000008152fd5b6040517f2d098d5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff428116600483015287166024820152604490fd5b9050421015386135f7565b60046040517f17d0b6db000000000000000000000000000000000000000000000000000000008152fd5b9050156126e1577fff0000000000000000000000000000000000000000000000000000000000000060208201511615386135dd565b60046040517f9f994b4b000000000000000000000000000000000000000000000000000000008152fd5b60046040517f6a1a6afe000000000000000000000000000000000000000000000000000000008152fd5b92919493948560211161078e578360005260016020526040600020602060405180928486833784820190815203019020600184013560005260205260406000205480158015613c6a575b613c22575094613c1a612a969392612a9c96973691612346565b503691612346565b604490613c30368987612346565b60208151910120604051917f48ce7fac00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b50613c76368886612346565b60208151910120811415613c00565b9081602091031261078e575161ffff8116810361078e5790565b604051906060820182811067ffffffffffffffff821117611f475760405260006040838281528260208201520152565b613cd7613c9f565b928260011161078e576001823560f81c03613df5578260051161078e57600182013560e01c60408501528260061161078e5760058201906042823560f81c0291826006018060051161078e57851061078e57613ddc90946001840194816039613d4f613d488360068a0181876122b9565b3691612346565b602081519101206040516020810191825260208152613d6d81611f2b565b51902096613dd0612782601083018d613d8b82600e8701898b6122b9565b7fffff00000000000000000000000000000000000000000000000000000000000091358281169160028110613de0575b5050905060f01c9052603084019086886122b9565b60208c015201916122b9565b9091565b8391925060020360031b1b1616803880613dbb565b60046040517fbcd5ac7a000000000000000000000000000000000000000000000000000000008152fd5b61228a9492606092825260208201528160408201520191612197565b613e536040929594939560608352606083019061204d565b9460208201520152565b73ffffffffffffffffffffffffffffffffffffffff604051917f1400000000000000000000000000000000000000000000000000000000000000602084015260006021840152166041820152604181526080810181811067ffffffffffffffff821117611f475760405290565b8115613ed4570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b610100811015613f19576003600191821b040190565b60046040517f9308529b000000000000000000000000000000000000000000000000000000008152fd5b6040908151613f5181611f2b565b606081526000928360208093015273ffffffffffffffffffffffffffffffffffffffff9063ffffffff8151947ff951975a0000000000000000000000000000000000000000000000000000000086521660048501528484602481857f0000000000000000000000000000000000000000000000000000000000000000165afa9485156140d0578095613fe6575b505050505090565b9091929394503d8082873e613ffb8187611fae565b850190848683031261079357855167ffffffffffffffff968782116105ec57019383858403126104b25783519661403188611f2b565b85519081116105ec5785019183601f840112156107935782519261406061405785611fef565b96519687611fae565b838652878087019460051b8201019485116104b257918780969497959301915b8383106140a6575050505061409893508552016121d6565b908201523880808080613fde565b918094965096909294965183811681036105ec578152870195879590949093909290860191614080565b9051903d90823e3d90fd5b919073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001692604080948151907f1a90a2190000000000000000000000000000000000000000000000000000000082526020948583600481855afa8015612946578693600091614224575b50906141a36fffffffffffffffffffffffffffffffff6141e6949316998a97815193849188830152614194815180928a868601910161202a565b81010386810184520182611fae565b84519586809481937fb19a437e0000000000000000000000000000000000000000000000000000000083526000600484015260606024840152606483019061204d565b600f604483015203925af190811561421a5750614201575050565b8161421792903d106119b9576119aa8183611fae565b50565b513d6000823e3d90fd5b929450509181813d8311614254575b61423d8183611fae565b8101031261078e57518692859290916141a361415a565b503d614233565b3d15614286573d9061426c8261230c565b9161427a6040519384611fae565b82523d6000602084013e565b606090565b81471061434557600080809373ffffffffffffffffffffffffffffffffffffffff8294165af16142b961425b565b50156142c157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152fd5b602161263e91939293846040519586927ffd000000000000000000000000000000000000000000000000000000000000006020850152848401378101600083820152036001810185520183611fae565b9161263e947fffffffffffff000000000000000000000000000000000000000000000000000060b0957fffffffffffffffff000000000000000000000000000000000000000000000000939998949960416040519b8c987f010000000000000000000000000000000000000000000000000000000000000060208b015260218a01528189013760828701521660a28501521660a883015261449d815180926020868601910161202a565b8101036090810185520183611fae565b93919290948160211161078e57846000526001602052604060002060206040518092826144e08b8481519384920161202a565b8201908152030190206001850135600052602052604060002054614b4d57846000526001602052604060002060206040518092826145248b8481519384920161202a565b820190815203019020600185013560005260205260016040600020558160a31161078e57608f84013560601c8260621161078e578060005260036020526040600020866000526020526040600020548751602089012003614a40578260ab1161078e5760a385013560c01c8015159081614a36575b506148ef578260b11161078e57600060405180927fd00689b600000000000000000000000000000000000000000000000000000000825288600483015260018801356024830152608060448301526041608483015260416021890160a48401378260e5830152610100606483015281838161463f61010482018c60b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8d019101612197565b039260ab8b013560d01cf16000918161486c575b50614812575061466161425b565b505a65ffffffffffff603f60ab87013560d01c041611612ef6576148046147bf6001927f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32957fffffffffffff000000000000000000000000000000000000000000000000000061478760209761477e604051917fff000000000000000000000000000000000000000000000000000000000000008b8401528c60b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8301910160218501378201827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7091600083820152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50810184520182611fae565b935b5a9061237d565b60d01b167fffffffffffffffff0000000000000000000000000000000000000000000000004260c01b169160218a01878b01356143f3565b9687518489012087600052838552604060002085604051809285516147e78184868a0161202a565b820190815203019020848801356000528552604060002055613532565b93604051958652013593a390565b602092506147bf6001927f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32957fffffffffffff00000000000000000000000000000000000000000000000000006147876148049593614780565b9091503d806000833e61487f8183611fae565b81019060208183031261078e5780519067ffffffffffffffff821161078e5782601f83830101121561078e5781810151906148b98261230c565b936148c76040519586611fae565b82855260208385840101011161078e5760206148e89381860192010161202a565b9038614653565b50508060b19592951161078e5760016148047f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32936149547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f6020950160b188016143a3565b97614a1160b0604051809b888201947f01000000000000000000000000000000000000000000000000000000000000008652888c01356021840152604160218d018185013760828301527f800000000000000000000000000000000000000000000000000000000000000060a28301527fffffffffffffffff0000000000000000000000000000000000000000000000004260c01b1660a8830152614a01815180928b868601910161202a565b810103609081018c52018a611fae565b8851902087600052838552604060002085604051809285516147e78184868a0161202a565b9050421138614599565b50908060b11161078e576148046147bf6001927f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32957fffffffffffff0000000000000000000000000000000000000000000000000000614787602097614780604051957ffe000000000000000000000000000000000000000000000000000000000000008b8801528c60b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8301910160218901378601867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7091600083820152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50810188520186611fae565b60046040517fe954aba2000000000000000000000000000000000000000000000000000000008152fd5b604090929192019163ffffffff614b9081855116613f43565b9384515115614c7757816020860151164211614c1e575b5050614bb4835151613f03565b8210614bde57614bc393612710565b9015614bd9575060019060405161274e81611f92565b600091565b50505050600090604051614bf181611f2b565b600981527f6e6f2071756f72756d0000000000000000000000000000000000000000000000602082015290565b51811690614c2a6121e7565b1603614c37573880614ba7565b50505050600090604051614c4a81611f2b565b601881527f677561726469616e207365742068617320657870697265640000000000000000602082015290565b505050505050600090604051614c8c81611f2b565b601481527f696e76616c696420677561726469616e20736574000000000000000000000000602082015290565b9460b0929194614d5d947fffffffffffffffffffffffffffffffffffffffff000000000000000000000000937fffffffffffffffff000000000000000000000000000000000000000000000000604051988996602088019b813060601b168d5260601b16603488015260c01b166048860152605085015260708401526090830152614d4d815180926020868601910161202a565b8101036090810184520182611fae565b51902090565b929788929997959496919965ffffffffffff809b81831692828216841115614f14575b506bffffffffffffffffffffffff9b8c9283921602169b8c965a900399169489861115614f0b575b8188168a029b8c9889890197021692021601918383039060008080808573ffffffffffffffffffffffffffffffffffffffff98898b8492149788614f01575b1690f115614eaf575b5050811694811691828614614e855750600080808086819681159788614e7c575bf115614e2f575b505050614e2a9161428b565b929190565b600093508392839283928391614e72575b7f00000000000000000000000000000000000000000000000000000000000000001690f1156122955783853880614e1e565b6108fc9150614e40565b506108fc614e17565b9050600094858095508094508093508215614ea6575bf11561229557929190565b506108fc614e9b565b600093965083929550829182918290614ef8575b887f00000000000000000000000000000000000000000000000000000000000000001690f11561229557869288923880614df6565b506108fc614ec3565b6108fc9250614ded565b98508498614dae565b905038614d86565b939791839b9995979a939165ffffffffffff809181841693828216851115615161575b506bffffffffffffffffffffffff9e8f938492160216965a90039a16928a841115615158575b818a168b029d8e88019a8b95021692021601958187039060008080808573ffffffffffffffffffffffffffffffffffffffff9c8d8099849214978861514e575b1690f115615103575b5050809116981698888a146150e15767ffffffffffffffff8094169182156150b4574285160384169363a8c0000085116150ab575b92938493168290036000811361507f57615001935060000302613eca565b019586915b600080808086819681159788615076575bf11561502c575b5050614e2a9103809461428b565b6000809381938293839161506c575b7f00000000000000000000000000000000000000000000000000000000000000001690f1156122955784388061501e565b6108fc915061503b565b506108fc615017565b90508181101561509d576150939202613eca565b9003958691615006565b505050506000958691615006565b60009450614fe3565b5097945050968791508692600080808086819681159788614e7c57f115614e2f57505050614e2a9161428b565b9760008096925080955080945080939a508215614ea657f11561229557929190565b6000809350809281928290615145575b8a7f00000000000000000000000000000000000000000000000000000000000000001690f11561229557843880614fae565b506108fc615113565b6108fc9250614fa5565b99508299614f65565b905038614f3f56fea2646970667358221220650436a56158a64f21f64a45b35532f08d567f83e44ecf57f2389ce0c20ad58064736f6c634300081600330000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722
Deployed Bytecode
0x608060409080825260048036101561001657600080fd5b600091823560e01c9081631a90a21914611e53575080631cfe795114611e0b5780632c3c02a414611d5757806335e78cfe14611ce85780633a24ef3514611c655780633ccb217e14611c0c5780633fccfa0614611b985780634355866714611a515780634993ef33146119c05780634cf842b5146118be57806353391d3f1461184f5780635a640514146118155780635d758e22146115d757806364d42b171461152d57806365a686de146114425780638caa89ec146113cd5780638dedc1791461135e57806392006e73146112755780639a8a0592146111c7578063a9e1189314611112578063b172b2221461102f578063c0fd8bde14610f73578063d60b347f14610eb4578063daf1e16714610e77578063dd6487fc14610e0c578063dfe1aef914610a13578063e039f22414610938578063ea901f1a146108dc578063eb8d3f12146107dc578063f8ce560a14610796578063f951975a146106dc578063fbe3c2cd146105f0578063fd4716c8146104b65763ffbeacc71461019a57600080fd5b346104b257827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25780359067ffffffffffffffff6024358181116104ae576101eb9036908401611efd565b9590913386526020936003855282872086885285528287205461045d5787156104365733875260028552828720868852855282872091881161040a57506102328154612090565b601f81116103c7575b5085601f88116001146103025791879187986102f195947f84cad7de2c9074e70f8d697042ecb3c00ae9e6622320cb8c48bdd4d5d709e79699916102f7575b508360011b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8560031b1c19161790555b6102b7368385612346565b8581519101209033895260038652808920878a52865281818a20558051968796338852870152850152608060608501526080840191612197565b0390a180f35b90508401353861027a565b818752848720907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08916885b8181106103b05750918993917f84cad7de2c9074e70f8d697042ecb3c00ae9e6622320cb8c48bdd4d5d709e796999a6102f197969410610378575b5050600183811b0190556102ac565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88660031b161c19908601351690553880610369565b91928760018192868a01358155019401920161032e565b818752848720601f890160051c810191868a10610400575b601f0160051c01905b8181106103f5575061023b565b8781556001016103e8565b90915081906103df565b8660416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b82517f9f994b4b000000000000000000000000000000000000000000000000000000008152fd5b33875260028552828720868852855282872083517fdba4785000000000000000000000000000000000000000000000000000000000815291820186905281906104aa9060248301906120e3565b0390fd5b8480fd5b5080fd5b8284346104b257606090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5760c0929167ffffffffffffffff916104ff612007565b938180519161050d83611f76565b8083528060a0602094828682015282858201528288820152826080820152015273ffffffffffffffffffffffffffffffffffffffff80971681528083528181206024358252835281812060443582528352209282519361056c85611f76565b80549465ffffffffffff9485871698898352600186840194888a60301c16865283850199871c8a5201549788966bffffffffffffffffffffffff98899660a089880197898c16895289608082019c8c1c168c52019b8f1c8c5285519d8e525116908c015251169089015251169086015251166080840152511660a0820152f35b8280fd5b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937ffbe3c2cd0000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d2576020939261069f575b5061ffff905191168152f35b61ffff9192506106c490843d86116106cb575b6106bc8183611fae565b810190613c85565b9190610693565b503d6106b2565b81513d85823e3d90fd5b5091903461079357602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b257359163ffffffff92838116810361078e5761072a90613f43565b9281519385855260608501938151948488880152855180915287608088019601915b81811061076457505050948495015116908301520390f35b825173ffffffffffffffffffffffffffffffffffffffff168752958801959188019160010161074c565b600080fd5b80fd5b509190346107935760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261079357506107d560209235613f03565b9051908152f35b508290346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020825180927feb8d3f120000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9283156108d157809361088e575b60208463ffffffff855191168152f35b9092506020833d6020116108c9575b816108aa60209383611fae565b81010312610793575063ffffffff6108c36020936121d6565b9261087e565b3d915061089d565b8251903d90823e3d90fd5b8284346104b25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576109349061092161091c612007565b613e5d565b905191829160208352602083019061204d565b0390f35b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937fe039f2240000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d257602093926109e4575b50519015158152f35b610a05919250833d8511610a0c575b6109fd8183611fae565b8101906122a1565b90836109db565b503d6109f3565b50346104b257827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff908035828111610e0857610a629036908301611efd565b5050602435828111610e085790610a91610a82610a9f9336908401611efd565b90610a8b613c9f565b50613ccf565b959497938897939197614b77565b9015610dcc57506020838111610dc8577f0000000000000000000000000000000000000000000000000000000000000018853503610da0578061ffff87511696015193885195828701958652828752610af787611f2b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082018015610d7457818401357fff00000000000000000000000000000000000000000000000000000000000000167f010000000000000000000000000000000000000000000000000000000000000003610d4c5780602111610d485760218201359681606211610d4457606e83013560601c94858c528b81528c8c208b8d5281528c8c20898d5281528c8c205460601c610d1c57858c52600381528c8c20908b8d52528b8b2054908951902003610cf457609011610cf057823b15610cf0578593927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff508a8094610c3e60b0958f8e90519a8b98899788967f11cbf805000000000000000000000000000000000000000000000000000000008852019301918d8601613e1f565b03925af18015610ce657610c81575b5050506102f1907ff865f210bf9219c3ca273416db27a66557c44a930cfbc569e325afc1e809a30794955193849384613e3b565b819796929711610cba578552939450807ff865f210bf9219c3ca273416db27a66557c44a930cfbc569e325afc1e809a3076102f1610c4d565b6024826041897f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b88513d89823e3d90fd5b8880fd5b858b517fc970156c000000000000000000000000000000000000000000000000000000008152fd5b878d517f3d1553f8000000000000000000000000000000000000000000000000000000008152fd5b8a80fd5b8980fd5b858b517fd41c17e7000000000000000000000000000000000000000000000000000000008152fd5b60248a6032887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8288517f3c1e02c0000000000000000000000000000000000000000000000000000000008152fd5b8680fd5b826104aa6020928a519384937f08c379a0000000000000000000000000000000000000000000000000000000008552840152602483019061204d565b8380fd5b8284346104b257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2578060209273ffffffffffffffffffffffffffffffffffffffff610e5d612007565b168152600384528181206024358252845220549051908152f35b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576109349061092130613e5d565b5082346105ec5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020610eef612007565b602473ffffffffffffffffffffffffffffffffffffffff9182855196879485937fd60b347f00000000000000000000000000000000000000000000000000000000855216908301527f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d257602093926109e45750519015158152f35b5090346107935760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107935781359067ffffffffffffffff821161079357611009610fde6109348661101a610fd2610a8236898b01611efd565b95949892909389614b77565b939094519788809863ffffffff6040809261ffff815116855260208101516020860152015116910152565b60c0606088015260c0870191612197565b911515608085015283820360a085015261204d565b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937fb172b2220000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d25783926110da575b6020838351908152f35b9091506020813d60201161110a575b816110f660209383611fae565b810103126105ec57602092505190836110d0565b3d91506110e9565b5090346107935760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107935781359067ffffffffffffffff8211610793576111a36111b46109348661117461116e36888a01611efd565b90613ccf565b94979290939195519889809963ffffffff6040809261ffff815116855260208101516020860152015116910152565b60c0606089015260c0880191612197565b92608086015284830360a0860152612197565b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937f9a8a05920000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d2576020939261069f575061ffff905191168152f35b50346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576020835180927f1a90a2190000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa908115611354578291611322575b5082519182526020820152f35b90506020813d60201161134c575b8161133d60209383611fae565b810103126104b2575138611315565b3d9150611330565b83513d84823e3d90fd5b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e168152f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff90602435828111610e08576114199036908301611efd565b6044929192359384116104ae5761143661143f9436908401611efd565b93909235613bb6565b80f35b5091906101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107935767ffffffffffffffff906024358281116104b2576114919036908601611efd565b6044929192358481116105ec576114ab9036908801611efd565b93909260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c36011261079357610124359586168603610793575060418203611505576114f9959635613552565b82519182526020820152f35b8686517fbbcd8eb1000000000000000000000000000000000000000000000000000000008152fd5b5082346105ec57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec576020815180937f64d42b170000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d25783926110da576020838351908152f35b508260a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5761160b612007565b91604435926064356bffffffffffffffffffffffff928382168203610dc8576084359284841684036118115773ffffffffffffffffffffffffffffffffffffffff1687528660205284872060243588526020528487208688526020528487209081548060601c156117e9576116a5908661169e8765ffffffffffff836116938a8388166134cb565b169460301c166134cb565b16906134ee565b906fffffffffffffffffffffffffffffffff90818316340361179357505050926117847fffffffffffffffffffffffffffffffffffffffff00000000000000000000000093829360017f0f9ca48bcb4f4aea18e50df9f1df3c02df5c9c48f05e389a349c8ceb242da58198970190611730611724835492878416613512565b93868360601c16613512565b9485931696879116178155907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b8351928352166020820152a280f35b87517f0b52a60b0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff938416918101918252349092169290921660208301529081906040010390fd5b5085517f970e41ec000000000000000000000000000000000000000000000000000000008152fd5b8780fd5b8284346104b25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25751908152602090f35b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff8135818111610e08576118999036908401611efd565b50506024359081116105ec5761143f916118b591369101611efd565b60443591612986565b5082346105ec5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5760206118f9612007565b602473ffffffffffffffffffffffffffffffffffffffff9182855196879485937f4cf842b500000000000000000000000000000000000000000000000000000000855216908301527f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d25760209392611987575b5067ffffffffffffffff905191168152f35b67ffffffffffffffff9192506119b290843d86116119b9575b6119aa8183611fae565b810190612966565b9190611975565b503d6119a0565b8284346104b257807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25780611a3f611a38926109349473ffffffffffffffffffffffffffffffffffffffff611a19612007565b16815260026020528181206024358252602052208251938480926120e3565b0383611fae565b5191829160208352602083019061204d565b509034610793577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6060813601126104b25767ffffffffffffffff92602435848111610e0857611aa49036908301611efd565b939092604435908682116105ec57879082360301126104b257865195611ac987611f2b565b818401359081116105ec578101366023820112156105ec5783810135611aee81611fef565b91611afb8a519384611fae565b8183526020916024602085019160051b83010191368311610dc857602401905b828210611b6c5750505090875250602401359063ffffffff82168203610793575091611b5293918593602061093497015235612710565b83929192519384931515845280602085015283019061204d565b813573ffffffffffffffffffffffffffffffffffffffff81168103611811578152908301908301611b1b565b5060807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25767ffffffffffffffff90602435828111610e0857611be49036908301611efd565b916064359384116104ae57611bff61143f9436908301611efd565b93909260443592356123b9565b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b257602090517f00000000000000000000000000000000000000000000000000000000000000188152f35b508290346105ec5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec5760243567ffffffffffffffff8111610e08579282916020611cbc819636908401611efd565b923584526001825284842083865194859384378201908152030190206044358252845220549051908152f35b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b2576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722168152f35b5082346105ec5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126105ec578051917f2c3c02a400000000000000000000000000000000000000000000000000000000835280359083015260208260248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d257602093926109e45750519015158152f35b8284346104b257817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104b25760209063ffffffff611e4b6121e7565b915191168152f35b9184915034610e0857837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e0857826020917f1a90a2190000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9182156106d25783926110da576020838351908152f35b9181601f8401121561078e5782359167ffffffffffffffff831161078e576020838186019501011161078e57565b6040810190811067ffffffffffffffff821117611f4757604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60c0810190811067ffffffffffffffff821117611f4757604052565b6020810190811067ffffffffffffffff821117611f4757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611f4757604052565b67ffffffffffffffff8111611f475760051b60200190565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361078e57565b60005b83811061203d5750506000910152565b818101518382015260200161202d565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f6020936120898151809281875287808801910161202a565b0116010190565b90600182811c921680156120d9575b60208310146120aa57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f169161209f565b8054600093926120f282612090565b9182825260209360019160018116908160001461215a5750600114612119575b5050505050565b90939495506000929192528360002092846000945b83861061214657505050500101903880808080612112565b80548587018301529401938590820161212e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168685015250505090151560051b010191503880808080612112565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b519063ffffffff8216820361078e57565b6040517f1cfe795100000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9081156122955760009161225e575090565b90506020813d60201161228d575b8161227960209383611fae565b8101031261078e5761228a906121d6565b90565b3d915061226c565b6040513d6000823e3d90fd5b9081602091031261078e5751801515810361078e5790565b9093929384831161078e57841161078e578101920390565b3590602081106122df575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b67ffffffffffffffff8111611f4757601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926123528261230c565b916123606040519384611fae565b82948184528183011161078e578281602093846000960137010152565b9190820391821161238a57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b939492909280156126e1577fff000000000000000000000000000000000000000000000000000000000000008635166126b7578060211161078e57600186013591600091868352600160205260409788842060208a518092898b8337898201908152030190208585526020528884205461268e578160ab11610e085760a3810135928360c01c80158015612684575b6126405750826062116104ae578260b1116104ae577fffffffffffffffff00000000000000000000000000000000000000000000000097946125a99997947f4b43f0920d04b8fb5f32167b55abce15859e8d8bb1bfe84ba2909268d77de8d2946125a39894612578948e519c8d947f0200000000000000000000000000000000000000000000000000000000000000602087015288602187015260416021850181880137166082850152608a84015260b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8301910160aa84013781017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99283820152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd981018a520188611fae565b885180916060825261258e606083018688612197565b908860208401528b8301520390a13691612346565b506140db565b906fffffffffffffffffffffffffffffffff908134169183168083036125cf5750505050565b80839495929311612625575050517f030748b50000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b90935061263e925061263891503461237d565b3361428b565b565b8a517f862c57f400000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9182166004820152429091166024820152604490fd5b5042811015612448565b600489517f7b042609000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3fcdbaba000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90929160008351519282156126e15760f890863560f81c9560019560005b888110612755575050505050505050505060019060405161274e81611f92565b6000815290565b868810156126e15760218801978a81013580871c9789906127a88e6127886127828f6001890187856122b9565b906122d1565b9d604261279e61278260418a01809489876122b9565b97019485926122b9565b9390357fff00000000000000000000000000000000000000000000000000000000000000908181169560018110612951575b5050508960ff939d6040968751918c83528660209889951c601b011684840152888301526060820152600080525a6000916001608092fa156129465773ffffffffffffffffffffffffffffffffffffffff91826000511693841561291d578715918215612911575b5050156128e8578a878110156128bf57885190815111156126e157611fe0859260f31c16010151160361287957505060010161272e565b99509a995050505050505050507f564d207369676e617475726520696e76616c696400000000000000000000000060009351916128b583611f2b565b6014835282015290565b600486517fba5b4315000000000000000000000000000000000000000000000000000000008152fd5b600485517f4fe17a6f000000000000000000000000000000000000000000000000000000008152fd5b168c1190503880612842565b600487517fba0e0fd5000000000000000000000000000000000000000000000000000000008152fd5b84513d6000823e3d90fd5b60010360031b82901b161693503880806127da565b9081602091031261078e575167ffffffffffffffff8116810361078e5790565b915a9281156134a1576129aa9261299f91610a8b613c9f565b949692909387614b77565b901561346357508160201161078e577f0000000000000000000000000000000000000000000000000000000000000018918282350361343957602061ffff8651169501519260405193602085015260208452612a0584611f2b565b60208301926000917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe084011561340c5784357fff000000000000000000000000000000000000000000000000000000000000001680612b27575050505091612a9693917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0612a9c97940191866144ad565b906140db565b6fffffffffffffffffffffffffffffffff803416908216808203612abf57505050565b808293949211612b165750506040517f030748b50000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b61263891935061263e92503461237d565b9091959692989394507f01000000000000000000000000000000000000000000000000000000000000008114600014612f745750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301602111610dc8577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301606211610dc857606e84013560601c87528660205260408720868852602052604087206021850135885260205260408720600181549101548160601c15612f4a57606e86013560601c89528860205260408920888a526020526040892060218701358a52602052886001604082208281550155606e86013560601c8952600360205260408920888a5260205260408920548751602089012003612f20577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08501609011610cf057888086612cdf612d0b8c6040519283918d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5060208501977f11cbf8050000000000000000000000000000000000000000000000000000000089520191602160b083019201359060248601613e1f565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282611fae565b519082606e8b013560601c65ffffffffffff8860301c16f115612edd575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08501608811610cf0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060a287013560d01c9501608211610cf0577f9f9aef021b9656e38cd2086f7d7ee0f6b6062c8078a049bfecd6356fdbeb1b3c9593612ed59360219693612e1f9373ffffffffffffffffffffffffffffffffffffffff9160a889013560c01c94838360c01c95169360828b013516928260601c926bffffffffffffffffffffffff8260601c16928a65ffffffffffff6bffffffffffffffffffffffff818560301c169516931691614f1c565b9890927ff865f210bf9219c3ca273416db27a66557c44a930cfbc569e325afc1e809a30760405180612e578c868c8c01359184613e3b565b0390a167ffffffffffffffff612e7d6fffffffffffffffffffffffffffffffff9a613532565b9980604051998a9901359c1695169316918693916080939695919660a0860197865267ffffffffffffffff80921660208701521660408501526fffffffffffffffffffffffffffffffff809216606085015216910152565b0390a3612a9c565b5a65ffffffffffff603f818560301c1604161115612d29575b60046040517f6bc33587000000000000000000000000000000000000000000000000000000008152fd5b60046040517fc970156c000000000000000000000000000000000000000000000000000000008152fd5b60046040517f8af35858000000000000000000000000000000000000000000000000000000008152fd5b91969392917f0200000000000000000000000000000000000000000000000000000000000000036133e2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0830160621161181157606e85013560601c885260036020526040882084895260205260408820548651602088012003612f20577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301606a1161181157608285013560c01c904282108015906133da575b613397577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08401608a11610cf05790846130a992613095367fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff56880160aa8b01612346565b92608a89013590606e8a013560601c614cb9565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301602111611811576021850135810361335c5750606e84013560601c875286602052604087208388526020526040872060218501358852602052604087209560018754970154928760601c15612f4a578861317e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff568293606e8a013560601c845283602052604084208985526020526040842060218b013585526020528360016040822082815501550160aa89016143a3565b6040516131d181612cdf60208201947f11cbf8050000000000000000000000000000000000000000000000000000000086528b602484015260218d0135604484015260606064840152608483019061204d565b519082606e8a013560601c65ffffffffffff8d60301c16f11561333a575b9261327d7f9f9aef021b9656e38cd2086f7d7ee0f6b6062c8078a049bfecd6356fdbeb1b3c9593612ed59373ffffffffffffffffffffffffffffffffffffffff602197169182918b60601c916bffffffffffffffffffffffff8160601c16918d6bffffffffffffffffffffffff65ffffffffffff8260301c1693169165ffffffffffff808316921690614d63565b9890927fb40655ff3c2bb146ef446b11cb0b22131f89ccd00240340eda5ef4e010794ea1604051806132b58c858c8c01359184613e3b565b0390a165ffffffffffff67ffffffffffffffff6132e26fffffffffffffffffffffffffffffffff9b613532565b6040805194855267ffffffffffffffff9390951683166020850152941616918101919091526fffffffffffffffffffffffffffffffff92881683166060820152979096161660808701529101359390819060a0820190565b9391925a65ffffffffffff603f818a60301c16041611612ef6579291936131ef565b846044916021604051927fe020885d000000000000000000000000000000000000000000000000000000008452013560048301526024820152fd5b6040517f862c57f400000000000000000000000000000000000000000000000000000000815267ffffffffffffffff838116600483015242166024820152604490fd5b508115613031565b60046040517fd41c17e7000000000000000000000000000000000000000000000000000000008152fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526032600452fd5b60046040517f3c1e02c0000000000000000000000000000000000000000000000000000000008152fd5b6104aa906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260206004840152602483019061204d565b60046040517ffc53a835000000000000000000000000000000000000000000000000000000008152fd5b9190916bffffffffffffffffffffffff8080941691160291821691820361238a57565b9190916fffffffffffffffffffffffffffffffff8080941691160191821161238a57565b9190916bffffffffffffffffffffffff8080941691160191821161238a57565b61354a9060206040519282848094519384920161202a565b810103902090565b929095949373ffffffffffffffffffffffffffffffffffffffff60a4351660a4350361078e5773ffffffffffffffffffffffffffffffffffffffff60a4351615613b8c573360005260026020526040600020846000526020526135c26135c96040600020604051928380926120e3565b0382611fae565b805115613b62578051600181149081613b2d575b50613b035767ffffffffffffffff86168015159081613af8575b50613ab55761363461360a368486612346565b867f0000000000000000000000000000000000000000000000000000000000000018438a33614cb9565b9733600052600060205260406000208660005260205260406000208960005260205260406000205460601c613a8b5765ffffffffffff606435166064350361078e5760c435926bffffffffffffffffffffffff8416840361078e576bffffffffffffffffffffffff6136b08565ffffffffffff606435166134cb565b1665ffffffffffff608435166084350361078e576bffffffffffffffffffffffff60e4351660e4350361078e57613704906bffffffffffffffffffffffff61169e60e43565ffffffffffff608435166134cb565b9733600052600060205260406000208860005260205260406000208b60005260205260406000207fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060a43560601b1665ffffffffffff606435166bffffffffffff00000000000060843560301b16171781556bffffffffffffffffffffffff86167fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060018301541617600182015561380560e43560018301907fffffffffffffffff000000000000000000000000ffffffffffffffffffffffff77ffffffffffffffffffffffff00000000000000000000000083549260601b169116179055565b67ffffffffffffffff6101043516610104350361078e57600101805477ffffffffffffffffffffffffffffffffffffffffffffffff167fffffffffffffffff0000000000000000000000000000000000000000000000006101043560c01b161790558a9582908861387533613e5d565b956040519a8b9760208901600090528b60218a01528051908160418b0191602001916138a09261202a565b8801916041830137019260c01b7fffffffffffffffff00000000000000000000000000000000000000000000000016604184015260643560d01b7fffffffffffff0000000000000000000000000000000000000000000000000000166049840152604f83013701604f81016000905203602f81018552604f016139239085611fae565b61392c90613532565b906040519085825260643565ffffffffffff16602083015260843565ffffffffffff16604083015260a43573ffffffffffffffffffffffffffffffffffffffff1660608301526bffffffffffffffffffffffff16608082015260e4356bffffffffffffffffffffffff1660a08201526101043567ffffffffffffffff1660c082015260e07f1b7379b49b96ce33cfd9daf95718231650c40a2c92da579412e588601186772791a36139dc916140db565b6139e5916134ee565b6fffffffffffffffffffffffffffffffff9081811691823410613a3a575050803411613a12575060009190565b3403613a368173ffffffffffffffffffffffffffffffffffffffff60a4351661428b565b9190565b6040517f030748b50000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff928316600482015234919091169091166024820152604490fd5b60046040517f068a62ee000000000000000000000000000000000000000000000000000000008152fd5b6040517f2d098d5900000000000000000000000000000000000000000000000000000000815267ffffffffffffffff428116600483015287166024820152604490fd5b9050421015386135f7565b60046040517f17d0b6db000000000000000000000000000000000000000000000000000000008152fd5b9050156126e1577fff0000000000000000000000000000000000000000000000000000000000000060208201511615386135dd565b60046040517f9f994b4b000000000000000000000000000000000000000000000000000000008152fd5b60046040517f6a1a6afe000000000000000000000000000000000000000000000000000000008152fd5b92919493948560211161078e578360005260016020526040600020602060405180928486833784820190815203019020600184013560005260205260406000205480158015613c6a575b613c22575094613c1a612a969392612a9c96973691612346565b503691612346565b604490613c30368987612346565b60208151910120604051917f48ce7fac00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b50613c76368886612346565b60208151910120811415613c00565b9081602091031261078e575161ffff8116810361078e5790565b604051906060820182811067ffffffffffffffff821117611f475760405260006040838281528260208201520152565b613cd7613c9f565b928260011161078e576001823560f81c03613df5578260051161078e57600182013560e01c60408501528260061161078e5760058201906042823560f81c0291826006018060051161078e57851061078e57613ddc90946001840194816039613d4f613d488360068a0181876122b9565b3691612346565b602081519101206040516020810191825260208152613d6d81611f2b565b51902096613dd0612782601083018d613d8b82600e8701898b6122b9565b7fffff00000000000000000000000000000000000000000000000000000000000091358281169160028110613de0575b5050905060f01c9052603084019086886122b9565b60208c015201916122b9565b9091565b8391925060020360031b1b1616803880613dbb565b60046040517fbcd5ac7a000000000000000000000000000000000000000000000000000000008152fd5b61228a9492606092825260208201528160408201520191612197565b613e536040929594939560608352606083019061204d565b9460208201520152565b73ffffffffffffffffffffffffffffffffffffffff604051917f1400000000000000000000000000000000000000000000000000000000000000602084015260006021840152166041820152604181526080810181811067ffffffffffffffff821117611f475760405290565b8115613ed4570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b610100811015613f19576003600191821b040190565b60046040517f9308529b000000000000000000000000000000000000000000000000000000008152fd5b6040908151613f5181611f2b565b606081526000928360208093015273ffffffffffffffffffffffffffffffffffffffff9063ffffffff8151947ff951975a0000000000000000000000000000000000000000000000000000000086521660048501528484602481857f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722165afa9485156140d0578095613fe6575b505050505090565b9091929394503d8082873e613ffb8187611fae565b850190848683031261079357855167ffffffffffffffff968782116105ec57019383858403126104b25783519661403188611f2b565b85519081116105ec5785019183601f840112156107935782519261406061405785611fef565b96519687611fae565b838652878087019460051b8201019485116104b257918780969497959301915b8383106140a6575050505061409893508552016121d6565b908201523880808080613fde565b918094965096909294965183811681036105ec578152870195879590949093909290860191614080565b9051903d90823e3d90fd5b919073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec97221692604080948151907f1a90a2190000000000000000000000000000000000000000000000000000000082526020948583600481855afa8015612946578693600091614224575b50906141a36fffffffffffffffffffffffffffffffff6141e6949316998a97815193849188830152614194815180928a868601910161202a565b81010386810184520182611fae565b84519586809481937fb19a437e0000000000000000000000000000000000000000000000000000000083526000600484015260606024840152606483019061204d565b600f604483015203925af190811561421a5750614201575050565b8161421792903d106119b9576119aa8183611fae565b50565b513d6000823e3d90fd5b929450509181813d8311614254575b61423d8183611fae565b8101031261078e57518692859290916141a361415a565b503d614233565b3d15614286573d9061426c8261230c565b9161427a6040519384611fae565b82523d6000602084013e565b606090565b81471061434557600080809373ffffffffffffffffffffffffffffffffffffffff8294165af16142b961425b565b50156142c157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152fd5b602161263e91939293846040519586927ffd000000000000000000000000000000000000000000000000000000000000006020850152848401378101600083820152036001810185520183611fae565b9161263e947fffffffffffff000000000000000000000000000000000000000000000000000060b0957fffffffffffffffff000000000000000000000000000000000000000000000000939998949960416040519b8c987f010000000000000000000000000000000000000000000000000000000000000060208b015260218a01528189013760828701521660a28501521660a883015261449d815180926020868601910161202a565b8101036090810185520183611fae565b93919290948160211161078e57846000526001602052604060002060206040518092826144e08b8481519384920161202a565b8201908152030190206001850135600052602052604060002054614b4d57846000526001602052604060002060206040518092826145248b8481519384920161202a565b820190815203019020600185013560005260205260016040600020558160a31161078e57608f84013560601c8260621161078e578060005260036020526040600020866000526020526040600020548751602089012003614a40578260ab1161078e5760a385013560c01c8015159081614a36575b506148ef578260b11161078e57600060405180927fd00689b600000000000000000000000000000000000000000000000000000000825288600483015260018801356024830152608060448301526041608483015260416021890160a48401378260e5830152610100606483015281838161463f61010482018c60b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8d019101612197565b039260ab8b013560d01cf16000918161486c575b50614812575061466161425b565b505a65ffffffffffff603f60ab87013560d01c041611612ef6576148046147bf6001927f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32957fffffffffffff000000000000000000000000000000000000000000000000000061478760209761477e604051917fff000000000000000000000000000000000000000000000000000000000000008b8401528c60b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8301910160218501378201827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7091600083820152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50810184520182611fae565b935b5a9061237d565b60d01b167fffffffffffffffff0000000000000000000000000000000000000000000000004260c01b169160218a01878b01356143f3565b9687518489012087600052838552604060002085604051809285516147e78184868a0161202a565b820190815203019020848801356000528552604060002055613532565b93604051958652013593a390565b602092506147bf6001927f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32957fffffffffffff00000000000000000000000000000000000000000000000000006147876148049593614780565b9091503d806000833e61487f8183611fae565b81019060208183031261078e5780519067ffffffffffffffff821161078e5782601f83830101121561078e5781810151906148b98261230c565b936148c76040519586611fae565b82855260208385840101011161078e5760206148e89381860192010161202a565b9038614653565b50508060b19592951161078e5760016148047f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32936149547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f6020950160b188016143a3565b97614a1160b0604051809b888201947f01000000000000000000000000000000000000000000000000000000000000008652888c01356021840152604160218d018185013760828301527f800000000000000000000000000000000000000000000000000000000000000060a28301527fffffffffffffffff0000000000000000000000000000000000000000000000004260c01b1660a8830152614a01815180928b868601910161202a565b810103609081018c52018a611fae565b8851902087600052838552604060002085604051809285516147e78184868a0161202a565b9050421138614599565b50908060b11161078e576148046147bf6001927f6025efc987827001c12f1f5c8b7831a6439083c88f60686671451fe479ec6e32957fffffffffffff0000000000000000000000000000000000000000000000000000614787602097614780604051957ffe000000000000000000000000000000000000000000000000000000000000008b8801528c60b17fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4f8301910160218901378601867fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7091600083820152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff50810188520186611fae565b60046040517fe954aba2000000000000000000000000000000000000000000000000000000008152fd5b604090929192019163ffffffff614b9081855116613f43565b9384515115614c7757816020860151164211614c1e575b5050614bb4835151613f03565b8210614bde57614bc393612710565b9015614bd9575060019060405161274e81611f92565b600091565b50505050600090604051614bf181611f2b565b600981527f6e6f2071756f72756d0000000000000000000000000000000000000000000000602082015290565b51811690614c2a6121e7565b1603614c37573880614ba7565b50505050600090604051614c4a81611f2b565b601881527f677561726469616e207365742068617320657870697265640000000000000000602082015290565b505050505050600090604051614c8c81611f2b565b601481527f696e76616c696420677561726469616e20736574000000000000000000000000602082015290565b9460b0929194614d5d947fffffffffffffffffffffffffffffffffffffffff000000000000000000000000937fffffffffffffffff000000000000000000000000000000000000000000000000604051988996602088019b813060601b168d5260601b16603488015260c01b166048860152605085015260708401526090830152614d4d815180926020868601910161202a565b8101036090810184520182611fae565b51902090565b929788929997959496919965ffffffffffff809b81831692828216841115614f14575b506bffffffffffffffffffffffff9b8c9283921602169b8c965a900399169489861115614f0b575b8188168a029b8c9889890197021692021601918383039060008080808573ffffffffffffffffffffffffffffffffffffffff98898b8492149788614f01575b1690f115614eaf575b5050811694811691828614614e855750600080808086819681159788614e7c575bf115614e2f575b505050614e2a9161428b565b929190565b600093508392839283928391614e72575b7f0000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e1690f1156122955783853880614e1e565b6108fc9150614e40565b506108fc614e17565b9050600094858095508094508093508215614ea6575bf11561229557929190565b506108fc614e9b565b600093965083929550829182918290614ef8575b887f0000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e1690f11561229557869288923880614df6565b506108fc614ec3565b6108fc9250614ded565b98508498614dae565b905038614d86565b939791839b9995979a939165ffffffffffff809181841693828216851115615161575b506bffffffffffffffffffffffff9e8f938492160216965a90039a16928a841115615158575b818a168b029d8e88019a8b95021692021601958187039060008080808573ffffffffffffffffffffffffffffffffffffffff9c8d8099849214978861514e575b1690f115615103575b5050809116981698888a146150e15767ffffffffffffffff8094169182156150b4574285160384169363a8c0000085116150ab575b92938493168290036000811361507f57615001935060000302613eca565b019586915b600080808086819681159788615076575bf11561502c575b5050614e2a9103809461428b565b6000809381938293839161506c575b7f0000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e1690f1156122955784388061501e565b6108fc915061503b565b506108fc615017565b90508181101561509d576150939202613eca565b9003958691615006565b505050506000958691615006565b60009450614fe3565b5097945050968791508692600080808086819681159788614e7c57f115614e2f57505050614e2a9161428b565b9760008096925080955080945080939a508215614ea657f11561229557929190565b6000809350809281928290615145575b8a7f0000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e1690f11561229557843880614fae565b506108fc615113565b6108fc9250614fa5565b99508299614f65565b905038614f3f56fea2646970667358221220650436a56158a64f21f64a45b35532f08d567f83e44ecf57f2389ce0c20ad58064736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722
-----Decoded View---------------
Arg [0] : sendLostGasTo (address): 0x0000000099263f0735D03bB2787cE8FB84f6ED6E
Arg [1] : wormhole_ (address): 0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000099263f0735d03bb2787ce8fb84f6ed6e
Arg [1] : 000000000000000000000000ee91c335eab126df5fdb3797ea9d6ad93aec9722
Deployed Bytecode Sourcemap
1304:3702:3:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1750:27:4;;1304:3702:3;1750:27:4;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1298:27:4;1304:3702:3;1298:27:4;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;1192:24:4;1304:3702:3;1192:24:4;;;1304:3702:3;;;;;1633:35:4;1304:3702:3;1633:35:4;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;1400:23:4;1304:3702:3;1400:23:4;;;1304:3702:3;;;;;795:37:4;1304:3702:3;795:37:4;;;1304:3702:3;;;;;;;;;;1510:34:4;1304:3702:3;1510:34:4;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;13444:10:1;;;;1304:3702:3;;;;13418:25:1;1304:3702:3;;;;;;;;;;;;;;13414:198:1;;13626:26;;13622:67;;13444:10;1304:3702:3;;13700:21:1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;13946:95:1;1304:3702:3;;;;;;;;;;;;13418:25:1;1304:3702:3;;;;;;;;2849:3:2;1304:3702:3;2849:3:2;;;:::i;:::-;1304:3702:3;;;;;13813:25:1;13444:10;;1304:3702:3;;13418:25:1;1304:3702:3;;;;;;;;;;;;;;;;;13444:10:1;;;;1304:3702:3;;;;;;;;;;;;;;;;;;:::i;:::-;13946:95:1;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;13946:95:1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;13418:25:1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;;;13622:67:1;1304:3702:3;;13661:28:1;;;;13414:198;13444:10;1304:3702:3;;13546:21:1;1304:3702:3;;;;;;;;;;;;;;;13501:111:1;;;;;;1304:3702:3;;;;;;;;;;;;:::i;:::-;13501:111:1;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2670:3:2;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1510:34:4;1304:3702:3;;1510:34:4;;1304:3702:3;1510:34:4;;:14;1304:3702:3;1510:14:4;1304:3702:3;1510:34:4;;;;;;;;;;;;1304:3702:3;;;;;;;;;;1510:34:4;1304:3702:3;1510:34:4;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;795:37:4;1304:3702:3;;795:37:4;;1304:3702:3;795:37:4;;:14;1304:3702:3;795:14:4;1304:3702:3;795:37:4;;;;;;;;;;;1304:3702:3;795:37:4;1304:3702:3;;;;;;;;;795:37:4;;;;;;;;;;;;;;;;;;:::i;:::-;;;1304:3702:3;;;;;;;795:37:4;1304:3702:3;;:::i;:::-;795:37:4;;;;;;-1:-1:-1;795:37:4;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;1400:23:4;1304:3702:3;;1400:23:4;;1304:3702:3;1400:23:4;;:14;1304:3702:3;1400:14:4;1304:3702:3;1400:23:4;;;;;;;;;;;;1304:3702:3;;;;;;;;;1400:23:4;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;1069:18:6;1304:3702:3;1261:42:6;1304:3702:3;;;;;;:::i;:::-;;;;:::i;:::-;;1069:18:6;:::i;:::-;1261:42;;;;;;;;;;:::i;:::-;1304:3702:3;;;;;;1853:2:2;;;;;3734:24:3;1853:2:2;;3704:54:3;3700:87;;1304:3702;;;;;4012:17;;1304:3702;;;;;;;;;;;;;;;;;:::i;:::-;1853:2:2;;;1746:1;;;;1853:2;;;61662:10:1;1746:1:2;;;61786:36:1;1746:1:2;;1853:2;;;;;;;;;;;2126;1853;;;;;;2016;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;62059:121:1;;1304:3702:3;;;62347:25:1;1304:3702:3;;;;;;;;;;;;;;;;;62465:35:1;;62424:76;62420:119;;3395:3:2;1853:2;;;62553:114:1;;;;;1304:3702:3;;;1853:2:2;1304:3702:3;;;62553:114:1;1853:2:2;1304:3702:3;;;;;62553:114:1;;;;;;;1304:3702:3;62553:114:1;;1853:2:2;;;62553:114:1;;;;;:::i;:::-;;;;;;;;;;;61782:1048;1304:3702:3;;;62686:74:1;1304:3702:3;62686:74:1;1304:3702:3;;;62686:74:1;;;;;:::i;62553:114::-;1304:3702:3;;;;;;;;;;;;-1:-1:-1;1304:3702:3;62686:74:1;;62553:114;;1304:3702:3;;;;;;;;;;62553:114:1;1304:3702:3;;;;;;;;;62553:114:1;1304:3702:3;;;62420:119:1;1304:3702:3;;;62509:30:1;;;;62059:121;1304:3702:3;;;62157:23:1;;;;1853:2:2;1304:3702:3;;;1853:2:2;1304:3702:3;;;61782:1048:1;1304:3702:3;;;62798:21:1;;;;1746:1:2;1304:3702:3;1746:1:2;;;;;;;;3700:87:3;1304:3702;;;3767:20;;;;1853:2:2;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;4115:80:1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;918:4:17;895:29;918:4;895:29;:::i;1304:3702:3:-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;1080:34:4;;;;;1304:3702:3;1080:34:4;;1304:3702:3;1080:34:4;;;1304:3702:3;1080:14:4;1304:3702:3;1080:34:4;;;;;;;1304:3702:3;1080:34:4;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1261:42:6;1304:3702:3;;;1069:18:6;1304:3702:3;;;;;;:::i;1069:18:6:-;1261:42;;;;;;;;:::i;:::-;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1633:35:4;1304:3702:3;;1633:35:4;;1304:3702:3;1633:35:4;;:14;1304:3702:3;1633:14:4;1304:3702:3;1633:35:4;;;;;;;;;;;1304:3702:3;1633:35:4;1304:3702:3;;;;;;;1633:35:4;;;;;;;;;;;;;;;;;;:::i;:::-;;;1304:3702:3;;;;1633:35:4;1304:3702:3;;;1633:35:4;;;;;;;-1:-1:-1;1633:35:4;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;1192:24:4;1304:3702:3;;1192:24:4;;1304:3702:3;1192:24:4;;:14;1304:3702:3;1192:14:4;1304:3702:3;1192:24:4;;;;;;;;;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;2198:21;1304:3702;;2198:21;;1304:3702;2198:21;;:8;1304:3702;2198:8;1304:3702;2198:21;;;;;;;;;;;1304:3702;;;;;;;2198:21;1304:3702;;;;2198:21;;;;;;;;;;;;;;;;;:::i;:::-;;;1304:3702;;;;;2198:21;;;;;;-1:-1:-1;2198:21:3;;;1304:3702;;;;;;;;;;;;;;;;;;;;;;;;;;3171:41:1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;219:30:17;247:2;219:30;;335:69;;18013:4228:1;1304:3702:3;;;18013:4228:1;:::i;:::-;1304:3702:3;;;;;;;;;;335:69:17;1304:3702:3;;;381:23:17;;;;1304:3702:3;;;;;;;;;;;;;1298:27:4;1304:3702:3;;1298:27:4;;1304:3702:3;1298:27:4;;:14;1304:3702:3;1298:14:4;1304:3702:3;1298:27:4;;;;;;;;;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15109:35:1;15105:69;;15394:26;1304:3702:3;;15329:41:1;1304:3702:3;;;15248:51:1;1304:3702:3;;;;15248:51:1;:::i;:::-;1304:3702:3;;;;;15329:41:1;:::i;:::-;1304:3702:3;15394:26:1;;:::i;:::-;2849:3:2;;;;;;15482:9:1;:16;15478:76;;15596:28;;;;15831:42;1304:3702:3;15596:28:1;;;1304:3702:3;15942:121:1;15596:28;;;1304:3702:3;15687:45:1;15596:55;1304:3702:3;;;;;;15596:55:1;:::i;:::-;1304:3702:3;;;;;;15687:45:1;:::i;:::-;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;15831:42:1;1304:3702:3;;;;;;;;;;15942:121:1;1304:3702:3;;15478:76:1;1304:3702:3;;15507:47:1;;;2849:3:2;;;;15507:47:1;;;2849:3:2;;;15482:9:1;2849:3:2;;;;;;;;;;;1304:3702:3;;;2849:3:2;;13501:111:1;;;15105:69;1304:3702:3;;;15153:21:1;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;1875:36:4;;;;;1304:3702:3;1875:36:4;;1304:3702:3;1875:36:4;;;1304:3702:3;1875:14:4;1304:3702:3;1875:36:4;;;;;;;1304:3702:3;1875:36:4;;;;1304:3702:3;;;;;;;;;;1875:36:4;1304:3702:3;1875:36:4;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;4035:74:1;1304:3702:3;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1304:3702:3;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;1604:49;1304:3702;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1426:35;1304:3702;;;;;;;;;;;;;;;;;;;939:47:4;1304:3702:3;939:47:4;;1304:3702:3;;939:47:4;;;1304:3702:3;;939:14:4;1304:3702:3;939:14:4;1304:3702:3;939:14:4;1304:3702:3;939:47:4;;;;;;;1304:3702:3;939:47:4;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;1750:27:4;;;1304:3702:3;1750:27:4;;:14;1304:3702:3;1750:14:4;1304:3702:3;1750:27:4;;;;;;;;;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;2849:3:2;1304:3702:3;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;1304:3702:3;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;-1:-1:-1;1304:3702:3;;;;;-1:-1:-1;1304:3702:3;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;1304:3702:3;;;;;;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;577:134:4:-;1304:3702:3;;;661:43:4;;;:14;:43;:14;1304:3702:3;661:14:4;1304:3702:3;661:43:4;;;;;;;;;;;654:50;577:134;:::o;661:43::-;;;;;;;;;;;;;;;;;:::i;:::-;;;1304:3702:3;;;;;;;:::i;:::-;577:134:4;:::o;661:43::-;;;-1:-1:-1;661:43:4;;;1304:3702:3;;;661:43:4;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;:::o;1853:2:2:-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;2849:3::-;;;;;;1304:3702:3;;;;2849:3:2;;;:::o;:::-;;;;;;;:::i;:::-;1304:3702:3;;;;;;;:::i;:::-;2849:3:2;;;;;;;;;;;;;;;;-1:-1:-1;2849:3:2;;1304:3702:3;;;;2849:3:2:o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;66920:3792:1;;;;;;1746:1:2;;;;;67491:20:1;;1746:1:2;67487:88:1;;1853:2:2;;;;;1800:1;1853:2;;;1746:1;;1304:3702:3;;;;1800:1:2;1853:2;1304:3702:3;;;;;;1853:2:2;1304:3702:3;;;;;;;;;;;;;;;;;;;;;1853:2:2;1304:3702:3;;;;;68037:65:1;;1853:2:2;2670:3;1853:2;;;2611:3;1853:2;;2670:3;;;;;69432:13:1;;:44;;;;66920:3792;69428:109;;1853:2:2;;2126;1853;;;;2849:3;1853:2;;;2670:3;1304:3702:3;;4240:764;1304:3702;;;69998:79:1;1304:3702:3;2849:3:2;1304:3702:3;;2849:3:2;1304:3702:3;;;2849:3:2;;;1746:1;1853:2;2849:3;;;;1853:2;2849:3;;1304:3702:3;1853:2:2;;;;2849:3;;;1304:3702:3;2670:3:2;1304:3702:3;;;2849:3:2;;;;1304:3702:3;2849:3:2;1853:2;;;;;2849:3;;;1304:3702:3;;;;;;;;;2849:3:2;;;;;;;;;:::i;:::-;1304:3702:3;;2849:3:2;;;;;;;;;;;;:::i;:::-;;;1853:2;2849:3;;1304:3702:3;2849:3:2;;;1304:3702:3;69998:79:1;;;2849:3:2;;;:::i;:::-;;4240:764:3;:::i;:::-;2849:3:2;;70381:9:1;;;2849:3:2;;;;70373:26:1;;;70369:337;;66920:3792;;;;:::o;70369:337::-;70419:25;;;;;;;70415:214;;-1:-1:-1;;1304:3702:3;70649:46:1;;;2849:3:2;;;;70649:46:1;;;2849:3:2;;;;;;;;;13501:111:1;70415:214;70381:9;;;70564:25;70381:9;;70564:25;70381:9;;;70564:25;:::i;:::-;70551:10;70564:25;:::i;:::-;70608:7::o;69428:109::-;1304:3702:3;;69485:52:1;;;2670:3:2;1304:3702:3;;;69485:52:1;;;1304:3702:3;69520:15:1;1304:3702:3;;;2670:3:2;;;1304:3702:3;2670:3:2;;13501:111:1;69432:44;69461:15;;69449:27;;;69432:44;;68037:65;68077:25;1304:3702:3;;68077:25:1;;;;67487:88;67549:26;1304:3702:3;;67549:26:1;;;;1746:1:2;;;;;;;;;;4413:2233:6;;;;4603:1;4638:16;;1304:3702:3;1746:1:2;;;;;1304:3702:3;4705:13:6;;;1304:3702:3;;4730:17:6;4746:1;4782:10;4603:1;4794:14;;;;;;6612:17;;;;;;;;;;4746:1;1304:3702:3;;;;;;:::i;:::-;4603:1:6;1304:3702:3;;4413:2233:6;:::o;4810:3::-;1746:1:2;;;;;;1304:3702:3;;;;1746:1:2;;;4864:17:6;1304:3702:3;;;;1746:1:2;;5122:28:6;1746:1:2;4933:38:6;4941:29;1304:3702:3;4746:1:6;1304:3702:3;;1746:1:2;;4941:29:6;:::i;:::-;4933:38;;:::i;:::-;1304:3702:3;;5022:38:6;5030:29;1304:3702:3;;;5030:29:6;;;;;:::i;5022:38::-;1304:3702:3;;5122:28:6;;;;:::i;:::-;5115:36;;1304:3702:3;1746:1:2;;;;;1304:3702:3;4746:1:6;1304:3702:3;;;;4810:3:6;1304:3702:3;;;;;5171:10:6;1304:3702:3;;;;;;;;;4967:2:6;;1304:3702:3;;;;5155:2:6;1304:3702:3;;;;;;;;;;;;;;4603:1:6;5215:24;;;4603:1;5215:24;4746:1;1304:3702:3;5215:24:6;;;;;1304:3702:3;5215:24:6;;4603:1;5215:24;1304:3702:3;5453:23:6;;;5449:54;;5597:6;;:35;;;;;4810:3;5595:38;;;5592:80;;5686:25;6160:30;;;;6156:69;;6377:16;;1304:3702:3;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;;6364:44:6;6361:119;;4810:3;;4746:1;1304:3702:3;4782:10:6;;6361:119;6427:38;;;;;;;;;;;;;1304:3702:3;4603:1:6;1304:3702:3;;;;;;:::i;:::-;;;;;;;6427:38:6;:::o;6156:69::-;6199:26;1304:3702:3;;6199:26:6;;;;5592:80;5642:30;1304:3702:3;;5642:30:6;;;;5597:35;1304:3702:3;5607:25:6;;;-1:-1:-1;5597:35:6;;;;5449:54;5485:18;1304:3702:3;;5485:18:6;;;;5215:24;1304:3702:3;;;4603:1:6;1304:3702:3;;;;;;4746:1:6;1304:3702:3;;;1853:2:2;;;1304:3702:3;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;:::o;23429:2888:1:-;;23626:9;23772:26;;;23768:59;;1261:42:6;1304:3702:3;1069:18:6;1304:3702:3;;;:::i;1069:18:6:-;1261:42;;;;;;;:::i;:::-;1304:3702:3;;;;1853:2:2;;3623::3;1853::2;;;3734:24:3;1853:2:2;;;;3704:54:3;3700:87;;3623:2;1304:3702;;;;4012:17;;1304:3702;;;;;3623:2;1304:3702;;;3623:2;1304:3702;;;;;:::i;:::-;3623:2;1853::2;;24167:16:1;-1:-1:-1;1853:2:2;;;;1746:1;;;24217:10:1;;1746:1:2;;24242:36:1;;;1853:2:2;;;;;24331:90:1;1853:2:2;;;24553:87:1;1853:2:2;;;24331:90:1;;;:::i;:::-;24553:87;;:::i;:::-;2849:3:2;25930:9:1;;2849:3:2;;;;25922:26:1;;;25918:393;;23429:2888;;;:::o;25918:393::-;25968:25;;;;;;25964:214;;-1:-1:-1;;1304:3702:3;;26254:46:1;;;2849:3:2;;;;26254:46:1;;;2849:3:2;;;;;;;;;13501:111:1;25964:214;26113:25;25930:9;;;26113:25;25930:9;;;26113:25;:::i;24238:1582::-;24661:36;;;;;;;;;1746:1:2;24661:36:1;;24657:1163;1746:1:2;;;1853:2;;;;;;;;;;;;2126;1853;;;;;;2016;1304:3702:3;;;;;3623:2;1304:3702;;;;;;;3623:2;1304:3702;;;;1853:2:2;;;;1304:3702:3;;3623:2;1304:3702;;;;1501:4:2;1304:3702:3;;36248:28:1;;1304:3702:3;;;;36586:25:1;36582:59;;1853:2:2;;;2016;1304:3702:3;;;;;3623:2;1304:3702;;;;;;;3623:2;1304:3702;;;;1853:2:2;;;;1304:3702:3;;3623:2;1304:3702;;1501:4:2;1304:3702:3;;;;;;;;1853:2:2;;;2016;1304:3702:3;;;;36921:25:1;3623:2:3;1304:3702;;;;;;;3623:2;1304:3702;;;;;;;3623:2;1304:3702;;37237:46:1;37196:87;37192:130;;1853:2:2;;;3395:3;1853:2;;;1304:3702:3;;;37602:133:1;;1304:3702:3;;;37602:133:1;;;;1853:2:2;3623::3;37602:133:1;;;;;;1853:2:2;;;;;;;;;37602:133:1;;;;;:::i;:::-;;1853:2:2;37602:133:1;;;;;;:::i;:::-;37767:775;1853:2:2;;;;;2016;1304:3702:3;;;;;;;37767:775:1;39626:8;39622:79;;24657:1163;1853:2:2;;;3216:3;1853:2;;;;;;;3216:3;;;1853:2;;3157:3;1853:2;;;41208:282:1;1304:3702:3;;41208:282:1;1304:3702:3;1853:2:2;1304:3702:3;;40588:498:1;1304:3702:3;;1853:2:2;;;;2670:3;;;;;;;;1304:3702:3;;1853:2:2;3157:3;1853:2;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;40588:498:1;;:::i;:::-;1304:3702:3;;;41102:91:1;1304:3702:3;;1853:2:2;41102:91:1;1853:2:2;;;;;;41102:91:1;;;:::i;:::-;;;;2670:3:2;41208:282:1;2849:3:2;41208:282:1;;:::i;:::-;1304:3702:3;;;;1853:2:2;;;;;2849:3;;;;1304:3702:3;;41208:282:1;;2510:4;;;;;;;;;;;1304:3702:3;;;;;;;2510:4:1;;;1304:3702:3;;2510:4:1;;;1304:3702:3;2849:3:2;;;;2510:4:1;;;2849:3:2;;2510:4:1;;2849:3:2;2510:4:1;41208:282;;;;24238:1582;;39622:79;39639:9;1304:3702:3;;;;;;;2510:4:1;1304:3702:3;-1:-1:-1;39636:65:1;39622:79;39636:65;;37602:133;1304:3702:3;;39678:23:1;;;;37192:130;37292:30;1304:3702:3;;37292:30:1;;;;36582:59;36620:21;1304:3702:3;;36620:21:1;;;;24657:1163;24978:38;;;;;1746:1:2;24978:38:1;1746:1:2;;1853:2;;;2126;1853;;;;;;2016;1304:3702:3;;;;48076:25:1;3623:2:3;1304:3702;;;;;;;3623:2;1304:3702;;;;;;;3623:2;1304:3702;;48355:35:1;48314:76;48310:119;;1853:2:2;;;3639:3;1853:2;;;;;;2670:3;;;48850:15:1;;48838:27;;;;:44;;;24974:846;48834:109;;1853:2:2;;;3818:3;1853:2;;;2849:3;;50098:288:1;2849:3:2;;;1853:2;;;;;;2849:3;:::i;:::-;1853:2;3818:3;1853:2;;;;;;;2016;1304:3702:3;;50098:288:1;:::i;:::-;1853:2:2;;;;;;;;;;;50625:46:1;;50621:126;;1853:2:2;;;;2016;1304:3702:3;;;;;3623:2;1304:3702;;;;;;;3623:2;1304:3702;;;;1853:2:2;;;;1304:3702:3;;3623:2;1304:3702;;;;;1501:4:2;1304:3702:3;;42679:28:1;;1304:3702:3;;;;;42961:25:1;42957:59;;1853:2:2;43710:51:1;1853:2:2;;;;;;2016;1304:3702:3;;;;;3623:2;1304:3702;;;;;;;3623:2;1304:3702;;;;1853:2:2;;;;1304:3702:3;;3623:2;1304:3702;;1501:4:2;1304:3702:3;;;;;;;;1853:2:2;;;;43710:51:1;:::i;:::-;1304:3702:3;;43607:155:1;;2510:4;3623:2:3;43607:155:1;;;;;;;;;;1304:3702:3;1853:2:2;;;;2510:4:1;;;1304:3702:3;;2510:4:1;;;;;;;;;:::i;43607:155::-;43794:807;1853:2:2;;;;;2016;1304:3702:3;;;;;;;43794:807:1;44982:8;44978:79;;24974:846;1304:3702:3;45144:463:1;45732:275;1304:3702:3;;45732:275:1;1304:3702:3;;1853:2:2;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;45144:463:1;;:::i;:::-;1304:3702:3;;;45623:94:1;1304:3702:3;;1853:2:2;45623:94:1;1853:2:2;;;;;;45623:94:1;;;:::i;:::-;;;;1304:3702:3;2670:3:2;45732:275:1;2849:3:2;45732:275:1;;:::i;:::-;1304:3702:3;;;;;;;;;;;;;2510:4:1;;;1304:3702:3;;;;2510:4:1;;;1304:3702:3;;;;2849:3:2;;;;;;2510:4:1;;;2849:3:2;;;;;;2510:4:1;;;2849:3:2;1853:2;;;;1304:3702:3;;;2510:4:1;;;;;44978:79;44995:9;;;;1304:3702:3;;;;;;;2510:4:1;1304:3702:3;-1:-1:-1;44992:65:1;;44978:79;;;;;50621:126;1304:3702:3;;;1853:2:2;1304:3702:3;;50680:67:1;;;;1853:2:2;;50680:67:1;;;1304:3702:3;;;;;50680:67:1;48834:109;1304:3702:3;;48891:52:1;;;2670:3:2;1304:3702:3;;;48891:52:1;;;1304:3702:3;48850:15:1;1304:3702:3;2670:3:2;;;1304:3702:3;2670:3:2;;13501:111:1;48838:44;48869:13;;;48838:44;;24974:846;25788:21;1304:3702:3;;25788:21:1;;;;1746:1:2;;;;;;;;;;3700:87:3;3767:20;1304:3702;;3767:20;;;;1304:3702;;;;;;;;;;;;;;;;;;;;;:::i;23768:59:1:-;23807:20;1304:3702:3;;23807:20:1;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;2849:3:2;;;;;;;1304:3702:3;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::o;2928:4:1:-;;;;1304:3702:3;;;;;;;;2928:4:1;;;;;:::i;:::-;;;;;;;:::o;18013:4228::-;;;;;;1304:3702:3;18383:21:1;1304:3702:3;;18383:21:1;1304:3702:3;;;;;18383:21:1;1304:3702:3;;18383:35:1;18379:67;;18670:10;-1:-1:-1;1304:3702:3;18648:21:1;1304:3702:3;;18383:21:1;-1:-1:-1;1304:3702:3;;-1:-1:-1;1304:3702:3;;;;;18383:21:1;-1:-1:-1;1304:3702:3;18383:21:1;1304:3702:3;;;;;;:::i;:::-;;;;:::i;:::-;;;18718:37:1;18714:78;;1304:3702:3;;18842:1:1;18806:37;;:101;;;;18013:4228;18802:129;;;1304:3702:3;;;19574:13:1;;;:44;;;;18013:4228;19570:106;;;10737:198;2849:3:2;;;;;:::i;:::-;2614:24:3;;10818:12:1;18670:10;;10737:198;:::i;:::-;18670:10;;-1:-1:-1;1304:3702:3;-1:-1:-1;1304:3702:3;;18383:21:1;-1:-1:-1;1304:3702:3;;-1:-1:-1;1304:3702:3;;;18383:21:1;-1:-1:-1;1304:3702:3;;-1:-1:-1;1304:3702:3;;;18383:21:1;-1:-1:-1;1304:3702:3;;;;60141:129:1;;1304:3702:3;;2928:4:1;1304:3702:3;;2928:4:1;;;;60370:28;1304:3702:3;;;;;;;;;;60343:55:1;2928:4;1304:3702:3;;2928:4:1;1304:3702:3;60343:55:1;:::i;:::-;1304:3702:3;;60428:19:1;2928:4;1304:3702:3;60428:19:1;2928:4;;;;1304:3702:3;60450:23:1;1304:3702:3;;60450:23:1;1304:3702:3;;;;60489:26:1;1304:3702:3;;60428:45:1;60450:23;1304:3702:3;;60428:19:1;2928:4;1304:3702:3;60428:45:1;:::i;60489:26::-;18670:10;;-1:-1:-1;1304:3702:3;-1:-1:-1;1304:3702:3;;18383:21:1;-1:-1:-1;1304:3702:3;;-1:-1:-1;1304:3702:3;;;18383:21:1;-1:-1:-1;1304:3702:3;;-1:-1:-1;1304:3702:3;;;18383:21:1;-1:-1:-1;1304:3702:3;;18383:21:1;1304:3702:3;;;;;;2928:4:1;1304:3702:3;;60428:19:1;2928:4;1304:3702:3;;;;;;;;;;;18842:1:1;1304:3702:3;;;;;18842:1:1;1304:3702:3;;;;60450:23:1;1304:3702:3;18842:1:1;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;18842:1:1;1304:3702:3;;;;;2126:2:2;1304:3702:3;;2126:2:2;;;1304:3702:3;;;;;;;18670:10:1;20339:26;18670:10;20339:26;:::i;:::-;1304:3702:3;18383:21:1;1304:3702:3;2928:4:1;;;1304:3702:3;2928:4:1;;-1:-1:-1;2849:3:2;;2928:4:1;;;;1304:3702:3;;;2928:4:1;;;;;;1304:3702:3;2928:4:1;;;;;:::i;:::-;;;;;;;1304:3702:3;;2126:2:2;;;;;2928:4:1;1304:3702:3;;2849:3:2;1304:3702:3;2928:4:1;;;;;;;;;;;;1304:3702:3;;2928:4:1;1304:3702:3;;-1:-1:-1;1304:3702:3;;2928:4:1;;;;;;;;;;;;:::i;:::-;20837:150;;;:::i;:::-;1304:3702:3;18383:21:1;1304:3702:3;;;;;;2928:4:1;1304:3702:3;;;2928:4:1;;1304:3702:3;60428:19:1;2928:4;1304:3702:3;;18383:21:1;2928:4;;1304:3702:3;18383:21:1;1304:3702:3;;;;2928:4:1;;1304:3702:3;;;60450:23:1;2928:4;;1304:3702:3;60450:23:1;1304:3702:3;;;;2928:4:1;;1304:3702:3;;;;;2126:2:2;2928:4:1;;1304:3702:3;2928:4:1;20837:150;;;21567:36;;;:::i;:::-;;;;:::i;:::-;2849:3:2;;;;;21703:9:1;;;:15;21699:73;;21703:9;;;;21896:15;21892:294;;22205:29;-1:-1:-1;22205:29:1;18013:4228;:::o;21892:294::-;21703:9;2928:4;22097:18;1304:3702:3;;18383:21:1;1304:3702:3;;22097:18:1;:::i;:::-;22134:37;;:::o;21699:73::-;18383:21;1304:3702:3;21727:45:1;;;2849:3:2;;;;21727:45:1;;;2849:3:2;21703:9:1;2849:3:2;;;;;;;;;;;;;13501:111:1;60141:129;60246:24;18383:21;1304:3702:3;60246:24:1;;;;19570:106;18383:21;1304:3702:3;19627:49:1;;;1304:3702:3;19649:15:1;1304:3702:3;;19627:49:1;;;1304:3702:3;;;2670:3:2;;;1304:3702:3;2670:3:2;;13501:111:1;19574:44;19591:15;;;:27;;19574:44;;;18802:129;18916:15;18383:21;1304:3702:3;18916:15:1;;;;18806:101;1304:3702:3;;;;;1746:1:2;1304:3702:3;;;;1746:1:2;18847:60:1;18806:101;;;18714:78;18764:28;18383:21;1304:3702:3;18764:28:1;;;;18379:67;18427:19;18383:21;1304:3702:3;18427:19:1;;;;63632:1628;;;;;;1853:2:2;;;;;1304:3702:3;-1:-1:-1;1304:3702:3;1800:1:2;1853:2;1304:3702:3;;-1:-1:-1;1304:3702:3;1853:2:2;1304:3702:3;;;;;;;;;;;;;;;;;;1800:1:2;1853:2;;;-1:-1:-1;1304:3702:3;1853:2:2;1304:3702:3;;-1:-1:-1;1304:3702:3;;64515:27:1;;:80;;;;63632:1628;64511:165;;2849:3:2;;;;;;64737:81:1;2849:3:2;;;;;:::i;:::-;;;;;:::i;64511:165:1:-;1304:3702:3;;2849:3:2;;;;;:::i;:::-;1853:2;1304:3702:3;;;;64643:32:1;1304:3702:3;;64604:72:1;;;;;;;1304:3702:3;;;;;64604:72:1;64515:80;2849:3:2;;;;;;:::i;:::-;1853:2;1304:3702:3;;;;64563:32:1;64546:49;;;64515:80;;1304:3702:3;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;:::o;6830:2662:6:-;1304:3702:3;;:::i;:::-;1853:2:2;;7121:1:6;1853:2:2;;;7121:1:6;1304:3702:3;;;;7600:12:6;7597:47;;1853:2:2;7703:3:6;1853:2:2;;;7121:1:6;1853:2:2;;1304:3702:3;;;7655:19:6;;;1304:3702:3;1853:2:2;7823:3:6;1853:2:2;;;7703:3:6;1853:2:2;;1304:3702:3;7884:9:6;1304:3702:3;;;;;;;7823:3:6;1304:3702:3;1853:2:2;7703:3:6;1853:2:2;;;;-1:-1:-1;1853:2:2;;9458:17:6;7839:56;1853:2:2;7121:1:6;1853:2:2;;1304:3702:3;;;2849:3:2;8799:17:6;1304:3702:3;7823:3:6;1304:3702:3;;8799:17:6;;;:::i;:::-;2849:3:2;;;:::i;:::-;1304:3702:3;;;;;8860:15:6;7655:19;1304:3702:3;;;;;;;;;;;;;:::i;:::-;;8837:40:6;;1304:3702:3;9199:34:6;9207:25;1304:3702:3;;;;9122:24:6;1304:3702:3;;;;9122:24:6;;;:::i;:::-;1304:3702:3;;;;;;;9144:1:6;1304:3702:3;;;;6830:2662:6;1304:3702:3;;;;;;;;;;;9207:25:6;;;;:::i;9199:34::-;1304:3702:3;9179:17:6;;1304:3702:3;;9458:17:6;;:::i;:::-;9448:27;;6830:2662::o;1304:3702:3:-;;;;;9144:1:6;1304:3702:3;;;1853:2:2;1304:3702:3;;;;;;;7597:47:6;7621:23;1304:3702:3;;7621:23:6;;;;3395:3:2;;;;;;1304:3702:3;;3395:3:2;;;1304:3702:3;3395:3:2;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;1304:3702:3;3395:3:2;1304:3702:3;3395:3:2:o;428:387:17:-;1304:3702:3;;;;1746:1:2;1304:3702:3;;;2849:3:2;650:1:17;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;428:387:17;:::o;1304:3702:3:-;;;;;;;:::o;:::-;;;;;;;;;;9612:311:6;9822:3;9806:19;;;9802:50;;9891:1;1304:3702:3;;;;;;9612:311:6;:::o;9802:50::-;9834:18;1304:3702:3;;9834:18:6;;;;424:147:4;1304:3702:3;;;;;;;:::i;:::-;;;;-1:-1:-1;1304:3702:3;;;;;;;;;;;;528:36:4;1304:3702:3;528:36:4;;1304:3702:3;528:36:4;;;1304:3702:3;528:14:4;;1304:3702:3;528:14:4;;;1304:3702:3;528:36:4;;;;;;;;;;;424:147;521:43;;;;;424:147;:::o;528:36::-;;;;;;;;;;;;;;;;:::i;:::-;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;528:36:4;;;;;;;1304:3702:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;528:36:4;1304:3702:3;;;;;;;;;;4240:764;;;1304:3702;4557:8;1304:3702;;;;;;;4557:21;1304:3702;4557:21;;;;;;;;;;;;;;;;;;;;;4240:764;2849:3:2;;1304:3702:3;2849:3:2;1753:2:3;2849:3:2;;;1304:3702:3;;;;;;;;;;;;2928:4:1;1304:3702:3;;;;;;;;2928:4:1;;;:::i;:::-;;;1304:3702:3;;;;;;;;;:::i;:::-;;;4768:229;;;;;;1304:3702;4768:229;;4557:21;;4768:229;;1304:3702;1753:2;;;;;;;;;;:::i;:::-;;;;;1304:3702;4768:229;;;;;;;;;;;;4240:764;;:::o;4768:229::-;;;;;;-1:-1:-1;4768:229:3;;;;;;:::i;:::-;;4240:764::o;4768:229::-;1304:3702;;4557:21;1304:3702;;;;;4557:21;;;;;;;;;;;;;;;;;;:::i;:::-;;;1304:3702;;;;;;;;;;;;4557:21;;;;;;;1304:3702;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;1304:3702:3;;;;:::o;:::-;;;:::o;2647:312:0:-;2736:21;;:31;1304:3702:3;;2831:33:0;1304:3702:3;;;;;;;2831:33:0;;;;:::i;:::-;;1304:3702:3;;;2647:312:0:o;1304:3702:3:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2510:4:1;;;;;;;1304:3702:3;;;2510:4:1;;;1746:1:2;2510:4:1;;;2849:3:2;2510:4:1;;;1304:3702:3;;;-1:-1:-1;1304:3702:3;;;;2510:4:1;;;;;;;;;:::i;2307:::-;;;;2928;2307;;2670:3:2;2307:4:1;;;;;1853:2:2;1304:3702:3;;2307:4:1;;;1746:1:2;2307:4:1;;;2849:3:2;2307:4:1;;;1304:3702:3;2307:4:1;;;1304:3702:3;;;;;2928:4:1;2307;;;2928;2670:3:2;2307:4:1;;;2849:3:2;2928:4:1;1304:3702:3;;2307:4:1;;;;;;2928;;;:::i;:::-;;;2307;;;;;;;;;:::i;26442:8689::-;;;;;;1853:2:2;;;;;1304:3702:3;-1:-1:-1;1304:3702:3;1800:1:2;1853:2;1304:3702:3;;-1:-1:-1;1304:3702:3;1853:2:2;1304:3702:3;;;;;2928:4:1;1304:3702:3;;;;2928:4:1;;;;;:::i;:::-;;;1304:3702:3;;;;;;;1800:1:2;1853:2;;;-1:-1:-1;1304:3702:3;1853:2:2;1304:3702:3;;-1:-1:-1;1304:3702:3;;27314:60:1;;1304:3702:3;-1:-1:-1;1304:3702:3;1800:1:2;1853:2;1304:3702:3;;-1:-1:-1;1304:3702:3;1853:2:2;1304:3702:3;;;;;2928:4:1;1304:3702:3;;;;2928:4:1;;;;;:::i;:::-;;;1304:3702:3;;;;;;;1800:1:2;1853:2;;;-1:-1:-1;1304:3702:3;1853:2:2;1304:3702:3;1800:1:2;1304:3702:3;-1:-1:-1;1304:3702:3;;1853:2:2;2611:3;1853:2;;;2436:3;1853:2;;2016;1304:3702:3;;1853:2:2;2126;1853;;;1304:3702:3;-1:-1:-1;1304:3702:3;28328:25:1;1853:2:2;1304:3702:3;;-1:-1:-1;1304:3702:3;;-1:-1:-1;1304:3702:3;1853:2:2;1304:3702:3;;-1:-1:-1;1304:3702:3;;;;1853:2:2;2928:4:1;;28695:41;28659:77;28655:1477;;1853:2:2;2670:3;1853:2;;;2611:3;1853:2;;2670:3;;;30438:13:1;;;:43;;;;26442:8689;30434:1440;;;1853:2:2;2849:3;1853:2;;;-1:-1:-1;1304:3702:3;;32665:147:1;;1304:3702:3;32665:147:1;;;;;;1304:3702:3;1800:1:2;1853:2;;;2789:3;;;1304:3702:3;2789:3:2;;;;;1853:2;2789:3;;;1304:3702:3;1853:2:2;;;;1304:3702:3;;;;;;;;;2789:3:2;;;;;1304:3702:3;;;2789:3:2;1304:3702:3;;;1853:2:2;2849:3;1853:2;;;;;2789:3;:::i;:::-;32665:147:1;1853:2:2;2670:3;1853:2;;3216:3;;;32665:147:1;-1:-1:-1;;32665:147:1;;;26442:8689;-1:-1:-1;32661:1098:1;;32895:864;;;:::i;:::-;;33299:9;3216:3:2;1304:3702:3;2670:3:2;1853:2;;3216:3;;;2510:4:1;1304:3702:3;-1:-1:-1;33296:62:1;;34936:85;33890:498;1800:1:2;1304:3702:3;34936:85:1;1304:3702:3;2928:4:1;34097:20;1853:2:2;1304:3702:3;2510:4:1;1304:3702:3;;2510:4:1;1746:1:2;2510:4:1;;;2849:3:2;1853:2;2849:3;1853:2;;;;;;2510:4:1;;1304:3702:3;;;;;;-1:-1:-1;1304:3702:3;;;;2510:4:1;;;;;;;;;:::i;:::-;32661:1098;;34108:9;34097:20;;:::i;:::-;3216:3:2;2928:4:1;;2670:3:2;34257:15:1;2670:3:2;2126:2;;1853;;;;;;;;33890:498:1;:::i;:::-;1304:3702:3;;;;;;34590:32:1;1304:3702:3;-1:-1:-1;1304:3702:3;;;;;-1:-1:-1;1304:3702:3;;;;;;;;2928:4:1;;;;;;;:::i;:::-;;;1304:3702:3;;;;;;;1853:2:2;;;;-1:-1:-1;1304:3702:3;;;;-1:-1:-1;1304:3702:3;;34936:85:1;:::i;:::-;1304:3702:3;;;;;;1853:2:2;;34936:85:1;;26442:8689;:::o;32661:1098::-;1853:2:2;32862:21:1;;33890:498;1800:1:2;32862:21:1;34936:85;32862:21;2928:4;34097:20;34936:85;32862:21;32661:1098;;;32665:147;;;;;;-1:-1:-1;32665:147:1;;;;;;:::i;:::-;;;2789:3:2;1853:2;2789:3;;;;;;;;;2670;2789;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1304:3702:3;;;;;;;:::i;:::-;2789:3:2;;;1853:2;2789:3;;;;;;;;;1853:2;2789:3;;;;;;;;;:::i;:::-;32665:147:1;;;;30434:1440;1853:2:2;;;2849:3;1853:2;;;;;;1800:1;31736:85:1;;1853:2:2;30515:108:1;1853:2:2;;;;2849:3;1853:2;;30515:108:1;:::i;:::-;1304:3702:3;2307:4:1;;1304:3702:3;;2307:4:1;;;;;2849:3:2;1746:1;2849:3;;1853:2;;;;;2307:4:1;;1304:3702:3;1853:2:2;;;;2307:4:1;;;1304:3702:3;;;;;2928:4:1;2307;;;2928;2670:3:2;31260:15:1;2670:3:2;2126:2;;2307:4:1;;;2849:3:2;2928:4:1;1304:3702:3;;2307:4:1;;;;;;2928;;;:::i;:::-;;;2307;;;;;;;;;:::i;:::-;1304:3702:3;;31609:32:1;;1304:3702:3;-1:-1:-1;1304:3702:3;;;;;-1:-1:-1;1304:3702:3;;;;;;;;2928:4:1;;;;;;;:::i;30438:43::-;30466:15;;;-1:-1:-1;30438:43:1;;;28655:1477;1853:2:2;;;2849:3;1853:2;;;29994:85:1;29106:531;1800:1:2;1304:3702:3;29994:85:1;1304:3702:3;2928:4:1;29334:20;1853:2:2;1304:3702:3;2510:4:1;1304:3702:3;;2510:4:1;1746:1:2;2510:4:1;;;2849:3:2;1853:2;2849:3;1853:2;;;;;;2510:4:1;;1304:3702:3;;;;;;-1:-1:-1;1304:3702:3;;;;2510:4:1;;;;;;;;;:::i;27314:60::-;27353:21;1304:3702:3;;27353:21:1;;;;1678:2240:6;2016:19;1678:2240;;;;2016:19;1304:3702:3;;2001:35:6;1304:3702:3;;;;2001:35:6;:::i;:::-;2500:16;;;1304:3702:3;2500:28:6;2497:95;;2718:26;;;;1304:3702:3;;2747:15:6;-1:-1:-1;2714:206:6;;1678:2240;3431:16;;3424:31;3431:16;;1304:3702:3;3424:31:6;:::i;:::-;3404:51;;3400:108;;3647:51;;;:::i;:::-;3711:16;;3708:74;;3894:17;3902:4;1304:3702:3;2016:19:6;1304:3702:3;;;;:::i;3708:74:6:-;-1:-1:-1;;3742:29:6:o;3400:108::-;3470:27;;;;-1:-1:-1;1304:3702:3;2016:19:6;1304:3702:3;;;;:::i;:::-;;;;;2718:26:6;1304:3702:3;;;3470:27:6;:::o;2714:206::-;1304:3702:3;;;;2805:28:6;;:::i;:::-;1304:3702:3;2782:51:6;2778:132;;2714:206;;;;2778:132;2853:42;;;;-1:-1:-1;1304:3702:3;2016:19:6;1304:3702:3;;;;:::i;:::-;;;;;2718:26:6;1304:3702:3;;;2853:42:6;:::o;2497:95::-;2543:38;;;;;;-1:-1:-1;1304:3702:3;2016:19:6;1304:3702:3;;;;:::i;:::-;;;;;;;;;2543:38:6;:::o;9736:598:1:-;;1304:3702:3;9736:598:1;;;1304:3702:3;9736:598:1;2016:2:2;1304:3702:3;2670:3:2;1304:3702:3;;;;;;;;10086:4:1;;;1304:3702:3;;2016:2:2;1304:3702:3;;;;;;;;;2126:2:2;;;1304:3702:3;;;2849:3:2;1304:3702:3;;;;;;;;;;;;2928:4:1;1304:3702:3;;;;;;;;2928:4:1;;;:::i;:::-;;;1304:3702:3;;;;;;;;;:::i;:::-;;10017:310:1;;9736:598;:::o;50896:8177::-;;;;;;;;;;;;1304:3702:3;;;;;;;;;;51596:39:1;;;51592:83;;50896:8177;1304:3702:3;;;;;;;;;;52086:9:1;;;;2928:4;;1304:3702:3;;52113:29:1;;;;;52109:63;;50896:8177;1304:3702:3;;;;;;;;;;;;;;;;;;2928:4:1;;;;1304:3702:3;;;;;;;53054:33:1;;;;;;;;;;50896:8177;1304:3702:3;53054:33:1;;53053:34;53050:171;;50896:8177;-1:-1:-1;;1304:3702:3;;;;;;53408:45:1;;;53404:269;;53924:50;1304:3702:3;53924:50:1;;;;;;;;;;;;50896:8177;53924:50;53923:51;53920:377;;50896:8177;54357:6;;;;;;:::i;:::-;54463:46;;50896:8177;:::o;53920:377::-;1304:3702:3;54170:47:1;;;;;;;;;;;;53920:377;54178:16;1304:3702:3;54170:47:1;;;;;53920:377;;;;;;54170:47;;;-1:-1:-1;54170:47:1;;53924:50;;;;;53404:269;53469:47;;1304:3702:3;53469:47:1;;;;;;;;;;;;;;;53404:269;53469:47;;;;53616:46;;;:::o;53469:47::-;;;;;53050:171;1304:3702:3;53103:42:1;;;;;;;;;;;;;;;53050:171;53111:16;;1304:3702:3;53103:42:1;;;;;53050:171;;;;;;;;53103:42;;;;;53054:33;;;-1:-1:-1;53054:33:1;;52109:63;52144:28;;;52109:63;;;51592:83;51637:38;;51592:83;;;50896:8177;;;;;;;;;;;;1304:3702:3;;;;;;;;;;51596:39:1;;;51592:83;;50896:8177;1304:3702:3;;;;;;;;;;52086:9:1;;2928:4;;1304:3702:3;;52113:29:1;;;;;52109:63;;50896:8177;1304:3702:3;;;;;;;;;;;;;;;;;;2928:4:1;;;;1304:3702:3;-1:-1:-1;1304:3702:3;;;;;53054:33:1;;;;;;;;;;;50896:8177;1304:3702:3;53054:33:1;;53053:34;53050:171;;50896:8177;1304:3702:3;;;;;;;53408:45:1;;;;53404:269;;1304:3702:3;;;;53756:16:1;;;53752:768;;54908:15;1304:3702:3;;;;;;55801:10:1;55785:26;;55781:49;;50896:8177;56183:43;;;;1304:3702:3;;;;-1:-1:-1;56449:34:1;;-1:-1:-1;;56893:63:1;1304:3702:3;;-1:-1:-1;1304:3702:3;;56893:63:1;:::i;:::-;1304:3702:3;56445:1706:1;;;;-1:-1:-1;58299:60:1;;;;;;;;;;;;56445:1706;58299:60;58298:61;58295:213;;56445:1706;2928:4;;58887:16;2928:4;;58887:16;;;:::i;58295:213::-;-1:-1:-1;58375:57:1;;;;;;;;;;58295:213;58383:16;1304:3702:3;58375:57:1;;;;;58295:213;;;;;58375:57;;;-1:-1:-1;58375:57:1;;58299:60;;;;;56445:1706;57232:52;-1:-1:-1;57232:52:1;;;;;;57608:66;1304:3702:3;;57608:66:1;:::i;:::-;2928:4;;57228:909;;;56445:1706;;57228:909;58093:25;;;;-1:-1:-1;57228:909:1;;;56445:1706;;55781:49;-1:-1:-1;;;55781:49:1;;53752:768;53924:50;;;;;;;;;;;-1:-1:-1;53924:50:1;;;;;;;;;;;;;53923:51;53920:377;;54357:6;;;;;;:::i;53404:269::-;53469:47;-1:-1:-1;53469:47:1;;;;;;;;;;;;;;;;;;;;;;53616:46;;;:::o;53050:171::-;-1:-1:-1;53103:42:1;;;;;;;;;;;53050:171;53111:16;;1304:3702:3;53103:42:1;;;;;53050:171;;;;;53103:42;;;;;53054:33;;;-1:-1:-1;53054:33:1;;52109:63;52144:28;;;52109:63;;;51592:83;51637:38;;51592:83;;
Swarm Source
ipfs://650436a56158a64f21f64a45b35532f08d567f83e44ecf57f2389ce0c20ad580
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
OP | Ether (ETH) | 100.00% | $4,002.51 | 0.00002882 | $0.115363 |
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.