Skip to content

Commit

Permalink
fix: Resolve BigInt type mismatch in eth_sendRawTransaction precheck (#…
Browse files Browse the repository at this point in the history
…3463)

fix: fixed BigInt mismatch types in eth_sendRawTransaction precheck

Signed-off-by: Logan Nguyen <[email protected]>
  • Loading branch information
quiet-node authored Feb 7, 2025
1 parent 237b452 commit 5003741
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 3 deletions.
8 changes: 5 additions & 3 deletions packages/relay/src/lib/precheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ export class Precheck {
*/
gasPrice(tx: Transaction, networkGasPriceInWeiBars: number, requestDetails: RequestDetails): void {
const networkGasPrice = BigInt(networkGasPriceInWeiBars);
const txGasPrice = tx.gasPrice || tx.maxFeePerGas! + tx.maxPriorityFeePerGas!;

const txGasPrice = BigInt(tx.gasPrice || tx.maxFeePerGas! + tx.maxPriorityFeePerGas!);

// **notice: Pass gasPrice precheck if txGasPrice is greater than the minimum network's gas price value,
// OR if the transaction is the deterministic deployment transaction (a special case).
Expand Down Expand Up @@ -221,8 +222,9 @@ export class Precheck {
passes: false,
error: predefined.INSUFFICIENT_ACCOUNT_BALANCE,
};
const txGas = tx.gasPrice || tx.maxFeePerGas! + tx.maxPriorityFeePerGas!;
const txTotalValue = tx.value + txGas * tx.gasLimit;

const txGasPrice = BigInt(tx.gasPrice || tx.maxFeePerGas! + tx.maxPriorityFeePerGas!);
const txTotalValue = tx.value + txGasPrice * tx.gasLimit;

if (account == null) {
if (this.logger.isLevelEnabled('trace')) {
Expand Down
100 changes: 100 additions & 0 deletions packages/relay/tests/lib/precheck.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,44 @@ describe('Precheck', async function () {
const adjustedGasPrice = parsedTxGasPrice + Number(constants.GAS_PRICE_TINY_BAR_BUFFER);
precheck.gasPrice(parsedTxWithMatchingChainId, adjustedGasPrice, requestDetails);
});

it('should pass for gas price if the transaction has valid gasPrice but null maxFeePerGas and null maxPriorityFeePerGas', async function () {
const parsedTx = {
...parsedTxWithMatchingChainId,
gasPrice: BigInt(100),
maxFeePerGas: null,
maxPriorityFeePerGas: null,
} as Transaction;

expect(() => precheck.gasPrice(parsedTx, 50, requestDetails)).to.not.throw();
});

it('should pass for gas price if the transaction has null gasPrice but valid maxFeePerGas and valid maxPriorityFeePerGas', async function () {
const parsedTx = {
...parsedTxWithMatchingChainId,
gasPrice: null,
maxFeePerGas: BigInt(100),
maxPriorityFeePerGas: BigInt(100),
} as Transaction;

expect(() => precheck.gasPrice(parsedTx, 50, requestDetails)).to.not.throw();
});

it('should not pass for gas price if the transaction has null gasPrice and null maxFeePerGas and null maxPriorityFeePerGas', async function () {
const tx = {
...parsedTxWithMatchingChainId,
gasPrice: null,
maxFeePerGas: null,
maxPriorityFeePerGas: null,
};
const signed = await signTransaction(tx);
const parsedTx = ethers.Transaction.from(signed);
const minGasPrice = 1000 * constants.TINYBAR_TO_WEIBAR_COEF;

expect(() => precheck.gasPrice(parsedTx, minGasPrice, requestDetails)).to.throw(
`Gas price '0' is below configured minimum gas price '${minGasPrice}'`,
);
});
});

describe('balance', async function () {
Expand Down Expand Up @@ -444,6 +482,68 @@ describe('Precheck', async function () {
const result = precheck.balance(parsedTransaction, account, requestDetails);
expect(result).to.not.exist;
});

it('should calculate balance correctly when transaction has valid gasPrice but null maxFeePerGas and null maxPriorityFeePerGas', async function () {
const tx = {
...parsedTxWithMatchingChainId,
value: BigInt(1000),
gasPrice: BigInt(100),
gasLimit: BigInt(21000),
maxFeePerGas: null,
maxPriorityFeePerGas: null,
} as Transaction;

const account = {
account: accountId,
balance: {
// Set balance higher than value + (gasPrice * gasLimit)
balance: (BigInt(1000) + BigInt(100) * BigInt(21000) + BigInt(1000)).toString(),
},
};

expect(() => precheck.balance(tx, account, requestDetails)).to.not.throw();
});

it('should calculate balance correctly when transaction has null gasPrice but valid maxFeePerGas and maxPriorityFeePerGas', async function () {
const tx = {
...parsedTxWithMatchingChainId,
value: BigInt(1000),
gasPrice: null,
gasLimit: BigInt(21000),
maxFeePerGas: BigInt(100),
maxPriorityFeePerGas: BigInt(100),
} as Transaction;

const account = {
account: accountId,
balance: {
// Set balance higher than value + ((maxFeePerGas + maxPriorityFeePerGas) * gasLimit)
balance: (BigInt(1000) + BigInt(200) * BigInt(21000) + BigInt(1000)).toString(),
},
};

expect(() => precheck.balance(tx, account, requestDetails)).to.not.throw();
});

it('should still pass when transaction has null gasPrice, null maxFeePerGas, and null maxPriorityFeePerGas', async function () {
const tx = {
...parsedTxWithMatchingChainId,
value: BigInt(1000),
gasPrice: null,
gasLimit: BigInt(21000),
maxFeePerGas: null,
maxPriorityFeePerGas: null,
} as Transaction;

const account = {
account: accountId,
balance: {
balance: BigInt(1000).toString(), // Any balance would fail as txTotalValue calculation would error
},
};

expect(() => precheck.balance(tx, account, requestDetails)).to.not.throw();
});
});

describe('nonce', async function () {
Expand Down
19 changes: 19 additions & 0 deletions packages/server/tests/acceptance/rpc_batch1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,25 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () {
expect(info).to.exist;
expect(info.result).to.equal('SUCCESS');
});

it('should fail "eth_sendRawTransaction" for transaction with null gasPrice, null maxFeePerGas, and null maxPriorityFeePerGas', async function () {
const transaction = {
...defaultLegacyTransactionData,
chainId: Number(CHAIN_ID),
gasPrice: null,
maxFeePerGas: null,
maxPriorityFeePerGas: null,
to: parentContractAddress,
nonce: await relay.getAccountNonce(accounts[2].address, requestId),
};
const signedTx = await accounts[2].wallet.signTransaction(transaction);
const error = predefined.GAS_PRICE_TOO_LOW(0, GAS_PRICE_REF);

await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [
signedTx,
requestDetails,
]);
});
});

it('@release should execute "eth_getTransactionByHash" for existing transaction', async function () {
Expand Down

0 comments on commit 5003741

Please sign in to comment.