UNPKG

@owlprotocol/contracts-account-abstraction

Version:

ERC4337 Account Abstraction support for deploying relevant smart contracts and interacting with UserOps.

130 lines (129 loc) 4.83 kB
import { encodeAbiParameters, keccak256, parseEther, toHex, zeroAddress } from "viem"; import { getChainId } from "viem/actions"; import { getAction } from "viem/utils"; import { optimismSepolia, optimism, base, baseSepolia, celoAlfajores, seiDevnet, seiTestnet, sei, celo } from "viem/chains"; import { getExecutionResult } from "./simulateHandleOp.js"; import { calcPreVerificationGas } from "./calcPreVerificationGas.js"; import { getSupportedEntryPoints } from "./getSupportedEntryPoints.js"; import { calcVerificationGasAndCallGasLimit } from "../../gasestimation/calcVerificationGasAndCallGasLimit.js"; import { dummySignature, encodeUserOp } from "../../models/UserOperation.js"; import { toPackedUserOperation } from "../../models/PackedUserOperation.js"; import { ENTRYPOINT_ADDRESS_V07 } from "../../constants.js"; const entryPointDepositSlot = 0; const getPaymasterSlot = (paymaster) => keccak256(encodeAbiParameters([{ type: "address" }, { type: "uint8" }], [paymaster, entryPointDepositSlot])); async function estimateUserOperationGas(client, parameters) { const { factory, factoryData, paymaster } = parameters; const { entryPointSimulationsAddress } = client; const supportedEntryPoints = await getAction(client, getSupportedEntryPoints, "getSupportedEntryPoints")({}); const entryPointAddress = supportedEntryPoints[0]; const chainId = client.chain?.id ?? await getAction(client, getChainId, "getChainId")({}); const userOperation = { ...parameters, factory: factory ?? zeroAddress, factoryData: factoryData ?? "0x", signature: dummySignature, // initial dummy gas values // populated first, based on byte-size of the user op, this is the gas cost of encoding the user op data before any contract execution preVerificationGas: 0n, // gas cost of verifying the user op (eg. smart account signature check) verificationGasLimit: 10000000n, // gas cost of executing the user op callGasLimit: 10000000n, maxFeePerGas: parameters.maxFeePerGas, maxPriorityFeePerGas: parameters.maxFeePerGas }; if (userOperation.paymaster) { userOperation.paymasterVerificationGasLimit = 5000000n; userOperation.paymasterPostOpGasLimit = 2000000n; } let stateOverride; if (paymaster) { const paymasterSlot = getPaymasterSlot(paymaster); stateOverride = { address: ENTRYPOINT_ADDRESS_V07, stateDiff: [ { slot: paymasterSlot, value: toHex(parseEther("1000000"), { size: 32 }) } ] }; } else { stateOverride = { // Either the paymaster or the smart account would pay. Override accordingly address: paymaster ?? userOperation.sender, balance: parseEther("1000000") }; } const executionResult = await getExecutionResult(client, { packedUserOperation: toPackedUserOperation(encodeUserOp(userOperation)), entryPoint: entryPointAddress, entryPointSimulationsAddress, stateOverride }); let { verificationGasLimit, callGasLimit } = calcVerificationGasAndCallGasLimit( userOperation, executionResult.data.executionResult, chainId, executionResult.data.callDataResult ); verificationGasLimit = verificationGasLimit * 120n / 100n; if (chainId === base.id || chainId === baseSepolia.id) { callGasLimit += 10000n; } if (chainId === base.id || chainId === optimism.id || chainId === baseSepolia.id || chainId === optimismSepolia.id) { const currCallGasLimit = callGasLimit; callGasLimit = currCallGasLimit > 120000n ? currCallGasLimit : 120000n; } if (chainId === celoAlfajores.id || chainId === celo.id || chainId === sei.id || chainId === seiDevnet.id || chainId === seiTestnet.id) { verificationGasLimit = 1000000n; callGasLimit = 1000000n; } if (userOperation.callData === "0x") { callGasLimit = 0n; } callGasLimit = callGasLimit * 110n / 100n; if (userOperation.paymaster != null) { userOperation.paymasterVerificationGasLimit = 100000n; userOperation.paymasterPostOpGasLimit = 100000n; } let preVerificationGas = await calcPreVerificationGas(client, { packedUserOperation: toPackedUserOperation(encodeUserOp(userOperation)), //TODO: Rename to entryPointAddress entryPoint: entryPointAddress }); preVerificationGas = preVerificationGas * 110n / 100n; const userOpGas = { preVerificationGas, verificationGasLimit, callGasLimit }; if (userOperation.paymasterVerificationGasLimit) { userOpGas.paymasterVerificationGasLimit = userOperation.paymasterVerificationGasLimit; } if (userOperation.paymasterPostOpGasLimit) { userOpGas.paymasterPostOpGasLimit = userOperation.paymasterPostOpGasLimit; } return userOpGas; } export { entryPointDepositSlot, estimateUserOperationGas, getPaymasterSlot };