diff --git a/packages/api-evm/package.json b/packages/api-evm/package.json index e1c295e67..5a4fd205f 100644 --- a/packages/api-evm/package.json +++ b/packages/api-evm/package.json @@ -28,6 +28,7 @@ "@mainsail/container": "workspace:*", "@mainsail/contracts": "workspace:*", "@mainsail/kernel": "workspace:*", + "dayjs": "1.11.10", "ethers": "6.11.0", "joi": "17.12.2" }, diff --git a/packages/api-evm/source/actions/eth-estimate-gas.ts b/packages/api-evm/source/actions/eth-estimate-gas.ts new file mode 100644 index 000000000..980776a5b --- /dev/null +++ b/packages/api-evm/source/actions/eth-estimate-gas.ts @@ -0,0 +1,93 @@ +import { inject, injectable, tagged } from "@mainsail/container"; +import { Contracts, Exceptions, Identifiers } from "@mainsail/contracts"; +import dayjs from "dayjs"; + +type TxData = { + from: string; + to: string; + data?: string; + gas?: string; + gasPrice?: string; + value?: string; +}; + +@injectable() +export class EthEstimateGasAction implements Contracts.Api.RPC.Action { + @inject(Identifiers.Evm.Instance) + @tagged("instance", "validator") + private readonly evm!: Contracts.Evm.Instance; + + @inject(Identifiers.Cryptography.Configuration) + protected readonly configuration!: Contracts.Crypto.Configuration; + + public readonly name: string = "eth_estimateGas"; + + public readonly schema = { + $id: `jsonRpc_${this.name}`, + + maxItems: 2, + minItems: 2, + + prefixItems: [ + { + additionalProperties: false, + properties: { + data: { $ref: "prefixedHex" }, + from: { $ref: "address" }, + gas: { $ref: "prefixedHex" }, + gasPrice: { $ref: "prefixedHex" }, + to: { $ref: "address" }, + value: { $ref: "prefixedHex" }, + }, + required: ["from", "to"], + type: "object", + }, + { $ref: "blockTag" }, + ], + + type: "array", + }; + + public async handle(parameters: [TxData, Contracts.Crypto.BlockTag]): Promise { + try { + const [data] = parameters; + + const { evmSpec, block, gas } = this.configuration.getMilestone(); + + const accountInfo = await this.evm.getAccountInfo(data.from); + + const commitKey = { height: BigInt(this.configuration.getHeight()), round: BigInt(0) }; + + const dataToProcess = { + blockContext: { + commitKey, + gasLimit: BigInt(block.maxGasLimit), + timestamp: BigInt(dayjs().valueOf()), + validatorAddress: "0x0000000000000000000000000000000000000001", + }, + caller: data.from, + data: data.data ? Buffer.from(data.data.slice(2), "hex") : Buffer.alloc(0), + gasLimit: data.gas ? BigInt(data.gas) : BigInt(block.maxGasLimit), + gasPrice: data.gasPrice ? BigInt(data.gasPrice) : BigInt(gas.minimumGasPrice), + nonce: accountInfo.nonce, + recipient: data.to, + specId: evmSpec, + txHash: "0".repeat(64), + value: data.value ? BigInt(data.value) : BigInt(0), + }; + + await this.evm.prepareNextCommit({ + commitKey, + }); + + const { receipt } = await this.evm.process(dataToProcess); + const { success, gasUsed } = receipt; + + if (success) { + return `0x${gasUsed.toString(16)}`; + } + } catch {} + + throw new Exceptions.RpcError("execution reverted"); + } +} diff --git a/packages/api-evm/source/actions/index.ts b/packages/api-evm/source/actions/index.ts index ecc60ecdb..ea306003b 100644 --- a/packages/api-evm/source/actions/index.ts +++ b/packages/api-evm/source/actions/index.ts @@ -1,5 +1,6 @@ export * from "./eth-block-number.js"; export * from "./eth-call.js"; +export * from "./eth-estimate-gas.js"; export * from "./eth-get-balance.js"; export * from "./eth-get-block-by-hash.js"; export * from "./eth-get-block-by-number.js"; diff --git a/packages/api-evm/source/service-provider.ts b/packages/api-evm/source/service-provider.ts index 83f941fa6..aa71e2df2 100644 --- a/packages/api-evm/source/service-provider.ts +++ b/packages/api-evm/source/service-provider.ts @@ -5,6 +5,7 @@ import Joi from "joi"; import { CallAction, EthBlockNumberAction, + EthEstimateGasAction, EthGetBalanceAction, EthGetBlockByHashAction, EthGetBlockByNumberAction, @@ -77,6 +78,7 @@ export class ServiceProvider extends AbstractServiceProvider { return [ this.app.resolve(CallAction), this.app.resolve(EthBlockNumberAction), + this.app.resolve(EthEstimateGasAction), this.app.resolve(EthGetBalanceAction), this.app.resolve(EthGetBlockByHashAction), this.app.resolve(EthGetBlockByNumberAction), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08561b9bb..67b7693be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -324,6 +324,9 @@ importers: '@mainsail/kernel': specifier: workspace:* version: link:../kernel + dayjs: + specifier: 1.11.10 + version: 1.11.10 ethers: specifier: 6.11.0 version: 6.11.0