12 / 19Module 2 – DappAdvanced
Dapp entities
A handful of entities cooperate to make private token transfers possible. Below you'll find what each one stores and the job it performs in the privacy-preserving token system.
Core privacy entities
spent_nullifier
entity spent_nullifier { key nullifier_hash: big_integer;}Purpose: Records nullifiers that have already been used, which blocks double-spending in the privacy system.
How it works:
- Spending a private token produces a unique nullifier derived from its commitment
- That nullifier is written to
spent_nullifier, flagging it as consumed - Every transaction is checked against this entity first to see whether its nullifier is already present
- As a result, a private token can never be spent more than once
unspent_commitment
entity unspent_commitment { key commitment_hash: big_integer;}Purpose: Holds every commitment that has not yet been spent, i.e. the private tokens currently available.
How it works:
- A private token takes the form of a commitment, which is a cryptographic hash
- Shielding tokens (making them private) or receiving them through a private transfer adds new commitments
- Spending or unshielding tokens deletes the corresponding commitments
- Together, these records serve as the "UTXO set" for private tokens
private_transfer_event
@logentity private_transfer_event { commitment_input: big_integer; commitment_output_sender: big_integer; commitment_output_recipient: big_integer; recipient_private_address: big_integer; nullifier: big_integer; encrypted_sender_note: byte_array; encrypted_recipient_note: byte_array;}Purpose: Keeps a log of every private transfer along with its encrypted details.
How it works:
- Writes an on-chain record for each private transfer transaction
- Stores the input commitment being consumed plus two output commitments: one for the sender's change and one for the recipient
- Carries encrypted notes that can be decrypted only by the sender and the recipient
- Because of the
@logannotation, you can query these records for wallet scanning - Wallets scan these events to discover private transactions addressed to them
Token shielding/unshielding entities
shield_log
@logentity shield_log { account_id: byte_array; commitment: big_integer; amount: big_integer; encrypted_note: byte_array;}Purpose: Records each conversion of public tokens into private tokens (shielding).
How it works:
- Captures the moment a user turns public FT4 tokens into private commitments
- Ties the public account to the private commitment that was produced
- Includes an encrypted note describing the transaction
- Gives users a way to review their shielding history
unshield_log
@logentity unshield_log { account_id: byte_array; nullifier: big_integer; commitment: big_integer; amount: big_integer;}Purpose: Records each conversion of private tokens back into public tokens (unshielding).
How it works:
- Captures the moment a user turns private commitments back into public FT4 tokens
- Ties the nullifier and commitment to the public account that receives the tokens
- Gives users a way to review their unshielding history
- Supplies an audit trail for regulatory compliance
User management entity
private_address_registry
entity private_address_registry { key account_id: byte_array; mutable private_address: big_integer; mutable public_encryption_key: text; mutable registered_at: timestamp;}Purpose: Associates each public FT4 account with its private address and encryption key.
How it works:
- You register a private address, derived from your private key, against your public FT4 account
- Your public encryption key is stored here so notes can be encrypted securely for you
- Senders look up a recipient's private address here before sending private tokens
- In effect, it is a "phone book" for private transactions
- You can update your keys whenever needed
Privacy model summary
Taken together, these entities form a UTXO-based privacy model:
- Shielding turns public tokens into private commitments
- Private commitments move between users privately with the help of zero-knowledge proofs
- Nullifiers stop double-spending while keeping the spent commitment hidden
- Encrypted notes let parties exchange transaction details securely
- The address registry makes it possible for users to receive private payments
You get transaction privacy, with amounts and recipients concealed, while auditability is preserved through the logged events and regulatory compliance is supported by shield/unshield logs that connect to public identities.