diff --git a/.changeset/forty-mugs-exist.md b/.changeset/forty-mugs-exist.md new file mode 100644 index 0000000000..0ca31ddf29 --- /dev/null +++ b/.changeset/forty-mugs-exist.md @@ -0,0 +1,5 @@ +--- +"@tevm/procedures": patch +--- + +Fixed bug with eth_estimateGas json-rpc not handling block tag and state overrides correctly diff --git a/packages/memory-client/src/test/viem/estimateGas.spec.ts b/packages/memory-client/src/test/viem/estimateGas.spec.ts index 2dad107c81..94e739bfdb 100644 --- a/packages/memory-client/src/test/viem/estimateGas.spec.ts +++ b/packages/memory-client/src/test/viem/estimateGas.spec.ts @@ -31,6 +31,13 @@ beforeEach(async () => { describe('estimateGas', () => { it('should work', async () => { + expect( + await mc.estimateGas({ + to: c.simpleContract.address, + data: encodeFunctionData(c.simpleContract.write.set(69n)), + blockTag: 'latest', + }), + ).toBe(27_784n) expect( await mc.estimateGas({ to: c.simpleContract.address, diff --git a/packages/procedures/src/eth/EthJsonRpcRequest.ts b/packages/procedures/src/eth/EthJsonRpcRequest.ts index ecf65336e5..15ff23175a 100644 --- a/packages/procedures/src/eth/EthJsonRpcRequest.ts +++ b/packages/procedures/src/eth/EthJsonRpcRequest.ts @@ -70,7 +70,15 @@ export type EthCoinbaseJsonRpcRequest = JsonRpcRequest<'eth_coinbase', readonly /** * JSON-RPC request for `eth_estimateGas` procedure */ -export type EthEstimateGasJsonRpcRequest = JsonRpcRequest<'eth_estimateGas', readonly [tx: JsonRpcTransaction]> +export type EthEstimateGasJsonRpcRequest = JsonRpcRequest< + 'eth_estimateGas', + readonly [ + tx: JsonRpcTransaction, + tag?: BlockTag | Hex, + stateOverrideSet?: SerializeToJson, + blockOverrideSet?: SerializeToJson, + ] +> // eth_hashrate /** * JSON-RPC request for `eth_hashrate` procedure diff --git a/packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap b/packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap index 05e781fa6a..cc0104ff43 100644 --- a/packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap +++ b/packages/procedures/src/eth/__snapshots__/ethEstimateGasProcedure.spec.ts.snap @@ -2,6 +2,8 @@ exports[`ethEstimateGasJsonRpcProcedure > should estimate gas successfully 1`] = `"0x5208"`; +exports[`ethEstimateGasJsonRpcProcedure > should handle block tag 1`] = `"0x5208"`; + exports[`ethEstimateGasJsonRpcProcedure > should handle errors from callProcedure 1`] = ` { "code": -32602, diff --git a/packages/procedures/src/eth/ethEstimateGasProcedure.js b/packages/procedures/src/eth/ethEstimateGasProcedure.js index 6fecc0189e..166dcff415 100644 --- a/packages/procedures/src/eth/ethEstimateGasProcedure.js +++ b/packages/procedures/src/eth/ethEstimateGasProcedure.js @@ -8,9 +8,30 @@ import { callProcedure } from '../call/callProcedure.js' export const ethEstimateGasJsonRpcProcedure = (client) => { return async (request) => { const estimateGasRequest = /** @type {import('./EthJsonRpcRequest.js').EthEstimateGasJsonRpcRequest}*/ (request) + const [_params, blockTag, stateOverrides, blockOverrides] = estimateGasRequest.params + + const getParams = () => { + /** + * @type {import('../call/CallJsonRpcRequest.js').CallJsonRpcRequest['params']} + */ + const params = [ + { + ..._params, + ...(blockTag !== undefined ? { blockTag } : {}), + }, + ] + if (blockOverrides !== undefined) { + params.push(stateOverrides ?? {}, blockOverrides) + } + if (stateOverrides !== undefined) { + params.push(...params, stateOverrides) + } + return params + } + const callResult = await callProcedure(client)({ ...estimateGasRequest, - params: [...estimateGasRequest.params], + params: getParams(), method: 'tevm_call', }) if (callResult.error || !callResult.result) { diff --git a/packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts b/packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts index b796b9f430..ba0b57246f 100644 --- a/packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts +++ b/packages/procedures/src/eth/ethEstimateGasProcedure.spec.ts @@ -1,4 +1,5 @@ import { type TevmNode, createTevmNode } from '@tevm/node' +import { numberToHex } from '@tevm/utils' import { beforeEach, describe, expect, it } from 'vitest' import type { EthEstimateGasJsonRpcRequest } from './EthJsonRpcRequest.js' import { ethEstimateGasJsonRpcProcedure } from './ethEstimateGasProcedure.js' @@ -31,6 +32,29 @@ describe('ethEstimateGasJsonRpcProcedure', () => { expect(response.id).toBe(request.id as any) expect(response.result).toMatchSnapshot() }) + it('should handle block tag', async () => { + const latestBlock = await client.getVm().then((vm) => vm.blockchain.getCanonicalHeadBlock()) + const request: EthEstimateGasJsonRpcRequest = { + jsonrpc: '2.0', + method: 'eth_estimateGas', + id: 1, + params: [ + { + from: '0x0000000000000000000000000000000000000000', + to: '0x0000000000000000000000000000000000000000', + data: '0x', + }, + numberToHex(latestBlock.header.number), + ], + } + + const response = await ethEstimateGasJsonRpcProcedure(client)(request) + expect(response.error).toBeUndefined() + expect(response.result).toBeDefined() + expect(response.method).toBe('eth_estimateGas') + expect(response.id).toBe(request.id as any) + expect(response.result).toMatchSnapshot() + }) it('should handle errors from callProcedure', async () => { const request: EthEstimateGasJsonRpcRequest = {