Builders
Chain Operators
Tutorials
Modifying Predeployed Contracts

Modifying Predeployed Contracts

⚠️

OP Stack Hacks are explicitly things that you can do with the OP Stack that are not currently intended for production use.

OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.

OP Stack blockchains have a number of predeployed contracts (opens in a new tab) that provide important functionality. Most of those contracts are proxies that can be upgraded using the proxyAdminOwner which was configured when the network was initially deployed.

Before You Begin

In this tutorial, you learn how to modify predeployed contracts for an OP Stack chain by upgrading the proxy. The predeploys are controlled from a predeploy called ProxyAdmin (opens in a new tab), whose address is 0x4200000000000000000000000000000000000018. The function to call is upgrade(address,address) (opens in a new tab). The first parameter is the proxy to upgrade, and the second is the address of a new implementation.

Modify the Legacy L1BlockNumber contract

For example, the legacy L1BlockNumber contract is at 0x420...013. To disable this function, we'll set the implementation to 0x00...00. We do this using the Foundry (opens in a new tab) command cast.

We'll need several constants.

  • Set these addresses as variables in your terminal.

    L1BLOCKNUM=0x4200000000000000000000000000000000000013
    PROXY_ADMIN=0x4200000000000000000000000000000000000018
    ZERO_ADDR=0x0000000000000000000000000000000000000000
  • Set PRIVKEY to the private key of your ADMIN address.

  • Set ETH_RPC_URL. If you're on the computer that runs the blockchain, use this command.

    export ETH_RPC_URL=http://localhost:8545

Verify L1BlockNumber works correctly.

See that when you call the contract you get a block number, and twelve seconds later you get the next one (block time on L1 is twelve seconds).

cast call $L1BLOCKNUM 'number()' | cast --to-dec
sleep 12 && cast call $L1BLOCKNUM 'number()' | cast --to-dec

Get the current implementation for the contract.

L1BLOCKNUM_IMPLEMENTATION=`cast call $L1BLOCKNUM "implementation()" | sed 's/000000000000000000000000//'`
echo $L1BLOCKNUM_IMPLEMENTATION 

Change the implementation to the zero address

cast send --private-key $PRIVKEY $PROXY_ADMIN "upgrade(address,address)" $L1BLOCKNUM $ZERO_ADDR

See that the implementation is address zero, and that calling it fails.

cast call $L1BLOCKNUM 'implementation()'
cast call $L1BLOCKNUM 'number()'

Fix the predeploy by returning it to the previous implementation, and verify it works.

cast send --private-key $PRIVKEY $PROXY_ADMIN "upgrade(address,address)" $L1BLOCKNUM $L1BLOCKNUM_IMPLEMENTATION
cast call $L1BLOCKNUM 'number()' | cast --to-dec