Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 1,096 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Process Blocks B... | 6588223 | 173 days ago | IN | 0 ETH | 0.01615409 | ||||
Process Blocks B... | 6587955 | 173 days ago | IN | 0 ETH | 0.00478047 | ||||
Process Blocks B... | 6587685 | 173 days ago | IN | 0 ETH | 0.01045515 | ||||
Process Blocks B... | 6587446 | 173 days ago | IN | 0 ETH | 0.01028923 | ||||
Process Blocks B... | 6587161 | 173 days ago | IN | 0 ETH | 0.00309673 | ||||
Process Blocks B... | 6586919 | 173 days ago | IN | 0 ETH | 0.00158452 | ||||
Process Blocks B... | 6586660 | 173 days ago | IN | 0 ETH | 0.00890756 | ||||
Process Blocks B... | 6586396 | 173 days ago | IN | 0 ETH | 0.00623668 | ||||
Process Blocks B... | 6586149 | 173 days ago | IN | 0 ETH | 0.00255489 | ||||
Process Blocks B... | 6585901 | 173 days ago | IN | 0 ETH | 0.00083478 | ||||
Process Blocks B... | 6585673 | 173 days ago | IN | 0 ETH | 0.00035776 | ||||
Process Blocks B... | 6585420 | 173 days ago | IN | 0 ETH | 0.00168219 | ||||
Process Blocks B... | 6585179 | 173 days ago | IN | 0 ETH | 0.00020315 | ||||
Process Blocks B... | 6584937 | 173 days ago | IN | 0 ETH | 0.00001102 | ||||
Process Blocks B... | 6584668 | 173 days ago | IN | 0 ETH | 0.00000415 | ||||
Process Blocks B... | 6584401 | 173 days ago | IN | 0 ETH | 0.00001053 | ||||
Process Blocks B... | 6584135 | 173 days ago | IN | 0 ETH | 0.00002384 | ||||
Process Blocks B... | 6583875 | 173 days ago | IN | 0 ETH | 0.00002405 | ||||
Process Blocks B... | 6583598 | 173 days ago | IN | 0 ETH | 0.00010973 | ||||
Process Blocks B... | 6583325 | 173 days ago | IN | 0 ETH | 0.00041878 | ||||
Process Blocks B... | 6583059 | 173 days ago | IN | 0 ETH | 0.00029788 | ||||
Process Blocks B... | 6582784 | 173 days ago | IN | 0 ETH | 0.00232571 | ||||
Process Blocks B... | 6582517 | 173 days ago | IN | 0 ETH | 0.00770056 | ||||
Process Blocks B... | 6582251 | 174 days ago | IN | 0 ETH | 0.00735642 | ||||
Process Blocks B... | 6581980 | 174 days ago | IN | 0 ETH | 0.00371245 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
HeadersProcessor
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; import {EVMHeaderRLP} from "../lib/EVMHeaderRLP.sol"; import {StatelessMmr} from "solidity-mmr/lib/StatelessMmr.sol"; interface IHeadersProcessor { function mmrsCount() external view returns (uint256); function receivedParentHashes(uint256 blockNumber) external view returns (bytes32); function receiveParentHash(uint256 blockNumber, bytes32 parentHash) external; function createBranchFromMessage(bytes32 mmrRoot, uint256 mmrSize, uint256 aggregatorId) external; function createBranchSingleElement( uint256 fromMmrId, uint256 mmrSize, uint256 elementIndex, bytes32 initialBlockHash, bytes32[] calldata mmrPeaks, bytes32[] calldata mmrInclusionProof ) external; function createBranchFromExisting(uint256 mmrId, uint256 mmrSize) external; function processBlocksBatch(bool isReferenceHeaderAccumulated, uint256 mmrId, bytes calldata ctx, bytes[] calldata headersSerialized) external; function getMMRRoot(uint256 mmrId, uint256 mmrSize) external view returns (bytes32); function getLatestMMRRoot(uint256 mmrId) external view returns (bytes32); function getLatestMMRSize(uint256 mmrId) external view returns (uint256); } contract HeadersProcessor is IHeadersProcessor { using EVMHeaderRLP for bytes; /// @notice This struct represents a Merkle Mountain Range accumulating provably valid block hash /// @dev each MMR is mapped to a unique ID also referred to as mmrId struct MMRInfo { /// @notice latestSize represents the latest size of the MMR uint256 latestSize; /// @notice mmrSizeToRoot maps the MMR size to the MMR root, that way we have automatic versioning mapping(uint256 => bytes32) mmrSizeToRoot; } /// @notice emitted when a new MMR is created from a single element /// @param newMMRId the ID of the new MMR /// @param newMMRRoot the root of the new MMR /// @param newMMRSize the size of the new MMR /// @param detachedFromMmrId the ID of the MMR from which the new MMR was created /// @param detachedFromMmrIdAtSize the size of the MMR from which the new MMR was created event BranchCreatedFromElement(uint256 newMMRId, bytes32 newMMRRoot, uint256 newMMRSize, uint256 detachedFromMmrId, uint256 detachedFromMmrIdAtSize); /// @notice emitted when a new MMR is created from an existing MMR /// @param newMMRId the ID of the new MMR /// @param detachedFromRoot the root of the MMR from which the new MMR was created /// @param detachedFromMmrId the ID of the MMR from which the new MMR was created /// @param detachedFromMmrIdAtSize the size of the MMR from which the new MMR was created event BranchCreatedClone(uint256 newMMRId, bytes32 detachedFromRoot, uint256 detachedFromMmrId, uint256 detachedFromMmrIdAtSize); /// @notice emitted when a new MMR is created from an L1 message /// @param newMMRId the ID of the new MMR /// @param mmrSize the size of the new MMR /// @param mmrRoot the root of the new MMR /// @param aggregatorId the ID of the L1 aggregator that is the origin of the message content event BranchCreatedFromL1Message(uint256 newMMRId, uint256 mmrSize, bytes32 mmrRoot, uint256 aggregatorId); /// @notice emitted when a new batch of blocks is processed /// @param startBlockHigh the block number of the first block in the batch /// @param endBlockLow the block number of the last block in the batch /// @param newMMRRoot the root of the new MMR /// @param newMMRSize the size of the new MMR /// @param updatedMMRId the ID of the MMR that was updated event ProcessedBatch(uint256 startBlockHigh, uint256 endBlockLow, bytes32 newMMRRoot, uint256 newMMRSize, uint256 updatedMMRId); /// @notice address of the MessagesInbox contract allowed to forward messages to this contract address public immutable messagesInboxAddr; /// @notice mapping of block number to the block parent hash mapping(uint256 => bytes32) public receivedParentHashes; /// @dev counter for the number of MMRs created uint256 public mmrsCount; /// @dev mapping of MMR ID to MMR info mapping(uint256 => MMRInfo) public mmrs; event ParentHashReceived(uint256 blockNumber, bytes32 parentHash); /// @param _messagesInboxAddr address of the MessagesInbox contract allowed to forward messages to this contract constructor(address _messagesInboxAddr) { messagesInboxAddr = _messagesInboxAddr; } /// @notice modifier to ensure the caller is the MessagesInbox contract modifier onlyMessagesInbox() { require(msg.sender == messagesInboxAddr, "ERR_ONLY_INBOX"); _; } /// @notice Called when a message is sent from L1 to L2 /// @notice saves the parent hash of the block number in the contract storage function receiveParentHash(uint256 blockNumber, bytes32 parentHash) external onlyMessagesInbox { receivedParentHashes[blockNumber] = parentHash; emit ParentHashReceived(blockNumber, parentHash); } /// @notice Creates a new branch from an L1 message, the sent MMR info comes from an L1 aggregator /// @param mmrRoot the root of the MMR /// @param mmrSize the size of the MMR /// @param aggregatorId the ID of the L1 aggregator that is the origin of the message content function createBranchFromMessage(bytes32 mmrRoot, uint256 mmrSize, uint256 aggregatorId) external onlyMessagesInbox { // 1. Assign an ID to the new MMR uint256 currentMMRsCount = mmrsCount; uint256 newMMRId = currentMMRsCount + 1; // 2. Create a new MMR mmrs[newMMRId].latestSize = mmrSize; mmrs[newMMRId].mmrSizeToRoot[mmrSize] = mmrRoot; // 3. Update the MMRs count mmrsCount++; // 4. Emit the event emit BranchCreatedFromL1Message(newMMRId, mmrSize, mmrRoot, aggregatorId); } /// @notice Creates a new branch with only one element taken from an existing MMR /// @param fromMmrId the ID of the MMR from which the new MMR will be created /// @param mmrSize the size of the MMR from which the new MMR will be created /// @param elementIndex the index of the element to take from the existing MMR /// @param initialBlockHash the block hash of the first block in the new MMR /// @param mmrPeaks the peaks of the new MMR /// @param mmrInclusionProof the inclusion proof of the element in the existing MMR function createBranchSingleElement( uint256 fromMmrId, uint256 mmrSize, uint256 elementIndex, bytes32 initialBlockHash, bytes32[] calldata mmrPeaks, bytes32[] calldata mmrInclusionProof ) external { // Verify that the given MMR at the given size has a non zero root bytes32 root = mmrs[fromMmrId].mmrSizeToRoot[mmrSize]; require(root != bytes32(0), "ERR_MMR_DOES_NOT_EXIST"); // Verify that the given element is in the MMR StatelessMmr.verifyProof(elementIndex, initialBlockHash, mmrInclusionProof, mmrPeaks, mmrSize, root); // === Create a new MMR === // // 1. Assign an ID to the new MMR uint256 currentMMRsCount = mmrsCount; uint256 newMMRId = currentMMRsCount + 1; // 2. Create a new MMR bytes32[] memory emptyPeaks = new bytes32[](0); (uint256 newMMRSize, bytes32 newMMRRoot) = StatelessMmr.append(initialBlockHash, emptyPeaks, 0, bytes32(0)); // 3. Update the MMRs mapping mmrs[newMMRId].latestSize = newMMRSize; mmrs[newMMRId].mmrSizeToRoot[newMMRSize] = newMMRRoot; // 4. Update the MMRs count mmrsCount++; // 5. Emit the event emit BranchCreatedFromElement(newMMRId, newMMRRoot, newMMRSize, fromMmrId, mmrSize); } /// @notice Creates a new branch from an existing MMR, effectively cloning it /// @param mmrId the ID of the MMR from which the new MMR will be created /// @param mmrSize size at which the MMR will be copied function createBranchFromExisting(uint256 mmrId, uint256 mmrSize) external { // 1. Load existing MMR data bytes32 root = mmrs[mmrId].mmrSizeToRoot[mmrSize]; // 2. Ensure the given MMR is not empty require(root != bytes32(0), "ERR_MMR_DOES_NOT_EXIST"); // 3. Assign an ID to the new MMR uint256 currentMMRsCount = mmrsCount; uint256 newMMRId = currentMMRsCount + 1; // 4. Copy the existing MMR data to the new MMR mmrs[newMMRId].latestSize = mmrSize; mmrs[newMMRId].mmrSizeToRoot[mmrSize] = root; // 5. Update the MMRs count mmrsCount++; // 6. Emit the event emit BranchCreatedClone(newMMRId, root, mmrId, mmrSize); } /// @notice Processes a batch of blocks /// @param isReferenceHeaderAccumulated whether the reference header is accumulated or not /// @param mmrId the ID of the MMR to update /// @param ctx the context of the batch, encoded as bytes. /// If the reference header is accumulated, the context contains the MMR proof and peaks. /// If the reference header is not accumulated, the context contains the block number of the reference header and the MMR peaks. /// @param headersSerialized the serialized headers of the batch function processBlocksBatch(bool isReferenceHeaderAccumulated, uint256 mmrId, bytes calldata ctx, bytes[] calldata headersSerialized) external { require(headersSerialized.length > 0, "ERR_EMPTY_BATCH"); require(mmrs[mmrId].latestSize != 0, "ERR_MMR_DOES_NOT_EXIST"); uint256 firstBlockInBatch; uint256 newMMRSize; bytes32 newMMRRoot; if (isReferenceHeaderAccumulated) { (firstBlockInBatch, newMMRSize, newMMRRoot) = _processBlocksBatchAccumulated(mmrId, ctx, headersSerialized); } else { (firstBlockInBatch, newMMRSize, newMMRRoot) = _processBlocksBatchNotAccumulated(mmrId, ctx, headersSerialized); } emit ProcessedBatch(firstBlockInBatch, firstBlockInBatch - headersSerialized.length + 1, newMMRRoot, newMMRSize, mmrId); } /// ========================= Internal functions ========================= // function _processBlocksBatchAccumulated( uint256 treeId, bytes memory ctx, bytes[] memory headersSerialized ) internal returns (uint256 firstBlockInBatch, uint256 newMMRSize, bytes32 newMMRRoot) { (uint256 referenceProofLeafIndex, bytes32[] memory referenceProof, bytes32[] memory mmrPeaks, bytes memory referenceHeaderSerialized) = abi.decode( ctx, (uint256, bytes32[], bytes32[], bytes) ); _validateParentBlockAndProofIntegrity(treeId, referenceProofLeafIndex, referenceProof, mmrPeaks, referenceHeaderSerialized); require(referenceHeaderSerialized.getParentHash() == keccak256(headersSerialized[0]), "ERR_NON_CONSECUTIVE_ELEMENT"); bytes32[] memory headersHashes = new bytes32[](headersSerialized.length); headersHashes[0] = referenceHeaderSerialized.getParentHash(); for (uint256 i = 1; i < headersSerialized.length; ++i) { bytes32 parentHash = headersSerialized[i - 1].getParentHash(); require(_isHeaderValid(parentHash, headersSerialized[i]), "ERR_INVALID_CHAIN_ELEMENT"); headersHashes[i] = parentHash; } (newMMRSize, newMMRRoot) = _appendMultipleBlockhashesToMMR(headersHashes, mmrPeaks, treeId); firstBlockInBatch = headersSerialized[0].getBlockNumber(); } function _processBlocksBatchNotAccumulated( uint256 treeId, bytes memory ctx, bytes[] memory headersSerialized ) internal returns (uint256 firstBlockInBatch, uint256 newMMRSize, bytes32 newMMRRoot) { (uint256 blockNumber, bytes32[] memory mmrPeaks) = abi.decode(ctx, (uint256, bytes32[])); bytes32 expectedHash = receivedParentHashes[blockNumber + 1]; require(expectedHash != bytes32(0), "ERR_NO_REFERENCE_HASH"); bytes32[] memory headersHashes = new bytes32[](headersSerialized.length); for (uint256 i = 0; i < headersSerialized.length; i++) { require(_isHeaderValid(expectedHash, headersSerialized[i]), "ERR_INVALID_CHAIN_ELEMENT"); headersHashes[i] = expectedHash; expectedHash = headersSerialized[i].getParentHash(); } (newMMRSize, newMMRRoot) = _appendMultipleBlockhashesToMMR(headersHashes, mmrPeaks, treeId); firstBlockInBatch = blockNumber; } function _appendMultipleBlockhashesToMMR(bytes32[] memory blockhashes, bytes32[] memory lastPeaks, uint256 mmrId) internal returns (uint256 newSize, bytes32 newRoot) { // Getting current mmr state for the treeId newSize = mmrs[mmrId].latestSize; newRoot = mmrs[mmrId].mmrSizeToRoot[newSize]; // Allocate temporary memory for the next peaks bytes32[] memory nextPeaks = lastPeaks; for (uint256 i = 0; i < blockhashes.length; ++i) { (newSize, newRoot, nextPeaks) = StatelessMmr.appendWithPeaksRetrieval(blockhashes[i], nextPeaks, newSize, newRoot); } // Update the contract storage mmrs[mmrId].mmrSizeToRoot[newSize] = newRoot; mmrs[mmrId].latestSize = newSize; } function _isHeaderValid(bytes32 hash, bytes memory header) internal pure returns (bool) { return keccak256(header) == hash; } function _validateParentBlockAndProofIntegrity( uint256 mmrId, uint256 referenceProofLeafIndex, bytes32[] memory referenceProof, bytes32[] memory mmrPeaks, bytes memory referenceHeaderSerialized ) internal view { // Verify the reference block is in the MMR and the proof is valid uint256 mmrSize = mmrs[mmrId].latestSize; bytes32 root = mmrs[mmrId].mmrSizeToRoot[mmrSize]; StatelessMmr.verifyProof(referenceProofLeafIndex, keccak256(referenceHeaderSerialized), referenceProof, mmrPeaks, mmrSize, root); } function getMMRRoot(uint256 mmrId, uint256 mmrSize) external view returns (bytes32) { return mmrs[mmrId].mmrSizeToRoot[mmrSize]; } function getLatestMMRRoot(uint256 mmrId) external view returns (bytes32) { uint256 latestSize = mmrs[mmrId].latestSize; return mmrs[mmrId].mmrSizeToRoot[latestSize]; } function getLatestMMRSize(uint256 mmrId) external view returns (uint256) { return mmrs[mmrId].latestSize; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.20; // This library extracts data from Block header encoded in RLP format. // 0 RLP length 1+2 // 1 parentHash 1+32 // 2 ommersHash 1+32 // 3 beneficiary 1+20 // 4 stateRoot 1+32 // 5 TransactionRoot 1+32 // 6 receiptsRoot 1+32 // logsBloom length 1+2 // 7 logsBloom 256 // Total static elements size: 448 bytes // 8 difficulty - starts at pos 448 // 9 number - blockNumber // 10 gasLimit // 11 gasUsed // 12 timestamp // 13 extraData // 14 mixHash // 15 nonce // 16 baseFee // 17 withdrawalsRoot library EVMHeaderRLP { function nextElementJump(uint8 prefix) public pure returns (uint8) { if (prefix <= 128) { return 1; } else if (prefix <= 183) { return prefix - 128 + 1; } revert("EVMHeaderRLP.nextElementJump: Given element length not implemented"); } // no loop saves ~300 gas function getBlockNumberPositionNoLoop(bytes memory rlp) public pure returns (uint256) { uint256 pos; //jumpting straight to the 1st dynamic element at pos 448 - difficulty pos = 448; //2nd element - block number pos += nextElementJump(uint8(rlp[pos])); return pos; } // no loop saves ~300 gas function getGasLimitPositionNoLoop(bytes memory rlp) public pure returns (uint256) { uint256 pos; //jumpting straight to the 1st dynamic element at pos 448 - difficulty pos = 448; //2nd element - block number pos += nextElementJump(uint8(rlp[pos])); //3rd element - gas limit pos += nextElementJump(uint8(rlp[pos])); return pos; } // no loop saves ~300 gas function getTimestampPositionNoLoop(bytes memory rlp) public pure returns (uint256) { uint256 pos; //jumpting straight to the 1st dynamic element at pos 448 - difficulty pos = 448; //2nd element - block number pos += nextElementJump(uint8(rlp[pos])); //3rd element - gas limit pos += nextElementJump(uint8(rlp[pos])); //4th element - gas used pos += nextElementJump(uint8(rlp[pos])); //timestamp - jackpot! pos += nextElementJump(uint8(rlp[pos])); return pos; } function getMixHashPositionNoLoop(bytes memory rlp) public pure returns (uint256) { uint256 pos; //jumpting straight to the 1st dynamic element at pos 448 - difficulty pos = 448; //2nd element - block number pos += nextElementJump(uint8(rlp[pos])); //3rd element - gas limit pos += nextElementJump(uint8(rlp[pos])); //4th element - gas used pos += nextElementJump(uint8(rlp[pos])); // 5th element - timestamp pos += nextElementJump(uint8(rlp[pos])); // 6th element - extradata pos += nextElementJump(uint8(rlp[pos])); // 7th element - mixhash pos += nextElementJump(uint8(rlp[pos])); return pos; } function getBaseFeePositionNoLoop(bytes memory rlp) public pure returns (uint256) { //jumping straight to the 1st dynamic element at pos 448 - difficulty uint256 pos = 448; // 2nd element - block number pos += nextElementJump(uint8(rlp[pos])); // 3rd element - gas limit pos += nextElementJump(uint8(rlp[pos])); // 4th element - gas used pos += nextElementJump(uint8(rlp[pos])); // timestamp pos += nextElementJump(uint8(rlp[pos])); // extradata pos += nextElementJump(uint8(rlp[pos])); // mixhash pos += nextElementJump(uint8(rlp[pos])); // nonce pos += nextElementJump(uint8(rlp[pos])); // nonce pos += nextElementJump(uint8(rlp[pos])); return pos; } function extractFromRLP(bytes calldata rlp, uint256 elementPosition) public pure returns (uint256 element) { // RLP hint: If the byte is less than 128 - than this byte IS the value needed - just return it. if (uint8(rlp[elementPosition]) < 128) { return uint256(uint8(rlp[elementPosition])); } // RLP hint: Otherwise - this byte stores the length of the element needed (in bytes). uint8 elementSize = uint8(rlp[elementPosition]) - 128; // ABI Encoding hint for dynamic bytes element: // 0x00-0x04 (4 bytes): Function signature // 0x05-0x23 (32 bytes uint): Offset to raw data of RLP[] // 0x24-0x43 (32 bytes uint): Length of RLP's raw data (in bytes) // 0x44-.... The RLP raw data starts here // 0x44 + elementPosition: 1 byte stores a length of our element // 0x44 + elementPosition + 1: Raw data of the element // Copies the element from calldata to uint256 stored in memory assembly { calldatacopy( add(mload(0x40), sub(32, elementSize)), // Copy to: Memory 0x40 (free memory pointer) + 32bytes (uint256 size) - length of our element (in bytes) add(0x44, add(elementPosition, 1)), // Copy from: Calldata 0x44 (RLP raw data offset) + elementPosition + 1 byte for the size of element elementSize ) element := mload(mload(0x40)) // Load the 32 bytes (uint256) stored at memory 0x40 pointer - into return value } return element; } function getBlockNumber(bytes calldata rlp) public pure returns (uint256) { return extractFromRLP(rlp, getBlockNumberPositionNoLoop(rlp)); } function getTimestamp(bytes calldata rlp) public pure returns (uint256) { return extractFromRLP(rlp, getTimestampPositionNoLoop(rlp)); } function getDifficulty(bytes calldata rlp) public pure returns (uint256) { return extractFromRLP(rlp, 448); } function getGasLimit(bytes calldata rlp) public pure returns (uint256) { return extractFromRLP(rlp, getGasLimitPositionNoLoop(rlp)); } function getBaseFee(bytes calldata rlp) public pure returns (uint256) { return extractFromRLP(rlp, getBaseFeePositionNoLoop(rlp)); } function getParentHash(bytes calldata rlp) public pure returns (bytes32) { return bytes32(extractFromRLP(rlp, 3)); } function getUnclesHash(bytes calldata rlp) public pure returns (bytes32) { return bytes32(extractFromRLP(rlp, 36)); } function getBeneficiary(bytes calldata rlp) public pure returns (address) { return address(uint160(extractFromRLP(rlp, 70))); } function getStateRoot(bytes calldata rlp) public pure returns (bytes32) { return bytes32(extractFromRLP(rlp, 90)); } function getTransactionsRoot(bytes calldata rlp) public pure returns (bytes32) { return bytes32(extractFromRLP(rlp, 123)); } function getReceiptsRoot(bytes calldata rlp) public pure returns (bytes32) { return bytes32(extractFromRLP(rlp, 156)); } function getMixHash(bytes calldata rlp) public pure returns (bytes32) { return bytes32(extractFromRLP(rlp, getMixHashPositionNoLoop(rlp))); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.17; import "./StatelessMmrHelpers.sol"; /// _____ _ _ _ _ _ __ __ __ __ _____ /// / ____| | (_) | (_) | | \/ | \/ | __ \ /// | (___ ___ | |_ __| |_| |_ _ _ | \ / | \ / | |__) | /// \___ \ / _ \| | |/ _` | | __| | | | | |\/| | |\/| | _ / /// ____) | (_) | | | (_| | | |_| |_| | | | | | | | | | \ \ /// |_____/ \___/|_|_|\__,_|_|\__|\__, | |_| |_|_| |_|_| \_\ /// __/ | /// |___/ /// /// @title StatelessMmr -- A Solidity implementation of Merkle Mountain Range /// @author Herodotus Ltd /// @notice Library for appending bytes32 values (i.e., acting as an accumulator) /// and verifying Merkle inclusion proofs /// library StatelessMmr { error InvalidProof(); error IndexOutOfBounds(); error InvalidRoot(); error InvalidPeaksArrayLength(); /// /// @notice Append a new element to the MMR /// @param elem Element to append /// @param peaks The latest peaks /// @param lastElementsCount The latest elements count /// @param lastRoot The latest root /// @return The updated elements count and the new root hash of the tree /// function append( bytes32 elem, bytes32[] memory peaks, uint lastElementsCount, bytes32 lastRoot ) internal pure returns (uint, bytes32) { (uint updatedElementsCount, bytes32 newRoot, ) = doAppend( elem, peaks, lastElementsCount, lastRoot ); return (updatedElementsCount, newRoot); } /// /// Same as `append` but also returns the updated peaks /// function appendWithPeaksRetrieval( bytes32 elem, bytes32[] memory peaks, uint lastElementsCount, bytes32 lastRoot ) internal pure returns (uint, bytes32, bytes32[] memory) { ( uint updatedElementsCount, bytes32 newRoot, bytes32[] memory updatedPeaks ) = doAppend(elem, peaks, lastElementsCount, lastRoot); return (updatedElementsCount, newRoot, updatedPeaks); } /// /// @param elems Elements to append (in order) /// @param peaks The latest peaks /// @param lastElementsCount The latest elements count /// @param lastRoot The latest tree root hash /// @return The newest elements count and the newest tree root hash /// function multiAppend( bytes32[] memory elems, bytes32[] memory peaks, uint lastElementsCount, bytes32 lastRoot ) internal pure returns (uint, bytes32) { uint elementsCount = lastElementsCount; bytes32 root = lastRoot; bytes32[] memory updatedPeaks = peaks; for (uint i = 0; i < elems.length; ++i) { (elementsCount, root, updatedPeaks) = appendWithPeaksRetrieval( elems[i], updatedPeaks, elementsCount, root ); } return (elementsCount, root); } /// /// Same as `multiAppend` but also returns the updated peaks /// function multiAppendWithPeaksRetrieval( bytes32[] memory elems, bytes32[] memory peaks, uint lastElementsCount, bytes32 lastRoot ) internal pure returns (uint, bytes32, bytes32[] memory) { uint elementsCount = lastElementsCount; bytes32 root = lastRoot; bytes32[] memory updatedPeaks = peaks; for (uint i = 0; i < elems.length; ++i) { (elementsCount, root, updatedPeaks) = appendWithPeaksRetrieval( elems[i], updatedPeaks, elementsCount, root ); } return (elementsCount, root, updatedPeaks); } /// /// @notice Efficient version of `multiAppend` that takes in all the precomputed peaks /// @param elems Elements to append (in order) /// @param allPeaks All the precomputed peaks computed off-chain (more gas efficient) /// @param lastElementsCount The latest elements count /// @param lastRoot The latest tree root hash /// @return The newest elements count and the newest tree root hash /// function multiAppendWithPrecomputedPeaks( bytes32[] memory elems, bytes32[][] memory allPeaks, uint lastElementsCount, bytes32 lastRoot ) internal pure returns (uint, bytes32) { uint elementsCount = lastElementsCount; bytes32 root = lastRoot; for (uint i = 0; i < elems.length; ++i) { (elementsCount, root) = append( elems[i], allPeaks[i], elementsCount, root ); } return (elementsCount, root); } /// /// @notice Verify a Merkle inclusion proof /// @dev Reverts if the proof is invalid /// @param proof The Merkle inclusion proof /// @param peaks The peaks at the time of inclusion /// @param elementsCount The element count at the time of inclusion /// @param root The tree root hash at the time of inclusion /// function verifyProof( uint index, bytes32 value, bytes32[] memory proof, bytes32[] memory peaks, uint elementsCount, bytes32 root ) internal pure { if (index > elementsCount) { revert IndexOutOfBounds(); } bytes32 computedRoot = computeRoot(peaks, bytes32(elementsCount)); if (computedRoot != root) { revert InvalidRoot(); } bytes32 topPeak = getProofTopPeak(0, value, index, proof); bool isValid = StatelessMmrHelpers.arrayContains(topPeak, peaks); if (!isValid) { revert InvalidProof(); } } /// _ _ _ ______ _ _ /// | | | | | | | ____| | | (_) /// | |__| | ___| |_ __ ___ _ __ | |__ _ _ _ __ ___| |_ _ ___ _ __ ___ /// | __ |/ _ \ | '_ \ / _ \ '__| | __| | | | '_ \ / __| __| |/ _ \| '_ \/ __| /// | | | | __/ | |_) | __/ | | | | |_| | | | | (__| |_| | (_) | | | \__ \ /// |_| |_|\___|_| .__/ \___|_| |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ /// | | /// |_| /// /// @notice Computes the root hash of the given peaks and tree size /// @param peaks Peaks to compute the root from /// @param size Tree size to to compute the root from /// @return The root hash of the following peaks and tree size /// function computeRoot( bytes32[] memory peaks, bytes32 size ) internal pure returns (bytes32) { bytes32 baggedPeaks = bagPeaks(peaks); return keccak256(abi.encode(size, baggedPeaks)); } /// /// @notice Bag the peaks: recursively hashing peaks together to form a single hash /// @param peaks The peaks to bag /// @return The bagged peaks /// function bagPeaks(bytes32[] memory peaks) internal pure returns (bytes32) { if (peaks.length < 1) { revert InvalidPeaksArrayLength(); } if (peaks.length == 1) { return peaks[0]; } uint len = peaks.length; bytes32 root0 = keccak256(abi.encode(peaks[len - 2], peaks[len - 1])); bytes32[] memory reversedPeaks = new bytes32[](len - 2); for (uint i = 0; i < len - 2; i++) { reversedPeaks[i] = peaks[len - 3 - i]; } bytes32 bags = root0; for (uint i = 0; i < reversedPeaks.length; i++) { bags = keccak256(abi.encode(reversedPeaks[i], bags)); } return bags; } function doAppend( bytes32 elem, bytes32[] memory peaks, uint lastElementsCount, bytes32 lastRoot ) internal pure returns (uint, bytes32, bytes32[] memory) { uint elementsCount = lastElementsCount + 1; if (lastElementsCount == 0) { bytes32 root0 = elem; bytes32 firstRoot = keccak256(abi.encode(uint(1), root0)); bytes32[] memory newPeaks = new bytes32[](1); newPeaks[0] = root0; return (elementsCount, firstRoot, newPeaks); } uint leafCount = StatelessMmrHelpers.mmrSizeToLeafCount( elementsCount - 1 ); uint numberOfPeaks = StatelessMmrHelpers.countOnes(leafCount); if (peaks.length != numberOfPeaks) { revert InvalidPeaksArrayLength(); } bytes32 computedRoot = computeRoot(peaks, bytes32(lastElementsCount)); if (computedRoot != lastRoot) { revert InvalidRoot(); } bytes32[] memory appendPeaks = StatelessMmrHelpers.newArrWithElem( peaks, elem ); uint appendNoMerges = StatelessMmrHelpers.leafCountToAppendNoMerges(leafCount); bytes32[] memory updatedPeaks = appendPerformMerging( appendPeaks, appendNoMerges ); uint updatedElementsCount = elementsCount + appendNoMerges; bytes32 newRoot = computeRoot( updatedPeaks, bytes32(updatedElementsCount) ); return (updatedElementsCount, newRoot, updatedPeaks); } function appendPerformMerging( bytes32[] memory peaks, uint noMerges ) internal pure returns (bytes32[] memory) { uint peaksLen = peaks.length; bytes32 accHash = peaks[peaksLen - 1]; for (uint i = 0; i < noMerges; i++) { bytes32 hash = peaks[peaksLen - i - 2]; accHash = keccak256(abi.encode(hash, accHash)); } bytes32[] memory newPeaks = new bytes32[](peaksLen - noMerges); for (uint i = 0; i < peaksLen - noMerges - 1; i++) { newPeaks[i] = peaks[i]; } newPeaks[peaksLen - noMerges - 1] = accHash; return newPeaks; } function getProofTopPeak( uint height, bytes32 hash, uint elementsCount, bytes32[] memory proof ) internal pure returns (bytes32) { uint leafIndex = StatelessMmrHelpers.mmrIndexToLeafIndex(elementsCount); for (uint i = 0; i < proof.length; ++i) { bytes32 currentSibling = proof[i]; bool isRightChild = leafIndex % 2 == 1; if (isRightChild) { bytes32 hashed = keccak256(abi.encode(currentSibling, hash)); elementsCount += 1; hash = hashed; } else { bytes32 hashed = keccak256(abi.encode(hash, currentSibling)); elementsCount += 2 << height; hash = hashed; } ++height; leafIndex /= 2; } return hash; } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.17; library StatelessMmrHelpers { // Returns the height of a given `index`. The height of the root is 0 function getHeight(uint index) internal pure returns (uint) { require(index >= 1, "index must be at least 1"); uint bits = bitLength(index); uint ones = allOnes(bits); if (index != ones) { uint shifted = 1 << (bits - 1); uint recHeight = getHeight(index - (shifted - 1)); return recHeight; } return bits - 1; } // Returns the number of bits in `num` function bitLength(uint256 num) internal pure returns (uint256) { require(num >= 0, "num must be greater than or equal to zero"); uint256 bitPosition = 0; uint256 curN = 1; while (num >= curN) { bitPosition += 1; curN <<= 1; } return bitPosition; } // Returns a number having all its bits set to 1 for a given `bitsLength` function allOnes(uint256 bitsLength) internal pure returns (uint256) { require(bitsLength >= 0, "bitsLength must be greater or equal to zero"); return (1 << bitsLength) - 1; } // Returns a number of ones in bit representation of a number function countOnes(uint256 num) internal pure returns (uint256) { uint256 count = 0; for (; num > 0; count++) { num = num & (num - 1); } return count; } // Returns the sibling offset from `height` function siblingOffset(uint256 height) internal pure returns (uint256) { return (2 << height) - 1; } // Returns the parent offset from `height` function parentOffset(uint256 height) internal pure returns (uint256) { return 2 << height; } // Returns number of leaves for a given mmr size function mmrSizeToLeafCount(uint256 mmrSize) internal pure returns (uint256) { uint256 leafCount = 0; uint256 mountainLeafCount = 1 << bitLength(mmrSize); for(; mountainLeafCount > 0; mountainLeafCount /= 2) { uint256 mountainSize = 2 * mountainLeafCount - 1; if (mountainSize <= mmrSize) { leafCount += mountainLeafCount; mmrSize -= mountainSize; } } require(mmrSize == 0, "mmrSize can't be associated with a valid MMR size"); return leafCount; } // Returns leaf index (0-based) for a given mmr (element) index function mmrIndexToLeafIndex(uint256 mmrIndex) internal pure returns (uint256) { return mmrSizeToLeafCount(mmrIndex - 1); } function leafCountToAppendNoMerges(uint256 leafCount) internal pure returns (uint256) { uint256 count = 0; while(leafCount > 0 && (leafCount & 1) == 1) { count += 1; leafCount /= 2; } return count; } // Creates a new array from source and returns a new one containing all previous elements + `elem` function newArrWithElem( bytes32[] memory sourceArr, bytes32 elem ) internal pure returns (bytes32[] memory) { bytes32[] memory outputArray = new bytes32[](sourceArr.length + 1); uint i = 0; for (; i < sourceArr.length; i++) { outputArray[i] = sourceArr[i]; } outputArray[i] = elem; return outputArray; } // Returns true if `elem` is in `arr` function arrayContains( bytes32 elem, bytes32[] memory arr ) internal pure returns (bool) { for (uint i = 0; i < arr.length; ++i) { if (arr[i] == elem) { return true; } } return false; } }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "@openzeppelin/=lib/openzeppelin-contracts/", "solidity-mmr/=lib/solidity-mmr/src/", "@optimism/libraries/=src/lib/external/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": { "src/lib/EVMHeaderRLP.sol": { "EVMHeaderRLP": "0x5eDD46819CCfea2D6ea227E5b2d93c1f8dce6f1a" } } }
[{"inputs":[{"internalType":"address","name":"_messagesInboxAddr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"IndexOutOfBounds","type":"error"},{"inputs":[],"name":"InvalidPeaksArrayLength","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"InvalidRoot","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMMRId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"detachedFromRoot","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"detachedFromMmrId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detachedFromMmrIdAtSize","type":"uint256"}],"name":"BranchCreatedClone","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMMRId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"newMMRRoot","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"newMMRSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detachedFromMmrId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detachedFromMmrIdAtSize","type":"uint256"}],"name":"BranchCreatedFromElement","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMMRId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mmrSize","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"mmrRoot","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"aggregatorId","type":"uint256"}],"name":"BranchCreatedFromL1Message","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"blockNumber","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"parentHash","type":"bytes32"}],"name":"ParentHashReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"startBlockHigh","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endBlockLow","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"newMMRRoot","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"newMMRSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"updatedMMRId","type":"uint256"}],"name":"ProcessedBatch","type":"event"},{"inputs":[{"internalType":"uint256","name":"mmrId","type":"uint256"},{"internalType":"uint256","name":"mmrSize","type":"uint256"}],"name":"createBranchFromExisting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"mmrRoot","type":"bytes32"},{"internalType":"uint256","name":"mmrSize","type":"uint256"},{"internalType":"uint256","name":"aggregatorId","type":"uint256"}],"name":"createBranchFromMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fromMmrId","type":"uint256"},{"internalType":"uint256","name":"mmrSize","type":"uint256"},{"internalType":"uint256","name":"elementIndex","type":"uint256"},{"internalType":"bytes32","name":"initialBlockHash","type":"bytes32"},{"internalType":"bytes32[]","name":"mmrPeaks","type":"bytes32[]"},{"internalType":"bytes32[]","name":"mmrInclusionProof","type":"bytes32[]"}],"name":"createBranchSingleElement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mmrId","type":"uint256"}],"name":"getLatestMMRRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mmrId","type":"uint256"}],"name":"getLatestMMRSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mmrId","type":"uint256"},{"internalType":"uint256","name":"mmrSize","type":"uint256"}],"name":"getMMRRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messagesInboxAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"mmrs","outputs":[{"internalType":"uint256","name":"latestSize","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mmrsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isReferenceHeaderAccumulated","type":"bool"},{"internalType":"uint256","name":"mmrId","type":"uint256"},{"internalType":"bytes","name":"ctx","type":"bytes"},{"internalType":"bytes[]","name":"headersSerialized","type":"bytes[]"}],"name":"processBlocksBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes32","name":"parentHash","type":"bytes32"}],"name":"receiveParentHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"receivedParentHashes","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a03461007857601f61180838819003918201601f19168301916001600160401b0383118484101761007d5780849260209460405283398101031261007857516001600160a01b038116810361007857608052604051611774908161009482396080518181816102b2015281816109e40152610b910152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe6040608081526004908136101561001557600080fd5b600091823560e01c9081630181e56c14610b71578163170deac414610b3d578163255b46a314610b165781633687321c14610af757816339200ec714610acf578163431cc8e414610acf57816359ab5e6514610a135781637b2745e0146109cf578163c31cd7ea146102fe578163ef0cfef414610273578163fba788f3146100d8575063fc50b6f3146100a757600080fd5b346100d457806020926100b936610c4b565b90825260028552600183832001908252845220549051908152f35b5080fd5b8383346100d45760c03660031901126100d45767ffffffffffffffff906024359084359060643560843585811161026f576101169036908901610c66565b60a43587811161026b57610174878a969594936101398d61017e95369101610c66565b9390958a8a5261016c602097600289528a60019c8d828220018882528b52205496610165881515610d06565b3691610eb3565b923691610eb3565b9086604435611066565b835496848801809811610258578351908282019081118282101761024557918960a09896949286947f0df6045decc2e38e91256e3df7519e307047e01f7c1e2741b3cb829cee6c53739b9997525282518181019085825283858201528481526101e681610d4b565b5190209161020684516101f881610d7d565b868152833681830137610f94565b52868a526002815283838b20818155818c5201815281838b205561022a8454610cf7565b8455825196875286015284015260608301526080820152a180f35b634e487b7160e01b8a5260418b5260248afd5b634e487b7160e01b895260118a52602489fd5b8880fd5b8680fd5b919050346102fa57806003193601126102fa577f739ec7da8ecc6bc018d914fc832fc7e42032312537324ff54309f204e056eed691356024356102e0337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614610c97565b81855284602052808386205582519182526020820152a180f35b8280fd5b839150346100d45760803660031901126100d45780359283151584036102fa576024938435916044359367ffffffffffffffff9283861161026f573660238701121561026f5785820135978489116109cb5780870196818a36920101116109cb5760643585811161026b576103769036908501610c66565b979099881561099757878a5260209360028552610397878c20541515610d06565b156107bb576103b4916103ab913691610dd7565b99883691610e26565b9489518a019060808b85840193031261061357838b015191868c01518281116107b75781866103e5928f0101610f33565b9160608d01518181116107335782878f92610401930101610f33565b9c608081015191821161073357019181603f840112156107b7578583015161042881610dbb565b936104358a519586610d99565b818552878501938a83830101116106d95783928f918f93610481968d61045b9301611017565b8c8352600289528a8320968b8854946001809a878352018c522054948751902090611066565b855163726ab4ef60e01b808252735edd46819ccfea2d6ea227e5b2d93c1f8dce6f1a93918681806104b4878c830161103a565b0381885af490811561073e578d9161078a575b506104d18a610f94565b5187815191012003610748576104fd866104eb8b51610f01565b948a51809381928683528c830161103a565b0381885af490811561073e57908392918e91610707575b5090915061052184610f94565b526001825b610628575b50505061054c91610544896105679a9b9c9d87946111a9565b939098610f94565b5190875180809b81946310862ffb60e11b83528a830161103a565b03915af496871561061e578a976105e9575b50610588909596985b89610ea6565b92600184018094116105d857897f590026708fa6cd340ca0408a29735869140bd2e27b0c904eb485065224b2746b60a08b8b8b8b8b8b8b825196875286015284015260608301526080820152a180f35b634e487b7160e01b8a526011905288fd5b9096508281813d8311610617575b6106018183610d99565b81010312610613575195610588610579565b8980fd5b503d6105f7565b85513d8c823e3d90fd5b89518110156107025760001981018181116106f0578761064b61065d928d610fb7565b518b51809381928783528d830161103a565b0381895af49081156106e457849392918f918d916106a8575b50906106958161068885889695610fb7565b518c815191012014610fcb565b61069f8288610fb7565b52019091610526565b9293945050508781813d83116106dd575b6106c38183610d99565b810103126106d95751839291908b610695610676565b8d80fd5b503d6106b9565b8e8b51903d90823e3d90fd5b634e487b7160e01b8e5260118952868efd5b61052b565b80929350888092503d8311610737575b6107218183610d99565b8101031261073357908291518f610514565b8c80fd5b503d610717565b89513d8f823e3d90fd5b875162461bcd60e51b8152808801879052601b818701527f4552525f4e4f4e5f434f4e53454355544956455f454c454d454e5400000000006044820152606490fd5b90508681813d83116107b0575b6107a18183610d99565b8101031261073357518e6104c7565b503d610797565b8b80fd5b6107de916107d5919b98999a9b9793949596973691610dd7565b96893691610e26565b9086518701968681890312610993578481015197878201519283116107b75761080d9286809201920101610f33565b906001808801808911610981578b528a8552868b20548015610947579080838d9361083a8d979651610f01565b9490735edd46819ccfea2d6ea227e5b2d93c1f8dce6f1a935b610878575b50505050509161086f9161058897969594936111a9565b95909698610582565b90919293949596835183101561093f57506108a4816108978486610fb7565b518b815191012014610fcb565b6108ae8287610fb7565b528d6108d9898c8c6108c08688610fb7565b51915163726ab4ef60e01b81529384928392830161103a565b0381885af4918215610934579086959493926108fe575b508d97969591850191610853565b8095508a8092503d831161092d575b6109178183610d99565b810103126109295784809451906108f0565b8e80fd5b503d61090d565b8c51903d90823e3d90fd5b969594610858565b875162461bcd60e51b81528088018790526015818701527408aa4a4be9c9ebea48a8c8aa48a9c868abe9082a69605b1b6044820152606490fd5b634e487b7160e01b8c5260118752848cfd5b8a80fd5b855162461bcd60e51b8152602081870152600f818501526e08aa4a4be8a9aa0a8b2be8482a8869608b1b6044820152606490fd5b8780fd5b5050346100d457816003193601126100d457517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b8383346100d457610a2336610c4b565b908084526002602052600192838186200183865260205280852054610a49811515610d06565b845494808601809611610abc5791859391608095937ff74fc47bc0ff3885b5fd3cdbee5c8fac996c70b2f2c8a83f916c28ae8901f4b4978952600260205280838a20868155868b520160205281838a2055610aa48154610cf7565b9055815194855260208501528301526060820152a180f35b634e487b7160e01b875260118852602487fd5b9050346102fa5760203660031901126102fa5760209282913581526002845220549051908152f35b5050346100d457816003193601126100d4576020906001549051908152f35b9050346102fa5760203660031901126102fa57602092829135815280845220549051908152f35b9050346102fa5760203660031901126102fa5760209282913581526002845260018282208054835201845220549051908152f35b8383346100d45760603660031901126100d45760243590833590610bbf337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614610c97565b6001549260018401809411610c38579183916080937f42f9954f84d101195747fd02416d3fa26cb9bbc0d5b479d4bffc33b7e442345a95875260026020526001828820828155828952016020528282882055610c1c600154610cf7565b600155815193845260208401528201526044356060820152a180f35b634e487b7160e01b855260118652602485fd5b6040906003190112610c61576004359060243590565b600080fd5b9181601f84011215610c615782359167ffffffffffffffff8311610c61576020808501948460051b010111610c6157565b15610c9e57565b60405162461bcd60e51b815260206004820152600e60248201526d08aa4a4be9e9c98b2be929c849eb60931b6044820152606490fd5b91908201809211610ce157565b634e487b7160e01b600052601160045260246000fd5b6000198114610ce15760010190565b15610d0d57565b60405162461bcd60e51b815260206004820152601660248201527511549497d3535497d113d154d7d393d517d1561254d560521b6044820152606490fd5b6060810190811067ffffffffffffffff821117610d6757604052565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff821117610d6757604052565b90601f8019910116810190811067ffffffffffffffff821117610d6757604052565b67ffffffffffffffff8111610d6757601f01601f191660200190565b929192610de382610dbb565b91610df16040519384610d99565b829481845281830111610c61578281602093846000960137010152565b67ffffffffffffffff8111610d675760051b60200190565b92919092610e3384610e0e565b91610e416040519384610d99565b829480845260208094019060051b830192828411610c615780915b848310610e6b57505050505050565b823567ffffffffffffffff8111610c6157820184601f82011215610c61578691610e9b8683858095359101610dd7565b815201920191610e5c565b91908203918211610ce157565b9291610ebe82610e0e565b91610ecc6040519384610d99565b829481845260208094019160051b8101928311610c6157905b828210610ef25750505050565b81358152908301908301610ee5565b90610f0b82610e0e565b610f186040519182610d99565b8281528092610f29601f1991610e0e565b0190602036910137565b9080601f83011215610c6157815190602091610f4e81610e0e565b93610f5c6040519586610d99565b81855260208086019260051b820101928311610c6157602001905b828210610f85575050505090565b81518152908301908301610f77565b805115610fa15760200190565b634e487b7160e01b600052603260045260246000fd5b8051821015610fa15760209160051b010190565b15610fd257565b60405162461bcd60e51b815260206004820152601960248201527f4552525f494e56414c49445f434841494e5f454c454d454e54000000000000006044820152606490fd5b60005b83811061102a5750506000910152565b818101518382015260200161101a565b6040916020825261105a8151809281602086015260208686019101611017565b601f01601f1916010190565b959394909192948087116111975761107e9086611245565b036111855760009490916000198201828111611171576110a390969396949294611644565b9183945b8251861015611147576110ba8684610fb7565b51600198858a168a0361110b576040805191602083019384528183015281526110e281610d4b565b51902093888201809211610ce1576110fd6001929596610cf7565b981c950194929693966110a7565b6111406110fd91600194979360408051916020830193845281830152815261113281610d4b565b519020966002891b90610cd4565b9596610cf7565b509592505050611158929150611271565b1561115f57565b6040516309bde33960e01b8152600490fd5b634e487b7160e01b87526011600452602487fd5b60405163504570e360e01b8152600490fd5b604051634e23d03560e01b8152600490fd5b92909160009080825260026020526040822080549560018092888652016020528160408520549685935b611205575b50505050818160409287945260026020526001838320018483526020528583832055815260026020522055565b829792989193985189101561123a578392839261122c926112268c8c610fb7565b516112a6565b9199909892019391926111d3565b8198508297506111d8565b61124e90611525565b604051906020820192835260408201526040815261126b81610d4b565b51902090565b60005b825181101561129e57816112888285610fb7565b511461129657600101611274565b505050600190565b505050600090565b9091939260019485810191828211610ce15781156114d7576112c782611644565b91600083805b6114b757508651036114a5576112e39086611245565b0361118557835192868401809411610ce1576112ff8794610f01565b946000975b61146b575b506113178596978596610fb7565b526000925b611438575b5083516000199390848101818111610ce15761133e859188610fb7565b519260005b8281106113e8575061135d6113588385610ea6565b610f01565b976000825b61139f575b50505061137391610ea6565b938401938411610ce15761138a6113909486610fb7565b52610cd4565b9161139b8383611245565b9190565b9091926113ac9085610ea6565b888101908111610ce15787908210156113e257509082826113ce829484610fb7565b516113d9828d610fb7565b52018793611362565b92611367565b939091506113f68484610ea6565b600119810191908211610ce15761140e83928a610fb7565b519060408051916020830193845281830152815261142b81610d4b565b5190209301908591611343565b9182151580611460575b1561145a57838101809111610ce15791831c8361131c565b91611321565b508380841614611442565b80969594965188101561149c578688611489829a849a98999a610fb7565b51611494828a610fb7565b520197611304565b95939495611309565b604051638355f42560e01b8152600490fd5b906000198201828111610ce1576114d19216918291610cf7565b916112cd565b505090939291506040516020810190848252856040820152604081526114fc81610d4b565b519020936040519361150d85610d7d565b84526020368186013761151f84610f94565b52929190565b9060019160018151106114a5576001815114611635578051926001198401848111610ce1576115548184610fb7565b516000198601868111610ce15761156b9085610fb7565b51916040918251602094602082019283528482015283815261158c81610d4b565b5190209661159982610f01565b95600219820191821160005b84811061160957505050505082939495946000945b6115c7575b505050505090565b8051851015611604578385966115de829784610fb7565b5190845190868201928352858201528481526115f981610d4b565b5190209601946115ba565b6115bf565b81610ce1578061162361161d8a9387610ea6565b85610fb7565b5161162e828c610fb7565b52016115a5565b611640919250610f94565b5190565b6000906001825b8183101561171557600191501b805b6116c557506116665790565b60405162461bcd60e51b815260206004820152603160248201527f6d6d7253697a652063616e2774206265206173736f636961746564207769746860448201527020612076616c6964204d4d522073697a6560781b6064820152608490fd5b600181901b6001600160ff1b0382168203610ce1576000198101908111610ce157828111156116f9575b5060011c8061165a565b6117088261170e939495610cd4565b93610ea6565b90386116ef565b6001810180911161172a579060011b9061164b565b634e487b7160e01b84526011600452602484fdfea26469706673582212206273970c85e8a1e2a7fbb57ee6f0b8cfc0aa4a394f182e37c203bd6fb36a502564736f6c6343000818003300000000000000000000000029777b5ff1641c9461a2a17a03c35b14abd1e8f4
Deployed Bytecode
0x6040608081526004908136101561001557600080fd5b600091823560e01c9081630181e56c14610b71578163170deac414610b3d578163255b46a314610b165781633687321c14610af757816339200ec714610acf578163431cc8e414610acf57816359ab5e6514610a135781637b2745e0146109cf578163c31cd7ea146102fe578163ef0cfef414610273578163fba788f3146100d8575063fc50b6f3146100a757600080fd5b346100d457806020926100b936610c4b565b90825260028552600183832001908252845220549051908152f35b5080fd5b8383346100d45760c03660031901126100d45767ffffffffffffffff906024359084359060643560843585811161026f576101169036908901610c66565b60a43587811161026b57610174878a969594936101398d61017e95369101610c66565b9390958a8a5261016c602097600289528a60019c8d828220018882528b52205496610165881515610d06565b3691610eb3565b923691610eb3565b9086604435611066565b835496848801809811610258578351908282019081118282101761024557918960a09896949286947f0df6045decc2e38e91256e3df7519e307047e01f7c1e2741b3cb829cee6c53739b9997525282518181019085825283858201528481526101e681610d4b565b5190209161020684516101f881610d7d565b868152833681830137610f94565b52868a526002815283838b20818155818c5201815281838b205561022a8454610cf7565b8455825196875286015284015260608301526080820152a180f35b634e487b7160e01b8a5260418b5260248afd5b634e487b7160e01b895260118a52602489fd5b8880fd5b8680fd5b919050346102fa57806003193601126102fa577f739ec7da8ecc6bc018d914fc832fc7e42032312537324ff54309f204e056eed691356024356102e0337f00000000000000000000000029777b5ff1641c9461a2a17a03c35b14abd1e8f46001600160a01b031614610c97565b81855284602052808386205582519182526020820152a180f35b8280fd5b839150346100d45760803660031901126100d45780359283151584036102fa576024938435916044359367ffffffffffffffff9283861161026f573660238701121561026f5785820135978489116109cb5780870196818a36920101116109cb5760643585811161026b576103769036908501610c66565b979099881561099757878a5260209360028552610397878c20541515610d06565b156107bb576103b4916103ab913691610dd7565b99883691610e26565b9489518a019060808b85840193031261061357838b015191868c01518281116107b75781866103e5928f0101610f33565b9160608d01518181116107335782878f92610401930101610f33565b9c608081015191821161073357019181603f840112156107b7578583015161042881610dbb565b936104358a519586610d99565b818552878501938a83830101116106d95783928f918f93610481968d61045b9301611017565b8c8352600289528a8320968b8854946001809a878352018c522054948751902090611066565b855163726ab4ef60e01b808252735edd46819ccfea2d6ea227e5b2d93c1f8dce6f1a93918681806104b4878c830161103a565b0381885af490811561073e578d9161078a575b506104d18a610f94565b5187815191012003610748576104fd866104eb8b51610f01565b948a51809381928683528c830161103a565b0381885af490811561073e57908392918e91610707575b5090915061052184610f94565b526001825b610628575b50505061054c91610544896105679a9b9c9d87946111a9565b939098610f94565b5190875180809b81946310862ffb60e11b83528a830161103a565b03915af496871561061e578a976105e9575b50610588909596985b89610ea6565b92600184018094116105d857897f590026708fa6cd340ca0408a29735869140bd2e27b0c904eb485065224b2746b60a08b8b8b8b8b8b8b825196875286015284015260608301526080820152a180f35b634e487b7160e01b8a526011905288fd5b9096508281813d8311610617575b6106018183610d99565b81010312610613575195610588610579565b8980fd5b503d6105f7565b85513d8c823e3d90fd5b89518110156107025760001981018181116106f0578761064b61065d928d610fb7565b518b51809381928783528d830161103a565b0381895af49081156106e457849392918f918d916106a8575b50906106958161068885889695610fb7565b518c815191012014610fcb565b61069f8288610fb7565b52019091610526565b9293945050508781813d83116106dd575b6106c38183610d99565b810103126106d95751839291908b610695610676565b8d80fd5b503d6106b9565b8e8b51903d90823e3d90fd5b634e487b7160e01b8e5260118952868efd5b61052b565b80929350888092503d8311610737575b6107218183610d99565b8101031261073357908291518f610514565b8c80fd5b503d610717565b89513d8f823e3d90fd5b875162461bcd60e51b8152808801879052601b818701527f4552525f4e4f4e5f434f4e53454355544956455f454c454d454e5400000000006044820152606490fd5b90508681813d83116107b0575b6107a18183610d99565b8101031261073357518e6104c7565b503d610797565b8b80fd5b6107de916107d5919b98999a9b9793949596973691610dd7565b96893691610e26565b9086518701968681890312610993578481015197878201519283116107b75761080d9286809201920101610f33565b906001808801808911610981578b528a8552868b20548015610947579080838d9361083a8d979651610f01565b9490735edd46819ccfea2d6ea227e5b2d93c1f8dce6f1a935b610878575b50505050509161086f9161058897969594936111a9565b95909698610582565b90919293949596835183101561093f57506108a4816108978486610fb7565b518b815191012014610fcb565b6108ae8287610fb7565b528d6108d9898c8c6108c08688610fb7565b51915163726ab4ef60e01b81529384928392830161103a565b0381885af4918215610934579086959493926108fe575b508d97969591850191610853565b8095508a8092503d831161092d575b6109178183610d99565b810103126109295784809451906108f0565b8e80fd5b503d61090d565b8c51903d90823e3d90fd5b969594610858565b875162461bcd60e51b81528088018790526015818701527408aa4a4be9c9ebea48a8c8aa48a9c868abe9082a69605b1b6044820152606490fd5b634e487b7160e01b8c5260118752848cfd5b8a80fd5b855162461bcd60e51b8152602081870152600f818501526e08aa4a4be8a9aa0a8b2be8482a8869608b1b6044820152606490fd5b8780fd5b5050346100d457816003193601126100d457517f00000000000000000000000029777b5ff1641c9461a2a17a03c35b14abd1e8f46001600160a01b03168152602090f35b8383346100d457610a2336610c4b565b908084526002602052600192838186200183865260205280852054610a49811515610d06565b845494808601809611610abc5791859391608095937ff74fc47bc0ff3885b5fd3cdbee5c8fac996c70b2f2c8a83f916c28ae8901f4b4978952600260205280838a20868155868b520160205281838a2055610aa48154610cf7565b9055815194855260208501528301526060820152a180f35b634e487b7160e01b875260118852602487fd5b9050346102fa5760203660031901126102fa5760209282913581526002845220549051908152f35b5050346100d457816003193601126100d4576020906001549051908152f35b9050346102fa5760203660031901126102fa57602092829135815280845220549051908152f35b9050346102fa5760203660031901126102fa5760209282913581526002845260018282208054835201845220549051908152f35b8383346100d45760603660031901126100d45760243590833590610bbf337f00000000000000000000000029777b5ff1641c9461a2a17a03c35b14abd1e8f46001600160a01b031614610c97565b6001549260018401809411610c38579183916080937f42f9954f84d101195747fd02416d3fa26cb9bbc0d5b479d4bffc33b7e442345a95875260026020526001828820828155828952016020528282882055610c1c600154610cf7565b600155815193845260208401528201526044356060820152a180f35b634e487b7160e01b855260118652602485fd5b6040906003190112610c61576004359060243590565b600080fd5b9181601f84011215610c615782359167ffffffffffffffff8311610c61576020808501948460051b010111610c6157565b15610c9e57565b60405162461bcd60e51b815260206004820152600e60248201526d08aa4a4be9e9c98b2be929c849eb60931b6044820152606490fd5b91908201809211610ce157565b634e487b7160e01b600052601160045260246000fd5b6000198114610ce15760010190565b15610d0d57565b60405162461bcd60e51b815260206004820152601660248201527511549497d3535497d113d154d7d393d517d1561254d560521b6044820152606490fd5b6060810190811067ffffffffffffffff821117610d6757604052565b634e487b7160e01b600052604160045260246000fd5b6040810190811067ffffffffffffffff821117610d6757604052565b90601f8019910116810190811067ffffffffffffffff821117610d6757604052565b67ffffffffffffffff8111610d6757601f01601f191660200190565b929192610de382610dbb565b91610df16040519384610d99565b829481845281830111610c61578281602093846000960137010152565b67ffffffffffffffff8111610d675760051b60200190565b92919092610e3384610e0e565b91610e416040519384610d99565b829480845260208094019060051b830192828411610c615780915b848310610e6b57505050505050565b823567ffffffffffffffff8111610c6157820184601f82011215610c61578691610e9b8683858095359101610dd7565b815201920191610e5c565b91908203918211610ce157565b9291610ebe82610e0e565b91610ecc6040519384610d99565b829481845260208094019160051b8101928311610c6157905b828210610ef25750505050565b81358152908301908301610ee5565b90610f0b82610e0e565b610f186040519182610d99565b8281528092610f29601f1991610e0e565b0190602036910137565b9080601f83011215610c6157815190602091610f4e81610e0e565b93610f5c6040519586610d99565b81855260208086019260051b820101928311610c6157602001905b828210610f85575050505090565b81518152908301908301610f77565b805115610fa15760200190565b634e487b7160e01b600052603260045260246000fd5b8051821015610fa15760209160051b010190565b15610fd257565b60405162461bcd60e51b815260206004820152601960248201527f4552525f494e56414c49445f434841494e5f454c454d454e54000000000000006044820152606490fd5b60005b83811061102a5750506000910152565b818101518382015260200161101a565b6040916020825261105a8151809281602086015260208686019101611017565b601f01601f1916010190565b959394909192948087116111975761107e9086611245565b036111855760009490916000198201828111611171576110a390969396949294611644565b9183945b8251861015611147576110ba8684610fb7565b51600198858a168a0361110b576040805191602083019384528183015281526110e281610d4b565b51902093888201809211610ce1576110fd6001929596610cf7565b981c950194929693966110a7565b6111406110fd91600194979360408051916020830193845281830152815261113281610d4b565b519020966002891b90610cd4565b9596610cf7565b509592505050611158929150611271565b1561115f57565b6040516309bde33960e01b8152600490fd5b634e487b7160e01b87526011600452602487fd5b60405163504570e360e01b8152600490fd5b604051634e23d03560e01b8152600490fd5b92909160009080825260026020526040822080549560018092888652016020528160408520549685935b611205575b50505050818160409287945260026020526001838320018483526020528583832055815260026020522055565b829792989193985189101561123a578392839261122c926112268c8c610fb7565b516112a6565b9199909892019391926111d3565b8198508297506111d8565b61124e90611525565b604051906020820192835260408201526040815261126b81610d4b565b51902090565b60005b825181101561129e57816112888285610fb7565b511461129657600101611274565b505050600190565b505050600090565b9091939260019485810191828211610ce15781156114d7576112c782611644565b91600083805b6114b757508651036114a5576112e39086611245565b0361118557835192868401809411610ce1576112ff8794610f01565b946000975b61146b575b506113178596978596610fb7565b526000925b611438575b5083516000199390848101818111610ce15761133e859188610fb7565b519260005b8281106113e8575061135d6113588385610ea6565b610f01565b976000825b61139f575b50505061137391610ea6565b938401938411610ce15761138a6113909486610fb7565b52610cd4565b9161139b8383611245565b9190565b9091926113ac9085610ea6565b888101908111610ce15787908210156113e257509082826113ce829484610fb7565b516113d9828d610fb7565b52018793611362565b92611367565b939091506113f68484610ea6565b600119810191908211610ce15761140e83928a610fb7565b519060408051916020830193845281830152815261142b81610d4b565b5190209301908591611343565b9182151580611460575b1561145a57838101809111610ce15791831c8361131c565b91611321565b508380841614611442565b80969594965188101561149c578688611489829a849a98999a610fb7565b51611494828a610fb7565b520197611304565b95939495611309565b604051638355f42560e01b8152600490fd5b906000198201828111610ce1576114d19216918291610cf7565b916112cd565b505090939291506040516020810190848252856040820152604081526114fc81610d4b565b519020936040519361150d85610d7d565b84526020368186013761151f84610f94565b52929190565b9060019160018151106114a5576001815114611635578051926001198401848111610ce1576115548184610fb7565b516000198601868111610ce15761156b9085610fb7565b51916040918251602094602082019283528482015283815261158c81610d4b565b5190209661159982610f01565b95600219820191821160005b84811061160957505050505082939495946000945b6115c7575b505050505090565b8051851015611604578385966115de829784610fb7565b5190845190868201928352858201528481526115f981610d4b565b5190209601946115ba565b6115bf565b81610ce1578061162361161d8a9387610ea6565b85610fb7565b5161162e828c610fb7565b52016115a5565b611640919250610f94565b5190565b6000906001825b8183101561171557600191501b805b6116c557506116665790565b60405162461bcd60e51b815260206004820152603160248201527f6d6d7253697a652063616e2774206265206173736f636961746564207769746860448201527020612076616c6964204d4d522073697a6560781b6064820152608490fd5b600181901b6001600160ff1b0382168203610ce1576000198101908111610ce157828111156116f9575b5060011c8061165a565b6117088261170e939495610cd4565b93610ea6565b90386116ef565b6001810180911161172a579060011b9061164b565b634e487b7160e01b84526011600452602484fdfea26469706673582212206273970c85e8a1e2a7fbb57ee6f0b8cfc0aa4a394f182e37c203bd6fb36a502564736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000029777b5ff1641c9461a2a17a03c35b14abd1e8f4
-----Decoded View---------------
Arg [0] : _messagesInboxAddr (address): 0x29777B5fF1641c9461a2A17A03c35B14abd1e8F4
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000029777b5ff1641c9461a2a17a03c35b14abd1e8f4
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.