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
5 changes: 5 additions & 0 deletions .changeset/tasty-adults-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/core-utils': patch
---

Add a `calldataCost` function that computes the cost of calldata
34 changes: 26 additions & 8 deletions packages/contracts/tasks/fetch-batches.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { ethers } from 'ethers'
import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes'
import { BatchType, SequencerBatch } from '@eth-optimism/core-utils'
import {
BatchType,
SequencerBatch,
calldataCost,
} from '@eth-optimism/core-utils'

import { names } from '../src/address-names'
import { getContractFromArtifact } from '../src/deploy-utils'
Expand Down Expand Up @@ -52,28 +56,42 @@ task('fetch-batches')
const tx = await provider.getTransaction(event.transactionHash)
const batch = (SequencerBatch as any).fromHex(tx.data)

// Add an extra field to the resulting json
// so that the serialization sizes can be observed
// Add extra fields to the resulting json
// so that the serialization sizes and gas usage can be observed
const json = batch.toJSON()
json.sizes = {
legacy: 0,
zlib: 0,
}
json.gasUsage = {
legacy: 0,
zlib: 0,
}

// Create a copy of the batch to serialize in
// the alternative format
const copy = (SequencerBatch as any).fromHex(tx.data)
let legacy: Buffer
let zlib: Buffer
if (batch.type === BatchType.ZLIB) {
copy.type = BatchType.LEGACY
json.sizes.legacy = copy.encode().length
json.sizes.zlib = batch.encode().length
legacy = copy.encode()
zlib = batch.encode()
} else {
copy.type = BatchType.ZLIB
json.sizes.zlib = copy.encode().length
json.sizes.legacy = batch.encode().length
zlib = copy.encode()
legacy = batch.encode()
}

json.compressionRatio = json.sizes.zlib / json.sizes.legacy
json.sizes.legacy = legacy.length
json.sizes.zlib = zlib.length

json.sizes.compressionRatio = json.sizes.zlib / json.sizes.legacy

json.gasUsage.legacy = calldataCost(legacy).toNumber()
json.gasUsage.zlib = calldataCost(zlib).toNumber()
json.gasUsage.compressionRatio =
json.gasUsage.zlib / json.gasUsage.legacy

batches.push(json)
}
Expand Down
18 changes: 16 additions & 2 deletions packages/core-utils/src/optimism/fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { BigNumber } from 'ethers'

import { remove0x } from '../common'

const txDataZeroGas = 4
const txDataNonZeroGasEIP2028 = 16
export const txDataZeroGas = 4
export const txDataNonZeroGasEIP2028 = 16
const big10 = BigNumber.from(10)

export const scaleDecimals = (
Expand Down Expand Up @@ -63,3 +63,17 @@ export const zeroesAndOnes = (data: Buffer | string): Array<number> => {
}
return [zeros, ones]
}

/**
* Computes the L1 calldata cost of bytes based
* on the London hardfork.
*
* @param data {Buffer|string} Bytes
* @returns {BigNumber} Gas consumed by the bytes
*/
export const calldataCost = (data: Buffer | string): BigNumber => {
const [zeros, ones] = zeroesAndOnes(data)
const zeroCost = BigNumber.from(zeros).mul(txDataZeroGas)
const nonZeroCost = BigNumber.from(ones).mul(txDataNonZeroGasEIP2028)
return zeroCost.add(nonZeroCost)
}
21 changes: 20 additions & 1 deletion packages/core-utils/test/fees.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { zeroesAndOnes } from '../src'
import './setup'

import { BigNumber } from 'ethers'

import { zeroesAndOnes, calldataCost } from '../src'

describe('Fees', () => {
it('should count zeros and ones', () => {
Expand All @@ -15,4 +19,19 @@ describe('Fees', () => {
ones.should.eq(test.ones)
}
})

it('should compute calldata costs', () => {
const cases = [
{ input: '0x', output: BigNumber.from(0) },
{ input: '0x00', output: BigNumber.from(4) },
{ input: '0xff', output: BigNumber.from(16) },
{ input: Buffer.alloc(32), output: BigNumber.from(4 * 32) },
{ input: Buffer.alloc(32, 0xff), output: BigNumber.from(16 * 32) },
]

for (const test of cases) {
const cost = calldataCost(test.input)
cost.should.deep.eq(test.output)
}
})
})