UNPKG

@ensuro/account-abstraction

Version:

ERC-4337 related contracts by the Ensuro Team

142 lines (132 loc) 4.46 kB
import * as ethers from "ethers"; const { ZeroAddress } = ethers; const defaultAbiCoder = ethers.AbiCoder.defaultAbiCoder(); export function packAccountGasLimits(verificationGasLimit, callGasLimit) { return ethers.toBeHex(verificationGasLimit, 16) + ethers.toBeHex(callGasLimit, 16).slice(2); } export function packUserOp(userOp) { const accountGasLimits = packAccountGasLimits(userOp.verificationGasLimit, userOp.callGasLimit); const gasFees = packAccountGasLimits(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas); let paymasterAndData = "0x"; if (userOp.paymaster?.length >= 20 && userOp.paymaster !== ZeroAddress) { paymasterAndData = packPaymasterData( userOp.paymaster, userOp.paymasterVerificationGasLimit, userOp.paymasterPostOpGasLimit, userOp.paymasterData ); } return { sender: userOp.sender, nonce: userOp.nonce, callData: userOp.callData, accountGasLimits, initCode: userOp.initCode, preVerificationGas: userOp.preVerificationGas, gasFees, paymasterAndData, signature: userOp.signature, }; } export function packedUserOpAsArray(packedUserOp, includeSignature = true) { if (includeSignature) { return [ packedUserOp.sender, packedUserOp.nonce, packedUserOp.initCode, packedUserOp.callData, packedUserOp.accountGasLimits, packedUserOp.preVerificationGas, packedUserOp.gasFees, packedUserOp.paymasterAndData, packedUserOp.signature, ]; } else { return [ packedUserOp.sender, packedUserOp.nonce, packedUserOp.initCode, packedUserOp.callData, packedUserOp.accountGasLimits, packedUserOp.preVerificationGas, packedUserOp.gasFees, packedUserOp.paymasterAndData, ]; } } export function encodeUserOp(userOp, forSignature = true) { const packedUserOp = packUserOp(userOp); if (forSignature) { return defaultAbiCoder.encode( ["address", "uint256", "bytes32", "bytes32", "bytes32", "uint256", "bytes32", "bytes32"], [ packedUserOp.sender, packedUserOp.nonce, ethers.keccak256(packedUserOp.initCode), ethers.keccak256(packedUserOp.callData), packedUserOp.accountGasLimits, packedUserOp.preVerificationGas, packedUserOp.gasFees, ethers.keccak256(packedUserOp.paymasterAndData), ] ); } else { // for the purpose of calculating gas cost encode also signature (and no keccak of bytes) return defaultAbiCoder.encode( ["address", "uint256", "bytes", "bytes", "bytes32", "uint256", "bytes32", "bytes", "bytes"], [ packedUserOp.sender, packedUserOp.nonce, packedUserOp.initCode, packedUserOp.callData, packedUserOp.accountGasLimits, packedUserOp.preVerificationGas, packedUserOp.gasFees, packedUserOp.paymasterAndData, packedUserOp.signature, ] ); } } export function getUserOpHash(op, entryPoint, chainId) { const userOpHash = ethers.keccak256(encodeUserOp(op, true)); const enc = defaultAbiCoder.encode(["bytes32", "address", "uint256"], [userOpHash, entryPoint, chainId]); return ethers.keccak256(enc); } export const DefaultsForUserOp = { sender: ZeroAddress, nonce: 0, initCode: "0x", callData: "0x", callGasLimit: 0n, verificationGasLimit: 150000n, // default verification gas. will add create2 cost (3200+200*length) if initCode exists preVerificationGas: 21000n, // should also cover calldata cost. maxFeePerGas: 0n, maxPriorityFeePerGas: 1e9, paymaster: ZeroAddress, paymasterData: "0x", paymasterVerificationGasLimit: 300000n, paymasterPostOpGasLimit: 0, signature: "0x", }; export async function signUserOp(op, signer, entryPoint, chainId) { const userOpHash = getUserOpHash(op, entryPoint, chainId); const signature = await signer.signMessage(ethers.getBytes(userOpHash)); return { ...op, signature, }; } export function fillUserOpDefaults(op, defaults = DefaultsForUserOp) { const partial = { ...op }; // we want "item:undefined" to be used from defaults, and not override defaults, so we must explicitly // remove those so "merge" will succeed. for (const key in partial) { if (partial[key] == null) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete partial[key]; } } const filled = { ...defaults, ...partial }; return filled; }