Skip to content

Commit 0d7e180

Browse files
authored
feat: add a tx gasPrice Buffer to reduce INSUFFICIENT_TX_FEE cases (#2692)
* chore: add buffer Signed-off-by: nikolay <n.atanasow94@gmail.com> * chore: edit tests in order to fix code duplication warning Signed-off-by: nikolay <n.atanasow94@gmail.com> * chore: move the calculations to formatter.ts Signed-off-by: nikolay <n.atanasow94@gmail.com> * chore: fix readme Signed-off-by: nikolay <n.atanasow94@gmail.com> * chore: add utils class Signed-off-by: nikolay <n.atanasow94@gmail.com> * chore: fix sonarcloud issue Signed-off-by: nikolay <n.atanasow94@gmail.com> --------- Signed-off-by: nikolay <n.atanasow94@gmail.com>
1 parent 5e24707 commit 0d7e180

File tree

7 files changed

+129
-1
lines changed

7 files changed

+129
-1
lines changed

charts/hedera-json-rpc-relay/value-test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ config:
135135
DEBUG_API_ENABLED: false
136136
FILTER_API_ENABLED: true
137137
MULTI_SET: true
138+
GAS_PRICE_PERCENTAGE_BUFFER: 0
138139

139140
# Enable rolling_restarts if SDK calls fail this is usually due to stale connections that get cycled on restart
140141
rolling_restart:

charts/hedera-json-rpc-relay/values.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ config:
5353
DEBUG_API_ENABLED: false
5454
FILTER_API_ENABLED: true
5555
MULTI_SET: true
56+
GAS_PRICE_PERCENTAGE_BUFFER: 0
5657

5758
# -- Extra environment variables from existing secrets or configmaps
5859
extraEnvFrom: []

docs/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Unless you need to set a non-default value, it is recommended to only populate o
6767
| `FEE_HISTORY_MAX_RESULTS` | "10" | The maximum number of results to returns as part of `eth_feeHistory`. |
6868
| `ETH_FEE_HISTORY_FIXED` | "true" | Flag to set if eth_feeHistory should return a fixed fee for the set of results. |
6969
| `ETH_POPULATE_SYNTHETIC_CONTRACT_RESULTS` | "true" | Flag to set if the relay should populate the contract results for synthetic contracts. |
70+
| `GAS_PRICE_PERCENTAGE_BUFFER` | "0" | The additional buffer that adds a percentage on top of the calculated network gasPrice. This may be used by operators to reduce the chances of `INSUFFICIENT_TX_FEE` errors experienced by users caused by minor fluctuations in the exchange rate. |
7071
| `GAS_PRICE_TINY_BAR_BUFFER` | "10000000000" | The additional buffer range to allow during a relay precheck of gas price. This supports slight fluctuations in network gasprice calculations. |
7172
| `HAPI_CLIENT_DURATION_RESET` | "3600000" | Time until client reinitialization. (ms) |
7273
| `HAPI_CLIENT_ERROR_RESET` | [21, 50] | Array of status codes, which when encountered will trigger a reinitialization. Status codes are availble [here](https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.proto). |

packages/relay/src/lib/eth.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { IContractCallRequest, IContractCallResponse, MirrorNodeClient } from '.
2626
import { JsonRpcError, predefined } from './errors/JsonRpcError';
2727
import { SDKClientError } from './errors/SDKClientError';
2828
import { MirrorNodeClientError } from './errors/MirrorNodeClientError';
29+
import { Utils } from './../utils';
2930
import constants from './constants';
3031
import { Precheck } from './precheck';
3132
import {
@@ -729,7 +730,8 @@ export class EthImpl implements Eth {
729730
);
730731

731732
if (!gasPrice) {
732-
gasPrice = await this.getFeeWeibars(EthImpl.ethGasPrice, requestIdPrefix);
733+
gasPrice = Utils.addPercentageBufferToGasPrice(await this.getFeeWeibars(EthImpl.ethGasPrice, requestIdPrefix));
734+
733735
this.cacheService.set(
734736
constants.CACHE_KEY.GAS_PRICE,
735737
gasPrice,

packages/relay/src/utils.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*-
2+
*
3+
* Hedera JSON RPC Relay
4+
*
5+
* Copyright (C) 2024 Hedera Hashgraph, LLC
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
import constants from './lib/constants';
22+
23+
export class Utils {
24+
public static readonly addPercentageBufferToGasPrice = (gasPrice: number): number => {
25+
// converting to tinybar and afterward to weibar again is needed
26+
// in order to handle the possibility of an invalid floating number being calculated as a gas price
27+
// e.g.
28+
// current gas price = 126
29+
// buffer = 10%
30+
// buffered gas price = 126 + 12.6 = 138.6 <--- invalid tinybars
31+
gasPrice +=
32+
Math.round(
33+
(gasPrice / constants.TINYBAR_TO_WEIBAR_COEF) * (Number(process.env.GAS_PRICE_PERCENTAGE_BUFFER || 0) / 100),
34+
) * constants.TINYBAR_TO_WEIBAR_COEF;
35+
36+
return gasPrice;
37+
};
38+
}

packages/relay/tests/lib/eth/eth_gasPrice.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { DEFAULT_NETWORK_FEES, NOT_FOUND_RES } from './eth-config';
3030
import { predefined } from '../../../src/lib/errors/JsonRpcError';
3131
import RelayAssertions from '../../assertions';
3232
import { generateEthTestEnv } from './eth-helpers';
33+
import { toHex } from '../../helpers';
3334

3435
dotenv.config({ path: path.resolve(__dirname, '../test.env') });
3536
use(chaiAsPromised);
@@ -90,6 +91,42 @@ describe('@ethGasPrice Gas Price spec', async function () {
9091
await RelayAssertions.assertRejection(predefined.COULD_NOT_ESTIMATE_GAS_PRICE, ethImpl.gasPrice, true, ethImpl);
9192
});
9293

94+
describe('@ethGasPrice different value for GAS_PRICE_PERCENTAGE_BUFFER env', async function () {
95+
const GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES = {
96+
'eth_gasPrice with GAS_PRICE_PERCENTAGE_BUFFER set to 10%': '10',
97+
'eth_gasPrice with GAS_PRICE_PERCENTAGE_BUFFER set to floating % that results in floating number for buffered gas price':
98+
'10.25',
99+
};
100+
101+
for (let testCaseName in GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES) {
102+
it(testCaseName, async function () {
103+
const GAS_PRICE_PERCENTAGE_BUFFER = GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES[testCaseName];
104+
const initialGasPrice = await ethImpl.gasPrice();
105+
process.env.GAS_PRICE_PERCENTAGE_BUFFER = GAS_PRICE_PERCENTAGE_BUFFER;
106+
107+
await cacheService.clear();
108+
109+
const gasPriceWithBuffer = await ethImpl.gasPrice();
110+
process.env.GAS_PRICE_PERCENTAGE_BUFFER = '0';
111+
112+
const expectedInitialGasPrice = toHex(DEFAULT_NETWORK_FEES.fees[2].gas * constants.TINYBAR_TO_WEIBAR_COEF);
113+
const expectedGasPriceWithBuffer = toHex(
114+
Number(expectedInitialGasPrice) +
115+
Math.round(
116+
(Number(expectedInitialGasPrice) / constants.TINYBAR_TO_WEIBAR_COEF) *
117+
(Number(GAS_PRICE_PERCENTAGE_BUFFER || 0) / 100),
118+
) *
119+
constants.TINYBAR_TO_WEIBAR_COEF,
120+
);
121+
122+
expect(expectedInitialGasPrice).to.not.equal(expectedGasPriceWithBuffer);
123+
expect(initialGasPrice).to.not.equal(gasPriceWithBuffer);
124+
expect(initialGasPrice).to.equal(expectedInitialGasPrice);
125+
expect(gasPriceWithBuffer).to.equal(expectedGasPriceWithBuffer);
126+
});
127+
}
128+
});
129+
93130
describe('eth_gasPrice not found', async function () {
94131
beforeEach(() => {
95132
restMock.onGet(`network/fees`).reply(404, NOT_FOUND_RES);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*-
2+
*
3+
* Hedera JSON RPC Relay
4+
*
5+
* Copyright (C) 2024 Hedera Hashgraph, LLC
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
import { expect } from 'chai';
22+
import { Utils } from '../../src/utils';
23+
import constants from '../../src/lib/constants';
24+
25+
describe('Utils', () => {
26+
describe('addPercentageBufferToGasPrice', () => {
27+
afterEach(() => {
28+
process.env.GAS_PRICE_PERCENTAGE_BUFFER = '0';
29+
});
30+
31+
const TW_COEF = constants.TINYBAR_TO_WEIBAR_COEF;
32+
const TEST_CASES = [
33+
{ testName: 'zero input', buffer: '0', input: 0, output: 0 },
34+
{ testName: 'buffer 0%', buffer: '0', input: 10 * TW_COEF, output: 10 * TW_COEF },
35+
{ testName: 'buffer 7%', buffer: '7', input: 140 * TW_COEF, output: 150 * TW_COEF },
36+
{ testName: 'buffer 10%', buffer: '10', input: 126 * TW_COEF, output: 139 * TW_COEF },
37+
{ testName: 'buffer 12.25%', buffer: '12.25', input: 56 * TW_COEF, output: 63 * TW_COEF },
38+
{ testName: 'negative buffer -6%', buffer: '-6', input: 100 * TW_COEF, output: 94 * TW_COEF },
39+
{ testName: 'negative buffer -12.58%', buffer: '-12.58', input: 136 * TW_COEF, output: 119 * TW_COEF },
40+
];
41+
for (let i in TEST_CASES) {
42+
it(TEST_CASES[i].testName, () => {
43+
process.env.GAS_PRICE_PERCENTAGE_BUFFER = TEST_CASES[i].buffer;
44+
expect(Utils.addPercentageBufferToGasPrice(TEST_CASES[i].input)).to.equal(TEST_CASES[i].output);
45+
});
46+
}
47+
});
48+
});

0 commit comments

Comments
 (0)