Skip to content
This repository has been archived by the owner on Dec 5, 2021. It is now read-only.

Commit

Permalink
feat(l2geth): sequencer fee pricing (#45)
Browse files Browse the repository at this point in the history
* feat(l2geth): sequencer fee pricing

ethereum-optimism/go-ethereum#273
ethereum-optimism/go-ethereum#306
ethereum-optimism/go-ethereum#305

* test(l1-l2-comms): fund l2 address before starting the test

* test(rpc): ensure that fee paid grows with data size

* test: add helper class for instantiation optimistic envs

* refactor: use the env container for l1<>l2 tests

* refactor: use the env container for queue ingestion and proxy tests

* fix(erc20-test): take fee payments into account

* refactor: use the env container for fee payment tests

* refactor: use the env container for native eth tests
  • Loading branch information
gakonst authored Apr 10, 2021
1 parent e5078f9 commit 69ffee2
Show file tree
Hide file tree
Showing 36 changed files with 650 additions and 337 deletions.
77 changes: 17 additions & 60 deletions integration-tests/test/basic-l1-l2-communication.spec.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,33 @@
import { expect } from 'chai'

/* Imports: External */
import { Contract, ContractFactory, Wallet, providers } from 'ethers'
import { Watcher } from '@eth-optimism/core-utils'
import {
initWatcher,
waitForXDomainTransaction,
Direction,
} from './shared/watcher-utils'
import { getContractFactory } from '@eth-optimism/contracts'
import { Contract, ContractFactory, utils } from 'ethers'
import { Direction } from './shared/watcher-utils'

/* Imports: Internal */
import l1SimpleStorageJson from '../artifacts/contracts/SimpleStorage.sol/SimpleStorage.json'
import l2SimpleStorageJson from '../artifacts-ovm/contracts/SimpleStorage.sol/SimpleStorage.json'
import { OptimismEnv } from './shared/env'

describe('Basic L1<>L2 Communication', async () => {
let l1Wallet: Wallet
let l2Wallet: Wallet
let l1Provider: providers.JsonRpcProvider
let l2Provider: providers.JsonRpcProvider
let AddressManager: Contract

let Factory__L1SimpleStorage: ContractFactory
let Factory__L2SimpleStorage: ContractFactory
let L1CrossDomainMessenger: Contract
let L2CrossDomainMessenger: Contract

let watcher: Watcher

let L1SimpleStorage: Contract
let L2SimpleStorage: Contract
let env: OptimismEnv

before(async () => {
const httpPort = 8545
const l1HttpPort = 9545
l1Provider = new providers.JsonRpcProvider(`http://localhost:${l1HttpPort}`)
l2Provider = new providers.JsonRpcProvider(`http://localhost:${httpPort}`)
l1Wallet = new Wallet(
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
l1Provider
)
l2Wallet = Wallet.createRandom().connect(l2Provider)

const addressManagerAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3'
AddressManager = getContractFactory('Lib_AddressManager')
.connect(l1Wallet)
.attach(addressManagerAddress)

env = await OptimismEnv.new()
Factory__L1SimpleStorage = new ContractFactory(
l1SimpleStorageJson.abi,
l1SimpleStorageJson.bytecode,
l1Wallet
env.l1Wallet
)
Factory__L2SimpleStorage = new ContractFactory(
l2SimpleStorageJson.abi,
l2SimpleStorageJson.bytecode,
l2Wallet
env.l2Wallet
)

watcher = await initWatcher(l1Provider, l2Provider, AddressManager)
L1CrossDomainMessenger = getContractFactory('iOVM_L1CrossDomainMessenger')
.connect(l1Wallet)
.attach(watcher.l1.messengerAddress)
L2CrossDomainMessenger = getContractFactory('iOVM_L2CrossDomainMessenger')
.connect(l2Wallet)
.attach(watcher.l2.messengerAddress)
})

beforeEach(async () => {
Expand All @@ -78,19 +41,16 @@ describe('Basic L1<>L2 Communication', async () => {
const value = `0x${'77'.repeat(32)}`

// Send L2 -> L1 message.
const transaction = await L2CrossDomainMessenger.sendMessage(
const transaction = await env.l2Messenger.sendMessage(
L1SimpleStorage.address,
L1SimpleStorage.interface.encodeFunctionData('setValue', [value]),
5000000,
{ gasLimit: 7000000 }
5000000
)

await waitForXDomainTransaction(watcher, transaction, Direction.L2ToL1)
await env.waitForXDomainTransaction(transaction, Direction.L2ToL1)

expect(await L1SimpleStorage.msgSender()).to.equal(
L1CrossDomainMessenger.address
)
expect(await L1SimpleStorage.xDomainSender()).to.equal(l2Wallet.address)
expect(await L1SimpleStorage.msgSender()).to.equal(env.l1Messenger.address)
expect(await L1SimpleStorage.xDomainSender()).to.equal(env.l2Wallet.address)
expect(await L1SimpleStorage.value()).to.equal(value)
expect((await L1SimpleStorage.totalCount()).toNumber()).to.equal(1)
})
Expand All @@ -99,19 +59,16 @@ describe('Basic L1<>L2 Communication', async () => {
const value = `0x${'42'.repeat(32)}`

// Send L1 -> L2 message.
const transaction = await L1CrossDomainMessenger.sendMessage(
const transaction = await env.l1Messenger.sendMessage(
L2SimpleStorage.address,
L2SimpleStorage.interface.encodeFunctionData('setValue', [value]),
5000000,
{ gasLimit: 7000000 }
5000000
)

await waitForXDomainTransaction(watcher, transaction, Direction.L1ToL2)
await env.waitForXDomainTransaction(transaction, Direction.L1ToL2)

expect(await L2SimpleStorage.msgSender()).to.equal(
L2CrossDomainMessenger.address
)
expect(await L2SimpleStorage.xDomainSender()).to.equal(l1Wallet.address)
expect(await L2SimpleStorage.msgSender()).to.equal(env.l2Messenger.address)
expect(await L2SimpleStorage.xDomainSender()).to.equal(env.l1Wallet.address)
expect(await L2SimpleStorage.value()).to.equal(value)
expect((await L2SimpleStorage.totalCount()).toNumber()).to.equal(1)
})
Expand Down
15 changes: 12 additions & 3 deletions integration-tests/test/erc20.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Contract, ContractFactory, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { expect } from 'chai'
import { GWEI } from './shared/utils'
import { OptimismEnv } from './shared/env'

describe('Basic ERC20 interactions', async () => {
const initialAmount = 1000
Expand All @@ -14,7 +16,8 @@ describe('Basic ERC20 interactions', async () => {
let ERC20: Contract

before(async () => {
wallet = Wallet.createRandom().connect(ethers.provider)
const env = await OptimismEnv.new()
wallet = env.l2Wallet
other = Wallet.createRandom().connect(ethers.provider)
Factory__ERC20 = await ethers.getContractFactory('ERC20', wallet)
})
Expand Down Expand Up @@ -57,10 +60,16 @@ describe('Basic ERC20 interactions', async () => {
const transfer = await ERC20.transfer(other.address, 100)
const receipt = await transfer.wait()

// The expected fee paid is the value returned by eth_estimateGas gas multiplied
// by 1 gwei, since that's the value eth_gasPrice always returns
const expectedFeePaid = (
await ERC20.estimateGas.transfer(other.address, 100)
).mul(GWEI)

// There are two events from the transfer with the first being
// the fee of value 0 and the second of the value transfered (100)
// the ETH fee paid and the second of the value transfered (100)
expect(receipt.events.length).to.equal(2)
expect(receipt.events[0].args._value.toNumber()).to.equal(0)
expect(receipt.events[0].args._value).to.deep.equal(expectedFeePaid)
expect(receipt.events[1].args._from).to.equal(wallet.address)
expect(receipt.events[1].args._value.toNumber()).to.equal(100)

Expand Down
42 changes: 12 additions & 30 deletions integration-tests/test/fee-payment.spec.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,35 @@
import chai, { expect } from 'chai'
import chaiAsPromised from 'chai-as-promised'
chai.use(chaiAsPromised)
import { BigNumber, Contract, utils } from 'ethers'

import {
l1Provider,
l2Provider,
l1Wallet,
l2Wallet,
getGateway,
getAddressManager,
getOvmEth,
fundUser,
} from './shared/utils'
import { initWatcher } from './shared/watcher-utils'
import { BigNumber, utils } from 'ethers'
import { OptimismEnv } from './shared/env'

describe('Fee Payment Integration Tests', async () => {
let OVM_L1ETHGateway: Contract
let OVM_ETH: Contract
let AddressManager: Contract
let env: OptimismEnv
const other = '0x1234123412341234123412341234123412341234'
const amount = utils.parseEther('1')

before(async () => {
AddressManager = getAddressManager(l1Wallet)
OVM_L1ETHGateway = await getGateway(l1Wallet, AddressManager)
OVM_ETH = getOvmEth(l2Wallet)
const watcher = await initWatcher(l1Provider, l2Provider, AddressManager)
await fundUser(watcher, OVM_L1ETHGateway, amount)
env = await OptimismEnv.new()
})

it('Paying a nonzero but acceptable gasPrice fee', async () => {
// manually set the gas price because otherwise it's returned as 0
const gasPrice = BigNumber.from(1_000_000)
const amt = amount.div(2)
const amount = utils.parseEther('0.5')

const balanceBefore = await l2Wallet.getBalance()
const tx = await OVM_ETH.transfer(other, amt, { gasPrice })
const balanceBefore = await env.l2Wallet.getBalance()
const tx = await env.ovmEth.transfer(other, amount)
await tx.wait()
const balanceAfter = await l2Wallet.getBalance()
const balanceAfter = await env.l2Wallet.getBalance()
// TODO: The fee paid MUST be the receipt.gasUsed, and not the tx.gasLimit
// https://github.com/ethereum-optimism/optimism/blob/0de7a2f9c96a7c4860658822231b2d6da0fefb1d/packages/contracts/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol#L103
expect(balanceBefore.sub(balanceAfter)).to.be.deep.eq(
gasPrice.mul(tx.gasLimit).add(amt)
tx.gasPrice.mul(tx.gasLimit).add(amount)
)
})

it('sequencer rejects transaction with a non-multiple-of-1M gasPrice', async () => {
const gasPrice = BigNumber.from(1_000_000 - 1)
await expect(
OVM_ETH.transfer(other, 0, { gasPrice })
env.ovmEth.transfer(other, 0, { gasPrice })
).to.be.eventually.rejectedWith(
'Gas price must be a multiple of 1,000,000 wei'
)
Expand Down
Loading

0 comments on commit 69ffee2

Please sign in to comment.