diff --git a/CHANGELOG.md b/CHANGELOG.md index 49b3c436df3..4d872bf670e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -407,6 +407,7 @@ Released with 1.0.0-beta.37 code base. ### Added - London transaction support (#4155) +- RPC support `eth_feehistory` call ### Changed - Grammar fix (#4088) and updated Swarm (#4151)and Whisper doc links (#4170) diff --git a/docs/web3-eth.rst b/docs/web3-eth.rst index 107b172e721..64fb7e0412c 100644 --- a/docs/web3-eth.rst +++ b/docs/web3-eth.rst @@ -716,6 +716,43 @@ Example ------------------------------------------------------------------------------ +.. _eth-feehistory: + + +getFeeHistory +===================== + +.. code-block:: javascript + + web3.eth.getFeeHistory(blockCount, newestBlock, rewardPercentiles, [callback]) + +Transaction fee history +Returns base fee per gas and transaction effective priority fee per gas history for the requested block range if available. +The range between headBlock-4 and headBlock is guaranteed to be available while retrieving data from the pending block and older +history are optional to support. For pre-EIP-1559 blocks the gas prices are returned as rewards and zeroes are returned for the base fee per gas. + +---------- +Parameters +---------- + +1. ``String|Number|BN|BigNumber`` - Number of blocks in the requested range. Between 1 and 1024 blocks can be requested in a single query. Less than requested may be returned if not all blocks are available. +2. ``String|Number|BN|BigNumber`` - Highest number block of the requested range. +3. ``Array of numbers`` - A monotonically increasing list of percentile values to sample from each block's effective priority fees per gas in ascending order, weighted by gas used. Example: `["0", "25", "50", "75", "100"]` or `["0", "0.5", "1", "1.5", "3", "80"]` +4. ``Function`` - (optional) Optional callback, returns an error object as first parameter and the result as second. + +------- +Returns +------- + +``Promise`` returns ``Object`` - Fee history for the returned block range. The object: + +- ``Number`` oldestBlock - Lowest number block of the returned range. +- ``Array of strings`` baseFeePerGas - An array of block base fees per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-EIP-1559 blocks. +- ``Array of numbers`` gasUsedRatio - An array of block gas used ratios. These are calculated as the ratio of gasUsed and gasLimit. +- ``Array of string arrays`` reward - An array of effective priority fee per gas data points from a single block. All zeroes are returned if the block is empty. + +------------------------------------------------------------------------------ + getAccounts ===================== diff --git a/packages/web3-eth/src/index.js b/packages/web3-eth/src/index.js index 33c9a93e20a..f78e1f5b915 100644 --- a/packages/web3-eth/src/index.js +++ b/packages/web3-eth/src/index.js @@ -385,6 +385,12 @@ var Eth = function Eth() { params: 0, outputFormatter: formatter.outputBigNumberFormatter }), + new Method({ + name: 'getFeeHistory', + call: 'eth_feeHistory', + params: 3, + inputFormatter: [utils.toNumber, utils.toHex, function(value) {return value}] + }), new Method({ name: 'getAccounts', call: 'eth_accounts', diff --git a/packages/web3-eth/types/index.d.ts b/packages/web3-eth/types/index.d.ts index c018c2dbcac..ef5d86c0d0d 100644 --- a/packages/web3-eth/types/index.d.ts +++ b/packages/web3-eth/types/index.d.ts @@ -149,6 +149,13 @@ export class Eth { callback?: (error: Error, gasPrice: string) => void ): Promise; + getFeeHistory( + blockCount: number | BigNumber | BN | string, + lastBlock: number | BigNumber | BN | string, + rewardPercentiles: number[], + callback?: (error: Error, feeHistory: FeeHistoryResult) => void + ): Promise; + getAccounts( callback?: (error: Error, accounts: string[]) => void ): Promise; @@ -439,3 +446,10 @@ export interface StorageProof { value: string; proof: string[]; } + +export interface FeeHistoryResult { + baseFeePerGas: string[]; + gasUsedRatio: number[]; + oldestBlock: number; + reward: string[][]; +} diff --git a/packages/web3-eth/types/tests/eth.tests.ts b/packages/web3-eth/types/tests/eth.tests.ts index 43475e0208c..818926ca18e 100644 --- a/packages/web3-eth/types/tests/eth.tests.ts +++ b/packages/web3-eth/types/tests/eth.tests.ts @@ -32,7 +32,8 @@ import { TransactionConfig, hardfork, Common, - chain + chain, + FeeHistoryResult } from 'web3-eth'; import BN = require('bn.js'); import BigNumber from 'bignumber.js'; @@ -178,6 +179,15 @@ eth.getGasPrice(); // $ExpectType Promise eth.getGasPrice((error: Error, gasPrice: string) => {}); +// $ExpectType Promise +eth.getFeeHistory( + 4, "0xA30953", [] +); +// $ExpectType Promise +eth.getFeeHistory( + 4, "0xA30953", [], + (error: Error, feeHistory: FeeHistoryResult) => {}); + // $ExpectType Promise eth.getAccounts(); // $ExpectType Promise diff --git a/packages/web3-utils/src/index.js b/packages/web3-utils/src/index.js index 1eec4b76c3d..baf01e768fb 100644 --- a/packages/web3-utils/src/index.js +++ b/packages/web3-utils/src/index.js @@ -436,5 +436,7 @@ module.exports = { isTopicInBloom: utils.isTopicInBloom, isInBloom: utils.isInBloom, - compareBlockNumbers: compareBlockNumbers + compareBlockNumbers: compareBlockNumbers, + + toNumber: utils.toNumber }; diff --git a/packages/web3-utils/src/utils.js b/packages/web3-utils/src/utils.js index 4e71b160f52..86bf3048601 100644 --- a/packages/web3-utils/src/utils.js +++ b/packages/web3-utils/src/utils.js @@ -521,6 +521,17 @@ var sha3Raw = function(value) { return value; }; +/** + * Auto converts any given value into it's hex representation, + * then converts hex to number. + * + * @method toNumber + * @param {String|Number|BN} value + * @return {Number} + */ +var toNumber = function(value) { + return typeof value === 'number' ? value : hexToNumber(toHex(value)); +} module.exports = { BN: BN, @@ -550,5 +561,6 @@ module.exports = { rightPad: rightPad, toTwosComplement: toTwosComplement, sha3: sha3, - sha3Raw: sha3Raw + sha3Raw: sha3Raw, + toNumber: toNumber }; diff --git a/packages/web3-utils/types/index.d.ts b/packages/web3-utils/types/index.d.ts index 59606095e47..c2254984753 100644 --- a/packages/web3-utils/types/index.d.ts +++ b/packages/web3-utils/types/index.d.ts @@ -122,6 +122,7 @@ export function testAddress(bloom: string, address: string): boolean; export function testTopic(bloom: string, topic: string): boolean; export function getSignatureParameters(signature: string): {r: string; s: string; v: number}; export function stripHexPrefix(str: string): string; +export function toNumber(value: number | string | BN): number; // interfaces export interface Utils { @@ -178,6 +179,7 @@ export interface Utils { testTopic(bloom: string, topic: string): boolean; getSignatureParameters(signature: string): {r: string; s: string; v: number}; stripHexPrefix(str: string): string; + toNumber(value: number | string | BN): number; } export interface Units { diff --git a/packages/web3-utils/types/tests/to-number-test.ts b/packages/web3-utils/types/tests/to-number-test.ts new file mode 100644 index 00000000000..c6a0a14f8ed --- /dev/null +++ b/packages/web3-utils/types/tests/to-number-test.ts @@ -0,0 +1,44 @@ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . +*/ +/** + * @file to-number-test.ts + * @author Josh Stevens + * @date 2018 + */ + +import BN = require('bn.js'); +import {toNumber} from 'web3-utils'; + +// $ExpectType number +toNumber('234'); +// $ExpectType number +toNumber(234); +// $ExpectType number +toNumber(new BN(3)); + +// $ExpectError +toNumber(['string']); +// $ExpectError +toNumber(true); +// $ExpectError +toNumber([4]); +// $ExpectError +toNumber({}); +// $ExpectError +toNumber(null); +// $ExpectError +toNumber(undefined); diff --git a/test/eth.feeHistory.js b/test/eth.feeHistory.js new file mode 100644 index 00000000000..f773075c166 --- /dev/null +++ b/test/eth.feeHistory.js @@ -0,0 +1,124 @@ +var BigNumber = require('bignumber.js'); + +var testMethod = require('./helpers/test.method.js'); + +var method = 'getFeeHistory'; +var methodCall = 'eth_feeHistory'; + +var tests = [ + { + args: [4, "0xA30953", []], + formattedArgs: [4, "0xA30953", []], + result: { + "baseFeePerGas": [ + "0xa", + "0x9", + "0x8", + "0x9", + "0x9" + ], + "gasUsedRatio": [ + 0.003920375, + 0.002625, + 0.904999125, + 0.348347625 + ], + "oldestBlock": 10684752 + }, + formattedResult: { + "baseFeePerGas": [ + "0xa", + "0x9", + "0x8", + "0x9", + "0x9" + ], + "gasUsedRatio": [ + 0.003920375, + 0.002625, + 0.904999125, + 0.348347625 + ], + "oldestBlock": 10684752 + }, + call: methodCall + }, + { + args: ['0x4', 10684755, []], + formattedArgs: [4, "0xa30953", []], + result: { + "baseFeePerGas": [ + "0xa", + "0x9", + "0x8", + "0x9", + "0x9" + ], + "gasUsedRatio": [ + 0.003920375, + 0.002625, + 0.904999125, + 0.348347625 + ], + "oldestBlock": 10684752 + }, + formattedResult: { + "baseFeePerGas": [ + "0xa", + "0x9", + "0x8", + "0x9", + "0x9" + ], + "gasUsedRatio": [ + 0.003920375, + 0.002625, + 0.904999125, + 0.348347625 + ], + "oldestBlock": 10684752 + }, + call: methodCall + }, + { + args: [new BigNumber(4), '10684755', []], + formattedArgs: [4, "0xa30953", []], + result: { + "baseFeePerGas": [ + "0xa", + "0x9", + "0x8", + "0x9", + "0x9" + ], + "gasUsedRatio": [ + 0.003920375, + 0.002625, + 0.904999125, + 0.348347625 + ], + "oldestBlock": 10684752 + }, + formattedResult: { + "baseFeePerGas": [ + "0xa", + "0x9", + "0x8", + "0x9", + "0x9" + ], + "gasUsedRatio": [ + 0.003920375, + 0.002625, + 0.904999125, + 0.348347625 + ], + "oldestBlock": 10684752 + }, + call: methodCall + } +]; + + +testMethod.runTests('eth', method, tests); + diff --git a/test/eth_methods.js b/test/eth_methods.js index 839984f2f5b..e66f7685f4a 100644 --- a/test/eth_methods.js +++ b/test/eth_methods.js @@ -27,6 +27,7 @@ describe('eth', function() { u.methodExists(eth, 'isMining'); u.methodExists(eth, 'getCoinbase'); u.methodExists(eth, 'getGasPrice'); + u.methodExists(eth, 'getFeeHistory'); u.methodExists(eth, 'getHashrate'); u.methodExists(eth, 'getAccounts'); u.methodExists(eth, 'getBlockNumber'); diff --git a/test/utils.hexToNumber.js b/test/utils.hexToNumber.js new file mode 100644 index 00000000000..a1d7767557d --- /dev/null +++ b/test/utils.hexToNumber.js @@ -0,0 +1,23 @@ +var assert = require('assert'); +var utils = require('../packages/web3-utils'); + +describe('lib/utils/utils', function () { + describe('hexToNumber', function () { + it('should return the correct value', function () { + + assert.equal(utils.hexToNumber("0x3e8"), 1000); + assert.equal(utils.hexToNumber('0x1f0fe294a36'), 2134567897654); + // allow compatiblity + assert.equal(utils.hexToNumber(100000), 100000); + }); + + it('should validate hex strings', function() { + try { + utils.hexToNumber('100000'); + assert.fail(); + } catch (error){ + assert(error.message.includes('is not a valid hex string')) + } + }) + }); +}); diff --git a/test/utils.toNumber.js b/test/utils.toNumber.js index a1d7767557d..c904a98b4e1 100644 --- a/test/utils.toNumber.js +++ b/test/utils.toNumber.js @@ -1,23 +1,61 @@ -var assert = require('assert'); +var chai = require('chai'); var utils = require('../packages/web3-utils'); -describe('lib/utils/utils', function () { - describe('hexToNumber', function () { - it('should return the correct value', function () { +var BigNumber = require('bignumber.js'); +var BN = require('bn.js'); - assert.equal(utils.hexToNumber("0x3e8"), 1000); - assert.equal(utils.hexToNumber('0x1f0fe294a36'), 2134567897654); - // allow compatiblity - assert.equal(utils.hexToNumber(100000), 100000); - }); +var assert = chai.assert; + +var tests = [ + { value: 1, expected: 1 }, + { value: '1', expected: 1 }, + { value: '0x1', expected: 1}, + { value: '15', expected: 15}, + { value: '0xf', expected: 15}, + { value: -1, expected: -1}, + { value: '-1', expected: -1}, + { value: '-0x1', expected: -1}, + { value: '-15', expected: -15}, + { value: '-0xf', expected: -15}, + { value: '0x657468657265756d', expected: '0x657468657265756d', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', expected: '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: '-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', expected: '-0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: '-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', expected: '-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: 0, expected: 0}, + { value: '0', expected: 0}, + { value: '0x0', expected: 0}, + { value: -0, expected: -0}, + { value: '-0', expected: -0}, + { value: '-0x0', expected: -0}, + { value: [1,2,3,{test: 'data'}], expected: '0x5b312c322c332c7b2274657374223a2264617461227d5d', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: {test: 'test'}, expected: '0x7b2274657374223a2274657374227d', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: '{"test": "test"}', expected: '0x7b2274657374223a202274657374227d', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: 'myString', expected: '0x6d79537472696e67', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: 'myString 34534!', expected: '0x6d79537472696e6720333435333421', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: new BN(15), expected: 15}, + { value: new BigNumber(15), expected: 15}, + { value: 'Heeäööä👅D34ɝɣ24Єͽ-.,äü+#/', expected: '0x486565c3a4c3b6c3b6c3a4f09f9185443334c99dc9a33234d084cdbd2d2e2cc3a4c3bc2b232f', error: true, errorMessage: 'Number can only safely store up to 53 bits'}, + { value: true, expected: 1}, + { value: false, expected: 0}, +]; - it('should validate hex strings', function() { - try { - utils.hexToNumber('100000'); - assert.fail(); - } catch (error){ - assert(error.message.includes('is not a valid hex string')) +describe('lib/utils/utils', function () { + describe('toNumber', function () { + tests.forEach(function (test) { + if (test.error) { + it('should error with message', function () { + try { + utils.toNumber(test.value) + assert.fail(); + } catch(err){ + assert.strictEqual(err.message, test.errorMessage); + } + }); + } else { + it('should turn ' + test.value + ' to ' + test.expected, function () { + assert.strictEqual(utils.toNumber(test.value), test.expected); + }); } - }) + }); }); });