diff --git a/README.md b/README.md index aef2886a..1bed5cfd 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ supersim --interop.autorelay ``` **2. Mint tokens to transfer on chain 901** -Run the following command to mint 1000 tokens to the recipient address: +Run the following command to mint 1000 `L2NativeSuperchainERC20` tokens to the recipient address: ```sh cast send 0x61a6eF395d217eD7C79e1B84880167a417796172 "mint(address _to, uint256 _amount)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 1000 --rpc-url http://127.0.0.1:9545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 diff --git a/docs/book.toml b/docs/book.toml index ec08714d..a4a29072 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -4,3 +4,6 @@ language = "en" multilingual = false src = "src" title = "Supersim" + +[output.html] +git-repository-url = "https://github.com/ethereum-optimism/supersim" \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index c1991a4b..545e40ca 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -23,8 +23,10 @@ - [Sending deposit transactions](./guides/deposit-transactions.md) - [Interoperability](./guides/interop/README.md) - - [Fetching identifiers for interop messages](./guides/interop/interop-identifiers.md) - - [Calling contract on other chain](./guides/interop/calling-destination-contract.md) + - [Manually relaying interop messages with cast](./guides/interop/manually-relaying-interop-messages-cast.md) + - [Using viem to relay interop messages (TypeScript)](./guides/interop/relay-using-viem.md) + - [Calling a contract on destination chain](./guides/interop/calling-destination-contract.md) + - [Writing a contract that uses `L2ToL2CrossDomainMessenger`](./guides/interop/writing-contract-using-l2cdm.md) - [Bridging SuperchainWETH](./guides/interop/bridging-superchainweth.md) # Examples diff --git a/docs/src/getting-started/first-steps.md b/docs/src/getting-started/first-steps.md index 9399a8a0..30662ecc 100644 --- a/docs/src/getting-started/first-steps.md +++ b/docs/src/getting-started/first-steps.md @@ -35,13 +35,13 @@ To simplify this process, you can use the `--interop.autorelay` flag. This flag ### 1. Start `supersim` with the autorelayer enabled -``` +```sh supersim --interop.autorelay ``` ### 2. Mint tokens to transfer on chain 901 -Run the following command to mint 1000 tokens to the recipient address: +Run the following command to mint 1000 `L2NativeSuperchainERC20` tokens to the recipient address: ```sh cast send 0x61a6eF395d217eD7C79e1B84880167a417796172 "mint(address _to, uint256 _amount)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 1000 --rpc-url http://127.0.0.1:9545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 diff --git a/docs/src/guides/interop/calling-destination-contract.md b/docs/src/guides/interop/calling-destination-contract.md index eb0a4ac8..c0b37c6b 100644 --- a/docs/src/guides/interop/calling-destination-contract.md +++ b/docs/src/guides/interop/calling-destination-contract.md @@ -1 +1,4 @@ # Calling contracts on destination chain + +This guide explains how to use the L2ToL2CrossDomainMessenger to call a contract on chain B from chain A. + diff --git a/docs/src/guides/interop/interop-identifiers.md b/docs/src/guides/interop/interop-identifiers.md deleted file mode 100644 index 59d93934..00000000 --- a/docs/src/guides/interop/interop-identifiers.md +++ /dev/null @@ -1 +0,0 @@ -# Fetching identifiers for interop messages diff --git a/docs/src/guides/interop/manually-relaying-interop-messages-cast.md b/docs/src/guides/interop/manually-relaying-interop-messages-cast.md new file mode 100644 index 00000000..cceca34b --- /dev/null +++ b/docs/src/guides/interop/manually-relaying-interop-messages-cast.md @@ -0,0 +1,226 @@ + +# Manually relaying interop messages with `cast` + +This guide describes how to form a [message identifier](https://specs.optimism.io/interop/messaging.html#message-identifier) to execute a message on a destination chain. + +We'll perform the SuperchainERC20 interop transfer in [First steps](../../getting-started/first-steps.md#send-an-interoperable-superchainerc20-token-from-chain-901-to-902-l2-to-l2-message-passing) again, this time manually relaying the message without the autorelayer. + +- [Overview](#overview) + - [Contracts used](#contracts-used) + - [High level steps](#high-level-steps) + - [Message identifier](#message-identifier) +- [Steps](#steps) + - [1. Start `supersim`](#1-start-supersim) + - [2. Mint tokens to transfer on chain 901](#2-mint-tokens-to-transfer-on-chain-901) + - [3. Initiate the send transaction on chain 901](#3-initiate-the-send-transaction-on-chain-901) + - [4. Get the log emitted by the `L2ToL2CrossDomainMessenger`](#4-get-the-log-emitted-by-the-l2tol2crossdomainmessenger) + - [5. Get the block timestamp the log was emitted in](#5-get-the-block-timestamp-the-log-was-emitted-in) + - [6. Prepare the identifier](#6-prepare-the-identifier) + - [7. Send the relayMessage transaction](#7-send-the-relaymessage-transaction) + - [8. Check the balance on chain 902](#8-check-the-balance-on-chain-902) +- [Alternatives](#alternatives) + + + +## Overview + +### Contracts used +- [L2NativeSuperchainERC20](https://github.com/ethereum-optimism/supersim/blob/main/contracts/src/L2NativeSuperchainERC20.sol) + - `0x61a6eF395d217eD7C79e1B84880167a417796172` +- [CrossL2Inbox](https://github.com/ethereum-optimism/optimism/blob/92ed64e171c6eb9c6a080c626640e8836f0653cc/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol) + - `0x4200000000000000000000000000000000000022` +- [L2ToL2CrossDomainMessenger](https://github.com/ethereum-optimism/optimism/blob/92ed64e171c6eb9c6a080c626640e8836f0653cc/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol) + - `0x4200000000000000000000000000000000000023` + + +### High level steps + +Sending an interop message using the `L2ToL2CrossDomainMessenger`: + +**on source chain** (OPChainA 901) + +1. call the `L2ToL2CrossChainMessenger.sendMessage` + - the `L2NativeSuperchainERC20.sendERC20` contract will call this under the hood when +2. get the log identifier and the message payload + +**on destination chain** (OPChainB 902) + +3. call `CrossL2Inbox.executeMessage` + - this calls `L2ToL2CrossDomainMessenger.relayMessage` + - which then calls `L2NativeSuperchainERC20.relayERC20` + +### Message identifier + +A message identifier uniquely identifies a log emitted on a chain. +The sequencer and smart contracts (CrossL2Inbox) use the identifier to perform [invariant checks](https://specs.optimism.io/interop/messaging.html#messaging-invariants) to confirm that the source message is valid. + +```solidity +struct Identifier { + address origin; // Account (contract) that emits the log + uint256 blocknumber; // Block number in which the log was emitted + uint256 logIndex; // Index of the log in the array of all logs emitted in the block + uint256 timestamp; // Timestamp that the log was emitted + uint256 chainid; // Chain ID of the chain that emitted the log +} +``` + +## Steps + +### 1. Start `supersim` + +```sh +supersim +``` + +### 2. Mint tokens to transfer on chain 901 + +Run the following command to mint 1000 `L2NativeSuperchainERC20` tokens to the recipient address: + +```sh +cast send 0x61a6eF395d217eD7C79e1B84880167a417796172 "mint(address _to, uint256 _amount)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 1000 --rpc-url http://127.0.0.1:9545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +``` + +### 3. Initiate the send transaction on chain 901 + +Send the tokens from Chain 901 to Chain 902 using the following command: + +```sh +cast send 0x61a6eF395d217eD7C79e1B84880167a417796172 "sendERC20(address _to, uint256 _amount, uint256 _chainId)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 1000 902 --rpc-url http://127.0.0.1:9545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +``` + +### 4. Get the log emitted by the `L2ToL2CrossDomainMessenger` + +The token contract calls the [L2ToL2CrossDomainMessenger](https://github.com/ethereum-optimism/optimism/blob/92ed64e171c6eb9c6a080c626640e8836f0653cc/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol), which emits a message (log) that can be executed on the destination chain. + +```sh +cast logs --address 0x4200000000000000000000000000000000000023 --rpc-url http://127.0.0.1:9545 +``` + +**example result:** + +```sh +- address: 0x4200000000000000000000000000000000000023 + blockHash: 0x81ddbe2f7ff770011077fcec89a7e3a3ca54f2b7ba110695dd22f2a730457f60 + blockNumber: 18 + data: 0x1ecd26f200000000000000000000000000000000000000000000000000000000000003860000000000000000000000000000000000000000000000000000000000000385000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061a6ef395d217ed7c79e1b84880167a41779617200000000000000000000000061a6ef395d217ed7c79e1b84880167a41779617200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000064d9f50046000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000 + logIndex: 1 + removed: false + topics: [] + transactionHash: 0x5fad43480766ee63bdd07864f6b811432b042fded2a7e05df643f0fac4490817 + transactionIndex: 0 +``` + +### 5. Get the block timestamp the log was emitted in + +Since the message identifier requires the block timestamp, fetch the block info to get the timestamp. + +```sh +cast block 0xREPLACE_WITH_CORRECT_BLOCKHASH --rpc-url http://127.0.0.1:9545 +``` + +**example result:** + +```sh +baseFeePerGas 1 +difficulty 0 +extraData 0x +gasLimit 30000000 +gasUsed 62040 +hash 0x81ddbe2f7ff770011077fcec89a7e3a3ca54f2b7ba110695dd22f2a730457f60 +logsBloom 0x00000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000010000000000000000008000000000000000000000000000002000008000000000000000000000000000000000000000000000000020000000000000100000800000000000008000000000010000000000000000000000000000000000000040000000000000000000000000000000000000200000000000000000000000000000000010000000000000000000000000000800002000000200000000000000000000000002000000000000000000020000000000000000000000000000000000000000000000000000000000000000000 +miner 0x4200000000000000000000000000000000000011 +mixHash 0x0000000000000000000000000000000000000000000000000000000000000000 +nonce 0x0000000000000000 +number 18 +parentHash 0x552eae8494730d53e845b762042cebda51821ecf544562481a78eb1b36134b72 +transactionsRoot 0x07ae44e7678b08e1d021fb83ede835cab2963f9627209bd8dc8cd4b6eb00df19 +receiptsRoot 0xbb5c311ac46940b49e19aae1802f988a66e2e180a0432f169f9382a49d7e9893 +sha3Uncles 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347 +size 723 +stateRoot 0xfa2449b5188bec99bda8a917e0494dce0f1a7d76ea1a8cc68edf42b6f8c1021b +timestamp 1726453637 +withdrawalsRoot +totalDifficulty 0 +transactions: [ + 0x5fad43480766ee63bdd07864f6b811432b042fded2a7e05df643f0fac4490817 +] +``` + +### 6. Prepare the identifier + +Now we have all the information needed for the message (log) identifier. + +| **Parameter** | **Value** | **Note** | +|---------------|--------------------------------------------|----------------------------| +| origin | 0x4200000000000000000000000000000000000023 | L2ToL2CrossDomainMessenger | +| blocknumber | 18 | from step 4 | +| logIndex | 1 | from step 4 | +| timestamp | 1726453637 | from step 5 | +| chainid | 901 | OPChainA chainID | + +### 7. Send the relayMessage transaction + +Call `executeMessage` on [CrossL2Inbox](https://github.com/ethereum-optimism/optimism/blob/92ed64e171c6eb9c6a080c626640e8836f0653cc/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol#L133), which in turn calls `relayMessage` on [L2ToL2CrossDomainMessenger](https://github.com/ethereum-optimism/optimism/blob/92ed64e171c6eb9c6a080c626640e8836f0653cc/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol#L126) + +```solidity +// CrossL2Inbox.sol (truncated for brevity) + +contract CrossL2Inbox { + struct Identifier { + address origin; + uint256 blockNumber; + uint256 logIndex; + uint256 timestamp; + uint256 chainId; + } + + // ... + + function executeMessage( + Identifier calldata _id, + address _target, + bytes memory _message + ) payable + + // ... +} +``` + + +**`executeMessage` parameters** + +- `Identifier calldata _id`: identifier pointing to the log on the source chain + - same as the identifier in step 6. +- `address _target`: address of the contract on the destination chain to call. + - address of the `L2ToL2CrossChainMessenger` +- `bytes memory _message`: calldata to call the contract on the destination chain with. + - `log.data` from the log emitted in step 4 + +Note that in our case, both `_id.origin` and `_target` happen to be the same with `0x4200000000000000000000000000000000000023`, the `L2ToL2CrossChainMessenger` predeploy. But they do not have to be the same if you're not using the messenger contracts. + +Below is an example call, but make sure to replace them with the correct values you received in previous steps. + +```sh +cast send 0x4200000000000000000000000000000000000022 \ +"executeMessage((address, uint256, uint256, uint256, uint256), address, bytes)" \ +"(0x4200000000000000000000000000000000000023, 18, 1, 1726453637, 901)" \ +0x4200000000000000000000000000000000000023 \ +0x1ecd26f200000000000000000000000000000000000000000000000000000000000003860000000000000000000000000000000000000000000000000000000000000385000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061a6ef395d217ed7c79e1b84880167a41779617200000000000000000000000061a6ef395d217ed7c79e1b84880167a41779617200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000064d9f50046000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000 \ +--rpc-url http://127.0.0.1:9546 \ +--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +``` + +### 8. Check the balance on chain 902 + +Verify that the balance of the L2NativeSuperchainERC20 on chain 902 has increased: + +```sh +cast balance --erc20 0x61a6eF395d217eD7C79e1B84880167a417796172 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --rpc-url http://127.0.0.1:9546 +``` + +## Alternatives + +This is obviously very tedious to do by hand 😅. Here are some alternatives + +- use `supersim --interop.autorelay` - this only works on supersim, but relayers for the testnet/prod environment will be available soon! +- [use `viem` bindings/actions](relay-using-viem.md) - if you're using typescript, we have bindings available to make fetching identifiers and relaying messages easier diff --git a/docs/src/guides/interop/relay-using-viem-full-code.md b/docs/src/guides/interop/relay-using-viem-full-code.md new file mode 100644 index 00000000..bb6bdabb --- /dev/null +++ b/docs/src/guides/interop/relay-using-viem-full-code.md @@ -0,0 +1,136 @@ +```ts +import { + http, + encodeFunctionData, + createWalletClient, + parseAbi, + defineChain, + publicActions, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { + publicActionsL2, + walletActionsL2, + extractMessageIdentifierFromLogs, +} from "@eth-optimism/viem"; +import { anvil } from "viem/chains"; + +// Define constants - L2NativeSuperchainERC20 contract address is the same on every chain +const L2_NATIVE_SUPERCHAINERC20_ADDRESS = + "0x61a6eF395d217eD7C79e1B84880167a417796172"; + +const L2_TO_L2_CROSS_DOMAIN_MESSENGER_ADDRESS = + "0x4200000000000000000000000000000000000023"; + +// account for 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +const account = privateKeyToAccount( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", +); + +// Define chains +const opChainA = defineChain({ + ...anvil, + id: 901, + name: "OPChainA", + rpcUrls: { + default: { + http: ["http://127.0.0.1:9545"], + }, + }, +}); + +const opChainB = defineChain({ + ...anvil, + id: 902, + name: "OPChainB", + rpcUrls: { + default: { + http: ["http://127.0.0.1:9546"], + }, + }, +}); + +// Configure op clients +const opChainAClient = createWalletClient({ + transport: http(), + chain: opChainA, + account, +}) + .extend(walletActionsL2()) + .extend(publicActionsL2()) + .extend(publicActions); + +const opChainBClient = createWalletClient({ + transport: http(), + chain: opChainB, + account, +}) + .extend(walletActionsL2()) + .extend(publicActionsL2()) + .extend(publicActions); + +// ####### +// OP Chain A +// ####### +// 1. Mint 1000 `L2NativeSuperchainERC20` token + +const mintTxHash = await opChainAClient.writeContract({ + address: L2_NATIVE_SUPERCHAINERC20_ADDRESS, + abi: parseAbi(["function mint(address to, uint256 amount)"]), + functionName: "mint", + args: [account.address, 1000n], +}); + +await opChainAClient.waitForTransactionReceipt({ hash: mintTxHash }); + +// 2. Initiate sendERC20 tx +console.log("Initiating sendERC20 on OPChainA..."); +const sendERC20TxHash = await opChainAClient.writeContract({ + address: L2_NATIVE_SUPERCHAINERC20_ADDRESS, + abi: parseAbi([ + "function sendERC20(address _to, uint256 _amount, uint256 _chainId)", + ]), + functionName: "sendERC20", + args: [account.address, 1000n, BigInt(opChainB.id)], +}); + +const sendERC20TxReceipt = await opChainAClient.waitForTransactionReceipt({ + hash: sendERC20TxHash, +}); + +// 3. Grab the message identifier from the logs +const { id: messageIdentifier, payload: l2ToL2CrossDomainMessengerCalldata } = + await extractMessageIdentifierFromLogs(opChainAClient, { + receipt: sendERC20TxReceipt, + }); + +// ########## +// OP Chain B +// ########## +// 4. Execute the relayERC20 function on OPChainB +console.log("Building execute relayERC20 on OPChainB..."); +const executeArgs = await opChainBClient.buildExecuteL2ToL2Message({ + id: messageIdentifier, + target: L2_TO_L2_CROSS_DOMAIN_MESSENGER_ADDRESS, + message: l2ToL2CrossDomainMessengerCalldata, +}); + +console.log("Executing L2 to L2 message on OPChainB..."); + +const executeTxHash = await opChainBClient.executeL2ToL2Message(executeArgs); + +await opChainBClient.waitForTransactionReceipt({ + hash: executeTxHash, +}); + +// 5. Check balance on OPChainB +const balance = await opChainBClient.readContract({ + address: L2_NATIVE_SUPERCHAINERC20_ADDRESS, + abi: parseAbi(["function balanceOf(address) view returns (uint256)"]), + functionName: "balanceOf", + args: [account.address], +}); + +console.log(`Balance on OPChainB: ${balance}`); + +``` \ No newline at end of file diff --git a/docs/src/guides/interop/relay-using-viem.md b/docs/src/guides/interop/relay-using-viem.md new file mode 100644 index 00000000..d2e7f7b4 --- /dev/null +++ b/docs/src/guides/interop/relay-using-viem.md @@ -0,0 +1,181 @@ + +# Using `viem` to relay interop messages (TypeScript) + +This guide describes how to use [`viem`](https://viem.sh/) to send and relay interop messages using the `L2ToL2CrossDomainMessenger` + +We'll perform the SuperchainERC20 interop transfer in [First steps](../../getting-started/first-steps.md#send-an-interoperable-superchainerc20-token-from-chain-901-to-902-l2-to-l2-message-passing) and [Manually relaying interop messages with `cast`](./manually-relaying-interop-messages-cast.md) again, this time using `viem` to relay the message without the autorelayer. + +- [Steps](#steps) + - [1. Start `supersim`](#1-start-supersim) + - [2. Install TypeScript packages](#2-install-typescript-packages) + - [3. Define chains and constants](#3-define-chains-and-constants) + - [4. Mint and send `L2NativeSuperchainERC20` on source chain](#4-mint-and-send-l2nativesuperchainerc20-on-source-chain) + - [5. Relay the message on the destination chain](#5-relay-the-message-on-the-destination-chain) + + +## Steps + +The full code snippet can be found [here](./relay-using-viem-full-code.md) + +### 1. Start `supersim` + +```sh +supersim +``` + +### 2. Install TypeScript packages +```sh +npm i viem @eth-optimism/viem +``` + +### 3. Define chains and constants + +```ts +import { + http, + encodeFunctionData, + createWalletClient, + parseAbi, + defineChain, + publicActions, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { + publicActionsL2, + walletActionsL2, + extractMessageIdentifierFromLogs, +} from "@eth-optimism/viem"; +import { anvil } from "viem/chains"; + +// Define constants - L2NativeSuperchainERC20 contract address is the same on every chain +const L2_NATIVE_SUPERCHAINERC20_ADDRESS = + "0x61a6eF395d217eD7C79e1B84880167a417796172"; + +const L2_TO_L2_CROSS_DOMAIN_MESSENGER_ADDRESS = + "0x4200000000000000000000000000000000000023"; + +// account for 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +const account = privateKeyToAccount( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", +); + +// Define chains +const opChainA = defineChain({ + ...anvil, + id: 901, + name: "OPChainA", + rpcUrls: { + default: { + http: ["http://127.0.0.1:9545"], + }, + }, +}); + +const opChainB = defineChain({ + ...anvil, + id: 902, + name: "OPChainB", + rpcUrls: { + default: { + http: ["http://127.0.0.1:9546"], + }, + }, +}); + +// Configure op clients +const opChainAClient = createWalletClient({ + transport: http(), + chain: opChainA, + account, +}) + .extend(walletActionsL2()) + .extend(publicActionsL2()) + .extend(publicActions); + +const opChainBClient = createWalletClient({ + transport: http(), + chain: opChainB, + account, +}) + .extend(walletActionsL2()) + .extend(publicActionsL2()) + .extend(publicActions); +``` + +### 4. Mint and send `L2NativeSuperchainERC20` on source chain + +```ts +// ####### +// OP Chain A +// ####### + +// 1. Mint 1000 `L2NativeSuperchainERC20` token + +const mintTxHash = await opChainAClient.writeContract({ + address: L2_NATIVE_SUPERCHAINERC20_ADDRESS, + abi: parseAbi(["function mint(address to, uint256 amount)"]), + functionName: "mint", + args: [account.address, 1000n], +}); + +await opChainAClient.waitForTransactionReceipt({ hash: mintTxHash }); + +// 2. Initiate sendERC20 tx +console.log("Initiating sendERC20 on OPChainA..."); +const sendERC20TxHash = await opChainAClient.writeContract({ + address: L2_NATIVE_SUPERCHAINERC20_ADDRESS, + abi: parseAbi([ + "function sendERC20(address _to, uint256 _amount, uint256 _chainId)", + ]), + functionName: "sendERC20", + args: [account.address, 1000n, BigInt(opChainB.id)], +}); + +const sendERC20TxReceipt = await opChainAClient.waitForTransactionReceipt({ + hash: sendERC20TxHash, +}); + +// 3. Grab the message identifier from the logs +const { id: messageIdentifier, payload: l2ToL2CrossDomainMessengerCalldata } = + await extractMessageIdentifierFromLogs(opChainAClient, { + receipt: sendERC20TxReceipt, + }); + +``` + +### 5. Relay the message on the destination chain + +```ts + +// ########## +// OP Chain B +// ########## + +// 4. Execute the relayERC20 function on OPChainB +console.log("Building execute relayERC20 on OPChainB..."); +const executeArgs = await opChainBClient.buildExecuteL2ToL2Message({ + id: messageIdentifier, + target: L2_TO_L2_CROSS_DOMAIN_MESSENGER_ADDRESS, + message: l2ToL2CrossDomainMessengerCalldata, +}); + +console.log("Executing L2 to L2 message on OPChainB..."); + +const executeTxHash = await opChainBClient.executeL2ToL2Message(executeArgs); + +await opChainBClient.waitForTransactionReceipt({ + hash: executeTxHash, +}); + +// 5. Check balance on OPChainB +const balance = await opChainBClient.readContract({ + address: L2_NATIVE_SUPERCHAINERC20_ADDRESS, + abi: parseAbi(["function balanceOf(address) view returns (uint256)"]), + functionName: "balanceOf", + args: [account.address], +}); + +console.log(`Balance on OPChainB: ${balance}`); +``` + +The full code snippet can be found [here](./relay-using-viem-full-code.md) \ No newline at end of file diff --git a/docs/src/guides/interop/writing-contract-using-l2cdm.md b/docs/src/guides/interop/writing-contract-using-l2cdm.md new file mode 100644 index 00000000..98b2e459 --- /dev/null +++ b/docs/src/guides/interop/writing-contract-using-l2cdm.md @@ -0,0 +1 @@ +# Writing a contract that uses L2ToL2CrossDomainMessenger