From d6cdd5d1b4bd0dc87c90b58040485c6545523ed0 Mon Sep 17 00:00:00 2001 From: James Kim Date: Mon, 16 Sep 2024 10:42:56 -0400 Subject: [PATCH] docs: add L2L2CDM contract example --- contracts/lib/optimism | 2 +- docs/src/SUMMARY.md | 6 +- .../guides/interop/bridging-superchainweth.md | 1 - .../interop/calling-destination-contract.md | 4 - .../interop/relay-using-viem-full-code.md | 136 ---------------- docs/src/guides/interop/relay-using-viem.md | 148 +++++++++++++++++- .../interop/writing-contract-using-l2cdm.md | 1 - 7 files changed, 150 insertions(+), 148 deletions(-) delete mode 100644 docs/src/guides/interop/bridging-superchainweth.md delete mode 100644 docs/src/guides/interop/calling-destination-contract.md delete mode 100644 docs/src/guides/interop/relay-using-viem-full-code.md delete mode 100644 docs/src/guides/interop/writing-contract-using-l2cdm.md diff --git a/contracts/lib/optimism b/contracts/lib/optimism index f70248a0..ac443ef0 160000 --- a/contracts/lib/optimism +++ b/contracts/lib/optimism @@ -1 +1 @@ -Subproject commit f70248a0ace5375d578e99697df7a5a1bd2d20ee +Subproject commit ac443ef0146b6c71d3460d3aba2f67cddd8c04c4 diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 545e40ca..bfb4669f 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -25,9 +25,9 @@ - [Interoperability](./guides/interop/README.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) + - [Calling a contract on destination chain]() + - [Writing a contract that uses `L2ToL2CrossDomainMessenger`]() + - [Bridging SuperchainWETH]() # Examples diff --git a/docs/src/guides/interop/bridging-superchainweth.md b/docs/src/guides/interop/bridging-superchainweth.md deleted file mode 100644 index f866e429..00000000 --- a/docs/src/guides/interop/bridging-superchainweth.md +++ /dev/null @@ -1 +0,0 @@ -# Bridging SuperchainWETH diff --git a/docs/src/guides/interop/calling-destination-contract.md b/docs/src/guides/interop/calling-destination-contract.md deleted file mode 100644 index c0b37c6b..00000000 --- a/docs/src/guides/interop/calling-destination-contract.md +++ /dev/null @@ -1,4 +0,0 @@ -# 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/relay-using-viem-full-code.md b/docs/src/guides/interop/relay-using-viem-full-code.md deleted file mode 100644 index bb6bdabb..00000000 --- a/docs/src/guides/interop/relay-using-viem-full-code.md +++ /dev/null @@ -1,136 +0,0 @@ -```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 index d2e7f7b4..f79c2be7 100644 --- a/docs/src/guides/interop/relay-using-viem.md +++ b/docs/src/guides/interop/relay-using-viem.md @@ -11,11 +11,12 @@ We'll perform the SuperchainERC20 interop transfer in [First steps](../../gettin - [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) +- [Full code snippet](#full-code-snippet) ## Steps -The full code snippet can be found [here](./relay-using-viem-full-code.md) +The full code snippet can be found [here](#full-code-snippet) ### 1. Start `supersim` @@ -178,4 +179,147 @@ const balance = await opChainBClient.readContract({ 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 + +## Full code snippet + +
+ Click to view + +```ts +// Using viem to transfer L2NativeSuperchainERC20 + +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/writing-contract-using-l2cdm.md b/docs/src/guides/interop/writing-contract-using-l2cdm.md deleted file mode 100644 index 98b2e459..00000000 --- a/docs/src/guides/interop/writing-contract-using-l2cdm.md +++ /dev/null @@ -1 +0,0 @@ -# Writing a contract that uses L2ToL2CrossDomainMessenger