UPP — Universal Private PoolSDK Reference
Note Indexer
makeRpcIndexer — scanning the blockchain for encrypted notes belonging to your account.
Note Indexer
The note indexer scans the blockchain for NoteCreated events, attempts to decrypt each one using your viewing key, and returns notes that belong to you.
makeRpcIndexer
import { makeRpcIndexer } from '@permissionless-technologies/upp-sdk/indexer'
const indexer = makeRpcIndexer({
publicClient, // viem PublicClient
contractAddress: Address, // UPP contract address
fromBlock?: bigint, // start block (default: contract deployment block)
})Usage
// Scan for notes owned by this account
const { notes, lastScannedBlock } = await indexer.scan({
viewingKey: myViewingKey, // derived from wallet signature
fromBlock?: bigint, // optional incremental scan
})Incremental Scanning
Store the last scanned block to avoid rescanning from genesis:
const lastBlock = localStorage.getItem('lastScannedBlock')
const { notes, lastScannedBlock } = await indexer.scan({
viewingKey,
fromBlock: lastBlock ? BigInt(lastBlock) : undefined,
})
localStorage.setItem('lastScannedBlock', lastScannedBlock.toString())How It Works
- Fetch all
NoteCreated(commitment, encryptedNote)events from the contract - For each event, extract the ephemeral public key R from the encrypted data
- Compute the ECDH shared secret:
shared = viewingKey * R - Check the 64-bit search tag (filter ~99% of non-matching notes without decrypting)
- For tag matches, attempt AES-GCM decryption
- Verify the decrypted note against the on-chain commitment
- Return verified notes
Nullifier Verification
After collecting notes, the indexer checks which ones are spent:
const spentNullifiers = await indexer.getSpentNullifiers(notes)
const unspentNotes = notes.filter(
note => !spentNullifiers.has(note.nullifier)
)Custom Indexer
You can implement your own indexer by implementing the IIndexer interface:
interface IIndexer {
scan(params: ScanParams): Promise<{ notes: Note[]; lastScannedBlock: bigint }>
getSpentNullifiers(notes: Note[]): Promise<Set<Hex>>
}This allows using a centralized indexer service or a subgraph for faster scanning, while keeping the scanning logic privacy-preserving (your viewing key never leaves your device).