Skip to content

Commit

Permalink
✨ Feat: Add maxPriorityFeePerGas and maxFeePerGas
Browse files Browse the repository at this point in the history
  • Loading branch information
William Cory authored and William Cory committed Jun 13, 2024
1 parent 36a03a4 commit 04ac982
Show file tree
Hide file tree
Showing 17 changed files with 337 additions and 317 deletions.
7 changes: 7 additions & 0 deletions .changeset/big-horses-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@tevm/actions": minor
"@tevm/tx": minor
"@tevm/vm": minor
---

Added support for setting maxFeePerGas and maxPriorityFeePerGas
5 changes: 5 additions & 0 deletions .changeset/long-islands-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tevm/actions": minor
---

Added support for passing in maxFeePerGas and maxPriorityFeePerGas to tevmCall tevmContract tevmDeploy and tevmScript
12 changes: 12 additions & 0 deletions packages/actions/src/BaseCall/BaseCallParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,20 @@ export type BaseCallParams<TThrowOnFail extends boolean = boolean> = BaseParams<
readonly gas?: bigint
/**
* The gas price for the call.
* Note atm because only EIP-1559 tx transactions are created using the `maxFeePerGas` and `maxPriorityFeePerGas` options
* this option will be ignored when creating transactions. This will be fixed in a future release
*/
readonly gasPrice?: bigint
/**
* The maximum fee per gas for the EIP-1559 tx. This is the maximum amount of ether that can be spent on gas
* for the call. This is the maximum amount of ether that can be spent on gas for the call.
* This is the maximum amount of ether that can be spent on gas for the call.
*/
readonly maxFeePerGas?: bigint
/**
* The maximum priority fee per gas for the EIP-1559 tx.
*/
readonly maxPriorityFeePerGas?: bigint
/**
* Low level control
* Refund counter. Defaults to `0`
Expand Down
14 changes: 14 additions & 0 deletions packages/actions/src/BaseCall/validateBaseCallParams.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
InvalidSkipBalanceError,
} from '@tevm/errors'
import { InvalidSelfdestructError, InvalidToError, InvalidValueError } from '@tevm/errors'
import { InvalidMaxFeePerGasError } from '../../../errors/types/input/InvalidMaxFeePerGaserror.js'
import { InvalidMaxPriorityFeePerGasError } from '../../../errors/types/input/InvalidMaxPriorityFeePerGas.js'
import { zBaseCallParams } from './zBaseCallParams.js'

/**
Expand Down Expand Up @@ -109,6 +111,18 @@ export const validateBaseCallParams = (action) => {
errors.push(new InvalidBlobVersionedHashesError(error))
})
}

if (formattedErrors.maxFeePerGas) {
formattedErrors.maxFeePerGas._errors.forEach((error) => {
errors.push(new InvalidMaxFeePerGasError(error))
})
}

if (formattedErrors.maxPriorityFeePerGas) {
formattedErrors.maxPriorityFeePerGas._errors.forEach((error) => {
errors.push(new InvalidMaxPriorityFeePerGasError(error))
})
}
}

return errors
Expand Down
8 changes: 8 additions & 0 deletions packages/actions/src/BaseCall/validateBaseCallParams.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
InvalidToError,
InvalidValueError,
} from '@tevm/errors'
import { InvalidMaxFeePerGasError } from '../../../errors/types/input/InvalidMaxFeePerGaserror.js'
import { InvalidMaxPriorityFeePerGasError } from '../../../errors/types/input/InvalidMaxPriorityFeePerGas.js'
import type { BaseCallParams } from './BaseCallParams.js'
import { validateBaseCallParams } from './validateBaseCallParams.js'

Expand Down Expand Up @@ -98,6 +100,7 @@ const validParamsCases: Array<BaseCallParams> = [
selfdestruct: new Set(['0x1111111111111111111111111111111111111111']),
to: '0x1111111111111111111111111111111111111111',
blobVersionedHashes: ['0x1111111111111111111111111111111111111111'],
maxFeePerGas: 5000000000n,
},
{
createTrace: true,
Expand Down Expand Up @@ -134,6 +137,7 @@ const validParamsCases: Array<BaseCallParams> = [
selfdestruct: new Set(['0x3333333333333333333333333333333333333333']),
to: '0x3333333333333333333333333333333333333333',
blobVersionedHashes: ['0x3333333333333333333333333333333333333333'],
maxPriorityFeePerGas: 7000000000n,
},
] as const

Expand All @@ -156,6 +160,8 @@ const mockInvalidParams = {
selfdestruct: 'invalid', // should be a boolean
to: 12345, // should be a string
blobVersionedHashes: ['invalid hash'], // should be a valid hash
maxFeePerGas: 'not a number', // should be a number
maxPriorityFeePerGas: 'not a number', // should be a number
}

test('should return errors for invalid parameters', () => {
Expand All @@ -173,6 +179,8 @@ test('should return errors for invalid parameters', () => {
expect(errors).toContainEqual(expect.any(InvalidToError))
expect(errors).toContainEqual(expect.any(InvalidBlobVersionedHashesError))
expect(errors).toContainEqual(expect.any(InvalidDepthError))
expect(errors).toContainEqual(expect.any(InvalidMaxPriorityFeePerGasError))
expect(errors).toContainEqual(expect.any(InvalidMaxFeePerGasError))
})

test('should validate if top level is wrong', () => {
Expand Down
10 changes: 10 additions & 0 deletions packages/actions/src/BaseCall/zBaseCallParams.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,15 @@ export const zBaseCallParams = zBaseParams
blobVersionedHashes: z.array(zHex).optional().describe('Versioned hashes for each blob in a blob transaction'),
stateOverrideSet: zStateOverrideSet.optional().describe('State override set for the call'),
blockOverrideSet: zBlockOverrideSet.optional().describe('Block override set for the call'),
maxFeePerGas: z
.bigint()
.optional()
.describe(
'The maximum fee per gas for the call for an EIP-1559 tx. If not set it will be calculated based on the parent block.',
),
maxPriorityFeePerGas: z
.bigint()
.optional()
.describe('The maximum priority fee per gas for the call for an EIP-1559 tx.'),
})
.describe('Properties shared across call-like actions')
2 changes: 2 additions & 0 deletions packages/actions/src/BaseCall/zBaseCallParams.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ test('zBaseCallParams', () => {
caller: `0x${'69'.repeat(20)}`,
skipBalance: true,
createTransaction: false,
maxFeePerGas: 0x420n,
maxPriorityFeePerGas: 0x420n,
} as const satisfies z.infer<typeof zBaseCallParams>
expect(zBaseCallParams.parse(callParams)).toEqual(callParams)
})
10 changes: 5 additions & 5 deletions packages/actions/src/Call/__snapshots__/callHandler.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`callHandler should handle errors returned during contract call 1`] = `
{
"amountSpent": 172858n,
"amountSpent": 154763n,
"createdAddresses": Set {},
"errors": [
[RevertError: revert
Expand All @@ -11,11 +11,11 @@ Docs: https://tevm.sh/reference/tevm/errors/classes/reverterror/
Details: {"error":"revert","errorType":"EvmError"}
Version: 1.1.0.next-73],
],
"executionGasUsed": 2754n,
"gas": 29975306n,
"executionGasUsed": 169n,
"gas": 29977891n,
"logs": [],
"rawData": "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000184461692f696e73756666696369656e742d62616c616e63650000000000000000",
"rawData": "0x",
"selfdestruct": Set {},
"totalGasSpent": 24694n,
"totalGasSpent": 22109n,
}
`;
37 changes: 20 additions & 17 deletions packages/actions/src/Call/callHandler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { InternalError } from '@tevm/errors'
import { InternalError, InvalidGasPriceError } from '@tevm/errors'
import { EthjsAccount, EthjsAddress, bytesToBigint, bytesToHex } from '@tevm/utils'
import { runTx } from '@tevm/vm'
import { numberToBytes } from 'viem'
Expand Down Expand Up @@ -206,6 +206,10 @@ export const callHandler =
trace = await runCallWithTrace(vm, client.logger, evmInput, true).then(({ trace }) => trace)
}
try {
const tx = await evmInputToImpersonatedTx({
...client,
getVm: () => Promise.resolve(vm),
})(evmInput, params.maxFeePerGas, params.maxPriorityFeePerGas)
evmOutput = await runTx(vm)({
reportAccessList: params.createAccessList ?? false,
reportPreimages: params.createAccessList ?? false,
Expand All @@ -215,24 +219,15 @@ export const callHandler =
skipNonce: true,
skipBalance: evmInput.skipBalance ?? false,
...(evmInput.block !== undefined ? { block: /** @type any*/ (evmInput.block) } : {}),
tx: await evmInputToImpersonatedTx({
...client,
getVm: () => Promise.resolve(vm),
})(evmInput),
tx,
})
} catch (e) {
if (!(e instanceof Error)) {
throw e
}
if (e.message.includes("is less than the block's baseFeePerGas")) {
return maybeThrowOnFail(params.throwOnFail ?? defaultThrowOnFail, {
errors: [
{
name: 'GasPriceTooLow',
_tag: 'GasPriceTooLow',
message: e.message,
},
],
errors: [new InvalidGasPriceError('Tx aborted because gasPrice or maxFeePerGas is too low', { cause: e })],
executionGasUsed: 0n,
rawData: '0x',
...(vm.common.sourceId !== undefined ? await l1FeeInfoPromise : {}),
Expand Down Expand Up @@ -315,6 +310,11 @@ export const callHandler =
},
]
try {
// TODO we should handle not impersonating real tx with real nonces
const tx = await evmInputToImpersonatedTx({
...client,
getVm: () => Promise.resolve(vm),
})(evmInput, params.maxFeePerGas, params.maxPriorityFeePerGas)
// user might have tracing turned on hoping to trace why it's using too much gas
/// calculate skipping all validation but still return errors too
evmOutput = await runTx(vm)({
Expand All @@ -326,10 +326,7 @@ export const callHandler =
skipNonce: true,
skipBalance: true,
...(evmInput.block !== undefined ? { block: /** @type any*/ (evmInput.block) } : {}),
tx: await evmInputToImpersonatedTx({
...client,
getVm: () => Promise.resolve(vm),
})(evmInput),
tx,
})
if (trace) {
trace.gas = evmOutput.execResult.executionGasUsed
Expand Down Expand Up @@ -446,7 +443,13 @@ export const callHandler =
...callHandlerResult(evmOutput, undefined, trace, accessList),
})
}
const txRes = await createTransaction(client)({ throwOnFail: false, evmOutput, evmInput })
const txRes = await createTransaction(client)({
throwOnFail: false,
evmOutput,
evmInput,
maxPriorityFeePerGas: params.maxPriorityFeePerGas,
maxFeePerGas: params.maxFeePerGas,
})
txHash = 'txHash' in txRes ? txRes.txHash : undefined
if ('errors' in txRes && txRes.errors.length) {
return /** @type {any}*/ (
Expand Down
Loading

0 comments on commit 04ac982

Please sign in to comment.