Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
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
Contract ABI
API[{"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 | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.