Overview
ETH Balance
0 ETH
Eth Value
$0.00Showing the last 1 internal transaction (View Advanced Filter)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x603d3d81 | 24335327 | 24 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Loading...
Loading
Minimal Proxy Contract for 0x2c53d0bd33a60db8881c7b049df6fd762a1f059c
Contract Name:
LightAccount
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 10000000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.28;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {SIG_VALIDATION_FAILED} from "account-abstraction/core/Helpers.sol";
import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {BaseLightAccount} from "./common/BaseLightAccount.sol";
import {CustomSlotInitializable} from "./common/CustomSlotInitializable.sol";
/// @title A simple ERC-4337 compatible smart contract account with a designated owner account.
/// @dev Like eth-infinitism's SimpleAccount, but with the following changes:
///
/// 1. Instead of the default storage slots, uses namespaced storage to avoid clashes when switching implementations.
///
/// 2. Ownership can be transferred via `transferOwnership`, similar to the behavior of an `Ownable` contract. This is
/// a simple single-step operation, so care must be taken to ensure that the ownership is being transferred to the
/// correct address.
///
/// 3. Supports [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) signature validation for both validating the
/// signature on user operations and in exposing its own `isValidSignature` method. This only works when the owner of
/// LightAccount also support ERC-1271.
///
/// ERC-4337's bundler validation rules limit the types of contracts that can be used as owners to validate user
/// operation signatures. For example, the contract's `isValidSignature` function may not use any forbidden opcodes
/// such as `TIMESTAMP` or `NUMBER`, and the contract may not be an ERC-1967 proxy as it accesses a constant
/// implementation slot not associated with the account, violating storage access rules. This also means that the
/// owner of a LightAccount may not be another LightAccount if you want to send user operations through a bundler.
///
/// 4. Event `SimpleAccountInitialized` renamed to `LightAccountInitialized`.
///
/// 5. Uses custom errors.
contract LightAccount is BaseLightAccount, CustomSlotInitializable {
using ECDSA for bytes32;
/// @dev The version used for namespaced storage is not linked to the release version of the contract. Storage
/// versions will be updated only when storage layout changes are made.
/// keccak256(abi.encode(uint256(keccak256("light_account_v1.storage")) - 1)) & ~bytes32(uint256(0xff));
bytes32 internal constant _STORAGE_POSITION = 0x691ec1a18226d004c07c9f8e5c4a6ff15a7b38db267cf7e3c945aef8be512200;
/// @dev keccak256(abi.encode(uint256(keccak256("light_account_v1.initializable")) - 1)) & ~bytes32(uint256(0xff));
bytes32 internal constant _INITIALIZABLE_STORAGE_POSITION =
0x33e4b41198cc5b8053630ed667ea7c0c4c873f7fc8d9a478b5d7259cec0a4a00;
struct LightAccountStorage {
address owner;
}
/// @notice Emitted when this account is first initialized.
/// @param entryPoint The entry point.
/// @param owner The initial owner.
event LightAccountInitialized(IEntryPoint indexed entryPoint, address indexed owner);
/// @notice Emitted when this account's owner changes. Also emitted once at initialization, with a
/// `previousOwner` of 0.
/// @param previousOwner The previous owner.
/// @param newOwner The new owner.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @dev The new owner is not a valid owner (e.g., `address(0)`, the account itself, or the current owner).
error InvalidOwner(address owner);
constructor(IEntryPoint entryPoint_) CustomSlotInitializable(_INITIALIZABLE_STORAGE_POSITION) {
_ENTRY_POINT = entryPoint_;
_disableInitializers();
}
/// @notice Called once as part of initialization, either during initial deployment or when first upgrading to
/// this contract.
/// @dev The `_ENTRY_POINT` member is immutable, to reduce gas consumption. To update the entry point address, a new
/// implementation of LightAccount must be deployed with the new entry point address, and then `upgradeToAndCall`
/// must be called to upgrade the implementation.
/// @param owner_ The initial owner of the account.
function initialize(address owner_) external virtual initializer {
_initialize(owner_);
}
/// @notice Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current
/// owner or from the entry point via a user operation signed by the current owner.
/// @param newOwner The new owner.
function transferOwnership(address newOwner) external virtual onlyAuthorized {
if (newOwner == address(0) || newOwner == address(this)) {
revert InvalidOwner(newOwner);
}
_transferOwnership(newOwner);
}
/// @notice Return the current owner of this account.
/// @return The current owner.
function owner() public view returns (address) {
return _getStorage().owner;
}
function _initialize(address owner_) internal virtual {
if (owner_ == address(0)) {
revert InvalidOwner(address(0));
}
_getStorage().owner = owner_;
emit LightAccountInitialized(_ENTRY_POINT, owner_);
emit OwnershipTransferred(address(0), owner_);
}
function _transferOwnership(address newOwner) internal virtual {
LightAccountStorage storage _storage = _getStorage();
address oldOwner = _storage.owner;
if (newOwner == oldOwner) {
revert InvalidOwner(newOwner);
}
_storage.owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/// @dev Implement template method of BaseAccount.
function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
internal
virtual
override
returns (uint256 validationData)
{
if (userOp.signature.length < 1) {
revert InvalidSignatureType();
}
uint8 signatureType = uint8(userOp.signature[0]);
if (signatureType == uint8(SignatureType.EOA)) {
return _successToValidationData(_isValidEOAOwnerSignature(userOpHash, userOp.signature[1:]));
} else if (signatureType == uint8(SignatureType.CONTRACT)) {
// Contract signature without address
bytes memory signature = userOp.signature[1:];
return _successToValidationData(_isValidContractOwnerSignatureNow(userOpHash, signature));
}
revert InvalidSignatureType();
}
/// @notice Check if the signature is a valid by the EOA owner for the given digest.
/// @dev Only supports 65-byte signatures, and uses the digest directly. Reverts if the signature is malformed.
/// @param digest The digest to be checked.
/// @param signature The signature to be checked.
/// @return True if the signature is valid and by the owner, false otherwise.
function _isValidEOAOwnerSignature(bytes32 digest, bytes memory signature) internal view returns (bool) {
address recovered = digest.recover(signature);
return recovered == owner();
}
/// @notice Check if the signature is a valid ERC-1271 signature by a contract owner for the given digest.
/// @param digest The digest to be checked.
/// @param signature The signature to be checked.
/// @return True if the signature is valid and by an owner, false otherwise.
function _isValidContractOwnerSignatureNow(bytes32 digest, bytes memory signature) internal view returns (bool) {
return SignatureChecker.isValidERC1271SignatureNow(owner(), digest, signature);
}
/// @dev The signature is valid if it is signed by the owner's private key (if the owner is an EOA) or if it is a
/// valid ERC-1271 signature from the owner (if the owner is a contract). Reverts if the signature is malformed.
/// Note that unlike the signature validation used in `validateUserOp`, this does **not** wrap the hash in an
/// "Ethereum Signed Message" envelope before checking the signature in the EOA-owner case.
function _isValidSignature(bytes32 replaySafeHash, bytes calldata signature)
internal
view
virtual
override
returns (bool)
{
if (signature.length < 1) {
revert InvalidSignatureType();
}
uint8 signatureType = uint8(signature[0]);
if (signatureType == uint8(SignatureType.EOA)) {
// EOA signature
return _isValidEOAOwnerSignature(replaySafeHash, signature[1:]);
} else if (signatureType == uint8(SignatureType.CONTRACT)) {
// Contract signature without address
return _isValidContractOwnerSignatureNow(replaySafeHash, signature[1:]);
}
revert InvalidSignatureType();
}
function _domainNameAndVersion()
internal
view
virtual
override
returns (string memory name, string memory version)
{
name = "LightAccount";
// Set to the major version of the GitHub release at which the contract was last updated.
version = "2";
}
function _isFromOwner() internal view virtual override returns (bool) {
return msg.sender == owner();
}
function _getStorage() internal pure returns (LightAccountStorage storage storageStruct) {
bytes32 position = _STORAGE_POSITION;
assembly ("memory-safe") {
storageStruct.slot := position
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/* solhint-disable no-empty-blocks */
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
/**
* Token callback handler.
* Handles supported tokens' callbacks, allowing account receiving these tokens.
*/
abstract contract TokenCallbackHandler is IERC721Receiver, IERC1155Receiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external pure override returns (bytes4) {
return IERC1155Receiver.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] calldata,
uint256[] calldata,
bytes calldata
) external pure override returns (bytes4) {
return IERC1155Receiver.onERC1155BatchReceived.selector;
}
function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
return
interfaceId == type(IERC721Receiver).interfaceId ||
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-empty-blocks */
/* solhint-disable no-inline-assembly */
import "../interfaces/IAccount.sol";
import "../interfaces/IEntryPoint.sol";
import "../utils/Exec.sol";
import "./UserOperationLib.sol";
/**
* Basic account implementation.
* This contract provides the basic logic for implementing the IAccount interface - validateUserOp
* Specific account implementation should inherit it and provide the account-specific logic.
*/
abstract contract BaseAccount is IAccount {
using UserOperationLib for PackedUserOperation;
struct Call {
address target;
uint256 value;
bytes data;
}
error ExecuteError(uint256 index, bytes error);
/**
* Return the account nonce.
* This method returns the next sequential nonce.
* For a nonce of a specific key, use `entrypoint.getNonce(account, key)`
*/
function getNonce() public view virtual returns (uint256) {
return entryPoint().getNonce(address(this), 0);
}
/**
* Return the entryPoint used by this account.
* Subclass should return the current entryPoint used by this account.
*/
function entryPoint() public view virtual returns (IEntryPoint);
/**
* execute a single call from the account.
*/
function execute(address target, uint256 value, bytes calldata data) virtual external {
_requireForExecute();
bool ok = Exec.call(target, value, data, gasleft());
if (!ok) {
Exec.revertWithReturnData();
}
}
/**
* execute a batch of calls.
* revert on the first call that fails.
* If the batch reverts, and it contains more than a single call, then wrap the revert with ExecuteError,
* to mark the failing call index.
*/
function executeBatch(Call[] calldata calls) virtual external {
_requireForExecute();
uint256 callsLength = calls.length;
for (uint256 i = 0; i < callsLength; i++) {
Call calldata call = calls[i];
bool ok = Exec.call(call.target, call.value, call.data, gasleft());
if (!ok) {
if (callsLength == 1) {
Exec.revertWithReturnData();
} else {
revert ExecuteError(i, Exec.getReturnData(0));
}
}
}
}
/// @inheritdoc IAccount
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external virtual override returns (uint256 validationData) {
_requireFromEntryPoint();
validationData = _validateSignature(userOp, userOpHash);
_validateNonce(userOp.nonce);
_payPrefund(missingAccountFunds);
}
/**
* Ensure the request comes from the known entrypoint.
*/
function _requireFromEntryPoint() internal view virtual {
require(
msg.sender == address(entryPoint()),
"account: not from EntryPoint"
);
}
function _requireForExecute() internal view virtual {
_requireFromEntryPoint();
}
/**
* Validate the signature is valid for this message.
* @param userOp - Validate the userOp.signature field.
* @param userOpHash - Convenient field: the hash of the request, to check the signature against.
* (also hashes the entrypoint and chain id)
* @return validationData - Signature and time-range of this operation.
* <20-byte> aggregatorOrSigFail - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an aggregator contract.
* <6-byte> validUntil - Last timestamp this operation is valid at, or 0 for "indefinitely"
* <6-byte> validAfter - first timestamp this operation is valid
* If the account doesn't use time-range, it is enough to return
* SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function _validateSignature(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual returns (uint256 validationData);
/**
* Validate the nonce of the UserOperation.
* This method may validate the nonce requirement of this account.
* e.g.
* To limit the nonce to use sequenced UserOps only (no "out of order" UserOps):
* `require(nonce < type(uint64).max)`
* For a hypothetical account that *requires* the nonce to be out-of-order:
* `require(nonce & type(uint64).max == 0)`
*
* The actual nonce uniqueness is managed by the EntryPoint, and thus no other
* action is needed by the account itself.
*
* @param nonce to validate
*
* solhint-disable-next-line no-empty-blocks
*/
function _validateNonce(uint256 nonce) internal view virtual {
}
/**
* Sends to the entrypoint (msg.sender) the missing funds for this transaction.
* SubClass MAY override this method for better funds management
* (e.g. send to the entryPoint more than the minimum required, so that in future transactions
* it will not be required to send again).
* @param missingAccountFunds - The minimum value this method should send the entrypoint.
* This value MAY be zero, in case there is enough deposit,
* or the userOp has a paymaster.
*/
function _payPrefund(uint256 missingAccountFunds) internal virtual {
if (missingAccountFunds != 0) {
(bool success,) = payable(msg.sender).call{
value: missingAccountFunds
}("");
(success);
// Ignore failure (its EntryPoint's job to verify, not account.)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/* solhint-disable no-inline-assembly */
/*
* For simulation purposes, validateUserOp (and validatePaymasterUserOp)
* must return this value in case of signature failure, instead of revert.
*/
uint256 constant SIG_VALIDATION_FAILED = 1;
/*
* For simulation purposes, validateUserOp (and validatePaymasterUserOp)
* return this value on success.
*/
uint256 constant SIG_VALIDATION_SUCCESS = 0;
/**
* Returned data from validateUserOp.
* validateUserOp returns a uint256, which is created by `_packedValidationData` and
* parsed by `_parseValidationData`.
* @param aggregator - address(0) - The account validated the signature by itself.
* address(1) - The account failed to validate the signature.
* otherwise - This is an address of a signature aggregator that must
* be used to validate the signature.
* @param validAfter - This UserOp is valid only after this timestamp.
* @param validUntil - Last timestamp this operation is valid at, or 0 for "indefinitely".
*/
struct ValidationData {
address aggregator;
uint48 validAfter;
uint48 validUntil;
}
/**
* Extract aggregator/sigFailed, validAfter, validUntil.
* Also convert zero validUntil to type(uint48).max.
* @param validationData - The packed validation data.
* @return data - The unpacked in-memory validation data.
*/
function _parseValidationData(
uint256 validationData
) pure returns (ValidationData memory data) {
address aggregator = address(uint160(validationData));
uint48 validUntil = uint48(validationData >> 160);
if (validUntil == 0) {
validUntil = type(uint48).max;
}
uint48 validAfter = uint48(validationData >> (48 + 160));
return ValidationData(aggregator, validAfter, validUntil);
}
/**
* Helper to pack the return value for validateUserOp.
* @param data - The ValidationData to pack.
* @return the packed validation data.
*/
function _packValidationData(
ValidationData memory data
) pure returns (uint256) {
return
uint160(data.aggregator) |
(uint256(data.validUntil) << 160) |
(uint256(data.validAfter) << (160 + 48));
}
/**
* Helper to pack the return value for validateUserOp, when not using an aggregator.
* @param sigFailed - True for signature failure, false for success.
* @param validUntil - Last timestamp this operation is valid at, or 0 for "indefinitely".
* @param validAfter - First timestamp this UserOperation is valid.
* @return the packed validation data.
*/
function _packValidationData(
bool sigFailed,
uint48 validUntil,
uint48 validAfter
) pure returns (uint256) {
return
(sigFailed ? SIG_VALIDATION_FAILED : SIG_VALIDATION_SUCCESS) |
(uint256(validUntil) << 160) |
(uint256(validAfter) << (160 + 48));
}
/**
* keccak function over calldata.
* @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it.
*
* @param data - the calldata bytes array to perform keccak on.
* @return ret - the keccak hash of the 'data' array.
*/
function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) {
assembly ("memory-safe") {
let mem := mload(0x40)
let len := data.length
calldatacopy(mem, data.offset, len)
ret := keccak256(mem, len)
}
}
/**
* The minimum of two numbers.
* @param a - First number.
* @param b - Second number.
* @return - the minimum value.
*/
function min(uint256 a, uint256 b) pure returns (uint256) {
return a < b ? a : b;
}
/**
* standard solidity memory allocation finalization.
* copied from solidity generated code
* @param memPointer - The current memory pointer
* @param allocationSize - Bytes allocated from memPointer.
*/
function finalizeAllocation(uint256 memPointer, uint256 allocationSize) pure {
assembly ("memory-safe"){
finalize_allocation(memPointer, allocationSize)
function finalize_allocation(memPtr, size) {
let newFreePtr := add(memPtr, round_up_to_mul_of_32(size))
mstore(64, newFreePtr)
}
function round_up_to_mul_of_32(value) -> result {
result := and(add(value, 31), not(31))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/* solhint-disable no-inline-assembly */
import "../interfaces/PackedUserOperation.sol";
import {calldataKeccak, min} from "./Helpers.sol";
/**
* Utility functions helpful when working with UserOperation structs.
*/
library UserOperationLib {
uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20;
uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36;
uint256 public constant PAYMASTER_DATA_OFFSET = 52;
/**
* Relayer/block builder might submit the TX with higher priorityFee,
* but the user should not pay above what he signed for.
* @param userOp - The user operation data.
*/
function gasPrice(
PackedUserOperation calldata userOp
) internal view returns (uint256) {
unchecked {
(uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = unpackUints(userOp.gasFees);
return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
}
}
bytes32 internal constant PACKED_USEROP_TYPEHASH =
keccak256(
"PackedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterAndData)"
);
/**
* Pack the user operation data into bytes for hashing.
* @param userOp - The user operation data.
* @param overrideInitCodeHash - If set, encode this instead of the initCode field in the userOp.
*/
function encode(
PackedUserOperation calldata userOp,
bytes32 overrideInitCodeHash
) internal pure returns (bytes memory ret) {
address sender = userOp.sender;
uint256 nonce = userOp.nonce;
bytes32 hashInitCode = overrideInitCodeHash != 0 ? overrideInitCodeHash : calldataKeccak(userOp.initCode);
bytes32 hashCallData = calldataKeccak(userOp.callData);
bytes32 accountGasLimits = userOp.accountGasLimits;
uint256 preVerificationGas = userOp.preVerificationGas;
bytes32 gasFees = userOp.gasFees;
bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData);
return abi.encode(
UserOperationLib.PACKED_USEROP_TYPEHASH,
sender, nonce,
hashInitCode, hashCallData,
accountGasLimits, preVerificationGas, gasFees,
hashPaymasterAndData
);
}
function unpackUints(
bytes32 packed
) internal pure returns (uint256 high128, uint256 low128) {
return (unpackHigh128(packed), unpackLow128(packed));
}
// Unpack just the high 128-bits from a packed value
function unpackHigh128(bytes32 packed) internal pure returns (uint256) {
return uint256(packed) >> 128;
}
// Unpack just the low 128-bits from a packed value
function unpackLow128(bytes32 packed) internal pure returns (uint256) {
return uint128(uint256(packed));
}
function unpackMaxPriorityFeePerGas(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackHigh128(userOp.gasFees);
}
function unpackMaxFeePerGas(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackLow128(userOp.gasFees);
}
function unpackVerificationGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackHigh128(userOp.accountGasLimits);
}
function unpackCallGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return unpackLow128(userOp.accountGasLimits);
}
function unpackPaymasterVerificationGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET]));
}
function unpackPostOpGasLimit(PackedUserOperation calldata userOp)
internal pure returns (uint256) {
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]));
}
function unpackPaymasterStaticFields(
bytes calldata paymasterAndData
) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) {
return (
address(bytes20(paymasterAndData[: PAYMASTER_VALIDATION_GAS_OFFSET])),
uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET : PAYMASTER_POSTOP_GAS_OFFSET])),
uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET : PAYMASTER_DATA_OFFSET]))
);
}
/**
* Hash the user operation data.
* @param userOp - The user operation data.
* @param overrideInitCodeHash - If set, the initCode hash will be replaced with this value just for UserOp hashing.
*/
function hash(
PackedUserOperation calldata userOp,
bytes32 overrideInitCodeHash
) internal pure returns (bytes32) {
return keccak256(encode(userOp, overrideInitCodeHash));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./PackedUserOperation.sol";
interface IAccount {
/**
* Validate user's signature and nonce
* the entryPoint will make the call to the recipient only if this validation call returns successfully.
* signature failure should be reported by returning SIG_VALIDATION_FAILED (1).
* This allows making a "simulation call" without a valid signature
* Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure.
*
* @dev Must validate caller is the entryPoint.
* Must validate the signature and nonce
* @param userOp - The operation that is about to be executed.
* @param userOpHash - Hash of the user's request data. can be used as the basis for signature.
* @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint.
* This is the minimum amount to transfer to the sender(entryPoint) to be
* able to make the call. The excess is left as a deposit in the entrypoint
* for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()".
* In case there is a paymaster in the request (or the current deposit is high
* enough), this value will be zero.
* @return validationData - Packaged ValidationData structure. use `_packValidationData` and
* `_unpackValidationData` to encode and decode.
* <20-byte> aggregatorOrSigFail - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an "aggregator" contract.
* <6-byte> validUntil - Last timestamp this operation is valid at, or 0 for "indefinitely"
* <6-byte> validAfter - First timestamp this operation is valid
* If an account doesn't use time-range, it is enough to
* return SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "./PackedUserOperation.sol";
/**
* Aggregated Signatures validator.
*/
interface IAggregator {
/**
* Validate an aggregated signature.
* Reverts if the aggregated signature does not match the given list of operations.
* @param userOps - An array of UserOperations to validate the signature for.
* @param signature - The aggregated signature.
*/
function validateSignatures(
PackedUserOperation[] calldata userOps,
bytes calldata signature
) external;
/**
* Validate the signature of a single userOp.
* This method should be called by bundler after EntryPointSimulation.simulateValidation() returns
* the aggregator this account uses.
* First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps.
* @param userOp - The userOperation received from the user.
* @return sigForUserOp - The value to put into the signature field of the userOp when calling handleOps.
* (usually empty, unless account and aggregator support some kind of "multisig".
*/
function validateUserOpSignature(
PackedUserOperation calldata userOp
) external view returns (bytes memory sigForUserOp);
/**
* Aggregate multiple signatures into a single value.
* This method is called off-chain to calculate the signature to pass with handleOps()
* bundler MAY use optimized custom code to perform this aggregation.
* @param userOps - An array of UserOperations to collect the signatures from.
* @return aggregatedSignature - The aggregated signature.
*/
function aggregateSignatures(
PackedUserOperation[] calldata userOps
) external view returns (bytes memory aggregatedSignature);
}/**
** Account-Abstraction (EIP-4337) singleton EntryPoint implementation.
** Only one instance required on each chain.
**/
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "./PackedUserOperation.sol";
import "./IStakeManager.sol";
import "./IAggregator.sol";
import "./INonceManager.sol";
import "./ISenderCreator.sol";
interface IEntryPoint is IStakeManager, INonceManager {
/***
* An event emitted after each successful request.
* @param userOpHash - Unique identifier for the request (hash its entire content, except signature).
* @param sender - The account that generates this request.
* @param paymaster - If non-null, the paymaster that pays for this request.
* @param nonce - The nonce value from the request.
* @param success - True if the sender transaction succeeded, false if reverted.
* @param actualGasCost - Actual amount paid (by account or paymaster) for this UserOperation.
* @param actualGasUsed - Total gas used by this UserOperation (including preVerification, creation,
* validation and execution).
*/
event UserOperationEvent(
bytes32 indexed userOpHash,
address indexed sender,
address indexed paymaster,
uint256 nonce,
bool success,
uint256 actualGasCost,
uint256 actualGasUsed
);
/**
* Account "sender" was deployed.
* @param userOpHash - The userOp that deployed this account. UserOperationEvent will follow.
* @param sender - The account that is deployed
* @param factory - The factory used to deploy this account (in the initCode)
* @param paymaster - The paymaster used by this UserOp
*/
event AccountDeployed(
bytes32 indexed userOpHash,
address indexed sender,
address factory,
address paymaster
);
/**
* An event emitted if the UserOperation "callData" reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the reverted "callData" call.
*/
event UserOperationRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
* @param revertReason - The return bytes from the reverted call to "postOp".
*/
event PostOpRevertReason(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce,
bytes revertReason
);
/**
* UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made.
* @param userOpHash - The request unique identifier.
* @param sender - The sender of this request.
* @param nonce - The nonce used in the request.
*/
event UserOperationPrefundTooLow(
bytes32 indexed userOpHash,
address indexed sender,
uint256 nonce
);
/**
* An event emitted by handleOps() and handleAggregatedOps(), before starting the execution loop.
* Any event emitted before this event, is part of the validation.
*/
event BeforeExecution();
/**
* Signature aggregator used by the following UserOperationEvents within this bundle.
* @param aggregator - The aggregator used for the following UserOperationEvents.
*/
event SignatureAggregatorChanged(address indexed aggregator);
/**
* A custom revert error of handleOps andhandleAggregatedOps, to identify the offending op.
* Should be caught in off-chain handleOps/handleAggregatedOps 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.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param 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.
*/
error FailedOp(uint256 opIndex, string reason);
/**
* A custom revert error of handleOps and handleAggregatedOps, to report a revert by account or paymaster.
* @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero).
* @param reason - Revert reason. see FailedOp(uint256,string), above
* @param inner - data from inner cought revert reason
* @dev note that inner is truncated to 2048 bytes
*/
error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner);
error PostOpReverted(bytes returnData);
/**
* Error case when a signature aggregator fails to verify the aggregated signature it had created.
* @param aggregator The aggregator that failed to verify the signature
*/
error SignatureValidationFailed(address aggregator);
// Return value of getSenderAddress.
error SenderAddressResult(address sender);
// UserOps handled, per aggregator.
struct UserOpsPerAggregator {
PackedUserOperation[] userOps;
// Aggregator address
IAggregator aggregator;
// Aggregated signature
bytes signature;
}
/**
* 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.
* @param ops - The operations to execute.
* @param beneficiary - The address to receive the fees.
*/
function handleOps(
PackedUserOperation[] calldata ops,
address payable beneficiary
) external;
/**
* Execute a batch of UserOperation with Aggregators
* @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts).
* @param beneficiary - The address to receive the fees.
*/
function handleAggregatedOps(
UserOpsPerAggregator[] calldata opsPerAggregator,
address payable beneficiary
) external;
/**
* Generate a request Id - unique identifier for this request.
* The request ID is a hash over the content of the userOp (except the signature), entrypoint address, chainId and (optionally) 7702 delegate address
* @param userOp - The user operation to generate the request ID for.
* @return hash the hash of this UserOperation
*/
function getUserOpHash(
PackedUserOperation calldata userOp
) external view returns (bytes32);
/**
* Gas and return values during simulation.
* @param preOpGas - The gas used for validation (including preValidationGas)
* @param prefund - The required prefund for this operation
* @param accountValidationData - returned validationData from account.
* @param paymasterValidationData - return validationData from paymaster.
* @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp)
*/
struct ReturnInfo {
uint256 preOpGas;
uint256 prefund;
uint256 accountValidationData;
uint256 paymasterValidationData;
bytes paymasterContext;
}
/**
* 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.
* @notice this method cannot be used for EIP-7702 derived contracts.
*
* @param initCode - The constructor code to be passed into the UserOperation.
*/
function getSenderAddress(bytes memory initCode) external;
error DelegateAndRevert(bool success, bytes ret);
/**
* Helper method for dry-run testing.
* @dev 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.
* @param target a target contract to make a delegatecall from entrypoint
* @param data data to pass to target in a delegatecall
*/
function delegateAndRevert(address target, bytes calldata data) external;
/**
* @notice Retrieves the immutable SenderCreator contract which is responsible for deployment of sender contracts.
*/
function senderCreator() external view returns (ISenderCreator);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface INonceManager {
/**
* 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.
*
* @param sender the account address
* @param key the high 192 bit of the nonce
* @return nonce a full nonce to pass for next UserOp with this sender.
*/
function getNonce(address sender, uint192 key)
external view returns (uint256 nonce);
/**
* 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.
*
* @param key - the "nonce key" to increment the "nonce sequence" for.
*/
function incrementNonce(uint192 key) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface ISenderCreator {
/**
* @dev Creates a new sender contract.
* @return sender Address of the newly created sender contract.
*/
function createSender(bytes calldata initCode) external returns (address sender);
/**
* Use initCallData to initialize an EIP-7702 account.
* The caller is the EntryPoint contract and it is already verified to be an EIP-7702 account.
* Note: Can be called multiple times as long as an appropriate initCode is supplied
*
* @param sender - the 'sender' EIP-7702 account to be initialized.
* @param initCallData - the call data to be passed to the sender account call.
*/
function initEip7702Sender(address sender, bytes calldata initCallData) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* Manage deposits and stakes.
* Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account).
* Stake is value locked for at least "unstakeDelay" by the staked entity.
*/
interface IStakeManager {
event Deposited(address indexed account, uint256 totalDeposit);
event Withdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
// Emitted when stake or unstake delay are modified.
event StakeLocked(
address indexed account,
uint256 totalStaked,
uint256 unstakeDelaySec
);
// Emitted once a stake is scheduled for withdrawal.
event StakeUnlocked(address indexed account, uint256 withdrawTime);
event StakeWithdrawn(
address indexed account,
address withdrawAddress,
uint256 amount
);
/**
* @param deposit - The entity's deposit.
* @param staked - True if this entity is staked.
* @param stake - Actual amount of ether staked for this entity.
* @param unstakeDelaySec - Minimum delay to withdraw the stake.
* @param withdrawTime - First block timestamp where 'withdrawStake' will be callable, or zero if already locked.
* @dev Sizes were chosen so that deposit fits into one cell (used during handleOp)
* and the rest fit into a 2nd cell (used during stake/unstake)
* - 112 bit allows for 10^15 eth
* - 48 bit for full timestamp
* - 32 bit allows 150 years for unstake delay
*/
struct DepositInfo {
uint256 deposit;
bool staked;
uint112 stake;
uint32 unstakeDelaySec;
uint48 withdrawTime;
}
// API struct used by getStakeInfo and simulateValidation.
struct StakeInfo {
uint256 stake;
uint256 unstakeDelaySec;
}
/**
* Get deposit info.
* @param account - The account to query.
* @return info - Full deposit information of given account.
*/
function getDepositInfo(
address account
) external view returns (DepositInfo memory info);
/**
* Get account balance.
* @param account - The account to query.
* @return - The deposit (for gas payment) of the account.
*/
function balanceOf(address account) external view returns (uint256);
/**
* Add to the deposit of the given account.
* @param account - The account to add to.
*/
function depositTo(address account) external payable;
/**
* Add to the account's stake - amount and delay
* any pending unstake is first cancelled.
* @param unstakeDelaySec - The new lock duration before the deposit can be withdrawn.
*/
function addStake(uint32 unstakeDelaySec) external payable;
/**
* Attempt to unlock the stake.
* The value can be withdrawn (using withdrawStake) after the unstake delay.
*/
function unlockStake() external;
/**
* Withdraw from the (unlocked) stake.
* Must first call unlockStake and wait for the unstakeDelay to pass.
* @param withdrawAddress - The address to send withdrawn value.
*/
function withdrawStake(address payable withdrawAddress) external;
/**
* Withdraw from the deposit.
* @param withdrawAddress - The address to send withdrawn value.
* @param withdrawAmount - The amount to withdraw.
*/
function withdrawTo(
address payable withdrawAddress,
uint256 withdrawAmount
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
/**
* User Operation struct
* @param sender - The sender account of this request.
* @param nonce - Unique value the sender uses to verify it is not a replay.
* @param initCode - If set, the account contract will be created by this constructor
* @param callData - The method call to execute on this account.
* @param accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
* @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid.
* Covers batch overhead.
* @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters.
* @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data
* The paymaster will pay for the transaction instead of the sender.
* @param signature - Sender-verified signature over the entire request, the EntryPoint address and the chain ID.
*/
struct PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees;
bytes paymasterAndData;
bytes signature;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
// solhint-disable no-inline-assembly
/**
* Utility functions helpful when making different kinds of contract calls in Solidity.
*/
library Exec {
function call(
address to,
uint256 value,
bytes memory data,
uint256 txGas
) internal returns (bool success) {
assembly ("memory-safe") {
success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
}
}
function staticcall(
address to,
bytes memory data,
uint256 txGas
) internal view returns (bool success) {
assembly ("memory-safe") {
success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0)
}
}
function delegateCall(
address to,
bytes memory data,
uint256 txGas
) internal returns (bool success) {
assembly ("memory-safe") {
success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
}
}
// get returned data from last call or delegateCall
// maxLen - maximum length of data to return, or zero, for the full length
function getReturnData(uint256 maxLen) internal pure returns (bytes memory returnData) {
assembly ("memory-safe") {
let len := returndatasize()
if gt(maxLen,0) {
if gt(len, maxLen) {
len := maxLen
}
}
let ptr := mload(0x40)
mstore(0x40, add(ptr, add(len, 0x20)))
mstore(ptr, len)
returndatacopy(add(ptr, 0x20), 0, len)
returnData := ptr
}
}
// revert with explicit byte array (probably reverted info from call)
function revertWithData(bytes memory returnData) internal pure {
assembly ("memory-safe") {
revert(add(returnData, 32), mload(returnData))
}
}
// Propagate revert data from last call
function revertWithReturnData() internal pure {
revertWithData(getReturnData(0));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC-1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC-1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(
bytes32 hash,
bytes memory signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures]
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC-1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC-1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
if (signer.code.length == 0) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return err == ECDSA.RecoverError.NoError && recovered == signer;
} else {
return isValidERC1271SignatureNow(signer, hash, signature);
}
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC-1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev 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[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.28;
import {BaseAccount} from "account-abstraction/core/BaseAccount.sol";
import {SIG_VALIDATION_FAILED, SIG_VALIDATION_SUCCESS} from "account-abstraction/core/Helpers.sol";
import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {TokenCallbackHandler} from "account-abstraction/accounts/callback/TokenCallbackHandler.sol";
import {UUPSUpgradeable} from "../external/solady/UUPSUpgradeable.sol";
import {ERC1271} from "./ERC1271.sol";
abstract contract BaseLightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, ERC1271 {
IEntryPoint internal immutable _ENTRY_POINT;
/// @notice Signature types used for user operation validation and ERC-1271 signature validation.
enum SignatureType {
EOA,
CONTRACT,
CONTRACT_WITH_ADDR
}
error ArrayLengthMismatch();
error CreateFailed();
error InvalidSignatureType();
error NotAuthorized(address caller);
error ZeroAddressNotAllowed();
modifier onlyAuthorized() {
_onlyAuthorized();
_;
}
// solhint-disable-next-line no-empty-blocks
receive() external payable virtual {}
/// @notice Execute a transaction. This may only be called directly by an owner or by the entry point via a user
/// operation signed by an owner.
/// @param dest The target of the transaction.
/// @param value The amount of wei sent in the transaction.
/// @param func The transaction's calldata.
function execute(address dest, uint256 value, bytes calldata func) external virtual override onlyAuthorized {
_call(dest, value, func);
}
/// @notice Execute a sequence of transactions.
/// @param dest An array of the targets for each transaction in the sequence.
/// @param func An array of calldata for each transaction in the sequence. Must be the same length as `dest`, with
/// corresponding elements representing the parameters for each transaction.
function executeBatch(address[] calldata dest, bytes[] calldata func) external virtual onlyAuthorized {
if (dest.length != func.length) {
revert ArrayLengthMismatch();
}
uint256 length = dest.length;
for (uint256 i = 0; i < length; ++i) {
_call(dest[i], 0, func[i]);
}
}
/// @notice Execute a sequence of transactions.
/// @param dest An array of the targets for each transaction in the sequence.
/// @param value An array of value for each transaction in the sequence.
/// @param func An array of calldata for each transaction in the sequence. Must be the same length as `dest`, with
/// corresponding elements representing the parameters for each transaction.
function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func)
external
virtual
onlyAuthorized
{
if (dest.length != func.length || dest.length != value.length) {
revert ArrayLengthMismatch();
}
uint256 length = dest.length;
for (uint256 i = 0; i < length; ++i) {
_call(dest[i], value[i], func[i]);
}
}
/// @notice Creates a contract.
/// @param value The value to send to the new contract constructor.
/// @param initCode The initCode to deploy.
/// @return createdAddr The created contract address.
///
/// @dev Assembly procedure:
/// 1. Load the free memory pointer.
/// 2. Get the initCode length.
/// 3. Copy the initCode from callata to memory at the free memory pointer.
/// 4. Create the contract.
/// 5. If creation failed (the address returned is zero), revert with CreateFailed().
function performCreate(uint256 value, bytes calldata initCode)
external
payable
virtual
onlyAuthorized
returns (address createdAddr)
{
assembly ("memory-safe") {
let fmp := mload(0x40)
let len := initCode.length
calldatacopy(fmp, initCode.offset, len)
createdAddr := create(value, fmp, len)
if iszero(createdAddr) {
mstore(0x00, 0x7e16b8cd)
revert(0x1c, 0x04)
}
}
}
/// @notice Creates a contract using create2 deterministic deployment.
/// @param value The value to send to the new contract constructor.
/// @param initCode The initCode to deploy.
/// @param salt The salt to use for the create2 operation.
/// @return createdAddr The created contract address.
///
/// @dev Assembly procedure:
/// 1. Load the free memory pointer.
/// 2. Get the initCode length.
/// 3. Copy the initCode from callata to memory at the free memory pointer.
/// 4. Create the contract using Create2 with the passed salt parameter.
/// 5. If creation failed (the address returned is zero), revert with CreateFailed().
function performCreate2(uint256 value, bytes calldata initCode, bytes32 salt)
external
payable
virtual
onlyAuthorized
returns (address createdAddr)
{
assembly ("memory-safe") {
let fmp := mload(0x40)
let len := initCode.length
calldatacopy(fmp, initCode.offset, len)
createdAddr := create2(value, fmp, len, salt)
if iszero(createdAddr) {
mstore(0x00, 0x7e16b8cd)
revert(0x1c, 0x04)
}
}
}
/// @notice Deposit more funds for this account in the entry point.
function addDeposit() external payable {
entryPoint().depositTo{value: msg.value}(address(this));
}
/// @notice Withdraw value from the account's deposit.
/// @param withdrawAddress Target to send to.
/// @param amount Amount to withdraw.
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) external onlyAuthorized {
if (withdrawAddress == address(0)) {
revert ZeroAddressNotAllowed();
}
entryPoint().withdrawTo(withdrawAddress, amount);
}
/// @notice Check current account deposit in the entry point.
/// @return The current account deposit.
function getDeposit() external view returns (uint256) {
return entryPoint().balanceOf(address(this));
}
/// @inheritdoc BaseAccount
function entryPoint() public view virtual override returns (IEntryPoint) {
return _ENTRY_POINT;
}
/// @dev Must override to allow calls to protected functions.
function _isFromOwner() internal view virtual returns (bool);
/// @dev Revert if the caller is not any of:
/// 1. The entry point
/// 2. The account itself (when redirected through `execute`, etc.)
/// 3. An owner
function _onlyAuthorized() internal view {
if (msg.sender != address(entryPoint()) && msg.sender != address(this) && !_isFromOwner()) {
revert NotAuthorized(msg.sender);
}
}
/// @dev Convert a boolean success value to a validation data value.
/// @param success The success value to be converted.
/// @return validationData The validation data value. 0 if success is true, 1 (SIG_VALIDATION_FAILED) if
/// success is false.
function _successToValidationData(bool success) internal pure returns (uint256 validationData) {
return success ? SIG_VALIDATION_SUCCESS : SIG_VALIDATION_FAILED;
}
/// @dev Assembly procedure:
/// 1. Execute the call, passing:
/// 1. The gas
/// 2. The target address
/// 3. The call value
/// 4. The pointer to the start location of the callData in memory
/// 5. The length of the calldata
/// 2. If the call failed, bubble up the revert reason by doing the following:
/// 1. Load the free memory pointer
/// 2. Copy the return data (which is the revert reason) to memory at the free memory pointer
/// 3. Revert with the copied return data
function _call(address target, uint256 value, bytes memory data) internal {
assembly ("memory-safe") {
let succ := call(gas(), target, value, add(data, 0x20), mload(data), 0x00, 0)
if iszero(succ) {
let fmp := mload(0x40)
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
}
}
function _authorizeUpgrade(address newImplementation) internal view override onlyAuthorized {
(newImplementation);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.28;
/// @dev Identical to OpenZeppelin's `Initializable`, except that custom storage slots can be used.
///
/// This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
/// behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
/// external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
/// function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
///
/// The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
/// reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
/// case an upgrade adds a module that needs to be initialized.
///
/// For example:
///
/// [.hljs-theme-light.nopadding]
/// ```solidity
/// contract MyToken is ERC20Upgradeable {
/// function initialize() initializer public {
/// __ERC20_init("MyToken", "MTK");
/// }
/// }
///
/// contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
/// function initializeV2() reinitializer(2) public {
/// __ERC20Permit_init("MyToken");
/// }
/// }
/// ```
///
/// TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
/// possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
///
/// CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
/// that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
///
/// [CAUTION]
/// ====
/// Avoid leaving a contract uninitialized.
///
/// An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
/// contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
/// the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
///
/// [.hljs-theme-light.nopadding]
/// ```
/// /// @custom:oz-upgrades-unsafe-allow constructor
/// constructor() {
/// _disableInitializers();
/// }
/// ```
/// ====
abstract contract CustomSlotInitializable {
bytes32 internal immutable _storagePosition;
struct CustomSlotInitializableStorage {
/// @dev Indicates that the contract has been initialized.
/// @custom:oz-retyped-from bool
uint64 initialized;
/// @dev Indicates that the contract is in the process of being initialized.
bool initializing;
}
/// @dev The contract is already initialized.
error InvalidInitialization();
/// @dev The contract is not initializing.
error NotInitializing();
/// @dev Triggered when the contract has been initialized or reinitialized.
event Initialized(uint64 version);
constructor(bytes32 storagePosition) {
_storagePosition = storagePosition;
}
/// @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
/// `onlyInitializing` functions can be used to initialize parent contracts.
///
/// Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
/// constructor.
///
/// Emits an {Initialized} event.
modifier initializer() {
CustomSlotInitializableStorage storage _storage = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !_storage.initializing;
uint64 initialized = _storage.initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
_storage.initialized = 1;
if (isTopLevelCall) {
_storage.initializing = true;
}
_;
if (isTopLevelCall) {
_storage.initializing = false;
emit Initialized(1);
}
}
/// @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
/// contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
/// used to initialize parent contracts.
///
/// A reinitializer may be used after the original initialization step. This is essential to configure modules that
/// are added through upgrades and that require initialization.
///
/// When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
/// cannot be nested. If one is invoked in the context of another, execution will revert.
///
/// Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
/// a contract, executing them in the right order is up to the developer or operator.
///
/// WARNING: setting the version to type(uint64).max will prevent any future reinitialization.
///
/// Emits an {Initialized} event.
modifier reinitializer(uint64 version) {
CustomSlotInitializableStorage storage _storage = _getInitializableStorage();
if (_storage.initializing || _storage.initialized >= version) {
revert InvalidInitialization();
}
_storage.initialized = version;
_storage.initializing = true;
_;
_storage.initializing = false;
emit Initialized(version);
}
/// @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
/// {initializer} and {reinitializer} modifiers, directly or indirectly.
modifier onlyInitializing() {
_checkInitializing();
_;
}
/// @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/// @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
/// Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
/// to any version. It is recommended to use this to lock implementation contracts that are designed to be called
/// through proxies.
///
/// Emits an {Initialized} event the first time it is successfully executed.
function _disableInitializers() internal virtual {
CustomSlotInitializableStorage storage _storage = _getInitializableStorage();
if (_storage.initializing) {
revert InvalidInitialization();
}
if (_storage.initialized != type(uint64).max) {
_storage.initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/// @dev Returns the highest version that has been initialized. See {reinitializer}.
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage().initialized;
}
/// @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage().initializing;
}
function _getInitializableStorage() private view returns (CustomSlotInitializableStorage storage _storage) {
bytes32 position = _storagePosition;
assembly ("memory-safe") {
_storage.slot := position
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {EIP712} from "../external/solady/EIP712.sol";
abstract contract ERC1271 is EIP712 {
/// @dev bytes4(keccak256("isValidSignature(bytes32,bytes)"))
bytes4 internal constant _1271_MAGIC_VALUE_SUCCESS = 0x1626ba7e;
bytes4 internal constant _1271_MAGIC_VALUE_FAILURE = 0xffffffff;
bytes32 internal constant _MESSAGE_TYPEHASH = keccak256("LightAccountMessage(bytes message)");
/// @notice Returns the replay-safe hash of a message that can be signed by owners.
/// @param message Message that should be hashed.
/// @return The replay-safe message hash.
function getMessageHash(bytes memory message) public view returns (bytes32) {
bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, keccak256(message)));
return _hashTypedData(structHash);
}
/// @dev The signature is valid if it is signed by the owner's private key (if the owner is an EOA) or if it is
/// a valid ERC-1271 signature from the owner (if the owner is a contract).
/// @param hash Hash of the data to be signed.
/// @param signature Signature byte array associated with the data.
/// @return Magic value `0x1626ba7e` if validation succeeded, else `0xffffffff`.
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4) {
if (_isValidSignature(getMessageHash(abi.encode(hash)), signature)) {
return _1271_MAGIC_VALUE_SUCCESS;
}
return _1271_MAGIC_VALUE_FAILURE;
}
/// @dev Must override to provide the signature verification logic.
/// @param replaySafeHash The replay-safe hash that is derived from the original message.
/// @param signature The signature passed to `isValidSignature`.
/// @return Whether the signature is valid.
function _isValidSignature(bytes32 replaySafeHash, bytes calldata signature) internal view virtual returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
uint256 cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice UUPS proxy mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol)
/// @author Modified from OpenZeppelin
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol)
///
/// Note:
/// - This implementation is intended to be used with ERC1967 proxies.
/// See: `LibClone.deployERC1967` and related functions.
/// - This implementation is NOT compatible with legacy OpenZeppelin proxies
/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`.
abstract contract UUPSUpgradeable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The upgrade failed.
error UpgradeFailed();
/// @dev The call is from an unauthorized call context.
error UnauthorizedCallContext();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For checking if the context is a delegate call.
uint256 private immutable __self = uint256(uint160(address(this)));
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when the proxy's implementation is upgraded.
event Upgraded(address indexed implementation);
/// @dev `keccak256(bytes("Upgraded(address)"))`.
uint256 private constant _UPGRADED_EVENT_SIGNATURE =
0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ERC-1967 storage slot for the implementation in the proxy.
/// `uint256(keccak256("eip1967.proxy.implementation")) - 1`.
bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UUPS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to check if `msg.sender` is authorized
/// to upgrade the proxy to `newImplementation`, reverting if not.
/// ```
/// function _authorizeUpgrade(address) internal override onlyOwner {}
/// ```
function _authorizeUpgrade(address newImplementation) internal virtual;
/// @dev Returns the storage slot used by the implementation,
/// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822).
///
/// Note: The `notDelegated` modifier prevents accidental upgrades to
/// an implementation that is a proxy contract.
function proxiableUUID() public view virtual notDelegated returns (bytes32) {
// This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967.
return _ERC1967_IMPLEMENTATION_SLOT;
}
/// @dev Upgrades the proxy's implementation to `newImplementation`.
/// Emits a {Upgraded} event.
///
/// Note: Passing in empty `data` skips the delegatecall to `newImplementation`.
function upgradeToAndCall(address newImplementation, bytes calldata data)
public
payable
virtual
onlyProxy
{
_authorizeUpgrade(newImplementation);
/// @solidity memory-safe-assembly
assembly {
newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits.
mstore(0x01, 0x52d1902d) // `proxiableUUID()`.
let s := _ERC1967_IMPLEMENTATION_SLOT
// Check if `newImplementation` implements `proxiableUUID` correctly.
if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) {
mstore(0x01, 0x55299b49) // `UpgradeFailed()`.
revert(0x1d, 0x04)
}
// Emit the {Upgraded} event.
log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation)
sstore(s, newImplementation) // Updates the implementation.
// Perform a delegatecall to `newImplementation` if `data` is non-empty.
if data.length {
// Forwards the `data` to `newImplementation` via delegatecall.
let m := mload(0x40)
calldatacopy(m, data.offset, data.length)
if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00))
{
// Bubble up the revert if the call reverts.
returndatacopy(m, 0x00, returndatasize())
revert(m, returndatasize())
}
}
}
}
/// @dev Requires that the execution is performed through a proxy.
modifier onlyProxy() {
uint256 s = __self;
/// @solidity memory-safe-assembly
assembly {
// To enable use cases with an immutable default implementation in the bytecode,
// (see: ERC6551Proxy), we don't require that the proxy address must match the
// value stored in the implementation slot, which may not be initialized.
if eq(s, address()) {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
_;
}
/// @dev Requires that the execution is NOT performed via delegatecall.
/// This is the opposite of `onlyProxy`.
modifier notDelegated() {
uint256 s = __self;
/// @solidity memory-safe-assembly
assembly {
if iszero(eq(s, address())) {
mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`.
revert(0x1c, 0x04)
}
}
_;
}
}{
"viaIR": true,
"metadata": {
"appendCBOR": true,
"bytecodeHash": "ipfs",
"useLiteralContent": false
},
"optimizer": {
"runs": 10000000,
"enabled": true
},
"evmVersion": "cancun",
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"account-abstraction/=lib/account-abstraction/contracts/",
"modular-account/=lib/modular-account/src/",
"@alchemy/light-account/=lib/modular-account/lib/light-account/",
"@eth-infinitism/account-abstraction/=lib/modular-account/lib/account-abstraction/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"light-account/=lib/modular-account/lib/light-account/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/modular-account/lib/openzeppelin-contracts/contracts/"
],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract ABI
API[{"inputs":[{"internalType":"contract IEntryPoint","name":"entryPoint_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CreateFailed","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"error","type":"bytes"}],"name":"ExecuteError","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"InvalidOwner","type":"error"},{"inputs":[],"name":"InvalidSignatureType","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"UnauthorizedCallContext","type":"error"},{"inputs":[],"name":"UpgradeFailed","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IEntryPoint","name":"entryPoint","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"LightAccountInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"addDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"contract IEntryPoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dest","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"func","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"dest","type":"address[]"},{"internalType":"bytes[]","name":"func","type":"bytes[]"}],"name":"executeBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct BaseAccount.Call[]","name":"calls","type":"tuple[]"}],"name":"executeBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"dest","type":"address[]"},{"internalType":"uint256[]","name":"value","type":"uint256[]"},{"internalType":"bytes[]","name":"func","type":"bytes[]"}],"name":"executeBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"message","type":"bytes"}],"name":"getMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"}],"name":"performCreate","outputs":[{"internalType":"address","name":"createdAddr","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"performCreate2","outputs":[{"internalType":"address","name":"createdAddr","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","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"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"withdrawAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawDepositTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.