Standards & EIPs
NEUS runs a single hub on Base Sepolia (chainId = 84532) today. All SDK/API payloads use numeric chain IDs. We do not pass CAIP strings in requests. Off-chain auth uses EIP-191 with a canonical NEUS message.
We support EOAs and smart accounts (EIP-1271 / EIP-6492). Cross-chain propagation uses ERC-7683-compatible vouchers anchored by qHash.
When we move to mainnet, only hub and target chains change and proof data remains stable.
Core Standards
Chain IDs
Numeric chain IDs only in SDK/API and in signed messages (e.g.,
1,8453,84532).Hub (enforced):
PRIMARY_CHAIN_ID = 84532(Base Sepolia).
Identity (DIDs)
did:pkh (
did:pkh:eip155:<chainId>:<address>) stored and emitted in proofs.We may show CAIP strings in UI/docs, but we don’t send them in requests and don’t store them except as part of the DID string.
Off-chain Signatures (used today)
EIP-191 (“personal_sign”) with a canonical NEUS message:
NEUS Verification Request Wallet: 0x<lowercased address> Chain: 84532 Verifiers: ownership-basic, ... Data: <deterministic JSON of the request payload> Timestamp: <unix ms, freshness window enforced>Server rebuilds/validates this and enforces
PRIMARY_CHAIN_ID(84532).
Signature Contract (Normative)
Signature standard: EIP-191 (“personal_sign”).
Hub chain (enforced): 84532 (Base Sepolia).
Smart accounts: EIP-1271 and EIP-6492 are supported server-side.
Canonical message (exactly 6 lines, exact order)
NEUS Verification Request
Wallet: 0x<lowercased address>
Chain: 84532
Verifiers: <comma-separated verifier ids>
Data: <deterministic JSON of request.data>
Timestamp: <unix ms>– Wallet MUST be lowercased inside the message.
– Chain MUST be the numeric hub chain id (84532).
– Verifiers MUST list the same IDs sent in verifierIds.
– Data MUST be the deterministic JSON of the data object only.
– Timestamp MUST be Unix milliseconds.
Deterministic JSON (Normative)
– Objects: sort keys ordinal; omit undefined; keep null.
– Arrays: preserve order; canonicalize elements recursively.
– Strings: standard JSON escaping.
– Numbers: culture-invariant decimal with a dot (.).
– No extra whitespace (no pretty print).
What is signed vs. what is sent
– The six-line message above is what gets signed.
– The HTTP body includes walletAddress, signature, signedTimestamp, chainId, verifierIds, data, [options...].
– options are not included in the signed message.
Freshness and errors
– Freshness window: signatures older than 5 minutes or >1 minute in the future are rejected.
– Typical errors: SIGNATURE_VERIFICATION_FAILED, SIGNATURE_EXPIRED, PAYLOAD_INVALID.
Smart Account Signatures
EIP-1271: contract-wallet validation when EOA recovery fails.
EIP-6492: detect/unwrap pre-deployment (counterfactual) signatures.
Both are implemented.
On-chain Transactions
EIP-155 applies automatically to on-chain tx (e.g., the hub transaction stored in
hubTransaction).We do not use EIP-155 for off-chain auth.
Cross-chain Vouchers (propagation)
ERC-7683-compatible, one voucher per target chain, all anchored by
qHash.struct Voucher { bytes32 qHash; // NEUS verification anchor uint256 sourceChainId; // hub chain (84532 today) uint256 targetChainId; // numeric address relayer; uint256 expiry; bytes proof; // opaque verifier/proof payload }
Cryptography
SHAKE-256 →
qHash(anchor for proofs/vouchers/storage references).ECDSA secp256k1 → wallet signatures; EIP-1271/6492 for smart accounts.
What’s not used right now
CAIP-2 in requests: Not used. CAIP for docs/DIDs only.
EIP-712: Not used yet. (Planned; see “Future” below.)
Minimal examples
SDK/API payload
const proof = await client.verify({
verifier: 'ownership-basic',
content: 'Hello NEUS...',
options: {
// spoke chains (if any). hub is implicit (84532) and enforced server-side.
targetChains: [421614, 11155111],
privacyLevel: 'public',
storeOriginalContent: true,
enableIpfs: true
},
meta: {
tags: ['campaign', 'proof-of-support']
}
});Stored proof (key fields)
did:did:pkh:eip155:84532:<wallet>hubTransaction.chainId:84532verifierContentHash: hex stringoptions.privacyLevel:'public' | 'private'Originals stored only when
privacyLevel === 'public'andstoreOriginalContent === true.
Implementation checklist
Chain IDs: numeric everywhere in code; do not send CAIP strings in requests.
Hub enforcement: server validates
Chain: 84532in the signed message.Signatures: EIP-191 canonical message; freshness window enforced; address lowercased in message.
Smart accounts: support EIP-1271 and EIP-6492 paths.
Vouchers: ERC-7683-compatible; one target per voucher; anchor by
qHash.Content proofs: verifiers emit hashed content
verifierContentHash; original content stored only ifpublic+storeOriginalContent=true.OAuth: code + PKCE; do not retain tokens; store only non-sensitive public identifiers if needed.
Future (what we’ll add when ready)
Hub switch: changed to Base mainnet; everything else unchanged.
Additional spokes: expand
targetChainsset (still numeric).ZK circuits: more verifiers using zk (RISC-Zero/STARK); current verifiers may mark
zk_not_required.
Last updated
Was this helpful?

