Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c750bda
feat: add Docker support for running upgrade scripts
yahgwai Feb 5, 2026
1f0f4f3
chore: add .DS_Store to gitignore
yahgwai Feb 5, 2026
eea7623
fix: use Yarn Classic (v1) in Dockerfile for lockfile compatibility
yahgwai Feb 5, 2026
c5948fa
fix: add submodule checkout for Docker build
yahgwai Feb 5, 2026
4c8edb8
fix: use forge install instead of git submodules for Docker build prereq
yahgwai Feb 5, 2026
9cb07c5
Update README.md
yahgwai Feb 5, 2026
c445d7d
feat: add browsable CLI for upgrade scripts
yahgwai Feb 6, 2026
15b87cf
feat: add Verify scripts for contract upgrades
yahgwai Feb 9, 2026
a8adfeb
ci: add Docker Hub publishing workflow
yahgwai Feb 9, 2026
4d391f9
refactor: rewrite CLI in TypeScript
yahgwai Feb 9, 2026
c5520f7
refactor: remove obvious comments and add named constants
yahgwai Feb 9, 2026
c93b86f
fix: update docker tests and eslint config for TS CLI
yahgwai Feb 9, 2026
92fbc31
test: add local smoke tests for CLI
yahgwai Feb 9, 2026
e3addac
docs: reorganize README with CLI and Docker sections
yahgwai Feb 9, 2026
596182b
refactor: simplify env loading to current directory only
yahgwai Feb 10, 2026
9dc0108
refactor: extract duplicate execute/verify logic in arbos-upgrade
yahgwai Feb 10, 2026
6dd231f
refactor: remove unused Commander subcommands
yahgwai Feb 10, 2026
aaa6eea
build: pin Foundry to specific nightly version
yahgwai Feb 10, 2026
ead0814
chore: remove obvious Dockerfile comments
yahgwai Feb 10, 2026
7977435
fix: correct Foundry version pin and lint issues
yahgwai Feb 10, 2026
2f30dde
revert: remove Foundry version pin that fails in Docker
yahgwai Feb 10, 2026
05c1282
Merge remote-tracking branch 'origin/main' into feat/380-dockerize
yahgwai Feb 10, 2026
80ab399
Update src/cli/commands/arbos-upgrade.ts
yahgwai Mar 4, 2026
f083a68
fix: add missing Interface import from ethers
yahgwai Mar 4, 2026
5a4ceca
refactor: pass env vars to runForgeScript instead of mutating process…
yahgwai Mar 4, 2026
2acea4e
fix: use try/catch for execa error handling instead of dead exitCode …
yahgwai Mar 4, 2026
a863cdb
refactor: constrain verbosity to valid forge range via union type
yahgwai Mar 4, 2026
4b4712e
fix: minor cleanups to Dockerfile, workflow, router, and arbos-upgrade
yahgwai Mar 9, 2026
94bef28
refactor: remove CLI auth args and combined command, use FOUNDRY_* en…
yahgwai Mar 10, 2026
3765422
fix: address PR review feedback on CLI UX and error handling
yahgwai Mar 10, 2026
54c190c
refactor: simplify env templates and remove command prefix from help …
yahgwai Mar 10, 2026
8344c39
chore: remove bin entry from package.json
yahgwai Mar 10, 2026
707cc07
refactor: remove log() wrapper, use console.log directly
yahgwai Mar 11, 2026
0efc403
Apply suggestion from @godzillaba
yahgwai Mar 11, 2026
657fd93
feat: auto-resolve action address from broadcast file for deploy && e…
yahgwai Mar 11, 2026
e7f8387
refactor: consolidate resolveActionAddress into forge utils
yahgwai Mar 11, 2026
7a6d9a6
fix: update docker tests for removed deploy-execute-verify command
yahgwai Mar 11, 2026
784b100
fix: update local test to use yarn cli instead of removed bin/router
yahgwai Mar 11, 2026
ede1c95
chore: fix prettier formatting in router.ts
yahgwai Mar 11, 2026
2853daa
fix: handle deploy simulation mode gracefully in CLI
yahgwai Mar 12, 2026
d336a84
refactor: use ethers for ABI encoding/decoding instead of cast
yahgwai Mar 12, 2026
a022a65
fix: inherit stderr in cast call utilities for visible error output
yahgwai Mar 12, 2026
1d77f3a
docs: document last-CREATE convention in deploy scripts and README
yahgwai Mar 12, 2026
cb1cbeb
fix: address PR review findings across CLI, Docker, and verify scripts
yahgwai Mar 12, 2026
e4fed49
fix: use --install flag for foundryup v1.5.0 CLI change
yahgwai Mar 12, 2026
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
28 changes: 28 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Dependencies (will be installed fresh in container)
node_modules/

# Build artifacts (will be built fresh in container)
out/
cache_forge/
cache/
artifacts/
typechain-types/

# Environment files (contain secrets)
/.env

# Git
.git/
.gitignore

# IDE
.vscode/
.idea/

# Test artifacts
broadcast/
coverage/

# Docker
Dockerfile
.dockerignore
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
lib/
dist/
8 changes: 1 addition & 7 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
plugins: ['@typescript-eslint', 'prettier', '@typescript-eslint/tslint'],
plugins: ['@typescript-eslint', 'prettier'],
rules: {
'no-empty-pattern': 'warn',
'prettier/prettier': ['error', { singleQuote: true }],
Expand All @@ -77,12 +77,6 @@ module.exports = {
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/tslint/config': [
'error',
{
rules: { 'strict-comparisons': true },
},
],
'no-implicit-coercion': 'error',
'@typescript-eslint/no-shadow': ['error'],
},
Expand Down
48 changes: 48 additions & 0 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Publish Docker

on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:

jobs:
publish:
name: Build and Push Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
# "orbit" is a deprecated term; the published image uses "chain-actions"
images: offchainlabs/chain-actions
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=ref,event=branch
Comment on lines +31 to +38
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The publish workflow targets offchainlabs/chain-actions, but the PR description/README/Docker usage references offchainlabs/orbit-actions. If orbit-actions is the intended image name, update images: accordingly to avoid publishing to the wrong repository.

Copilot uses AI. Check for mistakes.

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
32 changes: 32 additions & 0 deletions .github/workflows/test-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Test Docker

on:
pull_request:
workflow_dispatch:

jobs:
test-docker:
name: Test Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: orbit-actions:test
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run Docker smoke tests
run: ./test/docker/test-docker.bash
env:
DOCKER_IMAGE: orbit-actions:test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
node_modules
.env
.DS_Store

# TypeScript build output
/dist

# Hardhat files
/cache
Expand Down
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM node:22-slim

# Install dependencies for Foundry, git, and jq (for JSON parsing in upgrade scripts)
RUN apt-get update && apt-get install -y \
curl \
git \
jq \
&& rm -rf /var/lib/apt/lists/*

# Install Foundry
ENV PATH="/root/.foundry/bin:${PATH}"
RUN curl -L https://foundry.paradigm.xyz | bash && foundryup --install stable

# Install Yarn Classic (v1) - matches the repo's yarn.lock format
RUN npm install -g --force yarn@1.22.22

WORKDIR /app

# Copy package files first for better layer caching
COPY package.json yarn.lock ./

# --ignore-scripts: forge install runs separately after full copy
RUN yarn install --frozen-lockfile --ignore-scripts

COPY . .
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Docker build relies on Forge dependencies being present, but the Dockerfile never runs forge install (and yarn install --ignore-scripts skips the repo's prepare hook that would normally do it). This makes docker build fail unless the build context already contains lib/ from a prior host-side forge install (as your CI currently does). Add an explicit RUN forge install inside the Dockerfile (after COPY . . and before forge build) so the image builds reliably from a clean checkout.

Suggested change
COPY . .
COPY . .
RUN forge install

Copilot uses AI. Check for mistakes.
# forge install can't run here: it clones git submodules, but .dockerignore
# excludes .git/. CI runs forge install on the host so lib/ is copied in above.
RUN forge build
RUN yarn build:cli

ENTRYPOINT ["node", "/app/dist/cli/index.js"]
94 changes: 92 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ For ArbOS upgrades, a common pre-requisite is to deploy new Nitro contracts to t

### Nitro contracts 3.1.0 (for [BoLD](https://docs.arbitrum.io/how-arbitrum-works/bold/gentle-introduction))

The [`nitro-contracts 3.1.0` upgrade guide](scripts/foundry/contract-upgrades/3.1.0) will use the [BOLDUpgradeAction](https://github.com/OffchainLabs/nitro-contracts/blob/main/src/rollup/BOLDUpgradeAction.sol) from the [nitro-contract](https://github.com/OffchainLabs/nitro-contracts) repo. There is no associated ArbOS upgrade for BoLD.
The [`nitro-contracts 3.1.0` upgrade guide](scripts/foundry/contract-upgrades/3.1.0) will use the [BOLDUpgradeAction](https://github.com/OffchainLabs/nitro-contracts/blob/main/src/rollup/BOLDUpgradeAction.sol) from the [nitro-contract](https://github.com/OffchainLabs/nitro-contracts) repo. There is no associated ArbOS upgrade for BoLD.

### Nitro contracts 2.1.3

Expand Down Expand Up @@ -132,4 +132,94 @@ See [setCacheManager](scripts/foundry/stylus/setCacheManager).

Currently limited to L2s; L3 support is expected in a future update.

See [Nitro contracts 3.1.0 upgrade](https://github.com/OffchainLabs/orbit-actions/tree/main/scripts/foundry/contract-upgrades/3.1.0).
See [Nitro contracts 3.1.0 upgrade](https://github.com/OffchainLabs/orbit-actions/tree/main/scripts/foundry/contract-upgrades/3.1.0).

# CLI

The `orbit-actions` CLI provides a guided interface for running upgrade scripts. It wraps Foundry commands and handles the deploy/execute/verify workflow.

```bash
# Browse available scripts
yarn cli # List top-level directories
yarn cli -- contract-upgrades # List versions
yarn cli -- contract-upgrades/1.2.1 # List contents + commands

# View files
yarn cli -- contract-upgrades/1.2.1/README.md

# Run contract upgrade steps individually
yarn cli -- contract-upgrades/1.2.1/deploy
yarn cli -- contract-upgrades/1.2.1/execute
yarn cli -- contract-upgrades/1.2.1/verify

# Run ArbOS upgrade steps individually
yarn cli -- arbos-upgrades/at-timestamp/deploy 32
yarn cli -- arbos-upgrades/at-timestamp/execute
yarn cli -- arbos-upgrades/at-timestamp/verify
```

Run `yarn cli -- help` for full usage details.

### Configuration

The CLI reads chain-specific configuration (RPC URLs, contract addresses) from a `.env` file in the project root. See env templates in each version directory for examples.

Forge behavior -- broadcasting, authentication, verbosity, verification -- is controlled via standard `FOUNDRY_*` / `ETH_*` env vars in the same `.env` file. The CLI passes `process.env` through to forge, so any env var forge recognizes will work.

Key forge env vars:

| Variable | Effect |
| ------------------------ | ---------------------------------------------------------------- |
| `FOUNDRY_BROADCAST=true` | Broadcast transactions (without this, scripts run in simulation) |
| `ETH_PRIVATE_KEY=0x...` | Private key for signing transactions |

All `FOUNDRY_*` env vars are supported -- see [Foundry configuration](https://book.getfoundry.sh/reference/config/) for the full list.

### Full upgrade flow

The deploy, execute, and verify steps are run separately. This allows multisig users to submit Safe approvals between steps, and EOA users can chain the commands:

```bash
# 1. Deploy the upgrade action contract
yarn cli -- contract-upgrades/2.1.3/deploy
# Note the deployed action address printed at the end

# 2. Set UPGRADE_ACTION_ADDRESS in .env, then execute
yarn cli -- contract-upgrades/2.1.3/execute

# 3. Verify the upgrade
yarn cli -- contract-upgrades/2.1.3/verify
```

### Writing new deploy scripts

The CLI identifies the action contract address by reading the last `CREATE` transaction from Forge's broadcast file. Deploy scripts must deploy all dependencies first and the action contract last.

## Docker

The CLI is available as a Docker image at `offchainlabs/orbit-actions`:

```bash
# Check contract versions
docker run --rm \
-e INBOX_ADDRESS=0xaE21fDA3de92dE2FDAF606233b2863782Ba046F9 \
-e INFURA_KEY=$INFURA_KEY \
offchainlabs/orbit-actions:versioner \
--network arb1

# Browse upgrade scripts
docker run --rm offchainlabs/orbit-actions contract-upgrades

# Deploy with env file (simulation mode -- no FOUNDRY_BROADCAST)
docker run --rm \
-v $(pwd)/.env:/app/.env \
-v $(pwd)/broadcast:/app/broadcast \
offchainlabs/orbit-actions \
contract-upgrades/2.1.3/deploy

# Execute with broadcasting enabled
docker run --rm \
-v $(pwd)/.env:/app/.env \
offchainlabs/orbit-actions \
contract-upgrades/2.1.3/execute
```
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"repository": "https://github.com/OffchainLabs/blockchain-eng-template.git",
"license": "Apache 2.0",
"scripts": {
"build:cli": "tsc -p tsconfig.cli.json",
"cli": "ts-node src/cli/index.ts",
"prepare": "forge install && cd lib/arbitrum-sdk && yarn",
"minimal-publish": "./scripts/publish.bash",
"minimal-install": "yarn --ignore-scripts && forge install",
Expand All @@ -20,6 +22,7 @@
"test:gas-check": "forge snapshot --check --tolerance 1 --match-path \"test/unit/**/*.t.sol\"",
"test:sigs": "./test/signatures/test-sigs.bash",
"test:storage": "./test/storage/test-storage.bash",
"test:docker": "./test/docker/test-docker.bash",
"orbit:contracts:version": "hardhat run scripts/orbit-versioner/orbitVersioner.ts",
"gas-snapshot": "forge snapshot --match-path \"test/unit/**/*.t.sol\"",
"fix": "yarn format; yarn test:sigs; yarn test:storage; yarn gas-snapshot"
Expand Down Expand Up @@ -61,5 +64,9 @@
"ts-node": ">=8.0.0",
"typechain": "^8.3.0",
"typescript": ">=4.5.0"
},
"dependencies": {
"commander": "^12.0.0",
"execa": "^5.1.1"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ contract DeployUpgradeArbOSVersionAtTimestampActionScript is Script {

vm.startBroadcast();

// finally deploy upgrade action
// Deploy the action contract last. The CLI identifies the deployed action
// by taking the last CREATE from the broadcast file.
new UpgradeArbOSVersionAtTimestampAction({
_newArbOSVersion: uint64(arbosVersion), _upgradeTimestamp: uint64(scheduleTimestamp)
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## Forge configuration (uncomment/adjust as needed)
## All FOUNDRY_* env vars are supported: https://book.getfoundry.sh/reference/config/
# FOUNDRY_BROADCAST=true
# ETH_PRIVATE_KEY=0x...

## Chain and contract addresses
CHILD_CHAIN_RPC=
UPGRADE_ACTION_ADDRESS="0x217788c286797D56Cd59aF5e493f3699C39cbbe8"
CHILD_UPGRADE_EXECUTOR_ADDRESS="0x6A17B0D4EA37c4F519caCf776450cb42199Df0A4"
ARBOS_VERSION=20
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## Forge configuration (uncomment/adjust as needed)
## All FOUNDRY_* env vars are supported: https://book.getfoundry.sh/reference/config/
# FOUNDRY_BROADCAST=true
# ETH_PRIVATE_KEY=0x...

## Chain and contract addresses
CHILD_CHAIN_RPC=
UPGRADE_ACTION_ADDRESS="0x7A132A8130a0C2dCeABd1FDb42ed01FCf6B9494a"
CHILD_UPGRADE_EXECUTOR_ADDRESS="0x059EF6e2CaA4d779e087273646EfF49ef45dBD81"
ARBOS_VERSION=20
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ contract DeployNitroContracts1Point2Point1UpgradeActionScript is Script {
abi.encode(vm.envUint("MAX_DATA_SIZE"), reader4844Address, vm.envBool("IS_FEE_TOKEN_CHAIN"))
);

// finally deploy upgrade action
// Deploy the action contract last. The CLI identifies the deployed action
// by taking the last CREATE from the broadcast file.
new NitroContracts1Point2Point1UpgradeAction({
_newWasmModuleRoot: vm.envBytes32("WASM_MODULE_ROOT"),
_newSequencerInboxImpl: seqInbox,
Expand Down
4 changes: 2 additions & 2 deletions scripts/foundry/contract-upgrades/1.2.1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ forge script --sender $EXECUTOR --rpc-url $PARENT_CHAIN_RPC --broadcast ./Execut
```
If you have a multisig as executor, you can still run the above command without broadcasting to get the payload for the multisig transaction.

4. That's it, upgrade has been performed. You can make sure it has successfully executed by checking wasm module root:
4. That's it, upgrade has been performed. You can verify by running:
```bash
cast call --rpc-url $PARENT_CHAIN_RPC $ROLLUP "wasmModuleRoot()"
forge script --rpc-url $PARENT_CHAIN_RPC VerifyNitroContracts1Point2Point1Upgrade -vvv
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.16;

import "forge-std/Script.sol";

interface IRollupCore {
function wasmModuleRoot() external view returns (bytes32);
}

/**
* @title VerifyNitroContracts1Point2Point1Upgrade
* @notice Verifies the upgrade to Nitro Contracts 1.2.1 by checking the wasmModuleRoot
*/
contract VerifyNitroContracts1Point2Point1Upgrade is Script {
function run() public view {
address rollup = vm.envAddress("ROLLUP");
bytes32 wasmRoot = IRollupCore(rollup).wasmModuleRoot();
console.log("wasmModuleRoot:");
console.logBytes32(wasmRoot);
}
}
Loading
Loading