Contracts Overview
NiceTry's onchain footprint is deliberately small: three stateless verifiers and two independent account families, each with its own deterministic factory. The main family, SimpleAccount, is a minimal proxy that tracks per-key authorisation state for its rotating FORS+C signers and holds one static SPHINCS backup key. The second family, SphincsAccount, holds a single standard SPHINCS key and nothing else.
Contract map
| Contract | File | Role |
|---|---|---|
ForsVerifier | src/Verifiers/ForsVerifier.sol | Stateless FORS+C verifier: recover(sig, digest) → address |
SphincsVerifier | src/Verifiers/SphincsVerifier.sol | Stateless SPHINCS backup verifier (FORS+C under WOTS+C, 3,688 B): verify(pkSeed, pkRoot, digest, sig) → bool |
SphincsStandardVerifier | src/Verifiers/SphincsStandardVerifier.sol | Stateless standard SPHINCS verifier (plain FORS + WOTS+, 6,176 B), same verify ABI |
SimpleAccount | src/SimpleAccount.sol | ERC-4337 account: FORS+C primary, SPHINCS backup, per-key authState, addSigner |
SimpleAccountFactory | src/SimpleAccountFactory.sol | CREATE2 clone factory; address commits to root and backup key |
SphincsAccount | src/SphincsAccount.sol | ERC-4337 account with a single, never-rotated SPHINCS key |
SphincsAccountFactory | src/SphincsAccountFactory.sol | CREATE2 clone factory; address commits to the key |
InitialSignerCommitment | src/InitialSignerCommitment.sol | Domain separators for the SimpleAccount salt, the activation leaf and the backup-key leaf |
ISignatureVerifier | src/Interfaces/ISignatureVerifier.sol | FORS+C verifier interface (recover) |
ISphincsVerifier | src/Interfaces/ISphincsVerifier.sol | SPHINCS verifier interface (verify), implemented by both SPHINCS verifiers |
The two families never share an address, an implementation or a salt domain. Everything below describes SimpleAccount; the much smaller SphincsAccount picture is on its own page.
How they fit together
SimpleAccountFactory --(CREATE2 clone)--> SimpleAccount (EIP-1167 proxy)
|
| validateUserOp: dispatch on signature length
|
+-----------------------+-----------------------+
| 2,448 B | 2,451 + 32n B | 3,688 B
v v v
ForsVerifier.recover() recover() + MerkleProofLib SphincsVerifier.verify()
| | |
v v v
authState[key] ACTIVE? leaf in initialSignerRoot? (backupPkSeed, backupPkRoot)
| | |
+-----------------------+-----------------------+
|
v
burn current key, activate nextOwnerThe account talks to the verifiers only through their interfaces, so the signing schemes are pluggable dependencies rather than hard-wired logic:
interface ISignatureVerifier {
/// @return signer The recovered signer address, or address(0) on failure.
function recover(bytes calldata sig, bytes32 digest) external view returns (address signer);
}
interface ISphincsVerifier {
/// @return valid True iff sig verifies over message under (pkSeed, pkRoot).
function verify(bytes32 pkSeed, bytes32 pkRoot, bytes32 message, bytes calldata sig)
external view returns (bool valid);
}All verifiers are immutable and shared: one deployment of each serves every account. SimpleAccount holds its two in immutable VERIFIER and immutable SPHINCS_VERIFIER; SphincsAccount holds the standard verifier in immutable VERIFIER.
Conventions shared across the contracts
- EntryPoint: the canonical ERC-4337 EntryPoint v0.7, identical-address across mainnet, Sepolia, and major rollups.
- Rotation at validation time: key state advances inside
validateUserOp, so a key is retired even if the inner call reverts. See Standards → ERC-4337. - Next key in calldata: every UserOp's
callDataends withbytes20(nextOwner)(FORS+C and activation) orbytes20(currentKey) ‖ bytes20(nextOwner)(SPHINCS), so the signeduserOpHashcommits to the rotation. - Dispatch by length, no type tag: 2,448 bytes is FORS+C, 3,688 is SPHINCS, anything of the form 2,451 + 32·n is an activation envelope. The classes are disjoint by construction, and the 6,176-byte
SphincsAccountblob is disjoint from all of them. See Signature dispatch. - Deterministic addresses: verifiers, factories, and account implementations are deployed via CREATE2 so they share addresses across chains. See Multichain addresses.
- Toolchain:
solc 0.8.30, optimizer (200 runs),via-ir;account-abstraction(v0.7BaseAccount), OpenZeppelin (Initializable), and Solady (LibClone,MerkleProofLib) as dependencies.lib/kernelis kept as a submodule only because it vendors Solady.
What is no longer in the main line
The ECDSA and WOTS+C accounts, the ZeroDev Kernel / Nexus ERC-7579 validator modules, the EIP-8141 FrameAccount draft, and the stateful SphincsIndexedVerifier were removed from main and are preserved on the archive/dev-pre-cleanup branch. There is currently no ERC-7579 module for the FORS+C path: it ships only as the standalone SimpleAccount. See Archived designs.