Creating Your Own L2 Rollup Testnet on Phalcon Fork

6 min readJan 26, 2024


This document outlines the steps to create your own L2 rollup testnet using Phalcon Fork as the L1 blockchain. These instructions are based on the Optimism documentation. Ensure you have installed the necessary software dependencies from the Optimism docs before proceeding.

Get Access to Phalcon Fork

To deploy the OP Stack Rollup into Phalcon Fork, you must create a Fork and obtain the following parameters.

Create a Fork

Log into Register an account if you do not have one.

Click the created Fork and click ‘Settings’ button inside the Fork.

Obtain the following information.

  • RPC: the RPC of this Fork. We use [FORK_RPC] in the following.
  • RPC ID: the id of this Fork RPC. We use [RPC_ID] in the following.

We also need the API create of your account to verify the contracts. Click the account icon on the left bottom and click Account Settings. Get the Access Key (DO NOT SHARE THIS WITH ANYONE).

  • Access Key: The key used to verify the contracts. We use [API_KEY] in the following.

Build the Source Code

Build the Optimism Monorepo

cd ~
git clone
cd optimism
git checkout tutorials/chain

Install dependencies and build the packages.

git submodule update --init --recursive
pnpm install
make op-node op-batcher op-proposer
pnpm build

Build op-geth

Clone the repository and build op-geth.

cd ~
git clone
cd op-geth
make geth

Fill Out Environment Variables

cd ~/optimism
cp .envrc.example .envrc

Edit .envrc using a text editor of your choice. Modify the L1_RPC_URL to the RPC URL you obtained from Phalcon Fork and set L1_RPC_KIND to basic.

Generate Addresses

Follow the link to generate addresses. Copy the address to .envrc.

Fund these address using the Faucet in Phalcon Fork. In this blog, the addresses are:

  • export GS_ADMIN_ADDRESS=0x5E7AfB127cf0Bfbd7f259D1a5Bd05a53F7f1cC89
  • export GS_BATCHER_ADDRESS=0xed6DCC6B20Ee9d850B56C2b474BE5c666D7C44DB
  • export GS_PROPOSER_ADDRESS=0xf4425AdF3F4fD45dEb4E768A4F32FEa0b774a63E
  • export GS_SEQUENCER_ADDRESS=0x2012d9D7cc85Bc1d9231123B506a99C56d722A60

I added 100 Ether to each address using the Faucet tool.

Load Environment variables

cd ~/optimism
direnv allow

You need to confirm the variables were loaded!

Configure the network

cd ~/optimism
cd packages/contracts-bedrock

We need to change the L1 chainID to 1 in ./scripts/getting-started/ .

"l1ChainID": 1,
"l2ChainID": 42069,

Now execute the config script.


Deploy the Create2 Factory (Optional)

Since the Create2 factory is already deployed in the Ethereum mainnet (and is forked inside our Fork), we do not need to deploy this factory. We can use the following command to double confirm that.

cast codesize 0x4e59b44847b379578588920cA78FbF26c0B4956C --rpc-url $L1_RPC_URL

The output is 69.

Deploy and verify the L1 contracts.

If you do not need to verify the contracts, the parameters after --slow can be removed.

cd ~/optimism
cd packages/contracts-bedrock
forge script scripts/Deploy.s.sol:Deploy --private-key $GS_ADMIN_PRIVATE_KEY --broadcast --rpc-url $L1_RPC_URL --slow --verify --verifier-url[RPC_ID] --etherscan-api-key [API_KEY]

Note compared with the original article, I added a couple of parameters in the command.

  • — slow: Makes sure a transaction is sent, only after its previous one has been confirmed and succeeded. See this link.
  • — verify: Verify the deployed contracts
  • — verifier-url: the verifier URL in Phalcon Fork
  • — etherscan-api-key: the API key used to verify contracts in Phalcon Fork (not Etherscan)
forge script scripts/Deploy.s.sol:Deploy --sig 'sync()' --rpc-url $L1_RPC_URL

See all the deployed and verified smart contracts.

Generate the L2 config files

cd ~/optimism/op-node
go run cmd/main.go genesis l2 \
--deploy-config ../packages/contracts-bedrock/deploy-config/getting-started.json \
--deployment-dir ../packages/contracts-bedrock/deployments/getting-started/ \
--outfile.l2 genesis.json \
--outfile.rollup rollup.json \
--l1-rpc $L1_RPC_URL
openssl rand -hex 32 > jwt.txt
cp genesis.json ~/op-geth
cp jwt.txt ~/op-geth

Initialize op-geth

cd ~/op-geth
mkdir datadir
build/bin/geth init --datadir=datadir genesis.json

Start op-geth

cd ~/op-geth
./build/bin/geth \
--datadir ./datadir \
--http \
--http.corsdomain="*" \
--http.vhosts="*" \
--http.addr= \
--http.api=web3,debug,eth,txpool,net,engine \
--ws \
--ws.addr= \
--ws.port=8546 \"*" \
--ws.api=debug,eth,txpool,net,engine \
--syncmode=full \
--gcmode=archive \
--nodiscover \
--maxpeers=0 \
--networkid=42069 \
--authrpc.vhosts="*" \
--authrpc.addr= \
--authrpc.port=8551 \
--authrpc.jwtsecret=./jwt.txt \

Make Phalcon Fork Automatically Generate Block (Important!)

Different from Ethereum, Phalcon Fork does not automatically generate a new block every 12 seconds. However, we can use the evm_mine method to generate an empty block to simulate this behaviro.

Create a script called

curl --location [FORK_RPC]  \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"method": "evm_mine",
"params": [],
"id": 1

Then use watch to execute this script every 12 seconds.

chmod a+x
watch -n 12 -x ./

In Phalcon Block dashboard, we can see that the next block number changes every 12 seconds (if we refresh the page.) We can also see the transactions, blocks inside the Phalcon Block Scan.

Start op-node

cd ~/optimism/op-node
./bin/op-node \
--l2=http://localhost:8551 \
--l2.jwt-secret=./jwt.txt \
--sequencer.enabled \
--sequencer.l1-confs=5 \
--verifier.l1-confs=4 \
--rollup.config=./rollup.json \
--rpc.addr= \
--rpc.port=8547 \
--p2p.disable \
--rpc.enable-admin \
--p2p.sequencer.key=$GS_SEQUENCER_PRIVATE_KEY \
--l1=$L1_RPC_URL \

**Note that, we added --l1.trustrpc in the command. **That's because Phalcon Block does not support the eth_getProof method in the op-node code.

Start op-batcher

cd ~/optimism/op-batcher
./bin/op-batcher \
--l2-eth-rpc=http://localhost:8545 \
--rollup-rpc=http://localhost:8547 \
--poll-interval=1s \
--sub-safety-margin=6 \
--num-confirmations=1 \
--safe-abort-nonce-too-low-count=3 \
--resubmission-timeout=30s \
--rpc.addr= \
--rpc.port=8548 \
--rpc.enable-admin \
--max-channel-duration=1 \
--l1-eth-rpc=$L1_RPC_URL \

Start op-proposer

cd ~/optimism/op-proposer
./bin/op-proposer \
--poll-interval=12s \
--rpc.port=8560 \
--rollup-rpc=http://localhost:8547 \
--l2oo-address=$(cat ../packages/contracts-bedrock/deployments/getting-started/L2OutputOracleProxy.json | jq -r .address) \
--private-key=$GS_PROPOSER_PRIVATE_KEY \

Now we have a working l2 Rollup Testnet based on Phalcon Fork as L1.

Connect Your Wallet to Your Chain

We manually add a new network in MetaMask, with the following parameters:

  • Network Name: L2_ON_Phalcon_Fork (or anyname you want)
  • New RPC URL: http://localhost:8545 (or a publiciP:8545 )
  • Chain ID: 42069
  • Currency: Ether (ETH)
  • Explorer: leave blank

We also need to add the L1 network (Phalcon Fork) into the metamask. You can click “Add to MetaMask” button in the Fork dashboard.

Get ETH On Your Chain

We can then get ETH on the L2 chain by sending Ether from L1 (Phalcon Block) to the L1StandardBridgeProxy. In this blog, I sent 88 Ether to the L2. See this transaction.

And in L2, we can see the bridged 88 Ether. Please take a look at the following transactions.


View in Phalcon Explorer.


The Fork used in this blog can be found in this Phalcon Fork Scan

In summary, we create a L2 rollup testnet based on OP stack using a forked mainnet as L1. By doing so, we can learn how the L2 works, and make further changes if necessary.

Access Phalcon Fork today to make your own L2 rollup testnet!




The BlockSec focuses on the security of the blockchain ecosystem and the research of DeFi attack monitoring and blocking.