Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Using public testnet RPCs can be slow because many tutorials wait for transactio
- 🌉 [Bridging a custom token through the generic-custom gateway](./packages/custom-token-bridging/)
- 🌉 [Bridging a custom token through a custom gateway](./packages/custom-gateway-bridging/)
- ✈️ [Send a signed transaction from the parent chain](./packages/delayedInbox-l2msg/)
- 🛡️ [Force inclusion end-to-end test](./packages/force-inclusion/)
- 🎁 [Redeem pending retryable ticket](./packages/redeem-pending-retryable/)
- 🧮 [Gas estimation](./packages/gas-estimation/)
- 🌀 [Deposit Ether or Tokens from L1 to L3](./packages/l1-l3-teleport/)
Expand Down
18 changes: 18 additions & 0 deletions packages/force-inclusion/.env-sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This is a sample .env file for use in local development.
# Duplicate this file as .env here

# Private key of the deployer (must have ETH on the parent chain)
DEPLOYER_PRIVATE_KEY="0x your key here"

# The parent chain's RPC
# (default: Arbitrum Sepolia)
PARENT_CHAIN_RPC="https://sepolia-rollup.arbitrum.io/rpc"

# (Optional) Parent chain ID
# Defaults to Arbitrum Sepolia (421614) if not set
# PARENT_CHAIN_ID=421614

# (Optional) Batch poster and validator keys
# Auto-generated if not set (only needed for --with-node)
# BATCH_POSTER_PRIVATE_KEY="0x..."
# VALIDATOR_PRIVATE_KEY="0x..."
92 changes: 92 additions & 0 deletions packages/force-inclusion/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Tutorial: Force Inclusion End-to-End Test

`force-inclusion` demonstrates Arbitrum's **censorship resistance** mechanism end-to-end. It deploys a fresh Orbit rollup with a short force inclusion delay (90 seconds), deposits ETH via the delayed inbox, waits for the delay window to pass, and then force includes the deposit — all without a running sequencer.

## What is force inclusion?

Arbitrum's [Sequencer](https://docs.arbitrum.io/how-arbitrum-works/sequencer) normally orders transactions. But what if the sequencer goes offline or starts censoring? Arbitrum guarantees that any message sent to the **delayed inbox** on the parent chain can be **force included** into the chain's inbox after a time delay, bypassing the sequencer entirely.

This tutorial runs the full cycle in a single command:

1. **Deploy** a new Orbit rollup with `maxTimeVariation.delaySeconds = 90` (instead of the default 24 hours)
2. **Deposit** ETH via `Inbox.depositEth()` on the parent chain (goes into the delayed inbox)
3. **Force include** the deposit by calling `SequencerInbox.forceInclusion()` after the delay window passes
4. *(Optional)* **Start a fullnode** (no sequencer) to verify the deposit appears on the child chain

## Prerequisites

- Node.js 18+
- A deployer account with ETH on the parent chain (Arbitrum Sepolia or a custom parent chain)
- Docker (only needed for the `--with-node` option)

## Set environment variables

Copy the sample env file and fill in your values:

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

Required variables:

| Variable | Description |
|---|---|
| `DEPLOYER_PRIVATE_KEY` | Private key of the deployer (must have ETH on the parent chain) |
| `PARENT_CHAIN_RPC` | RPC URL of the parent chain |

Optional variables:

| Variable | Description |
|---|---|
| `PARENT_CHAIN_ID` | Parent chain ID (defaults to Arbitrum Sepolia 421614) |
| `BATCH_POSTER_PRIVATE_KEY` | Batch poster key (auto-generated if not set) |
| `VALIDATOR_PRIVATE_KEY` | Validator key (auto-generated if not set) |

## Run

Run steps 1–3 (deploy, deposit, force include):

```bash
yarn test
```

Run all 4 steps including fullnode verification (requires Docker):

```bash
yarn test:withNode
```

## How it works

### Rollup deployment

The script uses `@arbitrum/chain-sdk` (Orbit SDK) to deploy a new rollup. The key configuration is `sequencerInboxMaxTimeVariation`, which controls how long a delayed message must wait before it can be force included:

```js
sequencerInboxMaxTimeVariation: {
delayBlocks: 6n,
futureBlocks: 12n,
delaySeconds: 90n,
futureSeconds: 3600n,
}
```

### Delayed inbox deposit

ETH is deposited using `@arbitrum/sdk`'s `EthBridger.deposit()`. Under the hood, this calls `Inbox.depositEth()` on the parent chain, which routes through `bridge.enqueueDelayedMessage()`. Without a running sequencer, this message stays in the delayed inbox.

### Force inclusion

After the delay window passes (90 seconds + 6 blocks), `InboxTools.forceInclude()` calls `SequencerInbox.forceInclusion()` on the parent chain. This emits the same `SequencerBatchDelivered` event as a normal sequencer batch, making the deposit part of the canonical chain.

### Fullnode verification (--with-node)

When `--with-node` is passed, the script starts a Nitro fullnode via Docker with the sequencer disabled (`node.sequencer = false`, `execution.forwarding-target = "null"`). The fullnode reads from the parent chain and processes the force-included batch, allowing you to verify that the deposited ETH appears on the child chain.

## Related tutorials

- [Send a signed transaction from the parent chain](../delayedInbox-l2msg/) — demonstrates sending transactions via the delayed inbox with a running sequencer

<p align="left">
<img width="350" height="150" src= "../../assets/logo.svg" />
</p>
16 changes: 16 additions & 0 deletions packages/force-inclusion/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "force-inclusion",
"version": "1.0.0",
"description": "End-to-end force inclusion test: deploy a rollup with a short delay, deposit ETH via delayed inbox, and force include — all without a sequencer.",
"scripts": {
"test": "node scripts/force-inclusion-test.js",
"test:withNode": "node scripts/force-inclusion-test.js --with-node"
},
"author": "Offchain Labs, Inc.",
"license": "Apache-2.0",
"dependencies": {
"@arbitrum/sdk": "^v4.0.1",
"@arbitrum/chain-sdk": "^0.25.0",
"viem": "^1.20.0"
}
}
Loading
Loading