agents/contracts/test/pingpongclient/pingpongclient.contractinfo.json
{"solidity/PingPongClient.sol:IMessageRecipient":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.17;\n\n// contracts/interfaces/IMessageRecipient.sol\n\ninterface IMessageRecipient {\n /**\n * @notice Message recipient needs to implement this function in order to\n * receive cross-chain messages.\n * @dev Message recipient needs to ensure that merkle proof for the message\n * is at least as old as the optimistic period that the recipient is using.\n * Note: as this point it is checked that the \"message optimistic period\" has passed,\n * however the period value itself could be anything, and thus could differ from the one\n * that the recipient would like to enforce.\n * @param origin Domain where message originated\n * @param nonce Message nonce on the origin domain\n * @param sender Sender address on origin chain\n * @param proofMaturity Message's merkle proof age in seconds\n * @param version Message version specified by sender\n * @param content Raw bytes content of message\n */\n function receiveBaseMessage(\n uint32 origin,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable;\n}\n\n// contracts/interfaces/InterfaceOrigin.sol\n\ninterface InterfaceOrigin {\n // ═══════════════════════════════════════════════ SEND MESSAGES ═══════════════════════════════════════════════════\n\n /**\n * @notice Send a message to the recipient located on destination domain.\n * @dev Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered.\n * Will revert if any of these is true:\n * - `destination` is equal to contract's local domain\n * - `content` length is greater than `MAX_CONTENT_BYTES`\n * - `msg.value` is lower than value of minimum tips for the given message\n * @param destination Domain of destination chain\n * @param recipient Address of recipient on destination chain as bytes32\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param content Raw bytes content of message\n * @return messageNonce Nonce of the sent message\n * @return messageHash Hash of the sent message\n */\n function sendBaseMessage(\n uint32 destination,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 paddedRequest,\n bytes memory content\n ) external payable returns (uint32 messageNonce, bytes32 messageHash);\n\n /**\n * @notice Send a manager message to the destination domain.\n * @dev This could only be called by AgentManager, which takes care of encoding the calldata payload.\n * Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain\n * so that the AgentManager could verify where the Manager Message came from and how mature is the proof.\n * Note: function is not payable, as no tips are required for sending a manager message.\n * Will revert if `destination` is equal to contract's local domain.\n * @param destination Domain of destination chain\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param payload Payload for calling AgentManager on destination chain (with extra security args)\n */\n function sendManagerMessage(uint32 destination, uint32 optimisticPeriod, bytes memory payload)\n external\n returns (uint32 messageNonce, bytes32 messageHash);\n\n // ════════════════════════════════════════════════ TIPS LOGIC ═════════════════════════════════════════════════════\n\n /**\n * @notice Withdraws locked base message tips to the recipient.\n * @dev Could only be called by a local AgentManager.\n * @param recipient Address to withdraw tips to\n * @param amount Tips value to withdraw\n */\n function withdrawTips(address recipient, uint256 amount) external;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /**\n * @notice Returns the minimum tips value for sending a message to a given destination.\n * @dev Using at least `tipsValue` as `msg.value` for `sendBaseMessage()`\n * will guarantee that the message will be accepted.\n * @param destination Domain of destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param contentLength The length of the message content\n * @return tipsValue Minimum tips value for a message to be accepted\n */\n function getMinimumTipsValue(uint32 destination, uint256 paddedRequest, uint256 contentLength)\n external\n view\n returns (uint256 tipsValue);\n}\n\n// contracts/libs/Errors.sol\n\n// ══════════════════════════════ INVALID CALLER ═══════════════════════════════\n\nerror CallerNotAgentManager();\nerror CallerNotDestination();\nerror CallerNotInbox();\nerror CallerNotSummit();\n\n// ══════════════════════════════ INCORRECT DATA ═══════════════════════════════\n\nerror IncorrectAttestation();\nerror IncorrectAgentDomain();\nerror IncorrectAgentIndex();\nerror IncorrectAgentProof();\nerror IncorrectAgentRoot();\nerror IncorrectDataHash();\nerror IncorrectDestinationDomain();\nerror IncorrectOriginDomain();\nerror IncorrectSnapshotProof();\nerror IncorrectSnapshotRoot();\nerror IncorrectState();\nerror IncorrectStatesAmount();\nerror IncorrectTipsProof();\nerror IncorrectVersionLength();\n\nerror IncorrectNonce();\nerror IncorrectSender();\nerror IncorrectRecipient();\n\nerror FlagOutOfRange();\nerror IndexOutOfRange();\nerror NonceOutOfRange();\n\nerror OutdatedNonce();\n\nerror UnformattedAttestation();\nerror UnformattedAttestationReport();\nerror UnformattedBaseMessage();\nerror UnformattedCallData();\nerror UnformattedCallDataPrefix();\nerror UnformattedMessage();\nerror UnformattedReceipt();\nerror UnformattedReceiptReport();\nerror UnformattedSignature();\nerror UnformattedSnapshot();\nerror UnformattedState();\nerror UnformattedStateReport();\n\n// ═══════════════════════════════ MERKLE TREES ════════════════════════════════\n\nerror LeafNotProven();\nerror MerkleTreeFull();\nerror NotEnoughLeafs();\nerror TreeHeightTooLow();\n\n// ═════════════════════════════ OPTIMISTIC PERIOD ═════════════════════════════\n\nerror BaseClientOptimisticPeriod();\nerror MessageOptimisticPeriod();\nerror SlashAgentOptimisticPeriod();\nerror WithdrawTipsOptimisticPeriod();\nerror ZeroProofMaturity();\n\n// ═══════════════════════════════ AGENT MANAGER ═══════════════════════════════\n\nerror AgentNotGuard();\nerror AgentNotNotary();\n\nerror AgentCantBeAdded();\nerror AgentNotActive();\nerror AgentNotActiveNorUnstaking();\nerror AgentNotFraudulent();\nerror AgentNotUnstaking();\nerror AgentUnknown();\n\nerror AgentRootNotProposed();\nerror AgentRootTimeoutNotOver();\n\nerror NotStuck();\n\nerror DisputeAlreadyResolved();\nerror DisputeNotOpened();\nerror DisputeTimeoutNotOver();\nerror GuardInDispute();\nerror NotaryInDispute();\n\nerror MustBeSynapseDomain();\nerror SynapseDomainForbidden();\n\n// ════════════════════════════════ DESTINATION ════════════════════════════════\n\nerror AlreadyExecuted();\nerror AlreadyFailed();\nerror DuplicatedSnapshotRoot();\nerror IncorrectMagicValue();\nerror GasLimitTooLow();\nerror GasSuppliedTooLow();\n\n// ══════════════════════════════════ ORIGIN ═══════════════════════════════════\n\nerror ContentLengthTooBig();\nerror EthTransferFailed();\nerror InsufficientEthBalance();\n\n// ════════════════════════════════ GAS ORACLE ═════════════════════════════════\n\nerror LocalGasDataNotSet();\nerror RemoteGasDataNotSet();\n\n// ═══════════════════════════════════ TIPS ════════════════════════════════════\n\nerror SummitTipTooHigh();\nerror TipsClaimMoreThanEarned();\nerror TipsClaimZero();\nerror TipsOverflow();\nerror TipsValueTooLow();\n\n// ════════════════════════════════ MEMORY VIEW ════════════════════════════════\n\nerror IndexedTooMuch();\nerror ViewOverrun();\nerror OccupiedMemory();\nerror UnallocatedMemory();\nerror PrecompileOutOfGas();\n\n// ═════════════════════════════════ MULTICALL ═════════════════════════════════\n\nerror MulticallFailed();\n\n// contracts/libs/TypeCasts.sol\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n}\n\n// contracts/libs/stack/Request.sol\n\n/// Request is encoded data with \"message execution request\".\ntype Request is uint192;\n\nusing RequestLib for Request global;\n\n/// Library for formatting _the request part_ of _the base messages_.\n/// - Request represents a message sender requirements for the message execution on the destination chain.\n/// - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory.\n/// \u003e gasDrop field is included for future compatibility and is ignored at the moment.\n///\n/// # Request stack layout (from highest bits to lowest)\n///\n/// | Position | Field | Type | Bytes | Description |\n/// | ---------- | -------- | ------ | ----- | ---------------------------------------------------- |\n/// | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient |\n/// | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution |\n/// | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |\n\nlibrary RequestLib {\n /// @dev Amount of bits to shift to gasDrop field\n uint192 private constant SHIFT_GAS_DROP = 12 * 8;\n /// @dev Amount of bits to shift to gasLimit field\n uint192 private constant SHIFT_GAS_LIMIT = 4 * 8;\n\n /// @notice Returns an encoded request with the given fields\n /// @param gasDrop_ Minimum amount of gas token to drop to the recipient (ignored at the moment)\n /// @param gasLimit_ Minimum amount of gas units to supply for execution\n /// @param version_ Base message version to pass to the recipient\n function encodeRequest(uint96 gasDrop_, uint64 gasLimit_, uint32 version_) internal pure returns (Request) {\n // Casts below are upcasts, so they are safe\n return Request.wrap(uint192(gasDrop_) \u003c\u003c SHIFT_GAS_DROP | uint192(gasLimit_) \u003c\u003c SHIFT_GAS_LIMIT | version_);\n }\n\n /// @notice Wraps the padded encoded request into a Request-typed value.\n /// @dev The \"padded\" request is simply an encoded request casted to uint256 (highest bits are set to zero).\n /// Casting to uint256 is done automatically in Solidity, so no extra actions from consumers are needed.\n /// The highest bits are discarded, so that the contracts dealing with encoded requests\n /// don't need to be updated, if a new field is added.\n function wrapPadded(uint256 paddedRequest) internal pure returns (Request) {\n // Casting to uint192 will truncate the highest bits, which is the behavior we want\n return Request.wrap(uint192(paddedRequest));\n }\n\n /// @notice Returns the requested of gas token to drop to the recipient.\n function gasDrop(Request request) internal pure returns (uint96) {\n // Casting to uint96 will truncate the highest bits, which is the behavior we want\n return uint96(Request.unwrap(request) \u003e\u003e SHIFT_GAS_DROP);\n }\n\n /// @notice Returns the requested minimum amount of gas units to supply for execution.\n function gasLimit(Request request) internal pure returns (uint64) {\n // Casting to uint64 will truncate the highest bits, which is the behavior we want\n return uint64(Request.unwrap(request) \u003e\u003e SHIFT_GAS_LIMIT);\n }\n\n /// @notice Returns the requested base message version to pass to the recipient.\n function version(Request request) internal pure returns (uint32) {\n // Casting to uint32 will truncate the highest bits, which is the behavior we want\n return uint32(Request.unwrap(request));\n }\n}\n\n// contracts/client/MessageRecipient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\nabstract contract MessageRecipient is IMessageRecipient {\n struct MessageRequest {\n uint96 gasDrop;\n uint64 gasLimit;\n uint32 version;\n }\n\n /// @notice Local chain Origin: used for sending messages\n address public immutable origin;\n\n /// @notice Local chain Destination: used for receiving messages\n address public immutable destination;\n\n constructor(address origin_, address destination_) {\n origin = origin_;\n destination = destination_;\n }\n\n /// @inheritdoc IMessageRecipient\n function receiveBaseMessage(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable {\n if (msg.sender != destination) revert CallerNotDestination();\n if (nonce == 0) revert IncorrectNonce();\n if (sender == 0) revert IncorrectSender();\n if (proofMaturity == 0) revert ZeroProofMaturity();\n _receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);\n }\n\n /**\n * @dev Child contracts should implement the logic for receiving a Base Message in an \"unsafe way\".\n * Following checks HAVE been performed:\n * - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).\n * - Nonce is not zero.\n * - Message sender on origin chain is not a zero address.\n * - Proof maturity is not zero.\n * Following checks HAVE NOT been performed (thus \"unsafe\"):\n * - Message sender on origin chain could be anything non-zero at this point.\n * - Proof maturity could be anything non-zero at this point.\n */\n function _receiveBaseMessageUnsafe(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) internal virtual;\n\n /**\n * @dev Sends a message to given destination chain. Full `msg.value` is used to pay for the message tips.\n * `_getMinimumTipsValue()` could be used to calculate the minimum required tips value, and should be also\n * exposed as a public view function to estimate the tips value before sending a message off-chain.\n * This function is not exposed in MessageRecipient, as the message encoding is implemented by the child contract.\n * @param destination_ Domain of the destination chain\n * @param recipient Address of the recipient on destination chain\n * @param optimisticPeriod Optimistic period for the message\n * @param tipsValue Tips to be paid for sending the message\n * @param request Message execution request on destination chain\n * @param content The message content\n */\n function _sendBaseMessage(\n uint32 destination_,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 tipsValue,\n MessageRequest memory request,\n bytes memory content\n ) internal returns (uint32 messageNonce, bytes32 messageHash) {\n if (recipient == 0) revert IncorrectRecipient();\n return InterfaceOrigin(origin).sendBaseMessage{value: tipsValue}(\n destination_, recipient, optimisticPeriod, _encodeRequest(request), content\n );\n }\n\n /**\n * @dev Returns the minimum tips value for sending a message to given destination chain.\n * @param destination_ Domain of the destination chain\n * @param request Message execution request on destination chain\n * @param contentLength Length of the message content\n */\n function _getMinimumTipsValue(uint32 destination_, MessageRequest memory request, uint256 contentLength)\n internal\n view\n returns (uint256 tipsValue)\n {\n return InterfaceOrigin(origin).getMinimumTipsValue(destination_, _encodeRequest(request), contentLength);\n }\n\n /**\n * @dev Encodes a message execution request into format that Origin contract is using.\n * @param request Message execution request on destination chain\n * @return paddedRequest Encoded request\n */\n function _encodeRequest(MessageRequest memory request) internal pure returns (uint256 paddedRequest) {\n return Request.unwrap(RequestLib.encodeRequest(request.gasDrop, request.gasLimit, request.version));\n }\n}\n\n// contracts/client/PingPongClient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\ncontract PingPongClient is MessageRecipient {\n using TypeCasts for address;\n\n struct PingPongMessage {\n uint256 pingId;\n bool isPing;\n uint16 counter;\n }\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n uint256 public random;\n\n /// @notice Amount of \"Ping\" messages sent.\n uint256 public pingsSent;\n\n /// @notice Amount of \"Ping\" messages received.\n /// Every received Ping message leads to sending a Pong message back to initial sender.\n uint256 public pingsReceived;\n\n /// @notice Amount of \"Pong\" messages received.\n /// When all messages are delivered, should be equal to `pingsSent`\n uint256 public pongsReceived;\n\n // ══════════════════════════════════════════════════ EVENTS ═══════════════════════════════════════════════════════\n\n /// @notice Emitted when a Ping message is sent.\n /// Triggered externally, or by receveing a Pong message with instructions to do more pings.\n event PingSent(uint256 pingId);\n\n /// @notice Emitted when a Ping message is received.\n /// Will always send a Pong message back.\n event PingReceived(uint256 pingId);\n\n /// @notice Emitted when a Pong message is sent.\n /// Triggered whenever a Ping message is received.\n event PongSent(uint256 pingId);\n\n /// @notice Emitted when a Pong message is received.\n /// Will initiate a new Ping, if the counter in the message is non-zero.\n event PongReceived(uint256 pingId);\n\n // ════════════════════════════════════════════════ CONSTRUCTOR ════════════════════════════════════════════════════\n\n constructor(address origin_, address destination_) MessageRecipient(origin_, destination_) {\n // Initiate \"random\" value\n random = uint256(keccak256(abi.encode(block.number)));\n }\n\n // ═══════════════════════════════════════════════ MESSAGE LOGIC ═══════════════════════════════════════════════════\n\n function doPings(uint16 pingCount, uint32 destination_, address recipient, uint16 counter) external {\n for (uint256 i = 0; i \u003c pingCount; ++i) {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n }\n\n /// @notice Send a Ping message to destination chain.\n /// Upon receiving a Ping, a Pong message will be sent back.\n /// If `counter \u003e 0`, this process will be repeated when the Pong message is received.\n /// @param destination_ Chain to send Ping message to\n /// @param recipient Recipient of Ping message\n /// @param counter Additional amount of Ping-Pong rounds to conclude\n function doPing(uint32 destination_, address recipient, uint16 counter) external {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n function nextOptimisticPeriod() public view returns (uint32 period) {\n // Use random optimistic period up to one minute\n return uint32(random % 1 minutes);\n }\n\n // ═════════════════════════════════════ INTERNAL LOGIC: RECEIVE MESSAGES ══════════════════════════════════════════\n\n /// @inheritdoc MessageRecipient\n function _receiveBaseMessageUnsafe(uint32 origin_, uint32, bytes32 sender, uint256, uint32, bytes memory content)\n internal\n override\n {\n PingPongMessage memory message = abi.decode(content, (PingPongMessage));\n if (message.isPing) {\n // Ping is received\n ++pingsReceived;\n emit PingReceived(message.pingId);\n // Send Pong back\n _pong(origin_, sender, message);\n } else {\n // Pong is received\n ++pongsReceived;\n emit PongReceived(message.pingId);\n // Send extra ping, if initially requested\n if (message.counter != 0) {\n _ping(origin_, sender, message.counter - 1);\n }\n }\n }\n\n // ═══════════════════════════════════════ INTERNAL LOGIC: SEND MESSAGES ═══════════════════════════════════════════\n\n /// @dev Returns a random optimistic period value from 0 to 59 seconds.\n function _optimisticPeriod() internal returns (uint32 period) {\n // Use random optimistic period up to one minute\n period = nextOptimisticPeriod();\n // Adjust \"random\" value\n random = uint256(keccak256(abi.encode(random)));\n }\n\n /**\n * @dev Send a \"Ping\" or \"Pong\" message.\n * @param destination_ Domain of destination chain\n * @param recipient Message recipient on destination chain\n * @param message Ping-pong message\n */\n function _sendMessage(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n // TODO: this probably shouldn't be hardcoded\n MessageRequest memory request = MessageRequest({gasDrop: 0, gasLimit: 500_000, version: 0});\n bytes memory content = abi.encode(message);\n _sendBaseMessage({\n destination_: destination_,\n recipient: recipient,\n optimisticPeriod: _optimisticPeriod(),\n tipsValue: 0,\n request: request,\n content: content\n });\n }\n\n /// @dev Initiate a new Ping-Pong round.\n function _ping(uint32 destination_, bytes32 recipient, uint16 counter) internal {\n uint256 pingId = pingsSent++;\n _sendMessage(destination_, recipient, PingPongMessage({pingId: pingId, isPing: true, counter: counter}));\n emit PingSent(pingId);\n }\n\n /// @dev Send a Pong message back.\n function _pong(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n _sendMessage(\n destination_, recipient, PingPongMessage({pingId: message.pingId, isPing: false, counter: message.counter})\n );\n emit PongSent(message.pingId);\n }\n}\n","language":"Solidity","languageVersion":"0.8.17","compilerVersion":"0.8.17","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[{"internalType":"uint32","name":"origin","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint256","name":"proofMaturity","type":"uint256"},{"internalType":"uint32","name":"version","type":"uint32"},{"internalType":"bytes","name":"content","type":"bytes"}],"name":"receiveBaseMessage","outputs":[],"stateMutability":"payable","type":"function"}],"userDoc":{"kind":"user","methods":{"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":{"notice":"Message recipient needs to implement this function in order to receive cross-chain messages."}},"version":1},"developerDoc":{"kind":"dev","methods":{"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":{"details":"Message recipient needs to ensure that merkle proof for the message is at least as old as the optimistic period that the recipient is using. Note: as this point it is checked that the \"message optimistic period\" has passed, however the period value itself could be anything, and thus could differ from the one that the recipient would like to enforce.","params":{"content":"Raw bytes content of message","nonce":"Message nonce on the origin domain","origin":"Domain where message originated","proofMaturity":"Message's merkle proof age in seconds","sender":"Sender address on origin chain","version":"Message version specified by sender"}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"origin\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"proofMaturity\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"content\",\"type\":\"bytes\"}],\"name\":\"receiveBaseMessage\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)\":{\"details\":\"Message recipient needs to ensure that merkle proof for the message is at least as old as the optimistic period that the recipient is using. Note: as this point it is checked that the \\\"message optimistic period\\\" has passed, however the period value itself could be anything, and thus could differ from the one that the recipient would like to enforce.\",\"params\":{\"content\":\"Raw bytes content of message\",\"nonce\":\"Message nonce on the origin domain\",\"origin\":\"Domain where message originated\",\"proofMaturity\":\"Message's merkle proof age in seconds\",\"sender\":\"Sender address on origin chain\",\"version\":\"Message version specified by sender\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)\":{\"notice\":\"Message recipient needs to implement this function in order to receive cross-chain messages.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/PingPongClient.sol\":\"IMessageRecipient\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/PingPongClient.sol\":{\"keccak256\":\"0x03e643db760356ec5a463ecddaabfbf483dfed8b370419b4a390fc5f54909226\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25da370a66df76adbd5cdef0b7cf6802ac80d54a3b04c67b91c62d9d64aad27a\",\"dweb:/ipfs/QmP9VmcgX1XM6LJaAXYyTgg6FwspNBJQiaV7og686hPRKN\"]}},\"version\":1}"},"hashes":{"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":"032f287e"}},"solidity/PingPongClient.sol:InterfaceOrigin":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.17;\n\n// contracts/interfaces/IMessageRecipient.sol\n\ninterface IMessageRecipient {\n /**\n * @notice Message recipient needs to implement this function in order to\n * receive cross-chain messages.\n * @dev Message recipient needs to ensure that merkle proof for the message\n * is at least as old as the optimistic period that the recipient is using.\n * Note: as this point it is checked that the \"message optimistic period\" has passed,\n * however the period value itself could be anything, and thus could differ from the one\n * that the recipient would like to enforce.\n * @param origin Domain where message originated\n * @param nonce Message nonce on the origin domain\n * @param sender Sender address on origin chain\n * @param proofMaturity Message's merkle proof age in seconds\n * @param version Message version specified by sender\n * @param content Raw bytes content of message\n */\n function receiveBaseMessage(\n uint32 origin,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable;\n}\n\n// contracts/interfaces/InterfaceOrigin.sol\n\ninterface InterfaceOrigin {\n // ═══════════════════════════════════════════════ SEND MESSAGES ═══════════════════════════════════════════════════\n\n /**\n * @notice Send a message to the recipient located on destination domain.\n * @dev Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered.\n * Will revert if any of these is true:\n * - `destination` is equal to contract's local domain\n * - `content` length is greater than `MAX_CONTENT_BYTES`\n * - `msg.value` is lower than value of minimum tips for the given message\n * @param destination Domain of destination chain\n * @param recipient Address of recipient on destination chain as bytes32\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param content Raw bytes content of message\n * @return messageNonce Nonce of the sent message\n * @return messageHash Hash of the sent message\n */\n function sendBaseMessage(\n uint32 destination,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 paddedRequest,\n bytes memory content\n ) external payable returns (uint32 messageNonce, bytes32 messageHash);\n\n /**\n * @notice Send a manager message to the destination domain.\n * @dev This could only be called by AgentManager, which takes care of encoding the calldata payload.\n * Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain\n * so that the AgentManager could verify where the Manager Message came from and how mature is the proof.\n * Note: function is not payable, as no tips are required for sending a manager message.\n * Will revert if `destination` is equal to contract's local domain.\n * @param destination Domain of destination chain\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param payload Payload for calling AgentManager on destination chain (with extra security args)\n */\n function sendManagerMessage(uint32 destination, uint32 optimisticPeriod, bytes memory payload)\n external\n returns (uint32 messageNonce, bytes32 messageHash);\n\n // ════════════════════════════════════════════════ TIPS LOGIC ═════════════════════════════════════════════════════\n\n /**\n * @notice Withdraws locked base message tips to the recipient.\n * @dev Could only be called by a local AgentManager.\n * @param recipient Address to withdraw tips to\n * @param amount Tips value to withdraw\n */\n function withdrawTips(address recipient, uint256 amount) external;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /**\n * @notice Returns the minimum tips value for sending a message to a given destination.\n * @dev Using at least `tipsValue` as `msg.value` for `sendBaseMessage()`\n * will guarantee that the message will be accepted.\n * @param destination Domain of destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param contentLength The length of the message content\n * @return tipsValue Minimum tips value for a message to be accepted\n */\n function getMinimumTipsValue(uint32 destination, uint256 paddedRequest, uint256 contentLength)\n external\n view\n returns (uint256 tipsValue);\n}\n\n// contracts/libs/Errors.sol\n\n// ══════════════════════════════ INVALID CALLER ═══════════════════════════════\n\nerror CallerNotAgentManager();\nerror CallerNotDestination();\nerror CallerNotInbox();\nerror CallerNotSummit();\n\n// ══════════════════════════════ INCORRECT DATA ═══════════════════════════════\n\nerror IncorrectAttestation();\nerror IncorrectAgentDomain();\nerror IncorrectAgentIndex();\nerror IncorrectAgentProof();\nerror IncorrectAgentRoot();\nerror IncorrectDataHash();\nerror IncorrectDestinationDomain();\nerror IncorrectOriginDomain();\nerror IncorrectSnapshotProof();\nerror IncorrectSnapshotRoot();\nerror IncorrectState();\nerror IncorrectStatesAmount();\nerror IncorrectTipsProof();\nerror IncorrectVersionLength();\n\nerror IncorrectNonce();\nerror IncorrectSender();\nerror IncorrectRecipient();\n\nerror FlagOutOfRange();\nerror IndexOutOfRange();\nerror NonceOutOfRange();\n\nerror OutdatedNonce();\n\nerror UnformattedAttestation();\nerror UnformattedAttestationReport();\nerror UnformattedBaseMessage();\nerror UnformattedCallData();\nerror UnformattedCallDataPrefix();\nerror UnformattedMessage();\nerror UnformattedReceipt();\nerror UnformattedReceiptReport();\nerror UnformattedSignature();\nerror UnformattedSnapshot();\nerror UnformattedState();\nerror UnformattedStateReport();\n\n// ═══════════════════════════════ MERKLE TREES ════════════════════════════════\n\nerror LeafNotProven();\nerror MerkleTreeFull();\nerror NotEnoughLeafs();\nerror TreeHeightTooLow();\n\n// ═════════════════════════════ OPTIMISTIC PERIOD ═════════════════════════════\n\nerror BaseClientOptimisticPeriod();\nerror MessageOptimisticPeriod();\nerror SlashAgentOptimisticPeriod();\nerror WithdrawTipsOptimisticPeriod();\nerror ZeroProofMaturity();\n\n// ═══════════════════════════════ AGENT MANAGER ═══════════════════════════════\n\nerror AgentNotGuard();\nerror AgentNotNotary();\n\nerror AgentCantBeAdded();\nerror AgentNotActive();\nerror AgentNotActiveNorUnstaking();\nerror AgentNotFraudulent();\nerror AgentNotUnstaking();\nerror AgentUnknown();\n\nerror AgentRootNotProposed();\nerror AgentRootTimeoutNotOver();\n\nerror NotStuck();\n\nerror DisputeAlreadyResolved();\nerror DisputeNotOpened();\nerror DisputeTimeoutNotOver();\nerror GuardInDispute();\nerror NotaryInDispute();\n\nerror MustBeSynapseDomain();\nerror SynapseDomainForbidden();\n\n// ════════════════════════════════ DESTINATION ════════════════════════════════\n\nerror AlreadyExecuted();\nerror AlreadyFailed();\nerror DuplicatedSnapshotRoot();\nerror IncorrectMagicValue();\nerror GasLimitTooLow();\nerror GasSuppliedTooLow();\n\n// ══════════════════════════════════ ORIGIN ═══════════════════════════════════\n\nerror ContentLengthTooBig();\nerror EthTransferFailed();\nerror InsufficientEthBalance();\n\n// ════════════════════════════════ GAS ORACLE ═════════════════════════════════\n\nerror LocalGasDataNotSet();\nerror RemoteGasDataNotSet();\n\n// ═══════════════════════════════════ TIPS ════════════════════════════════════\n\nerror SummitTipTooHigh();\nerror TipsClaimMoreThanEarned();\nerror TipsClaimZero();\nerror TipsOverflow();\nerror TipsValueTooLow();\n\n// ════════════════════════════════ MEMORY VIEW ════════════════════════════════\n\nerror IndexedTooMuch();\nerror ViewOverrun();\nerror OccupiedMemory();\nerror UnallocatedMemory();\nerror PrecompileOutOfGas();\n\n// ═════════════════════════════════ MULTICALL ═════════════════════════════════\n\nerror MulticallFailed();\n\n// contracts/libs/TypeCasts.sol\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n}\n\n// contracts/libs/stack/Request.sol\n\n/// Request is encoded data with \"message execution request\".\ntype Request is uint192;\n\nusing RequestLib for Request global;\n\n/// Library for formatting _the request part_ of _the base messages_.\n/// - Request represents a message sender requirements for the message execution on the destination chain.\n/// - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory.\n/// \u003e gasDrop field is included for future compatibility and is ignored at the moment.\n///\n/// # Request stack layout (from highest bits to lowest)\n///\n/// | Position | Field | Type | Bytes | Description |\n/// | ---------- | -------- | ------ | ----- | ---------------------------------------------------- |\n/// | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient |\n/// | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution |\n/// | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |\n\nlibrary RequestLib {\n /// @dev Amount of bits to shift to gasDrop field\n uint192 private constant SHIFT_GAS_DROP = 12 * 8;\n /// @dev Amount of bits to shift to gasLimit field\n uint192 private constant SHIFT_GAS_LIMIT = 4 * 8;\n\n /// @notice Returns an encoded request with the given fields\n /// @param gasDrop_ Minimum amount of gas token to drop to the recipient (ignored at the moment)\n /// @param gasLimit_ Minimum amount of gas units to supply for execution\n /// @param version_ Base message version to pass to the recipient\n function encodeRequest(uint96 gasDrop_, uint64 gasLimit_, uint32 version_) internal pure returns (Request) {\n // Casts below are upcasts, so they are safe\n return Request.wrap(uint192(gasDrop_) \u003c\u003c SHIFT_GAS_DROP | uint192(gasLimit_) \u003c\u003c SHIFT_GAS_LIMIT | version_);\n }\n\n /// @notice Wraps the padded encoded request into a Request-typed value.\n /// @dev The \"padded\" request is simply an encoded request casted to uint256 (highest bits are set to zero).\n /// Casting to uint256 is done automatically in Solidity, so no extra actions from consumers are needed.\n /// The highest bits are discarded, so that the contracts dealing with encoded requests\n /// don't need to be updated, if a new field is added.\n function wrapPadded(uint256 paddedRequest) internal pure returns (Request) {\n // Casting to uint192 will truncate the highest bits, which is the behavior we want\n return Request.wrap(uint192(paddedRequest));\n }\n\n /// @notice Returns the requested of gas token to drop to the recipient.\n function gasDrop(Request request) internal pure returns (uint96) {\n // Casting to uint96 will truncate the highest bits, which is the behavior we want\n return uint96(Request.unwrap(request) \u003e\u003e SHIFT_GAS_DROP);\n }\n\n /// @notice Returns the requested minimum amount of gas units to supply for execution.\n function gasLimit(Request request) internal pure returns (uint64) {\n // Casting to uint64 will truncate the highest bits, which is the behavior we want\n return uint64(Request.unwrap(request) \u003e\u003e SHIFT_GAS_LIMIT);\n }\n\n /// @notice Returns the requested base message version to pass to the recipient.\n function version(Request request) internal pure returns (uint32) {\n // Casting to uint32 will truncate the highest bits, which is the behavior we want\n return uint32(Request.unwrap(request));\n }\n}\n\n// contracts/client/MessageRecipient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\nabstract contract MessageRecipient is IMessageRecipient {\n struct MessageRequest {\n uint96 gasDrop;\n uint64 gasLimit;\n uint32 version;\n }\n\n /// @notice Local chain Origin: used for sending messages\n address public immutable origin;\n\n /// @notice Local chain Destination: used for receiving messages\n address public immutable destination;\n\n constructor(address origin_, address destination_) {\n origin = origin_;\n destination = destination_;\n }\n\n /// @inheritdoc IMessageRecipient\n function receiveBaseMessage(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable {\n if (msg.sender != destination) revert CallerNotDestination();\n if (nonce == 0) revert IncorrectNonce();\n if (sender == 0) revert IncorrectSender();\n if (proofMaturity == 0) revert ZeroProofMaturity();\n _receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);\n }\n\n /**\n * @dev Child contracts should implement the logic for receiving a Base Message in an \"unsafe way\".\n * Following checks HAVE been performed:\n * - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).\n * - Nonce is not zero.\n * - Message sender on origin chain is not a zero address.\n * - Proof maturity is not zero.\n * Following checks HAVE NOT been performed (thus \"unsafe\"):\n * - Message sender on origin chain could be anything non-zero at this point.\n * - Proof maturity could be anything non-zero at this point.\n */\n function _receiveBaseMessageUnsafe(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) internal virtual;\n\n /**\n * @dev Sends a message to given destination chain. Full `msg.value` is used to pay for the message tips.\n * `_getMinimumTipsValue()` could be used to calculate the minimum required tips value, and should be also\n * exposed as a public view function to estimate the tips value before sending a message off-chain.\n * This function is not exposed in MessageRecipient, as the message encoding is implemented by the child contract.\n * @param destination_ Domain of the destination chain\n * @param recipient Address of the recipient on destination chain\n * @param optimisticPeriod Optimistic period for the message\n * @param tipsValue Tips to be paid for sending the message\n * @param request Message execution request on destination chain\n * @param content The message content\n */\n function _sendBaseMessage(\n uint32 destination_,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 tipsValue,\n MessageRequest memory request,\n bytes memory content\n ) internal returns (uint32 messageNonce, bytes32 messageHash) {\n if (recipient == 0) revert IncorrectRecipient();\n return InterfaceOrigin(origin).sendBaseMessage{value: tipsValue}(\n destination_, recipient, optimisticPeriod, _encodeRequest(request), content\n );\n }\n\n /**\n * @dev Returns the minimum tips value for sending a message to given destination chain.\n * @param destination_ Domain of the destination chain\n * @param request Message execution request on destination chain\n * @param contentLength Length of the message content\n */\n function _getMinimumTipsValue(uint32 destination_, MessageRequest memory request, uint256 contentLength)\n internal\n view\n returns (uint256 tipsValue)\n {\n return InterfaceOrigin(origin).getMinimumTipsValue(destination_, _encodeRequest(request), contentLength);\n }\n\n /**\n * @dev Encodes a message execution request into format that Origin contract is using.\n * @param request Message execution request on destination chain\n * @return paddedRequest Encoded request\n */\n function _encodeRequest(MessageRequest memory request) internal pure returns (uint256 paddedRequest) {\n return Request.unwrap(RequestLib.encodeRequest(request.gasDrop, request.gasLimit, request.version));\n }\n}\n\n// contracts/client/PingPongClient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\ncontract PingPongClient is MessageRecipient {\n using TypeCasts for address;\n\n struct PingPongMessage {\n uint256 pingId;\n bool isPing;\n uint16 counter;\n }\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n uint256 public random;\n\n /// @notice Amount of \"Ping\" messages sent.\n uint256 public pingsSent;\n\n /// @notice Amount of \"Ping\" messages received.\n /// Every received Ping message leads to sending a Pong message back to initial sender.\n uint256 public pingsReceived;\n\n /// @notice Amount of \"Pong\" messages received.\n /// When all messages are delivered, should be equal to `pingsSent`\n uint256 public pongsReceived;\n\n // ══════════════════════════════════════════════════ EVENTS ═══════════════════════════════════════════════════════\n\n /// @notice Emitted when a Ping message is sent.\n /// Triggered externally, or by receveing a Pong message with instructions to do more pings.\n event PingSent(uint256 pingId);\n\n /// @notice Emitted when a Ping message is received.\n /// Will always send a Pong message back.\n event PingReceived(uint256 pingId);\n\n /// @notice Emitted when a Pong message is sent.\n /// Triggered whenever a Ping message is received.\n event PongSent(uint256 pingId);\n\n /// @notice Emitted when a Pong message is received.\n /// Will initiate a new Ping, if the counter in the message is non-zero.\n event PongReceived(uint256 pingId);\n\n // ════════════════════════════════════════════════ CONSTRUCTOR ════════════════════════════════════════════════════\n\n constructor(address origin_, address destination_) MessageRecipient(origin_, destination_) {\n // Initiate \"random\" value\n random = uint256(keccak256(abi.encode(block.number)));\n }\n\n // ═══════════════════════════════════════════════ MESSAGE LOGIC ═══════════════════════════════════════════════════\n\n function doPings(uint16 pingCount, uint32 destination_, address recipient, uint16 counter) external {\n for (uint256 i = 0; i \u003c pingCount; ++i) {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n }\n\n /// @notice Send a Ping message to destination chain.\n /// Upon receiving a Ping, a Pong message will be sent back.\n /// If `counter \u003e 0`, this process will be repeated when the Pong message is received.\n /// @param destination_ Chain to send Ping message to\n /// @param recipient Recipient of Ping message\n /// @param counter Additional amount of Ping-Pong rounds to conclude\n function doPing(uint32 destination_, address recipient, uint16 counter) external {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n function nextOptimisticPeriod() public view returns (uint32 period) {\n // Use random optimistic period up to one minute\n return uint32(random % 1 minutes);\n }\n\n // ═════════════════════════════════════ INTERNAL LOGIC: RECEIVE MESSAGES ══════════════════════════════════════════\n\n /// @inheritdoc MessageRecipient\n function _receiveBaseMessageUnsafe(uint32 origin_, uint32, bytes32 sender, uint256, uint32, bytes memory content)\n internal\n override\n {\n PingPongMessage memory message = abi.decode(content, (PingPongMessage));\n if (message.isPing) {\n // Ping is received\n ++pingsReceived;\n emit PingReceived(message.pingId);\n // Send Pong back\n _pong(origin_, sender, message);\n } else {\n // Pong is received\n ++pongsReceived;\n emit PongReceived(message.pingId);\n // Send extra ping, if initially requested\n if (message.counter != 0) {\n _ping(origin_, sender, message.counter - 1);\n }\n }\n }\n\n // ═══════════════════════════════════════ INTERNAL LOGIC: SEND MESSAGES ═══════════════════════════════════════════\n\n /// @dev Returns a random optimistic period value from 0 to 59 seconds.\n function _optimisticPeriod() internal returns (uint32 period) {\n // Use random optimistic period up to one minute\n period = nextOptimisticPeriod();\n // Adjust \"random\" value\n random = uint256(keccak256(abi.encode(random)));\n }\n\n /**\n * @dev Send a \"Ping\" or \"Pong\" message.\n * @param destination_ Domain of destination chain\n * @param recipient Message recipient on destination chain\n * @param message Ping-pong message\n */\n function _sendMessage(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n // TODO: this probably shouldn't be hardcoded\n MessageRequest memory request = MessageRequest({gasDrop: 0, gasLimit: 500_000, version: 0});\n bytes memory content = abi.encode(message);\n _sendBaseMessage({\n destination_: destination_,\n recipient: recipient,\n optimisticPeriod: _optimisticPeriod(),\n tipsValue: 0,\n request: request,\n content: content\n });\n }\n\n /// @dev Initiate a new Ping-Pong round.\n function _ping(uint32 destination_, bytes32 recipient, uint16 counter) internal {\n uint256 pingId = pingsSent++;\n _sendMessage(destination_, recipient, PingPongMessage({pingId: pingId, isPing: true, counter: counter}));\n emit PingSent(pingId);\n }\n\n /// @dev Send a Pong message back.\n function _pong(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n _sendMessage(\n destination_, recipient, PingPongMessage({pingId: message.pingId, isPing: false, counter: message.counter})\n );\n emit PongSent(message.pingId);\n }\n}\n","language":"Solidity","languageVersion":"0.8.17","compilerVersion":"0.8.17","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[{"internalType":"uint32","name":"destination","type":"uint32"},{"internalType":"uint256","name":"paddedRequest","type":"uint256"},{"internalType":"uint256","name":"contentLength","type":"uint256"}],"name":"getMinimumTipsValue","outputs":[{"internalType":"uint256","name":"tipsValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"destination","type":"uint32"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint32","name":"optimisticPeriod","type":"uint32"},{"internalType":"uint256","name":"paddedRequest","type":"uint256"},{"internalType":"bytes","name":"content","type":"bytes"}],"name":"sendBaseMessage","outputs":[{"internalType":"uint32","name":"messageNonce","type":"uint32"},{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"destination","type":"uint32"},{"internalType":"uint32","name":"optimisticPeriod","type":"uint32"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"sendManagerMessage","outputs":[{"internalType":"uint32","name":"messageNonce","type":"uint32"},{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawTips","outputs":[],"stateMutability":"nonpayable","type":"function"}],"userDoc":{"kind":"user","methods":{"getMinimumTipsValue(uint32,uint256,uint256)":{"notice":"Returns the minimum tips value for sending a message to a given destination."},"sendBaseMessage(uint32,bytes32,uint32,uint256,bytes)":{"notice":"Send a message to the recipient located on destination domain."},"sendManagerMessage(uint32,uint32,bytes)":{"notice":"Send a manager message to the destination domain."},"withdrawTips(address,uint256)":{"notice":"Withdraws locked base message tips to the recipient."}},"version":1},"developerDoc":{"kind":"dev","methods":{"getMinimumTipsValue(uint32,uint256,uint256)":{"details":"Using at least `tipsValue` as `msg.value` for `sendBaseMessage()` will guarantee that the message will be accepted.","params":{"contentLength":"The length of the message content","destination":"Domain of destination chain","paddedRequest":"Padded encoded message execution request on destination chain"},"returns":{"tipsValue":" Minimum tips value for a message to be accepted"}},"sendBaseMessage(uint32,bytes32,uint32,uint256,bytes)":{"details":"Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered. Will revert if any of these is true: - `destination` is equal to contract's local domain - `content` length is greater than `MAX_CONTENT_BYTES` - `msg.value` is lower than value of minimum tips for the given message","params":{"content":"Raw bytes content of message","destination":"Domain of destination chain","optimisticPeriod":"Optimistic period for message execution on destination chain","paddedRequest":"Padded encoded message execution request on destination chain","recipient":"Address of recipient on destination chain as bytes32"},"returns":{"messageHash":" Hash of the sent message","messageNonce":" Nonce of the sent message"}},"sendManagerMessage(uint32,uint32,bytes)":{"details":"This could only be called by AgentManager, which takes care of encoding the calldata payload. Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain so that the AgentManager could verify where the Manager Message came from and how mature is the proof. Note: function is not payable, as no tips are required for sending a manager message. Will revert if `destination` is equal to contract's local domain.","params":{"destination":"Domain of destination chain","optimisticPeriod":"Optimistic period for message execution on destination chain","payload":"Payload for calling AgentManager on destination chain (with extra security args)"}},"withdrawTips(address,uint256)":{"details":"Could only be called by a local AgentManager.","params":{"amount":"Tips value to withdraw","recipient":"Address to withdraw tips to"}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"destination\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"paddedRequest\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contentLength\",\"type\":\"uint256\"}],\"name\":\"getMinimumTipsValue\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"tipsValue\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"destination\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"optimisticPeriod\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"paddedRequest\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"content\",\"type\":\"bytes\"}],\"name\":\"sendBaseMessage\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"messageNonce\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"destination\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"optimisticPeriod\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"name\":\"sendManagerMessage\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"messageNonce\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawTips\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"getMinimumTipsValue(uint32,uint256,uint256)\":{\"details\":\"Using at least `tipsValue` as `msg.value` for `sendBaseMessage()` will guarantee that the message will be accepted.\",\"params\":{\"contentLength\":\"The length of the message content\",\"destination\":\"Domain of destination chain\",\"paddedRequest\":\"Padded encoded message execution request on destination chain\"},\"returns\":{\"tipsValue\":\" Minimum tips value for a message to be accepted\"}},\"sendBaseMessage(uint32,bytes32,uint32,uint256,bytes)\":{\"details\":\"Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered. Will revert if any of these is true: - `destination` is equal to contract's local domain - `content` length is greater than `MAX_CONTENT_BYTES` - `msg.value` is lower than value of minimum tips for the given message\",\"params\":{\"content\":\"Raw bytes content of message\",\"destination\":\"Domain of destination chain\",\"optimisticPeriod\":\"Optimistic period for message execution on destination chain\",\"paddedRequest\":\"Padded encoded message execution request on destination chain\",\"recipient\":\"Address of recipient on destination chain as bytes32\"},\"returns\":{\"messageHash\":\" Hash of the sent message\",\"messageNonce\":\" Nonce of the sent message\"}},\"sendManagerMessage(uint32,uint32,bytes)\":{\"details\":\"This could only be called by AgentManager, which takes care of encoding the calldata payload. Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain so that the AgentManager could verify where the Manager Message came from and how mature is the proof. Note: function is not payable, as no tips are required for sending a manager message. Will revert if `destination` is equal to contract's local domain.\",\"params\":{\"destination\":\"Domain of destination chain\",\"optimisticPeriod\":\"Optimistic period for message execution on destination chain\",\"payload\":\"Payload for calling AgentManager on destination chain (with extra security args)\"}},\"withdrawTips(address,uint256)\":{\"details\":\"Could only be called by a local AgentManager.\",\"params\":{\"amount\":\"Tips value to withdraw\",\"recipient\":\"Address to withdraw tips to\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getMinimumTipsValue(uint32,uint256,uint256)\":{\"notice\":\"Returns the minimum tips value for sending a message to a given destination.\"},\"sendBaseMessage(uint32,bytes32,uint32,uint256,bytes)\":{\"notice\":\"Send a message to the recipient located on destination domain.\"},\"sendManagerMessage(uint32,uint32,bytes)\":{\"notice\":\"Send a manager message to the destination domain.\"},\"withdrawTips(address,uint256)\":{\"notice\":\"Withdraws locked base message tips to the recipient.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/PingPongClient.sol\":\"InterfaceOrigin\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/PingPongClient.sol\":{\"keccak256\":\"0x03e643db760356ec5a463ecddaabfbf483dfed8b370419b4a390fc5f54909226\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25da370a66df76adbd5cdef0b7cf6802ac80d54a3b04c67b91c62d9d64aad27a\",\"dweb:/ipfs/QmP9VmcgX1XM6LJaAXYyTgg6FwspNBJQiaV7og686hPRKN\"]}},\"version\":1}"},"hashes":{"getMinimumTipsValue(uint32,uint256,uint256)":"4fc6ad85","sendBaseMessage(uint32,bytes32,uint32,uint256,bytes)":"873661bd","sendManagerMessage(uint32,uint32,bytes)":"a1c702a7","withdrawTips(address,uint256)":"4e04e7a7"}},"solidity/PingPongClient.sol:MessageRecipient":{"code":"0x","runtime-code":"0x","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.17;\n\n// contracts/interfaces/IMessageRecipient.sol\n\ninterface IMessageRecipient {\n /**\n * @notice Message recipient needs to implement this function in order to\n * receive cross-chain messages.\n * @dev Message recipient needs to ensure that merkle proof for the message\n * is at least as old as the optimistic period that the recipient is using.\n * Note: as this point it is checked that the \"message optimistic period\" has passed,\n * however the period value itself could be anything, and thus could differ from the one\n * that the recipient would like to enforce.\n * @param origin Domain where message originated\n * @param nonce Message nonce on the origin domain\n * @param sender Sender address on origin chain\n * @param proofMaturity Message's merkle proof age in seconds\n * @param version Message version specified by sender\n * @param content Raw bytes content of message\n */\n function receiveBaseMessage(\n uint32 origin,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable;\n}\n\n// contracts/interfaces/InterfaceOrigin.sol\n\ninterface InterfaceOrigin {\n // ═══════════════════════════════════════════════ SEND MESSAGES ═══════════════════════════════════════════════════\n\n /**\n * @notice Send a message to the recipient located on destination domain.\n * @dev Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered.\n * Will revert if any of these is true:\n * - `destination` is equal to contract's local domain\n * - `content` length is greater than `MAX_CONTENT_BYTES`\n * - `msg.value` is lower than value of minimum tips for the given message\n * @param destination Domain of destination chain\n * @param recipient Address of recipient on destination chain as bytes32\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param content Raw bytes content of message\n * @return messageNonce Nonce of the sent message\n * @return messageHash Hash of the sent message\n */\n function sendBaseMessage(\n uint32 destination,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 paddedRequest,\n bytes memory content\n ) external payable returns (uint32 messageNonce, bytes32 messageHash);\n\n /**\n * @notice Send a manager message to the destination domain.\n * @dev This could only be called by AgentManager, which takes care of encoding the calldata payload.\n * Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain\n * so that the AgentManager could verify where the Manager Message came from and how mature is the proof.\n * Note: function is not payable, as no tips are required for sending a manager message.\n * Will revert if `destination` is equal to contract's local domain.\n * @param destination Domain of destination chain\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param payload Payload for calling AgentManager on destination chain (with extra security args)\n */\n function sendManagerMessage(uint32 destination, uint32 optimisticPeriod, bytes memory payload)\n external\n returns (uint32 messageNonce, bytes32 messageHash);\n\n // ════════════════════════════════════════════════ TIPS LOGIC ═════════════════════════════════════════════════════\n\n /**\n * @notice Withdraws locked base message tips to the recipient.\n * @dev Could only be called by a local AgentManager.\n * @param recipient Address to withdraw tips to\n * @param amount Tips value to withdraw\n */\n function withdrawTips(address recipient, uint256 amount) external;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /**\n * @notice Returns the minimum tips value for sending a message to a given destination.\n * @dev Using at least `tipsValue` as `msg.value` for `sendBaseMessage()`\n * will guarantee that the message will be accepted.\n * @param destination Domain of destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param contentLength The length of the message content\n * @return tipsValue Minimum tips value for a message to be accepted\n */\n function getMinimumTipsValue(uint32 destination, uint256 paddedRequest, uint256 contentLength)\n external\n view\n returns (uint256 tipsValue);\n}\n\n// contracts/libs/Errors.sol\n\n// ══════════════════════════════ INVALID CALLER ═══════════════════════════════\n\nerror CallerNotAgentManager();\nerror CallerNotDestination();\nerror CallerNotInbox();\nerror CallerNotSummit();\n\n// ══════════════════════════════ INCORRECT DATA ═══════════════════════════════\n\nerror IncorrectAttestation();\nerror IncorrectAgentDomain();\nerror IncorrectAgentIndex();\nerror IncorrectAgentProof();\nerror IncorrectAgentRoot();\nerror IncorrectDataHash();\nerror IncorrectDestinationDomain();\nerror IncorrectOriginDomain();\nerror IncorrectSnapshotProof();\nerror IncorrectSnapshotRoot();\nerror IncorrectState();\nerror IncorrectStatesAmount();\nerror IncorrectTipsProof();\nerror IncorrectVersionLength();\n\nerror IncorrectNonce();\nerror IncorrectSender();\nerror IncorrectRecipient();\n\nerror FlagOutOfRange();\nerror IndexOutOfRange();\nerror NonceOutOfRange();\n\nerror OutdatedNonce();\n\nerror UnformattedAttestation();\nerror UnformattedAttestationReport();\nerror UnformattedBaseMessage();\nerror UnformattedCallData();\nerror UnformattedCallDataPrefix();\nerror UnformattedMessage();\nerror UnformattedReceipt();\nerror UnformattedReceiptReport();\nerror UnformattedSignature();\nerror UnformattedSnapshot();\nerror UnformattedState();\nerror UnformattedStateReport();\n\n// ═══════════════════════════════ MERKLE TREES ════════════════════════════════\n\nerror LeafNotProven();\nerror MerkleTreeFull();\nerror NotEnoughLeafs();\nerror TreeHeightTooLow();\n\n// ═════════════════════════════ OPTIMISTIC PERIOD ═════════════════════════════\n\nerror BaseClientOptimisticPeriod();\nerror MessageOptimisticPeriod();\nerror SlashAgentOptimisticPeriod();\nerror WithdrawTipsOptimisticPeriod();\nerror ZeroProofMaturity();\n\n// ═══════════════════════════════ AGENT MANAGER ═══════════════════════════════\n\nerror AgentNotGuard();\nerror AgentNotNotary();\n\nerror AgentCantBeAdded();\nerror AgentNotActive();\nerror AgentNotActiveNorUnstaking();\nerror AgentNotFraudulent();\nerror AgentNotUnstaking();\nerror AgentUnknown();\n\nerror AgentRootNotProposed();\nerror AgentRootTimeoutNotOver();\n\nerror NotStuck();\n\nerror DisputeAlreadyResolved();\nerror DisputeNotOpened();\nerror DisputeTimeoutNotOver();\nerror GuardInDispute();\nerror NotaryInDispute();\n\nerror MustBeSynapseDomain();\nerror SynapseDomainForbidden();\n\n// ════════════════════════════════ DESTINATION ════════════════════════════════\n\nerror AlreadyExecuted();\nerror AlreadyFailed();\nerror DuplicatedSnapshotRoot();\nerror IncorrectMagicValue();\nerror GasLimitTooLow();\nerror GasSuppliedTooLow();\n\n// ══════════════════════════════════ ORIGIN ═══════════════════════════════════\n\nerror ContentLengthTooBig();\nerror EthTransferFailed();\nerror InsufficientEthBalance();\n\n// ════════════════════════════════ GAS ORACLE ═════════════════════════════════\n\nerror LocalGasDataNotSet();\nerror RemoteGasDataNotSet();\n\n// ═══════════════════════════════════ TIPS ════════════════════════════════════\n\nerror SummitTipTooHigh();\nerror TipsClaimMoreThanEarned();\nerror TipsClaimZero();\nerror TipsOverflow();\nerror TipsValueTooLow();\n\n// ════════════════════════════════ MEMORY VIEW ════════════════════════════════\n\nerror IndexedTooMuch();\nerror ViewOverrun();\nerror OccupiedMemory();\nerror UnallocatedMemory();\nerror PrecompileOutOfGas();\n\n// ═════════════════════════════════ MULTICALL ═════════════════════════════════\n\nerror MulticallFailed();\n\n// contracts/libs/TypeCasts.sol\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n}\n\n// contracts/libs/stack/Request.sol\n\n/// Request is encoded data with \"message execution request\".\ntype Request is uint192;\n\nusing RequestLib for Request global;\n\n/// Library for formatting _the request part_ of _the base messages_.\n/// - Request represents a message sender requirements for the message execution on the destination chain.\n/// - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory.\n/// \u003e gasDrop field is included for future compatibility and is ignored at the moment.\n///\n/// # Request stack layout (from highest bits to lowest)\n///\n/// | Position | Field | Type | Bytes | Description |\n/// | ---------- | -------- | ------ | ----- | ---------------------------------------------------- |\n/// | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient |\n/// | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution |\n/// | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |\n\nlibrary RequestLib {\n /// @dev Amount of bits to shift to gasDrop field\n uint192 private constant SHIFT_GAS_DROP = 12 * 8;\n /// @dev Amount of bits to shift to gasLimit field\n uint192 private constant SHIFT_GAS_LIMIT = 4 * 8;\n\n /// @notice Returns an encoded request with the given fields\n /// @param gasDrop_ Minimum amount of gas token to drop to the recipient (ignored at the moment)\n /// @param gasLimit_ Minimum amount of gas units to supply for execution\n /// @param version_ Base message version to pass to the recipient\n function encodeRequest(uint96 gasDrop_, uint64 gasLimit_, uint32 version_) internal pure returns (Request) {\n // Casts below are upcasts, so they are safe\n return Request.wrap(uint192(gasDrop_) \u003c\u003c SHIFT_GAS_DROP | uint192(gasLimit_) \u003c\u003c SHIFT_GAS_LIMIT | version_);\n }\n\n /// @notice Wraps the padded encoded request into a Request-typed value.\n /// @dev The \"padded\" request is simply an encoded request casted to uint256 (highest bits are set to zero).\n /// Casting to uint256 is done automatically in Solidity, so no extra actions from consumers are needed.\n /// The highest bits are discarded, so that the contracts dealing with encoded requests\n /// don't need to be updated, if a new field is added.\n function wrapPadded(uint256 paddedRequest) internal pure returns (Request) {\n // Casting to uint192 will truncate the highest bits, which is the behavior we want\n return Request.wrap(uint192(paddedRequest));\n }\n\n /// @notice Returns the requested of gas token to drop to the recipient.\n function gasDrop(Request request) internal pure returns (uint96) {\n // Casting to uint96 will truncate the highest bits, which is the behavior we want\n return uint96(Request.unwrap(request) \u003e\u003e SHIFT_GAS_DROP);\n }\n\n /// @notice Returns the requested minimum amount of gas units to supply for execution.\n function gasLimit(Request request) internal pure returns (uint64) {\n // Casting to uint64 will truncate the highest bits, which is the behavior we want\n return uint64(Request.unwrap(request) \u003e\u003e SHIFT_GAS_LIMIT);\n }\n\n /// @notice Returns the requested base message version to pass to the recipient.\n function version(Request request) internal pure returns (uint32) {\n // Casting to uint32 will truncate the highest bits, which is the behavior we want\n return uint32(Request.unwrap(request));\n }\n}\n\n// contracts/client/MessageRecipient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\nabstract contract MessageRecipient is IMessageRecipient {\n struct MessageRequest {\n uint96 gasDrop;\n uint64 gasLimit;\n uint32 version;\n }\n\n /// @notice Local chain Origin: used for sending messages\n address public immutable origin;\n\n /// @notice Local chain Destination: used for receiving messages\n address public immutable destination;\n\n constructor(address origin_, address destination_) {\n origin = origin_;\n destination = destination_;\n }\n\n /// @inheritdoc IMessageRecipient\n function receiveBaseMessage(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable {\n if (msg.sender != destination) revert CallerNotDestination();\n if (nonce == 0) revert IncorrectNonce();\n if (sender == 0) revert IncorrectSender();\n if (proofMaturity == 0) revert ZeroProofMaturity();\n _receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);\n }\n\n /**\n * @dev Child contracts should implement the logic for receiving a Base Message in an \"unsafe way\".\n * Following checks HAVE been performed:\n * - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).\n * - Nonce is not zero.\n * - Message sender on origin chain is not a zero address.\n * - Proof maturity is not zero.\n * Following checks HAVE NOT been performed (thus \"unsafe\"):\n * - Message sender on origin chain could be anything non-zero at this point.\n * - Proof maturity could be anything non-zero at this point.\n */\n function _receiveBaseMessageUnsafe(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) internal virtual;\n\n /**\n * @dev Sends a message to given destination chain. Full `msg.value` is used to pay for the message tips.\n * `_getMinimumTipsValue()` could be used to calculate the minimum required tips value, and should be also\n * exposed as a public view function to estimate the tips value before sending a message off-chain.\n * This function is not exposed in MessageRecipient, as the message encoding is implemented by the child contract.\n * @param destination_ Domain of the destination chain\n * @param recipient Address of the recipient on destination chain\n * @param optimisticPeriod Optimistic period for the message\n * @param tipsValue Tips to be paid for sending the message\n * @param request Message execution request on destination chain\n * @param content The message content\n */\n function _sendBaseMessage(\n uint32 destination_,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 tipsValue,\n MessageRequest memory request,\n bytes memory content\n ) internal returns (uint32 messageNonce, bytes32 messageHash) {\n if (recipient == 0) revert IncorrectRecipient();\n return InterfaceOrigin(origin).sendBaseMessage{value: tipsValue}(\n destination_, recipient, optimisticPeriod, _encodeRequest(request), content\n );\n }\n\n /**\n * @dev Returns the minimum tips value for sending a message to given destination chain.\n * @param destination_ Domain of the destination chain\n * @param request Message execution request on destination chain\n * @param contentLength Length of the message content\n */\n function _getMinimumTipsValue(uint32 destination_, MessageRequest memory request, uint256 contentLength)\n internal\n view\n returns (uint256 tipsValue)\n {\n return InterfaceOrigin(origin).getMinimumTipsValue(destination_, _encodeRequest(request), contentLength);\n }\n\n /**\n * @dev Encodes a message execution request into format that Origin contract is using.\n * @param request Message execution request on destination chain\n * @return paddedRequest Encoded request\n */\n function _encodeRequest(MessageRequest memory request) internal pure returns (uint256 paddedRequest) {\n return Request.unwrap(RequestLib.encodeRequest(request.gasDrop, request.gasLimit, request.version));\n }\n}\n\n// contracts/client/PingPongClient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\ncontract PingPongClient is MessageRecipient {\n using TypeCasts for address;\n\n struct PingPongMessage {\n uint256 pingId;\n bool isPing;\n uint16 counter;\n }\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n uint256 public random;\n\n /// @notice Amount of \"Ping\" messages sent.\n uint256 public pingsSent;\n\n /// @notice Amount of \"Ping\" messages received.\n /// Every received Ping message leads to sending a Pong message back to initial sender.\n uint256 public pingsReceived;\n\n /// @notice Amount of \"Pong\" messages received.\n /// When all messages are delivered, should be equal to `pingsSent`\n uint256 public pongsReceived;\n\n // ══════════════════════════════════════════════════ EVENTS ═══════════════════════════════════════════════════════\n\n /// @notice Emitted when a Ping message is sent.\n /// Triggered externally, or by receveing a Pong message with instructions to do more pings.\n event PingSent(uint256 pingId);\n\n /// @notice Emitted when a Ping message is received.\n /// Will always send a Pong message back.\n event PingReceived(uint256 pingId);\n\n /// @notice Emitted when a Pong message is sent.\n /// Triggered whenever a Ping message is received.\n event PongSent(uint256 pingId);\n\n /// @notice Emitted when a Pong message is received.\n /// Will initiate a new Ping, if the counter in the message is non-zero.\n event PongReceived(uint256 pingId);\n\n // ════════════════════════════════════════════════ CONSTRUCTOR ════════════════════════════════════════════════════\n\n constructor(address origin_, address destination_) MessageRecipient(origin_, destination_) {\n // Initiate \"random\" value\n random = uint256(keccak256(abi.encode(block.number)));\n }\n\n // ═══════════════════════════════════════════════ MESSAGE LOGIC ═══════════════════════════════════════════════════\n\n function doPings(uint16 pingCount, uint32 destination_, address recipient, uint16 counter) external {\n for (uint256 i = 0; i \u003c pingCount; ++i) {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n }\n\n /// @notice Send a Ping message to destination chain.\n /// Upon receiving a Ping, a Pong message will be sent back.\n /// If `counter \u003e 0`, this process will be repeated when the Pong message is received.\n /// @param destination_ Chain to send Ping message to\n /// @param recipient Recipient of Ping message\n /// @param counter Additional amount of Ping-Pong rounds to conclude\n function doPing(uint32 destination_, address recipient, uint16 counter) external {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n function nextOptimisticPeriod() public view returns (uint32 period) {\n // Use random optimistic period up to one minute\n return uint32(random % 1 minutes);\n }\n\n // ═════════════════════════════════════ INTERNAL LOGIC: RECEIVE MESSAGES ══════════════════════════════════════════\n\n /// @inheritdoc MessageRecipient\n function _receiveBaseMessageUnsafe(uint32 origin_, uint32, bytes32 sender, uint256, uint32, bytes memory content)\n internal\n override\n {\n PingPongMessage memory message = abi.decode(content, (PingPongMessage));\n if (message.isPing) {\n // Ping is received\n ++pingsReceived;\n emit PingReceived(message.pingId);\n // Send Pong back\n _pong(origin_, sender, message);\n } else {\n // Pong is received\n ++pongsReceived;\n emit PongReceived(message.pingId);\n // Send extra ping, if initially requested\n if (message.counter != 0) {\n _ping(origin_, sender, message.counter - 1);\n }\n }\n }\n\n // ═══════════════════════════════════════ INTERNAL LOGIC: SEND MESSAGES ═══════════════════════════════════════════\n\n /// @dev Returns a random optimistic period value from 0 to 59 seconds.\n function _optimisticPeriod() internal returns (uint32 period) {\n // Use random optimistic period up to one minute\n period = nextOptimisticPeriod();\n // Adjust \"random\" value\n random = uint256(keccak256(abi.encode(random)));\n }\n\n /**\n * @dev Send a \"Ping\" or \"Pong\" message.\n * @param destination_ Domain of destination chain\n * @param recipient Message recipient on destination chain\n * @param message Ping-pong message\n */\n function _sendMessage(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n // TODO: this probably shouldn't be hardcoded\n MessageRequest memory request = MessageRequest({gasDrop: 0, gasLimit: 500_000, version: 0});\n bytes memory content = abi.encode(message);\n _sendBaseMessage({\n destination_: destination_,\n recipient: recipient,\n optimisticPeriod: _optimisticPeriod(),\n tipsValue: 0,\n request: request,\n content: content\n });\n }\n\n /// @dev Initiate a new Ping-Pong round.\n function _ping(uint32 destination_, bytes32 recipient, uint16 counter) internal {\n uint256 pingId = pingsSent++;\n _sendMessage(destination_, recipient, PingPongMessage({pingId: pingId, isPing: true, counter: counter}));\n emit PingSent(pingId);\n }\n\n /// @dev Send a Pong message back.\n function _pong(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n _sendMessage(\n destination_, recipient, PingPongMessage({pingId: message.pingId, isPing: false, counter: message.counter})\n );\n emit PongSent(message.pingId);\n }\n}\n","language":"Solidity","languageVersion":"0.8.17","compilerVersion":"0.8.17","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"","srcMapRuntime":"","abiDefinition":[{"inputs":[],"name":"CallerNotDestination","type":"error"},{"inputs":[],"name":"IncorrectNonce","type":"error"},{"inputs":[],"name":"IncorrectSender","type":"error"},{"inputs":[],"name":"ZeroProofMaturity","type":"error"},{"inputs":[],"name":"destination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"origin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"origin_","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint256","name":"proofMaturity","type":"uint256"},{"internalType":"uint32","name":"version","type":"uint32"},{"internalType":"bytes","name":"content","type":"bytes"}],"name":"receiveBaseMessage","outputs":[],"stateMutability":"payable","type":"function"}],"userDoc":{"kind":"user","methods":{"destination()":{"notice":"Local chain Destination: used for receiving messages"},"origin()":{"notice":"Local chain Origin: used for sending messages"},"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":{"notice":"Message recipient needs to implement this function in order to receive cross-chain messages."}},"version":1},"developerDoc":{"kind":"dev","methods":{"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":{"details":"Message recipient needs to ensure that merkle proof for the message is at least as old as the optimistic period that the recipient is using. Note: as this point it is checked that the \"message optimistic period\" has passed, however the period value itself could be anything, and thus could differ from the one that the recipient would like to enforce.","params":{"content":"Raw bytes content of message","nonce":"Message nonce on the origin domain","origin":"Domain where message originated","proofMaturity":"Message's merkle proof age in seconds","sender":"Sender address on origin chain","version":"Message version specified by sender"}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"CallerNotDestination\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroProofMaturity\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"destination\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"origin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"origin_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"proofMaturity\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"content\",\"type\":\"bytes\"}],\"name\":\"receiveBaseMessage\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)\":{\"details\":\"Message recipient needs to ensure that merkle proof for the message is at least as old as the optimistic period that the recipient is using. Note: as this point it is checked that the \\\"message optimistic period\\\" has passed, however the period value itself could be anything, and thus could differ from the one that the recipient would like to enforce.\",\"params\":{\"content\":\"Raw bytes content of message\",\"nonce\":\"Message nonce on the origin domain\",\"origin\":\"Domain where message originated\",\"proofMaturity\":\"Message's merkle proof age in seconds\",\"sender\":\"Sender address on origin chain\",\"version\":\"Message version specified by sender\"}}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"destination()\":{\"notice\":\"Local chain Destination: used for receiving messages\"},\"origin()\":{\"notice\":\"Local chain Origin: used for sending messages\"},\"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)\":{\"notice\":\"Message recipient needs to implement this function in order to receive cross-chain messages.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/PingPongClient.sol\":\"MessageRecipient\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/PingPongClient.sol\":{\"keccak256\":\"0x03e643db760356ec5a463ecddaabfbf483dfed8b370419b4a390fc5f54909226\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25da370a66df76adbd5cdef0b7cf6802ac80d54a3b04c67b91c62d9d64aad27a\",\"dweb:/ipfs/QmP9VmcgX1XM6LJaAXYyTgg6FwspNBJQiaV7og686hPRKN\"]}},\"version\":1}"},"hashes":{"destination()":"b269681d","origin()":"938b5f32","receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":"032f287e"}},"solidity/PingPongClient.sol:PingPongClient":{"code":"0x60c060405234801561001057600080fd5b50604051610d4b380380610d4b83398101604081905261002f91610087565b6001600160a01b039182166080521660a052604080514360208083019190915282518083038201815291830190925280519101206000556100ba565b80516001600160a01b038116811461008257600080fd5b919050565b6000806040838503121561009a57600080fd5b6100a38361006b565b91506100b16020840161006b565b90509250929050565b60805160a051610c5e6100ed600039600081816101df015261024501526000818161016601526106cc0152610c5e6000f3fe6080604052600436106100b15760003560e01c8063938b5f3211610069578063b269681d1161004e578063b269681d146101cd578063b475cba314610201578063e3ac3ca01461021757600080fd5b8063938b5f3214610154578063aa402039146101ad57600080fd5b80632bd560251161009a5780632bd56025146100eb57806345a8b8ed1461011a5780635ec01e4d1461013e57600080fd5b8063032f287e146100b657806308fe5e4e146100cb575b600080fd5b6100c96100c436600461083f565b61022d565b005b3480156100d757600080fd5b506100c96100e6366004610991565b610367565b3480156100f757600080fd5b5061010061038d565b60405163ffffffff90911681526020015b60405180910390f35b34801561012657600080fd5b5061013060035481565b604051908152602001610111565b34801561014a57600080fd5b5061013060005481565b34801561016057600080fd5b506101887f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610111565b3480156101b957600080fd5b506100c96101c83660046109da565b6103a3565b3480156101d957600080fd5b506101887f000000000000000000000000000000000000000000000000000000000000000081565b34801561020d57600080fd5b5061013060015481565b34801561022357600080fd5b5061013060025481565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461029c576040517f6efcc49f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8463ffffffff166000036102dc576040517f674d8d8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000849003610317576040517f7d1c29f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82600003610351576040517fdce28ace00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61035f8686868686866103ea565b505050505050565b6103888373ffffffffffffffffffffffffffffffffffffffff8416836104de565b505050565b6000603c60005461039e9190610a34565b905090565b60005b8461ffff168110156103e3576103d38473ffffffffffffffffffffffffffffffffffffffff8516846104de565b6103dc81610a9e565b90506103a6565b5050505050565b6000818060200190518101906104009190610ad6565b90508060200151156104645760026000815461041b90610a9e565b9091555080516040519081527f51c4f05cea43f3d4604f77fd5a656743088090aa726deb5e3a9f670d8da75d659060200160405180910390a161045f87868361055a565b6104d5565b60036000815461047390610a9e565b9091555080516040519081527f08d46b5262cb13a84b9421fef5cfd01017e1cb48c879e3fc89acaadf34f2106e9060200160405180910390a1604081015161ffff16156104d5576104d58786600184604001516104d09190610b44565b6104de565b50505050505050565b60018054600091826104ef83610a9e565b919050559050610521848460405180606001604052808581526020016001151581526020018661ffff168152506105c8565b6040518181527f14089a5f67ef0667796ead5223612a15d24422be4bdaa19abc32fb26d4c8b3db9060200160405180910390a150505050565b61058e8383604051806060016040528085600001518152602001600015158152602001856040015161ffff168152506105c8565b80516040519081527f0a72872b9cfe43d6c13b13553f28d4879e427f3b456545649fd0761fdcbe03119060200160405180910390a1505050565b604080516060808201835260008083526207a120602080850191909152838501919091528351855181830152908501511515818501528484015161ffff168183015283518082039092018252608001909252906104d58585610628610631565b6000868661068e565b600061063b61038d565b905060005460405160200161065291815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012060005590565b6000808681036106ca576040517f519bdea700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663873661bd868a8a8a6107138a610785565b896040518763ffffffff1660e01b8152600401610734959493929190610b66565b604080518083038185885af1158015610751573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906107769190610bfa565b91509150965096945050505050565b60006107db82600001518360200151846040015177ffffffffffffffffffffffff000000000000000000000000606084901b166bffffffffffffffff00000000602084901b161763ffffffff8216179392505050565b77ffffffffffffffffffffffffffffffffffffffffffffffff1692915050565b63ffffffff8116811461080d57600080fd5b50565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060008060c0878903121561085857600080fd5b8635610863816107fb565b95506020870135610873816107fb565b945060408701359350606087013592506080870135610891816107fb565b915060a087013567ffffffffffffffff808211156108ae57600080fd5b818901915089601f8301126108c257600080fd5b8135818111156108d4576108d4610810565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561091a5761091a610810565b816040528281528c602084870101111561093357600080fd5b8260208601602083013760006020848301015280955050505050509295509295509295565b803573ffffffffffffffffffffffffffffffffffffffff8116811461097c57600080fd5b919050565b61ffff8116811461080d57600080fd5b6000806000606084860312156109a657600080fd5b83356109b1816107fb565b92506109bf60208501610958565b915060408401356109cf81610981565b809150509250925092565b600080600080608085870312156109f057600080fd5b84356109fb81610981565b93506020850135610a0b816107fb565b9250610a1960408601610958565b91506060850135610a2981610981565b939692955090935050565b600082610a6a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610acf57610acf610a6f565b5060010190565b600060608284031215610ae857600080fd5b6040516060810181811067ffffffffffffffff82111715610b0b57610b0b610810565b6040528251815260208301518015158114610b2557600080fd5b60208201526040830151610b3881610981565b60408201529392505050565b61ffff828116828216039080821115610b5f57610b5f610a6f565b5092915050565b600063ffffffff808816835260208781850152818716604085015285606085015260a06080850152845191508160a085015260005b82811015610bb75785810182015185820160c001528101610b9b565b5050600060c0828501015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509695505050505050565b60008060408385031215610c0d57600080fd5b8251610c18816107fb565b602093909301519294929350505056fea2646970667358221220d709bb7f0e7a1333a848628dfa392f85b5131eb4b64c71457370746c8a4da58b64736f6c63430008110033","runtime-code":"0x6080604052600436106100b15760003560e01c8063938b5f3211610069578063b269681d1161004e578063b269681d146101cd578063b475cba314610201578063e3ac3ca01461021757600080fd5b8063938b5f3214610154578063aa402039146101ad57600080fd5b80632bd560251161009a5780632bd56025146100eb57806345a8b8ed1461011a5780635ec01e4d1461013e57600080fd5b8063032f287e146100b657806308fe5e4e146100cb575b600080fd5b6100c96100c436600461083f565b61022d565b005b3480156100d757600080fd5b506100c96100e6366004610991565b610367565b3480156100f757600080fd5b5061010061038d565b60405163ffffffff90911681526020015b60405180910390f35b34801561012657600080fd5b5061013060035481565b604051908152602001610111565b34801561014a57600080fd5b5061013060005481565b34801561016057600080fd5b506101887f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610111565b3480156101b957600080fd5b506100c96101c83660046109da565b6103a3565b3480156101d957600080fd5b506101887f000000000000000000000000000000000000000000000000000000000000000081565b34801561020d57600080fd5b5061013060015481565b34801561022357600080fd5b5061013060025481565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461029c576040517f6efcc49f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8463ffffffff166000036102dc576040517f674d8d8800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000849003610317576040517f7d1c29f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82600003610351576040517fdce28ace00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61035f8686868686866103ea565b505050505050565b6103888373ffffffffffffffffffffffffffffffffffffffff8416836104de565b505050565b6000603c60005461039e9190610a34565b905090565b60005b8461ffff168110156103e3576103d38473ffffffffffffffffffffffffffffffffffffffff8516846104de565b6103dc81610a9e565b90506103a6565b5050505050565b6000818060200190518101906104009190610ad6565b90508060200151156104645760026000815461041b90610a9e565b9091555080516040519081527f51c4f05cea43f3d4604f77fd5a656743088090aa726deb5e3a9f670d8da75d659060200160405180910390a161045f87868361055a565b6104d5565b60036000815461047390610a9e565b9091555080516040519081527f08d46b5262cb13a84b9421fef5cfd01017e1cb48c879e3fc89acaadf34f2106e9060200160405180910390a1604081015161ffff16156104d5576104d58786600184604001516104d09190610b44565b6104de565b50505050505050565b60018054600091826104ef83610a9e565b919050559050610521848460405180606001604052808581526020016001151581526020018661ffff168152506105c8565b6040518181527f14089a5f67ef0667796ead5223612a15d24422be4bdaa19abc32fb26d4c8b3db9060200160405180910390a150505050565b61058e8383604051806060016040528085600001518152602001600015158152602001856040015161ffff168152506105c8565b80516040519081527f0a72872b9cfe43d6c13b13553f28d4879e427f3b456545649fd0761fdcbe03119060200160405180910390a1505050565b604080516060808201835260008083526207a120602080850191909152838501919091528351855181830152908501511515818501528484015161ffff168183015283518082039092018252608001909252906104d58585610628610631565b6000868661068e565b600061063b61038d565b905060005460405160200161065291815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012060005590565b6000808681036106ca576040517f519bdea700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663873661bd868a8a8a6107138a610785565b896040518763ffffffff1660e01b8152600401610734959493929190610b66565b604080518083038185885af1158015610751573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906107769190610bfa565b91509150965096945050505050565b60006107db82600001518360200151846040015177ffffffffffffffffffffffff000000000000000000000000606084901b166bffffffffffffffff00000000602084901b161763ffffffff8216179392505050565b77ffffffffffffffffffffffffffffffffffffffffffffffff1692915050565b63ffffffff8116811461080d57600080fd5b50565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060008060c0878903121561085857600080fd5b8635610863816107fb565b95506020870135610873816107fb565b945060408701359350606087013592506080870135610891816107fb565b915060a087013567ffffffffffffffff808211156108ae57600080fd5b818901915089601f8301126108c257600080fd5b8135818111156108d4576108d4610810565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561091a5761091a610810565b816040528281528c602084870101111561093357600080fd5b8260208601602083013760006020848301015280955050505050509295509295509295565b803573ffffffffffffffffffffffffffffffffffffffff8116811461097c57600080fd5b919050565b61ffff8116811461080d57600080fd5b6000806000606084860312156109a657600080fd5b83356109b1816107fb565b92506109bf60208501610958565b915060408401356109cf81610981565b809150509250925092565b600080600080608085870312156109f057600080fd5b84356109fb81610981565b93506020850135610a0b816107fb565b9250610a1960408601610958565b91506060850135610a2981610981565b939692955090935050565b600082610a6a577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610acf57610acf610a6f565b5060010190565b600060608284031215610ae857600080fd5b6040516060810181811067ffffffffffffffff82111715610b0b57610b0b610810565b6040528251815260208301518015158114610b2557600080fd5b60208201526040830151610b3881610981565b60408201529392505050565b61ffff828116828216039080821115610b5f57610b5f610a6f565b5092915050565b600063ffffffff808816835260208781850152818716604085015285606085015260a06080850152845191508160a085015260005b82811015610bb75785810182015185820160c001528101610b9b565b5050600060c0828501015260c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509695505050505050565b60008060408385031215610c0d57600080fd5b8251610c18816107fb565b602093909301519294929350505056fea2646970667358221220d709bb7f0e7a1333a848628dfa392f85b5131eb4b64c71457370746c8a4da58b64736f6c63430008110033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.17;\n\n// contracts/interfaces/IMessageRecipient.sol\n\ninterface IMessageRecipient {\n /**\n * @notice Message recipient needs to implement this function in order to\n * receive cross-chain messages.\n * @dev Message recipient needs to ensure that merkle proof for the message\n * is at least as old as the optimistic period that the recipient is using.\n * Note: as this point it is checked that the \"message optimistic period\" has passed,\n * however the period value itself could be anything, and thus could differ from the one\n * that the recipient would like to enforce.\n * @param origin Domain where message originated\n * @param nonce Message nonce on the origin domain\n * @param sender Sender address on origin chain\n * @param proofMaturity Message's merkle proof age in seconds\n * @param version Message version specified by sender\n * @param content Raw bytes content of message\n */\n function receiveBaseMessage(\n uint32 origin,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable;\n}\n\n// contracts/interfaces/InterfaceOrigin.sol\n\ninterface InterfaceOrigin {\n // ═══════════════════════════════════════════════ SEND MESSAGES ═══════════════════════════════════════════════════\n\n /**\n * @notice Send a message to the recipient located on destination domain.\n * @dev Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered.\n * Will revert if any of these is true:\n * - `destination` is equal to contract's local domain\n * - `content` length is greater than `MAX_CONTENT_BYTES`\n * - `msg.value` is lower than value of minimum tips for the given message\n * @param destination Domain of destination chain\n * @param recipient Address of recipient on destination chain as bytes32\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param content Raw bytes content of message\n * @return messageNonce Nonce of the sent message\n * @return messageHash Hash of the sent message\n */\n function sendBaseMessage(\n uint32 destination,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 paddedRequest,\n bytes memory content\n ) external payable returns (uint32 messageNonce, bytes32 messageHash);\n\n /**\n * @notice Send a manager message to the destination domain.\n * @dev This could only be called by AgentManager, which takes care of encoding the calldata payload.\n * Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain\n * so that the AgentManager could verify where the Manager Message came from and how mature is the proof.\n * Note: function is not payable, as no tips are required for sending a manager message.\n * Will revert if `destination` is equal to contract's local domain.\n * @param destination Domain of destination chain\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param payload Payload for calling AgentManager on destination chain (with extra security args)\n */\n function sendManagerMessage(uint32 destination, uint32 optimisticPeriod, bytes memory payload)\n external\n returns (uint32 messageNonce, bytes32 messageHash);\n\n // ════════════════════════════════════════════════ TIPS LOGIC ═════════════════════════════════════════════════════\n\n /**\n * @notice Withdraws locked base message tips to the recipient.\n * @dev Could only be called by a local AgentManager.\n * @param recipient Address to withdraw tips to\n * @param amount Tips value to withdraw\n */\n function withdrawTips(address recipient, uint256 amount) external;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /**\n * @notice Returns the minimum tips value for sending a message to a given destination.\n * @dev Using at least `tipsValue` as `msg.value` for `sendBaseMessage()`\n * will guarantee that the message will be accepted.\n * @param destination Domain of destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param contentLength The length of the message content\n * @return tipsValue Minimum tips value for a message to be accepted\n */\n function getMinimumTipsValue(uint32 destination, uint256 paddedRequest, uint256 contentLength)\n external\n view\n returns (uint256 tipsValue);\n}\n\n// contracts/libs/Errors.sol\n\n// ══════════════════════════════ INVALID CALLER ═══════════════════════════════\n\nerror CallerNotAgentManager();\nerror CallerNotDestination();\nerror CallerNotInbox();\nerror CallerNotSummit();\n\n// ══════════════════════════════ INCORRECT DATA ═══════════════════════════════\n\nerror IncorrectAttestation();\nerror IncorrectAgentDomain();\nerror IncorrectAgentIndex();\nerror IncorrectAgentProof();\nerror IncorrectAgentRoot();\nerror IncorrectDataHash();\nerror IncorrectDestinationDomain();\nerror IncorrectOriginDomain();\nerror IncorrectSnapshotProof();\nerror IncorrectSnapshotRoot();\nerror IncorrectState();\nerror IncorrectStatesAmount();\nerror IncorrectTipsProof();\nerror IncorrectVersionLength();\n\nerror IncorrectNonce();\nerror IncorrectSender();\nerror IncorrectRecipient();\n\nerror FlagOutOfRange();\nerror IndexOutOfRange();\nerror NonceOutOfRange();\n\nerror OutdatedNonce();\n\nerror UnformattedAttestation();\nerror UnformattedAttestationReport();\nerror UnformattedBaseMessage();\nerror UnformattedCallData();\nerror UnformattedCallDataPrefix();\nerror UnformattedMessage();\nerror UnformattedReceipt();\nerror UnformattedReceiptReport();\nerror UnformattedSignature();\nerror UnformattedSnapshot();\nerror UnformattedState();\nerror UnformattedStateReport();\n\n// ═══════════════════════════════ MERKLE TREES ════════════════════════════════\n\nerror LeafNotProven();\nerror MerkleTreeFull();\nerror NotEnoughLeafs();\nerror TreeHeightTooLow();\n\n// ═════════════════════════════ OPTIMISTIC PERIOD ═════════════════════════════\n\nerror BaseClientOptimisticPeriod();\nerror MessageOptimisticPeriod();\nerror SlashAgentOptimisticPeriod();\nerror WithdrawTipsOptimisticPeriod();\nerror ZeroProofMaturity();\n\n// ═══════════════════════════════ AGENT MANAGER ═══════════════════════════════\n\nerror AgentNotGuard();\nerror AgentNotNotary();\n\nerror AgentCantBeAdded();\nerror AgentNotActive();\nerror AgentNotActiveNorUnstaking();\nerror AgentNotFraudulent();\nerror AgentNotUnstaking();\nerror AgentUnknown();\n\nerror AgentRootNotProposed();\nerror AgentRootTimeoutNotOver();\n\nerror NotStuck();\n\nerror DisputeAlreadyResolved();\nerror DisputeNotOpened();\nerror DisputeTimeoutNotOver();\nerror GuardInDispute();\nerror NotaryInDispute();\n\nerror MustBeSynapseDomain();\nerror SynapseDomainForbidden();\n\n// ════════════════════════════════ DESTINATION ════════════════════════════════\n\nerror AlreadyExecuted();\nerror AlreadyFailed();\nerror DuplicatedSnapshotRoot();\nerror IncorrectMagicValue();\nerror GasLimitTooLow();\nerror GasSuppliedTooLow();\n\n// ══════════════════════════════════ ORIGIN ═══════════════════════════════════\n\nerror ContentLengthTooBig();\nerror EthTransferFailed();\nerror InsufficientEthBalance();\n\n// ════════════════════════════════ GAS ORACLE ═════════════════════════════════\n\nerror LocalGasDataNotSet();\nerror RemoteGasDataNotSet();\n\n// ═══════════════════════════════════ TIPS ════════════════════════════════════\n\nerror SummitTipTooHigh();\nerror TipsClaimMoreThanEarned();\nerror TipsClaimZero();\nerror TipsOverflow();\nerror TipsValueTooLow();\n\n// ════════════════════════════════ MEMORY VIEW ════════════════════════════════\n\nerror IndexedTooMuch();\nerror ViewOverrun();\nerror OccupiedMemory();\nerror UnallocatedMemory();\nerror PrecompileOutOfGas();\n\n// ═════════════════════════════════ MULTICALL ═════════════════════════════════\n\nerror MulticallFailed();\n\n// contracts/libs/TypeCasts.sol\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n}\n\n// contracts/libs/stack/Request.sol\n\n/// Request is encoded data with \"message execution request\".\ntype Request is uint192;\n\nusing RequestLib for Request global;\n\n/// Library for formatting _the request part_ of _the base messages_.\n/// - Request represents a message sender requirements for the message execution on the destination chain.\n/// - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory.\n/// \u003e gasDrop field is included for future compatibility and is ignored at the moment.\n///\n/// # Request stack layout (from highest bits to lowest)\n///\n/// | Position | Field | Type | Bytes | Description |\n/// | ---------- | -------- | ------ | ----- | ---------------------------------------------------- |\n/// | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient |\n/// | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution |\n/// | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |\n\nlibrary RequestLib {\n /// @dev Amount of bits to shift to gasDrop field\n uint192 private constant SHIFT_GAS_DROP = 12 * 8;\n /// @dev Amount of bits to shift to gasLimit field\n uint192 private constant SHIFT_GAS_LIMIT = 4 * 8;\n\n /// @notice Returns an encoded request with the given fields\n /// @param gasDrop_ Minimum amount of gas token to drop to the recipient (ignored at the moment)\n /// @param gasLimit_ Minimum amount of gas units to supply for execution\n /// @param version_ Base message version to pass to the recipient\n function encodeRequest(uint96 gasDrop_, uint64 gasLimit_, uint32 version_) internal pure returns (Request) {\n // Casts below are upcasts, so they are safe\n return Request.wrap(uint192(gasDrop_) \u003c\u003c SHIFT_GAS_DROP | uint192(gasLimit_) \u003c\u003c SHIFT_GAS_LIMIT | version_);\n }\n\n /// @notice Wraps the padded encoded request into a Request-typed value.\n /// @dev The \"padded\" request is simply an encoded request casted to uint256 (highest bits are set to zero).\n /// Casting to uint256 is done automatically in Solidity, so no extra actions from consumers are needed.\n /// The highest bits are discarded, so that the contracts dealing with encoded requests\n /// don't need to be updated, if a new field is added.\n function wrapPadded(uint256 paddedRequest) internal pure returns (Request) {\n // Casting to uint192 will truncate the highest bits, which is the behavior we want\n return Request.wrap(uint192(paddedRequest));\n }\n\n /// @notice Returns the requested of gas token to drop to the recipient.\n function gasDrop(Request request) internal pure returns (uint96) {\n // Casting to uint96 will truncate the highest bits, which is the behavior we want\n return uint96(Request.unwrap(request) \u003e\u003e SHIFT_GAS_DROP);\n }\n\n /// @notice Returns the requested minimum amount of gas units to supply for execution.\n function gasLimit(Request request) internal pure returns (uint64) {\n // Casting to uint64 will truncate the highest bits, which is the behavior we want\n return uint64(Request.unwrap(request) \u003e\u003e SHIFT_GAS_LIMIT);\n }\n\n /// @notice Returns the requested base message version to pass to the recipient.\n function version(Request request) internal pure returns (uint32) {\n // Casting to uint32 will truncate the highest bits, which is the behavior we want\n return uint32(Request.unwrap(request));\n }\n}\n\n// contracts/client/MessageRecipient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\nabstract contract MessageRecipient is IMessageRecipient {\n struct MessageRequest {\n uint96 gasDrop;\n uint64 gasLimit;\n uint32 version;\n }\n\n /// @notice Local chain Origin: used for sending messages\n address public immutable origin;\n\n /// @notice Local chain Destination: used for receiving messages\n address public immutable destination;\n\n constructor(address origin_, address destination_) {\n origin = origin_;\n destination = destination_;\n }\n\n /// @inheritdoc IMessageRecipient\n function receiveBaseMessage(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable {\n if (msg.sender != destination) revert CallerNotDestination();\n if (nonce == 0) revert IncorrectNonce();\n if (sender == 0) revert IncorrectSender();\n if (proofMaturity == 0) revert ZeroProofMaturity();\n _receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);\n }\n\n /**\n * @dev Child contracts should implement the logic for receiving a Base Message in an \"unsafe way\".\n * Following checks HAVE been performed:\n * - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).\n * - Nonce is not zero.\n * - Message sender on origin chain is not a zero address.\n * - Proof maturity is not zero.\n * Following checks HAVE NOT been performed (thus \"unsafe\"):\n * - Message sender on origin chain could be anything non-zero at this point.\n * - Proof maturity could be anything non-zero at this point.\n */\n function _receiveBaseMessageUnsafe(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) internal virtual;\n\n /**\n * @dev Sends a message to given destination chain. Full `msg.value` is used to pay for the message tips.\n * `_getMinimumTipsValue()` could be used to calculate the minimum required tips value, and should be also\n * exposed as a public view function to estimate the tips value before sending a message off-chain.\n * This function is not exposed in MessageRecipient, as the message encoding is implemented by the child contract.\n * @param destination_ Domain of the destination chain\n * @param recipient Address of the recipient on destination chain\n * @param optimisticPeriod Optimistic period for the message\n * @param tipsValue Tips to be paid for sending the message\n * @param request Message execution request on destination chain\n * @param content The message content\n */\n function _sendBaseMessage(\n uint32 destination_,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 tipsValue,\n MessageRequest memory request,\n bytes memory content\n ) internal returns (uint32 messageNonce, bytes32 messageHash) {\n if (recipient == 0) revert IncorrectRecipient();\n return InterfaceOrigin(origin).sendBaseMessage{value: tipsValue}(\n destination_, recipient, optimisticPeriod, _encodeRequest(request), content\n );\n }\n\n /**\n * @dev Returns the minimum tips value for sending a message to given destination chain.\n * @param destination_ Domain of the destination chain\n * @param request Message execution request on destination chain\n * @param contentLength Length of the message content\n */\n function _getMinimumTipsValue(uint32 destination_, MessageRequest memory request, uint256 contentLength)\n internal\n view\n returns (uint256 tipsValue)\n {\n return InterfaceOrigin(origin).getMinimumTipsValue(destination_, _encodeRequest(request), contentLength);\n }\n\n /**\n * @dev Encodes a message execution request into format that Origin contract is using.\n * @param request Message execution request on destination chain\n * @return paddedRequest Encoded request\n */\n function _encodeRequest(MessageRequest memory request) internal pure returns (uint256 paddedRequest) {\n return Request.unwrap(RequestLib.encodeRequest(request.gasDrop, request.gasLimit, request.version));\n }\n}\n\n// contracts/client/PingPongClient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\ncontract PingPongClient is MessageRecipient {\n using TypeCasts for address;\n\n struct PingPongMessage {\n uint256 pingId;\n bool isPing;\n uint16 counter;\n }\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n uint256 public random;\n\n /// @notice Amount of \"Ping\" messages sent.\n uint256 public pingsSent;\n\n /// @notice Amount of \"Ping\" messages received.\n /// Every received Ping message leads to sending a Pong message back to initial sender.\n uint256 public pingsReceived;\n\n /// @notice Amount of \"Pong\" messages received.\n /// When all messages are delivered, should be equal to `pingsSent`\n uint256 public pongsReceived;\n\n // ══════════════════════════════════════════════════ EVENTS ═══════════════════════════════════════════════════════\n\n /// @notice Emitted when a Ping message is sent.\n /// Triggered externally, or by receveing a Pong message with instructions to do more pings.\n event PingSent(uint256 pingId);\n\n /// @notice Emitted when a Ping message is received.\n /// Will always send a Pong message back.\n event PingReceived(uint256 pingId);\n\n /// @notice Emitted when a Pong message is sent.\n /// Triggered whenever a Ping message is received.\n event PongSent(uint256 pingId);\n\n /// @notice Emitted when a Pong message is received.\n /// Will initiate a new Ping, if the counter in the message is non-zero.\n event PongReceived(uint256 pingId);\n\n // ════════════════════════════════════════════════ CONSTRUCTOR ════════════════════════════════════════════════════\n\n constructor(address origin_, address destination_) MessageRecipient(origin_, destination_) {\n // Initiate \"random\" value\n random = uint256(keccak256(abi.encode(block.number)));\n }\n\n // ═══════════════════════════════════════════════ MESSAGE LOGIC ═══════════════════════════════════════════════════\n\n function doPings(uint16 pingCount, uint32 destination_, address recipient, uint16 counter) external {\n for (uint256 i = 0; i \u003c pingCount; ++i) {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n }\n\n /// @notice Send a Ping message to destination chain.\n /// Upon receiving a Ping, a Pong message will be sent back.\n /// If `counter \u003e 0`, this process will be repeated when the Pong message is received.\n /// @param destination_ Chain to send Ping message to\n /// @param recipient Recipient of Ping message\n /// @param counter Additional amount of Ping-Pong rounds to conclude\n function doPing(uint32 destination_, address recipient, uint16 counter) external {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n function nextOptimisticPeriod() public view returns (uint32 period) {\n // Use random optimistic period up to one minute\n return uint32(random % 1 minutes);\n }\n\n // ═════════════════════════════════════ INTERNAL LOGIC: RECEIVE MESSAGES ══════════════════════════════════════════\n\n /// @inheritdoc MessageRecipient\n function _receiveBaseMessageUnsafe(uint32 origin_, uint32, bytes32 sender, uint256, uint32, bytes memory content)\n internal\n override\n {\n PingPongMessage memory message = abi.decode(content, (PingPongMessage));\n if (message.isPing) {\n // Ping is received\n ++pingsReceived;\n emit PingReceived(message.pingId);\n // Send Pong back\n _pong(origin_, sender, message);\n } else {\n // Pong is received\n ++pongsReceived;\n emit PongReceived(message.pingId);\n // Send extra ping, if initially requested\n if (message.counter != 0) {\n _ping(origin_, sender, message.counter - 1);\n }\n }\n }\n\n // ═══════════════════════════════════════ INTERNAL LOGIC: SEND MESSAGES ═══════════════════════════════════════════\n\n /// @dev Returns a random optimistic period value from 0 to 59 seconds.\n function _optimisticPeriod() internal returns (uint32 period) {\n // Use random optimistic period up to one minute\n period = nextOptimisticPeriod();\n // Adjust \"random\" value\n random = uint256(keccak256(abi.encode(random)));\n }\n\n /**\n * @dev Send a \"Ping\" or \"Pong\" message.\n * @param destination_ Domain of destination chain\n * @param recipient Message recipient on destination chain\n * @param message Ping-pong message\n */\n function _sendMessage(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n // TODO: this probably shouldn't be hardcoded\n MessageRequest memory request = MessageRequest({gasDrop: 0, gasLimit: 500_000, version: 0});\n bytes memory content = abi.encode(message);\n _sendBaseMessage({\n destination_: destination_,\n recipient: recipient,\n optimisticPeriod: _optimisticPeriod(),\n tipsValue: 0,\n request: request,\n content: content\n });\n }\n\n /// @dev Initiate a new Ping-Pong round.\n function _ping(uint32 destination_, bytes32 recipient, uint16 counter) internal {\n uint256 pingId = pingsSent++;\n _sendMessage(destination_, recipient, PingPongMessage({pingId: pingId, isPing: true, counter: counter}));\n emit PingSent(pingId);\n }\n\n /// @dev Send a Pong message back.\n function _pong(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n _sendMessage(\n destination_, recipient, PingPongMessage({pingId: message.pingId, isPing: false, counter: message.counter})\n );\n emit PongSent(message.pingId);\n }\n}\n","language":"Solidity","languageVersion":"0.8.17","compilerVersion":"0.8.17","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"19658:7276:0:-:0;;;21927:196;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;15242:16:0;;;;;15268:26;;;22090:24:::1;::::0;;22101:12:::1;22090:24;::::0;;::::1;640:25:1::0;;;;22090:24:0;;;;;;;;;613:18:1;;;22090:24:0;;;22080:35;;;::::1;::::0;22072:44:::1;22063:53:::0;19658:7276;;14:177:1;93:13;;-1:-1:-1;;;;;135:31:1;;125:42;;115:70;;181:1;178;171:12;115:70;14:177;;;:::o;196:293::-;275:6;283;336:2;324:9;315:7;311:23;307:32;304:52;;;352:1;349;342:12;304:52;375:40;405:9;375:40;:::i;:::-;365:50;;434:49;479:2;468:9;464:18;434:49;:::i;:::-;424:59;;196:293;;;;;:::o;494:177::-;19658:7276:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;","srcMapRuntime":"19658:7276:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15345:535;;;;;;:::i;:::-;;:::i;:::-;;23096:156;;;;;;;;;;-1:-1:-1;23096:156:0;;;;;:::i;:::-;;:::i;23592:175::-;;;;;;;;;;;;;:::i;:::-;;;2824:10:1;2812:23;;;2794:42;;2782:2;2767:18;23592:175:0;;;;;;;;20587:28;;;;;;;;;;;;;;;;;;;2993:25:1;;;2981:2;2966:18;20587:28:0;2847:177:1;20177:21:0;;;;;;;;;;;;;;;;15031:31;;;;;;;;;;;;;;;;;;3205:42:1;3193:55;;;3175:74;;3163:2;3148:18;15031:31:0;3029:226:1;22447:239:0;;;;;;;;;;-1:-1:-1;22447:239:0;;;;;:::i;:::-;;:::i;15138:36::-;;;;;;;;;;;;;;;20253:24;;;;;;;;;;;;;;;;20428:28;;;;;;;;;;;;;;;;15345:535;15565:10;:25;15579:11;15565:25;;15561:60;;15599:22;;;;;;;;;;;;;;15561:60;15635:5;:10;;15644:1;15635:10;15631:39;;15654:16;;;;;;;;;;;;;;15631:39;15694:1;15684:11;;;15680:41;;15704:17;;;;;;;;;;;;;;15680:41;15735:13;15752:1;15735:18;15731:50;;15762:19;;;;;;;;;;;;;;15731:50;15791:82;15817:7;15826:5;15833:6;15841:13;15856:7;15865;15791:25;:82::i;:::-;15345:535;;;;;;:::o;23096:156::-;23187:58;23193:12;23207:26;;;23237:7;23187:5;:58::i;:::-;23096:156;;;:::o;23592:175::-;23645:13;23750:9;23741:6;;:18;;;;:::i;:::-;23727:33;;23592:175;:::o;22447:239::-;22562:9;22557:123;22581:9;22577:13;;:1;:13;22557:123;;;22611:58;22617:12;22631:26;;;22661:7;22611:5;:58::i;:::-;22592:3;;;:::i;:::-;;;22557:123;;;;22447:239;;;;:::o;24090:758::-;24252:30;24296:7;24285:38;;;;;;;;;;;;:::i;:::-;24252:71;;24337:7;:14;;;24333:509;;;24401:13;;24399:15;;;;;:::i;:::-;;;;-1:-1:-1;24446:14:0;;24433:28;;2993:25:1;;;24433:28:0;;2981:2:1;2966:18;24433:28:0;;;;;;;24505:31;24511:7;24520:6;24528:7;24505:5;:31::i;:::-;24333:509;;;24601:13;;24599:15;;;;;:::i;:::-;;;;-1:-1:-1;24646:14:0;;24633:28;;2993:25:1;;;24633:28:0;;2981:2:1;2966:18;24633:28:0;;;;;;;24734:15;;;;:20;;;24730:102;;24774:43;24780:7;24789:6;24815:1;24797:7;:15;;;:19;;;;:::i;:::-;24774:5;:43::i;:::-;24242:606;24090:758;;;;;;:::o;26322:270::-;26429:9;:11;;26412:14;;;26429:11;;;:::i;:::-;;;;;26412:28;;26450:104;26463:12;26477:9;26488:65;;;;;;;;26513:6;26488:65;;;;26529:4;26488:65;;;;;;26544:7;26488:65;;;;;26450:12;:104::i;:::-;26569:16;;2993:25:1;;;26569:16:0;;2981:2:1;2966:18;26569:16:0;;;;;;;26402:190;26322:270;;;:::o;26637:295::-;26743:143;26769:12;26783:9;26794:82;;;;;;;;26819:7;:14;;;26794:82;;;;26843:5;26794:82;;;;;;26859:7;:15;;;26794:82;;;;;26743:12;:143::i;:::-;26910:14;;26901:24;;2993:25:1;;;26901:24:0;;2981:2:1;2966:18;26901:24:0;;;;;;;26637:295;;;:::o;25705:566::-;25904:59;;;;;;;;;-1:-1:-1;25904:59:0;;;25942:7;25904:59;;;;;;;;;;;;;;;25996:19;;5667:13:1;;25996:19:0;;;5649:32:1;5739:17;;;5733:24;5726:32;5719:40;5697:20;;;5690:70;5808:17;;;5802:24;5828:6;5798:37;5776:20;;;5769:67;25996:19:0;;;;;;;;;;5622:18:1;;25996:19:0;;;25904:59;26025:239;26070:12;26107:9;26148:19;:17;:19::i;:::-;26192:1;26216:7;26246;26025:16;:239::i;25216:257::-;25263:13;25354:22;:20;:22::i;:::-;25345:31;;25457:6;;25446:18;;;;;;2993:25:1;;2981:2;2966:18;;2847:177;25446:18:0;;;;;;;;;;;;;;25436:29;;25446:18;25436:29;;;;25428:38;25419:47;25216:257;:::o;17605:514::-;17840:19;;17896:14;;;17892:47;;17919:20;;;;;;;;;;;;;;17892:47;17972:6;17956:39;;;18003:9;18027:12;18041:9;18052:16;18070:23;18085:7;18070:14;:23::i;:::-;18095:7;17956:156;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;17949:163;;;;17605:514;;;;;;;;;:::o;18995:217::-;19073:21;19128:76;19153:7;:15;;;19170:7;:16;;;19188:7;:15;;;12632:35;11996:6;12632:35;;;;12670:37;12106:5;12670:37;;;;12632:75;:86;;;;12442:284;;;;;;19128:76;19106:99;;;18995:217;-1:-1:-1;;18995:217:0:o;14:121:1:-;99:10;92:5;88:22;81:5;78:33;68:61;;125:1;122;115:12;68:61;14:121;:::o;140:184::-;192:77;189:1;182:88;289:4;286:1;279:15;313:4;310:1;303:15;329:1530;439:6;447;455;463;471;479;532:3;520:9;511:7;507:23;503:33;500:53;;;549:1;546;539:12;500:53;588:9;575:23;607:30;631:5;607:30;:::i;:::-;656:5;-1:-1:-1;713:2:1;698:18;;685:32;726;685;726;:::i;:::-;777:7;-1:-1:-1;831:2:1;816:18;;803:32;;-1:-1:-1;882:2:1;867:18;;854:32;;-1:-1:-1;938:3:1;923:19;;910:33;952:32;910:33;952:32;:::i;:::-;1003:7;-1:-1:-1;1061:3:1;1046:19;;1033:33;1085:18;1115:14;;;1112:34;;;1142:1;1139;1132:12;1112:34;1180:6;1169:9;1165:22;1155:32;;1225:7;1218:4;1214:2;1210:13;1206:27;1196:55;;1247:1;1244;1237:12;1196:55;1283:2;1270:16;1305:2;1301;1298:10;1295:36;;;1311:18;;:::i;:::-;1445:2;1439:9;1507:4;1499:13;;1350:66;1495:22;;;1519:2;1491:31;1487:40;1475:53;;;1543:18;;;1563:22;;;1540:46;1537:72;;;1589:18;;:::i;:::-;1629:10;1625:2;1618:22;1664:2;1656:6;1649:18;1704:7;1699:2;1694;1690;1686:11;1682:20;1679:33;1676:53;;;1725:1;1722;1715:12;1676:53;1781:2;1776;1772;1768:11;1763:2;1755:6;1751:15;1738:46;1826:1;1821:2;1816;1808:6;1804:15;1800:24;1793:35;1847:6;1837:16;;;;;;;329:1530;;;;;;;;:::o;1864:196::-;1932:20;;1992:42;1981:54;;1971:65;;1961:93;;2050:1;2047;2040:12;1961:93;1864:196;;;:::o;2065:117::-;2150:6;2143:5;2139:18;2132:5;2129:29;2119:57;;2172:1;2169;2162:12;2187:458;2262:6;2270;2278;2331:2;2319:9;2310:7;2306:23;2302:32;2299:52;;;2347:1;2344;2337:12;2299:52;2386:9;2373:23;2405:30;2429:5;2405:30;:::i;:::-;2454:5;-1:-1:-1;2478:38:1;2512:2;2497:18;;2478:38;:::i;:::-;2468:48;;2568:2;2557:9;2553:18;2540:32;2581;2605:7;2581:32;:::i;:::-;2632:7;2622:17;;;2187:458;;;;;:::o;3260:598::-;3343:6;3351;3359;3367;3420:3;3408:9;3399:7;3395:23;3391:33;3388:53;;;3437:1;3434;3427:12;3388:53;3476:9;3463:23;3495:30;3519:5;3495:30;:::i;:::-;3544:5;-1:-1:-1;3601:2:1;3586:18;;3573:32;3614;3573;3614;:::i;:::-;3665:7;-1:-1:-1;3691:38:1;3725:2;3710:18;;3691:38;:::i;:::-;3681:48;;3781:2;3770:9;3766:18;3753:32;3794;3818:7;3794:32;:::i;:::-;3260:598;;;;-1:-1:-1;3260:598:1;;-1:-1:-1;;3260:598:1:o;3863:266::-;3895:1;3921;3911:189;;3956:77;3953:1;3946:88;4057:4;4054:1;4047:15;4085:4;4082:1;4075:15;3911:189;-1:-1:-1;4114:9:1;;3863:266::o;4134:184::-;4186:77;4183:1;4176:88;4283:4;4280:1;4273:15;4307:4;4304:1;4297:15;4323:195;4362:3;4393:66;4386:5;4383:77;4380:103;;4463:18;;:::i;:::-;-1:-1:-1;4510:1:1;4499:13;;4323:195::o;4523:735::-;4625:6;4678:2;4666:9;4657:7;4653:23;4649:32;4646:52;;;4694:1;4691;4684:12;4646:52;4727:2;4721:9;4769:2;4761:6;4757:15;4838:6;4826:10;4823:22;4802:18;4790:10;4787:34;4784:62;4781:88;;;4849:18;;:::i;:::-;4885:2;4878:22;4924:16;;4909:32;;4984:2;4969:18;;4963:25;5024:13;;5017:21;5007:32;;4997:60;;5053:1;5050;5043:12;4997:60;5085:2;5073:15;;5066:30;5141:2;5126:18;;5120:25;5154:32;5120:25;5154:32;:::i;:::-;5214:2;5202:15;;5195:32;5206:6;4523:735;-1:-1:-1;;;4523:735:1:o;5263:171::-;5331:6;5370:10;;;5358;;;5354:27;;5393:12;;;5390:38;;;5408:18;;:::i;:::-;5390:38;5263:171;;;;:::o;5847:938::-;6065:4;6094:10;6143:2;6135:6;6131:15;6120:9;6113:34;6166:2;6204:6;6199:2;6188:9;6184:18;6177:34;6259:2;6251:6;6247:15;6242:2;6231:9;6227:18;6220:43;6299:6;6294:2;6283:9;6279:18;6272:34;6343:3;6337;6326:9;6322:19;6315:32;6376:6;6370:13;6356:27;;6420:6;6414:3;6403:9;6399:19;6392:35;6445:1;6455:141;6469:6;6466:1;6463:13;6455:141;;;6565:14;;;6561:23;;6555:30;6530:17;;;6549:3;6526:27;6519:67;6484:10;;6455:141;;;6459:3;;6646:1;6640:3;6631:6;6620:9;6616:22;6612:32;6605:43;6775:3;6705:66;6700:2;6692:6;6688:15;6684:88;6673:9;6669:104;6665:114;6657:122;;;5847:938;;;;;;;;:::o;6790:310::-;6868:6;6876;6929:2;6917:9;6908:7;6904:23;6900:32;6897:52;;;6945:1;6942;6935:12;6897:52;6977:9;6971:16;6996:30;7020:5;6996:30;:::i;:::-;7090:2;7075:18;;;;7069:25;7045:5;;7069:25;;-1:-1:-1;;;6790:310:1:o","abiDefinition":[{"inputs":[{"internalType":"address","name":"origin_","type":"address"},{"internalType":"address","name":"destination_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallerNotDestination","type":"error"},{"inputs":[],"name":"IncorrectNonce","type":"error"},{"inputs":[],"name":"IncorrectRecipient","type":"error"},{"inputs":[],"name":"IncorrectSender","type":"error"},{"inputs":[],"name":"ZeroProofMaturity","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pingId","type":"uint256"}],"name":"PingReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pingId","type":"uint256"}],"name":"PingSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pingId","type":"uint256"}],"name":"PongReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pingId","type":"uint256"}],"name":"PongSent","type":"event"},{"inputs":[],"name":"destination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"destination_","type":"uint32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint16","name":"counter","type":"uint16"}],"name":"doPing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"pingCount","type":"uint16"},{"internalType":"uint32","name":"destination_","type":"uint32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint16","name":"counter","type":"uint16"}],"name":"doPings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextOptimisticPeriod","outputs":[{"internalType":"uint32","name":"period","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"origin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pingsReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pingsSent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pongsReceived","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"random","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"origin_","type":"uint32"},{"internalType":"uint32","name":"nonce","type":"uint32"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"uint256","name":"proofMaturity","type":"uint256"},{"internalType":"uint32","name":"version","type":"uint32"},{"internalType":"bytes","name":"content","type":"bytes"}],"name":"receiveBaseMessage","outputs":[],"stateMutability":"payable","type":"function"}],"userDoc":{"events":{"PingReceived(uint256)":{"notice":"Emitted when a Ping message is received. Will always send a Pong message back."},"PingSent(uint256)":{"notice":"Emitted when a Ping message is sent. Triggered externally, or by receveing a Pong message with instructions to do more pings."},"PongReceived(uint256)":{"notice":"Emitted when a Pong message is received. Will initiate a new Ping, if the counter in the message is non-zero."},"PongSent(uint256)":{"notice":"Emitted when a Pong message is sent. Triggered whenever a Ping message is received."}},"kind":"user","methods":{"destination()":{"notice":"Local chain Destination: used for receiving messages"},"doPing(uint32,address,uint16)":{"notice":"Send a Ping message to destination chain. Upon receiving a Ping, a Pong message will be sent back. If `counter \u003e 0`, this process will be repeated when the Pong message is received."},"origin()":{"notice":"Local chain Origin: used for sending messages"},"pingsReceived()":{"notice":"Amount of \"Ping\" messages received. Every received Ping message leads to sending a Pong message back to initial sender."},"pingsSent()":{"notice":"Amount of \"Ping\" messages sent."},"pongsReceived()":{"notice":"Amount of \"Pong\" messages received. When all messages are delivered, should be equal to `pingsSent`"},"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":{"notice":"Message recipient needs to implement this function in order to receive cross-chain messages."}},"version":1},"developerDoc":{"kind":"dev","methods":{"doPing(uint32,address,uint16)":{"params":{"counter":"Additional amount of Ping-Pong rounds to conclude","destination_":"Chain to send Ping message to","recipient":"Recipient of Ping message"}},"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":{"details":"Message recipient needs to ensure that merkle proof for the message is at least as old as the optimistic period that the recipient is using. Note: as this point it is checked that the \"message optimistic period\" has passed, however the period value itself could be anything, and thus could differ from the one that the recipient would like to enforce.","params":{"content":"Raw bytes content of message","nonce":"Message nonce on the origin domain","origin":"Domain where message originated","proofMaturity":"Message's merkle proof age in seconds","sender":"Sender address on origin chain","version":"Message version specified by sender"}}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"origin_\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destination_\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CallerNotDestination\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectNonce\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectRecipient\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"IncorrectSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroProofMaturity\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"pingId\",\"type\":\"uint256\"}],\"name\":\"PingReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"pingId\",\"type\":\"uint256\"}],\"name\":\"PingSent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"pingId\",\"type\":\"uint256\"}],\"name\":\"PongReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"pingId\",\"type\":\"uint256\"}],\"name\":\"PongSent\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"destination\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"destination_\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"counter\",\"type\":\"uint16\"}],\"name\":\"doPing\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"pingCount\",\"type\":\"uint16\"},{\"internalType\":\"uint32\",\"name\":\"destination_\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"counter\",\"type\":\"uint16\"}],\"name\":\"doPings\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextOptimisticPeriod\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"period\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"origin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pingsReceived\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pingsSent\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pongsReceived\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"random\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"origin_\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"nonce\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"sender\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"proofMaturity\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"version\",\"type\":\"uint32\"},{\"internalType\":\"bytes\",\"name\":\"content\",\"type\":\"bytes\"}],\"name\":\"receiveBaseMessage\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"doPing(uint32,address,uint16)\":{\"params\":{\"counter\":\"Additional amount of Ping-Pong rounds to conclude\",\"destination_\":\"Chain to send Ping message to\",\"recipient\":\"Recipient of Ping message\"}},\"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)\":{\"details\":\"Message recipient needs to ensure that merkle proof for the message is at least as old as the optimistic period that the recipient is using. Note: as this point it is checked that the \\\"message optimistic period\\\" has passed, however the period value itself could be anything, and thus could differ from the one that the recipient would like to enforce.\",\"params\":{\"content\":\"Raw bytes content of message\",\"nonce\":\"Message nonce on the origin domain\",\"origin\":\"Domain where message originated\",\"proofMaturity\":\"Message's merkle proof age in seconds\",\"sender\":\"Sender address on origin chain\",\"version\":\"Message version specified by sender\"}}},\"version\":1},\"userdoc\":{\"events\":{\"PingReceived(uint256)\":{\"notice\":\"Emitted when a Ping message is received. Will always send a Pong message back.\"},\"PingSent(uint256)\":{\"notice\":\"Emitted when a Ping message is sent. Triggered externally, or by receveing a Pong message with instructions to do more pings.\"},\"PongReceived(uint256)\":{\"notice\":\"Emitted when a Pong message is received. Will initiate a new Ping, if the counter in the message is non-zero.\"},\"PongSent(uint256)\":{\"notice\":\"Emitted when a Pong message is sent. Triggered whenever a Ping message is received.\"}},\"kind\":\"user\",\"methods\":{\"destination()\":{\"notice\":\"Local chain Destination: used for receiving messages\"},\"doPing(uint32,address,uint16)\":{\"notice\":\"Send a Ping message to destination chain. Upon receiving a Ping, a Pong message will be sent back. If `counter \u003e 0`, this process will be repeated when the Pong message is received.\"},\"origin()\":{\"notice\":\"Local chain Origin: used for sending messages\"},\"pingsReceived()\":{\"notice\":\"Amount of \\\"Ping\\\" messages received. Every received Ping message leads to sending a Pong message back to initial sender.\"},\"pingsSent()\":{\"notice\":\"Amount of \\\"Ping\\\" messages sent.\"},\"pongsReceived()\":{\"notice\":\"Amount of \\\"Pong\\\" messages received. When all messages are delivered, should be equal to `pingsSent`\"},\"receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)\":{\"notice\":\"Message recipient needs to implement this function in order to receive cross-chain messages.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/PingPongClient.sol\":\"PingPongClient\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/PingPongClient.sol\":{\"keccak256\":\"0x03e643db760356ec5a463ecddaabfbf483dfed8b370419b4a390fc5f54909226\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25da370a66df76adbd5cdef0b7cf6802ac80d54a3b04c67b91c62d9d64aad27a\",\"dweb:/ipfs/QmP9VmcgX1XM6LJaAXYyTgg6FwspNBJQiaV7og686hPRKN\"]}},\"version\":1}"},"hashes":{"destination()":"b269681d","doPing(uint32,address,uint16)":"08fe5e4e","doPings(uint16,uint32,address,uint16)":"aa402039","nextOptimisticPeriod()":"2bd56025","origin()":"938b5f32","pingsReceived()":"e3ac3ca0","pingsSent()":"b475cba3","pongsReceived()":"45a8b8ed","random()":"5ec01e4d","receiveBaseMessage(uint32,uint32,bytes32,uint256,uint32,bytes)":"032f287e"}},"solidity/PingPongClient.sol:RequestLib":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122060dba553ae95c9bcad0dfb20ddf4bbb898e49ea51a634f86260d5a58c53ab83164736f6c63430008110033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122060dba553ae95c9bcad0dfb20ddf4bbb898e49ea51a634f86260d5a58c53ab83164736f6c63430008110033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.17;\n\n// contracts/interfaces/IMessageRecipient.sol\n\ninterface IMessageRecipient {\n /**\n * @notice Message recipient needs to implement this function in order to\n * receive cross-chain messages.\n * @dev Message recipient needs to ensure that merkle proof for the message\n * is at least as old as the optimistic period that the recipient is using.\n * Note: as this point it is checked that the \"message optimistic period\" has passed,\n * however the period value itself could be anything, and thus could differ from the one\n * that the recipient would like to enforce.\n * @param origin Domain where message originated\n * @param nonce Message nonce on the origin domain\n * @param sender Sender address on origin chain\n * @param proofMaturity Message's merkle proof age in seconds\n * @param version Message version specified by sender\n * @param content Raw bytes content of message\n */\n function receiveBaseMessage(\n uint32 origin,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable;\n}\n\n// contracts/interfaces/InterfaceOrigin.sol\n\ninterface InterfaceOrigin {\n // ═══════════════════════════════════════════════ SEND MESSAGES ═══════════════════════════════════════════════════\n\n /**\n * @notice Send a message to the recipient located on destination domain.\n * @dev Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered.\n * Will revert if any of these is true:\n * - `destination` is equal to contract's local domain\n * - `content` length is greater than `MAX_CONTENT_BYTES`\n * - `msg.value` is lower than value of minimum tips for the given message\n * @param destination Domain of destination chain\n * @param recipient Address of recipient on destination chain as bytes32\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param content Raw bytes content of message\n * @return messageNonce Nonce of the sent message\n * @return messageHash Hash of the sent message\n */\n function sendBaseMessage(\n uint32 destination,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 paddedRequest,\n bytes memory content\n ) external payable returns (uint32 messageNonce, bytes32 messageHash);\n\n /**\n * @notice Send a manager message to the destination domain.\n * @dev This could only be called by AgentManager, which takes care of encoding the calldata payload.\n * Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain\n * so that the AgentManager could verify where the Manager Message came from and how mature is the proof.\n * Note: function is not payable, as no tips are required for sending a manager message.\n * Will revert if `destination` is equal to contract's local domain.\n * @param destination Domain of destination chain\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param payload Payload for calling AgentManager on destination chain (with extra security args)\n */\n function sendManagerMessage(uint32 destination, uint32 optimisticPeriod, bytes memory payload)\n external\n returns (uint32 messageNonce, bytes32 messageHash);\n\n // ════════════════════════════════════════════════ TIPS LOGIC ═════════════════════════════════════════════════════\n\n /**\n * @notice Withdraws locked base message tips to the recipient.\n * @dev Could only be called by a local AgentManager.\n * @param recipient Address to withdraw tips to\n * @param amount Tips value to withdraw\n */\n function withdrawTips(address recipient, uint256 amount) external;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /**\n * @notice Returns the minimum tips value for sending a message to a given destination.\n * @dev Using at least `tipsValue` as `msg.value` for `sendBaseMessage()`\n * will guarantee that the message will be accepted.\n * @param destination Domain of destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param contentLength The length of the message content\n * @return tipsValue Minimum tips value for a message to be accepted\n */\n function getMinimumTipsValue(uint32 destination, uint256 paddedRequest, uint256 contentLength)\n external\n view\n returns (uint256 tipsValue);\n}\n\n// contracts/libs/Errors.sol\n\n// ══════════════════════════════ INVALID CALLER ═══════════════════════════════\n\nerror CallerNotAgentManager();\nerror CallerNotDestination();\nerror CallerNotInbox();\nerror CallerNotSummit();\n\n// ══════════════════════════════ INCORRECT DATA ═══════════════════════════════\n\nerror IncorrectAttestation();\nerror IncorrectAgentDomain();\nerror IncorrectAgentIndex();\nerror IncorrectAgentProof();\nerror IncorrectAgentRoot();\nerror IncorrectDataHash();\nerror IncorrectDestinationDomain();\nerror IncorrectOriginDomain();\nerror IncorrectSnapshotProof();\nerror IncorrectSnapshotRoot();\nerror IncorrectState();\nerror IncorrectStatesAmount();\nerror IncorrectTipsProof();\nerror IncorrectVersionLength();\n\nerror IncorrectNonce();\nerror IncorrectSender();\nerror IncorrectRecipient();\n\nerror FlagOutOfRange();\nerror IndexOutOfRange();\nerror NonceOutOfRange();\n\nerror OutdatedNonce();\n\nerror UnformattedAttestation();\nerror UnformattedAttestationReport();\nerror UnformattedBaseMessage();\nerror UnformattedCallData();\nerror UnformattedCallDataPrefix();\nerror UnformattedMessage();\nerror UnformattedReceipt();\nerror UnformattedReceiptReport();\nerror UnformattedSignature();\nerror UnformattedSnapshot();\nerror UnformattedState();\nerror UnformattedStateReport();\n\n// ═══════════════════════════════ MERKLE TREES ════════════════════════════════\n\nerror LeafNotProven();\nerror MerkleTreeFull();\nerror NotEnoughLeafs();\nerror TreeHeightTooLow();\n\n// ═════════════════════════════ OPTIMISTIC PERIOD ═════════════════════════════\n\nerror BaseClientOptimisticPeriod();\nerror MessageOptimisticPeriod();\nerror SlashAgentOptimisticPeriod();\nerror WithdrawTipsOptimisticPeriod();\nerror ZeroProofMaturity();\n\n// ═══════════════════════════════ AGENT MANAGER ═══════════════════════════════\n\nerror AgentNotGuard();\nerror AgentNotNotary();\n\nerror AgentCantBeAdded();\nerror AgentNotActive();\nerror AgentNotActiveNorUnstaking();\nerror AgentNotFraudulent();\nerror AgentNotUnstaking();\nerror AgentUnknown();\n\nerror AgentRootNotProposed();\nerror AgentRootTimeoutNotOver();\n\nerror NotStuck();\n\nerror DisputeAlreadyResolved();\nerror DisputeNotOpened();\nerror DisputeTimeoutNotOver();\nerror GuardInDispute();\nerror NotaryInDispute();\n\nerror MustBeSynapseDomain();\nerror SynapseDomainForbidden();\n\n// ════════════════════════════════ DESTINATION ════════════════════════════════\n\nerror AlreadyExecuted();\nerror AlreadyFailed();\nerror DuplicatedSnapshotRoot();\nerror IncorrectMagicValue();\nerror GasLimitTooLow();\nerror GasSuppliedTooLow();\n\n// ══════════════════════════════════ ORIGIN ═══════════════════════════════════\n\nerror ContentLengthTooBig();\nerror EthTransferFailed();\nerror InsufficientEthBalance();\n\n// ════════════════════════════════ GAS ORACLE ═════════════════════════════════\n\nerror LocalGasDataNotSet();\nerror RemoteGasDataNotSet();\n\n// ═══════════════════════════════════ TIPS ════════════════════════════════════\n\nerror SummitTipTooHigh();\nerror TipsClaimMoreThanEarned();\nerror TipsClaimZero();\nerror TipsOverflow();\nerror TipsValueTooLow();\n\n// ════════════════════════════════ MEMORY VIEW ════════════════════════════════\n\nerror IndexedTooMuch();\nerror ViewOverrun();\nerror OccupiedMemory();\nerror UnallocatedMemory();\nerror PrecompileOutOfGas();\n\n// ═════════════════════════════════ MULTICALL ═════════════════════════════════\n\nerror MulticallFailed();\n\n// contracts/libs/TypeCasts.sol\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n}\n\n// contracts/libs/stack/Request.sol\n\n/// Request is encoded data with \"message execution request\".\ntype Request is uint192;\n\nusing RequestLib for Request global;\n\n/// Library for formatting _the request part_ of _the base messages_.\n/// - Request represents a message sender requirements for the message execution on the destination chain.\n/// - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory.\n/// \u003e gasDrop field is included for future compatibility and is ignored at the moment.\n///\n/// # Request stack layout (from highest bits to lowest)\n///\n/// | Position | Field | Type | Bytes | Description |\n/// | ---------- | -------- | ------ | ----- | ---------------------------------------------------- |\n/// | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient |\n/// | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution |\n/// | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |\n\nlibrary RequestLib {\n /// @dev Amount of bits to shift to gasDrop field\n uint192 private constant SHIFT_GAS_DROP = 12 * 8;\n /// @dev Amount of bits to shift to gasLimit field\n uint192 private constant SHIFT_GAS_LIMIT = 4 * 8;\n\n /// @notice Returns an encoded request with the given fields\n /// @param gasDrop_ Minimum amount of gas token to drop to the recipient (ignored at the moment)\n /// @param gasLimit_ Minimum amount of gas units to supply for execution\n /// @param version_ Base message version to pass to the recipient\n function encodeRequest(uint96 gasDrop_, uint64 gasLimit_, uint32 version_) internal pure returns (Request) {\n // Casts below are upcasts, so they are safe\n return Request.wrap(uint192(gasDrop_) \u003c\u003c SHIFT_GAS_DROP | uint192(gasLimit_) \u003c\u003c SHIFT_GAS_LIMIT | version_);\n }\n\n /// @notice Wraps the padded encoded request into a Request-typed value.\n /// @dev The \"padded\" request is simply an encoded request casted to uint256 (highest bits are set to zero).\n /// Casting to uint256 is done automatically in Solidity, so no extra actions from consumers are needed.\n /// The highest bits are discarded, so that the contracts dealing with encoded requests\n /// don't need to be updated, if a new field is added.\n function wrapPadded(uint256 paddedRequest) internal pure returns (Request) {\n // Casting to uint192 will truncate the highest bits, which is the behavior we want\n return Request.wrap(uint192(paddedRequest));\n }\n\n /// @notice Returns the requested of gas token to drop to the recipient.\n function gasDrop(Request request) internal pure returns (uint96) {\n // Casting to uint96 will truncate the highest bits, which is the behavior we want\n return uint96(Request.unwrap(request) \u003e\u003e SHIFT_GAS_DROP);\n }\n\n /// @notice Returns the requested minimum amount of gas units to supply for execution.\n function gasLimit(Request request) internal pure returns (uint64) {\n // Casting to uint64 will truncate the highest bits, which is the behavior we want\n return uint64(Request.unwrap(request) \u003e\u003e SHIFT_GAS_LIMIT);\n }\n\n /// @notice Returns the requested base message version to pass to the recipient.\n function version(Request request) internal pure returns (uint32) {\n // Casting to uint32 will truncate the highest bits, which is the behavior we want\n return uint32(Request.unwrap(request));\n }\n}\n\n// contracts/client/MessageRecipient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\nabstract contract MessageRecipient is IMessageRecipient {\n struct MessageRequest {\n uint96 gasDrop;\n uint64 gasLimit;\n uint32 version;\n }\n\n /// @notice Local chain Origin: used for sending messages\n address public immutable origin;\n\n /// @notice Local chain Destination: used for receiving messages\n address public immutable destination;\n\n constructor(address origin_, address destination_) {\n origin = origin_;\n destination = destination_;\n }\n\n /// @inheritdoc IMessageRecipient\n function receiveBaseMessage(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable {\n if (msg.sender != destination) revert CallerNotDestination();\n if (nonce == 0) revert IncorrectNonce();\n if (sender == 0) revert IncorrectSender();\n if (proofMaturity == 0) revert ZeroProofMaturity();\n _receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);\n }\n\n /**\n * @dev Child contracts should implement the logic for receiving a Base Message in an \"unsafe way\".\n * Following checks HAVE been performed:\n * - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).\n * - Nonce is not zero.\n * - Message sender on origin chain is not a zero address.\n * - Proof maturity is not zero.\n * Following checks HAVE NOT been performed (thus \"unsafe\"):\n * - Message sender on origin chain could be anything non-zero at this point.\n * - Proof maturity could be anything non-zero at this point.\n */\n function _receiveBaseMessageUnsafe(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) internal virtual;\n\n /**\n * @dev Sends a message to given destination chain. Full `msg.value` is used to pay for the message tips.\n * `_getMinimumTipsValue()` could be used to calculate the minimum required tips value, and should be also\n * exposed as a public view function to estimate the tips value before sending a message off-chain.\n * This function is not exposed in MessageRecipient, as the message encoding is implemented by the child contract.\n * @param destination_ Domain of the destination chain\n * @param recipient Address of the recipient on destination chain\n * @param optimisticPeriod Optimistic period for the message\n * @param tipsValue Tips to be paid for sending the message\n * @param request Message execution request on destination chain\n * @param content The message content\n */\n function _sendBaseMessage(\n uint32 destination_,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 tipsValue,\n MessageRequest memory request,\n bytes memory content\n ) internal returns (uint32 messageNonce, bytes32 messageHash) {\n if (recipient == 0) revert IncorrectRecipient();\n return InterfaceOrigin(origin).sendBaseMessage{value: tipsValue}(\n destination_, recipient, optimisticPeriod, _encodeRequest(request), content\n );\n }\n\n /**\n * @dev Returns the minimum tips value for sending a message to given destination chain.\n * @param destination_ Domain of the destination chain\n * @param request Message execution request on destination chain\n * @param contentLength Length of the message content\n */\n function _getMinimumTipsValue(uint32 destination_, MessageRequest memory request, uint256 contentLength)\n internal\n view\n returns (uint256 tipsValue)\n {\n return InterfaceOrigin(origin).getMinimumTipsValue(destination_, _encodeRequest(request), contentLength);\n }\n\n /**\n * @dev Encodes a message execution request into format that Origin contract is using.\n * @param request Message execution request on destination chain\n * @return paddedRequest Encoded request\n */\n function _encodeRequest(MessageRequest memory request) internal pure returns (uint256 paddedRequest) {\n return Request.unwrap(RequestLib.encodeRequest(request.gasDrop, request.gasLimit, request.version));\n }\n}\n\n// contracts/client/PingPongClient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\ncontract PingPongClient is MessageRecipient {\n using TypeCasts for address;\n\n struct PingPongMessage {\n uint256 pingId;\n bool isPing;\n uint16 counter;\n }\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n uint256 public random;\n\n /// @notice Amount of \"Ping\" messages sent.\n uint256 public pingsSent;\n\n /// @notice Amount of \"Ping\" messages received.\n /// Every received Ping message leads to sending a Pong message back to initial sender.\n uint256 public pingsReceived;\n\n /// @notice Amount of \"Pong\" messages received.\n /// When all messages are delivered, should be equal to `pingsSent`\n uint256 public pongsReceived;\n\n // ══════════════════════════════════════════════════ EVENTS ═══════════════════════════════════════════════════════\n\n /// @notice Emitted when a Ping message is sent.\n /// Triggered externally, or by receveing a Pong message with instructions to do more pings.\n event PingSent(uint256 pingId);\n\n /// @notice Emitted when a Ping message is received.\n /// Will always send a Pong message back.\n event PingReceived(uint256 pingId);\n\n /// @notice Emitted when a Pong message is sent.\n /// Triggered whenever a Ping message is received.\n event PongSent(uint256 pingId);\n\n /// @notice Emitted when a Pong message is received.\n /// Will initiate a new Ping, if the counter in the message is non-zero.\n event PongReceived(uint256 pingId);\n\n // ════════════════════════════════════════════════ CONSTRUCTOR ════════════════════════════════════════════════════\n\n constructor(address origin_, address destination_) MessageRecipient(origin_, destination_) {\n // Initiate \"random\" value\n random = uint256(keccak256(abi.encode(block.number)));\n }\n\n // ═══════════════════════════════════════════════ MESSAGE LOGIC ═══════════════════════════════════════════════════\n\n function doPings(uint16 pingCount, uint32 destination_, address recipient, uint16 counter) external {\n for (uint256 i = 0; i \u003c pingCount; ++i) {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n }\n\n /// @notice Send a Ping message to destination chain.\n /// Upon receiving a Ping, a Pong message will be sent back.\n /// If `counter \u003e 0`, this process will be repeated when the Pong message is received.\n /// @param destination_ Chain to send Ping message to\n /// @param recipient Recipient of Ping message\n /// @param counter Additional amount of Ping-Pong rounds to conclude\n function doPing(uint32 destination_, address recipient, uint16 counter) external {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n function nextOptimisticPeriod() public view returns (uint32 period) {\n // Use random optimistic period up to one minute\n return uint32(random % 1 minutes);\n }\n\n // ═════════════════════════════════════ INTERNAL LOGIC: RECEIVE MESSAGES ══════════════════════════════════════════\n\n /// @inheritdoc MessageRecipient\n function _receiveBaseMessageUnsafe(uint32 origin_, uint32, bytes32 sender, uint256, uint32, bytes memory content)\n internal\n override\n {\n PingPongMessage memory message = abi.decode(content, (PingPongMessage));\n if (message.isPing) {\n // Ping is received\n ++pingsReceived;\n emit PingReceived(message.pingId);\n // Send Pong back\n _pong(origin_, sender, message);\n } else {\n // Pong is received\n ++pongsReceived;\n emit PongReceived(message.pingId);\n // Send extra ping, if initially requested\n if (message.counter != 0) {\n _ping(origin_, sender, message.counter - 1);\n }\n }\n }\n\n // ═══════════════════════════════════════ INTERNAL LOGIC: SEND MESSAGES ═══════════════════════════════════════════\n\n /// @dev Returns a random optimistic period value from 0 to 59 seconds.\n function _optimisticPeriod() internal returns (uint32 period) {\n // Use random optimistic period up to one minute\n period = nextOptimisticPeriod();\n // Adjust \"random\" value\n random = uint256(keccak256(abi.encode(random)));\n }\n\n /**\n * @dev Send a \"Ping\" or \"Pong\" message.\n * @param destination_ Domain of destination chain\n * @param recipient Message recipient on destination chain\n * @param message Ping-pong message\n */\n function _sendMessage(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n // TODO: this probably shouldn't be hardcoded\n MessageRequest memory request = MessageRequest({gasDrop: 0, gasLimit: 500_000, version: 0});\n bytes memory content = abi.encode(message);\n _sendBaseMessage({\n destination_: destination_,\n recipient: recipient,\n optimisticPeriod: _optimisticPeriod(),\n tipsValue: 0,\n request: request,\n content: content\n });\n }\n\n /// @dev Initiate a new Ping-Pong round.\n function _ping(uint32 destination_, bytes32 recipient, uint16 counter) internal {\n uint256 pingId = pingsSent++;\n _sendMessage(destination_, recipient, PingPongMessage({pingId: pingId, isPing: true, counter: counter}));\n emit PingSent(pingId);\n }\n\n /// @dev Send a Pong message back.\n function _pong(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n _sendMessage(\n destination_, recipient, PingPongMessage({pingId: message.pingId, isPing: false, counter: message.counter})\n );\n emit PongSent(message.pingId);\n }\n}\n","language":"Solidity","languageVersion":"0.8.17","compilerVersion":"0.8.17","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"11875:2478:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;11875:2478:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"11875:2478:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"notice":"Library for formatting _the request part_ of _the base messages_. - Request represents a message sender requirements for the message execution on the destination chain. - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory. \u003e gasDrop field is included for future compatibility and is ignored at the moment. # Request stack layout (from highest bits to lowest) | Position | Field | Type | Bytes | Description | | ---------- | -------- | ------ | ----- | ---------------------------------------------------- | | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient | | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution | | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |","version":1},"developerDoc":{"kind":"dev","methods":{},"stateVariables":{"SHIFT_GAS_DROP":{"details":"Amount of bits to shift to gasDrop field"},"SHIFT_GAS_LIMIT":{"details":"Amount of bits to shift to gasLimit field"}},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"stateVariables\":{\"SHIFT_GAS_DROP\":{\"details\":\"Amount of bits to shift to gasDrop field\"},\"SHIFT_GAS_LIMIT\":{\"details\":\"Amount of bits to shift to gasLimit field\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"notice\":\"Library for formatting _the request part_ of _the base messages_. - Request represents a message sender requirements for the message execution on the destination chain. - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory. \u003e gasDrop field is included for future compatibility and is ignored at the moment. # Request stack layout (from highest bits to lowest) | Position | Field | Type | Bytes | Description | | ---------- | -------- | ------ | ----- | ---------------------------------------------------- | | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient | | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution | | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/PingPongClient.sol\":\"RequestLib\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/PingPongClient.sol\":{\"keccak256\":\"0x03e643db760356ec5a463ecddaabfbf483dfed8b370419b4a390fc5f54909226\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25da370a66df76adbd5cdef0b7cf6802ac80d54a3b04c67b91c62d9d64aad27a\",\"dweb:/ipfs/QmP9VmcgX1XM6LJaAXYyTgg6FwspNBJQiaV7og686hPRKN\"]}},\"version\":1}"},"hashes":{}},"solidity/PingPongClient.sol:TypeCasts":{"code":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220a70e5c7fe26bfed7aa178c90dc0ad3f454b336c91590c44e804b947ab7747adc64736f6c63430008110033","runtime-code":"0x73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220a70e5c7fe26bfed7aa178c90dc0ad3f454b336c91590c44e804b947ab7747adc64736f6c63430008110033","info":{"source":"// SPDX-License-Identifier: MIT\npragma solidity =0.8.17;\n\n// contracts/interfaces/IMessageRecipient.sol\n\ninterface IMessageRecipient {\n /**\n * @notice Message recipient needs to implement this function in order to\n * receive cross-chain messages.\n * @dev Message recipient needs to ensure that merkle proof for the message\n * is at least as old as the optimistic period that the recipient is using.\n * Note: as this point it is checked that the \"message optimistic period\" has passed,\n * however the period value itself could be anything, and thus could differ from the one\n * that the recipient would like to enforce.\n * @param origin Domain where message originated\n * @param nonce Message nonce on the origin domain\n * @param sender Sender address on origin chain\n * @param proofMaturity Message's merkle proof age in seconds\n * @param version Message version specified by sender\n * @param content Raw bytes content of message\n */\n function receiveBaseMessage(\n uint32 origin,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable;\n}\n\n// contracts/interfaces/InterfaceOrigin.sol\n\ninterface InterfaceOrigin {\n // ═══════════════════════════════════════════════ SEND MESSAGES ═══════════════════════════════════════════════════\n\n /**\n * @notice Send a message to the recipient located on destination domain.\n * @dev Recipient has to conform to IMessageRecipient interface, otherwise message won't be delivered.\n * Will revert if any of these is true:\n * - `destination` is equal to contract's local domain\n * - `content` length is greater than `MAX_CONTENT_BYTES`\n * - `msg.value` is lower than value of minimum tips for the given message\n * @param destination Domain of destination chain\n * @param recipient Address of recipient on destination chain as bytes32\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param content Raw bytes content of message\n * @return messageNonce Nonce of the sent message\n * @return messageHash Hash of the sent message\n */\n function sendBaseMessage(\n uint32 destination,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 paddedRequest,\n bytes memory content\n ) external payable returns (uint32 messageNonce, bytes32 messageHash);\n\n /**\n * @notice Send a manager message to the destination domain.\n * @dev This could only be called by AgentManager, which takes care of encoding the calldata payload.\n * Note: (msgOrigin, proofMaturity) security args will be added to payload on the destination chain\n * so that the AgentManager could verify where the Manager Message came from and how mature is the proof.\n * Note: function is not payable, as no tips are required for sending a manager message.\n * Will revert if `destination` is equal to contract's local domain.\n * @param destination Domain of destination chain\n * @param optimisticPeriod Optimistic period for message execution on destination chain\n * @param payload Payload for calling AgentManager on destination chain (with extra security args)\n */\n function sendManagerMessage(uint32 destination, uint32 optimisticPeriod, bytes memory payload)\n external\n returns (uint32 messageNonce, bytes32 messageHash);\n\n // ════════════════════════════════════════════════ TIPS LOGIC ═════════════════════════════════════════════════════\n\n /**\n * @notice Withdraws locked base message tips to the recipient.\n * @dev Could only be called by a local AgentManager.\n * @param recipient Address to withdraw tips to\n * @param amount Tips value to withdraw\n */\n function withdrawTips(address recipient, uint256 amount) external;\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n /**\n * @notice Returns the minimum tips value for sending a message to a given destination.\n * @dev Using at least `tipsValue` as `msg.value` for `sendBaseMessage()`\n * will guarantee that the message will be accepted.\n * @param destination Domain of destination chain\n * @param paddedRequest Padded encoded message execution request on destination chain\n * @param contentLength The length of the message content\n * @return tipsValue Minimum tips value for a message to be accepted\n */\n function getMinimumTipsValue(uint32 destination, uint256 paddedRequest, uint256 contentLength)\n external\n view\n returns (uint256 tipsValue);\n}\n\n// contracts/libs/Errors.sol\n\n// ══════════════════════════════ INVALID CALLER ═══════════════════════════════\n\nerror CallerNotAgentManager();\nerror CallerNotDestination();\nerror CallerNotInbox();\nerror CallerNotSummit();\n\n// ══════════════════════════════ INCORRECT DATA ═══════════════════════════════\n\nerror IncorrectAttestation();\nerror IncorrectAgentDomain();\nerror IncorrectAgentIndex();\nerror IncorrectAgentProof();\nerror IncorrectAgentRoot();\nerror IncorrectDataHash();\nerror IncorrectDestinationDomain();\nerror IncorrectOriginDomain();\nerror IncorrectSnapshotProof();\nerror IncorrectSnapshotRoot();\nerror IncorrectState();\nerror IncorrectStatesAmount();\nerror IncorrectTipsProof();\nerror IncorrectVersionLength();\n\nerror IncorrectNonce();\nerror IncorrectSender();\nerror IncorrectRecipient();\n\nerror FlagOutOfRange();\nerror IndexOutOfRange();\nerror NonceOutOfRange();\n\nerror OutdatedNonce();\n\nerror UnformattedAttestation();\nerror UnformattedAttestationReport();\nerror UnformattedBaseMessage();\nerror UnformattedCallData();\nerror UnformattedCallDataPrefix();\nerror UnformattedMessage();\nerror UnformattedReceipt();\nerror UnformattedReceiptReport();\nerror UnformattedSignature();\nerror UnformattedSnapshot();\nerror UnformattedState();\nerror UnformattedStateReport();\n\n// ═══════════════════════════════ MERKLE TREES ════════════════════════════════\n\nerror LeafNotProven();\nerror MerkleTreeFull();\nerror NotEnoughLeafs();\nerror TreeHeightTooLow();\n\n// ═════════════════════════════ OPTIMISTIC PERIOD ═════════════════════════════\n\nerror BaseClientOptimisticPeriod();\nerror MessageOptimisticPeriod();\nerror SlashAgentOptimisticPeriod();\nerror WithdrawTipsOptimisticPeriod();\nerror ZeroProofMaturity();\n\n// ═══════════════════════════════ AGENT MANAGER ═══════════════════════════════\n\nerror AgentNotGuard();\nerror AgentNotNotary();\n\nerror AgentCantBeAdded();\nerror AgentNotActive();\nerror AgentNotActiveNorUnstaking();\nerror AgentNotFraudulent();\nerror AgentNotUnstaking();\nerror AgentUnknown();\n\nerror AgentRootNotProposed();\nerror AgentRootTimeoutNotOver();\n\nerror NotStuck();\n\nerror DisputeAlreadyResolved();\nerror DisputeNotOpened();\nerror DisputeTimeoutNotOver();\nerror GuardInDispute();\nerror NotaryInDispute();\n\nerror MustBeSynapseDomain();\nerror SynapseDomainForbidden();\n\n// ════════════════════════════════ DESTINATION ════════════════════════════════\n\nerror AlreadyExecuted();\nerror AlreadyFailed();\nerror DuplicatedSnapshotRoot();\nerror IncorrectMagicValue();\nerror GasLimitTooLow();\nerror GasSuppliedTooLow();\n\n// ══════════════════════════════════ ORIGIN ═══════════════════════════════════\n\nerror ContentLengthTooBig();\nerror EthTransferFailed();\nerror InsufficientEthBalance();\n\n// ════════════════════════════════ GAS ORACLE ═════════════════════════════════\n\nerror LocalGasDataNotSet();\nerror RemoteGasDataNotSet();\n\n// ═══════════════════════════════════ TIPS ════════════════════════════════════\n\nerror SummitTipTooHigh();\nerror TipsClaimMoreThanEarned();\nerror TipsClaimZero();\nerror TipsOverflow();\nerror TipsValueTooLow();\n\n// ════════════════════════════════ MEMORY VIEW ════════════════════════════════\n\nerror IndexedTooMuch();\nerror ViewOverrun();\nerror OccupiedMemory();\nerror UnallocatedMemory();\nerror PrecompileOutOfGas();\n\n// ═════════════════════════════════ MULTICALL ═════════════════════════════════\n\nerror MulticallFailed();\n\n// contracts/libs/TypeCasts.sol\n\nlibrary TypeCasts {\n // alignment preserving cast\n function addressToBytes32(address addr) internal pure returns (bytes32) {\n return bytes32(uint256(uint160(addr)));\n }\n\n // alignment preserving cast\n function bytes32ToAddress(bytes32 buf) internal pure returns (address) {\n return address(uint160(uint256(buf)));\n }\n}\n\n// contracts/libs/stack/Request.sol\n\n/// Request is encoded data with \"message execution request\".\ntype Request is uint192;\n\nusing RequestLib for Request global;\n\n/// Library for formatting _the request part_ of _the base messages_.\n/// - Request represents a message sender requirements for the message execution on the destination chain.\n/// - Request occupies a single storage word, and thus is stored on stack instead of being stored in memory.\n/// \u003e gasDrop field is included for future compatibility and is ignored at the moment.\n///\n/// # Request stack layout (from highest bits to lowest)\n///\n/// | Position | Field | Type | Bytes | Description |\n/// | ---------- | -------- | ------ | ----- | ---------------------------------------------------- |\n/// | (024..012] | gasDrop | uint96 | 12 | Minimum amount of gas token to drop to the recipient |\n/// | (012..004] | gasLimit | uint64 | 8 | Minimum amount of gas units to supply for execution |\n/// | (004..000] | version | uint32 | 4 | Base message version to pass to the recipient |\n\nlibrary RequestLib {\n /// @dev Amount of bits to shift to gasDrop field\n uint192 private constant SHIFT_GAS_DROP = 12 * 8;\n /// @dev Amount of bits to shift to gasLimit field\n uint192 private constant SHIFT_GAS_LIMIT = 4 * 8;\n\n /// @notice Returns an encoded request with the given fields\n /// @param gasDrop_ Minimum amount of gas token to drop to the recipient (ignored at the moment)\n /// @param gasLimit_ Minimum amount of gas units to supply for execution\n /// @param version_ Base message version to pass to the recipient\n function encodeRequest(uint96 gasDrop_, uint64 gasLimit_, uint32 version_) internal pure returns (Request) {\n // Casts below are upcasts, so they are safe\n return Request.wrap(uint192(gasDrop_) \u003c\u003c SHIFT_GAS_DROP | uint192(gasLimit_) \u003c\u003c SHIFT_GAS_LIMIT | version_);\n }\n\n /// @notice Wraps the padded encoded request into a Request-typed value.\n /// @dev The \"padded\" request is simply an encoded request casted to uint256 (highest bits are set to zero).\n /// Casting to uint256 is done automatically in Solidity, so no extra actions from consumers are needed.\n /// The highest bits are discarded, so that the contracts dealing with encoded requests\n /// don't need to be updated, if a new field is added.\n function wrapPadded(uint256 paddedRequest) internal pure returns (Request) {\n // Casting to uint192 will truncate the highest bits, which is the behavior we want\n return Request.wrap(uint192(paddedRequest));\n }\n\n /// @notice Returns the requested of gas token to drop to the recipient.\n function gasDrop(Request request) internal pure returns (uint96) {\n // Casting to uint96 will truncate the highest bits, which is the behavior we want\n return uint96(Request.unwrap(request) \u003e\u003e SHIFT_GAS_DROP);\n }\n\n /// @notice Returns the requested minimum amount of gas units to supply for execution.\n function gasLimit(Request request) internal pure returns (uint64) {\n // Casting to uint64 will truncate the highest bits, which is the behavior we want\n return uint64(Request.unwrap(request) \u003e\u003e SHIFT_GAS_LIMIT);\n }\n\n /// @notice Returns the requested base message version to pass to the recipient.\n function version(Request request) internal pure returns (uint32) {\n // Casting to uint32 will truncate the highest bits, which is the behavior we want\n return uint32(Request.unwrap(request));\n }\n}\n\n// contracts/client/MessageRecipient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\nabstract contract MessageRecipient is IMessageRecipient {\n struct MessageRequest {\n uint96 gasDrop;\n uint64 gasLimit;\n uint32 version;\n }\n\n /// @notice Local chain Origin: used for sending messages\n address public immutable origin;\n\n /// @notice Local chain Destination: used for receiving messages\n address public immutable destination;\n\n constructor(address origin_, address destination_) {\n origin = origin_;\n destination = destination_;\n }\n\n /// @inheritdoc IMessageRecipient\n function receiveBaseMessage(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) external payable {\n if (msg.sender != destination) revert CallerNotDestination();\n if (nonce == 0) revert IncorrectNonce();\n if (sender == 0) revert IncorrectSender();\n if (proofMaturity == 0) revert ZeroProofMaturity();\n _receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);\n }\n\n /**\n * @dev Child contracts should implement the logic for receiving a Base Message in an \"unsafe way\".\n * Following checks HAVE been performed:\n * - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).\n * - Nonce is not zero.\n * - Message sender on origin chain is not a zero address.\n * - Proof maturity is not zero.\n * Following checks HAVE NOT been performed (thus \"unsafe\"):\n * - Message sender on origin chain could be anything non-zero at this point.\n * - Proof maturity could be anything non-zero at this point.\n */\n function _receiveBaseMessageUnsafe(\n uint32 origin_,\n uint32 nonce,\n bytes32 sender,\n uint256 proofMaturity,\n uint32 version,\n bytes memory content\n ) internal virtual;\n\n /**\n * @dev Sends a message to given destination chain. Full `msg.value` is used to pay for the message tips.\n * `_getMinimumTipsValue()` could be used to calculate the minimum required tips value, and should be also\n * exposed as a public view function to estimate the tips value before sending a message off-chain.\n * This function is not exposed in MessageRecipient, as the message encoding is implemented by the child contract.\n * @param destination_ Domain of the destination chain\n * @param recipient Address of the recipient on destination chain\n * @param optimisticPeriod Optimistic period for the message\n * @param tipsValue Tips to be paid for sending the message\n * @param request Message execution request on destination chain\n * @param content The message content\n */\n function _sendBaseMessage(\n uint32 destination_,\n bytes32 recipient,\n uint32 optimisticPeriod,\n uint256 tipsValue,\n MessageRequest memory request,\n bytes memory content\n ) internal returns (uint32 messageNonce, bytes32 messageHash) {\n if (recipient == 0) revert IncorrectRecipient();\n return InterfaceOrigin(origin).sendBaseMessage{value: tipsValue}(\n destination_, recipient, optimisticPeriod, _encodeRequest(request), content\n );\n }\n\n /**\n * @dev Returns the minimum tips value for sending a message to given destination chain.\n * @param destination_ Domain of the destination chain\n * @param request Message execution request on destination chain\n * @param contentLength Length of the message content\n */\n function _getMinimumTipsValue(uint32 destination_, MessageRequest memory request, uint256 contentLength)\n internal\n view\n returns (uint256 tipsValue)\n {\n return InterfaceOrigin(origin).getMinimumTipsValue(destination_, _encodeRequest(request), contentLength);\n }\n\n /**\n * @dev Encodes a message execution request into format that Origin contract is using.\n * @param request Message execution request on destination chain\n * @return paddedRequest Encoded request\n */\n function _encodeRequest(MessageRequest memory request) internal pure returns (uint256 paddedRequest) {\n return Request.unwrap(RequestLib.encodeRequest(request.gasDrop, request.gasLimit, request.version));\n }\n}\n\n// contracts/client/PingPongClient.sol\n\n// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════\n\n// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════\n\ncontract PingPongClient is MessageRecipient {\n using TypeCasts for address;\n\n struct PingPongMessage {\n uint256 pingId;\n bool isPing;\n uint16 counter;\n }\n\n // ══════════════════════════════════════════════════ STORAGE ══════════════════════════════════════════════════════\n\n uint256 public random;\n\n /// @notice Amount of \"Ping\" messages sent.\n uint256 public pingsSent;\n\n /// @notice Amount of \"Ping\" messages received.\n /// Every received Ping message leads to sending a Pong message back to initial sender.\n uint256 public pingsReceived;\n\n /// @notice Amount of \"Pong\" messages received.\n /// When all messages are delivered, should be equal to `pingsSent`\n uint256 public pongsReceived;\n\n // ══════════════════════════════════════════════════ EVENTS ═══════════════════════════════════════════════════════\n\n /// @notice Emitted when a Ping message is sent.\n /// Triggered externally, or by receveing a Pong message with instructions to do more pings.\n event PingSent(uint256 pingId);\n\n /// @notice Emitted when a Ping message is received.\n /// Will always send a Pong message back.\n event PingReceived(uint256 pingId);\n\n /// @notice Emitted when a Pong message is sent.\n /// Triggered whenever a Ping message is received.\n event PongSent(uint256 pingId);\n\n /// @notice Emitted when a Pong message is received.\n /// Will initiate a new Ping, if the counter in the message is non-zero.\n event PongReceived(uint256 pingId);\n\n // ════════════════════════════════════════════════ CONSTRUCTOR ════════════════════════════════════════════════════\n\n constructor(address origin_, address destination_) MessageRecipient(origin_, destination_) {\n // Initiate \"random\" value\n random = uint256(keccak256(abi.encode(block.number)));\n }\n\n // ═══════════════════════════════════════════════ MESSAGE LOGIC ═══════════════════════════════════════════════════\n\n function doPings(uint16 pingCount, uint32 destination_, address recipient, uint16 counter) external {\n for (uint256 i = 0; i \u003c pingCount; ++i) {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n }\n\n /// @notice Send a Ping message to destination chain.\n /// Upon receiving a Ping, a Pong message will be sent back.\n /// If `counter \u003e 0`, this process will be repeated when the Pong message is received.\n /// @param destination_ Chain to send Ping message to\n /// @param recipient Recipient of Ping message\n /// @param counter Additional amount of Ping-Pong rounds to conclude\n function doPing(uint32 destination_, address recipient, uint16 counter) external {\n _ping(destination_, recipient.addressToBytes32(), counter);\n }\n\n // ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════\n\n function nextOptimisticPeriod() public view returns (uint32 period) {\n // Use random optimistic period up to one minute\n return uint32(random % 1 minutes);\n }\n\n // ═════════════════════════════════════ INTERNAL LOGIC: RECEIVE MESSAGES ══════════════════════════════════════════\n\n /// @inheritdoc MessageRecipient\n function _receiveBaseMessageUnsafe(uint32 origin_, uint32, bytes32 sender, uint256, uint32, bytes memory content)\n internal\n override\n {\n PingPongMessage memory message = abi.decode(content, (PingPongMessage));\n if (message.isPing) {\n // Ping is received\n ++pingsReceived;\n emit PingReceived(message.pingId);\n // Send Pong back\n _pong(origin_, sender, message);\n } else {\n // Pong is received\n ++pongsReceived;\n emit PongReceived(message.pingId);\n // Send extra ping, if initially requested\n if (message.counter != 0) {\n _ping(origin_, sender, message.counter - 1);\n }\n }\n }\n\n // ═══════════════════════════════════════ INTERNAL LOGIC: SEND MESSAGES ═══════════════════════════════════════════\n\n /// @dev Returns a random optimistic period value from 0 to 59 seconds.\n function _optimisticPeriod() internal returns (uint32 period) {\n // Use random optimistic period up to one minute\n period = nextOptimisticPeriod();\n // Adjust \"random\" value\n random = uint256(keccak256(abi.encode(random)));\n }\n\n /**\n * @dev Send a \"Ping\" or \"Pong\" message.\n * @param destination_ Domain of destination chain\n * @param recipient Message recipient on destination chain\n * @param message Ping-pong message\n */\n function _sendMessage(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n // TODO: this probably shouldn't be hardcoded\n MessageRequest memory request = MessageRequest({gasDrop: 0, gasLimit: 500_000, version: 0});\n bytes memory content = abi.encode(message);\n _sendBaseMessage({\n destination_: destination_,\n recipient: recipient,\n optimisticPeriod: _optimisticPeriod(),\n tipsValue: 0,\n request: request,\n content: content\n });\n }\n\n /// @dev Initiate a new Ping-Pong round.\n function _ping(uint32 destination_, bytes32 recipient, uint16 counter) internal {\n uint256 pingId = pingsSent++;\n _sendMessage(destination_, recipient, PingPongMessage({pingId: pingId, isPing: true, counter: counter}));\n emit PingSent(pingId);\n }\n\n /// @dev Send a Pong message back.\n function _pong(uint32 destination_, bytes32 recipient, PingPongMessage memory message) internal {\n _sendMessage(\n destination_, recipient, PingPongMessage({pingId: message.pingId, isPing: false, counter: message.counter})\n );\n emit PongSent(message.pingId);\n }\n}\n","language":"Solidity","languageVersion":"0.8.17","compilerVersion":"0.8.17","compilerOptions":"--combined-json bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc,metadata,hashes --optimize --optimize-runs 10000 --allow-paths ., ./, ../","srcMap":"10411:350:0:-:0;;;;;;;;;;;;;;;-1:-1:-1;;;10411:350:0;;;;;;;;;;;;;;;;;","srcMapRuntime":"10411:350:0:-:0;;;;;;;;","abiDefinition":[],"userDoc":{"kind":"user","methods":{},"version":1},"developerDoc":{"kind":"dev","methods":{},"version":1},"metadata":"{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"solidity/PingPongClient.sol\":\"TypeCasts\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"solidity/PingPongClient.sol\":{\"keccak256\":\"0x03e643db760356ec5a463ecddaabfbf483dfed8b370419b4a390fc5f54909226\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://25da370a66df76adbd5cdef0b7cf6802ac80d54a3b04c67b91c62d9d64aad27a\",\"dweb:/ipfs/QmP9VmcgX1XM6LJaAXYyTgg6FwspNBJQiaV7og686hPRKN\"]}},\"version\":1}"},"hashes":{}}}