aere-contracts/contracts/mpc/AereThresholdAccountFactory.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

62 lines
2.5 KiB
Solidity

// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity 0.8.23;
import {AereThresholdAccount} from "./AereThresholdAccount.sol";
/// @title AereThresholdAccountFactory
/// @notice Deterministic (CREATE2) factory for non-custodial post-quantum threshold accounts.
/// The account address is a pure function of (entryPoint, scheme, threshold, pubKeys,
/// salt), so it can be counterfactually funded before deployment. Permissionless; the
/// factory holds no authority over any account it deploys.
contract AereThresholdAccountFactory {
event AccountCreated(address indexed account, uint8 scheme, uint8 threshold, uint8 size, bytes32 salt);
/// @notice Deploy (or return the existing) threshold account for the given committee + salt.
function createAccount(
address entryPoint,
uint8 scheme,
uint8 threshold,
bytes[] calldata pubKeys,
bytes32 salt
) external returns (address account) {
bytes32 create2Salt = _salt(entryPoint, scheme, threshold, pubKeys, salt);
address predicted = _addressFor(create2Salt);
if (predicted.code.length > 0) {
return predicted; // idempotent
}
AereThresholdAccount acc = new AereThresholdAccount{salt: create2Salt}();
acc.initialize(entryPoint, scheme, threshold, pubKeys);
account = address(acc);
require(account == predicted, "addr mismatch");
emit AccountCreated(account, scheme, threshold, uint8(pubKeys.length), salt);
}
/// @notice The counterfactual address of the account for the given committee + salt.
function computeAddress(
address entryPoint,
uint8 scheme,
uint8 threshold,
bytes[] calldata pubKeys,
bytes32 salt
) external view returns (address) {
return _addressFor(_salt(entryPoint, scheme, threshold, pubKeys, salt));
}
function _salt(
address entryPoint,
uint8 scheme,
uint8 threshold,
bytes[] calldata pubKeys,
bytes32 salt
) internal pure returns (bytes32) {
return keccak256(abi.encode(entryPoint, scheme, threshold, keccak256(abi.encode(pubKeys)), salt));
}
function _addressFor(bytes32 create2Salt) internal view returns (address) {
bytes32 h = keccak256(
abi.encodePacked(bytes1(0xff), address(this), create2Salt, keccak256(type(AereThresholdAccount).creationCode))
);
return address(uint160(uint256(h)));
}
}