UNPKG

@owlprotocol/contracts-account-abstraction

Version:

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

1,636 lines (1,598 loc) 120 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var EntryPoint_exports = {}; __export(EntryPoint_exports, { EntryPoint: () => EntryPoint }); module.exports = __toCommonJS(EntryPoint_exports); const EntryPoint = { compiler: { version: "0.8.23+commit.f704f362" }, language: "Solidity", output: { abi: [ { inputs: [ { internalType: "bool", name: "success", type: "bool" }, { internalType: "bytes", name: "ret", type: "bytes" } ], name: "DelegateAndRevert", type: "error" }, { inputs: [ { internalType: "uint256", name: "opIndex", type: "uint256" }, { internalType: "string", name: "reason", type: "string" } ], name: "FailedOp", type: "error" }, { inputs: [ { internalType: "uint256", name: "opIndex", type: "uint256" }, { internalType: "string", name: "reason", type: "string" }, { internalType: "bytes", name: "inner", type: "bytes" } ], name: "FailedOpWithRevert", type: "error" }, { inputs: [ { internalType: "bytes", name: "returnData", type: "bytes" } ], name: "PostOpReverted", type: "error" }, { inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" }, { inputs: [ { internalType: "address", name: "sender", type: "address" } ], name: "SenderAddressResult", type: "error" }, { inputs: [ { internalType: "address", name: "aggregator", type: "address" } ], name: "SignatureValidationFailed", type: "error" }, { anonymous: false, inputs: [ { indexed: true, internalType: "bytes32", name: "userOpHash", type: "bytes32" }, { indexed: true, internalType: "address", name: "sender", type: "address" }, { indexed: false, internalType: "address", name: "factory", type: "address" }, { indexed: false, internalType: "address", name: "paymaster", type: "address" } ], name: "AccountDeployed", type: "event" }, { anonymous: false, inputs: [], name: "BeforeExecution", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "account", type: "address" }, { indexed: false, internalType: "uint256", name: "totalDeposit", type: "uint256" } ], name: "Deposited", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "bytes32", name: "userOpHash", type: "bytes32" }, { indexed: true, internalType: "address", name: "sender", type: "address" }, { indexed: false, internalType: "uint256", name: "nonce", type: "uint256" }, { indexed: false, internalType: "bytes", name: "revertReason", type: "bytes" } ], name: "PostOpRevertReason", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "aggregator", type: "address" } ], name: "SignatureAggregatorChanged", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "account", type: "address" }, { indexed: false, internalType: "uint256", name: "totalStaked", type: "uint256" }, { indexed: false, internalType: "uint256", name: "unstakeDelaySec", type: "uint256" } ], name: "StakeLocked", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "account", type: "address" }, { indexed: false, internalType: "uint256", name: "withdrawTime", type: "uint256" } ], name: "StakeUnlocked", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "account", type: "address" }, { indexed: false, internalType: "address", name: "withdrawAddress", type: "address" }, { indexed: false, internalType: "uint256", name: "amount", type: "uint256" } ], name: "StakeWithdrawn", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "bytes32", name: "userOpHash", type: "bytes32" }, { indexed: true, internalType: "address", name: "sender", type: "address" }, { indexed: true, internalType: "address", name: "paymaster", type: "address" }, { indexed: false, internalType: "uint256", name: "nonce", type: "uint256" }, { indexed: false, internalType: "bool", name: "success", type: "bool" }, { indexed: false, internalType: "uint256", name: "actualGasCost", type: "uint256" }, { indexed: false, internalType: "uint256", name: "actualGasUsed", type: "uint256" } ], name: "UserOperationEvent", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "bytes32", name: "userOpHash", type: "bytes32" }, { indexed: true, internalType: "address", name: "sender", type: "address" }, { indexed: false, internalType: "uint256", name: "nonce", type: "uint256" } ], name: "UserOperationPrefundTooLow", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "bytes32", name: "userOpHash", type: "bytes32" }, { indexed: true, internalType: "address", name: "sender", type: "address" }, { indexed: false, internalType: "uint256", name: "nonce", type: "uint256" }, { indexed: false, internalType: "bytes", name: "revertReason", type: "bytes" } ], name: "UserOperationRevertReason", type: "event" }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "account", type: "address" }, { indexed: false, internalType: "address", name: "withdrawAddress", type: "address" }, { indexed: false, internalType: "uint256", name: "amount", type: "uint256" } ], name: "Withdrawn", type: "event" }, { inputs: [ { internalType: "uint32", name: "unstakeDelaySec", type: "uint32" } ], name: "addStake", outputs: [], stateMutability: "payable", type: "function" }, { inputs: [ { internalType: "address", name: "account", type: "address" } ], name: "balanceOf", outputs: [ { internalType: "uint256", name: "", type: "uint256" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "address", name: "target", type: "address" }, { internalType: "bytes", name: "data", type: "bytes" } ], name: "delegateAndRevert", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "address", name: "account", type: "address" } ], name: "depositTo", outputs: [], stateMutability: "payable", type: "function" }, { inputs: [ { internalType: "address", name: "", type: "address" } ], name: "deposits", outputs: [ { internalType: "uint256", name: "deposit", type: "uint256" }, { internalType: "bool", name: "staked", type: "bool" }, { internalType: "uint112", name: "stake", type: "uint112" }, { internalType: "uint32", name: "unstakeDelaySec", type: "uint32" }, { internalType: "uint48", name: "withdrawTime", type: "uint48" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "address", name: "account", type: "address" } ], name: "getDepositInfo", outputs: [ { components: [ { internalType: "uint256", name: "deposit", type: "uint256" }, { internalType: "bool", name: "staked", type: "bool" }, { internalType: "uint112", name: "stake", type: "uint112" }, { internalType: "uint32", name: "unstakeDelaySec", type: "uint32" }, { internalType: "uint48", name: "withdrawTime", type: "uint48" } ], internalType: "struct IStakeManager.DepositInfo", name: "info", type: "tuple" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "address", name: "sender", type: "address" }, { internalType: "uint192", name: "key", type: "uint192" } ], name: "getNonce", outputs: [ { internalType: "uint256", name: "nonce", type: "uint256" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes", name: "initCode", type: "bytes" } ], name: "getSenderAddress", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { components: [ { internalType: "address", name: "sender", type: "address" }, { internalType: "uint256", name: "nonce", type: "uint256" }, { internalType: "bytes", name: "initCode", type: "bytes" }, { internalType: "bytes", name: "callData", type: "bytes" }, { internalType: "bytes32", name: "accountGasLimits", type: "bytes32" }, { internalType: "uint256", name: "preVerificationGas", type: "uint256" }, { internalType: "bytes32", name: "gasFees", type: "bytes32" }, { internalType: "bytes", name: "paymasterAndData", type: "bytes" }, { internalType: "bytes", name: "signature", type: "bytes" } ], internalType: "struct PackedUserOperation", name: "userOp", type: "tuple" } ], name: "getUserOpHash", outputs: [ { internalType: "bytes32", name: "", type: "bytes32" } ], stateMutability: "view", type: "function" }, { inputs: [ { components: [ { components: [ { internalType: "address", name: "sender", type: "address" }, { internalType: "uint256", name: "nonce", type: "uint256" }, { internalType: "bytes", name: "initCode", type: "bytes" }, { internalType: "bytes", name: "callData", type: "bytes" }, { internalType: "bytes32", name: "accountGasLimits", type: "bytes32" }, { internalType: "uint256", name: "preVerificationGas", type: "uint256" }, { internalType: "bytes32", name: "gasFees", type: "bytes32" }, { internalType: "bytes", name: "paymasterAndData", type: "bytes" }, { internalType: "bytes", name: "signature", type: "bytes" } ], internalType: "struct PackedUserOperation[]", name: "userOps", type: "tuple[]" }, { internalType: "contract IAggregator", name: "aggregator", type: "address" }, { internalType: "bytes", name: "signature", type: "bytes" } ], internalType: "struct IEntryPoint.UserOpsPerAggregator[]", name: "opsPerAggregator", type: "tuple[]" }, { internalType: "address payable", name: "beneficiary", type: "address" } ], name: "handleAggregatedOps", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { components: [ { internalType: "address", name: "sender", type: "address" }, { internalType: "uint256", name: "nonce", type: "uint256" }, { internalType: "bytes", name: "initCode", type: "bytes" }, { internalType: "bytes", name: "callData", type: "bytes" }, { internalType: "bytes32", name: "accountGasLimits", type: "bytes32" }, { internalType: "uint256", name: "preVerificationGas", type: "uint256" }, { internalType: "bytes32", name: "gasFees", type: "bytes32" }, { internalType: "bytes", name: "paymasterAndData", type: "bytes" }, { internalType: "bytes", name: "signature", type: "bytes" } ], internalType: "struct PackedUserOperation[]", name: "ops", type: "tuple[]" }, { internalType: "address payable", name: "beneficiary", type: "address" } ], name: "handleOps", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "uint192", name: "key", type: "uint192" } ], name: "incrementNonce", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "bytes", name: "callData", type: "bytes" }, { components: [ { components: [ { internalType: "address", name: "sender", type: "address" }, { internalType: "uint256", name: "nonce", type: "uint256" }, { internalType: "uint256", name: "verificationGasLimit", type: "uint256" }, { internalType: "uint256", name: "callGasLimit", type: "uint256" }, { internalType: "uint256", name: "paymasterVerificationGasLimit", type: "uint256" }, { internalType: "uint256", name: "paymasterPostOpGasLimit", type: "uint256" }, { internalType: "uint256", name: "preVerificationGas", type: "uint256" }, { internalType: "address", name: "paymaster", type: "address" }, { internalType: "uint256", name: "maxFeePerGas", type: "uint256" }, { internalType: "uint256", name: "maxPriorityFeePerGas", type: "uint256" } ], internalType: "struct EntryPoint.MemoryUserOp", name: "mUserOp", type: "tuple" }, { internalType: "bytes32", name: "userOpHash", type: "bytes32" }, { internalType: "uint256", name: "prefund", type: "uint256" }, { internalType: "uint256", name: "contextOffset", type: "uint256" }, { internalType: "uint256", name: "preOpGas", type: "uint256" } ], internalType: "struct EntryPoint.UserOpInfo", name: "opInfo", type: "tuple" }, { internalType: "bytes", name: "context", type: "bytes" } ], name: "innerHandleOp", outputs: [ { internalType: "uint256", name: "actualGasCost", type: "uint256" } ], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "address", name: "", type: "address" }, { internalType: "uint192", name: "", type: "uint192" } ], name: "nonceSequenceNumber", outputs: [ { internalType: "uint256", name: "", type: "uint256" } ], stateMutability: "view", type: "function" }, { inputs: [ { internalType: "bytes4", name: "interfaceId", type: "bytes4" } ], name: "supportsInterface", outputs: [ { internalType: "bool", name: "", type: "bool" } ], stateMutability: "view", type: "function" }, { inputs: [], name: "unlockStake", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "address payable", name: "withdrawAddress", type: "address" } ], name: "withdrawStake", outputs: [], stateMutability: "nonpayable", type: "function" }, { inputs: [ { internalType: "address payable", name: "withdrawAddress", type: "address" }, { internalType: "uint256", name: "withdrawAmount", type: "uint256" } ], name: "withdrawTo", outputs: [], stateMutability: "nonpayable", type: "function" }, { stateMutability: "payable", type: "receive" } ], devdoc: { "custom:security-contact": "https://bounty.ethereum.org", errors: { "FailedOp(uint256,string)": [ { params: { opIndex: "- Index into the array of ops to the failed one (in simulateValidation, this is always zero).", reason: '- Revert reason. The string starts with a unique code "AAmn", where "m" is "1" for factory, "2" for account and "3" for paymaster issues, so a failure can be attributed to the correct entity.' } } ], "FailedOpWithRevert(uint256,string,bytes)": [ { details: "note that inner is truncated to 2048 bytes", params: { inner: "- data from inner cought revert reason", opIndex: "- Index into the array of ops to the failed one (in simulateValidation, this is always zero).", reason: "- Revert reason. see FailedOp(uint256,string), above" } } ], "ReentrancyGuardReentrantCall()": [ { details: "Unauthorized reentrant call." } ], "SignatureValidationFailed(address)": [ { params: { aggregator: "The aggregator that failed to verify the signature" } } ] }, events: { "AccountDeployed(bytes32,address,address,address)": { params: { factory: "- The factory used to deploy this account (in the initCode)", paymaster: "- The paymaster used by this UserOp", sender: "- The account that is deployed", userOpHash: "- The userOp that deployed this account. UserOperationEvent will follow." } }, "PostOpRevertReason(bytes32,address,uint256,bytes)": { params: { nonce: "- The nonce used in the request.", revertReason: '- The return bytes from the (reverted) call to "callData".', sender: "- The sender of this request.", userOpHash: "- The request unique identifier." } }, "SignatureAggregatorChanged(address)": { params: { aggregator: "- The aggregator used for the following UserOperationEvents." } }, "UserOperationPrefundTooLow(bytes32,address,uint256)": { params: { nonce: "- The nonce used in the request.", sender: "- The sender of this request.", userOpHash: "- The request unique identifier." } }, "UserOperationRevertReason(bytes32,address,uint256,bytes)": { params: { nonce: "- The nonce used in the request.", revertReason: '- The return bytes from the (reverted) call to "callData".', sender: "- The sender of this request.", userOpHash: "- The request unique identifier." } } }, kind: "dev", methods: { "addStake(uint32)": { params: { unstakeDelaySec: "The new lock duration before the deposit can be withdrawn." } }, "balanceOf(address)": { params: { account: "- The account to query." }, returns: { _0: "- The deposit (for gas payment) of the account." } }, "delegateAndRevert(address,bytes)": { details: "calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result. The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace actual EntryPoint code is less convenient.", params: { data: "data to pass to target in a delegatecall", target: "a target contract to make a delegatecall from entrypoint" } }, "depositTo(address)": { params: { account: "- The account to add to." } }, "getDepositInfo(address)": { params: { account: "- The account to query." }, returns: { info: " - Full deposit information of given account." } }, "getNonce(address,uint192)": { params: { key: "the high 192 bit of the nonce", sender: "the account address" }, returns: { nonce: "a full nonce to pass for next UserOp with this sender." } }, "getSenderAddress(bytes)": { params: { initCode: "- The constructor code to be passed into the UserOperation." } }, "getUserOpHash((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes))": { params: { userOp: "- The user operation to generate the request ID for." }, returns: { _0: "hash the hash of this UserOperation" } }, "handleAggregatedOps(((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[],address,bytes)[],address)": { params: { beneficiary: "- The address to receive the fees.", opsPerAggregator: "- The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts)." } }, "handleOps((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[],address)": { params: { beneficiary: "- The address to receive the fees.", ops: "- The operations to execute." } }, "innerHandleOp(bytes,((address,uint256,uint256,uint256,uint256,uint256,uint256,address,uint256,uint256),bytes32,uint256,uint256,uint256),bytes)": { params: { callData: "- The callData to execute.", context: "- The context bytes.", opInfo: "- The UserOpInfo struct." }, returns: { actualGasCost: "- the actual cost in eth this UserOperation paid for gas" } }, "supportsInterface(bytes4)": { details: "Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] to learn more about how these ids are created. This function call must use less than 30 000 gas." }, "withdrawStake(address)": { params: { withdrawAddress: "- The address to send withdrawn value." } }, "withdrawTo(address,uint256)": { params: { withdrawAddress: "- The address to send withdrawn value.", withdrawAmount: "- The amount to withdraw." } } }, version: 1 }, userdoc: { errors: { "FailedOp(uint256,string)": [ { notice: "A custom revert error of handleOps, to identify the offending op. Should be caught in off-chain handleOps simulation and not happen on-chain. Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts. NOTE: If simulateValidation passes successfully, there should be no reason for handleOps to fail on it." } ], "FailedOpWithRevert(uint256,string,bytes)": [ { notice: "A custom revert error of handleOps, to report a revert by account or paymaster." } ], "SignatureValidationFailed(address)": [ { notice: "Error case when a signature aggregator fails to verify the aggregated signature it had created." } ] }, events: { "AccountDeployed(bytes32,address,address,address)": { notice: 'Account "sender" was deployed.' }, "BeforeExecution()": { notice: "An event emitted by handleOps(), before starting the execution loop. Any event emitted before this event, is part of the validation." }, "PostOpRevertReason(bytes32,address,uint256,bytes)": { notice: `An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length.` }, "SignatureAggregatorChanged(address)": { notice: "Signature aggregator used by the following UserOperationEvents within this bundle." }, "UserOperationPrefundTooLow(bytes32,address,uint256)": { notice: "UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made." }, "UserOperationRevertReason(bytes32,address,uint256,bytes)": { notice: 'An event emitted if the UserOperation "callData" reverted with non-zero length.' } }, kind: "user", methods: { "addStake(uint32)": { notice: "Add to the account's stake - amount and delay any pending unstake is first cancelled." }, "balanceOf(address)": { notice: "Get account balance." }, "delegateAndRevert(address,bytes)": { notice: "Helper method for dry-run testing." }, "depositTo(address)": { notice: "Add to the deposit of the given account." }, "deposits(address)": { notice: "maps paymaster to their deposits and stakes" }, "getDepositInfo(address)": { notice: "Get deposit info." }, "getNonce(address,uint192)": { notice: "Return the next nonce for this sender. Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop) But UserOp with different keys can come with arbitrary order." }, "getSenderAddress(bytes)": { notice: "Get counterfactual sender address. Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation. This method always revert, and returns the address in SenderAddressResult error" }, "getUserOpHash((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes))": { notice: "Generate a request Id - unique identifier for this request. The request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid." }, "handleAggregatedOps(((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[],address,bytes)[],address)": { notice: "Execute a batch of UserOperation with Aggregators" }, "handleOps((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[],address)": { notice: "Execute a batch of UserOperations. No signature aggregator is used. If any account requires an aggregator (that is, it returned an aggregator when performing simulateValidation), then handleAggregatedOps() must be used instead." }, "incrementNonce(uint192)": { notice: 'Manually increment the nonce of the sender. This method is exposed just for completeness.. Account does NOT need to call it, neither during validation, nor elsewhere, as the EntryPoint will update the nonce regardless. Possible use-case is call it with various keys to "initialize" their nonces to one, so that future UserOperations will not pay extra for the first transaction with a given key.' }, "innerHandleOp(bytes,((address,uint256,uint256,uint256,uint256,uint256,uint256,address,uint256,uint256),bytes32,uint256,uint256,uint256),bytes)": { notice: 'Inner function to handle a UserOperation. Must be declared "external" to open a call context, but it can only be called by handleOps.' }, "nonceSequenceNumber(address,uint192)": { notice: "The next valid sequence number for a given nonce key." }, "unlockStake()": { notice: "Attempt to unlock the stake. The value can be withdrawn (using withdrawStake) after the unstake delay." }, "withdrawStake(address)": { notice: "Withdraw from the (unlocked) stake. Must first call unlockStake and wait for the unstakeDelay to pass." }, "withdrawTo(address,uint256)": { notice: "Withdraw from the deposit." } }, version: 1 } }, settings: { compilationTarget: { "@account-abstraction/contracts/core/EntryPoint.sol": "EntryPoint" }, evmVersion: "paris", libraries: {}, metadata: { bytecodeHash: "ipfs", useLiteralContent: true }, optimizer: { enabled: true, runs: 1e6 }, remappings: [], viaIR: true }, sources: { "@account-abstraction/contracts/core/EntryPoint.sol": { content: `// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.23; /* solhint-disable avoid-low-level-calls */ /* solhint-disable no-inline-assembly */ import "../interfaces/IAccount.sol"; import "../interfaces/IAccountExecute.sol"; import "../interfaces/IPaymaster.sol"; import "../interfaces/IEntryPoint.sol"; import "../utils/Exec.sol"; import "./StakeManager.sol"; import "./SenderCreator.sol"; import "./Helpers.sol"; import "./NonceManager.sol"; import "./UserOperationLib.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; /* * Account-Abstraction (EIP-4337) singleton EntryPoint implementation. * Only one instance required on each chain. */ /// @custom:security-contact https://bounty.ethereum.org contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, ERC165 { using UserOperationLib for PackedUserOperation; SenderCreator private immutable _senderCreator = new SenderCreator(); function senderCreator() internal view virtual returns (SenderCreator) { return _senderCreator; } //compensate for innerHandleOps' emit message and deposit refund. // allow some slack for future gas price changes. uint256 private constant INNER_GAS_OVERHEAD = 10000; // Marker for inner call revert on out of gas bytes32 private constant INNER_OUT_OF_GAS = hex"deaddead"; bytes32 private constant INNER_REVERT_LOW_PREFUND = hex"deadaa51"; uint256 private constant REVERT_REASON_MAX_LEN = 2048; uint256 private constant PENALTY_PERCENT = 10; /// @inheritdoc IERC165 function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { // note: solidity "type(IEntryPoint).interfaceId" is without inherited methods but we want to check everything return interfaceId == (type(IEntryPoint).interfaceId ^ type(IStakeManager).interfaceId ^ type(INonceManager).interfaceId) || interfaceId == type(IEntryPoint).interfaceId || interfaceId == type(IStakeManager).interfaceId || interfaceId == type(INonceManager).interfaceId || super.supportsInterface(interfaceId); } /** * Compensate the caller's beneficiary address with the collected fees of all UserOperations. * @param beneficiary - The address to receive the fees. * @param amount - Amount to transfer. */ function _compensate(address payable beneficiary, uint256 amount) internal { require(beneficiary != address(0), "AA90 invalid beneficiary"); (bool success, ) = beneficiary.call{value: amount}(""); require(success, "AA91 failed send to beneficiary"); } /** * Execute a user operation. * @param opIndex - Index into the opInfo array. * @param userOp - The userOp to execute. * @param opInfo - The opInfo filled by validatePrepayment for this userOp. * @return collected - The total amount this userOp paid. */ function _executeUserOp( uint256 opIndex, PackedUserOperation calldata userOp, UserOpInfo memory opInfo ) internal returns (uint256 collected) { uint256 preGas = gasleft(); bytes memory context = getMemoryBytesFromOffset(opInfo.contextOffset); bool success; { uint256 saveFreePtr; assembly ("memory-safe") { saveFreePtr := mload(0x40) } bytes calldata callData = userOp.callData; bytes memory innerCall; bytes4 methodSig; assembly { let len := callData.length if gt(len, 3) { methodSig := calldataload(callData.offset) } } if (methodSig == IAccountExecute.executeUserOp.selector) { bytes memory executeUserOp = abi.encodeCall(IAccountExecute.executeUserOp, (userOp, opInfo.userOpHash)); innerCall = abi.encodeCall(this.innerHandleOp, (executeUserOp, opInfo, context)); } else { innerCall = abi.encodeCall(this.innerHandleOp, (callData, opInfo, context)); } assembly ("memory-safe") { success := call(gas(), address(), 0, add(innerCall, 0x20), mload(innerCall), 0, 32) collected := mload(0) mstore(0x40, saveFreePtr) } } if (!success) { bytes32 innerRevertCode; assembly ("memory-safe") { let len := returndatasize() if eq(32,len) { returndatacopy(0, 0, 32) innerRevertCode := mload(0) } } if (innerRevertCode == INNER_OUT_OF_GAS) { // handleOps was called with gas limit too low. abort entire bundle. //can only be caused by bundler (leaving not enough gas for inner call) revert FailedOp(opIndex, "AA95 out of gas"); } else if (innerRevertCode == INNER_REVERT_LOW_PREFUND) { // innerCall reverted on prefund too low. treat entire prefund as "gas cost" uint256 actualGas = preGas - gasleft() + opInfo.preOpGas; uint256 actualGasCost = opInfo.prefund; emitPrefundTooLow(opInfo); emitUserOperationEvent(opInfo, false, actualGasCost, actualGas); collected = actualGasCost; } else { emit PostOpRevertReason( opInfo.userOpHash, opInfo.mUserOp.sender, opInfo.mUserOp.nonce, Exec.getReturnData(REVERT_REASON_MAX_LEN) ); uint256 actualGas = preGas - gasleft() + opInfo.preOpGas; collected = _postExecution( IPaymaster.PostOpMode.postOpReverted, opInfo, context, actualGas ); } } } function emitUserOperationEvent(UserOpInfo memory opInfo, bool success, uint256 actualGasCost, uint256 actualGas) internal virtual { emit UserOperationEvent( opInfo.userOpHash, opInfo.mUserOp.sender, opInfo.mUserOp.paymaster, opInfo.mUserOp.nonce, success, actualGasCost, actualGas ); } function emitPrefundTooLow(UserOpInfo memory opInfo) internal virtual { emit UserOperationPrefundTooLow( opInfo.userOpHash, opInfo.mUserOp.sender, opInfo.mUserOp.nonce ); } /// @inheritdoc IEntryPoint function handleOps( PackedUserOperation[] calldata ops, address payable beneficiary ) public nonReentrant { uint256 opslen = ops.length; UserOpInfo[] memory opInfos = new UserOpInfo[](opslen); unchecked { for (uint256 i = 0; i < opslen; i++) { UserOpInfo memory opInfo = opInfos[i]; ( uint256 validationData, uint256 pmValidationData ) = _validatePrepayment(i, ops[i], opInfo); _validateAccountAndPaymasterValidationData( i, validationData, pmValidationData, address(0) ); } uint256 collected = 0; emit BeforeExecution(); for (uint256 i = 0; i < opslen; i++) { collected += _executeUserOp(i, ops[i], opInfos[i]); } _compensate(beneficiary, collected); } } /// @inheritdoc IEntryPoint function handleAggregatedOps( UserOpsPerAggregator[] calldata opsPerAggregator, address payable beneficiary ) public nonReentrant { uint256 opasLen = opsPerAggregator.length; uint256 totalOps = 0; for (uint256 i = 0; i < opasLen; i++) { UserOpsPerAggregator calldata opa = opsPerAggregator[i]; PackedUserOperation[] calldata ops = opa.userOps; IAggregator aggregator = opa.aggregator; //address(1) is special marker of "signature error" require( address(aggregator) != address(1), "AA96 invalid aggregator" ); if (address(aggregator) != address(0)) { // solhint-disable-next-line no-empty-blocks try aggregator.validateSignatures(ops, opa.signature) {} catch { revert SignatureValidationFailed(address(aggregator)); } } totalOps += ops.length; } UserOpInfo[] memory opInfos = new UserOpInfo[](totalOps); uint256 opIndex = 0; for (uint256 a = 0; a < opasLen; a++) { UserOpsPerAggregator calldata opa = opsPerAggregator[a]; PackedUserOperation[] calldata ops = opa.userOps; IAggregator aggregator = opa.aggregator; uint256 opslen = ops.length; for (uint256 i = 0; i < opslen; i++) { UserOpInfo memory opInfo = opInfos[opIndex]; ( uint256 validationData, uint256 paymasterValidationData ) = _validatePrepayment(opIndex, ops[i], opInfo); _validateAccountAndPaymasterValidationData( i, validationData, paymasterValidationData, address(aggregator) ); opIndex++; } } emit BeforeExecution(); uint256 collected = 0; opIndex = 0; for (uint256 a = 0; a < opasLen; a++) { UserOpsPerAggregator calldata opa = opsPerAggregator[a]; emit SignatureAggregatorChanged(address(opa.aggregator)); PackedUserOperation[] calldata ops = opa.userOps; uint256 opslen = ops.length; for (uint256 i = 0; i < opslen; i++) { collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]); opIndex++; } } emit SignatureAggregatorChanged(address(0)); _compensate(beneficiary, collected); } /** * A memory copy of UserOp static fields only. * Excluding: callData, initCode and signature. Replacing paymasterAndData with paymaster. */ struct MemoryUserOp { address sender; uint256 nonce; uint256 verificationGasLimit; uint256 callGasLimit; uint256 paymasterVerificationGasLimit; uint256 paymasterPostOpGasLimit; uint256 preVerificationGas; address paymaster; uint256 maxFeePerGas; uint256 maxPriorityFeePerGas; } struct UserOpInfo { MemoryUserOp mUserOp; bytes32 userOpHash; uint256 prefund; uint256 contextOffset; uint256 preOpGas; } /** * Inner function to handle a UserOperation. * Must be declared "external" to open a call context, but it can only be called by handleOps. * @param callData - The callData to execute. * @param opInfo - The UserOpInfo struct. * @param context - The context bytes. * @return actualGasCost - the actual cost in eth this UserOperation paid for gas */ function innerHandleOp( bytes memory callData, UserOpInfo memory opInfo, bytes calldata context ) external returns (uint256 actualGasCost) { uint256 preGas = gasleft(); require(msg.sender == address(this), "AA92 internal call only"); MemoryUserOp memory mUserOp = opInfo.mUserOp; uint256 callGasLimit = mUserOp.callGasLimit; unchecked { // handleOps was called with gas limit too low. abort entire bundle. if ( gasleft() * 63 / 64 < callGasLimit + mUserOp.paymasterPostOpGasLimit + I