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.
46 lines
2.3 KiB
Solidity
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);
|
|
}
|
|
}
|