Last active
May 20, 2025 08:08
-
-
Save mingder78/ee761cb77a3748c2469ec1b9ff396e5f to your computer and use it in GitHub Desktop.
AA 4337 Wallet https://x.com/i/grok/share/UMpepPjqcgAfZyqwT0y1qsVl0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.20; | |
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | |
import "@openzeppelin/contracts/interfaces/IERC1271.sol"; | |
import "./IEntryPoint.sol"; | |
// Simple Account Abstraction wallet with passkey-based signature verification | |
contract AAWallet is IERC1271 { | |
using ECDSA for bytes32; | |
// Owner's public key (or passkey-derived public key) | |
address public immutable owner; | |
// EntryPoint contract address | |
IEntryPoint public immutable entryPoint; | |
// Nonce for replay protection | |
uint256 public nonce; | |
// Events | |
event TransactionExecuted(address indexed target, uint256 value, bytes data); | |
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); | |
// Errors | |
error Unauthorized(); | |
error InvalidSignature(); | |
error InvalidEntryPoint(); | |
error ExecutionFailed(); | |
// Constructor: Initialize owner and EntryPoint | |
constructor(address _owner, address _entryPoint) { | |
owner = _owner; | |
entryPoint = IEntryPoint(_entryPoint); | |
} | |
// Modifier to restrict access to EntryPoint | |
modifier onlyEntryPoint() { | |
if (msg.sender != address(entryPoint)) { | |
revert InvalidEntryPoint(); | |
} | |
_; | |
} | |
// Fallback function to receive ETH | |
receive() external payable {} | |
// Validate UserOperation (called by EntryPoint) | |
function validateUserOp( | |
UserOperation calldata userOp, | |
bytes32 userOpHash, | |
uint256 missingAccountFunds | |
) external onlyEntryPoint returns (uint256 validationData) { | |
// Verify passkey signature | |
if (!_verifySignature(userOpHash, userOp.signature)) { | |
revert InvalidSignature(); | |
} | |
// Increment nonce | |
nonce++; | |
// Pay prefunded gas if needed | |
if (missingAccountFunds > 0) { | |
(bool success, ) = payable(msg.sender).call{value: missingAccountFunds}(""); | |
require(success, "Funding failed"); | |
} | |
// Return 0 for valid signature | |
return 0; | |
} | |
// Execute transaction (called by EntryPoint) | |
function execute( | |
address target, | |
uint256 value, | |
bytes calldata data | |
) external onlyEntryPoint { | |
(bool success, ) = target.call{value: value}(data); | |
if (!success) { | |
revert ExecutionFailed(); | |
} | |
emit TransactionExecuted(target, value, data); | |
} | |
// Verify passkey signature (ECDSA-based) | |
function _verifySignature(bytes32 hash, bytes memory signature) internal view returns (bool) { | |
bytes32 ethSignedHash = hash.toEthSignedMessageHash(); | |
address signer = ethSignedHash.recover(signature); | |
return signer == owner; | |
} | |
// IERC1271: Validate signature for smart contract wallets | |
function isValidSignature( | |
bytes32 hash, | |
bytes memory signature | |
) external view override returns (bytes4 magicValue) { | |
if (_verifySignature(hash, signature)) { | |
return 0x1626ba7e; // IERC1271 magic value | |
} | |
return 0xffffffff; | |
} | |
// Transfer ownership to a new passkey-derived public key | |
function transferOwnership(address newOwner) external { | |
if (msg.sender != address(this)) { | |
revert Unauthorized(); | |
} | |
emit OwnershipTransferred(owner, newOwner); | |
// Note: Owner is immutable, so this would require a new deployment or a proxy pattern | |
} | |
// Get current nonce | |
function getNonce() external view returns (uint256) { | |
return nonce; | |
} | |
} | |
// Minimal ERC-4337 UserOperation struct | |
struct UserOperation { | |
address sender; | |
uint256 nonce; | |
bytes initCode; | |
bytes callData; | |
uint256 callGasLimit; | |
uint256 verificationGasLimit; | |
uint256 preVerificationGas; | |
uint256 maxFeePerGas; | |
uint256 maxPriorityFeePerGas; | |
bytes paymasterAndData; | |
bytes signature; | |
} | |
// IEntryPoint interface (simplified) | |
interface IEntryPoint { | |
function handleOps(UserOperation[] calldata ops, address payable beneficiary) external; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment