Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 7,084 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Store Latest L1B... | 13884280 | 253 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13883319 | 253 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839959 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839863 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839853 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839843 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839834 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839821 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839811 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Store Latest L1B... | 13839807 | 254 days ago | IN | 0 ETH | 0.00000003 | ||||
Store Latest L1B... | 13839797 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Update Latest Pm... | 13838653 | 254 days ago | IN | 0 ETH | 0.00000057 | ||||
Store Latest L1B... | 13838650 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Update Latest Pm... | 13837396 | 254 days ago | IN | 0 ETH | 0.00000057 | ||||
Store Latest L1B... | 13837393 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Update Latest Pm... | 13836109 | 254 days ago | IN | 0 ETH | 0.00000057 | ||||
Store Latest L1B... | 13836106 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Update Latest Pm... | 13834919 | 254 days ago | IN | 0 ETH | 0.00000057 | ||||
Store Latest L1B... | 13834917 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Update Latest Pm... | 13833635 | 254 days ago | IN | 0 ETH | 0.00000054 | ||||
Store Latest L1B... | 13833632 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Update Latest Pm... | 13832475 | 254 days ago | IN | 0 ETH | 0.00000057 | ||||
Store Latest L1B... | 13832473 | 254 days ago | IN | 0 ETH | 0.00000005 | ||||
Update Latest Pm... | 13831539 | 254 days ago | IN | 0 ETH | 0.00000057 | ||||
Store Latest L1B... | 13831536 | 254 days ago | IN | 0 ETH | 0.00000005 |
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
11797658 | 301 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x826F8065...279a7b0E1 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
AxiomV2BaseBlockhashHeaderVerifier
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 100000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import { OpStackBlockhashHeaderVerifier } from "./OpStackBlockhashHeaderVerifier.sol"; import { MAINNET_CHAIN_ID, SEPOLIA_CHAIN_ID } from "../libraries/configuration/AxiomV2Configuration.sol"; import { BASE_CHAIN_ID, BASE_SEPOLIA_CHAIN_ID } from "../libraries/configuration/AxiomV2RemoteConfiguration.sol"; /// @title AxiomV2BaseBlockhashHeaderVerifier /// @notice BlockhashHeaderVerifier for Base. contract AxiomV2BaseBlockhashHeaderVerifier is OpStackBlockhashHeaderVerifier { /// @dev Initialize the contract. /// @param sourceChainId The chain ID of the source chain. /// @param _l1AxiomV2CoreAddress The address of the AxiomV2Core contract on L1. constructor(uint64 sourceChainId, address _l1AxiomV2CoreAddress) OpStackBlockhashHeaderVerifier(sourceChainId, _l1AxiomV2CoreAddress) { if (block.chainid != BASE_CHAIN_ID && block.chainid != BASE_SEPOLIA_CHAIN_ID) { revert InvalidChainId(); } if (block.chainid == BASE_CHAIN_ID && sourceChainId != MAINNET_CHAIN_ID) { revert InvalidSourceChainId(); } if (block.chainid == BASE_SEPOLIA_CHAIN_ID && sourceChainId != SEPOLIA_CHAIN_ID) { revert InvalidSourceChainId(); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import { BlockhashRemoteHeaderVerifier } from "./BlockhashRemoteHeaderVerifier.sol"; interface IL1Block { function number() external view returns (uint64); function hash() external view returns (bytes32); } /// @title OpStackBlockhashHeaderVerifier /// @notice BlockhashRemoteHeaderVerifier for OP Stack chains abstract contract OpStackBlockhashHeaderVerifier is BlockhashRemoteHeaderVerifier { /// @dev The address of the predeployed L1Block contract /// From: https://docs.optimism.io/stack/protocol/smart-contracts#l1block address constant L1_BLOCK_ADDRESS = 0x4200000000000000000000000000000000000015; /// @dev Initialize the contract. /// @param sourceChainId The chain ID of the source chain. /// @param _l1AxiomV2CoreAddress The address of the AxiomV2Core contract on L1. constructor(uint64 sourceChainId, address _l1AxiomV2CoreAddress) BlockhashRemoteHeaderVerifier(sourceChainId, _l1AxiomV2CoreAddress) { } /// @notice Read a verified blockhash from L1 for some block number, which is usually /// increasing across calls, but we do not make this assumption. /// @return blockNumber The block number /// @return blockhash The blockhash function _getLatestL1Blockhash() internal view override returns (uint32 blockNumber, bytes32 blockhash) { blockNumber = uint32(IL1Block(L1_BLOCK_ADDRESS).number()); blockhash = IL1Block(L1_BLOCK_ADDRESS).hash(); } /// @notice Read a verified blockhash from L1. Should revert if the blockhash is not available. /// @param blockNumber The block number to read /// @return blockhash The blockhash function _getL1Blockhash(uint32 blockNumber) internal view override returns (bytes32 blockhash) { require( blockNumber == IL1Block(L1_BLOCK_ADDRESS).number(), "BlockhashRemoteHeaderVerifier: block number mismatch" ); blockhash = IL1Block(L1_BLOCK_ADDRESS).hash(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // Constants and free functions to be inlined into by AxiomV2Core and AxiomV2Query /* * Constants for AxiomV2Core */ // AxiomV2Core caches blockhashes in batches, stored as Merkle roots of binary Merkle trees uint32 constant BLOCK_BATCH_SIZE = 1024; uint32 constant BLOCK_BATCH_DEPTH = 10; // constants for batch import of historical block hashes // historical uploads a bigger batch of block hashes, stored as Merkle roots of binary Merkle trees uint32 constant HISTORICAL_BLOCK_BATCH_SIZE = 131_072; // 2 ** 17 uint32 constant HISTORICAL_BLOCK_BATCH_DEPTH = 17; // we will consider the historical Merkle tree of blocks as a Merkle tree of the block batch roots uint32 constant HISTORICAL_NUM_ROOTS = 128; // HISTORICAL_BATCH_SIZE / BLOCK_BATCH_SIZE // The first 4 * 3 * 32 bytes of proof calldata are reserved for two BN254 G1 points for a pairing check // It will then be followed by (7 + BLOCK_BATCH_DEPTH * 2) * 32 bytes of public inputs/outputs uint32 constant PUBLIC_BYTES_START_IDX = 384; // 4 * 3 * 32 uint32 constant AUX_PEAKS_START_IDX = 608; // PUBLIC_BYTES_START_IDX + 7 * 32 /// @notice Read public instances from the ZK proof /// @param proofData the entire ZK proof /// @return prevHash the hash of the previous block /// @return endHash the hash of the last block in the batch /// @return startBlockNumber the block number of the first block in the batch /// @return endBlockNumber the block number of the last block in the batch /// @return root the Merkle root of the 0-padded batch of blocks /// @dev proofData stores bytes32 and uint256 values in hi-lo format as two uint128 values because the BN254 scalar field is 254 bits /// @dev The first 12 * 32 bytes of proofData are reserved for ZK proof verification data // Extract public instances from proof // The public instances are laid out in the proof calldata as follows: // First 4 * 3 * 32 = 384 bytes are reserved for proof verification data used with the pairing precompile // 384..384 + 32 * 2: prevHash (32 bytes) as two uint128 cast to uint256, because zk proof uses 254 bit field and cannot fit uint256 into a single element // 384 + 32 * 2..384 + 32 * 4: endHash (32 bytes) as two uint128 cast to uint256 // 384 + 32 * 4..384 + 32 * 5: startBlockNumber (uint32: 4 bytes) and endBlockNumber (uint32: 4 bytes) are concatenated as `startBlockNumber . endBlockNumber` (8 bytes) and then cast to uint256 // 384 + 32 * 5..384 + 32 * 7: root (32 bytes) as two uint128 cast to uint256, this is the highest peak of the MMR if endBlockNumber - startBlockNumber == 1023, otherwise 0 function getBoundaryBlockData(bytes calldata proofData) pure returns (bytes32 prevHash, bytes32 endHash, uint32 startBlockNumber, uint32 endBlockNumber, bytes32 root) { prevHash = bytes32(uint256(bytes32(proofData[PUBLIC_BYTES_START_IDX:416])) << 128 | uint256(bytes32(proofData[416:448]))); endHash = bytes32(uint256(bytes32(proofData[448:480])) << 128 | uint256(bytes32(proofData[480:512]))); startBlockNumber = uint32(bytes4(proofData[536:540])); endBlockNumber = uint32(bytes4(proofData[540:544])); root = bytes32(uint256(bytes32(proofData[544:576])) << 128 | uint256(bytes32(proofData[576:AUX_PEAKS_START_IDX]))); } // We have a Merkle mountain range of max depth BLOCK_BATCH_DEPTH (so length BLOCK_BATCH_DEPTH + 1 total) ordered in **decreasing** order of peak size, so: // `root` from `getBoundaryBlockData` is the peak for depth BLOCK_BATCH_DEPTH // `getAuxMmrPeak(proofData, i)` is the peaks for depth BLOCK_BATCH_DEPTH - 1 - i // 384 + 32 * 7 + 32 * 2 * i .. 384 + 32 * 7 + 32 * 2 * (i + 1): (32 bytes) as two uint128 cast to uint256, same as blockHash // Note that the decreasing ordering is *different* than the convention in library MerkleMountainRange function getAuxMmrPeak(bytes calldata proofData, uint256 i) pure returns (bytes32) { return bytes32( uint256(bytes32(proofData[AUX_PEAKS_START_IDX + i * 64:AUX_PEAKS_START_IDX + i * 64 + 32])) << 128 | uint256(bytes32(proofData[AUX_PEAKS_START_IDX + i * 64 + 32:AUX_PEAKS_START_IDX + (i + 1) * 64])) ); } /* * Constants for AxiomV2Query */ /// @dev Chain IDs for Ethereum mainnet and testnets uint64 constant MAINNET_CHAIN_ID = 1; uint64 constant GOERLI_CHAIN_ID = 5; uint64 constant SEPOLIA_CHAIN_ID = 11_155_111; uint64 constant HOLESKY_CHAIN_ID = 17_000; /// @dev Constant recording the fact that this is Axiom V2 uint8 constant VERSION = 2; /// @dev Largest deposit allowed at one time uint256 constant MAX_DEPOSIT_SIZE = 100 ether; /// @dev Conservative upper bound for `proofVerificationGas`. Real values should be lower. uint32 constant MAX_PROOF_VERIFICATION_GAS = 600_000; /// @dev Conservative upper bound for `axiomQueryFee`. Real values should be lower. uint256 constant MAX_AXIOM_QUERY_FEE = 0.05 ether;
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // Constants and free functions to be inlined into by Axiom V2 contracts for remote /// @dev Chain IDs for EVM-compatible L2s and chains uint64 constant ARBITRUM_CHAIN_ID = 42_161; uint64 constant ARBITRUM_SEPOLIA_CHAIN_ID = 421_614; uint64 constant OPTIMISM_CHAIN_ID = 10; uint64 constant OPTIMISM_SEPOLIA_CHAIN_ID = 11_155_420; uint64 constant BASE_CHAIN_ID = 8453; uint64 constant BASE_SEPOLIA_CHAIN_ID = 84_532; uint64 constant GNOSIS_CHAIN_ID = 100; uint64 constant GNOSIS_CHIADO_CHAIN_ID = 10_200; uint64 constant SCROLL_CHAIN_ID = 534_352; uint64 constant SCROLL_SEPOLIA_CHAIN_ID = 534_351; uint64 constant POLYGON_CHAIN_ID = 137; uint64 constant POLYGON_MUMBAI_CHAIN_ID = 80_001;
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import "forge-std/console.sol"; import { RLPReader } from "../libraries/optimism-mpt/rlp/RLPReader.sol"; import { MerkleTrie } from "../libraries/optimism-mpt/trie/MerkleTrie.sol"; import { SecureMerkleTrie } from "../libraries/optimism-mpt/trie/SecureMerkleTrie.sol"; import { RemoteHeaderVerifier } from "./RemoteHeaderVerifier.sol"; error L1AxiomV2CoreAddressIsZero(); error InvalidBlockHeader(); /// @title BlockhashRemoteHeaderVerifier /// @notice RemoteHeaderVerifier which assumes periodic oracle access to L1 block hashes, either through /// a cross-chain bridge or predeployed contract on a rollup. abstract contract BlockhashRemoteHeaderVerifier is RemoteHeaderVerifier { /// @dev dummy address to be used as the L1 broadcaster for interface compatibility address private constant DUMMY_L1_BROADCASTER = 0x00000000000000000000000000000000DeaDBeef; /// @dev Storage slot of the `pmmrSnapshots` variable in the AxiomV2Core contract uint256 public constant PMMR_SNAPSHOTS_SLOT = 0x000000000000000000000000000000000000000000000000000000000000014a; /// @dev Address of the AxiomV2Core contract on L1 address public immutable l1AxiomV2CoreAddress; /// @dev Verified L1 block hashes mapping(uint32 => bytes32) public l1Blockhashes; /// @dev Emitted when an L1 blockhash is stored /// @param blockNumber The block number /// @param blockhash The blockhash event L1BlockhashStored(uint32 indexed blockNumber, bytes32 blockhash); /// @dev Initialize the contract. /// @param sourceChainId The chain ID of the source chain. /// @param _l1AxiomV2CoreAddress The address of the AxiomV2Core contract on L1. constructor(uint64 sourceChainId, address _l1AxiomV2CoreAddress) RemoteHeaderVerifier(sourceChainId, DUMMY_L1_BROADCASTER) { if (_l1AxiomV2CoreAddress == address(0)) { revert L1AxiomV2CoreAddressIsZero(); } l1AxiomV2CoreAddress = _l1AxiomV2CoreAddress; } /// @notice No-op for interface compatibility. function updateLatestPmmr(uint32, /* pmmrSize */ bytes32 /* pmmrSnapshot */ ) external { revert NotBroadcaster(); } /// @notice Update with a new PMMR snapshot based on a blockhash from L1 /// @param blockNumber The block number of the blockhash. /// @param blockHeader The block header of the blockhash. /// @param accountProof The account proof of the AxiomV2Core contract. /// @param storageProof The storage proof of `pmmrSnapshots[pmmrSize]` into the storageRoot. /// @param pmmrSize The PMMR size of the snapshot. function updateLatestPmmrFromBlockhash( uint32 blockNumber, bytes calldata blockHeader, bytes[] calldata accountProof, bytes[] calldata storageProof, uint32 pmmrSize ) external { bytes32 l1Blockhash = l1Blockhashes[blockNumber]; if (l1Blockhash == bytes32(0)) { l1Blockhash = _getL1Blockhash(blockNumber); l1Blockhashes[blockNumber] = l1Blockhash; } // check block header proof if (keccak256(blockHeader) != l1Blockhash) { revert InvalidBlockHeader(); } RLPReader.RLPItem[] memory blockHeaderRlp = RLPReader.readList(blockHeader); // stateRoot is at index 3 in the block header bytes32 stateRoot = bytes32(RLPReader.readBytes(blockHeaderRlp[3])); // check account proof with relevant address bytes memory account = SecureMerkleTrie.get(abi.encodePacked(l1AxiomV2CoreAddress), accountProof, stateRoot); RLPReader.RLPItem[] memory accountRlp = RLPReader.readList(account); bytes32 storageRoot = bytes32(RLPReader.readBytes(accountRlp[2])); // check storage proof with relevant slot bytes32 trieSlot = keccak256(abi.encode(pmmrSize, PMMR_SNAPSHOTS_SLOT)); bytes memory rlpSlotValue = SecureMerkleTrie.get(abi.encodePacked(trieSlot), storageProof, storageRoot); bytes32 pmmrSnapshot = bytes32(RLPReader.readBytes(rlpSlotValue)); _updateLatestPmmr(pmmrSize, pmmrSnapshot); } /// @notice Read and store a verified blockhash from L1 for some block number, which is usually /// increasing across calls, but we do not make this assumption. function storeLatestL1Blockhash() external { (uint32 blockNumber, bytes32 blockhash) = _getLatestL1Blockhash(); l1Blockhashes[blockNumber] = blockhash; emit L1BlockhashStored(blockNumber, blockhash); } /// @notice Read and store a verified blockhash from L1 for some block number /// @param blockNumber The block number to read function storeL1Blockhash(uint32 blockNumber) external { bytes32 blockhash = _getL1Blockhash(blockNumber); l1Blockhashes[blockNumber] = blockhash; emit L1BlockhashStored(blockNumber, blockhash); } /// @notice Read a verified blockhash from L1 for some block number, which is usually /// increasing across calls, but we do not make this assumption. /// @return blockNumber The block number /// @return blockhash The blockhash function _getLatestL1Blockhash() internal virtual returns (uint32 blockNumber, bytes32 blockhash); /// @notice Read a verified blockhash from L1. Should revert if the blockhash is not available. /// @param blockNumber The block number to read /// @return blockhash The blockhash function _getL1Blockhash(uint32 blockNumber) internal virtual returns (bytes32 blockhash); }
// SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); function _sendLogPayload(bytes memory payload) private view { uint256 payloadLength = payload.length; address consoleAddress = CONSOLE_ADDRESS; /// @solidity memory-safe-assembly assembly { let payloadStart := add(payload, 32) let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) } } function log() internal view { _sendLogPayload(abi.encodeWithSignature("log()")); } function logInt(int p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); } function logUint(uint p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); } function logString(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function logBool(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function logAddress(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function logBytes(bytes memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } function logBytes1(bytes1 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } function logBytes2(bytes2 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } function logBytes3(bytes3 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } function logBytes4(bytes4 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } function logBytes5(bytes5 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } function logBytes6(bytes6 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } function logBytes7(bytes7 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } function logBytes8(bytes8 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } function logBytes9(bytes9 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } function logBytes10(bytes10 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } function logBytes11(bytes11 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } function logBytes12(bytes12 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } function logBytes13(bytes13 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } function logBytes14(bytes14 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } function logBytes15(bytes15 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } function logBytes16(bytes16 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } function logBytes17(bytes17 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } function logBytes18(bytes18 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } function logBytes19(bytes19 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } function logBytes20(bytes20 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } function logBytes21(bytes21 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } function logBytes22(bytes22 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } function logBytes23(bytes23 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } function logBytes24(bytes24 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } function logBytes25(bytes25 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } function logBytes26(bytes26 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } function logBytes27(bytes27 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } function logBytes28(bytes28 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } function logBytes29(bytes29 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } function logBytes30(bytes30 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } function logBytes31(bytes31 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } function logBytes32(bytes32 p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } function log(uint p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); } function log(string memory p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } function log(bool p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } function log(address p0) internal view { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } function log(uint p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); } function log(uint p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); } function log(uint p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); } function log(uint p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); } function log(string memory p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); } function log(string memory p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } function log(string memory p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } function log(string memory p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } function log(bool p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); } function log(bool p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } function log(bool p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } function log(bool p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } function log(address p0, uint p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); } function log(address p0, string memory p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } function log(address p0, bool p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } function log(address p0, address p1) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } function log(uint p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); } function log(uint p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); } function log(uint p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); } function log(uint p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); } function log(uint p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); } function log(uint p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); } function log(uint p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); } function log(uint p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); } function log(uint p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); } function log(uint p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); } function log(uint p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); } function log(uint p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); } function log(uint p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); } function log(uint p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); } function log(uint p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); } function log(uint p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); } function log(string memory p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); } function log(string memory p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); } function log(string memory p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); } function log(string memory p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); } function log(string memory p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); } function log(string memory p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } function log(string memory p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } function log(string memory p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } function log(string memory p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); } function log(string memory p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } function log(string memory p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } function log(string memory p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } function log(string memory p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); } function log(string memory p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } function log(string memory p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } function log(string memory p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } function log(bool p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); } function log(bool p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); } function log(bool p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); } function log(bool p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); } function log(bool p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); } function log(bool p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } function log(bool p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } function log(bool p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } function log(bool p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); } function log(bool p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } function log(bool p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } function log(bool p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } function log(bool p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); } function log(bool p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } function log(bool p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } function log(bool p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } function log(address p0, uint p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); } function log(address p0, uint p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); } function log(address p0, uint p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); } function log(address p0, uint p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); } function log(address p0, string memory p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); } function log(address p0, string memory p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } function log(address p0, string memory p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } function log(address p0, string memory p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } function log(address p0, bool p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); } function log(address p0, bool p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } function log(address p0, bool p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } function log(address p0, bool p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } function log(address p0, address p1, uint p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); } function log(address p0, address p1, string memory p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } function log(address p0, address p1, bool p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } function log(address p0, address p1, address p2) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } function log(uint p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); } function log(uint p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); } function log(uint p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); } function log(uint p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); } function log(uint p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); } function log(string memory p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); } function log(bool p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } function log(bool p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } function log(bool p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } function log(bool p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); } function log(address p0, uint p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); } function log(address p0, string memory p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } function log(address p0, bool p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, uint p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, string memory p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, bool p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, uint p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, string memory p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, bool p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); } function log(address p0, address p1, address p2, address p3) internal view { _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.8; /// @custom:attribution https://github.com/hamdiallam/Solidity-RLP /// @title RLPReader /// @notice RLPReader is a library for parsing RLP-encoded byte arrays into Solidity types. Adapted /// from Solidity-RLP (https://github.com/hamdiallam/Solidity-RLP) by Hamdi Allam with /// various tweaks to improve readability. library RLPReader { /// @notice Custom pointer type to avoid confusion between pointers and uint256s. type MemoryPointer is uint256; /// @notice RLP item types. /// @custom:value DATA_ITEM Represents an RLP data item (NOT a list). /// @custom:value LIST_ITEM Represents an RLP list item. enum RLPItemType { DATA_ITEM, LIST_ITEM } /// @notice Struct representing an RLP item. /// @custom:field length Length of the RLP item. /// @custom:field ptr Pointer to the RLP item in memory. struct RLPItem { uint256 length; MemoryPointer ptr; } /// @notice Max list length that this library will accept. uint256 internal constant MAX_LIST_LENGTH = 32; /// @notice Converts bytes to a reference to memory position and length. /// @param _in Input bytes to convert. /// @return out_ Output memory reference. function toRLPItem(bytes memory _in) internal pure returns (RLPItem memory out_) { // Empty arrays are not RLP items. require(_in.length > 0, "RLPReader: length of an RLP item must be greater than zero to be decodable"); MemoryPointer ptr; assembly { ptr := add(_in, 32) } out_ = RLPItem({ length: _in.length, ptr: ptr }); } /// @notice Reads an RLP list value into a list of RLP items. /// @param _in RLP list value. /// @return out_ Decoded RLP list items. function readList(RLPItem memory _in) internal pure returns (RLPItem[] memory out_) { (uint256 listOffset, uint256 listLength, RLPItemType itemType) = _decodeLength(_in); require(itemType == RLPItemType.LIST_ITEM, "RLPReader: decoded item type for list is not a list item"); require(listOffset + listLength == _in.length, "RLPReader: list item has an invalid data remainder"); // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by // writing to the length. Since we can't know the number of RLP items without looping over // the entire input, we'd have to loop twice to accurately size this array. It's easier to // simply set a reasonable maximum list length and decrease the size before we finish. out_ = new RLPItem[](MAX_LIST_LENGTH); uint256 itemCount = 0; uint256 offset = listOffset; while (offset < _in.length) { (uint256 itemOffset, uint256 itemLength,) = _decodeLength( RLPItem({ length: _in.length - offset, ptr: MemoryPointer.wrap(MemoryPointer.unwrap(_in.ptr) + offset) }) ); // We don't need to check itemCount < out.length explicitly because Solidity already // handles this check on our behalf, we'd just be wasting gas. out_[itemCount] = RLPItem({ length: itemLength + itemOffset, ptr: MemoryPointer.wrap(MemoryPointer.unwrap(_in.ptr) + offset) }); itemCount += 1; offset += itemOffset + itemLength; } // Decrease the array size to match the actual item count. assembly { mstore(out_, itemCount) } } /// @notice Reads an RLP list value into a list of RLP items. /// @param _in RLP list value. /// @return out_ Decoded RLP list items. function readList(bytes memory _in) internal pure returns (RLPItem[] memory out_) { out_ = readList(toRLPItem(_in)); } /// @notice Reads an RLP bytes value into bytes. /// @param _in RLP bytes value. /// @return out_ Decoded bytes. function readBytes(RLPItem memory _in) internal pure returns (bytes memory out_) { (uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in); require(itemType == RLPItemType.DATA_ITEM, "RLPReader: decoded item type for bytes is not a data item"); require(_in.length == itemOffset + itemLength, "RLPReader: bytes value contains an invalid remainder"); out_ = _copy(_in.ptr, itemOffset, itemLength); } /// @notice Reads an RLP bytes value into bytes. /// @param _in RLP bytes value. /// @return out_ Decoded bytes. function readBytes(bytes memory _in) internal pure returns (bytes memory out_) { out_ = readBytes(toRLPItem(_in)); } /// @notice Reads the raw bytes of an RLP item. /// @param _in RLP item to read. /// @return out_ Raw RLP bytes. function readRawBytes(RLPItem memory _in) internal pure returns (bytes memory out_) { out_ = _copy(_in.ptr, 0, _in.length); } /// @notice Decodes the length of an RLP item. /// @param _in RLP item to decode. /// @return offset_ Offset of the encoded data. /// @return length_ Length of the encoded data. /// @return type_ RLP item type (LIST_ITEM or DATA_ITEM). function _decodeLength(RLPItem memory _in) private pure returns (uint256 offset_, uint256 length_, RLPItemType type_) { // Short-circuit if there's nothing to decode, note that we perform this check when // the user creates an RLP item via toRLPItem, but it's always possible for them to bypass // that function and create an RLP item directly. So we need to check this anyway. require(_in.length > 0, "RLPReader: length of an RLP item must be greater than zero to be decodable"); MemoryPointer ptr = _in.ptr; uint256 prefix; assembly { prefix := byte(0, mload(ptr)) } if (prefix <= 0x7f) { // Single byte. return (0, 1, RLPItemType.DATA_ITEM); } else if (prefix <= 0xb7) { // Short string. // slither-disable-next-line variable-scope uint256 strLen = prefix - 0x80; require( _in.length > strLen, "RLPReader: length of content must be greater than string length (short string)" ); bytes1 firstByteOfContent; assembly { firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff)) } require( strLen != 1 || firstByteOfContent >= 0x80, "RLPReader: invalid prefix, single byte < 0x80 are not prefixed (short string)" ); return (1, strLen, RLPItemType.DATA_ITEM); } else if (prefix <= 0xbf) { // Long string. uint256 lenOfStrLen = prefix - 0xb7; require( _in.length > lenOfStrLen, "RLPReader: length of content must be > than length of string length (long string)" ); bytes1 firstByteOfContent; assembly { firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff)) } require( firstByteOfContent != 0x00, "RLPReader: length of content must not have any leading zeros (long string)" ); uint256 strLen; assembly { strLen := shr(sub(256, mul(8, lenOfStrLen)), mload(add(ptr, 1))) } require(strLen > 55, "RLPReader: length of content must be greater than 55 bytes (long string)"); require( _in.length > lenOfStrLen + strLen, "RLPReader: length of content must be greater than total length (long string)" ); return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM); } else if (prefix <= 0xf7) { // Short list. // slither-disable-next-line variable-scope uint256 listLen = prefix - 0xc0; require(_in.length > listLen, "RLPReader: length of content must be greater than list length (short list)"); return (1, listLen, RLPItemType.LIST_ITEM); } else { // Long list. uint256 lenOfListLen = prefix - 0xf7; require( _in.length > lenOfListLen, "RLPReader: length of content must be > than length of list length (long list)" ); bytes1 firstByteOfContent; assembly { firstByteOfContent := and(mload(add(ptr, 1)), shl(248, 0xff)) } require( firstByteOfContent != 0x00, "RLPReader: length of content must not have any leading zeros (long list)" ); uint256 listLen; assembly { listLen := shr(sub(256, mul(8, lenOfListLen)), mload(add(ptr, 1))) } require(listLen > 55, "RLPReader: length of content must be greater than 55 bytes (long list)"); require( _in.length > lenOfListLen + listLen, "RLPReader: length of content must be greater than total length (long list)" ); return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM); } } /// @notice Copies the bytes from a memory location. /// @param _src Pointer to the location to read from. /// @param _offset Offset to start reading from. /// @param _length Number of bytes to read. /// @return out_ Copied bytes. function _copy(MemoryPointer _src, uint256 _offset, uint256 _length) private pure returns (bytes memory out_) { out_ = new bytes(_length); if (_length == 0) { return out_; } // Mostly based on Solidity's copy_memory_to_memory: // https://github.com/ethereum/solidity/blob/34dd30d71b4da730488be72ff6af7083cf2a91f6/libsolidity/codegen/YulUtilFunctions.cpp#L102-L114 uint256 src = MemoryPointer.unwrap(_src) + _offset; assembly { let dest := add(out_, 32) let i := 0 for { } lt(i, _length) { i := add(i, 32) } { mstore(add(dest, i), mload(add(src, i))) } if gt(i, _length) { mstore(add(dest, _length), 0) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { Bytes } from "../Bytes.sol"; import { RLPReader } from "../rlp/RLPReader.sol"; /// @title MerkleTrie /// @notice MerkleTrie is a small library for verifying standard Ethereum Merkle-Patricia trie /// inclusion proofs. By default, this library assumes a hexary trie. One can change the /// trie radix constant to support other trie radixes. library MerkleTrie { /// @notice Struct representing a node in the trie. /// @custom:field encoded The RLP-encoded node. /// @custom:field decoded The RLP-decoded node. struct TrieNode { bytes encoded; RLPReader.RLPItem[] decoded; } /// @notice Determines the number of elements per branch node. uint256 internal constant TREE_RADIX = 16; /// @notice Branch nodes have TREE_RADIX elements and one value element. uint256 internal constant BRANCH_NODE_LENGTH = TREE_RADIX + 1; /// @notice Leaf nodes and extension nodes have two elements, a `path` and a `value`. uint256 internal constant LEAF_OR_EXTENSION_NODE_LENGTH = 2; /// @notice Prefix for even-nibbled extension node paths. uint8 internal constant PREFIX_EXTENSION_EVEN = 0; /// @notice Prefix for odd-nibbled extension node paths. uint8 internal constant PREFIX_EXTENSION_ODD = 1; /// @notice Prefix for even-nibbled leaf node paths. uint8 internal constant PREFIX_LEAF_EVEN = 2; /// @notice Prefix for odd-nibbled leaf node paths. uint8 internal constant PREFIX_LEAF_ODD = 3; /// @notice Verifies a proof that a given key/value pair is present in the trie. /// @param _key Key of the node to search for, as a hex string. /// @param _value Value of the node to search for, as a hex string. /// @param _proof Merkle trie inclusion proof for the desired node. Unlike traditional Merkle /// trees, this proof is executed top-down and consists of a list of RLP-encoded /// nodes that make a path down to the target node. /// @param _root Known root of the Merkle trie. Used to verify that the included proof is /// correctly constructed. /// @return valid_ Whether or not the proof is valid. function verifyInclusionProof(bytes memory _key, bytes memory _value, bytes[] memory _proof, bytes32 _root) internal pure returns (bool valid_) { valid_ = Bytes.equal(_value, get(_key, _proof, _root)); } /// @notice Retrieves the value associated with a given key. /// @param _key Key to search for, as hex bytes. /// @param _proof Merkle trie inclusion proof for the key. /// @param _root Known root of the Merkle trie. /// @return value_ Value of the key if it exists. function get(bytes memory _key, bytes[] memory _proof, bytes32 _root) internal pure returns (bytes memory value_) { require(_key.length > 0, "MerkleTrie: empty key"); TrieNode[] memory proof = _parseProof(_proof); bytes memory key = Bytes.toNibbles(_key); bytes memory currentNodeID = abi.encodePacked(_root); uint256 currentKeyIndex = 0; // Proof is top-down, so we start at the first element (root). for (uint256 i = 0; i < proof.length; i++) { TrieNode memory currentNode = proof[i]; // Key index should never exceed total key length or we'll be out of bounds. require(currentKeyIndex <= key.length, "MerkleTrie: key index exceeds total key length"); if (currentKeyIndex == 0) { // First proof element is always the root node. require( Bytes.equal(abi.encodePacked(keccak256(currentNode.encoded)), currentNodeID), "MerkleTrie: invalid root hash" ); } else if (currentNode.encoded.length >= 32) { // Nodes 32 bytes or larger are hashed inside branch nodes. require( Bytes.equal(abi.encodePacked(keccak256(currentNode.encoded)), currentNodeID), "MerkleTrie: invalid large internal hash" ); } else { // Nodes smaller than 32 bytes aren't hashed. require(Bytes.equal(currentNode.encoded, currentNodeID), "MerkleTrie: invalid internal node hash"); } if (currentNode.decoded.length == BRANCH_NODE_LENGTH) { if (currentKeyIndex == key.length) { // Value is the last element of the decoded list (for branch nodes). There's // some ambiguity in the Merkle trie specification because bytes(0) is a // valid value to place into the trie, but for branch nodes bytes(0) can exist // even when the value wasn't explicitly placed there. Geth treats a value of // bytes(0) as "key does not exist" and so we do the same. value_ = RLPReader.readBytes(currentNode.decoded[TREE_RADIX]); require(value_.length > 0, "MerkleTrie: value length must be greater than zero (branch)"); // Extra proof elements are not allowed. require(i == proof.length - 1, "MerkleTrie: value node must be last node in proof (branch)"); return value_; } else { // We're not at the end of the key yet. // Figure out what the next node ID should be and continue. uint8 branchKey = uint8(key[currentKeyIndex]); RLPReader.RLPItem memory nextNode = currentNode.decoded[branchKey]; currentNodeID = _getNodeID(nextNode); currentKeyIndex += 1; } } else if (currentNode.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) { bytes memory path = _getNodePath(currentNode); uint8 prefix = uint8(path[0]); uint8 offset = 2 - (prefix % 2); bytes memory pathRemainder = Bytes.slice(path, offset); bytes memory keyRemainder = Bytes.slice(key, currentKeyIndex); uint256 sharedNibbleLength = _getSharedNibbleLength(pathRemainder, keyRemainder); // Whether this is a leaf node or an extension node, the path remainder MUST be a // prefix of the key remainder (or be equal to the key remainder) or the proof is // considered invalid. require( pathRemainder.length == sharedNibbleLength, "MerkleTrie: path remainder must share all nibbles with key" ); if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) { // Prefix of 2 or 3 means this is a leaf node. For the leaf node to be valid, // the key remainder must be exactly equal to the path remainder. We already // did the necessary byte comparison, so it's more efficient here to check that // the key remainder length equals the shared nibble length, which implies // equality with the path remainder (since we already did the same check with // the path remainder and the shared nibble length). require( keyRemainder.length == sharedNibbleLength, "MerkleTrie: key remainder must be identical to path remainder" ); // Our Merkle Trie is designed specifically for the purposes of the Ethereum // state trie. Empty values are not allowed in the state trie, so we can safely // say that if the value is empty, the key should not exist and the proof is // invalid. value_ = RLPReader.readBytes(currentNode.decoded[1]); require(value_.length > 0, "MerkleTrie: value length must be greater than zero (leaf)"); // Extra proof elements are not allowed. require(i == proof.length - 1, "MerkleTrie: value node must be last node in proof (leaf)"); return value_; } else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) { // Prefix of 0 or 1 means this is an extension node. We move onto the next node // in the proof and increment the key index by the length of the path remainder // which is equal to the shared nibble length. currentNodeID = _getNodeID(currentNode.decoded[1]); currentKeyIndex += sharedNibbleLength; } else { revert("MerkleTrie: received a node with an unknown prefix"); } } else { revert("MerkleTrie: received an unparseable node"); } } revert("MerkleTrie: ran out of proof elements"); } /// @notice Parses an array of proof elements into a new array that contains both the original /// encoded element and the RLP-decoded element. /// @param _proof Array of proof elements to parse. /// @return proof_ Proof parsed into easily accessible structs. function _parseProof(bytes[] memory _proof) private pure returns (TrieNode[] memory proof_) { uint256 length = _proof.length; proof_ = new TrieNode[](length); for (uint256 i = 0; i < length;) { proof_[i] = TrieNode({ encoded: _proof[i], decoded: RLPReader.readList(_proof[i]) }); unchecked { ++i; } } } /// @notice Picks out the ID for a node. Node ID is referred to as the "hash" within the /// specification, but nodes < 32 bytes are not actually hashed. /// @param _node Node to pull an ID for. /// @return id_ ID for the node, depending on the size of its contents. function _getNodeID(RLPReader.RLPItem memory _node) private pure returns (bytes memory id_) { id_ = _node.length < 32 ? RLPReader.readRawBytes(_node) : RLPReader.readBytes(_node); } /// @notice Gets the path for a leaf or extension node. /// @param _node Node to get a path for. /// @return nibbles_ Node path, converted to an array of nibbles. function _getNodePath(TrieNode memory _node) private pure returns (bytes memory nibbles_) { nibbles_ = Bytes.toNibbles(RLPReader.readBytes(_node.decoded[0])); } /// @notice Utility; determines the number of nibbles shared between two nibble arrays. /// @param _a First nibble array. /// @param _b Second nibble array. /// @return shared_ Number of shared nibbles. function _getSharedNibbleLength(bytes memory _a, bytes memory _b) private pure returns (uint256 shared_) { uint256 max = (_a.length < _b.length) ? _a.length : _b.length; for (; shared_ < max && _a[shared_] == _b[shared_];) { unchecked { ++shared_; } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { MerkleTrie } from "./MerkleTrie.sol"; /// @title SecureMerkleTrie /// @notice SecureMerkleTrie is a thin wrapper around the MerkleTrie library that hashes the input /// keys. Ethereum's state trie hashes input keys before storing them. library SecureMerkleTrie { /// @notice Verifies a proof that a given key/value pair is present in the Merkle trie. /// @param _key Key of the node to search for, as a hex string. /// @param _value Value of the node to search for, as a hex string. /// @param _proof Merkle trie inclusion proof for the desired node. Unlike traditional Merkle /// trees, this proof is executed top-down and consists of a list of RLP-encoded /// nodes that make a path down to the target node. /// @param _root Known root of the Merkle trie. Used to verify that the included proof is /// correctly constructed. /// @return valid_ Whether or not the proof is valid. function verifyInclusionProof(bytes memory _key, bytes memory _value, bytes[] memory _proof, bytes32 _root) internal pure returns (bool valid_) { bytes memory key = _getSecureKey(_key); valid_ = MerkleTrie.verifyInclusionProof(key, _value, _proof, _root); } /// @notice Retrieves the value associated with a given key. /// @param _key Key to search for, as hex bytes. /// @param _proof Merkle trie inclusion proof for the key. /// @param _root Known root of the Merkle trie. /// @return value_ Value of the key if it exists. function get(bytes memory _key, bytes[] memory _proof, bytes32 _root) internal pure returns (bytes memory value_) { bytes memory key = _getSecureKey(_key); value_ = MerkleTrie.get(key, _proof, _root); } /// @notice Computes the hashed version of the input key. /// @param _key Key to hash. /// @return hash_ Hashed version of the key. function _getSecureKey(bytes memory _key) private pure returns (bytes memory hash_) { hash_ = abi.encodePacked(keccak256(_key)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import { IRemoteHeaderVerifier } from "../interfaces/remote/IRemoteHeaderVerifier.sol"; import { IAxiomV2State } from "../interfaces/core/IAxiomV2State.sol"; import { MerkleTree } from "../libraries/MerkleTree.sol"; import { MerkleMountainRange } from "../libraries/MerkleMountainRange.sol"; import { PaddedMerkleMountainRange } from "../libraries/PaddedMerkleMountainRange.sol"; import { BLOCK_BATCH_SIZE, BLOCK_BATCH_DEPTH } from "../libraries/configuration/AxiomV2Configuration.sol"; /// @title RemoteHeaderVerifier /// @notice Base IRemoteHeaderVerifier for all remote chains. abstract contract RemoteHeaderVerifier is IRemoteHeaderVerifier { using PaddedMerkleMountainRange for PaddedMerkleMountainRange.PMMR; using MerkleMountainRange for MerkleMountainRange.MMR; /// @dev The chain ID of the source chain PMMRs are received from uint64 internal immutable SOURCE_CHAIN_ID; /// @dev The address of the L1 broadcaster contract address public immutable L1_BROADCASTER; /// @dev Snapshots of commitments to `blockhashPmmr` as computed by `PaddedMerkleMountainRange.commit` /// `pmmrSnapshots[pmmrSize]` is a commitment to block hashes for blocks `[0, pmmrSize)` mapping(uint32 => bytes32) public pmmrSnapshots; /// @dev Initialize the contract. /// @param sourceChainId The chain ID of the source chain PMMRs are received from /// @param l1Broadcaster The address of the L1 broadcaster contract constructor(uint64 sourceChainId, address l1Broadcaster) { if (l1Broadcaster == address(0)) { revert BroadcasterAddressIsZero(); } SOURCE_CHAIN_ID = sourceChainId; L1_BROADCASTER = l1Broadcaster; } /// @dev This verifier handles the case of the L2 target chain and L1 source chain. /// /// At proof time, let `blockhashPmmr` commit to blocks `[0, currentPmmrSize)`. /// Each query also has some minimum range of blocks `[0, queryPmmrSize)` which must be accessed. /// -- Expect `queryBlockNum <= currentBlockNum`, then we check that `blockhashMmrKeccak` is committed /// to in `blockhashPmmr` using the commitment to `blockhashPmmr` in `pmmrSnapshots`. function verifyQueryHeaders(bytes32 proofMmrKeccak, MmrWitness calldata mmrWitness) external view { bytes32[] memory peaks = mmrWitness.proofMmrPeaks; uint32 snapshotPmmrSize = mmrWitness.snapshotPmmrSize; if (proofMmrKeccak != keccak256(abi.encodePacked(peaks))) { revert ProofMmrKeccakDoesNotMatch(); } uint32 proofMmrSize; uint256 proofMmrPeaksLength = peaks.length; // Get proofMmrSize from heights of witnessMmrPeaks for (uint256 idx; idx < proofMmrPeaksLength;) { if (peaks[idx] != bytes32(0)) { proofMmrSize = proofMmrSize + uint32(1 << idx); } unchecked { ++idx; } } if (proofMmrSize <= snapshotPmmrSize) { // Creating a proof PMMR with empty padded leaf and complete leaf peaks from proofMmrPeaks[BLOCK_BATCH_DEPTH:] PaddedMerkleMountainRange.PMMR memory proofPmmr = PaddedMerkleMountainRange.PMMR({ paddedLeaf: bytes32(0), completeLeaves: MerkleMountainRange.fromPeaks( peaks, BLOCK_BATCH_DEPTH, proofMmrPeaksLength - BLOCK_BATCH_DEPTH ), size: proofMmrSize - (proofMmrSize % BLOCK_BATCH_SIZE) }); MerkleMountainRange.MMR memory proofBatchMmr = MerkleMountainRange.fromPeaks(peaks, 0, BLOCK_BATCH_DEPTH); // We check `proofMmrPeaks` can be extended to the MMR committed to // in `pmmrSnapshots[mmrWitness.snapshotPmmrSize]` // // This can happen in two possible ways: // * If `snapshotPmmrSize - (snapshotPmmrSize % 1024) > proofMmrSize`, then we can check: // -- the completion of proofMmrPeaks[:10] to a full Merkle root // -- the extension of proofMmrPeaks[10:] by this Merkle root and additional Merkle roots // -- the extension of the resulting MMR by a padded leaf // -- this result should match `pmmrSnapshots[snapshotPmmrSize]` // // * If `snapshotPmmrSize - (snapshotPmmrSize % 1024) <= proofMmrSize`, then we can check: // -- the completion of proofMmrPeaks[:10] to a full Merkle root with zero padding // -- the commitment of the resulting PMMR should match `pmmrSnapshots[snapshotPmmrSize]` if (snapshotPmmrSize - (snapshotPmmrSize % BLOCK_BATCH_SIZE) >= proofMmrSize) { if (proofMmrSize % BLOCK_BATCH_SIZE > 0) { // complete the first 10 peaks of `proofMmrPeaks` to a full Merkle root and update `proofPmmr` bytes32 completedLeaf = proofBatchMmr.getComplementMerkleRoot(BLOCK_BATCH_DEPTH, mmrWitness.mmrComplementOrPeaks); proofPmmr.updatePaddedLeaf(BLOCK_BATCH_SIZE, completedLeaf, BLOCK_BATCH_SIZE); } if (snapshotPmmrSize - (snapshotPmmrSize % BLOCK_BATCH_SIZE) > proofMmrSize) { // append additional complete leaves proofPmmr.appendCompleteLeaves(BLOCK_BATCH_SIZE, mmrWitness.mmrComplementOrPeaks[11:]); } proofPmmr.updatePaddedLeaf( BLOCK_BATCH_SIZE, mmrWitness.mmrComplementOrPeaks[10], snapshotPmmrSize % BLOCK_BATCH_SIZE ); } else { // complete the first 10 peaks of `proofMmrPeaks` to a full Merkle root and update `proofPmmr` bytes32 completedLeaf = proofBatchMmr.getComplementMerkleRoot(BLOCK_BATCH_DEPTH, mmrWitness.mmrComplementOrPeaks); proofPmmr.updatePaddedLeaf(BLOCK_BATCH_SIZE, completedLeaf, snapshotPmmrSize % BLOCK_BATCH_SIZE); } // check the resulting PMMR is committed to in `pmmrSnapshots[mmrWitness.snapshotPmmrSize]` bytes32 completePmmrKeccak = proofPmmr.commit(); if (completePmmrKeccak != pmmrSnapshots[snapshotPmmrSize]) { revert BlockhashMmrKeccakDoesNotMatchProof(); } } else { revert NoMoreRecentBlockhashPmmr(); } } /// @dev Update with a new PMMR snapshot from L1. Assumes that `pmmrSize` and `pmmrSnapshot` have /// already been validated by chain-specific logic implemented before this call. function _updateLatestPmmr(uint32 pmmrSize, bytes32 pmmrSnapshot) internal { pmmrSnapshots[pmmrSize] = pmmrSnapshot; emit UpdatedPmmr(pmmrSize, pmmrSnapshot); } /// @notice Return the `chainId` of the source chain /// @return chainId The `chainId` function getSourceChainId() external view returns (uint64) { return SOURCE_CHAIN_ID; } /// @notice Implements ERC-165 interface check function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IRemoteHeaderVerifier).interfaceId; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Bytes /// @notice Bytes is a library for manipulating byte arrays. library Bytes { /// @custom:attribution https://github.com/GNSPS/solidity-bytes-utils /// @notice Slices a byte array with a given starting index and length. Returns a new byte array /// as opposed to a pointer to the original array. Will throw if trying to slice more /// bytes than exist in the array. /// @param _bytes Byte array to slice. /// @param _start Starting index of the slice. /// @param _length Length of the slice. /// @return Slice of the input byte array. function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { unchecked { require(_length + 31 >= _length, "slice_overflow"); require(_start + _length >= _start, "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; } /// @notice Slices a byte array with a given starting index up to the end of the original byte /// array. Returns a new array rathern than a pointer to the original. /// @param _bytes Byte array to slice. /// @param _start Starting index of the slice. /// @return Slice of the input byte array. function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) { if (_start >= _bytes.length) { return bytes(""); } return slice(_bytes, _start, _bytes.length - _start); } /// @notice Converts a byte array into a nibble array by splitting each byte into two nibbles. /// Resulting nibble array will be exactly twice as long as the input byte array. /// @param _bytes Input byte array to convert. /// @return Resulting nibble array. function toNibbles(bytes memory _bytes) internal pure returns (bytes memory) { bytes memory _nibbles; assembly { // Grab a free memory offset for the new array _nibbles := mload(0x40) // Load the length of the passed bytes array from memory let bytesLength := mload(_bytes) // Calculate the length of the new nibble array // This is the length of the input array times 2 let nibblesLength := shl(0x01, bytesLength) // Update the free memory pointer to allocate memory for the new array. // To do this, we add the length of the new array + 32 bytes for the array length // rounded up to the nearest 32 byte boundary to the current free memory pointer. mstore(0x40, add(_nibbles, and(not(0x1F), add(nibblesLength, 0x3F)))) // Store the length of the new array in memory mstore(_nibbles, nibblesLength) // Store the memory offset of the _bytes array's contents on the stack let bytesStart := add(_bytes, 0x20) // Store the memory offset of the nibbles array's contents on the stack let nibblesStart := add(_nibbles, 0x20) // Loop through each byte in the input array for { let i := 0x00 } lt(i, bytesLength) { i := add(i, 0x01) } { // Get the starting offset of the next 2 bytes in the nibbles array let offset := add(nibblesStart, shl(0x01, i)) // Load the byte at the current index within the `_bytes` array let b := byte(0x00, mload(add(bytesStart, i))) // Pull out the first nibble and store it in the new array mstore8(offset, shr(0x04, b)) // Pull out the second nibble and store it in the new array mstore8(add(offset, 0x01), and(b, 0x0F)) } } return _nibbles; } /// @notice Compares two byte arrays by comparing their keccak256 hashes. /// @param _bytes First byte array to compare. /// @param _other Second byte array to compare. /// @return True if the two byte arrays are equal, false otherwise. function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { return keccak256(_bytes) == keccak256(_other); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IAxiomV2HeaderVerifier } from "../query/IAxiomV2HeaderVerifier.sol"; interface IRemoteHeaderVerifier is IAxiomV2HeaderVerifier { /// @notice Emitted when any PMMR update is received from L1 /// @param pmmrSize The size of the PMMR in the update /// @param pmmrSnapshot The PMMR snapshot in the update event UpdatedPmmr(uint32 indexed pmmrSize, bytes32 indexed pmmrSnapshot); /// @dev Error returned if the broadcaster address is 0. error BroadcasterAddressIsZero(); /// @dev Error returned if the sender is not the broadcaster. error NotBroadcaster(); /// @dev Error returned if a IRemoteHeaderVerifier contract is initialized on the wrong chain. error InvalidChainId(); /// @dev Error returned if a IRemoteHeaderVerifier contract is initialized with the wrong source chain ID. error InvalidSourceChainId(); /// @notice Update the remote header verifier with a new PMMR snapshot from L1 /// @param pmmrSize The size of the PMMR in the update /// @param pmmrSnapshot The PMMR snapshot in the update function updateLatestPmmr(uint32 pmmrSize, bytes32 pmmrSnapshot) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { PaddedMerkleMountainRange } from "../../libraries/PaddedMerkleMountainRange.sol"; import { MerkleMountainRange } from "../../libraries/MerkleMountainRange.sol"; interface IAxiomV2State { /// @notice Returns the hash of a batch of consecutive blocks previously verified by the contract /// @param startBlockNumber The block number of the first block in the batch /// @dev The reads here will match the emitted #UpdateEvent /// @return historicalRoots(startBlockNumber) is 0 unless (startBlockNumber % 1024 == 0) /// historicalRoots(startBlockNumber) = 0 if block `startBlockNumber` is not verified /// historicalRoots(startBlockNumber) = keccak256(prevHash || root || numFinal) where || is concatenation /// - prevHash is the parent hash of block `startBlockNumber` /// - root is the keccak Merkle root of hash(i) for i in [0, 1024), where /// hash(i) is the blockhash of block `startBlockNumber + i` if i < numFinal, /// hash(i) = bytes32(0x0) if i >= numFinal /// - 0 < numFinal <= 1024 is the number of verified consecutive roots in [startBlockNumber, startBlockNumber + numFinal) function historicalRoots(uint32 startBlockNumber) external view returns (bytes32); /// @notice Get the number of consecutive blocks from genesis currently committed to in `blockhashPmmr` /// The padded Merkle mountain range `blockhashPmmr` commits to the block hashes of blocks /// `[0, pmmrSize)` /// @return pmmrSize indicates that the blockhashPmmr commits to blockhashes of blocks `[0, pmmrSize)` function blockhashPmmrSize() external view returns (uint32 pmmrSize); /// @notice Get the `paddedLeaf` of the padded Merkle mountain range `blockhashPmmr` /// @return paddedLeaf the `paddedLeaf` corresponding to `blockhashPmmr` function blockhashPmmrLeaf() external view returns (bytes32); /// @notice Returns the PMMR commitment to the blockhashes of blocks `[0, pmmrSize)`, if it exists, `bytes32(0x0)` otherwise /// @param pmmrSize The number of blocks committed to in the PMMR /// @return pmmrHash The hash of the PMMR, as computed by `PaddedMerkleMountainRange.commit` function pmmrSnapshots(uint32 pmmrSize) external view returns (bytes32); /// @notice Returns the Merkle mountain range of peaks in `blockhashPmmr /// @return mmr The Merkle mountain range. function blockhashPmmrPeaks() external view returns (MerkleMountainRange.MMR memory); /// @notice Get the full current `blockhashPmmr` /// @return blockhashPmmr The current PMMR commitment to historic block hashes function fullBlockhashPmmr() external view returns (PaddedMerkleMountainRange.PMMR memory); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { HISTORICAL_NUM_ROOTS } from "./configuration/AxiomV2Configuration.sol"; import { Hash } from "./Hash.sol"; /// @title Merkle Tree /// @notice Helper functions for computing Merkle roots of Merkle trees library MerkleTree { /// @dev Error returned if the empty hash depth is not in range [0, 10) error InvalidEmptyHashDepth(); /// @notice Compute the Merkle root of a Merkle tree with HISTORICAL_NUM_ROOTS leaves /// @param leaves The HISTORICAL_NUM_ROOTS leaves of the Merkle tree function merkleRoot(bytes32[HISTORICAL_NUM_ROOTS] memory leaves) internal pure returns (bytes32) { // we create a new array to avoid mutating `leaves`, which is passed by reference // unnecessary if calldata `leaves` is passed in since it is automatically copied to memory bytes32[] memory hashes = new bytes32[](HISTORICAL_NUM_ROOTS / 2); for (uint256 i; i < HISTORICAL_NUM_ROOTS / 2;) { hashes[i] = Hash.keccak(leaves[i << 1], leaves[(i << 1) | 1]); unchecked { ++i; } } uint256 len = HISTORICAL_NUM_ROOTS / 4; while (len != 0) { for (uint256 i; i < len;) { hashes[i] = Hash.keccak(hashes[i << 1], hashes[(i << 1) | 1]); unchecked { ++i; } } len >>= 1; } return hashes[0]; } /// @notice Compute the Merkle root of a Merkle tree with 2^depth leaves all equal to bytes32(0x0) /// @param depth The depth of the Merkle tree, 0 <= depth < BLOCK_BATCH_DEPTH. function getEmptyHash(uint256 depth) internal pure returns (bytes32) { // emptyHashes[idx] is the Merkle root of a tree of depth idx with 0's as leaves if (depth == 0) { return bytes32(0x0000000000000000000000000000000000000000000000000000000000000000); } if (depth == 1) { return bytes32(0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5); } if (depth == 2) { return bytes32(0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30); } if (depth == 3) { return bytes32(0x21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85); } if (depth == 4) { return bytes32(0xe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344); } if (depth == 5) { return bytes32(0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d); } if (depth == 6) { return bytes32(0x887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968); } if (depth == 7) { return bytes32(0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83); } if (depth == 8) { return bytes32(0x9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af); } if (depth == 9) { return bytes32(0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0); } revert InvalidEmptyHashDepth(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { MerkleTree } from "./MerkleTree.sol"; import { Hash } from "./Hash.sol"; uint256 constant MAX_MMR_PEAKS = 32; /// @title Merkle Mountain Range /// @author Axiom /// @notice Library for Merkle Mountain Range data structure library MerkleMountainRange { /// @notice A Merkle mountain range is a data structure for efficiently storing a commitment to a variable length list of bytes32 values. /// @param peaks The peaks of the MMR as a fixed-length array of length 32. /// `peaks` is ordered in *increasing* size of peaks: `peaks[i]` is the Merkle root of a tree of size `2 ** i` corresponding to the `i`th bit of `len` (see @dev for details) /// @param peaksLength The actual number of peaks in the MMR /// @dev peaks stores `peaksLength := bit_length(len)` Merkle roots, with /// `peaks[i] = root(list[((len >> i) << i) - 2^i : ((len >> i) << i)])` if 2^i & len != 0, otherwise 0 /// where root(single element) = single element, and `list` is the underlying list for the MMR /// Warning: Only use the check `peaks[i] == 0` to determine if `peaks[i]` is undefined if the original list is guaranteed to not contain 0 /// (e.g., if the original list is already of hashes) /// Default initialization is to `len = 0`, `peaksLength = 0`, and all `peaks[i] = 0` struct MMR { bytes32[MAX_MMR_PEAKS] peaks; uint256 peaksLength; } /// @dev Create an MMR from a variable length array /// @param peaks The variable length array /// @return out The MMR in memory function fromPeaks(bytes32[] memory peaks) internal pure returns (MMR memory out) { return fromPeaks(peaks, 0, peaks.length); } /// @notice Create an MMR from a slice of variable length array /// @dev Only reads the peaks up to `peaksLength` /// @param peaks The variable length array /// @param start The start index of the subarray /// @param length The length of the subarray /// @return out The MMR in memory function fromPeaks(bytes32[] memory peaks, uint256 start, uint256 length) internal pure returns (MMR memory out) { out.peaksLength = length; for (uint256 idx; idx < length;) { unchecked { out.peaks[idx] = peaks[start + idx]; ++idx; } } } /// @notice Copies the MMR to memory /// @dev Only reads the peaks up to `peaksLength` /// @param self The MMR /// @return out The MMR in memory function clone(MMR storage self) internal view returns (MMR memory out) { out.peaksLength = self.peaksLength; uint256 outPeaksLength = out.peaksLength; for (uint256 i; i < outPeaksLength;) { out.peaks[i] = self.peaks[i]; unchecked { ++i; } } } /// @notice Copies MMR from memory to storage /// @dev Only changes peaks up to `peaksChanged` to limit SSTOREs /// @param self The MMR in storage /// @param peaksChanged Only copy newMMR.peaks[0 : peaksChanged] function persistFrom(MMR storage self, MMR memory newMMR, uint256 peaksChanged) internal { self.peaksLength = newMMR.peaksLength; for (uint256 i; i < peaksChanged;) { self.peaks[i] = newMMR.peaks[i]; unchecked { ++i; } } } /// @notice Compute the keccak of the concatenated peaks /// @param self The MMR /// @return keccak of the concatenated peaks function commit(MMR memory self) internal pure returns (bytes32) { bytes32[] memory peaks = new bytes32[](self.peaksLength); uint256 peaksLength = self.peaksLength; for (uint256 i; i < peaksLength;) { peaks[i] = self.peaks[i]; unchecked { ++i; } } return keccak256(abi.encodePacked(peaks)); } /// @notice Append a new element to the underlying list of the MMR /// @param self The MMR /// @param leaf The new element to append /// @return peaksChanged self.peaks[0 : peaksChanged] have been changed function appendLeaf(MMR memory self, bytes32 leaf) internal pure returns (uint256 peaksChanged) { unchecked { bytes32 newPeak = leaf; uint256 i; uint256 peaksLength = self.peaksLength; for (; i < peaksLength && self.peaks[i] != bytes32(0);) { newPeak = Hash.keccak(self.peaks[i], newPeak); delete self.peaks[i]; ++i; } self.peaks[i] = newPeak; if (i >= peaksLength) { self.peaksLength = i + 1; } peaksChanged = i + 1; } } /// @notice Append a sequence of new elements to the underlying list of the MMR, in order /// @dev Optimized compared to looping over `appendLeaf` /// @param self The MMR /// @param leaves The new elements to append /// @return peaksChanged self.peaks[0 : peaksChanged] have been changed /// @dev Warning: To save gas, this method overwrites values of `leaves` with intermediate computations. /// The input values of `leaves` should be considered invalidated after calling this method. function appendLeaves(MMR memory self, bytes32[] memory leaves) internal pure returns (uint256 peaksChanged) { // keeps track of running length of `leaves` uint256 toAdd = leaves.length; uint256 shift; uint256 i; bytes32 left; bytes32 right; uint256 nextAdd; uint256 bound; while (toAdd != 0) { // shift records whether there is an existing peak in the range we should hash with shift = (self.peaks[i] == bytes32(0)) ? 0 : 1; // if shift, add peaks[i] to beginning of leaves // then hash all leaves unchecked { nextAdd = (toAdd + shift) >> 1; } bound = (nextAdd << 1); for (uint256 j; j < bound;) { if (shift == 1) { if (j == 0) { left = self.peaks[i]; } else { unchecked { left = leaves[j - 1]; } } right = leaves[j]; } else { left = leaves[j]; unchecked { right = leaves[j + 1]; } } leaves[j >> 1] = Hash.keccak(left, right); unchecked { j = j + 2; } } // if toAdd + shift is odd, the last element is new self.peaks[i], otherwise 0 if (toAdd & 1 != shift) { unchecked { // toAdd is non-zero in this branch self.peaks[i] = leaves[toAdd - 1]; } } else if (shift == 1) { // if shift == 0 then self.peaks[i] is already 0 self.peaks[i] = 0; } toAdd = nextAdd; unchecked { ++i; } } if (i > self.peaksLength) { self.peaksLength = i; } peaksChanged = i; } /** * @notice Compute the `completeLeaves` of an existing MMR when converted to a padded MMR with depth `paddingDepth`. * @param self The MMR. * @param paddingDepth The depth of the padded Merkle tree. * @return out The `completeLeaves` of the padded Merkle mountain range corresponding to the MMR. */ function getCompleteLeaves(MMR memory self, uint256 paddingDepth) internal pure returns (MMR memory out) { unchecked { // if self.peaksLength < paddingDepth, then out.peaksLength = 0 if (self.peaksLength >= paddingDepth) { out.peaksLength = self.peaksLength - paddingDepth; } for (uint256 i = paddingDepth; i < self.peaksLength;) { out.peaks[i - paddingDepth] = self.peaks[i]; ++i; } } } /** * @notice Hash an existing MMR to a Merkle root of a 0-padded Merkle tree with depth `paddingDepth`. * @param self The MMR. * @param paddingDepth The depth of the padded Merkle tree. * @return root The Merkle root of the padded MMR. */ function getZeroPaddedMerkleRoot(MMR memory self, uint256 paddingDepth) internal pure returns (bytes32) { bytes32 root; bool started; for (uint256 peakIdx; peakIdx < paddingDepth;) { if (!started && self.peaks[peakIdx] != bytes32(0)) { root = MerkleTree.getEmptyHash(peakIdx); started = true; } if (started) { root = self.peaks[peakIdx] != bytes32(0) ? Hash.keccak(self.peaks[peakIdx], root) : Hash.keccak(root, MerkleTree.getEmptyHash(peakIdx)); } unchecked { ++peakIdx; } } return root; } /** * @dev Extend an existing MMR to a Merkle root of a padded list of length `paddingSize` using complement peaks. * @param self The MMR. * @param paddingDepth The depth of the padded Merkle tree. * @param mmrComplement Entries which contain peaks of a complementary MMR, where `mmrComplement[idx]` is either `bytes32(0x0)` or the * Merkle root of a tree of depth `idx`. Only the relevant indices are accessed. * @dev As an example, if `mmr` has peaks of depth 9 8 6 3, then `mmrComplement` has peaks of depth 3 4 5 7 * In this example, the peaks of `mmr` are Merkle roots of the first 2^9 leaves, then the next 2^8 leaves, and so on. * The peaks of `mmrComplement` are Merkle roots of the first 2^3 leaves after `mmr`, then the next 2^4 leaves, and so on. * @return root The Merkle root of the completion of `mmr`. */ function getComplementMerkleRoot(MMR memory self, uint256 paddingDepth, bytes32[] memory mmrComplement) internal pure returns (bytes32) { bytes32 root; bool started; for (uint256 peakIdx; peakIdx < paddingDepth;) { if (!started && self.peaks[peakIdx] != bytes32(0)) { root = mmrComplement[peakIdx]; started = true; } if (started) { root = self.peaks[peakIdx] != bytes32(0) ? Hash.keccak(self.peaks[peakIdx], root) : Hash.keccak(root, mmrComplement[peakIdx]); } unchecked { ++peakIdx; } } return root; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { MerkleMountainRange } from "./MerkleMountainRange.sol"; /// @title Padded Merkle Mountain Range /// @author Axiom /// @notice Library for Merkle Mountain Range data structure library PaddedMerkleMountainRange { using MerkleMountainRange for MerkleMountainRange.MMR; /// @dev Error returned if the leaf is too big error PmmrLeafIsTooBig(); /// @dev Error returned if the leaf is not empty error PmmrLeafIsNotEmpty(); /** * @notice A Padded Merkle mountain range is a data structure for efficiently storing a commitment * to a variable length list of hash values batched by a specific size. For a fixed `paddingSize` * which must be a power of two, a PMMR consists of a standard MMR of Merkle roots of batches of * `paddingSize` hashes and a `paddedLeaf`, which is a Merkle root of the 0-padded last partial batch of * hashes, where the number of hashes lies in `[0, paddingSize)`. * We define `paddedLeaf = bytes32(0x0)` if the last partial batch of hashes is empty. * @param completeLeaves The MMR of the complete leaves of the PMMR * @param paddedLeaf The Merkle root of the 0-padded last partial batch of hashes * @param size The number of hashes this PMMR is a commitment to. */ struct PMMR { MerkleMountainRange.MMR completeLeaves; bytes32 paddedLeaf; uint32 size; } /** * @notice Copies the PMMR from storage to memory * @param self The PMMR in storage * @return out The PMMR in memory */ function clone(PMMR storage self) internal view returns (PMMR memory out) { out.completeLeaves = self.completeLeaves.clone(); out.paddedLeaf = self.paddedLeaf; out.size = self.size; } /** * @notice Copies PMMR from memory to storage * @param self The PMMR in storage * @param sourcePMMR The PMMR in memory * @param peaksChanged Only copy newMMR.peaks[0 : peaksChanged] */ function persistFrom(PMMR storage self, PMMR memory sourcePMMR, uint256 peaksChanged) internal { self.completeLeaves.persistFrom(sourcePMMR.completeLeaves, peaksChanged); self.paddedLeaf = sourcePMMR.paddedLeaf; self.size = sourcePMMR.size; } /** * @notice Compute a commitment to the PMMR, defined by * keccak(paddedLeaf || completeLeaves.peaks[0] || ... || completeLeaves.peaks[completeLeaves.peaksLength - 1]) * @param self The PMMR * @return keccak the hash of the concatenation */ function commit(PMMR memory self) internal pure returns (bytes32) { bytes32[] memory peaks = new bytes32[](self.completeLeaves.peaksLength); for (uint256 i; i < self.completeLeaves.peaksLength;) { peaks[i] = self.completeLeaves.peaks[i]; unchecked { ++i; } } return keccak256(abi.encodePacked(self.paddedLeaf, peaks)); } /** * @notice Updates the first peak representing the padded batch leaf * @dev Warning: This method can overflow if `self.size + leafSize` exceeds `2**32 - 1`. * This cannot happen for realistic values of block numbers, which is not an issue * in our application. * @param self The PMMR * @param paddingSize The size of the padded batch * @param leaf The padded leaf update * @param leafSize The size of the padded leaf, defined as the number of non-zero hashes it contains * @return completePeaksChanged amount of peaks that have been changed */ function updatePaddedLeaf(PMMR memory self, uint32 paddingSize, bytes32 leaf, uint32 leafSize) internal pure returns (uint256 completePeaksChanged) { if (leafSize > paddingSize) { revert PmmrLeafIsTooBig(); } unchecked { self.size = self.size - self.size % paddingSize + leafSize; } // just updating the padded leaf that is always at index 0 if (leafSize < paddingSize) { self.paddedLeaf = leaf; return 0; } // If leaf is complete delete self.paddedLeaf; completePeaksChanged = self.completeLeaves.appendLeaf(leaf); } /** * @notice Append a sequence of complete leaves to the underlying list of the PMMR * @dev The padded leaf should be empty to be able to append complete leaves * @param self The PMMR * @param paddingSize The size of the padded batch * @param leaves The new elements to append * @return completePeaksChanged amount of peaks that have been changed * @dev Warning: To save gas, this method overwrites values of `leaves` with intermediate computations. * The input values of `leaves` should be considered invalidated after calling this method. */ function appendCompleteLeaves(PMMR memory self, uint32 paddingSize, bytes32[] memory leaves) internal pure returns (uint256 completePeaksChanged) { if (self.paddedLeaf != bytes32(0)) { revert PmmrLeafIsNotEmpty(); } self.size += uint32(leaves.length) * paddingSize; completePeaksChanged = self.completeLeaves.appendLeaves(leaves); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IAxiomV2HeaderVerifier { /** * @notice Stores witness data for checking MMRs * @param snapshotPmmrSize The `pmmrSize` as in `IAxiomV2State`. * @param proofMmrPeaks Peaks of the MMR, formatted so that `proofMmrPeaks[i]` is a Merkle * root of `2 ** i` claimed block hashes. * @param mmrComplementOrPeaks This has two different semantic meanings depending on the * value of `proofPmmrSize = number of blocks committed to by proofMmrPeaks`. * If `proofPmmrSize <= snapshotPmmrSize`: * -- `mmrComplementOrPeaks[:10]` form a complementary MMR to `proofMmrPeaks[:10]` * formatted so that `mmrComplementOrPeaks[idx]` is a Merkle root of `2 ** idx` hashes * which together with `witnessMmrPeaks` forms a padded leaf. * -- `mmrComplementOrPeaks[10]` is either `bytes32(0x0)` or a Merkle root of a padded leaf. * -- It is expected to be a Merkle root of a padded leaf exactly when * snapshotPmmrSize % BLOCK_BATCH_SIZE != 0 * -- The remaining elements are a list of Merkle roots of 1024 block hashes, to be * appended in increasing index order. * If `proofPmmrSize > snapshotPmmrSize`: * -- This is the MMR peaks committed to in the PMMR at `snapshotPmmrSize`, * formatted so that `mmrComplementOrPeaks[idx]` is a Merkle root of `2 ** idx` * block hashes. */ struct MmrWitness { uint32 snapshotPmmrSize; bytes32[] proofMmrPeaks; bytes32[] mmrComplementOrPeaks; } /// @notice Error returned if the AxiomV2Core address is 0. error AxiomCoreAddressIsZero(); /// @notice Error returned when the claimed blockhashMmr is not consistent with the source of truth error BlockhashMmrKeccakDoesNotMatchProof(); /// @notice Error returned when last block in the claimed MMR of the proof is not in the recent 256 /// block hash window. error MmrEndBlockNotRecent(); /// @notice Error returned when last block in the claimed MMR of the proof is not in the recent 256 /// block hash window. error BlockHashWitnessNotRecent(); /// @notice Error returned when the claimed MMR of the proof is not consistent with the source of truth error ClaimedMmrDoesNotMatchRecent(); /// @notice Error returned when the claimed MMR of the proof cannot be verified against a more recent /// blockhashPmmr. error NoMoreRecentBlockhashPmmr(); /// @notice Error returned if the proofMmrKeccak does not match witness. error ProofMmrKeccakDoesNotMatch(); /// @notice Verify the claimed `proofMmrKeccak` is validly read from the history /// of the source chain using witness data from `mmrWitness` /// @param proofMmrKeccak The Keccak hash of the claimed MMR of historic block hashes /// formatted so that hash `idx` is the Merkle root of `2 ** idx` block hashes /// @param mmrWitness Witness data for verification function verifyQueryHeaders(bytes32 proofMmrKeccak, MmrWitness calldata mmrWitness) external; /// @notice Return the `chainId` of the source chain /// @return chainId The `chainId` function getSourceChainId() external view returns (uint64); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /// @title Hash /// @notice Gas-optimized library for computing packed Keccak hashes library Hash { /// @notice Compute the Keccak hash of the packed values `keccak(a || b)` /// Gas-optimized equivalent of `keccak256(abi.encodePacked(a, b))` function keccak(bytes32 a, bytes32 b) internal pure returns (bytes32 hash) { assembly { mstore(0x00, a) mstore(0x20, b) hash := keccak256(0x00, 0x40) } } }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@solmate-utils/=lib/solmate/src/utils/", "@create3-factory/=lib/create3-factory/src/", "@gnosis/hashi/=lib/hashi/packages/evm/contracts/", "create3-factory/=lib/create3-factory/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/", "weird-erc20/=lib/solmate/lib/weird-erc20/src/" ], "optimizer": { "enabled": true, "runs": 100000, "details": { "constantOptimizer": false, "yul": false } }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": false, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"uint64","name":"sourceChainId","type":"uint64"},{"internalType":"address","name":"_l1AxiomV2CoreAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AxiomCoreAddressIsZero","type":"error"},{"inputs":[],"name":"BlockHashWitnessNotRecent","type":"error"},{"inputs":[],"name":"BlockhashMmrKeccakDoesNotMatchProof","type":"error"},{"inputs":[],"name":"BroadcasterAddressIsZero","type":"error"},{"inputs":[],"name":"ClaimedMmrDoesNotMatchRecent","type":"error"},{"inputs":[],"name":"InvalidBlockHeader","type":"error"},{"inputs":[],"name":"InvalidChainId","type":"error"},{"inputs":[],"name":"InvalidSourceChainId","type":"error"},{"inputs":[],"name":"L1AxiomV2CoreAddressIsZero","type":"error"},{"inputs":[],"name":"MmrEndBlockNotRecent","type":"error"},{"inputs":[],"name":"NoMoreRecentBlockhashPmmr","type":"error"},{"inputs":[],"name":"NotBroadcaster","type":"error"},{"inputs":[],"name":"PmmrLeafIsNotEmpty","type":"error"},{"inputs":[],"name":"PmmrLeafIsTooBig","type":"error"},{"inputs":[],"name":"ProofMmrKeccakDoesNotMatch","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"blockNumber","type":"uint32"},{"indexed":false,"internalType":"bytes32","name":"blockhash","type":"bytes32"}],"name":"L1BlockhashStored","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"pmmrSize","type":"uint32"},{"indexed":true,"internalType":"bytes32","name":"pmmrSnapshot","type":"bytes32"}],"name":"UpdatedPmmr","type":"event"},{"inputs":[],"name":"L1_BROADCASTER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PMMR_SNAPSHOTS_SLOT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSourceChainId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l1AxiomV2CoreAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"l1Blockhashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"pmmrSnapshots","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"blockNumber","type":"uint32"}],"name":"storeL1Blockhash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storeLatestL1Blockhash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"updateLatestPmmr","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"blockNumber","type":"uint32"},{"internalType":"bytes","name":"blockHeader","type":"bytes"},{"internalType":"bytes[]","name":"accountProof","type":"bytes[]"},{"internalType":"bytes[]","name":"storageProof","type":"bytes[]"},{"internalType":"uint32","name":"pmmrSize","type":"uint32"}],"name":"updateLatestPmmrFromBlockhash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"proofMmrKeccak","type":"bytes32"},{"components":[{"internalType":"uint32","name":"snapshotPmmrSize","type":"uint32"},{"internalType":"bytes32[]","name":"proofMmrPeaks","type":"bytes32[]"},{"internalType":"bytes32[]","name":"mmrComplementOrPeaks","type":"bytes32[]"}],"internalType":"struct IAxiomV2HeaderVerifier.MmrWitness","name":"mmrWitness","type":"tuple"}],"name":"verifyQueryHeaders","outputs":[],"stateMutability":"view","type":"function"}]
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100d45760003560e01c8063a4f5aec011610081578063edc55b941161005b578063edc55b9414610225578063f9dd4e2614610238578063fe428ed31461024b57600080fd5b8063a4f5aec0146101b7578063e1d2d353146101eb578063e6bba6691461021257600080fd5b806351d46aff116100b257806351d46aff146101645780635ed366131461018457806372738888146101a457600080fd5b806301ffc9a7146100d9578063112b9d9a14610144578063141e08be1461014e575b600080fd5b61012e6100e7366004612523565b7fffffffff00000000000000000000000000000000000000000000000000000000167ff9dd4e26000000000000000000000000000000000000000000000000000000001490565b60405161013b919061254e565b60405180910390f35b61014c610279565b005b61015761014a81565b60405161013b9190612562565b610157610172366004612587565b60016020526000908152604090205481565b610157610192366004612587565b60006020819052908152604090205481565b61014c6101b2366004612587565b6102df565b6101de7f00000000000000000000000069963768f8407de501029680de46945f838fc98b81565b60405161013b91906125cf565b6101de7f00000000000000000000000000000000000000000000000000000000deadbeef81565b61014c61022036600461267a565b610336565b61014c61023336600461277c565b610596565b61014c6102463660046127d4565b610949565b7f0000000000000000000000000000000000000000000000000000000000aa36a760405161013b9190612817565b60008061028461097b565b63ffffffff82166000818152600160205260409081902083905551929450909250907fb6f5acbb648aa6231c4c1e1f45d7fa132d1b6bded69c96eda86ea6b1b7906e8a906102d3908490612562565b60405180910390a25050565b60006102ea82610a8c565b63ffffffff83166000818152600160205260409081902083905551919250907fb6f5acbb648aa6231c4c1e1f45d7fa132d1b6bded69c96eda86ea6b1b7906e8a906102d3908490612562565b63ffffffff8816600090815260016020526040902054806103755761035a89610a8c565b63ffffffff8a16600090815260016020526040902081905590505b808888604051610386929190612844565b6040518091039020146103c5576040517f464db2f800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061040689898080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610beb92505050565b9050600061042d8260038151811061042057610420612851565b6020026020010151610bfe565b6104369061288a565b905060006104ac7f00000000000000000000000069963768f8407de501029680de46945f838fc98b60405160200161046e919061290d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526104a68a8c612af0565b84610cbb565b905060006104b982610beb565b905060006104d38260028151811061042057610420612851565b6104dc9061288a565b905060008761014a6040516020016104f5929190612b09565b6040516020818303038152906040528051906020012090506000610561826040516020016105239190612b24565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905261055b8c8e612af0565b85610cbb565b9050600061056e82610cd5565b6105779061288a565b90506105838a82610ce8565b5050505050505050505050505050505050565b60006105a56020830183612b39565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394506105e6925050506020840184612587565b9050816040516020016105f99190612c0a565b604051602081830303815290604052805190602001208414610647576040517ff7e4c42900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151600090815b81811015610691576000801b85828151811061066c5761066c612851565b602002602001015114610689576106866001821b84612c45565b92505b60010161064e565b508263ffffffff168263ffffffff161161090f576040805160608101909152600090806106c987600a6106c48188612c65565b610d2c565b8152600060208201526040016106e161040086612ca7565b6106eb9086612cce565b63ffffffff169052905060006107038682600a610d2c565b905063ffffffff841661071861040087612ca7565b6107229087612cce565b63ffffffff161061087857600061073b61040086612ca7565b63ffffffff1611156107a9576000610796600a61075b60408b018b612b39565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250879493925050610d8d9050565b90506107a6836104008381610e85565b50505b63ffffffff84166107bc61040087612ca7565b6107c69087612cce565b63ffffffff1611156108305761082e6104006107e560408a018a612b39565b6107f391600b908290612cee565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250879493925050610f4d9050565b505b61087261040061084360408a018a612b39565b600a81811061085457610854612851565b90506020020135610400886108699190612ca7565b85929190610e85565b506108ab565b600061088c600a61075b60408b018b612b39565b90506108a86104008261089f828a612ca7565b86929190610e85565b50505b60006108b683610fc5565b63ffffffff87166000908152602081905260409020549091508114610907576040517feff8f8f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050610941565b6040517f8d034e6f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b6040517facfff30800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638381f58a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109dd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a019190612d3c565b915073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166309bd5a606040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a869190612d68565b90509091565b600073420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff16638381f58a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610aed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b119190612d3c565b67ffffffffffffffff168263ffffffff1614610b62576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990612de6565b60405180910390fd5b73420000000000000000000000000000000000001573ffffffffffffffffffffffffffffffffffffffff166309bd5a606040518163ffffffff1660e01b8152600401602060405180830381865afa158015610bc1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be59190612d68565b92915050565b6060610be5610bf983611097565b611104565b60606000806000610c0e856112ba565b919450925090506000816001811115610c2957610c29612df6565b14610c60576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990612e7f565b610c6a8284612e8f565b855114610ca3576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990612efc565b610cb285602001518484611757565b95945050505050565b60606000610cc8856117eb565b9050610cb281858561181b565b6060610be5610ce383611097565b610bfe565b63ffffffff8216600081815260208190526040808220849055518392917fcc60f3dfe43ea98df0bbc7a811480a1dd011972076bf69b351139fe5f7fd67fc91a35050565b610d346124a7565b6020810182905260005b82811015610d85578481850181518110610d5a57610d5a612851565b602002602001015182600001518260208110610d7857610d78612851565b6020020152600101610d3e565b509392505050565b6000806000805b85811015610e785781158015610dc3575086516000908260208110610dbb57610dbb612851565b602002015114155b15610de957848181518110610dda57610dda612851565b60200260200101519250600191505b8115610e705786516000908260208110610e0557610e05612851565b602002015103610e4157610e3c83868381518110610e2557610e25612851565b602002602001015160009182526020526040902090565b610e6d565b8651610e6d908260208110610e5857610e58612851565b60200201518460009182526020526040902090565b92505b600101610d94565b50909150505b9392505050565b60008363ffffffff168263ffffffff161115610ecd576040517f6f4617f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818463ffffffff16866040015163ffffffff1681610eed57610eed612c78565b0686604001510301856040019063ffffffff16908163ffffffff16815250508363ffffffff168263ffffffff161015610f2f5750602084018290526000610f45565b600060208601528451610f429084611e1a565b90505b949350505050565b602083015160009015610f8c576040517f27e3c14100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828251610f999190612f0c565b84604001818151610faa9190612c45565b63ffffffff9081169091528551610f459250908490611ecf16565b60008082600001516020015167ffffffffffffffff811115610fe957610fe9612922565b604051908082528060200260200182016040528015611012578160200160208202803683370190505b50905060005b83516020015181101561106357835151816020811061103957611039612851565b602002015182828151811061105057611050612851565b6020908102919091010152600101611018565b5060208084015160405161107992849101612f2f565b60405160208183030381529060405280519060200120915050919050565b604080518082019091526000808252602082015260008251116110e6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990612fce565b50604080518082019091528151815260209182019181019190915290565b60606000806000611114856112ba565b91945092509050600181600181111561112f5761112f612df6565b14611166576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613038565b84516111728385612e8f565b146111a9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b59906130a2565b604080516020808252610420820190925290816020015b60408051808201909152600080825260208201528152602001906001900390816111c05790505093506000835b86518110156112ae576000806112336040518060400160405280858c600001516112179190612c65565b8152602001858c6020015161122c9190612e8f565b90526112ba565b50915091506040518060400160405280838361124f9190612e8f565b8152602001848b602001516112649190612e8f565b81525088858151811061127957611279612851565b602090810291909101015261128f600185612e8f565b935061129b8183612e8f565b6112a59084612e8f565b925050506111ed565b50845250919392505050565b6000806000808460000151116112fc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990612fce565b6020840151805160001a607f8111611321576000600160009450945094505050611750565b60b78111611437576000611336608083612c65565b905080876000015111611375576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613132565b6001838101517fff000000000000000000000000000000000000000000000000000000000000001690821415806113ee57507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b611424576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b59906131c2565b5060019550935060009250611750915050565b60bf811161159557600061144c60b783612c65565b90508087600001511161148b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613252565b60018301517fff000000000000000000000000000000000000000000000000000000000000001660008190036114ed576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b59906132e2565b600184015160088302610100031c60378111611535576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613372565b61153f8184612e8f565b895111611578576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613402565b611583836001612e8f565b97509550600094506117509350505050565b60f781116115fa5760006115aa60c083612c65565b9050808760000151116115e9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613492565b600195509350849250611750915050565b600061160760f783612c65565b905080876000015111611646576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613522565b60018301517fff000000000000000000000000000000000000000000000000000000000000001660008190036116a8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b59906135b2565b600184015160088302610100031c603781116116f0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613642565b6116fa8184612e8f565b895111611733576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b59906136d2565b61173e836001612e8f565b97509550600194506117509350505050565b9193909250565b60608167ffffffffffffffff81111561177257611772612922565b6040519080825280601f01601f19166020018201604052801561179c576020820181803683370190505b5090508115610e7e5760006117b18486612e8f565b90506020820160005b848110156117d25782810151828201526020016117ba565b848111156117e1576000858301525b5050509392505050565b606081805190602001206040516020016118059190612b24565b6040516020818303038152906040529050919050565b60606000845111611858576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613719565b6000611863846120c7565b90506000611870866121b3565b90506000846040516020016118859190612b24565b60405160208183030381529060405290506000805b8451811015611de75760008582815181106118b7576118b7612851565b6020026020010151905084518311156118fc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613783565b82600003611981578051805160209182012060405161194692611920929101612b24565b604051602081830303815290604052858051602091820120825192909101919091201490565b61197c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b59906137c7565b611a28565b8051516020116119dd57805180516020918201206040516119a792611920929101612b24565b61197c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613831565b805184516020808701919091208251919092012014611a28576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b599061389b565b611a3460106001612e8f565b81602001515103611b575784518303611aef57611a61816020015160108151811061042057610420612851565b96506000875111611a9e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613905565b60018651611aac9190612c65565b8214611ae4576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b599061396f565b505050505050610e7e565b6000858481518110611b0357611b03612851565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110611b2e57611b2e612851565b60200260200101519050611b4181612216565b9550611b4e600186612e8f565b94505050611dd4565b600281602001515103611da2576000611b6f8261223b565b9050600081600081518110611b8657611b86612851565b016020015160f81c90506000611b9d60028361397f565b611ba8906002613990565b90506000611bb9848360ff1661225f565b90506000611bc78a8961225f565b90506000611bd58383612295565b905080835114611c11576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613a07565b60ff851660021480611c26575060ff85166003145b15611d135780825114611c65576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613a71565b611c7f876020015160018151811061042057610420612851565b9c5060008d5111611cbc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613adb565b60018c51611cca9190612c65565b8814611d02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613b45565b505050505050505050505050610e7e565b60ff85161580611d26575060ff85166001145b15611d6557611d528760200151600181518110611d4557611d45612851565b6020026020010151612216565b9950611d5e818a612e8f565b9850611d97565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613baf565b505050505050611dd4565b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613c19565b5080611ddf81613c29565b91505061189a565b506040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613cbb565b6020820151600090829082905b8082108015611e4f575085516000908360208110611e4757611e47612851565b602002015114155b15611e98578551611e6b908360208110610e5857610e58612851565b86519093508260208110611e8157611e81612851565b602002016000801916815250816001019150611e27565b855183908360208110611ead57611ead612851565b6020020152808210611ec3576001820160208701525b50600101949350505050565b80516000908180808080805b86156120a55789516000908660208110611ef757611ef7612851565b602002015114611f08576001611f0b565b60005b60ff1695505050848401600181901c907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1660005b8181101561202b5786600103611fb85780600003611f77578a518660208110611f6b57611f6b612851565b60200201519450611f97565b896001820381518110611f8c57611f8c612851565b602002602001015194505b898181518110611fa957611fa9612851565b60200260200101519350611ff4565b898181518110611fca57611fca612851565b60200260200101519450898160010181518110611fe957611fe9612851565b602002602001015193505b60008581526020859052604090208a600183901c8151811061201857612018612851565b6020908102919091010152600201611f40565b508587600116146120735788600188038151811061204b5761204b612851565b60200260200101518a60000151866020811061206957612069612851565b6020020152612097565b85600103612097578951600090866020811061209157612091612851565b60200201525b819650846001019450611edb565b89602001518511156120b95760208a018590525b509298975050505050505050565b80516060908067ffffffffffffffff8111156120e5576120e5612922565b60405190808252806020026020018201604052801561212a57816020015b60408051808201909152606080825260208201528152602001906001900390816121035790505b50915060005b818110156121ac57604051806040016040528085838151811061215557612155612851565b6020026020010151815260200161218486848151811061217757612177612851565b6020026020010151610beb565b81525083828151811061219957612199612851565b6020908102919091010152600101612130565b5050919050565b606080604051905082518060011b603f8101601f19168301604052808352602085016020840160005b8481101561220a578060011b82018184015160001a8060041c8253600f8116600183015350506001016121dc565b50939695505050505050565b606060208260000151106122325761222d82610bfe565b610be5565b610be582612349565b6060610be561225a836020015160008151811061042057610420612851565b6121b3565b60608251821061227e5750604080516020810190915260008152610be5565b610e7e83838486516122909190612c65565b61235f565b60008082518451106122a85782516122ab565b83515b90505b808210801561233257508282815181106122ca576122ca612851565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191684838151811061230957612309612851565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b15612342578160010191506122ae565b5092915050565b6060610be5826020015160008460000151611757565b60608182601f01101561239e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613cff565b8282840110156123da576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613cff565b81830184511015612417576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b5990613d43565b606082158015612436576040519150600082526020820160405261249e565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561246f578051835260209283019201612457565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b60405180604001604052806124ba6124c7565b8152602001600081525090565b6040518061040001604052806020906020820280368337509192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000081165b811461251557600080fd5b50565b8035610be5816124e6565b60006020828403121561253857612538600080fd5b6000610f458484612518565b8015155b82525050565b60208101610be58284612544565b80612548565b60208101610be5828461255c565b63ffffffff811661250a565b8035610be581612570565b60006020828403121561259c5761259c600080fd5b6000610f45848461257c565b600073ffffffffffffffffffffffffffffffffffffffff8216610be5565b612548816125a8565b60208101610be582846125c6565b60008083601f8401126125f2576125f2600080fd5b50813567ffffffffffffffff81111561260d5761260d600080fd5b60208301915083600182028301111561262857612628600080fd5b9250929050565b60008083601f84011261264457612644600080fd5b50813567ffffffffffffffff81111561265f5761265f600080fd5b60208301915083602082028301111561262857612628600080fd5b60008060008060008060008060a0898b03121561269957612699600080fd5b60006126a58b8b61257c565b985050602089013567ffffffffffffffff8111156126c5576126c5600080fd5b6126d18b828c016125dd565b9750975050604089013567ffffffffffffffff8111156126f3576126f3600080fd5b6126ff8b828c0161262f565b9550955050606089013567ffffffffffffffff81111561272157612721600080fd5b61272d8b828c0161262f565b935093505060806127408b828c0161257c565b9150509295985092959890939650565b8061250a565b8035610be581612750565b60006060828403121561277657612776600080fd5b50919050565b6000806040838503121561279257612792600080fd5b600061279e8585612756565b925050602083013567ffffffffffffffff8111156127be576127be600080fd5b6127ca85828601612761565b9150509250929050565b600080604083850312156127ea576127ea600080fd5b60006127f6858561257c565b92505060206127ca85828601612756565b67ffffffffffffffff8116612548565b60208101610be58284612807565b82818337506000910152565b600061283e838584612825565b50500190565b6000610f45828486612831565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000610be5825190565b6000612894825190565b602083016128a181612880565b925060208210156121ac576128db7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff836020036008021b90565b9092169392505050565b6000610be58260601b90565b6000610be5826128e5565b612548612908826125a8565b6128f1565b600061291982846128fc565b50601401919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116810181811067ffffffffffffffff8211171561299557612995612922565b6040525050565b60006129a760405190565b90506129b38282612951565b919050565b600067ffffffffffffffff8211156129d2576129d2612922565b5060209081020190565b600067ffffffffffffffff8211156129f6576129f6612922565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011660200192915050565b6000612a38612a33846129dc565b61299c565b905082815260208101848484011115612a5357612a53600080fd5b610d85848285612825565b600082601f830112612a7257612a72600080fd5b8135610f45848260208601612a25565b6000612a90612a33846129b8565b83815290506020808201908402830185811115612aaf57612aaf600080fd5b835b818110156117e157803567ffffffffffffffff811115612ad357612ad3600080fd5b808601612ae08982612a5e565b8552505060209283019201612ab1565b6000610e7e368484612a82565b63ffffffff8116612548565b60408101612b178285612afd565b610e7e602083018461255c565b6000612b30828461255c565b50602001919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe136859003018112612b7257612b72600080fd5b80840192508235915067ffffffffffffffff821115612b9357612b93600080fd5b60209283019282023603831315612bac57612bac600080fd5b509250929050565b6000612bc0838361255c565b505060200190565b6000612bd2825190565b602083018060005b83811015612bff578151612bee8882612bb4565b975060208301925050600101612bda565b509495945050505050565b6000610e7e8284612bc8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b63ffffffff918216919081169082820190811115610be557610be5612c16565b81810381811115610be557610be5612c16565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600063ffffffff8216915063ffffffff83165b925082612cc957612cc9612c78565b500690565b63ffffffff918216919081169082820390811115610be557610be5612c16565b60008085851115612d0157612d01600080fd5b83861115612d1157612d11600080fd5b5050602083020193919092039150565b67ffffffffffffffff811661250a565b8051610be581612d21565b600060208284031215612d5157612d51600080fd5b6000610f458484612d31565b8051610be581612750565b600060208284031215612d7d57612d7d600080fd5b6000610f458484612d5d565b603481526000602082017f426c6f636b6861736852656d6f746548656164657256657269666965723a206281527f6c6f636b206e756d626572206d69736d61746368000000000000000000000000602082015291505b5060400190565b60208082528101610be581612d89565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b603981526000602082017f524c505265616465723a206465636f646564206974656d207479706520666f7281527f206279746573206973206e6f7420612064617461206974656d0000000000000060208201529150612ddf565b60208082528101610be581612e25565b80820180821115610be557610be5612c16565b603481526000602082017f524c505265616465723a2062797465732076616c756520636f6e7461696e732081527f616e20696e76616c69642072656d61696e64657200000000000000000000000060208201529150612ddf565b60208082528101610be581612ea2565b63ffffffff91821691908116908282029081169081811461234257612342612c16565b6000612f3b828561255c565b602082019150610f458284612bc8565b604a81526000602082017f524c505265616465723a206c656e677468206f6620616e20524c50206974656d81527f206d7573742062652067726561746572207468616e207a65726f20746f20626560208201527f206465636f6461626c6500000000000000000000000000000000000000000000604082015291505b5060600190565b60208082528101610be581612f4b565b603881526000602082017f524c505265616465723a206465636f646564206974656d207479706520666f7281527f206c697374206973206e6f742061206c697374206974656d000000000000000060208201529150612ddf565b60208082528101610be581612fde565b603281526000602082017f524c505265616465723a206c697374206974656d2068617320616e20696e766181527f6c696420646174612072656d61696e646572000000000000000000000000000060208201529150612ddf565b60208082528101610be581613048565b604e81526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f742062652067726561746572207468616e20737472696e67206c656e6774682060208201527f2873686f727420737472696e672900000000000000000000000000000000000060408201529150612fc7565b60208082528101610be5816130b2565b604d81526000602082017f524c505265616465723a20696e76616c6964207072656669782c2073696e676c81527f652062797465203c203078383020617265206e6f74207072656669786564202860208201527f73686f727420737472696e67290000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be581613142565b605181526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60208201527f67746820286c6f6e6720737472696e672900000000000000000000000000000060408201529150612fc7565b60208082528101610be5816131d2565b604a81526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60208201527f6e6720737472696e67290000000000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be581613262565b604881526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f742062652067726561746572207468616e20353520627974657320286c6f6e6760208201527f20737472696e672900000000000000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be5816132f2565b604c81526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f742062652067726561746572207468616e20746f74616c206c656e677468202860208201527f6c6f6e6720737472696e6729000000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be581613382565b604a81526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f742062652067726561746572207468616e206c697374206c656e67746820287360208201527f686f7274206c697374290000000000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be581613412565b604d81526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460208201527f6820286c6f6e67206c697374290000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be5816134a2565b604881526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60208201527f6e67206c6973742900000000000000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be581613532565b604681526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f742062652067726561746572207468616e20353520627974657320286c6f6e6760208201527f206c69737429000000000000000000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be5816135c2565b604a81526000602082017f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757381527f742062652067726561746572207468616e20746f74616c206c656e677468202860208201527f6c6f6e67206c697374290000000000000000000000000000000000000000000060408201529150612fc7565b60208082528101610be581613652565b601581526000602082017f4d65726b6c65547269653a20656d707479206b65790000000000000000000000815291505b5060200190565b60208082528101610be5816136e2565b602e81526000602082017f4d65726b6c65547269653a206b657920696e646578206578636565647320746f81527f74616c206b6579206c656e67746800000000000000000000000000000000000060208201529150612ddf565b60208082528101610be581613729565b601d81526000602082017f4d65726b6c65547269653a20696e76616c696420726f6f74206861736800000081529150613712565b60208082528101610be581613793565b602781526000602082017f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e81527f616c20686173680000000000000000000000000000000000000000000000000060208201529150612ddf565b60208082528101610be5816137d7565b602681526000602082017f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6481527f652068617368000000000000000000000000000000000000000000000000000060208201529150612ddf565b60208082528101610be581613841565b603b81526000602082017f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626581527f2067726561746572207468616e207a65726f20286272616e636829000000000060208201529150612ddf565b60208082528101610be5816138ab565b603a81526000602082017f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c81527f617374206e6f646520696e2070726f6f6620286272616e63682900000000000060208201529150612ddf565b60208082528101610be581613915565b600060ff8216915060ff8316612cba565b60ff918216919081169082820390811115610be557610be5612c16565b603a81526000602082017f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742081527f736861726520616c6c206e6962626c65732077697468206b657900000000000060208201529150612ddf565b60208082528101610be5816139ad565b603d81526000602082017f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206281527f65206964656e746963616c20746f20706174682072656d61696e64657200000060208201529150612ddf565b60208082528101610be581613a17565b603981526000602082017f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626581527f2067726561746572207468616e207a65726f20286c656166290000000000000060208201529150612ddf565b60208082528101610be581613a81565b603881526000602082017f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c81527f617374206e6f646520696e2070726f6f6620286c65616629000000000000000060208201529150612ddf565b60208082528101610be581613aeb565b603281526000602082017f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746881527f20616e20756e6b6e6f776e20707265666978000000000000000000000000000060208201529150612ddf565b60208082528101610be581613b55565b602881526000602082017f4d65726b6c65547269653a20726563656976656420616e20756e70617273656181527f626c65206e6f646500000000000000000000000000000000000000000000000060208201529150612ddf565b60208082528101610be581613bbf565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613c5a57613c5a612c16565b5060010190565b602581526000602082017f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6581527f6d656e747300000000000000000000000000000000000000000000000000000060208201529150612ddf565b60208082528101610be581613c61565b600e81526000602082017f736c6963655f6f766572666c6f7700000000000000000000000000000000000081529150613712565b60208082528101610be581613ccb565b601181526000602082017f736c6963655f6f75744f66426f756e647300000000000000000000000000000081529150613712565b60208082528101610be581613d0f56fea2646970667358221220f8674bfe5c6ef24b1830ddbdbd6eecd02527efbf9f007472b2fe16190f745de564736f6c63430008130033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.