How server-side nonces prevent replay attacks on PINTs
Every PINT includes a nonce field that prevents replay attacks. The maintains a server-side nonce registry per wallet, ensuring each PINT can only be processed once.
Each wallet address has a monotonically increasing nonce counter maintained by the SIS. When a PINT is submitted for token exchange, the nonce in the PINT must be strictly greater than the wallet’s last-used nonce.
Wallet: 0xE23c...Last nonce: 41PINT with nonce 42 → ✅ Accepted (42 > 41). Last nonce updated to 42.PINT with nonce 42 → ❌ Rejected (42 = 42). Error: PINT-409-001PINT with nonce 40 → ❌ Rejected (40 < 42). Error: PINT-409-001PINT with nonce 50 → ✅ Accepted (50 > 42). Last nonce updated to 50.
Nonces must increase. Each PINT’s nonce must be strictly greater than the wallet’s current last_nonce. Equal or lower values are rejected with 409 Conflict and error code PINT-409-001.Gaps are allowed. You don’t need to use sequential nonces. Jumping from nonce 5 to nonce 8 is valid. This supports concurrent signing workflows where multiple PINTs may be prepared in parallel but submitted in unpredictable order.Atomic validation. The nonce check and update happen atomically — there’s no window for race conditions when multiple PINT exchanges are submitted simultaneously for the same wallet.Per-wallet scope. Nonce counters are scoped to individual wallet addresses. Different wallets have independent nonce sequences.
Track the last-used nonce locally (or retrieve the current nonce from the PINT list endpoint)
Set the PINT’s nonce to a value greater than the last-used nonce
If the exchange returns PINT-409-001, increment the nonce and retry
A simple approach is to use Unix millisecond timestamps as nonces — they’re naturally monotonically increasing and allow concurrent operations without coordination:
The nonce is a uint256 in the EIP-712 schema. Make sure your implementation handles large integers correctly — JavaScript’s Number type will lose precision above 2^53. Use BigInt in JavaScript/TypeScript.