Skip to content

Commit

Permalink
throw outOfGas err when gaslimit too low
Browse files Browse the repository at this point in the history
  • Loading branch information
shunjizhan committed Dec 6, 2023
1 parent afa754f commit efcec17
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 57 deletions.
44 changes: 22 additions & 22 deletions packages/eth-providers/src/base-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
LogFilter,
PROVIDER_ERRORS,
SanitizedLogFilter,
TxRequestWithGas,
calcEthereumTransactionParams,
calcSubstrateTransactionParams,
checkEvmExecutionError,
Expand Down Expand Up @@ -789,32 +790,31 @@ export abstract class BaseProvider extends AbstractProvider {
return code.toHex();
};

// TODO: removable?
call = async (
_transaction: Deferrable<TransactionRequest>,
transaction: Deferrable<TxRequestWithGas>,
_blockTag?: BlockTag | Promise<BlockTag> | Eip1898BlockTag
): Promise<string> => {
const blockTag = await this._ensureSafeModeBlockTagFinalization(await parseBlockTag(_blockTag));

const [txRequest, blockHash] = await Promise.all([
getTransactionRequest(_transaction),
getTransactionRequest(transaction),
this._getBlockHash(blockTag),
]);

const transaction = txRequest.gasLimit && txRequest.gasPrice
? txRequest
: { ...txRequest, ...(await this._getEthGas()) };
txRequest.gasPrice ??= await this.getGasPrice();
txRequest.gasLimit ??= BigNumber.from(999999920);

const { storageLimit, gasLimit } = this._getSubstrateGasParams(transaction);
const { storageLimit, gasLimit } = this._getSubstrateGasParams(txRequest);
console.log(txRequest, { storageLimit, gasLimit });

const callRequest: SubstrateEvmCallRequest = {
from: transaction.from,
to: transaction.to,
from: txRequest.from,
to: txRequest.to,
gasLimit,
storageLimit,
value: transaction.value?.toBigInt(),
data: transaction.data,
accessList: transaction.accessList,
value: txRequest.value?.toBigInt(),
data: txRequest.data,
accessList: txRequest.accessList,
};

const res = await this._ethCall(callRequest, blockHash);
Expand All @@ -826,7 +826,7 @@ export abstract class BaseProvider extends AbstractProvider {
const api = at ? await this.api.at(at) : this.api;

const { from, to, gasLimit, storageLimit, value, data, accessList } = callRequest;
const estimate = true;
const estimate = false;

const res = to
? await api.call.evmRuntimeRPCApi.call(from, to, data, value, gasLimit, storageLimit, accessList, estimate)
Expand Down Expand Up @@ -919,8 +919,8 @@ export abstract class BaseProvider extends AbstractProvider {
};

_getGasConsts = (): GasConsts => ({
storageDepositPerByte: (this.api.consts.evm.storageDepositPerByte as UInt).toBigInt(),
txFeePerGas: (this.api.consts.evm.txFeePerGas as UInt).toBigInt(),
storageDepositPerByte: this.api.consts.evm.storageDepositPerByte.toBigInt(),
txFeePerGas: this.api.consts.evm.txFeePerGas.toBigInt(),
});

/**
Expand Down Expand Up @@ -1026,8 +1026,8 @@ export abstract class BaseProvider extends AbstractProvider {
validUntil = blockNumber + 100;
}

const storageByteDeposit = (this.api.consts.evm.storageDepositPerByte as UInt).toBigInt();
const txFeePerGas = (this.api.consts.evm.txFeePerGas as UInt).toBigInt();
const storageByteDeposit = this.api.consts.evm.storageDepositPerByte.toBigInt();
const txFeePerGas = this.api.consts.evm.txFeePerGas.toBigInt();

const { txGasLimit, txGasPrice } = calcEthereumTransactionParams({
gasLimit,
Expand Down Expand Up @@ -1061,8 +1061,8 @@ export abstract class BaseProvider extends AbstractProvider {
gasLimit: BigNumber;
}> => {
const validUntil = _validUntil || (await this.getBlockNumber()) + 150; // default 150 * 12 / 60 = 30min
const storageByteDeposit = (this.api.consts.evm.storageDepositPerByte as UInt).toBigInt();
const txFeePerGas = (this.api.consts.evm.txFeePerGas as UInt).toBigInt();
const storageByteDeposit = this.api.consts.evm.storageDepositPerByte.toBigInt();
const txFeePerGas = this.api.consts.evm.txFeePerGas.toBigInt();

const { txGasLimit, txGasPrice } = calcEthereumTransactionParams({
gasLimit,
Expand Down Expand Up @@ -1092,8 +1092,8 @@ export abstract class BaseProvider extends AbstractProvider {
storageLimit: BigNumber;
validUntil: BigNumber;
} => {
const storageByteDeposit = (this.api.consts.evm.storageDepositPerByte as UInt).toBigInt();
const txFeePerGas = (this.api.consts.evm.txFeePerGas as UInt).toBigInt();
const storageByteDeposit = this.api.consts.evm.storageDepositPerByte.toBigInt();
const txFeePerGas = this.api.consts.evm.txFeePerGas.toBigInt();

return calcSubstrateTransactionParams({
txGasPrice: gasPrice,
Expand Down Expand Up @@ -1124,7 +1124,7 @@ export abstract class BaseProvider extends AbstractProvider {
const txRequest = {
..._txRequest,
value: BigNumber.isBigNumber(_txRequest.value) ? _txRequest.value.toBigInt() : _txRequest.value,
gasLimit: _txRequest.gasLimit?.toBigInt() || MAX_GAS_LIMIT,
gasLimit: _txRequest.gasLimit?.toBigInt() ?? MAX_GAS_LIMIT,
storageLimit: STORAGE_LIMIT,
};

Expand Down
43 changes: 14 additions & 29 deletions packages/eth-providers/src/utils/transactionHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { GAS_LIMIT_CHUNK, GAS_MASK, MAX_GAS_LIMIT_CC, ONE_HUNDRED_GWEI, STORAGE_
import { ethToNativeDecimal } from './utils';
import { formatter } from './receiptHelper';

export type TxRequestWithGas = TransactionRequest & { gas?: BigNumberish };

type TxConsts = {
storageByteDeposit: BigNumberish;
txFeePerGas: BigNumberish;
Expand Down Expand Up @@ -78,45 +80,28 @@ export const calcSubstrateTransactionParams = (
};

export const getTransactionRequest = async (
transaction: Deferrable<TransactionRequest>
txRequest: Deferrable<TxRequestWithGas>
): Promise<Partial<Transaction>> => {
const values: any = await transaction;
const req = await resolveProperties(txRequest);
const tx: Partial<Transaction> = {};

const tx: any = {};
if (!req.gasLimit && req.gas !== undefined) {
req.gasLimit = req.gas;
}

['from', 'to'].forEach(key => {
if (values[key] === null || values[key] === undefined) {
return;
}
tx[key] = Promise.resolve(values[key]).then(v => (v ? v : null));
['from', 'to', 'type'].forEach(key => {
tx[key] = req[key];
});

['gasLimit', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'value'].forEach(key => {
if (values[key] === null || values[key] === undefined) {
return;
}
tx[key] = Promise.resolve(values[key]).then(v => (v ? BigNumber.from(v) : null));
});

['type'].forEach(key => {
if (values[key] === null || values[key] === undefined) {
return;
}
tx[key] = Promise.resolve(values[key]).then(v => (v !== null || v !== undefined ? v : null));
tx[key] = req[key] && BigNumber.from(req[key]);
});

if (values.accessList) {
tx.accessList = accessListify(values.accessList);
}
tx.accessList = req.accessList && accessListify(req.accessList);

['data'].forEach(key => {
if (values[key] === null || values[key] === undefined) {
return;
}
tx[key] = Promise.resolve(values[key]).then(v => (v ? hexlify(v) : null));
});
tx.data = req.data && hexlify(req.data);

return formatter.transactionRequest(await resolveProperties(tx));
return formatter.transactionRequest(tx);
};

export const encodeGasLimit = (
Expand Down
65 changes: 59 additions & 6 deletions packages/eth-rpc-adapter/src/__tests__/e2e/errors.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Contract, Wallet } from 'ethers';
import { JsonRpcProvider } from '@ethersproject/providers';
import { RPC_URL, rpcGet } from './utils';
import { Wallet } from 'ethers';
import { describe, expect, it } from 'vitest';
import ADDRESS from '@acala-network/contracts/utils/MandalaAddress';
import TokenABI from '@acala-network/contracts/build/contracts/Token.json';
import axios from 'axios';

const eth_getEthGas = rpcGet('eth_getEthGas', RPC_URL);
const eth_sendRawTransaction = rpcGet('eth_sendRawTransaction', RPC_URL);
const eth_chainId = rpcGet('eth_chainId', RPC_URL);
const eth_call = rpcGet('eth_call', RPC_URL);
import { AcalaJsonRpcProvider } from '@acala-network/eth-providers';
import { RPC_URL, eth_call, eth_chainId, eth_estimateGas, eth_getEthGas, eth_sendRawTransaction } from './utils';
import { evmAccounts } from './consts';
import { parseEther } from 'ethers/lib/utils';

describe('errors', () => {
const POOR_ACCOUNT = '0xa872f6cbd25a0e04a08b1e21098017a9e6194d101d75e13111f71410c59cd570';
Expand Down Expand Up @@ -113,4 +114,56 @@ describe('errors', () => {
message: 'execution reverted: ERC20: insufficient allowance',
});
});

describe.only('throws outOfGas error when gaslimit too small', () => {
const provider = new AcalaJsonRpcProvider(RPC_URL);
const wallet = new Wallet(evmAccounts[0].privateKey, provider);
const aca = new Contract(ADDRESS.ACA, TokenABI.abi, wallet);

it('when eth call', async () => {
const tx = await aca.populateTransaction.transfer(evmAccounts[1].evmAddress, parseEther('1.32'));
const { error } = (await eth_call([{ ...tx, gas: 0 }, 'latest'])).data;

expect(error).toMatchInlineSnapshot(`
{
"code": -32603,
"message": "execution error: outOfGas",
}
`);
});

it('when estimateGas', async () => {
const tx = await aca.populateTransaction.transfer(evmAccounts[1].evmAddress, parseEther('1.32'));
const { error } = (await eth_estimateGas([{ ...tx, gas: 0 }, 'latest'])).data;

expect(error).toMatchInlineSnapshot(`
{
"code": -32603,
"message": "execution error: outOfGas",
}
`);
});

it('when send raw transaction', async () => {
const tx = await aca.populateTransaction.transfer(evmAccounts[1].evmAddress, parseEther('1.32'));
const signedTx = await wallet.signTransaction({ ...tx, gasLimit: 0 });

const { error } = (await eth_sendRawTransaction([signedTx])).data;
expect(error).toMatchInlineSnapshot(`
{
"code": -32603,
"message": "execution error: outOfGas",
}
`);
});

it('when send transaction with ethers', async () => {
try {
await aca.transfer(evmAccounts[1].evmAddress, parseEther('1.32'), { gasLimit: 0 });
expect.fail('did not throw an err');
} catch (err) {
expect((err as any).error).toMatchInlineSnapshot('[Error: execution error: outOfGas]');
}
});
});
});

0 comments on commit efcec17

Please sign in to comment.