Try   HackMD

Cheatsheet for running OP devnet

Dependencies

Install golang and rust

Install docker and docker-compose

wget -qO- https://get.pnpm.io/install.sh | sh -

curl -L https://foundry.paradigm.xyz | bash
foundryup

download jq from https://jqlang.github.io/jq/
sudo mv jq-linux-amd64 /usr/bin/jq

sudo apt install direnv

cargo install just

1. Geth & prism devnet for L1

git clone https://github.com/uprendis/eth-pos-devnet.git
docker compose up -d # use docker-compose if running an older version
docker logs --follow eth-pos-devnet_geth_1 # read logs
sudo ./clean.sh # stop and erase the datadirs

It'll listen on 8545 for http web3 API

Here is the original instruction: https://docs.prylabs.network/docs/advanced/proof-of-stake-devnet

The genesis will pre-fund all dev accounts

2. Deploy contracts and generate genesis for L2

git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
git checkout v1.9.3 # I tested this version but it probably works with the latest one too
make build

mkdir build

cd packages/contracts-bedrock

Create file deploy-config/devnetL1.json with this content:

{
  "l1ChainID": 32382,
  "l2ChainID": 901,
  "l2BlockTime": 2,
  "maxSequencerDrift": 300,
  "sequencerWindowSize": 200,
  "channelTimeout": 120,
  "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
  "batchInboxAddress": "0xff00000000000000000000000000000000000901",
  "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
  "l1StartingBlockTag": "earliest",
  "l2OutputOracleSubmissionInterval": 10,
  "l2OutputOracleStartingTimestamp": 0,
  "l2OutputOracleStartingBlockNumber": 0,
  "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
  "l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65",
  "l2GenesisBlockGasLimit": "0x1c9c380",
  "l1BlockTime": 12,
  "baseFeeVaultRecipient": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955",
  "l1FeeVaultRecipient": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f",
  "sequencerFeeVaultRecipient": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
  "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
  "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
  "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
  "baseFeeVaultWithdrawalNetwork": 0,
  "l1FeeVaultWithdrawalNetwork": 0,
  "sequencerFeeVaultWithdrawalNetwork": 0,
  "proxyAdminOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
  "finalSystemOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
  "superchainConfigGuardian": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
  "finalizationPeriodSeconds": 2,
  "fundDevAccounts": true,
  "l2GenesisBlockBaseFeePerGas": "0x1",
  "gasPriceOracleOverhead": 2100,
  "gasPriceOracleScalar": 1000000,
  "gasPriceOracleBaseFeeScalar": 1368,
  "gasPriceOracleBlobBaseFeeScalar": 810949,
  "enableGovernance": true,
  "governanceTokenSymbol": "NB",
  "governanceTokenName": "Nebra",
  "governanceTokenOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
  "eip1559Denominator": 50,
  "eip1559DenominatorCanyon": 250,
  "eip1559Elasticity": 6,
  "l1GenesisBlockTimestamp": "0x123",
  "l2GenesisRegolithTimeOffset": "0x0",
  "l2GenesisCanyonTimeOffset": "0x0",
  "l2GenesisDeltaTimeOffset": "0x0",
  "l2GenesisEcotoneTimeOffset": "0x0",
  "l2GenesisFjordTimeOffset": "0x0",
  "l1CancunTimeOffset": "0x0",
  "systemConfigStartBlock": 0,
  "requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "recommendedProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "faultGameAbsolutePrestate": "0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98",
  "faultGameMaxDepth": 50,
  "faultGameClockExtension": 0,
  "faultGameMaxClockDuration": 1200,
  "faultGameGenesisBlock": 0,
  "faultGameGenesisOutputRoot": "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
  "faultGameSplitDepth": 14,
  "faultGameWithdrawalDelay": 604800,
  "preimageOracleMinProposalSize": 10000,
  "preimageOracleChallengePeriod": 120,
  "proofMaturityDelaySeconds": 12,
  "disputeGameFinalityDelaySeconds": 6,
  "respectedGameType": 254,
  "useFaultProofs": false,
  "useAltDA": false,
  "daCommitmentType": "KeccakCommitment",
  "daChallengeWindow": 16,
  "daResolveWindow": 16,
  "daBondSize": 1000000,
  "daResolverRefundPercentage": 0
}

# deploy the L1 contracts
# This step has to be ran against a clean network without prior deployments! Clean your L1 network if you've already deployed before
# the private key is for a standard dev account, it's prefunded. It's from mnemonic 'test test test test test test test test test test test junk'
DEPLOYMENT_OUTFILE=deployments/artifact.json DEPLOY_CONFIG_PATH=deploy-config/devnetL1.json forge script -vvv scripts/deploy/Deploy.s.sol:Deploy --rpc-url 127.0.0.1:8545 --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

# intermediate files for genesis
FORK=latest STATE_DUMP_PATH=deployments/state_dump.json DEPLOY_CONFIG_PATH=deploy-config/devnetL1.json CONTRACT_ADDRESSES_PATH=deployments/artifact.json forge script scripts/L2Genesis.s.sol:L2Genesis --sig 'runWithStateDump()'

cp deployments/artifact.json ../../build/artifact.json

# generate the genesis files for op-geth and op-node
go run ../../op-node/cmd/main.go genesis l2 \
  --deploy-config=deploy-config/devnetL1.json \
  --l1-deployments=deployments/artifact.json \
  --l2-allocs=deployments/state_dump.json \
  --outfile.l2=../../build/genesis.json \
  --outfile.rollup=../../build/rollup.json \
  --l1-rpc=http://127.0.0.1:8545

3. Run op-geth

openssl rand -hex 32 > /tmp/jwt.txt

git clone https://github.com/ethereum-optimism/op-geth.git

git checkout v1.101408.0

make all

# Prime the datadir with our genesis
./build/bin/geth --datadir=~/.op-geth/ --gcmode=archive init --state.scheme=hash ../optimism/build/genesis.json

# Run the node
./build/bin/geth \
  --datadir ~/.op-geth \
  --http \
  --http.corsdomain="*" \
  --http.vhosts="*" \
  --http.port=7547 \
  --http.addr=0.0.0.0 \
  --http.api=web3,debug,eth,txpool,net,engine \
  --ws \
  --ws.addr=0.0.0.0 \
  --ws.port=7546 \
  --ws.origins="*" \
  --ws.api=debug,eth,txpool,net,engine \
  --syncmode=full \
  --gcmode=archive \
  --nodiscover \
  --maxpeers=0 \
  --networkid=901 \
  --authrpc.vhosts="*" \
  --authrpc.addr=0.0.0.0 \
  --authrpc.port=8552 \
  --authrpc.jwtsecret=/tmp/jwt.txt \
  --rollup.disabletxpoolgossip=true
  
# erase ~/.op-geth and re-prime it with new genesis for a clean restart

4. Run op-node

Must be launched after op-geth

cd optimism

./op-node/bin/op-node   --l2=http://localhost:8552   --l2.jwt-secret=/tmp/jwt.txt   --sequencer.enabled   --sequencer.l1-confs=5   --verifier.l1-confs=4   --rollup.config=build/rollup.json   --rpc.addr=0.0.0.0   --p2p.disable   --rpc.enable-admin   --p2p.sequencer.key=0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba   --l1=http://127.0.0.1:8545 --l1.beacon=http://127.0.0.1:3500   --l1.rpckind=standard

4. Run op-batcher

Must be launched after op-node

cd optimism

./op-batcher/bin/op-batcher \
  --l2-eth-rpc=http://localhost:7547 \
  --rollup-rpc=http://localhost:9545 \
  --poll-interval=1s \
  --sub-safety-margin=6 \
  --num-confirmations=1 \
  --safe-abort-nonce-too-low-count=3 \
  --resubmission-timeout=30s \
  --rpc.addr=0.0.0.0 \
  --rpc.port=8548 \
  --rpc.enable-admin \
  --max-channel-duration=25 \
  --l1-eth-rpc=http://127.0.0.1:8545 \
  --private-key=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a

9545 is a default port for op-node's RPC

5. Run op-proposer

Must be launched after op-node

cd optimism

./op-proposer/bin/op-proposer \
  --poll-interval=12s \
  --rpc.port=8560 \
  --rollup-rpc=http://localhost:9545 \
  --l2oo-address=$(cat ./build/artifact.json | jq -r .L2OutputOracleProxy) \
  --private-key=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d \
  --l1-eth-rpc=http://127.0.0.1:8545

6. Send a transaction to L2

mkdir viem
cd viem
npm install viem

You may create this JS script which uses viem library:

import {createPublicClient, http, createWalletClient, defineChain} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

export const mychain = /*#__PURE__*/ defineChain({
  id: 901,
  name: 'Devnet',
  nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
  rpcUrls: {
    default: {
      http: ['http://localhost:7547'],
    },
  },
})

const client = createPublicClient({
  chain: mychain,
  transport: http('http://localhost:7547'),
});

const acc = privateKeyToAccount('0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e')

const wallet = createWalletClient({
  account: acc,
  chain: mychain,
  transport: http('http://localhost:7547'),
});

async function sendEth() {
  const balance = await client.getBalance({ address: acc.address });
  console.log(`Balance: ${balance} wei`);
  const txHash = await wallet.sendTransaction({
    to: '0x7626f6940e2eB28930efB4cEF49B2d1F2C9C1199',
    value: 1n,
  });
  console.log('Transaction hash:', txHash);
}

sendEth();

And run it with node script.js

You can also use node in REPL mode by typing node and pasting the code in there, but it'll require a different syntax