@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
JavaScript
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
};