Skip to content

Commit

Permalink
estimateGas supports blocktag (#894)
Browse files Browse the repository at this point in the history
* estimateGas supports blocktag

* fix

* fix

* fix
  • Loading branch information
shunjizhan authored Dec 4, 2023
1 parent da83d1f commit 0451b70
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 12 deletions.
28 changes: 20 additions & 8 deletions packages/eth-providers/src/base-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -928,8 +928,15 @@ export abstract class BaseProvider extends AbstractProvider {
* @param transaction The transaction to estimate the gas of
* @returns The estimated gas used by this transaction
*/
estimateGas = async (transaction: Deferrable<TransactionRequest>): Promise<BigNumber> => {
const { usedGas, gasLimit, usedStorage } = await this.estimateResources(transaction);
estimateGas = async (
transaction: Deferrable<TransactionRequest>,
blockTag?: BlockTag | Promise<BlockTag>
): Promise<BigNumber> => {
const blockHash = blockTag && blockTag !== 'latest'
? await this._getBlockHash(blockTag)
: undefined; // if blockTag is latest, avoid explicit blockhash for better performance

const { usedGas, gasLimit, usedStorage } = await this.estimateResources(transaction, blockHash);

const tx = await resolveProperties(transaction);
const data = tx.data?.toString() ?? '0x';
Expand All @@ -948,7 +955,7 @@ export abstract class BaseProvider extends AbstractProvider {
? this.api.tx.evm.call(...callParams)
: this.api.tx.evm.create(...createParams);

let txFee = await this._estimateGasCost(extrinsic);
let txFee = await this._estimateGasCost(extrinsic, blockHash);
txFee = txFee.mul(gasLimit).div(usedGas); // scale it to the same ratio when estimate passing gasLimit

if (usedStorage.gt(0)) {
Expand All @@ -965,9 +972,13 @@ export abstract class BaseProvider extends AbstractProvider {
return encodeGasLimit(txFee, gasPrice, gasLimit, usedStorage, isTokenTransfer);
};

_estimateGasCost = async (extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>) => {
_estimateGasCost = async (
extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>,
at?: string,
) => {
const apiAt = await this.api.at(at ?? await this.bestBlockHash);

const u8a = extrinsic.toU8a();
const apiAt = await this.api.at(await this.bestBlockHash);
const lenIncreaseAfterSignature = 100; // approximate length increase after signature
const feeDetails = await apiAt.call.transactionPaymentApi.queryFeeDetails(
u8a,
Expand Down Expand Up @@ -1098,7 +1109,8 @@ export abstract class BaseProvider extends AbstractProvider {
* @returns The estimated resources used by this transaction
*/
estimateResources = async (
transaction: Deferrable<TransactionRequest>
transaction: Deferrable<TransactionRequest>,
blockHash?: string,
): Promise<{
usedGas: BigNumber;
gasLimit: BigNumber;
Expand All @@ -1116,7 +1128,7 @@ export abstract class BaseProvider extends AbstractProvider {
storageLimit: STORAGE_LIMIT,
};

const gasInfo = await this._ethCall(txRequest);
const gasInfo = await this._ethCall(txRequest, blockHash);
const usedGas = BigNumber.from(gasInfo.used_gas).toNumber();
const usedStorage = gasInfo.used_storage;

Expand Down Expand Up @@ -1523,7 +1535,7 @@ export abstract class BaseProvider extends AbstractProvider {

switch (blockTag) {
case 'pending': {
return logger.throwError('pending tag not implemented', Logger.errors.UNSUPPORTED_OPERATION);
return logger.throwError('pending tag not supported', Logger.errors.UNSUPPORTED_OPERATION);
}
case 'latest': {
return this.safeMode ? this.finalizedBlockHash : this.bestBlockHash;
Expand Down
3 changes: 3 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 @@ -1940,6 +1940,9 @@ describe('endpoint', () => {
const { gasLimit } = await estimateGas(tx);
const bbb = (gasLimit.toNumber() % 100000) / 100;

const { gasLimit: gasLimitWithBlockTag } = await estimateGas(tx, 'latest');
expect(gasLimitWithBlockTag.toBigInt()).to.equal(gasLimit.toBigInt());

// should be passing gasLimit instead of usedGas
expect(bbb).to.gt(GAS_MONSTER_GAS_REQUIRED / 30000);

Expand Down
5 changes: 3 additions & 2 deletions packages/eth-rpc-adapter/src/__tests__/e2e/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BigNumber, ContractFactory, Signer } from 'ethers';
import { BlockTagish } from '@acala-network/eth-providers';
import { Log, TransactionRequest } from '@ethersproject/abstract-provider';
import { expect } from 'vitest';
import { hexValue } from '@ethersproject/bytes';
Expand Down Expand Up @@ -71,9 +72,9 @@ 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) => {
export const estimateGas = async (tx: TransactionRequest, blockTag?: BlockTagish) => {
const gasPrice = (await eth_gasPrice([])).data.result;
const res = await eth_estimateGas([{ ...tx, gasPrice }]);
const res = await eth_estimateGas([{ ...tx, gasPrice }, blockTag]);
if (res.data.error) {
throw new Error(res.data.error.message);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/eth-rpc-adapter/src/eip1193-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ class Eip1193BridgeImpl {
* @returns GAS USED - the amount of gas used.
*/
async eth_estimateGas(params: any[]): Promise<string> {
validate([{ type: 'transaction' }], params);
const val = await this.#provider.estimateGas(params[0]);
validate([{ type: 'transaction' }, { type: 'block?' }], params);
const val = await this.#provider.estimateGas(params[0], params[1]);
return hexValue(val);
}

Expand Down
5 changes: 5 additions & 0 deletions packages/eth-rpc-adapter/src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type Schema = {
type:
| 'address'
| 'block'
| 'block?'
| 'transaction'
| 'blockHash'
| 'trasactionHash'
Expand Down Expand Up @@ -148,6 +149,10 @@ export const validate = (schema: Schema, data: unknown[]) => {
validateBlock(data[i]);
break;
}
case 'block?': {
data[i] && validateBlock(data[i]);
break;
}
case 'transaction': {
validateTransaction(data[i]);
break;
Expand Down

0 comments on commit 0451b70

Please sign in to comment.