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

✨ Feat: Add maxPriorityFeePerGas and maxFeePerGas #1198

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
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

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/actions/docs/functions/validateBaseCallParams.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions packages/actions/docs/type-aliases/BaseCallParams.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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
Comment on lines +75 to +84
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure consistency in the description of new parameters.

The descriptions for maxFeePerGas and maxPriorityFeePerGas are somewhat redundant. Consider simplifying to improve clarity and reduce repetition.

- 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.
+ The maximum fee per gas for the EIP-1559 tx, representing the total amount of ether that can be spent on gas.
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* 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
/**
* The maximum fee per gas for the EIP-1559 tx, representing the total amount of ether that can be spent on gas.
*/
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
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ Version: 1.1.0.next-73],
[InvalidBlobVersionedHashesError: value must be a hex string

Docs: https://tevm.sh/reference/tevm/errors/classes/invalidblobversionedhasheserror/
Version: 1.1.0.next-73],
[InvalidMaxFeePerGasError: Expected bigint, received string

Docs: https://tevm.sh/reference/tevm/errors/classes/invalidmaxfeepergaserror/
Version: 1.1.0.next-73],
[InvalidMaxPriorityFeePerGasError: Expected bigint, received string

Docs: https://tevm.sh/reference/tevm/errors/classes/invalidmaxpriorityfeepergaserror/
Version: 1.1.0.next-73],
]
`;
20 changes: 19 additions & 1 deletion packages/actions/src/BaseCall/validateBaseCallParams.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import {
InvalidParamsError,
InvalidSkipBalanceError,
} from '@tevm/errors'
import { InvalidSelfdestructError, InvalidToError, InvalidValueError } from '@tevm/errors'
import {
InvalidSelfdestructError,
InvalidToError,
InvalidValueError,
InvalidMaxPriorityFeePerGasError,
InvalidMaxFeePerGasError,
} from '@tevm/errors'
import { zBaseCallParams } from './zBaseCallParams.js'

// TODO we are missing some validation including stateOverrides
Expand Down Expand Up @@ -124,6 +130,18 @@ export const validateBaseCallParams = (action) => {
if (errors.length === 0 && parsedParams.success === false) {
errors.push(new InvalidParamsError(parsedParams.error.message))
}

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 @@ -12,6 +12,8 @@ import {
InvalidSkipBalanceError,
InvalidToError,
InvalidValueError,
InvalidMaxFeePerGasError,
InvalidMaxPriorityFeePerGasError,
} from '@tevm/errors'
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 work for invalid blobVersionedHashes', () => {
Expand Down Expand Up @@ -191,6 +197,8 @@ test('should return errors for invalid parameters', () => {
expect(errors.find((e) => e instanceof InvalidToError)).toBeDefined()
expect(errors.find((e) => e instanceof InvalidDepthError)).toBeDefined()
expect(errors.find((e) => e instanceof InvalidBlobVersionedHashesError)).toBeDefined()
expect(errors.find((e) => e instanceof InvalidMaxFeePerGasError)).toBeDefined()
expect(errors.find((e) => e instanceof InvalidMaxPriorityFeePerGasError)).toBeDefined()
})

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 @@ -49,5 +49,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": 180082n,
"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": 3786n,
"gas": 29974274n,
"logs": [],
"rawData": "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000184461692f696e73756666696369656e742d62616c616e63650000000000000000",
"rawData": "0xfb8f41b2000000000000000000000000232323232323232323232323232323232323232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
"selfdestruct": Set {},
"totalGasSpent": 24694n,
"totalGasSpent": 25726n,
}
`;
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
Loading