Skip to content

Commit

Permalink
Merge pull request #127 from OffchainLabs/update-tutorials-3
Browse files Browse the repository at this point in the history
Update tutorials to ArbSDK v4 (3)
  • Loading branch information
TucksonDev authored Nov 6, 2024
2 parents 6944861 + 250f5fc commit 99c0af9
Show file tree
Hide file tree
Showing 24 changed files with 402 additions and 410 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ yarn install

#### :white_check_mark: Moving stuff around

- ⤴️ 🔹 [Deposit Ether](./packages/eth-deposit/)
- ⤵️ 🔹 [Withdraw Ether](./packages/eth-withdraw/)
- ⤴️ 🔹 [Deposit Ether or native token](./packages/eth-deposit/)
- ⤴️ 🔹 [Deposit Ether or native token to a different address](./packages/eth-deposit-to-different-address/)
- ⤵️ 🔹 [Withdraw Ether or native token](./packages/eth-withdraw/)
- ⤴️ 💸 [Deposit Token](./packages/token-deposit/)
- ⤵️ 💸 [Withdraw token](./packages/token-withdraw/)
- ⤴️ 🔹 [Contract alias control in the child chain, and fund-transfer guide](./packages/contract-deposit/)
Expand All @@ -38,7 +39,7 @@ yarn install

- ®️ [Arb Address Table](./packages/address-table/)
- 🌉 [Bridging Custom Token](./packages/custom-token-bridging/)
- ✈️ [Delayed inbox message(l2MSG)](./packages/delayedInbox-l2msg/)
- ✈️ [Send a signed transaction from the parent chain](./packages/delayedInbox-l2msg/)
- 🎁 [Redeem Retryable Ticket](./packages/redeem-failed-retryable/)
- 🌀 [Deposit Ether or Tokens from L1 to L3](./packages/l1-l3-teleport/)

Expand Down
18 changes: 8 additions & 10 deletions packages/delayedInbox-l2msg/.env-sample
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# This is a sample .env file for use in local development.

# Duplicate this file as .env here

# Your Private key

DEVNET_PRIVKEY ='0x your key here'

# Hosted Aggregator Node (JSON-RPC Endpoint)

L2RPC="https://sepolia-rollup.arbitrum.io/rpc"
# Your private key
PRIVATE_KEY="0x your key here"

# Ethereum RPC; i.e., for Sepolia https://sepolia.infura.io/v3/<your infura key>
# The main chain's RPC
# (this can be an Arbitrum network, or your Orbit chain)
CHAIN_RPC="https://sepolia-rollup.arbitrum.io/rpc"

L1RPC=
# The parent chain's RPC
# (this can be Ethereum, or the chain your Orbit chain settles to)
PARENT_CHAIN_RPC="https://sepolia.infura.io/v3/<your infura key>"
29 changes: 15 additions & 14 deletions packages/delayedInbox-l2msg/README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
# delayedInbox-l2msg Tutorial
# Tutorial: send a signed transaction from the parent chain

(Note this can only be done in nitro stack, not in Arbitrum classic.)
`delayedInbox-l2msg` shows how to send a signed transaction to your chain (also referred to as an L2Message) only using an RPC of the parent chain. This demo has 2 parts:

delayedInbox-l2msg is a simple sample example that allows you to send a l2 msg without using sequencer way, this can be used when a sequencer censors your tx or when a sequencer is down.
1. how to send a normal signed transaction using the delayed inbox ([./scripts/normalTx.js](./scripts/normalTx.js))
2. how to withdraw your funds back without sending a transaction directly to the sequencer ([./scripts/withdrawFunds.js](./scripts/withdrawFunds.js))

The demo allows you to send an L2 message without having to use the L2 RPC (only using the L1 RPC). This demo has 2 parts; (1) one part will show how to send a normal L2 transaction using the delayed inbox, (2) another will show how to withdraw your funds back without the sequencer.
## Bypassing the sequencer

If the sequencer goes down when running the `Withdraw Funds`, you need to use our [Arbitrum SDK](https://github.com/OffchainLabs/arbitrum-sdk/blob/master/src/lib/inbox/inbox.ts#L256) to force include your tx to continue. (example [here](https://github.com/OffchainLabs/arbitrum-sdk/blob/401fa424bb4c21b54b77d95fbc95faec15787fe2/fork_test/inbox.test.ts#L131))
This tutorial also shows the initial step to take if the [sequencer is misbehaving](https://docs.arbitrum.io/how-arbitrum-works/sequencer#unhappyuncommon-case-sequencer-isnt-doing-its-job). In that case, after 24 hours have passed from the moment the message was sent from the parent chain, you can use the SequencerInbox's `forceInclusion` method to move it from the delayed inbox into the core inbox, at which point it's considered finalized.

## Config Environment Variables
You can also use the [Arbitrum SDK](https://github.com/OffchainLabs/arbitrum-sdk/blob/v4.0.1/src/lib/inbox/inbox.ts#L349-L355) to force include your transaction. See an example [here](https://github.com/OffchainLabs/arbitrum-sdk/blob/v4.0.1/tests/fork/inbox.test.ts#L112).

## Set environment variables

Set the values shown in `.env-sample` as environmental variables. To copy it into a `.env` file:

```bash
cp .env-sample .env
```

(you'll still need to edit some variables, i.e., `DEVNET_PRIVKEY`)
You'll still need to edit some variables, i.e., `PRIVATE_KEY`, `CHAIN_RPC` and `PARENT_CHAIN_RPC`.

Note that you can also set the environment variables in an `.env` file in the root of the monorepo, which will be available in all tutorials.

### Run Demo
## Run

Normal Transaction:
Normal transaction:

```bash
yarn normalTx
```

Withdraw Funds:
Withdraw funds:

```bash
yarn withdrawFunds
```

## Curious to see the output on the Arbitrum chain?

Once the script is successfully executed, you can go to the [Arbitrum nitro block explorer](https://sepolia.arbiscan.io), enter your L2 address, and see the corresponding transactions on the Arbitrum chain!

<p align="left">
<img width="350" height="150" src= "../../assets/logo.svg" />
</p>
4 changes: 2 additions & 2 deletions packages/delayedInbox-l2msg/contracts/greeter.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.6.11;
pragma solidity ^0.8.0;

contract Greeter {
string greeting;
Expand All @@ -11,7 +11,7 @@ contract Greeter {
_;
}

constructor(string memory _greeting) public {
constructor(string memory _greeting) {
greeting = _greeting;
deployer = msg.sender;
}
Expand Down
1 change: 0 additions & 1 deletion packages/delayedInbox-l2msg/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require('@nomiclabs/hardhat-ethers')

const { hardhatConfig } = require('arb-shared-dependencies')

module.exports = hardhatConfig
6 changes: 1 addition & 5 deletions packages/delayedInbox-l2msg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@
},
"author": "Offchain Labs, Inc.",
"license": "Apache-2.0",
"devDependencies": {
"hardhat": "^2.10.1"
},
"dependencies": {
"hardhat": "^2.10.1",
"@arbitrum/sdk": "^v3.1.9"
"@arbitrum/sdk": "^v4.0.1"
}
}
142 changes: 76 additions & 66 deletions packages/delayedInbox-l2msg/scripts/normalTx.js
Original file line number Diff line number Diff line change
@@ -1,112 +1,122 @@
const { providers, Wallet, ethers } = require('ethers')
const hre = require('hardhat')
const { arbLog, requireEnvVariables } = require('arb-shared-dependencies')
const { ethers } = require('hardhat')
const { providers, Wallet } = require('ethers')
const {
getL2Network,
addDefaultLocalNetwork,
} = require('@arbitrum/sdk/dist/lib/dataEntities/networks')
const { InboxTools } = require('@arbitrum/sdk')
requireEnvVariables(['DEVNET_PRIVKEY', 'L2RPC', 'L1RPC'])
arbLog,
requireEnvVariables,
addCustomNetworkFromFile,
} = require('arb-shared-dependencies')
const { getArbitrumNetwork, InboxTools } = require('@arbitrum/sdk')
require('dotenv').config()
requireEnvVariables(['PRIVATE_KEY', 'CHAIN_RPC', 'PARENT_CHAIN_RPC'])

/**
* Set up: instantiate L1 / L2 wallets connected to providers
* Set up: instantiate wallets connected to providers
*/
const walletPrivateKey = process.env.DEVNET_PRIVKEY
const walletPrivateKey = process.env.PRIVATE_KEY

const l1Provider = new providers.JsonRpcProvider(process.env.L1RPC)
const l2Provider = new providers.JsonRpcProvider(process.env.L2RPC)
const parentChainProvider = new providers.JsonRpcProvider(
process.env.PARENT_CHAIN_RPC
)
const childChainProvider = new providers.JsonRpcProvider(process.env.CHAIN_RPC)

const l1Wallet = new Wallet(walletPrivateKey, l1Provider)
const l2Wallet = new Wallet(walletPrivateKey, l2Provider)
const parentChainWallet = new Wallet(walletPrivateKey, parentChainProvider)
const childChainWallet = new Wallet(walletPrivateKey, childChainProvider)

const main = async () => {
await arbLog('DelayedInbox normal contract call (L2MSG_signedTx)')

/**
* Add the default local network configuration to the SDK
* to allow this script to run on a local node
* Add the custom network configuration to the SDK if present
*/
addDefaultLocalNetwork()

const l2Network = await getL2Network(await l2Wallet.getChainId())

const inboxSdk = new InboxTools(l1Wallet, l2Network)
addCustomNetworkFromFile()

/**
* We deploy greeter to L2, to see if delayed inbox tx can be executed as we thought
* Use childChainNetwork to create an Arbitrum SDK InboxTools instance
*/
const L2Greeter = await (
await hre.ethers.getContractFactory('Greeter')
).connect(l2Wallet)

console.log('Deploying Greeter on L2 👋👋')

const l2Greeter = await L2Greeter.deploy('Hello world')
await l2Greeter.deployed()
console.log(`deployed to ${l2Greeter.address}`)
const childChainNetwork = await getArbitrumNetwork(childChainProvider)
const inboxTools = new InboxTools(parentChainWallet, childChainNetwork)

/**
* Let's log the L2 greeting string
* We deploy greeter to the child chain, to interact with it from the parent chain
*/
const currentL2Greeting = await l2Greeter.greet()
console.log(`Current L2 greeting: "${currentL2Greeting}"`)
console.log('Deploying Greeter to the child chain 👋👋')

console.log(
`Now we send a l2 tx through l1 delayed inbox (Please don't send any tx on l2 using ${l2Wallet.address} during this time):`
const Greeter = (await ethers.getContractFactory('Greeter')).connect(
childChainWallet
)
const greeter = await Greeter.deploy('Hello world')
await greeter.deployed()
console.log(`Greeter deployed to ${greeter.address}`)

/**
* Here we have a new greeting message that we want to set as the L2 greeting; we'll be setting it by sending it as a message from delayed inbox!!!
* Let's log the starting greeting string
*/
const newGreeting = 'Greeting from delayedInbox'

const GreeterIface = l2Greeter.interface
const currentGreeting = await greeter.greet()
console.log(`Current greeting: "${currentGreeting}"`)

const calldatal2 = GreeterIface.encodeFunctionData('setGreeting', [
newGreeting,
/**
* Here we have a new greeting message that we want to set in the contract;
* we'll be setting it by sending it as a message from the parent chain through the delayed inbox!!!
*/
console.log(
`Now we send a message to be executed on the child chain, through the delayed inbox of the parent chain (make sure you don't send any transaction directly on the child chain using ${childChainWallet.address} during this time):`
)
const newGreetingToSet = 'Greeting from delayedInbox'
const GreeterIface = greeter.interface
const calldata = GreeterIface.encodeFunctionData('setGreeting', [
newGreetingToSet,
])

const transactionl2Request = {
data: calldatal2,
to: l2Greeter.address,
const transactionRequest = {
data: calldata,
to: greeter.address,
value: 0,
}

/**
* We need extract l2's tx hash first so we can check if this tx executed on l2 later.
* We need to extract the transaction hash in the child chain first so we can check later if it was executed
*/
const l2SignedTx = await inboxSdk.signL2Tx(transactionl2Request, l2Wallet)

const l2Txhash = ethers.utils.parseTransaction(l2SignedTx).hash

const l1Tx = await inboxSdk.sendL2SignedTx(l2SignedTx)

const inboxRec = await l1Tx.wait()

console.log(`Greeting txn confirmed on L1! 🙌 ${inboxRec.transactionHash}`)
const signedTransaction = await inboxTools.signChildTx(
transactionRequest,
childChainWallet
)
const transactionHash = ethers.utils.parseTransaction(signedTransaction).hash

/**
* Now we successfully send the tx to l1 delayed inbox, then we need to wait the tx executed on l2
* We now send the transaction through the Delayed Inbox on the parent chain
*/
const sendMessageParentChainTransactionRequest =
await inboxTools.sendChildSignedTx(signedTransaction)
const sendMessageParentChainTransactionReceipt =
await sendMessageParentChainTransactionRequest.wait()
console.log(
`Now we need to wait tx: ${l2Txhash} to be included on l2 (may take 15 minutes) ....... `
`Greeting transaction confirmed on the parent chain! 🙌 ${sendMessageParentChainTransactionReceipt.transactionHash}`
)

const l2TxReceipt = await l2Provider.waitForTransaction(l2Txhash)

const status = l2TxReceipt.status
/**
* Now we successfully send the transaction to the delayed inbox on the parent chain
* We wait for the transaction to be executed on the child chain
*/
console.log(
`Now we need to wait tx: ${transactionHash} to be executed on the child chain (may take ~15 minutes) ... `
)
const transactionReceipt = await childChainProvider.waitForTransaction(
transactionHash
)
const status = transactionReceipt.status
if (status == true) {
console.log(`L2 txn executed!!! 🥳 `)
console.log(`Transaction executed on the child chain!!! 🥳`)
} else {
console.log(`L2 txn failed, see if your gas is enough?`)
console.log(
`The transaction failed to execute on the child chain. Please verify if the gas provided was enough`
)
return
}

/**
* Now when we call greet again, we should see our new string on L2!
* Now when we call greet again, we should see our new string!
*/
const newGreetingL2 = await l2Greeter.greet()
console.log(`Updated L2 greeting: "${newGreetingL2}"`)
const newGreeting = await greeter.greet()
console.log(`Updated greeting: "${newGreeting}"`)
console.log('✌️')
}

Expand Down
Loading

0 comments on commit 99c0af9

Please sign in to comment.