Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
AxiomV2HeaderVerifier
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 { IAxiomV2HeaderVerifier } from "../interfaces/query/IAxiomV2HeaderVerifier.sol"; import { IAxiomV2State } from "../interfaces/core/IAxiomV2State.sol"; import { MerkleMountainRange } from "../libraries/MerkleMountainRange.sol"; import { PaddedMerkleMountainRange } from "../libraries/PaddedMerkleMountainRange.sol"; import { BLOCK_BATCH_DEPTH, BLOCK_BATCH_SIZE } from "../libraries/configuration/AxiomV2Configuration.sol"; contract AxiomV2HeaderVerifier is IAxiomV2HeaderVerifier { using PaddedMerkleMountainRange for PaddedMerkleMountainRange.PMMR; using MerkleMountainRange for MerkleMountainRange.MMR; address public immutable axiomCoreAddress; uint64 internal immutable _CHAIN_ID; /// @dev Initialize the contract. /// @param chainId The chain ID of the source chain this verifies headers from. /// @param _axiomCoreAddress The address of the AxiomV2Core contract. constructor(uint64 chainId, address _axiomCoreAddress) { _CHAIN_ID = chainId; if (_axiomCoreAddress == address(0)) { revert AxiomCoreAddressIsZero(); } axiomCoreAddress = _axiomCoreAddress; } /// @dev This verifier handles the case of the same source and target chain. For the purpose /// of this discussion, we call recent blocks to be blocks in [block.number - 256, block.number) /// /// We make the assumption that AxiomV2Core guarantees that `blockhashPmmr` is a commitment /// to block hashes up to a recent block at all times. /// /// We consider the state of AxiomV2Core at three block times: /// -- Proof time: Time of query submission / proof initiation /// -- Submission time: Time of verification tx submission /// -- Execution time: Time of verification tx execution /// /// 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. /// -- If `queryBlockNum <= currentBlockNum`, then we check that `blockhashMmrKeccak` is committed /// to in `blockhashPmmr` using the commitment to `blockhashPmmr` in `pmmrSnapshots`. /// -- Otherwise, at transaction submission time, if `blockhashPmmr` is more recent than /// `queryBlockNum`, then `queryBlockNum` is committed to in `blockhashPmmr` and /// we can submit witness data allowing us to check that commitment as in the previous case. /// -- Otherwise, at transaction submission time, `queryBlockNum` must be recent (as otherwise /// `blockhashPmmr` is recent and thus more recent). At transaction execution time, if: /// -- `queryBlockNum` is still recent, then at least one of the following must hold, and we can use /// recent block hashes to verify: /// -- `pmmrSnapshot` is still recent /// -- `queryBlockNum <= blockhashPmmr.size` /// -- If `queryBlockNum` is no longer recent, the transaction will fail. /// In this case, we should resubmit with the new recent `blockhashPmmr` at time of transaction execution. 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 != IAxiomV2State(axiomCoreAddress).pmmrSnapshots(snapshotPmmrSize)) { revert BlockhashMmrKeccakDoesNotMatchProof(); } } else { // We check in order of preference that: // * If `mmrWitness.snapshotPmmrSize >= block.number - 256`, we can check the PMMR committed to // by `pmmrSnapshots[mmrWitness.snapshotPmmrSize]` can be extended to `proofMmrPeaks` by blockhashes // accessible in EVM. This happens by: // -- Decommit the PMMR in `pmmrSnapshots[mmrWitness.snapshotPmmrSize]` // -- Decommit the padded leaf in the decommitted PMMR. // -- Extend the padded leaf with `blockhash` calls // -- If necessary, extend the MMR and padding with `blockhash` calls // // * If `proofMmrSize >= block.number - 256` and `proofMmrSize <= blockhashPmmr.size`, // we can check that `proofMmrPeaks` can be extended to `blockhashPmmr` by blockhashes accessible in EVM via: // -- Extend the MMR with `blockhash` calls // -- Form the padded leaf and check against `blockhashPmmr` if (proofMmrSize > block.number) { revert MmrEndBlockNotRecent(); } // if both mmrWitness.snapshotPmmrSize >= block.number - 256 and // proofMmrSize >= block.number - 256 and `proofMmrSize <= blockhashPmmr.size` // // then we have two options for which way to validate, which require calling `blockhash` // * case 1: `proofMmrSize - snapshotPmmrSize` times // * case 2: `blockhashPmmr.size - proofMmrSize` times // // we choose the smaller one uint32 corePmmrSize = IAxiomV2State(axiomCoreAddress).blockhashPmmrSize(); if ( snapshotPmmrSize >= block.number - 256 && (proofMmrSize > corePmmrSize || proofMmrSize - snapshotPmmrSize <= corePmmrSize - proofMmrSize) ) { // Decommit the PMMR in `pmmrSnapshots[mmrWitness.snapshotPmmrSize]` PaddedMerkleMountainRange.PMMR memory snapshotPmmr = PaddedMerkleMountainRange.PMMR({ paddedLeaf: bytes32(0), completeLeaves: MerkleMountainRange.fromPeaks( mmrWitness.mmrComplementOrPeaks, BLOCK_BATCH_DEPTH, mmrWitness.mmrComplementOrPeaks.length - BLOCK_BATCH_DEPTH ), size: snapshotPmmrSize - (snapshotPmmrSize % BLOCK_BATCH_SIZE) }); bytes32 snapshotLeaf = MerkleMountainRange.fromPeaks( mmrWitness.mmrComplementOrPeaks, 0, BLOCK_BATCH_DEPTH ).getZeroPaddedMerkleRoot(BLOCK_BATCH_DEPTH); snapshotPmmr.updatePaddedLeaf(BLOCK_BATCH_SIZE, snapshotLeaf, snapshotPmmrSize % BLOCK_BATCH_SIZE); bytes32 snapshotPmmrKeccak = snapshotPmmr.commit(); if (snapshotPmmrKeccak != IAxiomV2State(axiomCoreAddress).pmmrSnapshots(snapshotPmmrSize)) { revert BlockhashMmrKeccakDoesNotMatchProof(); } // check appending to the committed MMR with recent blocks will yield the claimed MMR uint256 appendRemaining; unchecked { // in this branch, proofMmrSize > snapshotPmmrSize appendRemaining = proofMmrSize - snapshotPmmrSize; } bytes32[] memory append = new bytes32[](appendRemaining); for (uint256 idx; idx < appendRemaining;) { unchecked { append[idx] = blockhash(snapshotPmmrSize + idx); } unchecked { ++idx; } } MerkleMountainRange.MMR memory snapshotMmr = MerkleMountainRange.fromPeaks(mmrWitness.mmrComplementOrPeaks); snapshotMmr.appendLeaves(append); uint256 snapshotMmrPeaksLength = snapshotMmr.peaksLength; for (uint256 idx; idx < snapshotMmrPeaksLength;) { if (snapshotMmr.peaks[idx] != peaks[idx]) { revert ClaimedMmrDoesNotMatchRecent(); } unchecked { ++idx; } } } else if (proofMmrSize >= block.number - 256) { if (proofMmrSize > corePmmrSize) { revert NoMoreRecentBlockhashPmmr(); } uint256 appendRemaining; unchecked { // in this branch, corePmmrSize >= proofMmrSize appendRemaining = corePmmrSize - proofMmrSize; } bytes32[] memory append = new bytes32[](appendRemaining); for (uint256 idx; idx < appendRemaining;) { unchecked { append[idx] = blockhash(proofMmrSize + idx); } unchecked { ++idx; } } MerkleMountainRange.MMR memory proofMmr = MerkleMountainRange.fromPeaks(peaks); proofMmr.appendLeaves(append); PaddedMerkleMountainRange.PMMR memory completePmmr = PaddedMerkleMountainRange.PMMR({ paddedLeaf: proofMmr.getZeroPaddedMerkleRoot(BLOCK_BATCH_DEPTH), completeLeaves: proofMmr.getCompleteLeaves(BLOCK_BATCH_DEPTH), size: corePmmrSize }); bytes32 completePmmrKeccak = completePmmr.commit(); if (completePmmrKeccak != IAxiomV2State(axiomCoreAddress).pmmrSnapshots(corePmmrSize)) { revert BlockhashMmrKeccakDoesNotMatchProof(); } } else { revert BlockHashWitnessNotRecent(); } } } /// @inheritdoc IAxiomV2HeaderVerifier function getSourceChainId() external view returns (uint64) { return _CHAIN_ID; } /// @notice Implements ERC-165 interface check function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IAxiomV2HeaderVerifier).interfaceId; } }
// 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; 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 { 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; // 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; 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; /// @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": [ "@create3-factory/=lib/create3-factory/src/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@solmate-utils/=lib/solmate/src/utils/", "create3-factory/=lib/create3-factory/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "nitro-contracts/=lib/nitro-contracts/src/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "solmate/=lib/solmate/src/", "weird-erc20/=lib/solmate/lib/weird-erc20/src/", "lib/create3-factory:ds-test/=lib/create3-factory/lib/forge-std/lib/ds-test/src/", "lib/create3-factory:forge-std/=lib/create3-factory/lib/forge-std/src/", "lib/create3-factory:solmate/=lib/create3-factory/lib/solmate/src/", "lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/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", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"uint64","name":"chainId","type":"uint64"},{"internalType":"address","name":"_axiomCoreAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AxiomCoreAddressIsZero","type":"error"},{"inputs":[],"name":"BlockHashWitnessNotRecent","type":"error"},{"inputs":[],"name":"BlockhashMmrKeccakDoesNotMatchProof","type":"error"},{"inputs":[],"name":"ClaimedMmrDoesNotMatchRecent","type":"error"},{"inputs":[],"name":"InvalidEmptyHashDepth","type":"error"},{"inputs":[],"name":"MmrEndBlockNotRecent","type":"error"},{"inputs":[],"name":"NoMoreRecentBlockhashPmmr","type":"error"},{"inputs":[],"name":"PmmrLeafIsNotEmpty","type":"error"},{"inputs":[],"name":"PmmrLeafIsTooBig","type":"error"},{"inputs":[],"name":"ProofMmrKeccakDoesNotMatch","type":"error"},{"inputs":[],"name":"axiomCoreAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSourceChainId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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"}]
Contract Creation Code
60c06040523480156200001157600080fd5b5060405162001c3838038062001c38833981016040819052620000349162000115565b67ffffffffffffffff821660a05273ffffffffffffffffffffffffffffffffffffffff811662000090576040517ff0f7b81f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff166080525062000158565b67ffffffffffffffff81165b8114620000c857600080fd5b50565b8051620000d881620000b0565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff8216620000d8565b620000bc81620000de565b8051620000d881620000fd565b600080604083850312156200012d576200012d600080fd5b60006200013b8585620000cb565b92505060206200014e8582860162000108565b9150509250929050565b60805160a051611a9f62000199600039600061010701526000818160c1015281816104930152818161058d015281816107c60152610b6d0152611a9f6000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806301ffc9a714610051578063cddc3b93146100bc578063edc55b94146100f0578063fe428ed314610105575b600080fd5b6100a661005f366004611626565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1387d547000000000000000000000000000000000000000000000000000000001490565b6040516100b39190611651565b60405180910390f35b6100e37f000000000000000000000000000000000000000000000000000000000000000081565b6040516100b39190611686565b6101036100fe3660046116c0565b610133565b005b7f00000000000000000000000000000000000000000000000000000000000000006040516100b39190611728565b60006101426020830183611736565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394506101839250505060208401846117c8565b9050816040516020016101969190611845565b6040516020818303038152906040528051906020012084146101e4576040517ff7e4c42900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151600090815b8181101561022e576000801b85828151811061020957610209611858565b602002602001015114610226576102236001821b846118b6565b92505b6001016101eb565b508263ffffffff168263ffffffff16116105495760408051606081019091526000908061026687600a61026181886118d6565b610c61565b81526000602082015260400161027e61040086611918565b6102889086611936565b63ffffffff169052905060006102a08682600a610c61565b905063ffffffff84166102b561040087611918565b6102bf9087611936565b63ffffffff16106104155760006102d861040086611918565b63ffffffff161115610346576000610333600a6102f860408b018b611736565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250879493925050610cc29050565b9050610343836104008381610db8565b50505b63ffffffff841661035961040087611918565b6103639087611936565b63ffffffff1611156103cd576103cb61040061038260408a018a611736565b61039091600b908290611956565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250879493925050610e809050565b505b61040f6104006103e060408a018a611736565b600a8181106103f1576103f1611858565b90506020020135610400886104069190611918565b85929190610db8565b50610448565b6000610429600a6102f860408b018b611736565b90506104456104008261043c828a611918565b86929190610db8565b50505b600061045383610ef8565b6040517f5ed3661300000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690635ed36613906104c8908990600401611995565b602060405180830381865afa1580156104e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050991906119ae565b8114610541576040517feff8f8f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050610c59565b438263ffffffff161115610589576040517f299b0e4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663596ef8a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061a91906119da565b9050610628610100436118d6565b8463ffffffff161015801561067057508063ffffffff168363ffffffff16118061067057506106578382611936565b63ffffffff166106678585611936565b63ffffffff1611155b156109e657600060405180606001604052806106e48980604001906106959190611736565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250600a92508291506106d9905060408e018e611736565b6102619291506118d6565b8152600060208201526040016106fc61040088611918565b6107069088611936565b63ffffffff16905290506000610767600a61076161072760408c018c611736565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509250600a9150610c619050565b90610fca565b905061077a61040082610406828a611918565b50600061078683610ef8565b6040517f5ed3661300000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690635ed36613906107fb908a90600401611995565b602060405180830381865afa158015610818573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083c91906119ae565b8114610874576040517feff8f8f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8787031660008167ffffffffffffffff811115610898576108986119fb565b6040519080825280602002602001820160405280156108c1578160200160208202803683370190505b50905060005b828110156108fe57808a63ffffffff1601408282815181106108eb576108eb611858565b60209081029190910101526001016108c7565b50600061094861091160408e018e611736565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061108a92505050565b9050610954818361109f565b50602081015160005b818110156109d9578c818151811061097757610977611858565b60200260200101518360000151826020811061099557610995611858565b6020020151146109d1576040517fb21aeaed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60010161095d565b5050505050505050610c57565b6109f2610100436118d6565b8363ffffffff1610610c25578063ffffffff168363ffffffff161115610a44576040517f8d034e6f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8382031660008167ffffffffffffffff811115610a6857610a686119fb565b604051908082528060200260200182016040528015610a91578160200160208202803683370190505b50905060005b82811015610ace57808663ffffffff160140828281518110610abb57610abb611858565b6020908102919091010152600101610a97565b506000610ada8861108a565b9050610ae6818361109f565b50604080516060810190915260009080610b0184600a611297565b8152602001610b1184600a610fca565b81526020018663ffffffff1681525090506000610b2d82610ef8565b6040517f5ed3661300000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690635ed3661390610ba2908990600401611995565b602060405180830381865afa158015610bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be391906119ae565b8114610c1b576040517feff8f8f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050610c57565b6040517f7df2555400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b505050505050565b610c696115aa565b6020810182905260005b82811015610cba578481850181518110610c8f57610c8f611858565b602002602001015182600001518260208110610cad57610cad611858565b6020020152600101610c73565b509392505050565b6000806000805b85811015610dad5781158015610cf8575086516000908260208110610cf057610cf0611858565b602002015114155b15610d1e57848181518110610d0f57610d0f611858565b60200260200101519250600191505b8115610da55786516000908260208110610d3a57610d3a611858565b602002015103610d7657610d7183868381518110610d5a57610d5a611858565b602002602001015160009182526020526040902090565b610da2565b8651610da2908260208110610d8d57610d8d611858565b60200201518460009182526020526040902090565b92505b600101610cc9565b509095945050505050565b60008363ffffffff168263ffffffff161115610e00576040517f6f4617f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818463ffffffff16866040015163ffffffff1681610e2057610e206118e9565b0686604001510301856040019063ffffffff16908163ffffffff16815250508363ffffffff168263ffffffff161015610e625750602084018290526000610e78565b600060208601528451610e75908461130a565b90505b949350505050565b602083015160009015610ebf576040517f27e3c14100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828251610ecc9190611a2a565b84604001818151610edd91906118b6565b63ffffffff9081169091528551610e78925090849061109f16565b60008082600001516020015167ffffffffffffffff811115610f1c57610f1c6119fb565b604051908082528060200260200182016040528015610f45578160200160208202803683370190505b50905060005b835160200151811015610f96578351518160208110610f6c57610f6c611858565b6020020151828281518110610f8357610f83611858565b6020908102919091010152600101610f4b565b50602080840151604051610fac92849101611a4d565b60405160208183030381529060405280519060200120915050919050565b6000806000805b8481101561107e5781158015611000575085516000908260208110610ff857610ff8611858565b602002015114155b156110155761100e816113bf565b9250600191505b8115611076578551600090826020811061103157611031611858565b60200201510361105c5761105783611048836113bf565b60009182526020526040902090565b611073565b8551611073908260208110610d8d57610d8d611858565b92505b600101610fd1565b50909150505b92915050565b6110926115aa565b6110848260008451610c61565b80516000908180808080805b861561127557895160009086602081106110c7576110c7611858565b6020020151146110d85760016110db565b60005b60ff1695505050848401600181901c907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1660005b818110156111fb57866001036111885780600003611147578a51866020811061113b5761113b611858565b60200201519450611167565b89600182038151811061115c5761115c611858565b602002602001015194505b89818151811061117957611179611858565b602002602001015193506111c4565b89818151811061119a5761119a611858565b602002602001015194508981600101815181106111b9576111b9611858565b602002602001015193505b60008581526020859052604090208a600183901c815181106111e8576111e8611858565b6020908102919091010152600201611110565b508587600116146112435788600188038151811061121b5761121b611858565b60200260200101518a60000151866020811061123957611239611858565b6020020152611267565b85600103611267578951600090866020811061126157611261611858565b60200201525b8196508460010194506110ab565b89602001518511156112895760208a018590525b509298975050505050505050565b61129f6115aa565b818360200151106112b857602080840151839003908201525b815b836020015181101561130357835181602081106112d9576112d9611858565b60200201518260000151848303602081106112f6576112f6611858565b60200201526001016112ba565b5092915050565b6020820151600090829082905b808210801561133f57508551600090836020811061133757611337611858565b602002015114155b1561138857855161135b908360208110610d8d57610d8d611858565b8651909350826020811061137157611371611858565b602002016000801916815250816001019150611317565b85518390836020811061139d5761139d611858565b60200201528082106113b3576001820160208701525b50600101949350505050565b6000816000036113d157506000919050565b8160010361140057507fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5919050565b8160020361142f57507fb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30919050565b8160030361145e57507f21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85919050565b8160040361148d57507fe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344919050565b816005036114bc57507f0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d919050565b816006036114eb57507f887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968919050565b8160070361151a57507fffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83919050565b8160080361154957507f9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af919050565b8160090361157857507fcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0919050565b6040517f90fa88a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806115bd6115ca565b8152602001600081525090565b6040518061040001604052806020906020820280368337509192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000081165b811461161857600080fd5b50565b8035611084816115e9565b60006020828403121561163b5761163b600080fd5b6000610e78848461161b565b8015155b82525050565b602081016110848284611647565b600073ffffffffffffffffffffffffffffffffffffffff8216611084565b61164b8161165f565b60208101611084828461167d565b8061160d565b803561108481611694565b6000606082840312156116ba576116ba600080fd5b50919050565b600080604083850312156116d6576116d6600080fd5b60006116e2858561169a565b925050602083013567ffffffffffffffff81111561170257611702600080fd5b61170e858286016116a5565b9150509250929050565b67ffffffffffffffff811661164b565b602081016110848284611718565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13685900301811261176f5761176f600080fd5b80840192508235915067ffffffffffffffff82111561179057611790600080fd5b602092830192820236038313156117a9576117a9600080fd5b509250929050565b63ffffffff811661160d565b8035611084816117b1565b6000602082840312156117dd576117dd600080fd5b6000610e7884846117bd565b8061164b565b60006117fb83836117e9565b505060200190565b600061180d825190565b602083018060005b8381101561183a57815161182988826117ef565b975060208301925050600101611815565b509495945050505050565b60006118518284611803565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b63ffffffff91821691908116908282019081111561108457611084611887565b8181038181111561108457611084611887565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b63ffffffff9182169116600082611931576119316118e9565b500690565b63ffffffff91821691908116908282039081111561108457611084611887565b6000808585111561196957611969600080fd5b8386111561197957611979600080fd5b5050602083020193919092039150565b63ffffffff811661164b565b602081016110848284611989565b805161108481611694565b6000602082840312156119c3576119c3600080fd5b6000610e7884846119a3565b8051611084816117b1565b6000602082840312156119ef576119ef600080fd5b6000610e7884846119cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b63ffffffff91821691908116908282029081169081811461130357611303611887565b6000611a5982856117e9565b602082019150610e78828461180356fea26469706673582212204203c50a1f48c76f62d3fcbad5f186fd184b3f7b0671c0f45c29a3a304c48cb864736f6c63430008130033000000000000000000000000000000000000000000000000000000000000000100000000000000000000000069963768f8407de501029680de46945f838fc98b
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806301ffc9a714610051578063cddc3b93146100bc578063edc55b94146100f0578063fe428ed314610105575b600080fd5b6100a661005f366004611626565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1387d547000000000000000000000000000000000000000000000000000000001490565b6040516100b39190611651565b60405180910390f35b6100e37f00000000000000000000000069963768f8407de501029680de46945f838fc98b81565b6040516100b39190611686565b6101036100fe3660046116c0565b610133565b005b7f00000000000000000000000000000000000000000000000000000000000000016040516100b39190611728565b60006101426020830183611736565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394506101839250505060208401846117c8565b9050816040516020016101969190611845565b6040516020818303038152906040528051906020012084146101e4576040517ff7e4c42900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151600090815b8181101561022e576000801b85828151811061020957610209611858565b602002602001015114610226576102236001821b846118b6565b92505b6001016101eb565b508263ffffffff168263ffffffff16116105495760408051606081019091526000908061026687600a61026181886118d6565b610c61565b81526000602082015260400161027e61040086611918565b6102889086611936565b63ffffffff169052905060006102a08682600a610c61565b905063ffffffff84166102b561040087611918565b6102bf9087611936565b63ffffffff16106104155760006102d861040086611918565b63ffffffff161115610346576000610333600a6102f860408b018b611736565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250879493925050610cc29050565b9050610343836104008381610db8565b50505b63ffffffff841661035961040087611918565b6103639087611936565b63ffffffff1611156103cd576103cb61040061038260408a018a611736565b61039091600b908290611956565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250879493925050610e809050565b505b61040f6104006103e060408a018a611736565b600a8181106103f1576103f1611858565b90506020020135610400886104069190611918565b85929190610db8565b50610448565b6000610429600a6102f860408b018b611736565b90506104456104008261043c828a611918565b86929190610db8565b50505b600061045383610ef8565b6040517f5ed3661300000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000069963768f8407de501029680de46945f838fc98b1690635ed36613906104c8908990600401611995565b602060405180830381865afa1580156104e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061050991906119ae565b8114610541576040517feff8f8f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050610c59565b438263ffffffff161115610589576040517f299b0e4100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60007f00000000000000000000000069963768f8407de501029680de46945f838fc98b73ffffffffffffffffffffffffffffffffffffffff1663596ef8a76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061a91906119da565b9050610628610100436118d6565b8463ffffffff161015801561067057508063ffffffff168363ffffffff16118061067057506106578382611936565b63ffffffff166106678585611936565b63ffffffff1611155b156109e657600060405180606001604052806106e48980604001906106959190611736565b80806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250600a92508291506106d9905060408e018e611736565b6102619291506118d6565b8152600060208201526040016106fc61040088611918565b6107069088611936565b63ffffffff16905290506000610767600a61076161072760408c018c611736565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509250600a9150610c619050565b90610fca565b905061077a61040082610406828a611918565b50600061078683610ef8565b6040517f5ed3661300000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000069963768f8407de501029680de46945f838fc98b1690635ed36613906107fb908a90600401611995565b602060405180830381865afa158015610818573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083c91906119ae565b8114610874576040517feff8f8f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8787031660008167ffffffffffffffff811115610898576108986119fb565b6040519080825280602002602001820160405280156108c1578160200160208202803683370190505b50905060005b828110156108fe57808a63ffffffff1601408282815181106108eb576108eb611858565b60209081029190910101526001016108c7565b50600061094861091160408e018e611736565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061108a92505050565b9050610954818361109f565b50602081015160005b818110156109d9578c818151811061097757610977611858565b60200260200101518360000151826020811061099557610995611858565b6020020151146109d1576040517fb21aeaed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60010161095d565b5050505050505050610c57565b6109f2610100436118d6565b8363ffffffff1610610c25578063ffffffff168363ffffffff161115610a44576040517f8d034e6f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff8382031660008167ffffffffffffffff811115610a6857610a686119fb565b604051908082528060200260200182016040528015610a91578160200160208202803683370190505b50905060005b82811015610ace57808663ffffffff160140828281518110610abb57610abb611858565b6020908102919091010152600101610a97565b506000610ada8861108a565b9050610ae6818361109f565b50604080516060810190915260009080610b0184600a611297565b8152602001610b1184600a610fca565b81526020018663ffffffff1681525090506000610b2d82610ef8565b6040517f5ed3661300000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000069963768f8407de501029680de46945f838fc98b1690635ed3661390610ba2908990600401611995565b602060405180830381865afa158015610bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610be391906119ae565b8114610c1b576040517feff8f8f200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050610c57565b6040517f7df2555400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b505050505050565b610c696115aa565b6020810182905260005b82811015610cba578481850181518110610c8f57610c8f611858565b602002602001015182600001518260208110610cad57610cad611858565b6020020152600101610c73565b509392505050565b6000806000805b85811015610dad5781158015610cf8575086516000908260208110610cf057610cf0611858565b602002015114155b15610d1e57848181518110610d0f57610d0f611858565b60200260200101519250600191505b8115610da55786516000908260208110610d3a57610d3a611858565b602002015103610d7657610d7183868381518110610d5a57610d5a611858565b602002602001015160009182526020526040902090565b610da2565b8651610da2908260208110610d8d57610d8d611858565b60200201518460009182526020526040902090565b92505b600101610cc9565b509095945050505050565b60008363ffffffff168263ffffffff161115610e00576040517f6f4617f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818463ffffffff16866040015163ffffffff1681610e2057610e206118e9565b0686604001510301856040019063ffffffff16908163ffffffff16815250508363ffffffff168263ffffffff161015610e625750602084018290526000610e78565b600060208601528451610e75908461130a565b90505b949350505050565b602083015160009015610ebf576040517f27e3c14100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828251610ecc9190611a2a565b84604001818151610edd91906118b6565b63ffffffff9081169091528551610e78925090849061109f16565b60008082600001516020015167ffffffffffffffff811115610f1c57610f1c6119fb565b604051908082528060200260200182016040528015610f45578160200160208202803683370190505b50905060005b835160200151811015610f96578351518160208110610f6c57610f6c611858565b6020020151828281518110610f8357610f83611858565b6020908102919091010152600101610f4b565b50602080840151604051610fac92849101611a4d565b60405160208183030381529060405280519060200120915050919050565b6000806000805b8481101561107e5781158015611000575085516000908260208110610ff857610ff8611858565b602002015114155b156110155761100e816113bf565b9250600191505b8115611076578551600090826020811061103157611031611858565b60200201510361105c5761105783611048836113bf565b60009182526020526040902090565b611073565b8551611073908260208110610d8d57610d8d611858565b92505b600101610fd1565b50909150505b92915050565b6110926115aa565b6110848260008451610c61565b80516000908180808080805b861561127557895160009086602081106110c7576110c7611858565b6020020151146110d85760016110db565b60005b60ff1695505050848401600181901c907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1660005b818110156111fb57866001036111885780600003611147578a51866020811061113b5761113b611858565b60200201519450611167565b89600182038151811061115c5761115c611858565b602002602001015194505b89818151811061117957611179611858565b602002602001015193506111c4565b89818151811061119a5761119a611858565b602002602001015194508981600101815181106111b9576111b9611858565b602002602001015193505b60008581526020859052604090208a600183901c815181106111e8576111e8611858565b6020908102919091010152600201611110565b508587600116146112435788600188038151811061121b5761121b611858565b60200260200101518a60000151866020811061123957611239611858565b6020020152611267565b85600103611267578951600090866020811061126157611261611858565b60200201525b8196508460010194506110ab565b89602001518511156112895760208a018590525b509298975050505050505050565b61129f6115aa565b818360200151106112b857602080840151839003908201525b815b836020015181101561130357835181602081106112d9576112d9611858565b60200201518260000151848303602081106112f6576112f6611858565b60200201526001016112ba565b5092915050565b6020820151600090829082905b808210801561133f57508551600090836020811061133757611337611858565b602002015114155b1561138857855161135b908360208110610d8d57610d8d611858565b8651909350826020811061137157611371611858565b602002016000801916815250816001019150611317565b85518390836020811061139d5761139d611858565b60200201528082106113b3576001820160208701525b50600101949350505050565b6000816000036113d157506000919050565b8160010361140057507fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5919050565b8160020361142f57507fb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30919050565b8160030361145e57507f21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85919050565b8160040361148d57507fe58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344919050565b816005036114bc57507f0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d919050565b816006036114eb57507f887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968919050565b8160070361151a57507fffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83919050565b8160080361154957507f9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af919050565b8160090361157857507fcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0919050565b6040517f90fa88a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405180604001604052806115bd6115ca565b8152602001600081525090565b6040518061040001604052806020906020820280368337509192915050565b7fffffffff0000000000000000000000000000000000000000000000000000000081165b811461161857600080fd5b50565b8035611084816115e9565b60006020828403121561163b5761163b600080fd5b6000610e78848461161b565b8015155b82525050565b602081016110848284611647565b600073ffffffffffffffffffffffffffffffffffffffff8216611084565b61164b8161165f565b60208101611084828461167d565b8061160d565b803561108481611694565b6000606082840312156116ba576116ba600080fd5b50919050565b600080604083850312156116d6576116d6600080fd5b60006116e2858561169a565b925050602083013567ffffffffffffffff81111561170257611702600080fd5b61170e858286016116a5565b9150509250929050565b67ffffffffffffffff811661164b565b602081016110848284611718565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13685900301811261176f5761176f600080fd5b80840192508235915067ffffffffffffffff82111561179057611790600080fd5b602092830192820236038313156117a9576117a9600080fd5b509250929050565b63ffffffff811661160d565b8035611084816117b1565b6000602082840312156117dd576117dd600080fd5b6000610e7884846117bd565b8061164b565b60006117fb83836117e9565b505060200190565b600061180d825190565b602083018060005b8381101561183a57815161182988826117ef565b975060208301925050600101611815565b509495945050505050565b60006118518284611803565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b63ffffffff91821691908116908282019081111561108457611084611887565b8181038181111561108457611084611887565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b63ffffffff9182169116600082611931576119316118e9565b500690565b63ffffffff91821691908116908282039081111561108457611084611887565b6000808585111561196957611969600080fd5b8386111561197957611979600080fd5b5050602083020193919092039150565b63ffffffff811661164b565b602081016110848284611989565b805161108481611694565b6000602082840312156119c3576119c3600080fd5b6000610e7884846119a3565b8051611084816117b1565b6000602082840312156119ef576119ef600080fd5b6000610e7884846119cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b63ffffffff91821691908116908282029081169081811461130357611303611887565b6000611a5982856117e9565b602082019150610e78828461180356fea26469706673582212204203c50a1f48c76f62d3fcbad5f186fd184b3f7b0671c0f45c29a3a304c48cb864736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000000100000000000000000000000069963768f8407de501029680de46945f838fc98b
-----Decoded View---------------
Arg [0] : chainId (uint64): 1
Arg [1] : _axiomCoreAddress (address): 0x69963768F8407dE501029680dE46945F838Fc98B
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [1] : 00000000000000000000000069963768f8407de501029680de46945f838fc98b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.