@kleros/vea-contracts
Version:
Smart contracts for Vea
451 lines (450 loc) • 49.1 kB
JSON
{
"address": "0xc0804E4FcEEfD958050356A429DAaaA71aA39385",
"abi": [
{
"inputs": [
{
"internalType": "uint256",
"name": "_epochPeriod",
"type": "uint256"
},
{
"internalType": "address",
"name": "_routerGnosisToArb",
"type": "address"
},
{
"internalType": "contract IAMB",
"name": "_amb",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes",
"name": "_nodeData",
"type": "bytes"
}
],
"name": "MessageSent",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes32",
"name": "_snapshot",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_epoch",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint64",
"name": "_count",
"type": "uint64"
}
],
"name": "SnapshotSaved",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "_epochSent",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "_ticketId",
"type": "bytes32"
}
],
"name": "SnapshotSent",
"type": "event"
},
{
"inputs": [],
"name": "amb",
"outputs": [
{
"internalType": "contract IAMB",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "count",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_timestamp",
"type": "uint256"
}
],
"name": "epochAt",
"outputs": [
{
"internalType": "uint256",
"name": "epoch",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "epochFinalized",
"outputs": [
{
"internalType": "uint256",
"name": "epoch",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "epochNow",
"outputs": [
{
"internalType": "uint256",
"name": "epoch",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "epochPeriod",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "routerGnosisToArb",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "saveSnapshot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "bytes4",
"name": "_fnSelector",
"type": "bytes4"
},
{
"internalType": "bytes",
"name": "_data",
"type": "bytes"
}
],
"name": "sendMessage",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_epoch",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_inboxIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_maxSubmissionCost",
"type": "uint256"
},
{
"internalType": "address",
"name": "_excessFeeRefundAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "_gasLimit",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_maxFeePerGas",
"type": "uint256"
}
],
"name": "sendSnapshot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "snapshots",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
}
],
"transactionHash": "0x998c4f6ddfab1e8ad2fc22782c2dd772df2da39b78371f280030474e7e5987fd",
"receipt": {
"to": null,
"from": "0x407DDEC61d69b212498B140a26da70AE1C879f5a",
"contractAddress": "0xc0804E4FcEEfD958050356A429DAaaA71aA39385",
"transactionIndex": 1,
"gasUsed": "665312",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0xe56be25709b6fa265d73a0f589c3fc0e76f7016b2a25dc34cbacf5a8ed3724fa",
"transactionHash": "0x998c4f6ddfab1e8ad2fc22782c2dd772df2da39b78371f280030474e7e5987fd",
"logs": [],
"blockNumber": 4814093,
"cumulativeGasUsed": "694400",
"status": 1,
"byzantium": true
},
"args": [
1800,
"0xE14fA0B3910CB0853E811375B9a6fcEEE32db521",
"0x99Ca51a3534785ED619f46A79C7Ad65Fa8d85e7a"
],
"numDeployments": 10,
"solcInputHash": "76a6a9e6371fade9c8f1066bebbf4e7c",
"metadata": "{\"compiler\":{\"version\":\"0.8.18+commit.87f61d96\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_epochPeriod\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_routerGnosisToArb\",\"type\":\"address\"},{\"internalType\":\"contract IAMB\",\"name\":\"_amb\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"_nodeData\",\"type\":\"bytes\"}],\"name\":\"MessageSent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"_snapshot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_epoch\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"_count\",\"type\":\"uint64\"}],\"name\":\"SnapshotSaved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_epochSent\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"_ticketId\",\"type\":\"bytes32\"}],\"name\":\"SnapshotSent\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"amb\",\"outputs\":[{\"internalType\":\"contract IAMB\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"count\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_timestamp\",\"type\":\"uint256\"}],\"name\":\"epochAt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"epoch\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"epochFinalized\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"epoch\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"epochNow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"epoch\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"epochPeriod\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"routerGnosisToArb\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"saveSnapshot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"bytes4\",\"name\":\"_fnSelector\",\"type\":\"bytes4\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"sendMessage\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_epoch\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_inboxIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_maxSubmissionCost\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_excessFeeRefundAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_maxFeePerGas\",\"type\":\"uint256\"}],\"name\":\"sendSnapshot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"snapshots\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Vea Inbox From Gnosis to Arbitrum. Note: This contract is deployed on the Gnosis.\",\"events\":{\"MessageSent(bytes)\":{\"details\":\"Relayers watch for these events to construct merkle proofs to execute transactions on Ethereum.\",\"params\":{\"_nodeData\":\"The data to create leaves in the merkle tree. abi.encodePacked(msgId, to, message), outbox relays to.call(message).\"}},\"SnapshotSaved(bytes32,uint256,uint64)\":{\"params\":{\"_count\":\"The count of messages in the merkle tree.\",\"_epoch\":\"The epoch of the snapshot.\",\"_snapshot\":\"The snapshot of the merkle tree state root.\"}},\"SnapshotSent(uint256,bytes32)\":{\"details\":\"The event is emitted when a snapshot is sent through the canonical arbitrum bridge.\",\"params\":{\"_epochSent\":\"The epoch of the snapshot.\",\"_ticketId\":\"The ticketId of the L2->L1 message.\"}}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"details\":\"Constructor. Note: epochPeriod must match the VeaOutboxGnosisToArb contract deployment on Gnosis, since it's on a different chain, we can't read it and trust the deployer to set a correct value\",\"params\":{\"_amb\":\"The address of the AMB contract on Gnosis.\",\"_epochPeriod\":\"The duration in seconds between epochs.\",\"_routerGnosisToArb\":\"The router on Ethereum that routes from Gnosis to Arbitrum.\"}},\"epochAt(uint256)\":{\"details\":\"Get the epoch from the inbox's point of view using timestamp.\",\"params\":{\"_timestamp\":\"The timestamp to calculate the epoch from.\"},\"returns\":{\"epoch\":\"The calculated epoch.\"}},\"epochFinalized()\":{\"details\":\"Get the most recent epoch for which snapshots are finalized.\",\"returns\":{\"epoch\":\"The epoch associated with the current inbox block.timestamp\"}},\"epochNow()\":{\"details\":\"Get the current epoch from the inbox's point of view using the Arbitrum L2 clock.\",\"returns\":{\"epoch\":\"The epoch associated with the current inbox block.timestamp\"}},\"saveSnapshot()\":{\"details\":\"Saves snapshot of state root. Snapshots can be saved a maximum of once per epoch. `O(log(count))` where count number of messages in the inbox. Note: See merkle tree docs for details how inbox manages state.\"},\"sendMessage(address,bytes4,bytes)\":{\"details\":\"Sends an arbitrary message to Gnosis. `O(log(count))` where count is the number of messages already sent. Amortized cost is constant. Note: See docs for details how inbox manages merkle tree state.\",\"params\":{\"_data\":\"The message calldata, abi.encode(param1, param2, ...)\",\"_fnSelector\":\"The function selector of the receiving contract.\",\"_to\":\"The address of the contract on the receiving chain which receives the calldata.\"},\"returns\":{\"_0\":\"msgId The zero based index of the message in the inbox.\"}},\"sendSnapshot(uint256,uint256,uint256,address,uint256,uint256)\":{\"details\":\"Sends the state root snapshot using Arbitrum's canonical bridge.\",\"params\":{\"_epoch\":\"The epoch of the snapshot requested to send.\",\"_excessFeeRefundAddress\":\"Address to refund any excess fee to\",\"_gasLimit\":\"Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)\",\"_inboxIndex\":\"The index of the inbox in the Arbitrum bridge contract.\",\"_maxFeePerGas\":\"price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)\",\"_maxSubmissionCost\":\"Max gas deducted from user's L2 balance to cover base submission fee\"}}},\"version\":1},\"userdoc\":{\"events\":{\"SnapshotSaved(bytes32,uint256,uint64)\":{\"notice\":\"The bridgers can watch this event to claim the stateRoot on the veaOutbox.\"}},\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/gnosisToArbitrum/VeaInboxGnosisToArb.sol\":\"VeaInboxGnosisToArb\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"src/canonical/gnosis-chain/IAMB.sol\":{\"content\":\"// https://docs.gnosischain.com/bridges/tokenbridge/amb-bridge#gnosis\\n// https://github.com/omni/tokenbridge-contracts/blob/908a48107919d4ab127f9af07d44d47eac91547e/contracts/interfaces/IAMB.sol\\n// interface is pruned for relevant function stubs\\n\\npragma solidity 0.8.18;\\n\\ninterface IAMB {\\n function requireToPassMessage(address _contract, bytes memory _data, uint256 _gas) external returns (bytes32);\\n\\n function maxGasPerTx() external view returns (uint256);\\n\\n function messageSender() external view returns (address);\\n\\n function messageSourceChainId() external view returns (bytes32);\\n}\\n\",\"keccak256\":\"0x529d45e46ca0c046c32b296b35ae8073c4a999ef37510545ec44d26e78deca0d\"},\"src/gnosisToArbitrum/VeaInboxGnosisToArb.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\n/// @custom:authors: [@jaybuidl, @shotaronowhere]\\n/// @custom:reviewers: []\\n/// @custom:auditors: []\\n/// @custom:bounties: []\\n/// @custom:deployments: []\\n\\npragma solidity 0.8.18;\\n\\nimport \\\"../canonical/gnosis-chain/IAMB.sol\\\";\\nimport \\\"../interfaces/inboxes/IVeaInbox.sol\\\";\\nimport \\\"../interfaces/routers/IRouterToArb.sol\\\";\\n\\n/// @dev Vea Inbox From Gnosis to Arbitrum.\\n/// Note: This contract is deployed on the Gnosis.\\ncontract VeaInboxGnosisToArb is IVeaInbox {\\n // ************************************* //\\n // * Storage * //\\n // ************************************* //\\n\\n IAMB public immutable amb; // The address of the AMB contract on Gnosis.\\n\\n uint256 public immutable epochPeriod; // Epochs mark the period between potential snapshots.\\n address public immutable routerGnosisToArb; // The router on Ethereum.\\n\\n mapping(uint256 => bytes32) public snapshots; // epoch => state root snapshot\\n\\n // Inbox represents minimum data availability to maintain incremental merkle tree.\\n // Supports a max of 2^64 - 1 messages. See merkle tree docs for details how inbox manages state.\\n\\n bytes32[64] internal inbox; // stores minimal set of complete subtree roots of the merkle tree to increment.\\n uint64 public count; // count of messages in the merkle tree\\n\\n // ************************************* //\\n // * Events * //\\n // ************************************* //\\n\\n /// @dev Relayers watch for these events to construct merkle proofs to execute transactions on Ethereum.\\n /// @param _nodeData The data to create leaves in the merkle tree. abi.encodePacked(msgId, to, message), outbox relays to.call(message).\\n event MessageSent(bytes _nodeData);\\n\\n /// The bridgers can watch this event to claim the stateRoot on the veaOutbox.\\n /// @param _snapshot The snapshot of the merkle tree state root.\\n /// @param _epoch The epoch of the snapshot.\\n /// @param _count The count of messages in the merkle tree.\\n event SnapshotSaved(bytes32 _snapshot, uint256 _epoch, uint64 _count);\\n\\n /// @dev The event is emitted when a snapshot is sent through the canonical arbitrum bridge.\\n /// @param _epochSent The epoch of the snapshot.\\n /// @param _ticketId The ticketId of the L2->L1 message.\\n event SnapshotSent(uint256 indexed _epochSent, bytes32 _ticketId);\\n\\n /// @dev Constructor.\\n /// Note: epochPeriod must match the VeaOutboxGnosisToArb contract deployment on Gnosis, since it's on a different chain, we can't read it and trust the deployer to set a correct value\\n /// @param _epochPeriod The duration in seconds between epochs.\\n /// @param _routerGnosisToArb The router on Ethereum that routes from Gnosis to Arbitrum.\\n /// @param _amb The address of the AMB contract on Gnosis.\\n constructor(uint256 _epochPeriod, address _routerGnosisToArb, IAMB _amb) {\\n epochPeriod = _epochPeriod;\\n routerGnosisToArb = _routerGnosisToArb;\\n amb = _amb;\\n }\\n\\n // ************************************* //\\n // * State Modifiers * //\\n // ************************************* //\\n\\n /// @dev Sends an arbitrary message to Gnosis.\\n /// `O(log(count))` where count is the number of messages already sent.\\n /// Amortized cost is constant.\\n /// Note: See docs for details how inbox manages merkle tree state.\\n /// @param _to The address of the contract on the receiving chain which receives the calldata.\\n /// @param _fnSelector The function selector of the receiving contract.\\n /// @param _data The message calldata, abi.encode(param1, param2, ...)\\n /// @return msgId The zero based index of the message in the inbox.\\n function sendMessage(address _to, bytes4 _fnSelector, bytes memory _data) external override returns (uint64) {\\n uint64 oldCount = count;\\n\\n // Given arbitrum's speed limit of 7 million gas / second, it would take atleast 8 million years of full blocks to overflow.\\n // It *should* be impossible to overflow, but we check to be safe when appending to the tree.\\n require(oldCount < type(uint64).max, \\\"Inbox is full.\\\");\\n\\n bytes memory nodeData = abi.encodePacked(\\n oldCount,\\n _to,\\n // _data is abi.encode(param1, param2, ...), we need to encode it again to get the correct leaf data\\n abi.encodePacked( // equivalent to abi.encodeWithSelector(fnSelector, msg.sender, param1, param2, ...)\\n _fnSelector,\\n bytes32(uint256(uint160(msg.sender))), // big endian padded encoding of msg.sender, simulating abi.encodeWithSelector\\n _data\\n )\\n );\\n\\n // single hashed leaf\\n bytes32 newInboxNode = keccak256(nodeData);\\n\\n // double hashed leaf\\n // avoids second order preimage attacks\\n // https://flawed.net.nz/2018/02/21/attacking-merkle-trees-with-a-second-preimage-attack/\\n assembly {\\n // efficient hash using EVM scratch space\\n mstore(0x00, newInboxNode)\\n newInboxNode := keccak256(0x00, 0x20)\\n }\\n\\n // increment merkle tree calculating minimal number of hashes\\n unchecked {\\n uint256 height;\\n\\n // x = oldCount + 1; acts as a bit mask to determine if a hash is needed\\n // note: x is always non-zero, and x is bit shifted to the right each loop\\n // hence this loop will always terminate in a maximum of log_2(oldCount + 1) iterations\\n for (uint64 x = oldCount + 1; x & 1 == 0; x = x >> 1) {\\n // sort sibling hashes as a convention for efficient proof validation\\n newInboxNode = sortConcatAndHash(inbox[height], newInboxNode);\\n height++;\\n }\\n\\n inbox[height] = newInboxNode;\\n\\n // finally increment count\\n count = oldCount + 1;\\n }\\n\\n emit MessageSent(nodeData);\\n\\n // old count is the zero indexed leaf position in the tree, acts as a msgId\\n // gateways should index these msgIds to later relay proofs\\n return oldCount;\\n }\\n\\n /// @dev Saves snapshot of state root. Snapshots can be saved a maximum of once per epoch.\\n /// `O(log(count))` where count number of messages in the inbox.\\n /// Note: See merkle tree docs for details how inbox manages state.\\n function saveSnapshot() external {\\n uint256 epoch;\\n bytes32 stateRoot;\\n\\n unchecked {\\n epoch = block.timestamp / epochPeriod;\\n\\n // calculate the current root of the incremental merkle tree encoded in the inbox\\n\\n uint256 height;\\n\\n // x acts as a bit mask to determine if the hash stored in the inbox contributes to the root\\n uint256 x;\\n\\n // x is bit shifted to the right each loop, hence this loop will always terminate in a maximum of log_2(count) iterations\\n for (x = uint256(count); x > 0; x = x >> 1) {\\n if ((x & 1) == 1) {\\n // first hash is special case\\n // inbox stores the root of complete subtrees\\n // eg if count = 4 = 0b100, then the first complete subtree is inbox[2]\\n // inbox = [H(3), H(1,2), H(1,4)], we read inbox[2] directly\\n\\n stateRoot = inbox[height];\\n break;\\n }\\n height++;\\n }\\n\\n // after the first hash, we can calculate the root incrementally\\n for (x = x >> 1; x > 0; x = x >> 1) {\\n height++;\\n if ((x & 1) == 1) {\\n // sort sibling hashes as a convention for efficient proof validation\\n stateRoot = sortConcatAndHash(inbox[height], stateRoot);\\n }\\n }\\n }\\n\\n snapshots[epoch] = stateRoot;\\n\\n emit SnapshotSaved(stateRoot, epoch, count);\\n }\\n\\n /// @dev Helper function to calculate merkle tree interior nodes by sorting and concatenating and hashing a pair of children nodes, left and right.\\n /// Note: EVM scratch space is used to efficiently calculate hashes.\\n /// @param _left The left hash.\\n /// @param _right The right hash.\\n /// @return parent The parent hash.\\n function sortConcatAndHash(bytes32 _left, bytes32 _right) internal pure returns (bytes32 parent) {\\n // sort sibling hashes as a convention for efficient proof validation\\n if (_left < _right) {\\n // efficient hash using EVM scratch space\\n assembly {\\n mstore(0x00, _left)\\n mstore(0x20, _right)\\n parent := keccak256(0x00, 0x40)\\n }\\n } else {\\n assembly {\\n mstore(0x00, _right)\\n mstore(0x20, _left)\\n parent := keccak256(0x00, 0x40)\\n }\\n }\\n }\\n\\n /// @dev Sends the state root snapshot using Arbitrum's canonical bridge.\\n /// @param _epoch The epoch of the snapshot requested to send.\\n /// @param _inboxIndex The index of the inbox in the Arbitrum bridge contract.\\n /// @param _maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee\\n /// @param _excessFeeRefundAddress Address to refund any excess fee to\\n /// @param _gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)\\n /// @param _maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)\\n function sendSnapshot(\\n uint256 _epoch,\\n uint256 _inboxIndex,\\n uint256 _maxSubmissionCost,\\n address _excessFeeRefundAddress,\\n uint256 _gasLimit,\\n uint256 _maxFeePerGas\\n ) external virtual {\\n unchecked {\\n require(_epoch < block.timestamp / epochPeriod, \\\"Can only send past epoch snapshot.\\\");\\n }\\n\\n bytes memory data = abi.encodeCall(\\n IRouterToArb.route,\\n (\\n _epoch,\\n snapshots[_epoch],\\n _inboxIndex,\\n _maxSubmissionCost,\\n _excessFeeRefundAddress,\\n _gasLimit,\\n _maxFeePerGas\\n )\\n );\\n // Note: using maxGasPerTx here means the relaying txn on Gnosis will need to pass that (large) amount of gas, though almost all will be unused and refunded. This is preferred over hardcoding a gas limit.\\n bytes32 ticketID = amb.requireToPassMessage(routerGnosisToArb, data, amb.maxGasPerTx());\\n\\n emit SnapshotSent(_epoch, ticketID);\\n }\\n\\n // ************************************* //\\n // * Pure / Views * //\\n // ************************************* //\\n\\n /// @dev Get the current epoch from the inbox's point of view using the Arbitrum L2 clock.\\n /// @return epoch The epoch associated with the current inbox block.timestamp\\n function epochNow() external view returns (uint256 epoch) {\\n epoch = block.timestamp / epochPeriod;\\n }\\n\\n /// @dev Get the most recent epoch for which snapshots are finalized.\\n /// @return epoch The epoch associated with the current inbox block.timestamp\\n function epochFinalized() external view returns (uint256 epoch) {\\n epoch = block.timestamp / epochPeriod - 1;\\n }\\n\\n /// @dev Get the epoch from the inbox's point of view using timestamp.\\n /// @param _timestamp The timestamp to calculate the epoch from.\\n /// @return epoch The calculated epoch.\\n function epochAt(uint256 _timestamp) external view returns (uint256 epoch) {\\n epoch = _timestamp / epochPeriod;\\n }\\n}\\n\",\"keccak256\":\"0x0f6903fbd2d4f68f94851fa1325a6889fedb386ae9c270fefba850fbd9ce4da0\",\"license\":\"MIT\"},\"src/interfaces/inboxes/IVeaInbox.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\n/// @custom:authors: [@jaybuidl, @shotaronowhere]\\n/// @custom:reviewers: []\\n/// @custom:auditors: []\\n/// @custom:bounties: []\\n/// @custom:deployments: []\\n\\npragma solidity 0.8.18;\\n\\ninterface IVeaInbox {\\n /// @dev Sends an arbitrary message to receiving chain.\\n /// Note: Calls authenticated by receiving gateway checking the sender argument.\\n /// @param _to The cross-domain contract address which receives the calldata.\\n /// @param _fnSelection The function selector of the receiving contract.\\n /// @param _data The message calldata, abi.encode(...)\\n /// @return msgId The index of the message in the inbox, as a message Id, needed to relay the message.\\n function sendMessage(address _to, bytes4 _fnSelection, bytes memory _data) external returns (uint64 msgId);\\n\\n /// @dev Snapshots can be saved a maximum of once per epoch.\\n /// Saves snapshot of state root.\\n /// `O(log(count))` where count number of messages in the inbox.\\n function saveSnapshot() external;\\n}\\n\",\"keccak256\":\"0x053799bf55019a7f1db4cd889ce83cbe7319e832eec0234b1d4020a2aa0026f9\",\"license\":\"MIT\"},\"src/interfaces/routers/IRouterToArb.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\n/// @custom:authors: [@shotaronowhere]\\n/// @custom:reviewers: []\\n/// @custom:auditors: []\\n/// @custom:bounties: []\\n/// @custom:deployments: []\\n\\npragma solidity 0.8.18;\\n\\n/// @dev Interface of the Vea Router intended to be deployed on an intermediary chain which routes messages to Arbitrum where calldata is the primary cost.\\n/// eg. Gnosis (L1) -> Ethereum (L1) -> Arbitrum (L2), the IRouterToL2 will be deployed on Ethereum (L1) routing messages to Arbitrum (L2).\\ninterface IRouterToArb {\\n /// @dev Resolves any challenge of the optimistic claim for 'epoch' using the canonical bridge.\\n /// Note: Access restricted to canonical sending-chain bridge.\\n /// @param _epoch The epoch to verify.\\n /// @param _stateroot The true state root for the epoch.\\n /// @param _inboxIndex The index of the inbox in the Arbitrum bridge contract.\\n /// @param _maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee\\n /// @param _excessFeeRefundAddress Address to refund any excess fee to\\n /// @param _gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)\\n /// @param _maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)\\n function route(\\n uint256 _epoch,\\n bytes32 _stateroot,\\n uint256 _inboxIndex,\\n uint256 _maxSubmissionCost,\\n address _excessFeeRefundAddress,\\n uint256 _gasLimit,\\n uint256 _maxFeePerGas\\n ) external;\\n}\\n\",\"keccak256\":\"0xc265211871b748c9191df4fd6419d57abf030314ee5ef7aa89f14a863521f174\",\"license\":\"MIT\"}},\"version\":1}",
"bytecode": "0x60e060405234801561001057600080fd5b50604051610c12380380610c1283398101604081905261002f91610064565b60a0929092526001600160a01b0390811660c052166080526100a7565b6001600160a01b038116811461006157600080fd5b50565b60008060006060848603121561007957600080fd5b83519250602084015161008b8161004c565b604085015190925061009c8161004c565b809150509250925092565b60805160a05160c051610b0c610106600039600081816101cc015261036f015260008181610185015281816101f5015281816102210152818161048c015281816104c201526104f201526000818160e501526103460152610b0c6000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80634a439cfe116100715780634a439cfe1461015257806351920535146101655780635f85896c1461016d578063b5b7a18414610180578063d6565a2d146101a7578063eb8dedfa146101c757600080fd5b806306661abd146100ae5780631062b39a146100e0578063222ae7861461011f5780632bbb4704146101355780633ac3b6b61461014a575b600080fd5b6041546100c29067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020015b60405180910390f35b6101077f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100d7565b6101276101ee565b6040519081526020016100d7565b6101486101433660046107ca565b61021f565b005b610127610483565b61012761016036600461081b565b6104bb565b6101486104ed565b6100c261017b36600461084a565b610611565b6101277f000000000000000000000000000000000000000000000000000000000000000081565b6101276101b536600461081b565b60006020819052908152604090205481565b6101077f000000000000000000000000000000000000000000000000000000000000000081565b600061021a7f000000000000000000000000000000000000000000000000000000000000000042610941565b905090565b7f0000000000000000000000000000000000000000000000000000000000000000428161024e5761024e61092b565b0486106102ad5760405162461bcd60e51b815260206004820152602260248201527f43616e206f6e6c792073656e6420706173742065706f636820736e617073686f6044820152613a1760f11b60648201526084015b60405180910390fd5b60008681526020819052604080822054905160248101899052604481019190915260648101879052608481018690526001600160a01b03851660a482015260c4810184905260e481018390526101040160408051601f19818403018152918152602080830180516001600160e01b031663507b5ad360e01b179052815163e5789d0360e01b815291519293506000926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263dc8601b3927f0000000000000000000000000000000000000000000000000000000000000000928792869263e5789d03926004808401938290030181865afa1580156103b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103dc9190610963565b6040518463ffffffff1660e01b81526004016103fa939291906109cc565b6020604051808303816000875af1158015610419573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043d9190610963565b9050877f6fdd49f435101fc7b6ebdec7c8972932a926d18f6cb78a8891dfe950743b6b828260405161047191815260200190565b60405180910390a25050505050505050565b600060016104b17f000000000000000000000000000000000000000000000000000000000000000042610941565b61021a9190610a00565b60006104e77f000000000000000000000000000000000000000000000000000000000000000083610941565b92915050565b6000807f0000000000000000000000000000000000000000000000000000000000000000428161051f5761051f61092b565b604154919004925060009067ffffffffffffffff165b801561056e5780600116600103610562576001826040811061055957610559610a21565b0154925061056e565b6001918201911c610535565b60011c5b80156105b05760019182019181811690036105a8576105a56001836040811061059d5761059d610a21565b01548461077d565b92505b60011c610572565b505060008281526020818152604091829020839055604154825184815291820185905267ffffffffffffffff168183015290517f592424eb1d6135501bd20833f15fd127c29d08eed4f03872f6f75182126b1e489181900360600190a15050565b60415460009067ffffffffffffffff9081169081106106635760405162461bcd60e51b815260206004820152600e60248201526d24b73137bc1034b990333ab6361760911b60448201526064016102a4565b6040516000908290879061067f90889033908990602001610a37565b60408051601f198184030181529082905261069e939291602001610a6d565b60408051601f1981840301815291905280516020808301919091206000908152908120919250600184015b60018116600003610702576106ea6001836040811061059d5761059d610a21565b92506001918201911c677fffffffffffffff166106c9565b50816001826040811061071757610717610a21565b0155506041805467ffffffffffffffff19166001850167ffffffffffffffff161790556040517f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b0369061076a908490610abc565b60405180910390a1509095945050505050565b60008183101561079b578260005281602052604060002090506104e7565b5060009081526020919091526040902090565b80356001600160a01b03811681146107c557600080fd5b919050565b60008060008060008060c087890312156107e357600080fd5b863595506020870135945060408701359350610801606088016107ae565b92506080870135915060a087013590509295509295509295565b60006020828403121561082d57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561085f57600080fd5b610868846107ae565b925060208401356001600160e01b03198116811461088557600080fd5b9150604084013567ffffffffffffffff808211156108a257600080fd5b818601915086601f8301126108b657600080fd5b8135818111156108c8576108c8610834565b604051601f8201601f19908116603f011681019083821181831017156108f0576108f0610834565b8160405282815289602084870101111561090957600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b634e487b7160e01b600052601260045260246000fd5b60008261095e57634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561097557600080fd5b5051919050565b60005b8381101561099757818101518382015260200161097f565b50506000910152565b600081518084526109b881602086016020860161097c565b601f01601f19169290920160200192915050565b6001600160a01b03841681526060602082018190526000906109f0908301856109a0565b9050826040830152949350505050565b818103818111156104e757634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b63ffffffff60e01b8416815282600482015260008251610a5e81602485016020870161097c565b91909101602401949350505050565b67ffffffffffffffff60c01b8460c01b1681526bffffffffffffffffffffffff198360601b16600882015260008251610aad81601c85016020870161097c565b91909101601c01949350505050565b602081526000610acf60208301846109a0565b939250505056fea26469706673582212208f85936a0d5ffa471af8a38c879ca78b06fcc0561f714b8395bb25bd09df413f64736f6c63430008120033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a95760003560e01c80634a439cfe116100715780634a439cfe1461015257806351920535146101655780635f85896c1461016d578063b5b7a18414610180578063d6565a2d146101a7578063eb8dedfa146101c757600080fd5b806306661abd146100ae5780631062b39a146100e0578063222ae7861461011f5780632bbb4704146101355780633ac3b6b61461014a575b600080fd5b6041546100c29067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020015b60405180910390f35b6101077f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100d7565b6101276101ee565b6040519081526020016100d7565b6101486101433660046107ca565b61021f565b005b610127610483565b61012761016036600461081b565b6104bb565b6101486104ed565b6100c261017b36600461084a565b610611565b6101277f000000000000000000000000000000000000000000000000000000000000000081565b6101276101b536600461081b565b60006020819052908152604090205481565b6101077f000000000000000000000000000000000000000000000000000000000000000081565b600061021a7f000000000000000000000000000000000000000000000000000000000000000042610941565b905090565b7f0000000000000000000000000000000000000000000000000000000000000000428161024e5761024e61092b565b0486106102ad5760405162461bcd60e51b815260206004820152602260248201527f43616e206f6e6c792073656e6420706173742065706f636820736e617073686f6044820152613a1760f11b60648201526084015b60405180910390fd5b60008681526020819052604080822054905160248101899052604481019190915260648101879052608481018690526001600160a01b03851660a482015260c4810184905260e481018390526101040160408051601f19818403018152918152602080830180516001600160e01b031663507b5ad360e01b179052815163e5789d0360e01b815291519293506000926001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263dc8601b3927f0000000000000000000000000000000000000000000000000000000000000000928792869263e5789d03926004808401938290030181865afa1580156103b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103dc9190610963565b6040518463ffffffff1660e01b81526004016103fa939291906109cc565b6020604051808303816000875af1158015610419573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061043d9190610963565b9050877f6fdd49f435101fc7b6ebdec7c8972932a926d18f6cb78a8891dfe950743b6b828260405161047191815260200190565b60405180910390a25050505050505050565b600060016104b17f000000000000000000000000000000000000000000000000000000000000000042610941565b61021a9190610a00565b60006104e77f000000000000000000000000000000000000000000000000000000000000000083610941565b92915050565b6000807f0000000000000000000000000000000000000000000000000000000000000000428161051f5761051f61092b565b604154919004925060009067ffffffffffffffff165b801561056e5780600116600103610562576001826040811061055957610559610a21565b0154925061056e565b6001918201911c610535565b60011c5b80156105b05760019182019181811690036105a8576105a56001836040811061059d5761059d610a21565b01548461077d565b92505b60011c610572565b505060008281526020818152604091829020839055604154825184815291820185905267ffffffffffffffff168183015290517f592424eb1d6135501bd20833f15fd127c29d08eed4f03872f6f75182126b1e489181900360600190a15050565b60415460009067ffffffffffffffff9081169081106106635760405162461bcd60e51b815260206004820152600e60248201526d24b73137bc1034b990333ab6361760911b60448201526064016102a4565b6040516000908290879061067f90889033908990602001610a37565b60408051601f198184030181529082905261069e939291602001610a6d565b60408051601f1981840301815291905280516020808301919091206000908152908120919250600184015b60018116600003610702576106ea6001836040811061059d5761059d610a21565b92506001918201911c677fffffffffffffff166106c9565b50816001826040811061071757610717610a21565b0155506041805467ffffffffffffffff19166001850167ffffffffffffffff161790556040517f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b0369061076a908490610abc565b60405180910390a1509095945050505050565b60008183101561079b578260005281602052604060002090506104e7565b5060009081526020919091526040902090565b80356001600160a01b03811681146107c557600080fd5b919050565b60008060008060008060c087890312156107e357600080fd5b863595506020870135945060408701359350610801606088016107ae565b92506080870135915060a087013590509295509295509295565b60006020828403121561082d57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561085f57600080fd5b610868846107ae565b925060208401356001600160e01b03198116811461088557600080fd5b9150604084013567ffffffffffffffff808211156108a257600080fd5b818601915086601f8301126108b657600080fd5b8135818111156108c8576108c8610834565b604051601f8201601f19908116603f011681019083821181831017156108f0576108f0610834565b8160405282815289602084870101111561090957600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b634e487b7160e01b600052601260045260246000fd5b60008261095e57634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561097557600080fd5b5051919050565b60005b8381101561099757818101518382015260200161097f565b50506000910152565b600081518084526109b881602086016020860161097c565b601f01601f19169290920160200192915050565b6001600160a01b03841681526060602082018190526000906109f0908301856109a0565b9050826040830152949350505050565b818103818111156104e757634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b63ffffffff60e01b8416815282600482015260008251610a5e81602485016020870161097c565b91909101602401949350505050565b67ffffffffffffffff60c01b8460c01b1681526bffffffffffffffffffffffff198360601b16600882015260008251610aad81601c85016020870161097c565b91909101601c01949350505050565b602081526000610acf60208301846109a0565b939250505056fea26469706673582212208f85936a0d5ffa471af8a38c879ca78b06fcc0561f714b8395bb25bd09df413f64736f6c63430008120033",
"devdoc": {
"details": "Vea Inbox From Gnosis to Arbitrum. Note: This contract is deployed on the Gnosis.",
"events": {
"MessageSent(bytes)": {
"details": "Relayers watch for these events to construct merkle proofs to execute transactions on Ethereum.",
"params": {
"_nodeData": "The data to create leaves in the merkle tree. abi.encodePacked(msgId, to, message), outbox relays to.call(message)."
}
},
"SnapshotSaved(bytes32,uint256,uint64)": {
"params": {
"_count": "The count of messages in the merkle tree.",
"_epoch": "The epoch of the snapshot.",
"_snapshot": "The snapshot of the merkle tree state root."
}
},
"SnapshotSent(uint256,bytes32)": {
"details": "The event is emitted when a snapshot is sent through the canonical arbitrum bridge.",
"params": {
"_epochSent": "The epoch of the snapshot.",
"_ticketId": "The ticketId of the L2->L1 message."
}
}
},
"kind": "dev",
"methods": {
"constructor": {
"details": "Constructor. Note: epochPeriod must match the VeaOutboxGnosisToArb contract deployment on Gnosis, since it's on a different chain, we can't read it and trust the deployer to set a correct value",
"params": {
"_amb": "The address of the AMB contract on Gnosis.",
"_epochPeriod": "The duration in seconds between epochs.",
"_routerGnosisToArb": "The router on Ethereum that routes from Gnosis to Arbitrum."
}
},
"epochAt(uint256)": {
"details": "Get the epoch from the inbox's point of view using timestamp.",
"params": {
"_timestamp": "The timestamp to calculate the epoch from."
},
"returns": {
"epoch": "The calculated epoch."
}
},
"epochFinalized()": {
"details": "Get the most recent epoch for which snapshots are finalized.",
"returns": {
"epoch": "The epoch associated with the current inbox block.timestamp"
}
},
"epochNow()": {
"details": "Get the current epoch from the inbox's point of view using the Arbitrum L2 clock.",
"returns": {
"epoch": "The epoch associated with the current inbox block.timestamp"
}
},
"saveSnapshot()": {
"details": "Saves snapshot of state root. Snapshots can be saved a maximum of once per epoch. `O(log(count))` where count number of messages in the inbox. Note: See merkle tree docs for details how inbox manages state."
},
"sendMessage(address,bytes4,bytes)": {
"details": "Sends an arbitrary message to Gnosis. `O(log(count))` where count is the number of messages already sent. Amortized cost is constant. Note: See docs for details how inbox manages merkle tree state.",
"params": {
"_data": "The message calldata, abi.encode(param1, param2, ...)",
"_fnSelector": "The function selector of the receiving contract.",
"_to": "The address of the contract on the receiving chain which receives the calldata."
},
"returns": {
"_0": "msgId The zero based index of the message in the inbox."
}
},
"sendSnapshot(uint256,uint256,uint256,address,uint256,uint256)": {
"details": "Sends the state root snapshot using Arbitrum's canonical bridge.",
"params": {
"_epoch": "The epoch of the snapshot requested to send.",
"_excessFeeRefundAddress": "Address to refund any excess fee to",
"_gasLimit": "Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)",
"_inboxIndex": "The index of the inbox in the Arbitrum bridge contract.",
"_maxFeePerGas": "price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)",
"_maxSubmissionCost": "Max gas deducted from user's L2 balance to cover base submission fee"
}
}
},
"version": 1
},
"userdoc": {
"events": {
"SnapshotSaved(bytes32,uint256,uint64)": {
"notice": "The bridgers can watch this event to claim the stateRoot on the veaOutbox."
}
},
"kind": "user",
"methods": {},
"version": 1
},
"storageLayout": {
"storage": [
{
"astId": 880,
"contract": "src/gnosisToArbitrum/VeaInboxGnosisToArb.sol:VeaInboxGnosisToArb",
"label": "snapshots",
"offset": 0,
"slot": "0",
"type": "t_mapping(t_uint256,t_bytes32)"
},
{
"astId": 884,
"contract": "src/gnosisToArbitrum/VeaInboxGnosisToArb.sol:VeaInboxGnosisToArb",
"label": "inbox",
"offset": 0,
"slot": "1",
"type": "t_array(t_bytes32)64_storage"
},
{
"astId": 886,
"contract": "src/gnosisToArbitrum/VeaInboxGnosisToArb.sol:VeaInboxGnosisToArb",
"label": "count",
"offset": 0,
"slot": "65",
"type": "t_uint64"
}
],
"types": {
"t_array(t_bytes32)64_storage": {
"base": "t_bytes32",
"encoding": "inplace",
"label": "bytes32[64]",
"numberOfBytes": "2048"
},
"t_bytes32": {
"encoding": "inplace",
"label": "bytes32",
"numberOfBytes": "32"
},
"t_mapping(t_uint256,t_bytes32)": {
"encoding": "mapping",
"key": "t_uint256",
"label": "mapping(uint256 => bytes32)",
"numberOfBytes": "32",
"value": "t_bytes32"
},
"t_uint256": {
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32"
},
"t_uint64": {
"encoding": "inplace",
"label": "uint64",
"numberOfBytes": "8"
}
}
}
}