# credit-tier-v1

tier >= minTier (tier 0 POOR .. 4 EXCELLENT, the score attestation's tier commitment). 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 | `0xda6c12665684695797838670a2465d6ba9f2bf812b8629506d9dbc573d8cc9d0` = keccak256("credit-tier-v1") |
| vkHash | `0xfd31a9160ce340b7ce4bdb66ae4fdfa9f146920c81d2ab466dd4261b9e3e3a26` |
| ceremonyContributions | 1 (one Super Wallet contribution plus a public Xphere beacon; see TRANSCRIPT.md) |
| Committed attribute | `tier`: HACS credit tier 0 POOR .. 4 EXCELLENT; the commitment is the same Poseidon(tier, salt) the score attestation carries (as in tier-threshold-v1) |
| Public signals | `commitment`, `threshold`, `nonce` (snarkjs order; the commitment first, the nonce last) |
| Verifier call | `verifyProof(uint256[2],uint256[2][2],uint256[2],uint256[3])` |
| Request (`<sw-verify request>`) | `tier>=2` -> predicate `{ "circuit": "credit-tier-v1", "params": { "minTier": 2 } }` |
| Registry | `0x852688D815410e3FEa015FD3a07028BF6b981d80` on Xphere (chain id 20250217). **Not registered as of 2026-09-27**: until `register-circuit` is sent, `circuit(0xda6c1266...)` returns the zero address and verifiers refuse this predicate |
| uri | https://trendy.storydot.kr/sdk/zk/credit-tier-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: `threshold` >= minTier and <= 4.

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

## Files

| File | What it is |
|---|---|
| `credit-tier-v1.circom`, `templates/`, `lib/` | The circuit (circom 2.1.9; circomlib 2.0.5 for Poseidon, bitify and comparators) |
| `credit-tier-v1.r1cs` | The constraint system (250 constraints); needed to check the ceremony |
| `credit-tier-v1.wasm` | Witness calculator, for proving |
| `credit-tier-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 credit-tier-v1.circom --r1cs --wasm --O2 -l . -l node_modules
sha256sum credit-tier-v1.r1cs      # fab328b540d782e2c84b2efb163db946d063f8b081084df53f62f7193fc63776

npx --yes solc@0.8.24 --standard-json < solc-input.json > solc-output.json
# keccak256(runtime bytecode of contracts/credit-tier-v1.Verifier.sol:Groth16Verifier) = 0x6e61443bbb7d1a42ac152a1407c6b40de81c6f1417173a81e1a1b721243831d6
# (../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.
