Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/chatty-oranges-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/l2geth': patch
'@eth-optimism/contracts': patch
---

Add support for system addresses
5 changes: 4 additions & 1 deletion integration-tests/test/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const procEnv = cleanEnv(process.env, {
L1_URL: str({ default: 'http://localhost:9545' }),
L1_POLLING_INTERVAL: num({ default: 10 }),

L2_CHAINID: num({ default: 420 }),
L2_CHAINID: num({ default: 987 }),
L2_GAS_PRICE: gasPriceValidator({
default: 'onchain',
}),
Expand Down Expand Up @@ -87,6 +87,9 @@ const procEnv = cleanEnv(process.env, {
RUN_VERIFIER_TESTS: bool({
default: true,
}),
RUN_SYSTEM_ADDRESS_TESTS: bool({
default: false,
}),

MOCHA_TIMEOUT: num({
default: 120_000,
Expand Down
105 changes: 105 additions & 0 deletions integration-tests/test/system-addresses.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* Imports: External */
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { futurePredeploys } from '@eth-optimism/contracts'

/* Imports: Internal */
import { expect } from './shared/setup'
import { OptimismEnv } from './shared/env'
import { envConfig } from './shared/utils'

const SYSTEM_ADDRESSES = [futurePredeploys.System0, futurePredeploys.System1]

describe('System addresses', () => {
let env: OptimismEnv

let deployerWallets: Wallet[] = []

let contracts: Contract[]

let Factory__ERC20: ContractFactory

before(async function () {
if (!envConfig.RUN_SYSTEM_ADDRESS_TESTS) {
console.log('Skipping system address tests.')
this.skip()
return
}

env = await OptimismEnv.new()

deployerWallets = [
new Wallet(process.env.SYSTEM_ADDRESS_0_DEPLOYER_KEY, env.l2Provider),
new Wallet(process.env.SYSTEM_ADDRESS_1_DEPLOYER_KEY, env.l2Provider),
]
for (const deployer of deployerWallets) {
await env.l2Wallet.sendTransaction({
to: deployer.address,
value: utils.parseEther('0.1'),
})
}
contracts = []

Factory__ERC20 = await ethers.getContractFactory('ERC20', env.l2Wallet)
})

it('should have no code for the system addresses initially', async () => {
for (const addr of SYSTEM_ADDRESSES) {
const code = await env.l2Provider.getCode(addr, 'latest')
expect(code).to.eq('0x')
}
})

it('should deploy to the system address', async () => {
for (let i = 0; i < deployerWallets.length; i++) {
const contract = await Factory__ERC20.connect(deployerWallets[i]).deploy(
100000000,
'OVM Test',
8,
'OVM'
)
// have to use the receipt here since ethers calculates the
// contract address on-the-fly
const receipt = await contract.deployTransaction.wait()
expect(receipt.contractAddress).to.eq(SYSTEM_ADDRESSES[i])

const fetchedReceipt = await env.l2Provider.getTransactionReceipt(
receipt.transactionHash
)
expect(fetchedReceipt.contractAddress).to.eq(SYSTEM_ADDRESSES[i])

contracts.push(await ethers.getContractAt('ERC20', SYSTEM_ADDRESSES[i]))
}
})

it('contracts deployed at the system addresses should function', async () => {
expect(contracts.length).to.eq(2)

for (let i = 0; i < contracts.length; i++) {
const wallet = deployerWallets[i]
const contract = contracts[i].connect(wallet)
const code = await env.l2Provider.getCode(contract.address, 'latest')
expect(code).not.to.eq('0x')

const tx = await contract.transfer(env.l2Wallet.address, 1000)
await tx.wait()
const bal = await contract.balanceOf(env.l2Wallet.address)
expect(bal).to.deep.equal(BigNumber.from(1000))
}
})

it('should not deploy any additional contracts from the deployer at the system address', async () => {
for (let i = 0; i < deployerWallets.length; i++) {
const contract = await Factory__ERC20.connect(deployerWallets[i]).deploy(
100000000,
'OVM Test',
8,
'OVM'
)
await contract.deployed()
const receipt = await contract.deployTransaction.wait()
expect(receipt.contractAddress).not.to.eq(SYSTEM_ADDRESSES[i])
expect(receipt.contractAddress).not.to.eq(null)
}
})
})
14 changes: 13 additions & 1 deletion l2geth/core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ethereum-optimism/optimism/l2geth/crypto"
"github.com/ethereum-optimism/optimism/l2geth/params"
"github.com/ethereum-optimism/optimism/l2geth/rollup/fees"
"github.com/ethereum-optimism/optimism/l2geth/rollup/rcfg"
)

// StateProcessor is a basic Processor, which takes care of transitioning
Expand Down Expand Up @@ -132,7 +133,18 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
receipt.GasUsed = gas
// if the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
if rcfg.UsingOVM {
sysAddress := rcfg.SystemAddressFor(config.ChainID, vmenv.Context.Origin)
// If nonce is zero, and the deployer is a system address deployer,
// set the provided system contract address.
if sysAddress != rcfg.ZeroSystemAddress && tx.Nonce() == 0 && tx.To() == nil {
receipt.ContractAddress = sysAddress
} else {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
} else {
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
}
// Set the receipt logs and create a bloom for filtering
receipt.Logs = statedb.GetLogs(tx.Hash())
Expand Down
17 changes: 16 additions & 1 deletion l2geth/core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"math/big"
"unsafe"

"github.com/ethereum-optimism/optimism/l2geth/rollup/rcfg"

"github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum-optimism/optimism/l2geth/common/hexutil"
"github.com/ethereum-optimism/optimism/l2geth/crypto"
Expand Down Expand Up @@ -352,7 +354,20 @@ func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, num
if txs[i].To() == nil {
// Deriving the signer is expensive, only do if it's actually needed
from, _ := Sender(signer, txs[i])
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
nonce := txs[i].Nonce()

if rcfg.UsingOVM {
sysAddress := rcfg.SystemAddressFor(config.ChainID, from)
// If nonce is zero, and the deployer is a system address deployer,
// set the provided system contract address.
if sysAddress != rcfg.ZeroSystemAddress && nonce == 0 && txs[i].To() == nil {
r[i].ContractAddress = sysAddress
} else {
r[i].ContractAddress = crypto.CreateAddress(from, nonce)
}
} else {
r[i].ContractAddress = crypto.CreateAddress(from, nonce)
}
}
// The used gas can be calculated based on previous r
if i == 0 {
Expand Down
10 changes: 10 additions & 0 deletions l2geth/core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,16 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
return ret, common.Address{}, gas, errExecutionReverted
}

// Get the system address for this caller.
sysAddr := rcfg.SystemAddressFor(evm.ChainConfig().ChainID, caller.Address())

// If there is a configured system address for this caller, and the caller's nonce is zero,
// and there is no contract already deployed at this system address, then set the created
// address to the system address.
if sysAddr != rcfg.ZeroSystemAddress && evm.StateDB.GetNonce(caller.Address()) == 0 {
address = sysAddr
}
}
nonce := evm.StateDB.GetNonce(caller.Address())
evm.StateDB.SetNonce(caller.Address(), nonce+1)
Expand Down
8 changes: 4 additions & 4 deletions l2geth/rollup/rcfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import (
var UsingOVM bool

var (
// l2GasPriceSlot refers to the storage slot that the L2 gas price is stored
// L2GasPriceSlot refers to the storage slot that the L2 gas price is stored
// in in the OVM_GasPriceOracle predeploy
L2GasPriceSlot = common.BigToHash(big.NewInt(1))
// l1GasPriceSlot refers to the storage slot that the L1 gas price is stored
// L1GasPriceSlot refers to the storage slot that the L1 gas price is stored
// in in the OVM_GasPriceOracle predeploy
L1GasPriceSlot = common.BigToHash(big.NewInt(2))
// l2GasPriceOracleOwnerSlot refers to the storage slot that the owner of
// L2GasPriceOracleOwnerSlot refers to the storage slot that the owner of
// the OVM_GasPriceOracle is stored in
L2GasPriceOracleOwnerSlot = common.BigToHash(big.NewInt(0))
// l2GasPriceOracleAddress is the address of the OVM_GasPriceOracle
// L2GasPriceOracleAddress is the address of the OVM_GasPriceOracle
// predeploy
L2GasPriceOracleAddress = common.HexToAddress("0x420000000000000000000000000000000000000F")
// OverheadSlot refers to the storage slot in the OVM_GasPriceOracle that
Expand Down
92 changes: 92 additions & 0 deletions l2geth/rollup/rcfg/system_address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package rcfg

import (
"math/big"
"os"

"github.com/ethereum-optimism/optimism/l2geth/common"
)

// SystemAddress0 is the first deployable system address.
var SystemAddress0 = common.HexToAddress("0x4200000000000000000000000000000000000042")

// SystemAddress1 is the second deployable system address.
var SystemAddress1 = common.HexToAddress("0x4200000000000000000000000000000000000014")

// ZeroSystemAddress is the emprt system address.
var ZeroSystemAddress common.Address
Comment thread
mslipper marked this conversation as resolved.
Outdated

// SystemAddressDeployer is a tuple containing the deployment
// addresses for SystemAddress0 and SystemAddress1.
type SystemAddressDeployer [2]common.Address

// SystemAddressFor returns the system address for a given deployment
// address. If no system address is configured for this deployer,
// ZeroSystemAddress is returned.
func (s SystemAddressDeployer) SystemAddressFor(addr common.Address) common.Address {
if s[0] == addr {
return SystemAddress0
}

if s[1] == addr {
return SystemAddress1
}

return ZeroSystemAddress
}

// SystemAddressFor is a convenience method that returns an environment-based
// system address if the passed-in chain ID is not hardcoded.
func SystemAddressFor(chainID *big.Int, addr common.Address) common.Address {
sysDeployer, hasHardcodedSysDeployer := SystemAddressDeployers[chainID.Uint64()]
if !hasHardcodedSysDeployer {
sysDeployer = envSystemAddressDeployer
}

return sysDeployer.SystemAddressFor(addr)
}

// SystemAddressDeployers maintains a hardcoded map of chain IDs to
// system addresses.
var SystemAddressDeployers = map[uint64]SystemAddressDeployer{
// Mainnet
10: {
common.HexToAddress("0xcDE47C1a5e2d60b9ff262b0a3b6d486048575Ad9"),
common.HexToAddress("0x53A6eecC2dD4795Fcc68940ddc6B4d53Bd88Bd9E"),
},

// Kovan
69: {
common.HexToAddress("0xd23eb5c2dd7035e6eb4a7e129249d9843123079f"),
common.HexToAddress("0xa81224490b9fa4930a2e920550cd1c9106bb6d9e"),
},

// Goerli
420: {
common.HexToAddress("0xc30276833798867c1dbc5c468bf51ca900b44e4c"),
common.HexToAddress("0x5c679a57e018f5f146838138d3e032ef4913d551"),
},
}

var envSystemAddressDeployer SystemAddressDeployer

func initEnvSystemAddressDeployer() {
deployer0Env := os.Getenv("SYSTEM_ADDRESS_0_DEPLOYER")
deployer1Env := os.Getenv("SYSTEM_ADDRESS_1_DEPLOYER")

Comment thread
mslipper marked this conversation as resolved.
Outdated
if deployer0Env == "" && deployer1Env == "" {
return
}
if !common.IsHexAddress(deployer0Env) {
panic("SYSTEM_ADDRESS_0_DEPLOYER specified but invalid")
}
if !common.IsHexAddress(deployer1Env) {
panic("SYSTEM_ADDRESS_1_DEPLOYER specified but invalid")
}
envSystemAddressDeployer[0] = common.HexToAddress(deployer0Env)
envSystemAddressDeployer[1] = common.HexToAddress(deployer1Env)
}

func init() {
initEnvSystemAddressDeployer()
}
Loading