# UTXOs, Commitments & Nullifiers

### UTXOs, Commitments, and Nullifiers

Hinkal uses a UTXO (Unspent Transaction Output) model, where each UTXO contains the following elements:

* **tokenAddress**: The address of the token represented by the UTXO.
* **amount**: The token amount within the UTXO.
* **stealthAddress**: The stealth address that "owns" the UTXO.
* **timestamp**: The creation time of the UTXO.
* **tokenId (optional)**: A unique NFT identifier.

For each UTXO, a cryptographic commitment is generated either off-chain or on-chain. A commitment is a cryptographic primitive that lets a user commit to a value while keeping it hidden, retaining the ability to reveal it later. Commitments are created during deposits, transfers, and other interactions, and are later spent (nullified) when the UTXO is consumed. Each commitment is the Poseidon hash of the UTXO's attributes:

```
commitment = Poseidon(amount, tokenAddress, stealthAddress, timeStamp)
```

The nullifier — which guarantees a UTXO can be spent only once — is itself a Poseidon hash derived from the nullifier signature and the commitment, where the signature ties the nullifier to the user's nullifying key:

```
signature = Poseidon(nullifyingKey, commitment)
nullifier = Poseidon(commitment, signature)
```

Spending a UTXO requires the user to generate a zero-knowledge proof that they control the keys behind the commitment's stealth address — the nullifying key that links the stealth points and the spending key committed inside the stealth address — for a commitment that exists in Hinkal's Merkle tree, together with that commitment's unique nullifier. Once the transaction is processed, the commitment is marked as nullified and the nullifier is recorded on-chain, creating an immutable record that prevents the commitment from being reused.

<figure><img src="/files/GAxk5sl8l17xM8fZ2Yw8" alt=""><figcaption></figcaption></figure>

### UTXOs: On-Chain vs Off-Chain Creation

UTXOs in the Hinkal Protocol can be created through two distinct mechanisms: off-chain creation and on-chain creation. This dual approach provides flexibility for different transaction types and privacy requirements while maintaining the protocol's zero-knowledge properties.

#### Off-Chain UTXO Creation

When a UTXO is created off-chain, the complete UTXO data — amount, token address, stealth address, timestamp, and any other metadata — is computed and encrypted entirely client-side. Only the UTXO's commitment hash is published on-chain and inserted into the protocol's Merkle tree, accompanied by an encrypted output blob that lets the intended recipient decrypt and reconstruct the full UTXO. The commitment is a Poseidon hash over the core fields:

```
commitment = Poseidon(amount, tokenAddress, stealthAddress, timeStamp)
```

Off-chain UTXOs apply whenever the transaction amounts are known in advance — most commonly when sending tokens to another user. Here the sender already knows the exact amount, the recipient's stealth address, and every other UTXO parameter before submitting the transaction, so the entire UTXO can be prepared client-side. This maximizes privacy: all transaction details stay encrypted, and only the commitment hash is ever revealed on-chain.

To spend an off-chain UTXO, a user proves ownership in two steps. First, they reconstruct the commitment by re-applying the Poseidon hash to the decrypted UTXO fields, demonstrating the committed UTXO exists in the Merkle tree. Second, they derive its nullifier from their nullifying key — `signature = Poseidon(nullifyingKey, commitment)` and `nullifier = Poseidon(commitment, signature)` — which both binds the spend to the key that controls the UTXO's stealth address and marks the UTXO as consumed, preventing double-spends.

#### On-Chain UTXO Creation

When a UTXO is created on-chain, the smart contract itself constructs the UTXO data during transaction execution based on the actual token balances and external action results. The contract creates the full UTXO struct and computes its commitment on-chain.

The fundamental requirement for on-chain UTXO creation arises when we cannot determine the exact amounts before executing the transaction on-chain. In scenarios such as DEX swaps with slippage, yield farming rewards, or liquidity provision where the final output amounts depend on real-time market conditions, external protocol states, or complex calculations that can only be performed during execution, the protocol must create UTXOs on-chain using the actual resulting amounts rather than predetermined values.

This dual approach allows Hinkal Protocol to maintain privacy for predetermined transactions while supporting complex DeFi interactions where output amounts are only known during the execution.

When a user wants to utilize an on-chain UTXO, they must demonstrate ownership through elliptic curve cryptographic operations on the Baby Jubjub curve. Ownership is split across two keys: a **nullifying key** `nk` (derived deterministically from the user's login signature) and a **spending keypair** whose public key `P_spend = (P_x, P_y)` is an EdDSA-Poseidon Baby Jubjub point. The verification process works as follows:

**Point Generation:** From a per-UTXO randomization scalar `s`, the system derives a pair of points on the Baby Jubjub curve. The first point is the randomization point on the base point `B` (Base8), and the second is that point scaled by the nullifying key:

```
H0 = s · B
H1 = nk · H0
```

**Stealth Address Reconstruction:** The stealth address is a Poseidon commitment that binds both points, the spending public key, and the nullifying key together:

```
signs = 2 · sign(H0_x) + sign(H1_x)
stealthAddress = Poseidon(signs, H0_y, H1_y, P_x, P_y, nk)
```

Only the y-coordinates of `H0` and `H1` are committed directly; their x-coordinate parities are packed into `signs`. On-chain, the `StealthAddressStructure` carries `H0_y`, `H1_y`, the `stealthAddress` hash, and an `extraRandomization` field that packs `H0_x` together with a high-bit "new style" flag (bit 255).

**Mathematical Verification:** Given `H0` and `H1`, the protocol recomputes the second point from the candidate nullifying key and checks it reproduces the committed point, then checks the stealth address reproduces against the committed spending public key:

```
nk · H0 == H1      (y-coordinate match)
Poseidon(signs, H0_y, H1_y, P_x, P_y, nk) == stealthAddress
```

This cryptographic proof system ensures that only a user holding both the nullifying key `nk` (which maps `H0` to `H1`) and the spending key behind `P_spend` (committed inside the stealth address) can demonstrate ownership of the on-chain UTXO — maintaining security while enabling the transparency required for external protocol interactions. The stealth address structure serves as both a privacy-preserving identifier and a cryptographic commitment to the user's ownership.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hinkal-team.gitbook.io/hinkal/technical-description/setup/utxos-commitments-and-nullifiers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
