HinkalBase

Overview

HinkalBase owns the canonical state that backs private assets: the Merkle tree of commitments, the ledger of spent nullifiers, and the global configuration for helper and logic modules. While higher-level contracts orchestrate flows, this base ensures that when a transaction finishes, the state transition is sound, indexable, and future-proof.

Storage

  • mapping(uint256 => bool) public nullifiers — Tracks used nullifiers to prevent double-spend.

  • mapping(uint256 => address) public externalActionMap — Maps external action IDs to contract addresses.

  • IHinkalHelper public hinkalHelper — Helper contract for checks and fees.

  • IHinkalInLogic public hinkalInLogic — Logic contract for complex flows.

Functions

createCommitment

function createCommitment(UTXO memory utxo)
    internal view returns (OnChainCommitment memory)

Computes a Poseidon commitment:

  • Fungible (tokenId == 0): Poseidon4(amount, erc20, stealthAddress, timestamp)

  • NFT (tokenId > 0): Poseidon5(amount, erc20, stealthAddress, timestamp, tokenId)

  • Returns OnChainCommitment{ utxo, commitment }.

A user action often results in new, private notes (UTXOs). Each note is converted into a field element commitment using Poseidon. For fungible tokens, the essence of the note is amount, token address, stealth address, and time. NFTs additionally bind tokenId, ensuring uniqueness. These commitments are what enter the Merkle tree as leaves, forming the cryptographic backbone of private ownership.

Why two arities?

  • NFT commitments include tokenId to bind the unique asset to the note; fungible commitments omit it to reduce hash arity and gas. Both are deterministic and circuit-compatible.

insertCommitments

function insertCommitments(
    uint256[][] memory outCommitments,
    bytes[][] memory encryptedOutputs,
    OnChainCommitment[] memory onChainCommitments,
    bool[] memory onChainCreation
) internal
  • Flattens commitment leaves, inserts via insertMany, and emits events:

    • For off-chain commitments: NewCommitment(commitment, +index, encryptedOutput)

    • For on-chain commitments: NewCommitment(commitment, -index, abi.encode(utxo))

After a successful proof and execution, the protocol crystallizes newly created notes into the Merkle tree. Off-chain generated commitments (from the zk-circuit outputs) and on-chain generated commitments (derived here from UTXO[]) are flattened and inserted together to preserve ordering. Indexers learn not just the commitment values but also the provenance:

  • Positive indices → off-chain provided commitments (encrypted outputs accompany them).

  • Negative indices → on-chain constructed commitments, where the full UTXO is ABI-encoded in the event for transparency and recovery.

This dual-path insertion lets the protocol support both fully off-chain note creation and on-chain note creation paths (e.g., proofless deposits or DeFi integrations that mint notes in-process).

insertNullifiers

function insertNullifiers(
    uint256[][] calldata inputNullifiers,
    bool[] calldata onChainCreation
) internal

Ensures each nullifier is unused, sets it used, and emits Nullified(nullifier).

Every spend consumes one or more prior notes. In zero-knowledge, the spender proves knowledge of secrets; on-chain, we only see derived nullifiers. The act of inserting a nullifier is how the chain acknowledges a note as irrevocably spent. Reuse attempts revert, making double-spend attempts impossible within the same root set.

Security and Design Notes

  • Double-spend prevention via nullifiers mapping and strict checks.

  • Event-rich state updates for efficient indexing and monitoring.

  • Access controls separate admin duties for helper vs. broader protocol admin.

  • Uses Transferer helpers for robust ERC20/ETH/NFT handling and interface support.

Merkle implementation highlights:

  • Batched insertion (insertMany) sorts leaves to minimize hashing and gas.

  • Roots are circular-buffered to allow proofs against recent historical states.

  • Poseidon hash functions are injected via constructor for flexibility and auditability.

Last updated