aere-contracts/contracts/passkey/AerePasskeyAccountFactory.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

46 lines
2.3 KiB
Solidity

// 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);
}
}