diff --git a/src/chains/ethereum/ethereum/src/api.ts b/src/chains/ethereum/ethereum/src/api.ts index d1a44f82a6..aa31aec94b 100644 --- a/src/chains/ethereum/ethereum/src/api.ts +++ b/src/chains/ethereum/ethereum/src/api.ts @@ -909,9 +909,6 @@ export default class EthereumApi implements Api { blockNumber: QUANTITY | Ethereum.Tag = Tag.latest ): Promise { const blockchain = this.#blockchain; - const blocks = blockchain.blocks; - const parentBlock = await blocks.get(blockNumber); - const parentHeader = parentBlock.header; const options = this.#options; const generateVM = async () => { @@ -924,38 +921,26 @@ export default class EthereumApi implements Api { ); return vm; }; + const { coinbase } = blockchain; + const tx = TransactionFactory.fromRpc( + transaction as Transaction, + blockchain.common + ); + if (tx.from == null) { + tx.from = coinbase; + } + if (tx.gas.isNull()) { + // eth_estimateGas isn't subject to regular transaction gas limits + tx.gas = options.miner.callGasLimit; + } + const block = await blockchain.gasEstimateBlock(tx, blockNumber); + const runArgs: EstimateGasRunArgs = { + tx: tx.toVmTransaction(), + block, + skipBalance: true, + skipNonce: true + }; return new Promise((resolve, reject) => { - const { coinbase } = blockchain; - const tx = TransactionFactory.fromRpc( - transaction as Transaction, - blockchain.common - ); - if (tx.from == null) { - tx.from = coinbase; - } - if (tx.gas.isNull()) { - // eth_estimateGas isn't subject to regular transaction gas limits - tx.gas = options.miner.callGasLimit; - } - - const block = new RuntimeBlock( - Quantity.from((parentHeader.number.toBigInt() || 0n) + 1n), - parentHeader.parentHash, - new Address(parentHeader.miner.toBuffer()), - tx.gas, - parentHeader.gasUsed, - parentHeader.timestamp, - options.miner.difficulty, - parentHeader.totalDifficulty, - blockchain.getMixHash(parentHeader.parentHash.toBuffer()), - 0n // no baseFeePerGas for estimates - ); - const runArgs: EstimateGasRunArgs = { - tx: tx.toVmTransaction(), - block, - skipBalance: true, - skipNonce: true - }; estimateGas( generateVM, runArgs, @@ -1274,9 +1259,7 @@ export default class EthereumApi implements Api { @assertArgLength(1) async eth_getBlockTransactionCountByHash(hash: DATA) { const { blocks } = this.#blockchain; - const block = await blocks - .getByHash(hash) - .catch(_ => null); + const block = await blocks.getByHash(hash).catch(_ => null); if (!block) return null; const transactions = block.getTransactions(); return Quantity.from(transactions.length); diff --git a/src/chains/ethereum/ethereum/src/blockchain.ts b/src/chains/ethereum/ethereum/src/blockchain.ts index 191fd2c0e3..ffc4a46073 100644 --- a/src/chains/ethereum/ethereum/src/blockchain.ts +++ b/src/chains/ethereum/ethereum/src/blockchain.ts @@ -16,7 +16,9 @@ import { StructLog, TraceTransactionOptions, EthereumRawAccount, - TraceTransactionResult + TraceTransactionResult, + QUANTITY, + Tag } from "@ganache/ethereum-utils"; import type { InterpreterStep } from "@ethereumjs/evm"; import { decode } from "@ganache/rlp"; @@ -49,7 +51,10 @@ import { calculateIntrinsicGas, InternalTransactionReceipt, VmTransaction, - TypedTransaction + TypedTransaction, + LegacyTransaction, + EIP2930AccessListTransaction, + EIP1559FeeMarketTransaction } from "@ganache/ethereum-transaction"; import { Block, RuntimeBlock, Snapshots } from "@ganache/ethereum-block"; import { @@ -77,6 +82,7 @@ import { dumpTrieStorageDetails } from "./helpers/storage-range-at"; import { GanacheStateManager } from "./state-manager"; import { TrieDB } from "./trie-db"; import { Trie } from "@ethereumjs/trie"; +import { Ethereum } from "./api-types"; const mclInitPromise = mcl.init(mcl.BLS12_381).then(() => { mcl.setMapToMode(mcl.IRTF); // set the right map mode; otherwise mapToG2 will return wrong values. @@ -577,6 +583,40 @@ export default class Blockchain extends Emittery { coinbase: Address; + gasEstimateBlock = async ( + tx: + | LegacyTransaction + | EIP2930AccessListTransaction + | EIP1559FeeMarketTransaction, + blockNumber: QUANTITY | Ethereum.Tag = Tag.latest + ) => { + const previousBlock = await this.blocks.get(blockNumber); + const previousHeader = previousBlock.header; + const options = this.#options; + + let timestamp = previousHeader.timestamp; + if (blockNumber === "latest") + timestamp = Quantity.from(this.#adjustedTime(previousHeader.timestamp)); + + let gas = tx.gas; + if (gas.isNull()) + // eth_estimateGas isn't subject to regular transaction gas limits + gas = options.miner.callGasLimit; + + return new RuntimeBlock( + Quantity.from((previousHeader.number.toBigInt() || 0n) + 1n), + previousHeader.parentHash, + new Address(previousHeader.miner.toBuffer()), + gas, + previousHeader.gasUsed, + timestamp, + this.isPostMerge ? Quantity.Zero : options.miner.difficulty, + previousHeader.totalDifficulty, + this.getMixHash(previousHeader.parentHash.toBuffer()), + 0n // no baseFeePerGas for estimates + ); + }; + #readyNextBlock = (previousBlock: Block, timestamp?: number) => { const previousHeader = previousBlock.header; const previousNumber = previousHeader.number.toBigInt() || 0n;