Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve OP fee estimation #196

Open
wants to merge 2 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Improve OP fee estimation
  • Loading branch information
lok52 committed Jul 22, 2023
commit bf14c391c082d8ae0b8856a2f081383d3413f302
7 changes: 6 additions & 1 deletion zp-relayer/services/fee/DynamicFeeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export class DynamicFeeManager extends FeeManager {
async _fetchFeeOptions(): Promise<DynamicFeeOptions> {
const gasPrice = await this.gasPrice.fetchOnce()
const oneByteFee = FeeManager.executionFee(gasPrice, toBN(NZERO_BYTE_GAS))
return DynamicFeeOptions.fromGasPice(gasPrice, oneByteFee, relayerConfig.minBaseFee)
return DynamicFeeOptions.fromParams({
gasPrice,
oneByteFee,
minFee: relayerConfig.minBaseFee,
baseExtra: toBN(0),
})
}
}
12 changes: 10 additions & 2 deletions zp-relayer/services/fee/FeeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,18 @@ type DynamicFeeKeys = [
'oneByteFee',
'nativeConvertFee'
]

interface DynamicFeeParams {
gasPrice: GasPriceValue
oneByteFee: BN
minFee: BN
baseExtra: BN
}

// Utility class for dynamic fee estimations
export class DynamicFeeOptions extends FeeOptions<DynamicFeeKeys> {
static fromGasPice(gasPrice: GasPriceValue, oneByteFee: BN, minFee: BN) {
const getFee = (txType: TxType) => FeeManager.executionFee(gasPrice, config.baseTxGas[txType])
static fromParams({ gasPrice, baseExtra, oneByteFee, minFee }: DynamicFeeParams) {
const getFee = (txType: TxType) => FeeManager.executionFee(gasPrice, config.baseTxGas[txType]).add(baseExtra)
const fees: Fees<DynamicFeeKeys> = {
[TxType.DEPOSIT]: getFee(TxType.DEPOSIT),
[TxType.PERMITTABLE_DEPOSIT]: getFee(TxType.PERMITTABLE_DEPOSIT),
Expand Down
42 changes: 26 additions & 16 deletions zp-relayer/services/fee/OptimismFeeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import relayerConfig from '@/configs/relayerConfig'
import { ZERO_BYTE_GAS, NZERO_BYTE_GAS } from '@/utils/constants'
import type { EstimationType, GasPrice } from '../gas-price'

// Rough estimation of tx RLP encoding overhead in bytes
const RLP_ENCODING_OVERHEAD = toBN(120)
const SIGNATURE_GAS = toBN(68 * 16)

export class OptimismFeeManager extends FeeManager {
private oracle: Contract
public oracle: Contract
private overhead!: BN
private decimals!: BN
private scalar!: BN
Expand All @@ -27,16 +31,21 @@ export class OptimismFeeManager extends FeeManager {
this.scalar = await contractCallRetry(this.oracle, 'scalar').then(toBN)
}

private getL1GasUsed(data: string): BN {
private getL1GasUsed(data: string, includeOverhead: boolean): BN {
const byteToGas = (byte: number) => (byte === 0 ? ZERO_BYTE_GAS : NZERO_BYTE_GAS)
const bytes = hexToBytes(data)
const l1GasUsed = bytes.reduce((acc, byte) => acc + byteToGas(byte), 0)
return toBN(l1GasUsed).add(this.overhead)
const total = bytes.reduce((acc, byte) => acc + byteToGas(byte), 0)
const unsigned = toBN(total)
if (includeOverhead) {
const totalOverhead = this.overhead.add(SIGNATURE_GAS).add(RLP_ENCODING_OVERHEAD)
unsigned.iadd(totalOverhead)
}
return unsigned
}

// Mimics OP gas price oracle algorithm
private getL1Fee(data: string, l1BaseFee: BN): BN {
const l1GasUsed = this.getL1GasUsed(data)
private getL1Fee(data: string, l1BaseFee: BN, includeOverhead: boolean): BN {
const l1GasUsed = this.getL1GasUsed(data, includeOverhead)
const l1Fee = l1GasUsed.mul(l1BaseFee)
const divisor = toBN(10).pow(this.decimals)
const unscaled = l1Fee.mul(this.scalar)
Expand All @@ -46,14 +55,9 @@ export class OptimismFeeManager extends FeeManager {

async _estimateFee({ txType, nativeConvert, txData }: IFeeEstimateParams, feeOptions: DynamicFeeOptions) {
const { [txType]: baseFee, nativeConvertFee, oneByteFee } = feeOptions.fees

const unscaledL1Fee = this.getL1Fee(txData, oneByteFee)

// Because oneByteFee = l1BaseFee * NZERO_BYTE_GAS, we need to divide the estimation
// We do it here to get a more accurate result
const l1Fee = unscaledL1Fee.divn(NZERO_BYTE_GAS)

const fee = baseFee.add(l1Fee)
// -1 to account for the 0x prefix
const calldataLen = (txData.length >> 1) - 1
const fee = baseFee.add(oneByteFee.muln(calldataLen))
if (nativeConvert) {
fee.iadd(nativeConvertFee)
}
Expand All @@ -65,8 +69,14 @@ export class OptimismFeeManager extends FeeManager {

const l1BaseFee = await contractCallRetry(this.oracle, 'l1BaseFee').then(toBN)

const oneByteFee = l1BaseFee.muln(NZERO_BYTE_GAS)
const oneByteFee = this.getL1Fee('0xff', l1BaseFee, false)
const baseExtra = this.getL1Fee('0x', l1BaseFee, true)

return DynamicFeeOptions.fromGasPice(gasPrice, oneByteFee, relayerConfig.minBaseFee)
return DynamicFeeOptions.fromParams({
gasPrice,
oneByteFee,
minFee: relayerConfig.minBaseFee,
baseExtra,
})
}
}
1 change: 1 addition & 0 deletions zp-relayer/test.env
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ RELAYER_SENT_TX_DELAY=2000
RELAYER_GAS_PRICE_FALLBACK=
RELAYER_GAS_PRICE_UPDATE_INTERVAL=100000
RELAYER_GAS_PRICE_ESTIMATION_TYPE="eip1559-gas-estimation"
RELAYER_FEE_MANAGER_TYPE=static
13 changes: 6 additions & 7 deletions zp-relayer/test/worker-tests/poolWorker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import flowZeroAddressWithdraw from '../flows/flow_zero-address_withdraw_2.json'
import { Params } from 'libzkbob-rs-node'
import { directDepositQueue } from '../../queue/directDepositQueue'
import { createDirectDepositWorker } from '../../workers/directDepositWorker'
import { DynamicFeeManager, FeeManager } from '../../services/fee'
import { FeeManager, StaticFeeManager } from '../../services/fee'
import { NativePriceFeed } from '../../services/price-feed'

chai.use(chaiAsPromised)
const expect = chai.expect
Expand Down Expand Up @@ -96,18 +97,16 @@ describe('poolWorker', () => {
gasPriceService = new GasPrice(web3, { gasPrice: config.gasPriceFallback }, 10000, EstimationType.Web3, {})
await gasPriceService.start()

const mockPriceFeed = {
convert: (amounts: BN[]) => Promise.resolve(amounts.map(() => toBN(0))),
}
const mockPriceFeed = new NativePriceFeed()
const managerConfig = {
gasPrice: gasPriceService,
priceFeed: mockPriceFeed,
scaleFactor: toBN(1),
marginFactor: toBN(1),
scaleFactor: toBN(100),
marginFactor: toBN(100),
updateInterval: config.feeManagerUpdateInterval,
defaultFeeOptionsParams: { gasLimit: config.relayerGasLimit },
}
feeManager = new DynamicFeeManager(managerConfig, gasPriceService)
feeManager = new StaticFeeManager(managerConfig, toBN(0))
await feeManager.start()

txManager = new TxManager(web3, config.relayerPrivateKey, gasPriceService)
Expand Down
3 changes: 1 addition & 2 deletions zp-relayer/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Constants } from 'libzkbob-rs-node'

const BASE_CALLDATA_SIZE = 644 // constant calldata size for tx without memo
const RAW_TX_RLP_OVERHEAD = 110

const constants = {
FALLBACK_RPC_URL_SWITCH_TIMEOUT: 60 * 60 * 1000,
Expand All @@ -19,7 +18,7 @@ const constants = {
ZERO_ADDRESS: '0x0000000000000000000000000000000000000000',
PERMIT2_CONTRACT: '0x000000000022D473030F116dDEE9F6B43aC78BA3',
INIT_ROOT: '11469701942666298368112882412133877458305516134926649826543144744382391691533',
MOCK_CALLDATA: '0x' + 'ff'.repeat(BASE_CALLDATA_SIZE + RAW_TX_RLP_OVERHEAD),
MOCK_CALLDATA: '0x' + 'ff'.repeat(BASE_CALLDATA_SIZE),
ZERO_BYTE_GAS: 4,
NZERO_BYTE_GAS: 16,
OP_GAS_ORACLE_ADDRESS: '0x420000000000000000000000000000000000000F',
Expand Down
4 changes: 1 addition & 3 deletions zp-relayer/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,5 @@ export function getFileHash(path: string) {
}

export function applyDenominator(n: BN, d: BN) {
return d.testn(255)
? n.div(d.maskn(255))
: n.mul(d)
return d.testn(255) ? n.div(d.maskn(255)) : n.mul(d)
}