Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

allowing to specify percentage-based factors (like 1.125 for 112.5%) #7332

6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2754,6 +2754,10 @@ If there are any bugs, improvements, optimizations or any new feature proposal f

- update the type for `baseFeePerGas` at `web3.eth.getFeeHistory` to be a number. (#7291)

#### web3-eth

- Allow specifying percentage based factor in Web3Eth.calculateFeeData Param baseFeePerGasFactor #7332

### Fixed

#### web3-eth-abi
Expand All @@ -2770,4 +2774,4 @@ If there are any bugs, improvements, optimizations or any new feature proposal f

#### web3-rpc-providers

- PublicNodeProvider was added (#7322)
- PublicNodeProvider was added (#7322)
96 changes: 59 additions & 37 deletions packages/web3-eth/src/web3_eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,43 +272,57 @@ export class Web3Eth extends Web3Context<Web3EthExecutionAPI, RegisteredSubscrip
}

/**
* Calculates the current Fee Data.
* If the node supports EIP-1559, then `baseFeePerGas` and `maxPriorityFeePerGas` will be returned along with the calculated `maxFeePerGas` value.
* `maxFeePerGas` is calculated as `baseFeePerGas` * `baseFeePerGasFactor` + `maxPriorityFeePerGas`.
* If the node does not support EIP-1559, then the `gasPrice` will be returned and the other values will be undefined.
*
* @param baseFeePerGasFactor (optional) The factor to multiply the `baseFeePerGas` with when calculating `maxFeePerGas`, if the node supports EIP-1559. The default value is 2.
* @param alternativeMaxPriorityFeePerGas (optional) The alternative `maxPriorityFeePerGas` to use when calculating `maxFeePerGas`, if the node supports EIP-1559, but does not support the method `eth_maxPriorityFeePerGas`. The default value is 1 gwei.
* @returns The current fee data.
*
* ```ts
* web3.eth.calculateFeeData().then(console.log);
* > {
* gasPrice: 20000000000n,
* maxFeePerGas: 60000000000n,
* maxPriorityFeePerGas: 20000000000n,
* baseFeePerGas: 20000000000n
* }
*
* web3.eth.calculateFeeData(1n).then(console.log);
* > {
* gasPrice: 20000000000n,
* maxFeePerGas: 40000000000n,
* maxPriorityFeePerGas: 20000000000n,
* baseFeePerGas: 20000000000n
* }
*
* web3.eth.calculateFeeData(3n).then(console.log);
* > {
* gasPrice: 20000000000n,
* maxFeePerGas: 80000000000n,
* maxPriorityFeePerGas: 20000000000n,
* baseFeePerGas: 20000000000n
* }
* ```
*/
* Calculates the current Fee Data.
* If the node supports EIP-1559, then `baseFeePerGas` and `maxPriorityFeePerGas` will be returned along with the calculated `maxFeePerGas` value.
* `maxFeePerGas` is calculated as `baseFeePerGas` * `baseFeePerGasFactor` + `maxPriorityFeePerGas`.
* If the node does not support EIP-1559, then the `gasPrice` will be returned and the other values will be undefined.
*
* @param baseFeePerGasFactor (optional) The factor to multiply the `baseFeePerGas` with when calculating `maxFeePerGas`, if the node supports EIP-1559. This can be a `bigint` for precise calculation or a `number` to support decimals. The default value is 2 (BigInt).
* If a `number` is provided, it will be converted to `bigint` with three decimal precision.
* @param alternativeMaxPriorityFeePerGas (optional) The alternative `maxPriorityFeePerGas` to use when calculating `maxFeePerGas`, if the node supports EIP-1559 but does not support the method `eth_maxPriorityFeePerGas`. The default value is 1 gwei.
* @returns The current fee data.
*
* @example
* web3.eth.calculateFeeData().then(console.log);
* > {
* gasPrice: 20000000000n,
* maxFeePerGas: 60000000000n,
* maxPriorityFeePerGas: 20000000000n,
* baseFeePerGas: 20000000000n
* }
*
* @example
* // Using a `bigint` for baseFeePerGasFactor
* web3.eth.calculateFeeData(1n).then(console.log);
* > {
* gasPrice: 20000000000n,
* maxFeePerGas: 40000000000n,
* maxPriorityFeePerGas: 20000000000n,
* baseFeePerGas: 20000000000n
* }
*
* @example
* // Using a `number` for baseFeePerGasFactor (with decimals)
* web3.eth.calculateFeeData(1.5).then(console.log);
* > {
* gasPrice: 20000000000n,
* maxFeePerGas: 50000000000n, // baseFeePerGasFactor is converted to BigInt(1.500)
* maxPriorityFeePerGas: 20000000000n,
* baseFeePerGas: 20000000000n
* }
*
* @example
* web3.eth.calculateFeeData(3n).then(console.log);
* > {
* gasPrice: 20000000000n,
* maxFeePerGas: 80000000000n,
* maxPriorityFeePerGas: 20000000000n,
* baseFeePerGas: 20000000000n
* }
*/

public async calculateFeeData(
baseFeePerGasFactor = BigInt(2),
baseFeePerGasFactor: bigint | number = BigInt(2),
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks @TemirlanBasitov I'll suggest to also add few examples and short description in above documentation comments so with this PR merge it will update docs website. https://docs.web3js.org/libdocs/Web3Eth#calculatefeedata

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jdevcs where i can add comment and short description here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I adjusted description of method with examples in the web3_eth.ts
Here also short explanation:

calculateFeeData method now supports both bigint and number types for the baseFeePerGasFactor parameter. If a number is provided, it will be converted to a bigint with three decimal precision. This allows for more flexible calculations when dealing with decimal values.

Ex:
await calculateFeeData(BigInt(2));
// baseFeePerGasFactor remains as 2n

await calculateFeeData(1.5);
// baseFeePerGasFactor is converted to BigInt(1.500) for precise calculations

These changes make the method more versatile while ensuring backward compatibility with previous bigint usage.

Copy link
Contributor

Choose a reason for hiding this comment

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

@luu-alex web3-eth package's changelog is not update for this PR but root. so In release for this PR, web3-eth will be patch bump if there are no other changes in eth.

alternativeMaxPriorityFeePerGas = ethUnitMap.Gwei,
): Promise<FeeData> {
const block = await this.getBlock<{ number: FMT_NUMBER.BIGINT; bytes: FMT_BYTES.HEX }>(
Expand Down Expand Up @@ -348,7 +362,15 @@ export class Web3Eth extends Web3Context<Web3EthExecutionAPI, RegisteredSubscrip
// and we multiply the `baseFeePerGas` by `baseFeePerGasFactor`, to allow
// trying to include the transaction in the next few blocks even if the
// baseFeePerGas is increasing fast
maxFeePerGas = baseFeePerGas * baseFeePerGasFactor + maxPriorityFeePerGas;
let baseFeeMultiplier: bigint;
if (typeof baseFeePerGasFactor === 'number') {
// Convert number to bigint with three decimal places
baseFeeMultiplier = BigInt(Math.floor(baseFeePerGasFactor * 1000)) / BigInt(1000);
} else {
// It's already a BigInt, so just use it as-is
baseFeeMultiplier = baseFeePerGasFactor;
}
maxFeePerGas = baseFeePerGas * baseFeeMultiplier + maxPriorityFeePerGas;
}

return { gasPrice, maxFeePerGas, maxPriorityFeePerGas, baseFeePerGas };
Expand Down
21 changes: 21 additions & 0 deletions packages/web3-eth/test/unit/web3_eth_calculate_fee_data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,25 @@ describe('Web3Eth.calculateFeeData', () => {
baseFeePerGas,
});
});

it('should use default baseFeePerGasFactor if none is provided', async () => {
const gasPrice = BigInt(20 * 1000);
const baseFeePerGas = BigInt(1000);
const maxPriorityFeePerGas = BigInt(100); // this will be used directly

jest.spyOn(ethRpcMethods, 'getBlockByNumber').mockReturnValueOnce({ baseFeePerGas } as any);
jest.spyOn(ethRpcMethods, 'getGasPrice').mockReturnValueOnce(gasPrice as any);
jest.spyOn(ethRpcMethods, 'getMaxPriorityFeePerGas').mockReturnValueOnce(
maxPriorityFeePerGas as any,
);

const feeData = await web3Eth.calculateFeeData(); // no baseFeePerGasFactor passed
const defaultBaseFeePerGasFactor = BigInt(2);
expect(feeData).toMatchObject({
gasPrice,
maxFeePerGas: baseFeePerGas * defaultBaseFeePerGasFactor + maxPriorityFeePerGas,
maxPriorityFeePerGas,
baseFeePerGas,
});
});
});
Loading