# adverse-loans-max-v1

0 <= adverseLoans12m <= max (late loans in the last 12 months, counted as LimitStepUp counts them: any day past due - repaid late, still late, defaulted or written off - from Super Wallet's operator ledger, not confirmed by lenders). Proven without revealing the attribute, against a
commitment `Poseidon(value, salt)` the issuer recorded in SuperWalletTrustRegistry
(`commitment(credentialId, circuitId)`), and bound to the verifier's nonce.

| | |
|---|---|
| circuitId | `0x69484b162813af9a662f7a34cdfbafb17be1d322cf77e04b0dd5cf6cbea53a47` = keccak256("adverse-loans-max-v1") |
| vkHash | `0x36c49b3256f286f2b2672a1982f09cb542bacf554f91b679b33caa0246816bac` |
| ceremonyContributions | 1 (one Super Wallet contribution plus a public Xphere beacon; see TRANSCRIPT.md) |
| Committed attribute | `adverseLoans12m`: late loans in the last 12 months (any day past due: repaid late, still late, defaulted or written off, counted as `LimitStepUp` counts them), 0..255, from Super Wallet's operator ledger (`loan_performance`, `loan_repayments`); not confirmed by the lenders |
| Public signals | `commitment`, `min`, `max`, `nonce` (snarkjs order; the commitment first, the nonce last) |
| Verifier call | `verifyProof(uint256[2],uint256[2][2],uint256[2],uint256[4])` |
| Request (`<sw-verify request>`) | `adverse-loans<=0` -> predicate `{ "circuit": "adverse-loans-max-v1", "params": { "maxCount": 0 } }` |
| Registry | `0x852688D815410e3FEa015FD3a07028BF6b981d80` on Xphere (chain id 20250217). **Not registered as of 2026-09-27**: until `register-circuit` is sent, `circuit(0x69484b16...)` returns the zero address and verifiers refuse this predicate |
| uri | https://trendy.storydot.kr/sdk/zk/adverse-loans-max-v1/ |

## What a verifier checks

1. The circuit is registered and its vkHash is the one above; the proof passes `verifyProof` on
   the registered verifier contract (a view `eth_call`).
2. `publicSignals[0]` equals `commitment(credentialId, circuitId)` on the registry, the credential
   is anchored and not revoked, and its issuer is active.
3. The last public signal equals the nonce the verifier issued for this request (a proof made for
   another request fails the pairing check, and is refused before that).
4. The statement inputs are what was asked: `max` <= maxCount (`min` is 0).

`@superwallet/verifier-sdk` `verify(response, { zkRequest })` and `<sw-verify request="adverse-loans<=0">`
do all four.

## Files

| File | What it is |
|---|---|
| `adverse-loans-max-v1.circom`, `templates/`, `lib/` | The circuit (circom 2.1.9; circomlib 2.0.5 for Poseidon, bitify and comparators) |
| `adverse-loans-max-v1.r1cs` | The constraint system (281 constraints); needed to check the ceremony |
| `adverse-loans-max-v1.wasm` | Witness calculator, for proving |
| `adverse-loans-max-v1.zkey` | The final proving key; contains the whole contribution chain |
| `verification_key.json` | The Groth16 verification key |
| `Verifier.sol`, `solc-input.json` | The snarkjs Solidity verifier and the exact compiler input (solc 0.8.24, optimizer 200 runs, evmVersion paris) |
| `circuit.json` | Machine-readable manifest and ceremony record |
| `TRANSCRIPT.md` | The ceremony, step by step, with every hash |

## Reproduce

```sh
npm install circomlib@2.0.5 circom2@0.2.23
npx circom2 adverse-loans-max-v1.circom --r1cs --wasm --O2 -l . -l node_modules
sha256sum adverse-loans-max-v1.r1cs      # 20b490b76247b339ae1666590ec153213ae4e432da4be4c400e75037931ad1fc

npx --yes solc@0.8.24 --standard-json < solc-input.json > solc-output.json
# keccak256(runtime bytecode of contracts/adverse-loans-max-v1.Verifier.sol:Groth16Verifier) = 0x5340ab2b4bd858b53c5c951b15c1f108fdd605b1f447ffedc51519a6e2f08859
# (../tier-threshold-v1/check-bytecode.cjs compares it with the deployed contract)
```

vkHash: RFC 8785 canonical JSON of `verification_key.json` (keys sorted, no whitespace), then
keccak256: `node scripts/governance.cjs vk-hash verification_key.json` in chain/trust-registry, or
`vkHash()` in @superwallet/zk-kit.
