Skip to content

Contracts Overview

Onchain architecture

NiceTry's onchain footprint is deliberately small: a stateless verifier, an ERC-4337 account, and a deterministic factory. Each FORS+C account is a minimal proxy that stores one signer commitment and rotates it on every UserOp.

Contract map

ContractFileRole
ForsVerifiersrc/Verifiers/ForsVerifier.solStateless FORS+C verifier: recover(sig, digest) → address
SimpleAccountsrc/SimpleAccount.solERC-4337 account; verifies + rotates owner on every UserOp
SimpleAccountFactorysrc/SimpleAccountFactory.solCREATE2 factory deploying account clones
InitialSignerCommitmentsrc/InitialSignerCommitment.solDomain separators for salts and activation leaves
ISignatureVerifiersrc/Interfaces/ISignatureVerifier.solVerifier interface (lets the account stay scheme-agnostic)
FrameAccountsrc/FrameAccount.solEIP-8141 frame-native variant of the same rule

How they fit together

SimpleAccountFactory  --(CREATE2 clone)-->  SimpleAccount (EIP-1167 proxy)
                                                  |
                                                  | validateUserOp
                                                  v
                                            ISignatureVerifier.recover()  ->  ForsVerifier
                                                  |
                                                  v
                                            compare to owner, rotate to nextOwner

The account talks to the verifier only through ISignatureVerifier, so the signing scheme is a pluggable dependency 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);
}

The ForsVerifier is immutable and shared: one deployment serves every account, and the account holds it in an immutable VERIFIER set at construction.

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: the owner commitment advances inside validateUserOp, so a key is retired even if the inner call reverts. See Standards → ERC-4337.
  • The nextOwner lives in calldata: every UserOp's callData ends with bytes20(nextOwner), so the signed userOpHash commits to the next signer.
  • Deterministic addresses: verifier, factory, and account implementation are deployed via CREATE2 so they share addresses across chains.
  • Toolchain: solc 0.8.30, optimizer on, via-ir enabled; account-abstraction, OpenZeppelin, and Solady as dependencies.

ERC-7579 module variant

For the legacy schemes, the rotation-validation logic is also packaged as ERC-7579 validator modules (KernelRotatingECDSAValidator, KernelRotatingWOTSValidator), so it can run inside an existing modular account (e.g. Kernel, Nexus) without deploying a new one. Module state is keyed by account address as the outermost mapping key to satisfy ERC-7562 storage rules. The ERC-7579 interfaces and mock-account integrations live under other-implementations/kernel/.

A FORS+C validator module is not implemented yet: today the FORS+C path ships only as the standalone SimpleAccount.