Skip to content

Commit

Permalink
decode eth gas params before estimate gas (#917)
Browse files Browse the repository at this point in the history
* decode eth gas params before estimate gas

* fix

* fix test

* fix

* polish

* polish

* polish

* add tests

* check gasprice

* polish
  • Loading branch information
shunjizhan authored Jan 4, 2024
1 parent d218d40 commit be71cd6
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 29 deletions.
7 changes: 0 additions & 7 deletions examples/waffle/dex/test/Dex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ describe('Dex', () => {
expect(
await dex.swapWithExactSupply([ADDRESS.ACA, ADDRESS.AUSD], 1_000_000_000_000, 1, {
value: ethers.utils.parseEther('1'),
gasLimit: 2_000_000
})
).to.be.ok;

Expand All @@ -92,7 +91,6 @@ describe('Dex', () => {
expect(
await dex.swapWithExactSupply([ADDRESS.ACA, ADDRESS.AUSD, ADDRESS.DOT], 1_000_000_000_000, 1, {
value: ethers.utils.parseEther('1'),
gasLimit: 2_000_000
})
).to.be.ok;
let pool_3 = await dex.getLiquidityPool(ADDRESS.ACA, ADDRESS.AUSD);
Expand All @@ -114,7 +112,6 @@ describe('Dex', () => {
expect(
await dex.swapWithExactTarget([ADDRESS.ACA, ADDRESS.AUSD], 1, 1_000_000_000_000, {
value: ethers.utils.parseEther('1'),
gasLimit: 2_000_000
})
).to.be.ok;

Expand All @@ -125,7 +122,6 @@ describe('Dex', () => {
expect(
await dex.swapWithExactTarget([ADDRESS.ACA, ADDRESS.AUSD, ADDRESS.DOT], 1, 1_000_000_000_000, {
value: ethers.utils.parseEther('1'),
gasLimit: 2_000_000
})
).to.be.ok;
let pool_3 = await dex.getLiquidityPool(ADDRESS.AUSD, ADDRESS.DOT);
Expand All @@ -147,7 +143,6 @@ describe('Dex', () => {
expect(
await dex.swapWithExactTarget([ADDRESS.ACA, ADDRESS.AUSD], 1_000_000_000_000, 1_000_000_000_000, {
value: ethers.utils.parseEther('1'),
gasLimit: 2_000_000
})
).to.be.ok;

Expand All @@ -157,7 +152,6 @@ describe('Dex', () => {
expect(
await dex.addLiquidity(ADDRESS.ACA, ADDRESS.AUSD, 1_000_000_000_000, 1_000_000_000_000, 0, {
value: ethers.utils.parseEther('1'),
gasLimit: 2_000_000
})
).to.be.ok;

Expand All @@ -167,7 +161,6 @@ describe('Dex', () => {
expect(
await dex.removeLiquidity(ADDRESS.ACA, ADDRESS.AUSD, 100_000_000_000, 0, 0, {
value: ethers.utils.parseEther('1'),
gasLimit: 2_000_000
})
).to.be.ok;
});
Expand Down
4 changes: 1 addition & 3 deletions examples/waffle/erc20/test/LP-ACA-AUSD-Token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ describe('LP ACA-AUSD Token', () => {

let pool_1 = await dex.getLiquidityPool(ADDRESS.ACA, ADDRESS.AUSD);
expect(
await dex.addLiquidity(ADDRESS.ACA, ADDRESS.AUSD, 1_000_000_000_000, 1_000_000_000_000, 0, {
gasLimit: 2_000_000
})
await dex.addLiquidity(ADDRESS.ACA, ADDRESS.AUSD, 1_000_000_000_000, 1_000_000_000_000, 0)
).to.be.ok;
let pool_2 = await dex.getLiquidityPool(ADDRESS.ACA, ADDRESS.AUSD);
expect(pool_2[1].sub(pool_1[1])).to.equal(1_000_000_000_000);
Expand Down
5 changes: 1 addition & 4 deletions examples/waffle/scheduler/test/Scheduler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ describe('Schedule', () => {
wallet,
RecurringPayment,
[3, 4, ethers.utils.parseEther('1000'), transferTo],
{ gasLimit: 2_000_000 }
);
// ACA as erc20 decimals is 12
await erc20.transfer(recurringPayment.address, dollar.mul(5000));
Expand Down Expand Up @@ -178,7 +177,6 @@ describe('Schedule', () => {

const subscription = await deployContract(wallet, Subscription, [subPrice, period], {
value: ethers.utils.parseEther('5000'),
gasLimit: 2_000_000
});
if (!process.argv.includes('--with-ethereum-compatibility')) {
// If it is not called by the maintainer, developer, or contract, it needs to be deployed first
Expand All @@ -192,7 +190,6 @@ describe('Schedule', () => {
const subscriberContract = subscription.connect(subscriber as any);
await subscriberContract.subscribe({
value: ethers.utils.parseEther(formatAmount('10_000')).toString(),
gasLimit: 2_000_000
});

expect((await subscription.balanceOf(subscriberAddr)).toString()).to.equal(
Expand Down Expand Up @@ -225,7 +222,7 @@ describe('Schedule', () => {
expect((await subscription.subTokensOf(subscriberAddr)).toString()).to.equal('6');
expect((await subscription.monthsSubscribed(subscriberAddr)).toString()).to.equal('3');

await subscriberContract.unsubscribe({ gasLimit: 2_000_000 });
await subscriberContract.unsubscribe();

current_block_number = await provider.getBlockNumber();
await nextBlock(provider);
Expand Down
46 changes: 32 additions & 14 deletions packages/eth-providers/src/base-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,23 +1086,43 @@ export abstract class BaseProvider extends AbstractProvider {
* @returns The estimated resources used by this transaction
*/
estimateResources = async (
transaction: Deferrable<TransactionRequest>,
transaction: Deferrable<TxRequestWithGas>,
blockHash?: string,
): Promise<{
usedGas: BigNumber;
gasLimit: BigNumber;
usedStorage: BigNumber;
}> => {
const MAX_GAS_LIMIT = BLOCK_GAS_LIMIT * 10; // capped at 10x (1000%) the current block gas limit
const MIN_GAS_LIMIT = 21000;
const STORAGE_LIMIT = BLOCK_STORAGE_LIMIT;
const ethTx = await getTransactionRequest(transaction);

const minGasLimit = 21000;
let maxGasLimit = BLOCK_GAS_LIMIT * 10;
let storageLimit = BLOCK_STORAGE_LIMIT;

// if user explicitly provides gasLimit override, decode it and use as max values
if (ethTx.gasLimit !== undefined || ethTx.gasPrice !== undefined) {
const substrateGasParams = decodeEthGas({
gasLimit: ethTx.gasLimit ?? BigNumber.from(199999), // use max storage limit and gas limit
gasPrice: ethTx.gasPrice ?? await this.getGasPrice(),
});

if (substrateGasParams.validUntil < await this.getBlockNumber()) {
return logger.throwError(
'invalid gasPrice, which corresponds to a too low validUntil',
Logger.errors.CALL_EXCEPTION,
transaction
);
}

maxGasLimit = Number(substrateGasParams.gasLimit);
storageLimit = Number(substrateGasParams.storageLimit);
}

const _txRequest = await getTransactionRequest(transaction);
const txRequest = {
..._txRequest,
value: BigNumber.isBigNumber(_txRequest.value) ? _txRequest.value.toBigInt() : _txRequest.value,
gasLimit: _txRequest.gasLimit?.toBigInt() ?? MAX_GAS_LIMIT,
storageLimit: STORAGE_LIMIT,
...ethTx,
gasLimit: maxGasLimit,
storageLimit,
value: BigNumber.isBigNumber(ethTx.value) ? ethTx.value.toBigInt() : ethTx.value,
};

const gasInfo = await this._ethCall(txRequest, blockHash);
Expand All @@ -1127,8 +1147,8 @@ export abstract class BaseProvider extends AbstractProvider {

if (!gasAlreadyWorks) {
// need to binary search the best passing gasLimit
let lowest = MIN_GAS_LIMIT;
let highest = MAX_GAS_LIMIT;
let lowest = minGasLimit;
let highest = maxGasLimit;
let mid = Math.min(usedGas * 3, Math.floor((lowest + highest) / 2));
let prevHighest = highest;
while (highest - lowest > 1) {
Expand Down Expand Up @@ -1209,9 +1229,7 @@ export abstract class BaseProvider extends AbstractProvider {
return accountInfo.unwrap().contractInfo;
};

_getSubstrateGasParams = (
ethTx: Partial<AcalaEvmTX>
): {
_getSubstrateGasParams = (ethTx: Partial<AcalaEvmTX>): {
gasLimit: bigint;
storageLimit: bigint;
validUntil: bigint;
Expand Down
60 changes: 60 additions & 0 deletions packages/eth-rpc-adapter/src/__tests__/e2e/endpoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
deployGasMonster,
toDeterministic,
waitForHeight,
eth_estimateGas,
} from './utils';

import {
Expand Down Expand Up @@ -2015,5 +2016,64 @@ describe('endpoint', () => {

await (await gm.run()).wait(); // make sure running has no error
});

describe('works with gas overrides', () => {
let token: Contract;

beforeAll(async () => {
token = await deployErc20(wallet);
});

it('works with valid gas overrides', async () => {
const tx = await token.populateTransaction.transfer(evmAccounts[1].evmAddress, 1000);
const { gasLimit: realGasLimit, gasPrice } = await estimateGas(tx);

const resps = await Promise.all([
eth_estimateGas([{ ...tx, gasPrice, gas: realGasLimit }]),
eth_estimateGas([{ ...tx, gasPrice }]),
eth_estimateGas([{ ...tx, gas: realGasLimit }]),

eth_estimateGas([{ ...tx, gas: 101520 }]), // increase gas and storagelimits
eth_estimateGas([{ ...tx, gasPrice: parseUnits('234.001298752', 'gwei') }]), // increase tip and valid until
eth_estimateGas([{ ...tx, gasPrice: parseUnits('321.001000000', 'gwei'), gas: 102518 }]), // increase everything
]);

const errs = resps.map(r => r.data.error);
if (errs.some(e => e !== undefined)) {
expect.fail(`some of the requests failed: ${JSON.stringify(errs, null, 2) }`);
}

const results = resps.map(r => r.data.result)
.slice(0, 4); // last request has slightly different estimated gaslimit since it has different gasPrice
if (results.some(e => BigInt(e) !== realGasLimit.toBigInt())) {
expect.fail(`some of the requests returned wrong gasLimit: ${JSON.stringify(results, null, 2) }`);
}
});

it('throws error with invalid gas overrides', async () => {
const tx = await token.populateTransaction.transfer(Wallet.createRandom().address, 100000);

const resps = await Promise.all([
eth_estimateGas([{ ...tx, gasPrice: parseUnits('100.000000001', 'gwei') }]), // too low valid until
eth_estimateGas([{ ...tx, gasPrice: parseUnits('38.0000090000', 'gwei') }]), // invalid gasPrice
eth_estimateGas([{ ...tx, gas: 20100 }]), // too low storagelimit
eth_estimateGas([{ ...tx, gas: 200109 }]), // too low gaslimit
eth_estimateGas([{ ...tx, gas: 200301 }]), // too low gaslimit + storagelimit
eth_estimateGas([{ ...tx, gasPrice: parseUnits('100.000000001', 'gwei'), gas: 200301 }]), // too low everything
]);

const errs = resps.map(r => r.data.error?.message);
if (errs.some(e => e === undefined)) {
expect.fail(`some of the requests didn't fail when it should: ${JSON.stringify(errs, null, 2) }`);
}

expect(errs[0]).to.contain('Error: invalid gasPrice');
expect(errs[1]).to.contain('Error: invalid gasPrice');
expect(errs[2]).to.contain('evm.OutOfStorage');
expect(errs[3]).to.contain('execution error: outOfGas');
expect(errs[4]).to.contain('evm.OutOfStorage');
expect(errs[5]).to.contain('Error: invalid gasPrice');
});
});
});
});
5 changes: 4 additions & 1 deletion packages/eth-rpc-adapter/src/__tests__/e2e/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ export const eth_getTransactionByHash_karura = rpcGet('eth_getTransactionByHash'
export const eth_getBlockByNumber_karura = rpcGet('eth_getBlockByNumber', KARURA_ETH_RPC_URL);
export const eth_getStorageAt_karura = rpcGet('eth_getStorageAt', KARURA_ETH_RPC_URL);

export const estimateGas = async (tx: TransactionRequest, blockTag?: BlockTagish) => {
export const estimateGas = async (
tx: TransactionRequest,
blockTag?: BlockTagish
) => {
const gasPrice = (await eth_gasPrice([])).data.result;
const res = await eth_estimateGas([{ ...tx, gasPrice }, blockTag]);
if (res.data.error) {
Expand Down

0 comments on commit be71cd6

Please sign in to comment.