Created
June 13, 2023 12:02
-
-
Save arcticfloyd1984/b96b2c1f2408056fce002490368cf347 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static async calcPreVerificationGas( | |
userOp: UserOperationType, | |
chainId: number, | |
entryPointContract: ethers.Contract, | |
overheads?: Partial<DefaultGasOverheadType>, | |
) { | |
const { defaultGasOverheads } = config; | |
const ov = { ...defaultGasOverheads, ...(overheads ?? {}) }; | |
const p: UserOperationType = { | |
...userOp, | |
// dummy values, in case the UserOp is incomplete. | |
callGasLimit: BigNumber.from('0'), | |
maxFeePerGas: BigNumber.from('0'), | |
maxPriorityFeePerGas: BigNumber.from('0'), | |
verificationGasLimit: '0xF4240', // 1000000 | |
preVerificationGas: 21000, // dummy value, just for calldata cost | |
signature: hexlify(Buffer.alloc(ov.sigSize, 1)), // dummy signature | |
} as any; | |
const packed = arrayify(packUserOp(p, false)); | |
const callDataCost = packed | |
.map((x) => (x === 0 ? ov.zeroByte : ov.nonZeroByte)) | |
.reduce((sum, x) => sum + x); | |
let ret = Math.round( | |
callDataCost | |
+ ov.fixed / ov.bundleSize | |
+ ov.perUserOp | |
+ ov.perUserOpWord * packed.length, | |
); | |
// calculate offset for Arbitrum | |
if ( | |
chainId === BLOCKCHAINS.ARBITRUM_GOERLI_TESTNET | |
|| chainId === BLOCKCHAINS.ARBITRUM_NOVA_MAINNET | |
|| chainId === BLOCKCHAINS.ARBITRUM_ONE_MAINNET | |
) { | |
const data = await calcGasPrice( | |
entryPointContract.address, | |
userOp, | |
chainId, | |
); | |
ret += data; | |
} | |
return ret; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async estimateCreationGas( | |
entryPointAddress: string, | |
initCode?: string, | |
): Promise<number> { | |
if (initCode == null || initCode === '0x') return 0; | |
const deployerAddress = initCode.substring(0, 42); | |
const deployerCallData = `0x${initCode.substring(42)}`; | |
return this.networkService | |
.estimateCallGas(entryPointAddress, deployerAddress, deployerCallData) | |
.then((callGasLimitResponse) => callGasLimitResponse.toNumber()) | |
.catch((error) => { | |
const message = error.message.match(/reason="(.*?)"/)?.at(1) ?? 'execution reverted'; | |
log.info(`message: ${JSON.stringify(message)}`); | |
return 0; | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async estimateUserOperationGas( | |
estimateUserOperationGasData: EstimateUserOperationGasDataType, | |
) { | |
const { userOp, entryPointContract, chainId } = estimateUserOperationGasData; | |
// 1. callGasLimit | |
let callGasLimit = 0; | |
if (userOp.callData === '0x') { | |
callGasLimit = 21000; | |
} else if (userOp.initCode !== '0x') { | |
// wallet not deployed yet and can't estimate on undeployed wallet | |
callGasLimit = 600000; | |
} else { | |
callGasLimit = await this.networkService | |
.estimateCallGas( | |
entryPointContract.address, | |
userOp.sender, | |
userOp.callData, | |
) | |
.then((callGasLimitResponse) => callGasLimitResponse.toNumber()) | |
.catch((error) => { | |
const message = error.message.match(/reason="(.*?)"/)?.at(1) ?? 'execution reverted'; | |
log.info(`message: ${JSON.stringify(message)}`); | |
return 0; | |
}); | |
} | |
// 2. verificationGasLimit | |
const initGas = await this.estimateCreationGas( | |
entryPointContract.address, | |
userOp.initCode, | |
); | |
log.info(`initGas: ${initGas} on chainId: ${chainId}`); | |
const DefaultGasLimits = { | |
validateUserOpGas: 100000, | |
validatePaymasterUserOpGas: 100000, | |
postOpGas: 10877, | |
}; | |
const validateUserOpGas = DefaultGasLimits.validatePaymasterUserOpGas | |
+ DefaultGasLimits.validateUserOpGas; | |
const { postOpGas } = DefaultGasLimits; | |
let verificationGasLimit = BigNumber.from(validateUserOpGas) | |
.add(initGas) | |
.toNumber(); | |
if (BigNumber.from(postOpGas).gt(verificationGasLimit)) { | |
verificationGasLimit = postOpGas; | |
} | |
// validAfter, validUntil, deadline | |
const entryPointStatic = entryPointContract.connect( | |
this.networkService.ethersProvider.getSigner(config.zeroAddress), | |
); | |
const fullUserOp = { | |
...userOp, | |
// default values for missing fields. | |
signature: | |
'0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b', // a valid signature | |
callGasLimit: BigNumber.from('0'), | |
maxFeePerGas: BigNumber.from('0'), | |
maxPriorityFeePerGas: BigNumber.from('0'), | |
preVerificationGas: BigNumber.from('0'), | |
verificationGasLimit, | |
}; | |
let returnInfo; | |
try { | |
const simulationResult: any = await entryPointStatic.callStatic | |
.simulateValidation(fullUserOp) | |
.catch((e: any) => e); | |
log.info(`simulationResult: ${JSON.stringify(simulationResult)}`); | |
returnInfo = BundlerSimulationAndValidationService.parseUserOpSimulationResult( | |
userOp, | |
simulationResult, | |
).returnInfo; | |
} catch (error: any) { | |
log.info(`Bundler Simulation failed: ${parseError(error)}`); | |
return { | |
code: error.code, | |
message: parseError(error), | |
data: { | |
preVerificationGas: 0, | |
verificationGasLimit: 0, | |
callGasLimit: 0, | |
validAfter: 0, | |
validUntil: 0, | |
deadline: 0, | |
}, | |
}; | |
} | |
let { validAfter, validUntil } = returnInfo; | |
validAfter = BigNumber.from(validAfter); | |
validUntil = BigNumber.from(validUntil); | |
if (validUntil === BigNumber.from(0)) { | |
validUntil = undefined; | |
} | |
if (validAfter === BigNumber.from(0)) { | |
validAfter = undefined; | |
} | |
let deadline: any; | |
if (returnInfo.deadline) { | |
deadline = BigNumber.from(returnInfo.deadline); | |
} | |
const simulateUserOp = { | |
...userOp, | |
// default values for missing fields. | |
signature: | |
'0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b', // a valid signature | |
callGasLimit: '0x00', | |
maxFeePerGas: '0x00', | |
maxPriorityFeePerGas: '0x00', | |
preVerificationGas: '0x00', | |
}; | |
// 3. preVerificationGas | |
const preVerificationGas = await BundlerSimulationAndValidationService.calcPreVerificationGas( | |
simulateUserOp, | |
chainId, | |
entryPointContract, | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment