Skip to content

Commit

Permalink
enhancement: contract based binary search for call gas limit estimate
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilkumar1612 committed Jul 7, 2024
1 parent 44f7ef7 commit b645781
Show file tree
Hide file tree
Showing 10 changed files with 668 additions and 23 deletions.
5 changes: 5 additions & 0 deletions packages/executor/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,8 @@ export interface KnownEntities {
accounts: string[];
otherEntities: string[];
}

export interface ExecutionResultAndCallGasLimit {
returnInfo: ExecutionResult;
callGasLimit: BigNumber;
}
14 changes: 7 additions & 7 deletions packages/executor/src/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export class Eth {
userOp.signature = ECDSA_DUMMY_SIGNATURE;
}

const returnInfo = await this.userOpValidationService.validateForEstimation(
let {returnInfo, callGasLimit} = await this.userOpValidationService.validateForEstimation(
userOp,
entryPoint
);
Expand All @@ -194,12 +194,12 @@ export class Eth {

// calculate callGasLimit based on paid fee
const { cglMarkup } = this.config;
const totalGas: BigNumber = BigNumber.from(paid).div(userOp.maxFeePerGas);
let callGasLimit = totalGas
.sub(preOpGas)
.mul(10000 + this.config.cglMarkupPercent)
.div(10000) // % markup
.add(cglMarkup || 0);
// const totalGas: BigNumber = BigNumber.from(paid).div(userOp.maxFeePerGas);
// let callGasLimit = totalGas
// .sub(preOpGas)
// .mul(10000 + this.config.cglMarkupPercent)
// .div(10000) // % markup
// .add(cglMarkup || 0);

if (callGasLimit.lt(cglMarkup)) {
callGasLimit = BigNumber.from(cglMarkup);
Expand Down
2 changes: 2 additions & 0 deletions packages/executor/src/services/EntryPointService/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export const DefaultGasOverheads = {
bundleSize: 1,
sigSize: 65,
};

export const IMPLEMENTATION_ADDRESS_MARKER = "0xA13dB4eCfbce0586E57D1AeE224FbE64706E8cd3";
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ export function decodeRevertReason(
}
}

export function decodeTargetData(data: string) {
try {
const methodSig = data.slice(0, 10);
const dataParams = "0x" + data.slice(10);
if(methodSig === "0x8c83589a") {
const res = ethers.utils.defaultAbiCoder.decode(
["uint256", "uint256"],
dataParams
);
return res;
}
throw Error("Error decoding target data");
} catch (error) {
throw error;
}
}

// not sure why ethers fail to decode revert reasons, not even "Error()" (and obviously, not custom errors)
export function rethrowWithRevertReason(e: Error): never {
throw new Error(decodeRevertReason(e, false) as any);
Expand Down
41 changes: 35 additions & 6 deletions packages/executor/src/services/EntryPointService/versions/0.0.7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,21 @@ import {
StakeInfo,
UserOpValidationResult,
} from "../../../interfaces";
import { DefaultGasOverheads } from "../constants";
import { DefaultGasOverheads, IMPLEMENTATION_ADDRESS_MARKER } from "../constants";
import { StateOverrides } from "../interfaces";
import { decodeRevertReason } from "../utils/decodeRevertReason";
import { decodeRevertReason, decodeTargetData } from "../utils/decodeRevertReason";
import { getUserOpGasLimit } from "../../BundlingService/utils";
import { IEntryPointService } from "./base";
import {
CallGasEstimationProxy__factory,
_deployedBytecode as _callGasEstimationProxyDeployedBytecode
} from "@skandha/types/lib/contracts/EPv7/factories/core/CallGasEstimationProxy__factory";
import {
CallGasEstimationProxy
} from "@skandha/types/lib/contracts/EPv7/core/CallGasEstimationProxy";

const entryPointSimulations = IEntryPointSimulations__factory.createInterface();
const callGasEstimateProxy = CallGasEstimationProxy__factory.createInterface();

export class EntryPointV7Service implements IEntryPointService {
contract: EntryPoint;
Expand Down Expand Up @@ -70,11 +78,22 @@ export class EntryPointV7Service implements IEntryPointService {
this.networkConfig.estimationGasLimit
)
: undefined;

const estimateCallGasArgs: CallGasEstimationProxy.EstimateCallGasArgsStruct = {
userOp: packUserOp(userOp),
isContinuation: true,
maxGas: "100000",
minGas: "21000",
rounding: "2"
}

const [data, stateOverrides] = this.encodeSimulateHandleOp(
const [data] = this.encodeSimulateHandleOp(
userOp,
AddressZero,
BytesZero
this.address,
callGasEstimateProxy.encodeFunctionData(
"estimateCallGas",
[estimateCallGasArgs]
)
);

const tx: providers.TransactionRequest = {
Expand All @@ -83,6 +102,15 @@ export class EntryPointV7Service implements IEntryPointService {
gasLimit,
};

const stateOverrides: StateOverrides = {
[this.address]: {
code: _callGasEstimationProxyDeployedBytecode
},
[IMPLEMENTATION_ADDRESS_MARKER]: {
code: _deployedBytecode
}
}

try {
const simulationResult = await this.provider.send("eth_call", [
tx,
Expand All @@ -93,7 +121,8 @@ export class EntryPointV7Service implements IEntryPointService {
"simulateHandleOp",
simulationResult
);
return res[0];
const [callGasLimit] = decodeTargetData(res[0].targetResult);
return {returnInfo: res[0], callGasLimit: callGasLimit};
} catch (error: any) {
console.log(error);
const err = decodeRevertReason(error);
Expand Down
3 changes: 2 additions & 1 deletion packages/executor/src/services/UserOpValidation/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { UserOperation } from "@skandha/types/lib/contracts/UserOperation";
import { Config } from "../../config";
import {
ExecutionResult,
ExecutionResultAndCallGasLimit,
NetworkConfig,
UserOpValidationResult,
} from "../../interfaces";
Expand Down Expand Up @@ -62,7 +63,7 @@ export class UserOpValidationService {
async validateForEstimation(
userOp: UserOperation,
entryPoint: string
): Promise<ExecutionResult> {
): Promise<ExecutionResultAndCallGasLimit> {
return await this.estimationService.estimateUserOp(userOp, entryPoint);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { providers } from "ethers";
import { Logger } from "@skandha/types/lib";
import { UserOperation } from "@skandha/types/lib/contracts/UserOperation";
import { ExecutionResult } from "../../../interfaces";
import { ExecutionResultAndCallGasLimit } from "../../../interfaces";
import { EntryPointService } from "../../EntryPointService";
import { mergeValidationDataValues } from "../../EntryPointService/utils";

Expand All @@ -15,8 +15,8 @@ export class EstimationService {
async estimateUserOp(
userOp: UserOperation,
entryPoint: string
): Promise<ExecutionResult> {
const returnInfo = await this.entryPointService.simulateHandleOp(
): Promise<ExecutionResultAndCallGasLimit> {
const { returnInfo, callGasLimit } = await this.entryPointService.simulateHandleOp(
entryPoint,
userOp
);
Expand All @@ -25,12 +25,15 @@ export class EstimationService {
returnInfo.paymasterValidationData
);
return {
preOpGas: returnInfo.preOpGas,
paid: returnInfo.paid,
validAfter: validAfter,
validUntil: validUntil,
targetSuccess: returnInfo.targetSuccess,
targetResult: returnInfo.targetResult,
returnInfo: {
preOpGas: returnInfo.preOpGas,
paid: returnInfo.paid,
validAfter: validAfter,
validUntil: validUntil,
targetSuccess: returnInfo.targetSuccess,
targetResult: returnInfo.targetResult,
},
callGasLimit
};
}
}
Loading

0 comments on commit b645781

Please sign in to comment.