> For the complete documentation index, see [llms.txt](https://hinkal-team.gitbook.io/hinkal/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hinkal-team.gitbook.io/hinkal/technical-description/circuits/swapper-m.md).

# EVM/Tron Circuit

### Overview

Hinkal's `MainEVMCircuit` is a generalized, multi‑token transaction circuit. It allows a user to **consume up to `inputCount` input UTXOs for each of `tokenCount` distinct token types**, and to **create up to `outputCount` output UTXOs per token type**, inside a single zero‑knowledge proof. The circuit:

* reconstructs each input note's stealth public key from the spender's secret key,
* recomputes each input commitment and proves its membership in the on‑chain commitment tree,
* recomputes each input's nullifier and binds it to the publicly declared nullifier (preventing double‑spends),
* recomputes and checks each output commitment,
* enforces a balance equation per token type (linking inputs, outputs and a declared net change),
* proves spend authorization through an EdDSA signature over a message that is itself pinned to a secret seed,
* and derives the sender's stealth address plus the elliptic‑curve helper values that the smart contract reuses on‑chain.

Because the number of tokens, inputs and outputs is parameterized, all per‑UTXO data is **matrix‑shaped** (`[tokenCount][inputCount]` or `[tokenCount][outputCount]`) and the circuit loops over these dimensions internally. It is instantiated at fixed sizes (e.g. `1x2x1`, `2x2x2`, `3x2x1`, `Min0`) to generate the concrete proving artifacts.

It is built from Poseidon‑based primitives in circomlib:

| Gadget                             | Role                                                                                                  |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `StealthAddressCalculator`         | Derives the **sender's** stealth address and `H1` from `H0`, the spending key and the nullifying key. |
| `StealthAddressCalculatorExtended` | Derives each **input note's** public key, supporting both a legacy and a new derivation scheme.       |
| `CommitmentCalculator`             | Poseidon‑4 note commitment, zeroed when the amount is zero.                                           |
| `Signature`                        | Poseidon‑2 signature of a commitment under the nullifying key.                                        |
| `NullifierCalculator`              | Poseidon‑2 nullifier from commitment + signature, zeroed when the commitment is zero.                 |
| `SignatureVerifier`                | EdDSA‑Poseidon verification of the spending‑key signature.                                            |
| `MerkleRootCalculator`             | Recomputes a Merkle root from a leaf + sibling path.                                                  |
| `OverflowPreventer`                | Range‑checks each amount to prevent field wrap‑around.                                                |

***

### Template parameters

```
MainEVMCircuit(tokenCount, inputCount, outputCount, treeDepth)
```

* `tokenCount` — number of distinct token types handled in one proof.
* `inputCount` — maximum input UTXOs **per token type**.
* `outputCount` — maximum output UTXOs **per token type**.
* `treeDepth` — depth of the commitment Merkle tree.

***

### Public inputs

* **`rootHashHinkal`** — Merkle root of the on‑chain note‑commitment tree. Every non‑empty input must prove membership against this root.
* **`signedMessageHash`** — the message that the EdDSA signature must verify against under `spendingPublicKey`.
* **`erc20TokenAddresses[tokenCount]`** — the token address for each token type (zero address = ETH). All entries must be **distinct**.
* **`amountChanges[tokenCount]`** — the net change for each token type. For each token, the sum of outputs must equal the sum of inputs plus this value (positive for deposits/refunds, negative for withdrawals/spends).
* **`outTimeStamp`** — a single timestamp applied to **all** output commitments.
* **`inNullifiers[tokenCount][inputCount]`** — the declared nullifier for each input; each must equal the value the circuit recomputes.
* **`outCommitments[tokenCount][outputCount]`** — the claimed output commitments.
* **`calldataHash`** — a calldata digest exposed for binding by the contract; **not otherwise constrained** inside the circuit.
* **`H0Ax`, `H0Ay`** — the BabyJubJub curve point used to derive the **sender's own** stealth address.

(`message` and `outH1Ay` below are also part of the public signal vector, as circuit outputs.)

### Public outputs

* **`outStealthAddress`** — the sender's stealth address: `Poseidon(6)(signs, H0Ay, H1Ay, spendingPublicKey[0], spendingPublicKey[1], nullifyingPrivateKey)`, where `signs` comes from compressing the `(H0, H1)` coordinates.
* **`outH1Ay`** — the y‑coordinate of `H1 = nullifyingPrivateKey · H0`. Exposed so the contract can reconstruct commitments **without** redoing the expensive elliptic‑curve scalar multiplication.
* **`message`** — `Poseidon(1)(messageSeed)`, binding the publicly visible message to a private seed.

### Private inputs (witness)

* **`nullifyingPrivateKey`** — the spender's secret (nullifying) key.
* **`spendingPublicKey[2]`** — the spending public key, a point on the BabyJubJub curve.
* **`eddsaSignature[3]`** — the EdDSA signature `(R8x, R8y, S)` over `signedMessageHash`.
* **`messageSeed`** — secret preimage of `message`.
* **`inAmounts[tokenCount][inputCount]`** — amount of each input UTXO.
* **`inH0Ax`, `inH0Ay`** **`[tokenCount][inputCount]`** — the per‑input curve point `H0` used to rebuild that note's public key.
* **`isNewStyle[tokenCount][inputCount]`** — per‑input flag selecting the **new** (`Poseidon‑6`) vs **legacy** (`Poseidon‑3`) public‑key derivation.
* **`inTimeStamps[tokenCount][inputCount]`** — timestamp baked into each input commitment.
* **`inCommitmentSiblings[tokenCount][inputCount][treeDepth]`** and **`inCommitmentSiblingSides[tokenCount][inputCount][treeDepth]`** — the Merkle authentication path for each input (`left = 0`, `right = 1`).
* **`outAmounts[tokenCount][outputCount]`** — amount of each output UTXO.
* **`outPublicKeys[tokenCount][outputCount]`** — recipient stealth public key for each output.

***

### Conditions enforced by the circuit

#### 1. Valid spending signature

A `SignatureVerifier` (`EdDSAPoseidonVerifier`, always enabled) checks that `eddsaSignature` is a valid signature on `signedMessageHash` under `spendingPublicKey`. This proves the prover controls the spending key without revealing it.

#### 2. Message pinning

`message <== Poseidon(1)([messageSeed])`. The public `message` output is forced to be the Poseidon hash of the private `messageSeed`, binding the exposed value to a secret the prover knows.

#### 3. Correct nullifiers (per input)

For every token `i` and input `j`:

1. **Reconstruct the note's public key** via `StealthAddressCalculatorExtended` from `nullifyingPrivateKey`, `(inH0Ax, inH0Ay)` and `isNewStyle[i][j]`. Internally it computes `H1 = nullifyingPrivateKey · H0`, compresses the coordinates to obtain `signs`, and then blends two derivations:

   * **new style:** `Poseidon(6)(signs, H0Ay, H1Ay, spendingPublicKey[0], spendingPublicKey[1], nullifyingPrivateKey)`
   * **legacy style:** `Poseidon(3)(signs, H0Ay, H1Ay)`

   selecting between them with `out = new + isOldStyle·(old − new)` (i.e. `isNewStyle = 0` ⇒ legacy, else new). This lets old and new notes be spent together in one proof.
2. **Rebuild the input commitment** with `CommitmentCalculator`: `Poseidon(4)(inAmounts, erc20TokenAddresses[i], publicKey, inTimeStamps) · (1 − isZero(inAmounts))`. A zero amount yields a zero commitment.
3. **Sign the commitment** with `Signature`: `Poseidon(2)(nullifyingPrivateKey, commitment)`.
4. **Compute the nullifier** with `NullifierCalculator`: `Poseidon(2)(commitment, signature) · (1 − isZero(commitment))`.
5. **Assert** `inNullifiers[i][j] === computed nullifier`.

This binds each spent note to its owner and prevents double‑spending. Empty (zero‑amount) slots collapse to a zero commitment and a zero nullifier.

#### 4. Valid Merkle membership for inputs (per input)

For each input, `MerkleRootCalculator(treeDepth)` folds the input commitment up through `inCommitmentSiblings` / `inCommitmentSiblingSides` to a candidate root. A `ForceEqualIfEnabled` then asserts that root equals `rootHashHinkal`, **enabled only when `inAmounts[i][j]` is non‑zero**. Zero‑amount padding inputs therefore skip the membership check.

#### 5. Correct output commitments (per output)

For every token `i` and output `j`, `CommitmentCalculator` recomputes `Poseidon(4)(outAmounts, erc20TokenAddresses[i], outPublicKeys, outTimeStamp)` and asserts it equals `outCommitments[i][j]`. (Negative output amounts are impossible — they would fail as a non‑field element.)

#### 6. Balanced amounts (per token type)

For each token type `i`, the circuit accumulates `inTotal` over all inputs and `outTotal` over all outputs and enforces:

```
inTotal + amountChanges[i] === outTotal
```

so the difference between inputs and outputs exactly equals the declared `amountChanges[i]` — no value is minted or destroyed.

#### 7. Distinct token addresses

For every pair `(i, j)` with `i < j`, an `IsEqual` gadget is constrained so its output is `0`, asserting that every entry of `erc20TokenAddresses` is unique. This requires `tokenCount·(tokenCount−1)/2` comparators.

#### 8. Sender stealth‑address derivation

Using `H0Ax`, `H0Ay`, `spendingPublicKey` and `nullifyingPrivateKey`, `StealthAddressCalculator` computes `H1 = nullifyingPrivateKey · H0` and the Poseidon‑6 stealth address, exposing `outStealthAddress` and `outH1Ay`. The contract reuses these to reconstruct commitments cheaply, avoiding the elliptic‑curve work on‑chain.

#### 9. Overflow prevention

Every `inAmounts[i][j]` and `outAmounts[i][j]` is passed through an `OverflowPreventer` (a `ConditionalOverflowPreventer`, always enabled), range‑checking the value to prevent field wrap‑around. All hashing and signature checks use Poseidon‑based primitives from circomlib.
