From 32df08859661bedb8504f4b0a1c03ecb3674c5ed Mon Sep 17 00:00:00 2001 From: James Bond Date: Tue, 1 Nov 2022 16:57:01 +0100 Subject: [PATCH 1/8] support for eth_gasPrice --- packages/client/lib/rpc/modules/eth.ts | 55 +++++++ packages/client/test/rpc/eth/gasPrice.spec.ts | 155 ++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 packages/client/test/rpc/eth/gasPrice.spec.ts diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index f885dc0675..dcace8bcbf 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -333,6 +333,8 @@ export class Eth { 1, [[validators.blockOption]] ) + + this.gasPrice = middleware(this.gasPrice.bind(this), 0, []) } /** @@ -973,4 +975,57 @@ export class Eth { const block = await getBlockByOption(blockOpt, this._chain) return intToHex(block.transactions.length) } + + /** + * Gas price oracle. + * Returns a suggested gas price based on the last few blocks median gas price. + * @returns a hex code of an integer representing the current gas price in wei. + */ + async gasPrice() { + let gasPrice = BigInt(0) + const latest = await this._chain.getCanonicalHeadHeader() + if (this._vm !== undefined && this._vm._common.isActivatedEIP(1559)) { + const baseFee = latest.calcNextBaseFee() + let priorityFee = BigInt(0) + const block = await this._chain.getBlock(latest.number) + const blockTransactionsLength = block.transactions.length + if (blockTransactionsLength > 0) { + for (const tx of block.transactions) { + const maxPriorityFeePerGas = (tx as FeeMarketEIP1559Transaction).maxPriorityFeePerGas + priorityFee += maxPriorityFeePerGas + } + } + priorityFee = + priorityFee !== BigInt(0) ? priorityFee / BigInt(blockTransactionsLength) : BigInt(1) + gasPrice = baseFee + priorityFee + } else { + // For chains that don't support EIP-1559 we iterate over the last 20 + // blocks to get an average gas price. We cap the tx lookup to 500. + const blockIterations = 20 < latest.number ? 20 : latest.number + let txCount = 0 + for (let i = 0; i < blockIterations; i++) { + const block = await this._chain.getBlock(latest.number - BigInt(i)) + if (block.transactions.length === 0) { + continue + } + + for (const tx of block.transactions) { + const txGasPrice = (tx as Transaction).gasPrice + gasPrice += txGasPrice + txCount++ + if (txCount >= 500) break + } + + if (txCount >= 500) break + } + + if (txCount > 0) { + gasPrice = gasPrice / BigInt(txCount) + } else { + gasPrice = this._chain.config.chainCommon.param('gasConfig', 'minPrice') + } + } + + return bigIntToHex(gasPrice) + } } diff --git a/packages/client/test/rpc/eth/gasPrice.spec.ts b/packages/client/test/rpc/eth/gasPrice.spec.ts new file mode 100644 index 0000000000..fc0e8c6e23 --- /dev/null +++ b/packages/client/test/rpc/eth/gasPrice.spec.ts @@ -0,0 +1,155 @@ +import { FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' +import { bigIntToHex, intToHex } from '@ethereumjs/util' +import * as tape from 'tape' + +import { + baseRequest, + dummy, + gethGenesisStartLondon, + params, + runBlockWithTxs, + setupChain, +} from '../helpers' + +import pow = require('./../../testdata/geth-genesis/pow.json') + +const method = 'eth_gasPrice' + +tape(`${method}: call with legacy transaction data`, async (t) => { + const { chain, common, execution, server } = await setupChain(pow, 'pow') + + const GAS_PRICE = 100 + // construct tx + const tx = Transaction.fromTxData( + { gasLimit: 21000, gasPrice: GAS_PRICE, to: '0x0000000000000000000000000000000000000000' }, + { common } + ).sign(dummy.privKey) + + await runBlockWithTxs(chain, execution, [tx]) + + const req = params(method, []) + const expectRes = (res: any) => { + const msg = 'should return the correct suggested gas price with 1 legacy transaction' + t.equal(res.body.result, intToHex(GAS_PRICE), msg) + } + await baseRequest(t, server, req, 200, expectRes) +}) + +tape(`${method}: call with multiple legacy transactions`, async (t) => { + const { chain, common, execution, server } = await setupChain(pow, 'pow') + const iterations = BigInt(20) + let averageGasPrice = BigInt(0) + for (let i = 0; i < iterations; i++) { + const gasPrice = i * 100 + averageGasPrice += BigInt(gasPrice) + const tx = Transaction.fromTxData( + { nonce: i, gasLimit: 21000, gasPrice, to: '0x0000000000000000000000000000000000000000' }, + { common } + ).sign(dummy.privKey) + await runBlockWithTxs(chain, execution, [tx]) + } + + averageGasPrice = averageGasPrice / iterations + const req = params(method, []) + const expectRes = (res: any) => { + const msg = 'should return the correct gas price with multiple legacy transactions' + t.equal(res.body.result, bigIntToHex(averageGasPrice), msg) + } + await baseRequest(t, server, req, 200, expectRes) +}) + +tape(`${method}: call with multiple legacy transactions in a single block`, async (t) => { + const { chain, common, execution, server } = await setupChain(pow, 'pow') + + const G1 = 100 + const G2 = 1231231 + + const tx1 = Transaction.fromTxData( + { gasLimit: 21000, gasPrice: G1, to: '0x0000000000000000000000000000000000000000' }, + { common } + ).sign(dummy.privKey) + const tx2 = Transaction.fromTxData( + { nonce: 1, gasLimit: 21000, gasPrice: G2, to: '0x0000000000000000000000000000000000000000' }, + { common } + ).sign(dummy.privKey) + + await runBlockWithTxs(chain, execution, [tx1, tx2]) + + const averageGasPrice = (G1 + G2) / 2 + const req = params(method, []) + const expectRes = (res: any) => { + const msg = 'should return the correct gas price with multiple legacy transactions in a block' + t.equal(res.body.result, intToHex(Math.trunc(averageGasPrice)), msg) + } + await baseRequest(t, server, req, 200, () => expectRes) +}) + +tape(`${method}: call with 1559 transaction data`, async (t) => { + const { chain, common, execution, server } = await setupChain( + gethGenesisStartLondon(pow), + 'powLondon' + ) + + const tx = FeeMarketEIP1559Transaction.fromTxData( + { + gasLimit: 21000, + maxPriorityFeePerGas: 10, + maxFeePerGas: 975000000, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + + await runBlockWithTxs(chain, execution, [tx]) + const req = params(method, []) + const latest = await chain.getCanonicalHeadHeader() + const baseFee = latest.calcNextBaseFee() + const gasPrice = BigInt(baseFee + tx.maxPriorityFeePerGas) + + const expectRes = (res: any) => { + const msg = 'should return the correct gas price with 1 1559 transaction' + t.equal(res.body.result, bigIntToHex(gasPrice), msg) + } + await baseRequest(t, server, req, 200, expectRes) +}) + +tape(`${method}: call with multiple 1559 transactions`, async (t) => { + const { chain, common, execution, server } = await setupChain( + gethGenesisStartLondon(pow), + 'powLondon' + ) + + const maxPriority1 = 10 + const maxPriority2 = 1231231 + const tx1 = FeeMarketEIP1559Transaction.fromTxData( + { + gasLimit: 21000, + maxPriorityFeePerGas: maxPriority1, + maxFeePerGas: 975000000, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + const tx2 = FeeMarketEIP1559Transaction.fromTxData( + { + nonce: 1, + gasLimit: 21000, + maxPriorityFeePerGas: maxPriority2, + maxFeePerGas: 975000000, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + + await runBlockWithTxs(chain, execution, [tx1, tx2]) + const req = params(method, []) + const averagePriorityFee = BigInt(Math.trunc((maxPriority1 + maxPriority2) / 2)) + const latest = await chain.getCanonicalHeadHeader() + const baseFee = latest.calcNextBaseFee() + const gasPrice = BigInt(baseFee + averagePriorityFee) + const expectRes = (res: any) => { + const msg = 'should return the correct gas price with 1 1559 transaction' + t.equal(res.body.result, bigIntToHex(gasPrice), msg) + } + await baseRequest(t, server, req, 200, expectRes) +}) From 529d51017bbf8746e39ebc02d052359ff7f8a2fa Mon Sep 17 00:00:00 2001 From: James Bond Date: Tue, 1 Nov 2022 17:00:44 +0100 Subject: [PATCH 2/8] improve description --- packages/client/lib/rpc/modules/eth.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index dcace8bcbf..1ed224d25d 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -978,7 +978,8 @@ export class Eth { /** * Gas price oracle. - * Returns a suggested gas price based on the last few blocks median gas price. + * + * Returns a suggested gas price. * @returns a hex code of an integer representing the current gas price in wei. */ async gasPrice() { From c243abb7e954bb3e8d6a33e21b6eafb7cae59011 Mon Sep 17 00:00:00 2001 From: Rodrigo Herrera Itie <80117772+rodrigoherrerai@users.noreply.github.com> Date: Tue, 1 Nov 2022 17:10:02 +0100 Subject: [PATCH 3/8] Update eth.ts --- packages/client/lib/rpc/modules/eth.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index 1ed224d25d..bb8578fcd6 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -1016,8 +1016,6 @@ export class Eth { txCount++ if (txCount >= 500) break } - - if (txCount >= 500) break } if (txCount > 0) { From beb81ce9eced5fc7c15291ed56e95759a19945a6 Mon Sep 17 00:00:00 2001 From: James Bond Date: Fri, 4 Nov 2022 20:13:14 +0100 Subject: [PATCH 4/8] pr changes --- packages/client/test/rpc/eth/gasPrice.spec.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/client/test/rpc/eth/gasPrice.spec.ts b/packages/client/test/rpc/eth/gasPrice.spec.ts index fc0e8c6e23..7a6f985741 100644 --- a/packages/client/test/rpc/eth/gasPrice.spec.ts +++ b/packages/client/test/rpc/eth/gasPrice.spec.ts @@ -153,3 +153,49 @@ tape(`${method}: call with multiple 1559 transactions`, async (t) => { } await baseRequest(t, server, req, 200, expectRes) }) + +tape(`${method}: compute average gas price for 21 blocks`, async (t) => { + const { chain, common, execution, server } = await setupChain(pow, 'pow') + const iterations = BigInt(21) + const gasPrice = BigInt(20) + const firstBlockGasPrice = BigInt(11111111111111) + let tx: Transaction + for (let i = 0; i < iterations; i++) { + if (i === 0) { + tx = Transaction.fromTxData( + { + nonce: i, + gasLimit: 21000, + gasPrice: firstBlockGasPrice, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + } else { + tx = Transaction.fromTxData( + { + nonce: i, + gasLimit: 21000, + gasPrice, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + } + await runBlockWithTxs(chain, execution, [tx!]) + } + + const latest = await chain.getCanonicalHeadHeader() + const blockNumber = latest.number + + // Should be block number 21 + t.equal(blockNumber, 21n) + + const req = params(method, []) + const expectRes = (res: any) => { + const msg = 'should return the correct gas price for 21 blocks' + t.equal(res.body.result, bigIntToHex(gasPrice), msg) + } + + await baseRequest(t, server, req, 200, expectRes) +}) From e6c52b1c38e323b37626bc8a9d16407411174437 Mon Sep 17 00:00:00 2001 From: James Bond Date: Fri, 4 Nov 2022 20:19:00 +0100 Subject: [PATCH 5/8] update gas price main entry point for pr changes --- packages/client/lib/rpc/modules/eth.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index bb8578fcd6..b171dffc6b 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -989,19 +989,17 @@ export class Eth { const baseFee = latest.calcNextBaseFee() let priorityFee = BigInt(0) const block = await this._chain.getBlock(latest.number) - const blockTransactionsLength = block.transactions.length - if (blockTransactionsLength > 0) { - for (const tx of block.transactions) { - const maxPriorityFeePerGas = (tx as FeeMarketEIP1559Transaction).maxPriorityFeePerGas - priorityFee += maxPriorityFeePerGas - } + for (const tx of block.transactions) { + const maxPriorityFeePerGas = (tx as FeeMarketEIP1559Transaction).maxPriorityFeePerGas + priorityFee += maxPriorityFeePerGas } + priorityFee = - priorityFee !== BigInt(0) ? priorityFee / BigInt(blockTransactionsLength) : BigInt(1) + priorityFee !== BigInt(0) ? priorityFee / BigInt(block.transactions.length) : BigInt(1) gasPrice = baseFee + priorityFee } else { // For chains that don't support EIP-1559 we iterate over the last 20 - // blocks to get an average gas price. We cap the tx lookup to 500. + // blocks to get an average gas price. We cap the total tx lookup to 500. const blockIterations = 20 < latest.number ? 20 : latest.number let txCount = 0 for (let i = 0; i < blockIterations; i++) { @@ -1016,6 +1014,7 @@ export class Eth { txCount++ if (txCount >= 500) break } + if (txCount >= 500) break } if (txCount > 0) { From b8710c0a620232e3fee4df506ff3cc11bf552a53 Mon Sep 17 00:00:00 2001 From: James Bond Date: Sat, 5 Nov 2022 19:06:54 +0100 Subject: [PATCH 6/8] removing tx lookup --- packages/client/lib/rpc/modules/eth.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index b171dffc6b..579dc75675 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -980,7 +980,7 @@ export class Eth { * Gas price oracle. * * Returns a suggested gas price. - * @returns a hex code of an integer representing the current gas price in wei. + * @returns a hex code of an integer representing the suggested gas price in wei. */ async gasPrice() { let gasPrice = BigInt(0) @@ -999,7 +999,7 @@ export class Eth { gasPrice = baseFee + priorityFee } else { // For chains that don't support EIP-1559 we iterate over the last 20 - // blocks to get an average gas price. We cap the total tx lookup to 500. + // blocks to get an average gas price. const blockIterations = 20 < latest.number ? 20 : latest.number let txCount = 0 for (let i = 0; i < blockIterations; i++) { @@ -1012,9 +1012,7 @@ export class Eth { const txGasPrice = (tx as Transaction).gasPrice gasPrice += txGasPrice txCount++ - if (txCount >= 500) break } - if (txCount >= 500) break } if (txCount > 0) { From db416feb78105a1b57e5e7aedde2d42fb8c05cc9 Mon Sep 17 00:00:00 2001 From: James Bond Date: Tue, 8 Nov 2022 19:14:47 +0100 Subject: [PATCH 7/8] adding minGasPrice config for 1559 chains --- package-lock.json | 10 +++++----- packages/client/lib/rpc/modules/eth.ts | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index ac8517f687..52e668a0a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17820,7 +17820,7 @@ "@ethereumjs/common": "3.0.1", "@ethereumjs/devp2p": "5.0.1", "@ethereumjs/ethash": "2.0.1", - "@ethereumjs/evm": "1.2.1", + "@ethereumjs/evm": "1.2.2", "@ethereumjs/rlp": "4.0.0", "@ethereumjs/statemanager": "1.0.1", "@ethereumjs/trie": "5.0.1", @@ -18065,7 +18065,7 @@ }, "packages/evm": { "name": "@ethereumjs/evm", - "version": "1.2.1", + "version": "1.2.2", "license": "MPL-2.0", "dependencies": { "@ethereumjs/common": "^3.0.1", @@ -18225,7 +18225,7 @@ "@ethereumjs/block": "^4.0.1", "@ethereumjs/blockchain": "^6.0.2", "@ethereumjs/common": "^3.0.1", - "@ethereumjs/evm": "^1.2.1", + "@ethereumjs/evm": "^1.2.2", "@ethereumjs/rlp": "^4.0.0", "@ethereumjs/statemanager": "^1.0.1", "@ethereumjs/trie": "^5.0.1", @@ -19480,7 +19480,7 @@ "@ethereumjs/common": "3.0.1", "@ethereumjs/devp2p": "5.0.1", "@ethereumjs/ethash": "2.0.1", - "@ethereumjs/evm": "1.2.1", + "@ethereumjs/evm": "1.2.2", "@ethereumjs/rlp": "4.0.0", "@ethereumjs/statemanager": "1.0.1", "@ethereumjs/trie": "5.0.1", @@ -19780,7 +19780,7 @@ "@ethereumjs/block": "^4.0.1", "@ethereumjs/blockchain": "^6.0.2", "@ethereumjs/common": "^3.0.1", - "@ethereumjs/evm": "^1.2.1", + "@ethereumjs/evm": "^1.2.2", "@ethereumjs/rlp": "^4.0.0", "@ethereumjs/statemanager": "^1.0.1", "@ethereumjs/trie": "^5.0.1", diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index 579dc75675..f607f01167 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -983,6 +983,7 @@ export class Eth { * @returns a hex code of an integer representing the suggested gas price in wei. */ async gasPrice() { + const minGasPrice: bigint = this._chain.config.chainCommon.param('gasConfig', 'minPrice') let gasPrice = BigInt(0) const latest = await this._chain.getCanonicalHeadHeader() if (this._vm !== undefined && this._vm._common.isActivatedEIP(1559)) { @@ -996,12 +997,12 @@ export class Eth { priorityFee = priorityFee !== BigInt(0) ? priorityFee / BigInt(block.transactions.length) : BigInt(1) - gasPrice = baseFee + priorityFee + gasPrice = baseFee + priorityFee > minGasPrice ? baseFee + priorityFee : minGasPrice } else { // For chains that don't support EIP-1559 we iterate over the last 20 // blocks to get an average gas price. const blockIterations = 20 < latest.number ? 20 : latest.number - let txCount = 0 + let txCount = BigInt(0) for (let i = 0; i < blockIterations; i++) { const block = await this._chain.getBlock(latest.number - BigInt(i)) if (block.transactions.length === 0) { @@ -1016,9 +1017,9 @@ export class Eth { } if (txCount > 0) { - gasPrice = gasPrice / BigInt(txCount) + gasPrice = gasPrice / txCount > minGasPrice ? gasPrice / txCount : minGasPrice } else { - gasPrice = this._chain.config.chainCommon.param('gasConfig', 'minPrice') + gasPrice = minGasPrice } } From 038f7b4be006fbadba530314117acbab70190581 Mon Sep 17 00:00:00 2001 From: Rodrigo Herrera Itie <80117772+rodrigoherrerai@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:57:27 +0100 Subject: [PATCH 8/8] Update packages/client/lib/rpc/modules/eth.ts Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com> --- packages/client/lib/rpc/modules/eth.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index f607f01167..91e6e63663 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -1017,7 +1017,8 @@ export class Eth { } if (txCount > 0) { - gasPrice = gasPrice / txCount > minGasPrice ? gasPrice / txCount : minGasPrice + const avgGasPrice = gasPrice / txCount + gasPrice = avgGasPrice > minGasPrice ? avgGasPrice : minGasPrice } else { gasPrice = minGasPrice }