Commit 76c4ceb0 authored by Georgios Konstantopoulos's avatar Georgios Konstantopoulos Committed by GitHub

Calculate fees correctly based on zero/non-zero bytes (#540)

* test: pin down gas estimation for transfers

* fix(l2geth): charge fees differently for zero/non-zero bytes of data

* test: update estimated gas values

* chore: gofmt & changeset

* fix(l2geth): calculate zeros correctly
parent 27f32ca0
---
"@eth-optimism/l2geth": patch
---
Calculate data fees based on if a byte was zero or non-zero
...@@ -45,13 +45,13 @@ describe('Native ETH Integration Tests', async () => { ...@@ -45,13 +45,13 @@ describe('Native ETH Integration Tests', async () => {
const amount = utils.parseEther('0.5') const amount = utils.parseEther('0.5')
const addr = await l2Bob.getAddress() const addr = await l2Bob.getAddress()
const gas = await env.ovmEth.estimateGas.transfer(addr, amount) const gas = await env.ovmEth.estimateGas.transfer(addr, amount)
expect(gas).is.instanceof(BigNumber) expect(gas).to.be.deep.eq(BigNumber.from(213546))
}) })
it('Should estimate gas for ETH withdraw', async () => { it('Should estimate gas for ETH withdraw', async () => {
const amount = utils.parseEther('0.5') const amount = utils.parseEther('0.5')
const gas = await env.ovmEth.estimateGas.withdraw(amount) const gas = await env.ovmEth.estimateGas.withdraw(amount)
expect(gas).is.instanceof(BigNumber) expect(gas).to.be.deep.eq(BigNumber.from(503749))
}) })
}) })
......
...@@ -2,21 +2,38 @@ package core ...@@ -2,21 +2,38 @@ package core
import ( import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/params"
) )
// RollupBaseTxSize is the encoded rollup transaction's compressed size excluding // RollupBaseTxSize is the encoded rollup transaction's compressed size excluding
// the variable length data. // the variable length data.
// Ref: https://github.com/ethereum-optimism/optimism/blob/91a9a3dcddf534ae1c906133b6d8e015a23c463b/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_SequencerEntrypoint.sol#L47 // Ref: https://github.com/ethereum-optimism/optimism/blob/91a9a3dcddf534ae1c906133b6d8e015a23c463b/packages/contracts/contracts/optimistic-ethereum/OVM/predeploys/OVM_SequencerEntrypoint.sol#L47
const RollupBaseTxSize int = 96 const RollupBaseTxSize uint64 = 96
// CalculateFee calculates the fee that must be paid to the Rollup sequencer, taking into // CalculateFee calculates the fee that must be paid to the Rollup sequencer, taking into
// account the cost of publishing data to L1. // account the cost of publishing data to L1.
// Returns: (RollupBaseTxSize + len(data)) * dataPrice + executionPrice * gasUsed // Returns: (4 * zeroDataBytes + 16 * (nonZeroDataBytes + RollupBaseTxSize)) * dataPrice + executionPrice * gasUsed
func CalculateRollupFee(data []byte, gasUsed uint64, dataPrice, executionPrice *big.Int) *big.Int { func CalculateRollupFee(data []byte, gasUsed uint64, dataPrice, executionPrice *big.Int) *big.Int {
dataLen := int64(RollupBaseTxSize + len(data)) zeroes, ones := zeroesAndOnes(data)
zeroesCost := new(big.Int).SetUint64(zeroes * params.TxDataZeroGas)
onesCost := new(big.Int).SetUint64((RollupBaseTxSize + ones) * params.TxDataNonZeroGasEIP2028)
dataCost := new(big.Int).Add(zeroesCost, onesCost)
// get the data fee // get the data fee
dataFee := new(big.Int).Mul(dataPrice, big.NewInt(dataLen)) dataFee := new(big.Int).Mul(dataPrice, dataCost)
executionFee := new(big.Int).Mul(executionPrice, new(big.Int).SetUint64(gasUsed)) executionFee := new(big.Int).Mul(executionPrice, new(big.Int).SetUint64(gasUsed))
fee := new(big.Int).Add(dataFee, executionFee) fee := new(big.Int).Add(dataFee, executionFee)
return fee return fee
} }
func zeroesAndOnes(data []byte) (uint64, uint64) {
var zeroes uint64
for _, byt := range data {
if byt == 0 {
zeroes++
}
}
ones := uint64(len(data)) - zeroes
return zeroes, ones
}
...@@ -23,9 +23,14 @@ func TestCalculateRollupFee(t *testing.T) { ...@@ -23,9 +23,14 @@ func TestCalculateRollupFee(t *testing.T) {
data := make([]byte, 0, tt.dataLen) data := make([]byte, 0, tt.dataLen)
fee := CalculateRollupFee(data, tt.gasUsed, big.NewInt(tt.dataPrice), big.NewInt(tt.executionPrice)) fee := CalculateRollupFee(data, tt.gasUsed, big.NewInt(tt.dataPrice), big.NewInt(tt.executionPrice))
dataFee := uint64((RollupBaseTxSize + len(data)) * int(tt.dataPrice)) zeroes, ones := zeroesAndOnes(data)
zeroesCost := zeroes * 4
onesCost := (96 + ones) * 16
dataCost := zeroesCost + onesCost
dataFee := int64(dataCost) * tt.dataPrice
executionFee := uint64(tt.executionPrice) * tt.gasUsed executionFee := uint64(tt.executionPrice) * tt.gasUsed
expectedFee := dataFee + executionFee expectedFee := uint64(dataFee) + executionFee
if fee.Cmp(big.NewInt(int64(expectedFee))) != 0 { if fee.Cmp(big.NewInt(int64(expectedFee))) != 0 {
t.Errorf("rollup fee check failed: expected %d, got %s", expectedFee, fee.String()) t.Errorf("rollup fee check failed: expected %d, got %s", expectedFee, fee.String())
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment