Skip to content

Commit be71cd6

Browse files
authored
decode eth gas params before estimate gas (#917)
* decode eth gas params before estimate gas * fix * fix test * fix * polish * polish * polish * add tests * check gasprice * polish
1 parent d218d40 commit be71cd6

File tree

6 files changed

+98
-29
lines changed

6 files changed

+98
-29
lines changed

examples/waffle/dex/test/Dex.test.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ describe('Dex', () => {
8181
expect(
8282
await dex.swapWithExactSupply([ADDRESS.ACA, ADDRESS.AUSD], 1_000_000_000_000, 1, {
8383
value: ethers.utils.parseEther('1'),
84-
gasLimit: 2_000_000
8584
})
8685
).to.be.ok;
8786

@@ -92,7 +91,6 @@ describe('Dex', () => {
9291
expect(
9392
await dex.swapWithExactSupply([ADDRESS.ACA, ADDRESS.AUSD, ADDRESS.DOT], 1_000_000_000_000, 1, {
9493
value: ethers.utils.parseEther('1'),
95-
gasLimit: 2_000_000
9694
})
9795
).to.be.ok;
9896
let pool_3 = await dex.getLiquidityPool(ADDRESS.ACA, ADDRESS.AUSD);
@@ -114,7 +112,6 @@ describe('Dex', () => {
114112
expect(
115113
await dex.swapWithExactTarget([ADDRESS.ACA, ADDRESS.AUSD], 1, 1_000_000_000_000, {
116114
value: ethers.utils.parseEther('1'),
117-
gasLimit: 2_000_000
118115
})
119116
).to.be.ok;
120117

@@ -125,7 +122,6 @@ describe('Dex', () => {
125122
expect(
126123
await dex.swapWithExactTarget([ADDRESS.ACA, ADDRESS.AUSD, ADDRESS.DOT], 1, 1_000_000_000_000, {
127124
value: ethers.utils.parseEther('1'),
128-
gasLimit: 2_000_000
129125
})
130126
).to.be.ok;
131127
let pool_3 = await dex.getLiquidityPool(ADDRESS.AUSD, ADDRESS.DOT);
@@ -147,7 +143,6 @@ describe('Dex', () => {
147143
expect(
148144
await dex.swapWithExactTarget([ADDRESS.ACA, ADDRESS.AUSD], 1_000_000_000_000, 1_000_000_000_000, {
149145
value: ethers.utils.parseEther('1'),
150-
gasLimit: 2_000_000
151146
})
152147
).to.be.ok;
153148

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

@@ -167,7 +161,6 @@ describe('Dex', () => {
167161
expect(
168162
await dex.removeLiquidity(ADDRESS.ACA, ADDRESS.AUSD, 100_000_000_000, 0, 0, {
169163
value: ethers.utils.parseEther('1'),
170-
gasLimit: 2_000_000
171164
})
172165
).to.be.ok;
173166
});

examples/waffle/erc20/test/LP-ACA-AUSD-Token.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ describe('LP ACA-AUSD Token', () => {
2424

2525
let pool_1 = await dex.getLiquidityPool(ADDRESS.ACA, ADDRESS.AUSD);
2626
expect(
27-
await dex.addLiquidity(ADDRESS.ACA, ADDRESS.AUSD, 1_000_000_000_000, 1_000_000_000_000, 0, {
28-
gasLimit: 2_000_000
29-
})
27+
await dex.addLiquidity(ADDRESS.ACA, ADDRESS.AUSD, 1_000_000_000_000, 1_000_000_000_000, 0)
3028
).to.be.ok;
3129
let pool_2 = await dex.getLiquidityPool(ADDRESS.ACA, ADDRESS.AUSD);
3230
expect(pool_2[1].sub(pool_1[1])).to.equal(1_000_000_000_000);

examples/waffle/scheduler/test/Scheduler.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ describe('Schedule', () => {
122122
wallet,
123123
RecurringPayment,
124124
[3, 4, ethers.utils.parseEther('1000'), transferTo],
125-
{ gasLimit: 2_000_000 }
126125
);
127126
// ACA as erc20 decimals is 12
128127
await erc20.transfer(recurringPayment.address, dollar.mul(5000));
@@ -178,7 +177,6 @@ describe('Schedule', () => {
178177

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

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

228-
await subscriberContract.unsubscribe({ gasLimit: 2_000_000 });
225+
await subscriberContract.unsubscribe();
229226

230227
current_block_number = await provider.getBlockNumber();
231228
await nextBlock(provider);

packages/eth-providers/src/base-provider.ts

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,23 +1086,43 @@ export abstract class BaseProvider extends AbstractProvider {
10861086
* @returns The estimated resources used by this transaction
10871087
*/
10881088
estimateResources = async (
1089-
transaction: Deferrable<TransactionRequest>,
1089+
transaction: Deferrable<TxRequestWithGas>,
10901090
blockHash?: string,
10911091
): Promise<{
10921092
usedGas: BigNumber;
10931093
gasLimit: BigNumber;
10941094
usedStorage: BigNumber;
10951095
}> => {
1096-
const MAX_GAS_LIMIT = BLOCK_GAS_LIMIT * 10; // capped at 10x (1000%) the current block gas limit
1097-
const MIN_GAS_LIMIT = 21000;
1098-
const STORAGE_LIMIT = BLOCK_STORAGE_LIMIT;
1096+
const ethTx = await getTransactionRequest(transaction);
1097+
1098+
const minGasLimit = 21000;
1099+
let maxGasLimit = BLOCK_GAS_LIMIT * 10;
1100+
let storageLimit = BLOCK_STORAGE_LIMIT;
1101+
1102+
// if user explicitly provides gasLimit override, decode it and use as max values
1103+
if (ethTx.gasLimit !== undefined || ethTx.gasPrice !== undefined) {
1104+
const substrateGasParams = decodeEthGas({
1105+
gasLimit: ethTx.gasLimit ?? BigNumber.from(199999), // use max storage limit and gas limit
1106+
gasPrice: ethTx.gasPrice ?? await this.getGasPrice(),
1107+
});
1108+
1109+
if (substrateGasParams.validUntil < await this.getBlockNumber()) {
1110+
return logger.throwError(
1111+
'invalid gasPrice, which corresponds to a too low validUntil',
1112+
Logger.errors.CALL_EXCEPTION,
1113+
transaction
1114+
);
1115+
}
1116+
1117+
maxGasLimit = Number(substrateGasParams.gasLimit);
1118+
storageLimit = Number(substrateGasParams.storageLimit);
1119+
}
10991120

1100-
const _txRequest = await getTransactionRequest(transaction);
11011121
const txRequest = {
1102-
..._txRequest,
1103-
value: BigNumber.isBigNumber(_txRequest.value) ? _txRequest.value.toBigInt() : _txRequest.value,
1104-
gasLimit: _txRequest.gasLimit?.toBigInt() ?? MAX_GAS_LIMIT,
1105-
storageLimit: STORAGE_LIMIT,
1122+
...ethTx,
1123+
gasLimit: maxGasLimit,
1124+
storageLimit,
1125+
value: BigNumber.isBigNumber(ethTx.value) ? ethTx.value.toBigInt() : ethTx.value,
11061126
};
11071127

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

11281148
if (!gasAlreadyWorks) {
11291149
// need to binary search the best passing gasLimit
1130-
let lowest = MIN_GAS_LIMIT;
1131-
let highest = MAX_GAS_LIMIT;
1150+
let lowest = minGasLimit;
1151+
let highest = maxGasLimit;
11321152
let mid = Math.min(usedGas * 3, Math.floor((lowest + highest) / 2));
11331153
let prevHighest = highest;
11341154
while (highest - lowest > 1) {
@@ -1209,9 +1229,7 @@ export abstract class BaseProvider extends AbstractProvider {
12091229
return accountInfo.unwrap().contractInfo;
12101230
};
12111231

1212-
_getSubstrateGasParams = (
1213-
ethTx: Partial<AcalaEvmTX>
1214-
): {
1232+
_getSubstrateGasParams = (ethTx: Partial<AcalaEvmTX>): {
12151233
gasLimit: bigint;
12161234
storageLimit: bigint;
12171235
validUntil: bigint;

packages/eth-rpc-adapter/src/__tests__/e2e/endpoint.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import {
6767
deployGasMonster,
6868
toDeterministic,
6969
waitForHeight,
70+
eth_estimateGas,
7071
} from './utils';
7172

7273
import {
@@ -2015,5 +2016,64 @@ describe('endpoint', () => {
20152016

20162017
await (await gm.run()).wait(); // make sure running has no error
20172018
});
2019+
2020+
describe('works with gas overrides', () => {
2021+
let token: Contract;
2022+
2023+
beforeAll(async () => {
2024+
token = await deployErc20(wallet);
2025+
});
2026+
2027+
it('works with valid gas overrides', async () => {
2028+
const tx = await token.populateTransaction.transfer(evmAccounts[1].evmAddress, 1000);
2029+
const { gasLimit: realGasLimit, gasPrice } = await estimateGas(tx);
2030+
2031+
const resps = await Promise.all([
2032+
eth_estimateGas([{ ...tx, gasPrice, gas: realGasLimit }]),
2033+
eth_estimateGas([{ ...tx, gasPrice }]),
2034+
eth_estimateGas([{ ...tx, gas: realGasLimit }]),
2035+
2036+
eth_estimateGas([{ ...tx, gas: 101520 }]), // increase gas and storagelimits
2037+
eth_estimateGas([{ ...tx, gasPrice: parseUnits('234.001298752', 'gwei') }]), // increase tip and valid until
2038+
eth_estimateGas([{ ...tx, gasPrice: parseUnits('321.001000000', 'gwei'), gas: 102518 }]), // increase everything
2039+
]);
2040+
2041+
const errs = resps.map(r => r.data.error);
2042+
if (errs.some(e => e !== undefined)) {
2043+
expect.fail(`some of the requests failed: ${JSON.stringify(errs, null, 2) }`);
2044+
}
2045+
2046+
const results = resps.map(r => r.data.result)
2047+
.slice(0, 4); // last request has slightly different estimated gaslimit since it has different gasPrice
2048+
if (results.some(e => BigInt(e) !== realGasLimit.toBigInt())) {
2049+
expect.fail(`some of the requests returned wrong gasLimit: ${JSON.stringify(results, null, 2) }`);
2050+
}
2051+
});
2052+
2053+
it('throws error with invalid gas overrides', async () => {
2054+
const tx = await token.populateTransaction.transfer(Wallet.createRandom().address, 100000);
2055+
2056+
const resps = await Promise.all([
2057+
eth_estimateGas([{ ...tx, gasPrice: parseUnits('100.000000001', 'gwei') }]), // too low valid until
2058+
eth_estimateGas([{ ...tx, gasPrice: parseUnits('38.0000090000', 'gwei') }]), // invalid gasPrice
2059+
eth_estimateGas([{ ...tx, gas: 20100 }]), // too low storagelimit
2060+
eth_estimateGas([{ ...tx, gas: 200109 }]), // too low gaslimit
2061+
eth_estimateGas([{ ...tx, gas: 200301 }]), // too low gaslimit + storagelimit
2062+
eth_estimateGas([{ ...tx, gasPrice: parseUnits('100.000000001', 'gwei'), gas: 200301 }]), // too low everything
2063+
]);
2064+
2065+
const errs = resps.map(r => r.data.error?.message);
2066+
if (errs.some(e => e === undefined)) {
2067+
expect.fail(`some of the requests didn't fail when it should: ${JSON.stringify(errs, null, 2) }`);
2068+
}
2069+
2070+
expect(errs[0]).to.contain('Error: invalid gasPrice');
2071+
expect(errs[1]).to.contain('Error: invalid gasPrice');
2072+
expect(errs[2]).to.contain('evm.OutOfStorage');
2073+
expect(errs[3]).to.contain('execution error: outOfGas');
2074+
expect(errs[4]).to.contain('evm.OutOfStorage');
2075+
expect(errs[5]).to.contain('Error: invalid gasPrice');
2076+
});
2077+
});
20182078
});
20192079
});

packages/eth-rpc-adapter/src/__tests__/e2e/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ export const eth_getTransactionByHash_karura = rpcGet('eth_getTransactionByHash'
7272
export const eth_getBlockByNumber_karura = rpcGet('eth_getBlockByNumber', KARURA_ETH_RPC_URL);
7373
export const eth_getStorageAt_karura = rpcGet('eth_getStorageAt', KARURA_ETH_RPC_URL);
7474

75-
export const estimateGas = async (tx: TransactionRequest, blockTag?: BlockTagish) => {
75+
export const estimateGas = async (
76+
tx: TransactionRequest,
77+
blockTag?: BlockTagish
78+
) => {
7679
const gasPrice = (await eth_gasPrice([])).data.result;
7780
const res = await eth_estimateGas([{ ...tx, gasPrice }, blockTag]);
7881
if (res.data.error) {

0 commit comments

Comments
 (0)