Setup & Configuration (Stylus-first Shims)
This document describes installation and configuration for the Stylus-first precompile shims used in this integration. Scope is limited to shim mode, reseeding from a Stylus RPC, and validation via Hardhat scripts.
Prerequisites
- Node.js: v22 LTS recommended.
- Hardhat: project includes
@nomicfoundation/hardhat-ethers(ethers v6).
Installation
Before configuring the plugin, you must first install it as a development dependency in your Hardhat project:
npm install --save-dev @dappsoverapps.com/hardhat-patch
-
Repository layout (relevant parts):
TEXTsrc/hardhat-patch/ index.ts arbitrum-patch.ts native-precompiles.ts shims/ ArbSysShim.json ArbGasInfoShim.json tasks/ arb-install-shims.ts arb-reseed-shims.ts arb-gas-info.ts
Options (Hardhat arbitrum block)
| Key | Type | Default | Notes |
|---|---|---|---|
enabled | boolean | true | Turns the patch on/off. |
runtime | "stylus" | "nitro" | "stylus" | Stylus-first target. |
precompiles.mode | "shim" | "auto" | "native" | "auto" | integration uses shim path; auto gracefully installs shims. |
stylusRpc | string | (unset) | Remote RPC used by tasks when --stylus is selected. Can also be set via STYLUS_RPC. |
gas.pricesInWei | string[6] | (unset) | Shim order: [l2BaseFee, l1BaseFeeEstimate, l1CalldataCost, l1StorageCost, congestionFee, aux]. Used when no RPC/ cache is chosen. |
arbSysChainId | number | 42161 | Returned by ArbSysShim at slot 0. |
costing.enabled | boolean | false | If implemented, returns a small, stable non-zero gasUsed for precompile calls in shim mode (off by default). |
Configuration
hardhat.config.js (Stylus-first defaults)
require("@nomicfoundation/hardhat-ethers");
require("@dappsoverapps.com/hardhat-patch"); // plugin entry; auto-loads tasks
module.exports = {
solidity: "0.8.19",
networks: {
hardhat: { chainId: 42161 },
// optional persistent node:
// localhost: { url: "http://127.0.0.1:8549", chainId: 42161 }
},
arbitrum: {
enabled: true,
runtime: "stylus", // Stylus-first
precompiles: { mode: "auto" }, // tries native later; shims today
// stylusRpc: "https://sepolia-rollup.arbitrum.io/rpc", // optional default
},
};
runtime: "stylus"sets Stylus as the default runtime flavor.precompiles.mode: "auto"enables lazy shim installation in this integration (native hooks can be introduced in a later phase).
precompiles.config.json (optional)
{
"arbSysChainId": 42161,
"runtime": "stylus",
"gas": {
"pricesInWei": ["69440", "496", "2000000000000", "100000000", "0", "100000000"]
},
"stylusRpc": "https://sepolia-rollup.arbitrum.io/rpc"
}
-
gas.pricesInWeiis a 6-tuple in shim order:TEXT[ l2BaseFee, l1BaseFeeEstimate, l1CalldataCost, l1StorageCost, congestionFee, aux ] -
When both
stylusRpcandgas.pricesInWeiare present, reseeding prioritizes RPC (§3.4).
2.3 Environment variables (optional)
STYLUS_RPC— RPC endpoint for Stylus (for example,https://sepolia-rollup.arbitrum.io/rpc).ARB_PRECOMPILES_CONFIG— path override to a non-defaultprecompiles.config.json.
Stability toggles (often helpful on WSL/docker):
NODE_OPTIONS=--dns-result-order=ipv4firstNODE_NO_HTTP2=1
Example:
export NODE_OPTIONS=--dns-result-order=ipv4first
export NODE_NO_HTTP2=1
export STYLUS_RPC=https://sepolia-rollup.arbitrum.io/rpc
Shim installation & reseeding
Two flows are supported: ephemeral (in-process Hardhat VM) and persistent (a running Hardhat node on a port, e.g., 8549). Both install shims at the canonical addresses:
- ArbSys —
0x0000000000000000000000000000000000000064 - ArbGasInfo —
0x000000000000000000000000000000000000006c
Ephemeral Hardhat (fastest loop)
This mode spins up a fresh in-process Hardhat network per command.
# One-shot install + verify
npx hardhat --config hardhat.config.js run scripts/install-and-check-shims.js
Expected example:
getPricesInWei(): [ '69440', '496', '2000000000000', '100000000', '0', '100000000' ]
✅ shims installed, seeded, and verified in one run.
Tasks auto-loaded by the plugin can also be used:
npx hardhat --config hardhat.config.js arb:install-shims
Persistent Hardhat node (HTTP)
Start a node and point “localhost” at it:
# Start persistent node on 8549
npx hardhat node --port 8549
# …leave running
Optional environment setup:
export NODE_OPTIONS=--dns-result-order=ipv4first
export NODE_NO_HTTP2=1
export STYLUS_RPC=https://sepolia-rollup.arbitrum.io/rpc
Network entry for the persistent node:
networks: {
localhost: { url: "http://127.0.0.1:8549", chainId: 42161 },
}
Predeploy shim bytecode:
npx hardhat --config hardhat.config.js run --network localhost scripts/predeploy-precompiles.js
# Example:
# ArbSys code len: 582 ArbGasInfo code len: 1600
# ✅ Shims predeployed at 0x...64 and 0x...6c
Reseed from Stylus (preferred)
# Reseed using Stylus RPC (explicit flag)
npx hardhat --config hardhat.config.js arb:reseed-shims --network localhost --stylus
Examples:
Success
ℹ️ fetched from Stylus: [
'26440960','188864','2000000000000','100000000','0','100000000'
]
✅ Re-seeded getPricesInWei -> [
'26440960','188864','2000000000000','100000000','0','100000000'
]
Temporary failure (fallback)
⚠️ RPC fetch failed (https://sepolia-rollup.arbitrum.io/rpc): fetch failed
ℹ️ Stylus/Nitro RPC unavailable; will try other sources.
ℹ️ using JSON/config (shim order): [ '69440','496','2000000000000','100000000','0','100000000' ]
✅ Re-seeded getPricesInWei -> [ '69440','496','2000000000000','100000000','0','100000000' ]
Source priority (reseeding)
- Stylus RPC (flag
--stylus, orstylusRpc/STYLUS_RPC) precompiles.config.json(gas.pricesInWeiin shim order)- Built-in fallback (safe defaults)
Supported flags:
--stylus— prioritize Stylus RPC--nitro— prioritize Nitro RPC (optional, for parity checks)- No flag — follow config/env; otherwise fall back to config/defaults
Validation scripts
Readback sanity check
npx hardhat --config hardhat.config.js run --network localhost scripts/check-gasinfo-local.js
# Example:
# [ '69440', '496', '2000000000000', '100000000', '0', '100000000' ]
Full validation (raw selectors + contract calls)
npx hardhat --config hardhat.config.js run --network localhost scripts/validate-precompiles.js
Expected example:
Validating Arbitrum Precompiles in Hardhat Context...
Arbitrum patch found in HRE
Found 2 precompile handlers:
ArbSys: 0x0000000000000000000000000000000000000064
ArbGasInfo: 0x000000000000000000000000000000000000006c
Testing ArbSys arbChainID...
ArbSys arbChainID returned: 42161
⛽ Testing ArbGasInfo getL1BaseFeeEstimate...
ArbGasInfo getL1BaseFeeEstimate returned: 1000000000 wei (1 gwei)
📄 Testing contract deployment and precompile calls...
ArbProbes contract deployed at: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
Contract ArbSys call returned: 42161
Contract ArbGasInfo call returned: 496
Precompile validation completed!
Note: in shim mode, getCurrentTxL1GasFees() returns the estimate (slot 1) for deterministic test results.
Operational notes
- Address immutability: shims are placed at canonical precompile addresses; this mirrors Arbitrum expectations in local testing.
- Determinism vs. realism: shim values can be fixed via config or sampled via RPC. For reproducible tests, the config tuple is recommended.
- RPC networking:
ipv4firstandNODE_NO_HTTP2=1often resolve transient TLS/HTTP2 issues on WSL/docker. - ChainId:
ArbSysShimreturnsarbSysChainId(from config) at slot 0; default resolves to 42161.
Quick reference (common commands)
Ephemeral, one-shot
npx hardhat --config hardhat.config.js run scripts/install-and-check-shims.js
# or
npx hardhat --config hardhat.config.js arb:install-shims
Persistent node (8549)
# Terminal A
npx hardhat node --port 8549
# Terminal B
npx hardhat --config hardhat.config.js run --network localhost scripts/predeploy-precompiles.js
STYLUS_RPC=https://sepolia-rollup.arbitrum.io/rpc \
npx hardhat --config hardhat.config.js arb:reseed-shims --network localhost --stylus
npx hardhat --config hardhat.config.js run --network localhost scripts/check-gasinfo-local.js
npx hardhat --config hardhat.config.js run --network localhost scripts/validate-precompiles.js