Skip to content

Commit

Permalink
feat: add a tx gasPrice Buffer to reduce INSUFFICIENT_TX_FEE cases (#…
Browse files Browse the repository at this point in the history
…2692)

* chore: add buffer

Signed-off-by: nikolay <[email protected]>

* chore: edit tests in order to fix code duplication warning

Signed-off-by: nikolay <[email protected]>

* chore: move the calculations to formatter.ts

Signed-off-by: nikolay <[email protected]>

* chore: fix readme

Signed-off-by: nikolay <[email protected]>

* chore: add utils class

Signed-off-by: nikolay <[email protected]>

* chore: fix sonarcloud issue

Signed-off-by: nikolay <[email protected]>

---------

Signed-off-by: nikolay <[email protected]>
Signed-off-by: ebadiere <[email protected]>
  • Loading branch information
natanasow authored and ebadiere committed Aug 1, 2024
1 parent 6d27451 commit 288c23a
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 1 deletion.
1 change: 1 addition & 0 deletions charts/hedera-json-rpc-relay/value-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ config:
DEBUG_API_ENABLED: false
FILTER_API_ENABLED: true
MULTI_SET: true
GAS_PRICE_PERCENTAGE_BUFFER: 0

# Enable rolling_restarts if SDK calls fail this is usually due to stale connections that get cycled on restart
rolling_restart:
Expand Down
1 change: 1 addition & 0 deletions charts/hedera-json-rpc-relay/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ config:
DEBUG_API_ENABLED: false
FILTER_API_ENABLED: true
MULTI_SET: true
GAS_PRICE_PERCENTAGE_BUFFER: 0

# -- Extra environment variables from existing secrets or configmaps
extraEnvFrom: []
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Unless you need to set a non-default value, it is recommended to only populate o
| `FEE_HISTORY_MAX_RESULTS` | "10" | The maximum number of results to returns as part of `eth_feeHistory`. |
| `ETH_FEE_HISTORY_FIXED` | "true" | Flag to set if eth_feeHistory should return a fixed fee for the set of results. |
| `ETH_POPULATE_SYNTHETIC_CONTRACT_RESULTS` | "true" | Flag to set if the relay should populate the contract results for synthetic contracts. |
| `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. |
| `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. |
| `HAPI_CLIENT_DURATION_RESET` | "3600000" | Time until client reinitialization. (ms) |
| `HAPI_CLIENT_ERROR_RESET` | [21, 50] | Array of status codes, which when encountered will trigger a reinitialization. Status codes are availble [here](https:/hashgraph/hedera-protobufs/blob/main/services/response_code.proto). |
Expand Down
4 changes: 3 additions & 1 deletion packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { IContractCallRequest, IContractCallResponse, MirrorNodeClient } from '.
import { JsonRpcError, predefined } from './errors/JsonRpcError';
import { SDKClientError } from './errors/SDKClientError';
import { MirrorNodeClientError } from './errors/MirrorNodeClientError';
import { Utils } from './../utils';
import constants from './constants';
import { Precheck } from './precheck';
import {
Expand Down Expand Up @@ -729,7 +730,8 @@ export class EthImpl implements Eth {
);

if (!gasPrice) {
gasPrice = await this.getFeeWeibars(EthImpl.ethGasPrice, requestIdPrefix);
gasPrice = Utils.addPercentageBufferToGasPrice(await this.getFeeWeibars(EthImpl.ethGasPrice, requestIdPrefix));

this.cacheService.set(
constants.CACHE_KEY.GAS_PRICE,
gasPrice,
Expand Down
38 changes: 38 additions & 0 deletions packages/relay/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*-
*
* Hedera JSON RPC Relay
*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import constants from './lib/constants';

export class Utils {
public static readonly addPercentageBufferToGasPrice = (gasPrice: number): number => {
// converting to tinybar and afterward to weibar again is needed
// in order to handle the possibility of an invalid floating number being calculated as a gas price
// e.g.
// current gas price = 126
// buffer = 10%
// buffered gas price = 126 + 12.6 = 138.6 <--- invalid tinybars
gasPrice +=
Math.round(
(gasPrice / constants.TINYBAR_TO_WEIBAR_COEF) * (Number(process.env.GAS_PRICE_PERCENTAGE_BUFFER || 0) / 100),
) * constants.TINYBAR_TO_WEIBAR_COEF;

return gasPrice;
};
}
37 changes: 37 additions & 0 deletions packages/relay/tests/lib/eth/eth_gasPrice.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { DEFAULT_NETWORK_FEES, NOT_FOUND_RES } from './eth-config';
import { predefined } from '../../../src/lib/errors/JsonRpcError';
import RelayAssertions from '../../assertions';
import { generateEthTestEnv } from './eth-helpers';
import { toHex } from '../../helpers';

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

describe('@ethGasPrice different value for GAS_PRICE_PERCENTAGE_BUFFER env', async function () {
const GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES = {
'eth_gasPrice with GAS_PRICE_PERCENTAGE_BUFFER set to 10%': '10',
'eth_gasPrice with GAS_PRICE_PERCENTAGE_BUFFER set to floating % that results in floating number for buffered gas price':
'10.25',
};

for (let testCaseName in GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES) {
it(testCaseName, async function () {
const GAS_PRICE_PERCENTAGE_BUFFER = GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES[testCaseName];
const initialGasPrice = await ethImpl.gasPrice();
process.env.GAS_PRICE_PERCENTAGE_BUFFER = GAS_PRICE_PERCENTAGE_BUFFER;

await cacheService.clear();

const gasPriceWithBuffer = await ethImpl.gasPrice();
process.env.GAS_PRICE_PERCENTAGE_BUFFER = '0';

const expectedInitialGasPrice = toHex(DEFAULT_NETWORK_FEES.fees[2].gas * constants.TINYBAR_TO_WEIBAR_COEF);
const expectedGasPriceWithBuffer = toHex(
Number(expectedInitialGasPrice) +
Math.round(
(Number(expectedInitialGasPrice) / constants.TINYBAR_TO_WEIBAR_COEF) *
(Number(GAS_PRICE_PERCENTAGE_BUFFER || 0) / 100),
) *
constants.TINYBAR_TO_WEIBAR_COEF,
);

expect(expectedInitialGasPrice).to.not.equal(expectedGasPriceWithBuffer);
expect(initialGasPrice).to.not.equal(gasPriceWithBuffer);
expect(initialGasPrice).to.equal(expectedInitialGasPrice);
expect(gasPriceWithBuffer).to.equal(expectedGasPriceWithBuffer);
});
}
});

describe('eth_gasPrice not found', async function () {
beforeEach(() => {
restMock.onGet(`network/fees`).reply(404, NOT_FOUND_RES);
Expand Down
48 changes: 48 additions & 0 deletions packages/relay/tests/lib/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*-
*
* Hedera JSON RPC Relay
*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import { expect } from 'chai';
import { Utils } from '../../src/utils';
import constants from '../../src/lib/constants';

describe('Utils', () => {
describe('addPercentageBufferToGasPrice', () => {
afterEach(() => {
process.env.GAS_PRICE_PERCENTAGE_BUFFER = '0';
});

const TW_COEF = constants.TINYBAR_TO_WEIBAR_COEF;
const TEST_CASES = [
{ testName: 'zero input', buffer: '0', input: 0, output: 0 },
{ testName: 'buffer 0%', buffer: '0', input: 10 * TW_COEF, output: 10 * TW_COEF },
{ testName: 'buffer 7%', buffer: '7', input: 140 * TW_COEF, output: 150 * TW_COEF },
{ testName: 'buffer 10%', buffer: '10', input: 126 * TW_COEF, output: 139 * TW_COEF },
{ testName: 'buffer 12.25%', buffer: '12.25', input: 56 * TW_COEF, output: 63 * TW_COEF },
{ testName: 'negative buffer -6%', buffer: '-6', input: 100 * TW_COEF, output: 94 * TW_COEF },
{ testName: 'negative buffer -12.58%', buffer: '-12.58', input: 136 * TW_COEF, output: 119 * TW_COEF },
];
for (let i in TEST_CASES) {
it(TEST_CASES[i].testName, () => {
process.env.GAS_PRICE_PERCENTAGE_BUFFER = TEST_CASES[i].buffer;
expect(Utils.addPercentageBufferToGasPrice(TEST_CASES[i].input)).to.equal(TEST_CASES[i].output);
});
}
});
});

0 comments on commit 288c23a

Please sign in to comment.