Skip to content

Commit 6e32af0

Browse files
authored
Add intermittent gasPrice Buffer Logic (#690)
During long lived operations of the relay it's possible for the relay to observe a change int eh gasPrice calculation needed by submitted transactions. As a result some checks are often 1 tiny bar off - add logic to provide a buffer for gas price prechecks - default to 1 tinybar - add tests - updated helm chart with default value Signed-off-by: Nana Essilfie-Conduah <[email protected]>
1 parent 555385f commit 6e32af0

File tree

7 files changed

+41
-43
lines changed

7 files changed

+41
-43
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ RATE_LIMIT_DISABLED =
1919
ETH_GET_LOGS_BLOCK_RANGE_LIMIT =
2020
DEV_MODE =
2121
MIRROR_NODE_RETRIES =
22-
MIRROR_NODE_RETRY_DELAY =
22+
MIRROR_NODE_RETRY_DELAY =
23+
GAS_PRICE_TINY_BAR_BUFFER =

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ RATE_LIMIT_DISABLED = false
8383
DEV_MODE = false
8484
MIRROR_NODE_RETRIES = 3
8585
MIRROR_NODE_RETRY_DELAY = 500
86+
GAS_PRICE_TINY_BAR_BUFFER = 10000000000
8687
```
8788

8889
Note: Read more about `DEV_MODE` [here](docs/dev-mode.md)

helm-chart/templates/deployment.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ spec:
150150
name: {{ include "json-rpc-relay.fullname" . }}
151151
key: DEV_MODE
152152
optional: true
153+
- name: GAS_PRICE_TINY_BAR_BUFFER
154+
valueFrom:
155+
configMapKeyRef:
156+
name: {{ include "json-rpc-relay.fullname" . }}
157+
key: GAS_PRICE_TINY_BAR_BUFFER
158+
optional: true
153159
ports:
154160
- containerPort: {{ .Values.ports.containerPort }}
155161
name: {{ .Values.ports.name }}

helm-chart/values.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ config:
112112
ETH_GET_LOGS_BLOCK_RANGE_LIMIT: 1000
113113
RATE_LIMIT_DISABLED: false
114114
DEV_MODE: false
115+
GAS_PRICE_TINY_BAR_BUFFER: 10000000000
115116

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

packages/relay/src/lib/precheck.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,12 @@
1818
*
1919
*/
2020

21-
import * as ethers from 'ethers';
2221
import { predefined } from './errors/JsonRpcError';
2322
import { MirrorNodeClient, SDKClient } from './clients';
2423
import { EthImpl } from './eth';
2524
import { Logger } from 'pino';
2625
import constants from './constants';
27-
import { Transaction } from 'ethers';
26+
import { ethers, Transaction } from 'ethers';
2827
import { formatRequestIdMessage } from '../formatters';
2928

3029
export class Precheck {
@@ -110,11 +109,20 @@ export class Precheck {
110109
*/
111110
gasPrice(tx: Transaction, gasPrice: number, requestId?: string) {
112111
const requestIdPrefix = formatRequestIdMessage(requestId);
113-
const minGasPrice = ethers.ethers.BigNumber.from(gasPrice);
112+
const minGasPrice = ethers.BigNumber.from(gasPrice);
114113
const txGasPrice = tx.gasPrice || tx.maxFeePerGas!.add(tx.maxPriorityFeePerGas!);
115114
const passes = txGasPrice.gte(minGasPrice);
116115

117116
if (!passes) {
117+
if (process.env.GAS_PRICE_TINY_BAR_BUFFER) {
118+
// Check if failure is within buffer range (Often it's by 1 tinybar) as network gasprice calculation can change slightly.
119+
// e.g gasPrice=1450000000000, requiredGasPrice=1460000000000, in which case we should allow users to go through and let the network check
120+
const txGasPriceWithBuffer = txGasPrice.add(ethers.BigNumber.from(process.env.GAS_PRICE_TINY_BAR_BUFFER));
121+
if (txGasPriceWithBuffer.gte(minGasPrice)) {
122+
return;
123+
}
124+
}
125+
118126
this.logger.trace(`${requestIdPrefix} Failed gas price precheck for sendRawTransaction(transaction=%s, gasPrice=%s, requiredGasPrice=%s)`, JSON.stringify(tx), txGasPrice, minGasPrice);
119127
throw predefined.GAS_PRICE_TOO_LOW;
120128
}
@@ -142,10 +150,10 @@ export class Precheck {
142150

143151
try {
144152
tinybars = await this.sdkClient.getAccountBalanceInTinyBar(accountResponse.account, callerName, requestId);
145-
result.passes = ethers.ethers.BigNumber.from(tinybars.toString()).mul(constants.TINYBAR_TO_WEIBAR_COEF).gte(txTotalValue);
153+
result.passes = ethers.BigNumber.from(tinybars.toString()).mul(constants.TINYBAR_TO_WEIBAR_COEF).gte(txTotalValue);
146154
} catch (error: any) {
147155
this.logger.trace(`${requestIdPrefix} Error on balance precheck for sendRawTransaction(transaction=%s, totalValue=%s, error=%s)`, JSON.stringify(tx), txTotalValue, error.message);
148-
throw predefined.INTERNAL_ERROR();
156+
throw predefined.INTERNAL_ERROR('balance precheck');
149157
}
150158

151159
if (!result.passes) {

packages/relay/tests/lib/precheck.spec.ts

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ describe('Precheck', async function() {
3939

4040
const txWithMatchingChainId = '0x02f87482012a0485a7a358200085a7a3582000832dc6c09400000000000000000000000000000000000003f78502540be40080c001a006f4cd8e6f84b76a05a5c1542a08682c928108ef7163d9c1bf1f3b636b1cd1fba032097cbf2dda17a2dcc40f62c97964d9d930cdce2e8a9df9a8ba023cda28e4ad';
4141
const parsedTxWithMatchingChainId = ethers.utils.parseTransaction(txWithMatchingChainId);
42+
const parsedTxGasPrice = 1440000000000;
4243
const txWithNonMatchingChainId = '0xf86a0385a7a3582000832dc6c09400000000000000000000000000000000000003f78502540be400801ca06750e92db52fa708e27f94f27e0cfb7f5800f9b657180bb2e94c1520cfb1fb6da01bec6045068b6db38b55017bb8b50166699384bc1791fd8331febab0cf629a2a';
4344
const parsedTxWithNonMatchingChainId = ethers.utils.parseTransaction(txWithNonMatchingChainId);
4445
const txWithValueMoreThanOneTinyBar = '0xf8628080809449858d4445908c12fcf70251d3f4682e8c9c381085174876e800801ba015ec73d3e329c7f5c0228be39bf30758f974d69468847dd507082c89ec453fe2a04124cc1dd6ac07417e7cdbe04cb99d698bddc6ce4d04054dd8978dec3493f3d2';
@@ -135,42 +136,6 @@ describe('Precheck', async function() {
135136
});
136137
});
137138

138-
describe('gas price', async function() {
139-
it('should return true for gas price gt to required gas price', async function() {
140-
const result = precheck.gasPrice(parsedTxWithMatchingChainId, 10);
141-
expect(result).to.not.exist;
142-
});
143-
144-
it('should return true for gas price equal to required gas price', async function() {
145-
const result = precheck.gasPrice(parsedTxWithMatchingChainId, defaultGasPrice);
146-
expect(result).to.not.exist;
147-
});
148-
149-
it('should not pass for non-matching chainId', async function() {
150-
try {
151-
precheck.chainId(parsedTxWithNonMatchingChainId);
152-
expectedError();
153-
}
154-
catch(e: any) {
155-
expect(e).to.exist;
156-
expect(e.code).to.eq(-32000);
157-
expect(e.message).to.eq('ChainId (0x0) not supported. The correct chainId is 0x12a');
158-
}
159-
});
160-
161-
it('should not pass for gas price not enough', async function() {
162-
const minGasPrice = 1000 * constants.TINYBAR_TO_WEIBAR_COEF;
163-
try {
164-
precheck.gasPrice(parsedTxWithMatchingChainId, minGasPrice);
165-
expectedError();
166-
} catch(e: any) {
167-
expect(e).to.exist;
168-
expect(e.code).to.eq(-32009);
169-
expect(e.message).to.eq('Gas price below configured minimum gas price');
170-
}
171-
});
172-
});
173-
174139
describe('gasLimit', async function() {
175140
const defaultTx = {
176141
value: oneTinyBar,
@@ -228,6 +193,16 @@ describe('Precheck', async function() {
228193
});
229194

230195
describe('gas price', async function() {
196+
let initialMinGasPriceBuffer;
197+
before(async () =>{
198+
initialMinGasPriceBuffer = process.env.GAS_PRICE_TINY_BAR_BUFFER;
199+
process.env.GAS_PRICE_TINY_BAR_BUFFER = '10000000000'; // 1 tinybar
200+
})
201+
202+
after(async () =>{
203+
process.env.GAS_PRICE_TINY_BAR_BUFFER = initialMinGasPriceBuffer;
204+
})
205+
231206
it('should pass for gas price gt to required gas price', async function() {
232207
const result = precheck.gasPrice(parsedTxWithMatchingChainId, 10);
233208
expect(result).to.not.exist;
@@ -249,6 +224,11 @@ describe('Precheck', async function() {
249224
expect(e.message).to.eq('Gas price below configured minimum gas price');
250225
}
251226
});
227+
228+
it('should pass for gas price not enough but within buffer', async function() {
229+
const adjustedGasPrice = parsedTxGasPrice + Number(process.env.GAS_PRICE_TINY_BAR_BUFFER);
230+
precheck.gasPrice(parsedTxWithMatchingChainId, adjustedGasPrice);
231+
});
252232
});
253233

254234
describe('balance', async function() {

packages/server/tests/localAcceptance.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ HBAR_RATE_LIMIT_DURATION = 80000
2424
RATE_LIMIT_DISABLED = false
2525
DEV_MODE = false
2626
MIRROR_NODE_RETRIES = 3
27-
MIRROR_NODE_RETRY_DELAY = 500
27+
MIRROR_NODE_RETRY_DELAY = 500
28+
GAS_PRICE_TINY_BAR_BUFFER = 10000000000

0 commit comments

Comments
 (0)