Development of L3/Appchain on OP Stack
Running own appchain stopped being prerogative of large protocols with $10M+ budget. OP Stack — modular framework that Optimism, Base, Zora, Mode are built from — allows raising EVM-compatible L2 in days, not months. L3 (appchain on top of L2) is next step: sovereign blockchain with custom gas token, private transactions, specific precompiles, working on top of Base or OP Mainnet instead of Ethereum L1.
When Own Appchain Makes Sense
Launching appchain justified if you have at least two of following requirements:
Performance. Shared L2 (Arbitrum, Base) has competition for block space — at peak times gas rises. On own chain all throughput belongs to your application.
Custom gas token. On shared L2 fees paid in ETH. On appchain can make gas token — native token of your protocol. Token economics changes radically: every transaction burns/captures value for holders.
Transaction privacy. On public L2 all transactions visible. For gaming (hidden moves), financial apps with corporate data — need isolation.
Custom consensus logic. Permissioned sequencer with KYC, or fully decentralized sequencer set for censorship resistance.
Not if: you have < 100K transactions/day, no team for node operational support, no liquidity for bridge.
OP Stack Architecture
OP Stack built on modularity principle. Components replaceable independently:
L1 (Ethereum / Base / OP Mainnet)
↑ settlements, DA
[OptimismPortal Contract] ← bridge L1↔L2
[L2OutputOracle Contract] ← state roots
↑
L2 / L3 Chain
├─ op-node (consensus client) ← derives chain from L1 data
├─ op-geth (execution client) ← EVM, state, mempool
└─ op-batcher ← submits transaction batches to L1
└─ op-proposer ← submits state roots to L1
op-node — "consensus client" of L2. Reads data from L1 (or parent L2), applies derivation rules, syncs op-geth via Engine API. Fork of Ethereum consensus client under OP Stack.
op-geth — fork of go-ethereum with minimal changes: proof-of-work removed, deposit transaction type added (L1→L2 bridge), custom precompiles.
op-batcher — takes transactions from L2 mempool, packs into batches, publishes to L1 as calldata or EIP-4844 blobs. Key component for cost: EIP-4844 blobs ~10x cheaper than calldata.
Custom L3 Configuration
Step 1: Deploy Contracts to Parent Chain
git clone https://github.com/ethereum-optimism/optimism
cd packages/contracts-bedrock
# Chain configuration
cat > deploy-config/my-l3.json << EOF
{
"l1ChainID": 8453, // Base (parent L2)
"l2ChainID": 12345678, // our L3
"l2BlockTime": 2,
"maxSequencerDrift": 600,
"sequencerWindowSize": 3600,
"channelTimeout": 300,
"p2pSequencerAddress": "0x...",
"batchInboxAddress": "0x...",
"batchSenderAddress": "0x...",
"l2OutputOracleSubmissionInterval": 120,
"l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleStartingTimestamp": 1700000000,
"l2OutputOracleProposer": "0x...",
"l2OutputOracleChallenger": "0x...",
"finalizationPeriodSeconds": 604800,
"proxyAdminOwner": "0x...", // multisig
"baseFeeVaultRecipient": "0x...",
"l1FeeVaultRecipient": "0x...",
"sequencerFeeVaultRecipient": "0x...",
"governanceTokenName": "MyApp Token",
"governanceTokenSymbol": "MYAPP",
"governanceTokenOwner": "0x..."
}
EOF
forge script scripts/Deploy.s.sol --rpc-url $BASE_RPC_URL --broadcast
Step 2: Custom Gas Token
OP Stack supports Custom Gas Token starting with OP Stack Fjord. Configuration in genesis:
{
"customGasToken": {
"enabled": true,
"l1Address": "0x...MyToken on Base",
"l2Address": "0x4200000000000000000000000000000000000023"
}
}
Native L3 token = your ERC-20 from parent network. Gas fees in transactions paid in it. Important nuance: token must be standard ERC-20 without transfer fees (no-fee-on-transfer), otherwise bridge breaks.
Step 3: Run Nodes
# docker-compose for L3 sequencer
services:
op-geth:
image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:latest
volumes:
- ./data/geth:/data
- ./genesis.json:/genesis.json
command: >
--datadir=/data
--networkid=12345678
--http --http.addr=0.0.0.0 --http.port=8545
--http.api=web3,debug,eth,txpool,net,engine
--ws --ws.addr=0.0.0.0 --ws.port=8546
--authrpc.addr=0.0.0.0 --authrpc.port=8551
--authrpc.jwtsecret=/data/jwt.txt
--syncmode=full
--gcmode=archive # needed for indexer
--override.fjord=1700000000
op-node:
image: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:latest
depends_on:
- op-geth
command: >
op-node
--l1=$BASE_WS_RPC
--l1.beacon=$BASE_BEACON_URL
--l2=http://op-geth:8551
--l2.jwt-secret=/data/jwt.txt
--rollup.config=/rollup.json
--p2p.sequencer.key=$SEQUENCER_PRIVATE_KEY
--sequencer.enabled
--sequencer.l1-confs=4
--rpc.addr=0.0.0.0 --rpc.port=9545
Data Availability: Choice of DA Layer
By default OP Stack publishes data to L1 (Ethereum or Base). This is expensive and creates dependency. Alternatives:
EIP-4844 Blobs (Dencun). Already built into OP Stack. Blobs cheaper than calldata ~5-10x, but stored only ~18 days (pruning). Sufficient for fraud proofs (7-day window).
Celestia DA. OP Stack + Celestia via op-plasma or AltDA mode. Data published to Celestia (~$0.001 per KB vs ~$0.01+ on Ethereum), L1 stores only commitment. Mode: op-batcher → Celestia, op-node reads from Celestia. DA costs reduction 50-100x.
# Run op-node with Celestia DA
op-node \
--altda.enabled=true \
--altda.da-server=http://celestia-da-server:3100 \
--altda.da-service=true \
...
EigenDA. Similar to Celestia, but via restaking on Ethereum. Younger solution, but with stronger economic safety guarantees.
Avail DA. Another DA-specialized network, popular in Polygon ecosystem.
Custom Precompiles
Precompiles are built-in functions at EVM level, execute without bytecode (C++ in geth). Standard: ecrecover (0x01), SHA256 (0x02), BN254 pairing (0x08) etc.
In op-geth can add custom precompiles for specific operations: ZK verification, custom crypto primitives, fast access to L1 state:
// In op-geth: register custom precompile
var customPrecompiles = map[common.Address]vm.PrecompiledContract{
common.HexToAddress("0x0000...1234"): &MyCustomPrecompile{},
}
type MyCustomPrecompile struct{}
func (c *MyCustomPrecompile) RequiredGas(input []byte) uint64 {
return 1000 // fixed gas cost
}
func (c *MyCustomPrecompile) Run(input []byte) ([]byte, error) {
// custom logic: for example Groth16 proof verification
return verifyGroth16Proof(input)
}
Practical applications: batch BLS signature verification for oracle networks, efficient Poseidon hashing for ZK applications, VRF verification.
Bridging and Liquidity
Standard OP Stack bridge is native, via OptimismPortal. Deposit (L2→L3): ~15 minutes. Withdrawal (L3→L2): 7 days (fraud proof window). 7-day wait unacceptable for users.
Solution: fast bridge via liquidity providers. Protocols like Across, Hop, Stargate provide "fast" withdrawals: LP fronts funds immediately, receives funds from L3 after 7 days + risk fee. Integration via Across Protocol SDK:
import { AcrossClient } from '@across-protocol/sdk';
const client = AcrossClient.create({ chains: [myL3Chain, base] });
const quote = await client.getQuote({
route: { originChainId: myL3Chain.id, destinationChainId: base.id },
inputToken: USDC_ON_L3,
outputToken: USDC_ON_BASE,
inputAmount: parseUnits('1000', 6),
});
// Execute fast withdrawal (seconds, not 7 days)
await client.executeQuote({ quote, walletClient });
Sequencer: Centralized vs Decentralized
Centralized sequencer (standard for most appchains): single operator orders transactions. Risks: downtime (if sequencer down — chain doesn't move), censorship. Mitigation: force inclusion via L1 Portal contract (transaction included forcefully after 12+ hours if sequencer censors).
Decentralized sequencer via OP Stack + MEVA or via Espresso Systems Shared Sequencer. More complex, needs leader election protocol. For production appchain with $1M+ TVL worth considering.
Operational Infrastructure
Minimal production setup:
| Node | Purpose | Requirements |
|---|---|---|
| Sequencer | Process transactions | 32GB RAM, 500GB NVMe SSD, reliable uptime |
| op-batcher | Publish batches to L1 | 8GB RAM, stable L1 RPC |
| op-proposer | Publish state roots | 8GB RAM |
| RPC node | Public RPC for users | 32GB RAM, 1TB+ SSD |
| Archive node | Historical data for indexing | 64GB RAM, 2TB+ SSD |
Monitoring: block production (alert if no new block > 30 sec), batcher lag (unsubmitted batches), proposer status (missed proposals), L1 gas price (batcher may get stuck at extreme L1 gas).
Timeline and Phases
Phase 1 — Testnet (3-4 weeks). Deploy contracts on test network, run nodes, basic bridge UI, test custom gas token.
Phase 2 — Mainnet prep (2-3 weeks). Security review of contracts, set up multisig for admin keys, monitoring, runbooks for operators.
Phase 3 — Mainnet launch (1 week). Deploy, migrate liquidity, public announcement, 24/7 monitoring first two weeks.
Ongoing: OP Stack updates (regular Optimism Superchain hardforks), monitoring, bridge liquidity support.
Total infrastructure cost: ~$500-2000/month for nodes + L1 DA costs ($500-5000/month depending on activity).







