// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {AerePasskeyAccount} from "./AerePasskeyAccount.sol"; /// @title AerePasskeyAccountFactory — CREATE2 deployer for passkey-controlled accounts /// @notice One factory per chain. Given a P-256 public key + salt, deterministically /// computes (and on first call, deploys) the AerePasskeyAccount address. /// /// The salt allows the same passkey to control multiple AERE addresses /// (e.g., personal/business/savings) without provisioning multiple passkeys. contract AerePasskeyAccountFactory { /// @notice Reference implementation bytecode hash — for offchain getAddress() helpers. bytes32 public constant ACCOUNT_INIT_CODE_HASH = keccak256(type(AerePasskeyAccount).creationCode); event AccountCreated(address indexed account, uint256 indexed pubkeyX, uint256 indexed pubkeyY, uint256 salt); /// @notice Returns the deterministic address of the account for (pubkey, salt), /// whether or not it's been deployed yet. function predictAddress(uint256 pubkeyX, uint256 pubkeyY, uint256 salt) public view returns (address) { bytes32 saltMix = keccak256(abi.encode(pubkeyX, pubkeyY, salt)); return address(uint160(uint256(keccak256(abi.encodePacked( bytes1(0xff), address(this), saltMix, ACCOUNT_INIT_CODE_HASH ))))); } /// @notice Deploys the AerePasskeyAccount for (pubkey, salt) if not yet deployed, /// then initializes it with the passkey. Idempotent — if already deployed, /// returns the existing address without reverting. function createAccount(uint256 pubkeyX, uint256 pubkeyY, uint256 salt) external returns (AerePasskeyAccount account) { address predicted = predictAddress(pubkeyX, pubkeyY, salt); if (predicted.code.length > 0) { // Already deployed (idempotent). return AerePasskeyAccount(payable(predicted)); } bytes32 saltMix = keccak256(abi.encode(pubkeyX, pubkeyY, salt)); account = new AerePasskeyAccount{salt: saltMix}(); account.initialize(pubkeyX, pubkeyY); require(address(account) == predicted, "factory: address mismatch"); emit AccountCreated(address(account), pubkeyX, pubkeyY, salt); } }