Scoring Model
Veil builds a behavioral credit score from events submitted by registered issuers. Midnight keeps the score and accumulators private, while CKB stores only the user's immutable Veil Identity DOB anchor. Credit decisions expose a band and policy limits, never the raw score.
Overview
The scoring model is intentionally behavioral rather than balance-based. A wallet with large holdings but a poor repayment history is less trustworthy than a smaller wallet with a consistent on-time record. Veil captures this by accumulating behavioral events over time and weighting them according to a configurable formula.
The score lives entirely in private state — it is never stored in plaintext on any public ledger. Only a ZK commitment to the score is published on-chain. When a protocol requests a credit decision, the user authorizes that request with their CKB wallet and Veil returns a decision band without revealing the raw score value.
Issuers & Data Providers
An issuer is a DeFi protocol that has been approved to submit behavioral data to Veil. Issuers are registered on-chain by the Veil admin via Admin_addIssuer, which derives and returns the issuer's public key (issuerPk). This key must be included in every scoring event the issuer submits.
Issuers fulfill two roles:
- Data provider — submit repayment, liquidation, protocol usage, and debt state events tied to a user's Veil ID.
- Verifier — call the credit decision endpoint to check the user's current band and lending limits after the user authorizes the request.
Issuers never learn the real identity of the users they track. All interactions use the user's Veil ID (userPk) — a deterministic, anonymous public key.
Scoring Events
Issuers submit behavioral events via four dedicated endpoints. Each event is processed by a ZK circuit that validates the issuer's authorization, enforces replay protection, and updates the user's private score accumulator.
eventEpoch field must be equal to or greater than the epoch of the last event submitted for that user. Backdated events are rejected by the contract to prevent retroactive score manipulation.Repayment Events
Submit whenever a user repays a loan. This is the highest-weight signal in the scoring model.
curl -X POST https://{VEIL_API}/scoring-events/repayments \
-H "Content-Type: application/json" \
-d '{
"userPk": "0xabc123...",
"issuerPk": "0xdef456...",
"paidOnTimeFlag": 1,
"amountWeight": 1000,
"eventEpoch": 3,
"eventId": "0x..."
}'| Field | Type | Description |
|---|---|---|
userPk | Bytes (hex) | The user's Veil ID |
issuerPk | Bytes (hex) | Your registered issuer public key |
paidOnTimeFlag | 1 | 0 | 1 = paid on time, 0 = late |
amountWeight | BigInt | Relative loan size weight (e.g. 1000 for a normalized unit) |
eventEpoch | BigInt | Protocol epoch at which the event occurred |
eventId | Bytes (hex, optional) | Unique event identifier for replay protection. Auto-generated if omitted. |
Liquidation Events
Submit when a user's position is liquidated. Severity determines the penalty magnitude.
curl -X POST https://{VEIL_API}/scoring-events/liquidations \
-H "Content-Type: application/json" \
-d '{
"userPk": "0xabc123...",
"issuerPk": "0xdef456...",
"severity": 2,
"eventEpoch": 3,
"eventId": "0x..."
}'| severity | Meaning | Score impact |
|---|---|---|
1 | Minor liquidation — small position, managed outcome | Low penalty |
2 | Moderate liquidation — significant position | Medium penalty |
3 | Severe liquidation — large or full position loss | High penalty |
Protocol Usage Events
Submit each time a user meaningfully interacts with your protocol (e.g. takes a position, provides liquidity). Diversity of protocol usage is a positive scoring signal — but each unique (user, protocol) pair is counted only once, preventing inflation.
| Field | Type | Description |
|---|---|---|
userPk | Bytes (hex) | The user's Veil ID |
issuerPk | Bytes (hex) | Your registered issuer public key |
protocolId | Bytes (hex) | A stable identifier for your protocol |
eventEpoch | BigInt | Protocol epoch at which the interaction occurred |
Debt State Events
Periodic debt state snapshots capture a user's current risk exposure. The riskBand (0–3) encodes collateralization quality, and activeDebtFlag indicates whether the user currently holds open debt.
| Field | Type | Description |
|---|---|---|
activeDebtFlag | 0 | 1 | 1 = user has active debt, 0 = no active debt |
riskBand | 0–3 | 0 = healthy, 3 = high-risk collateral position |
Score Computation
The score is derived from private score accumulators updated by issuer-submitted events. The current implementation records the accumulator and score commitments on Midnight, then derives credit decisions from the private state associated with the user's Veil ID. The formula combines positive behavioral signals with penalty deductions:
| Parameter | Default | Description |
|---|---|---|
baseScore | 350 | Starting score for all users |
maxScore | 900 | Score ceiling |
repaymentWeight | 2 | Multiplier on repayment ratio |
protocolWeight | 10 | Per unique protocol interaction |
tenureWeight | 1 | Per active epoch |
liquidationWeight | 3 | Penalty multiplier per liquidation point |
activeDebtPenalty | 5 | Flat penalty when user holds active debt |
riskBandWeight | 5 | Penalty per risk band level (0–3) |
Trust Tiers
Trust tiers are returned in the credit decision response. They are derived from the current private score and should be treated as policy bands rather than public score disclosures:
| Tier | Score profile | Access profile |
|---|---|---|
| Unranked | No score entry, insufficient history, or low current score | No privileged access; standard over-collateralized terms |
| Bronze | Basic positive behavior | Basic credit access; reduced collateral requirements |
| Silver | Consistent repayment and moderate activity | Moderate benefit tier; improved LTV and rate adjustments |
| Gold | Strong repayment behavior and low risk exposure | High-trust tier; preferential rates, higher LTV limits |
| Platinum | Best current risk profile | Maximum trust tier; undercollateralized borrowing, fee waivers |
Protocols consuming Veil decisions should handle all tiers, including Unranked. They should also use the returned maxLtvBps and riskPremiumBpsrather than hard-coding their own interpretation of the band.
Veil Identity DOB
Veil no longer mints Proof-of-Trustworthiness NFTs on Midnight. The public identity anchor is a user-minted Veil Identity DOB on CKB using Spore. The DOB stores only stable identity-anchor metadata: veilIdHash, the owner CKB lock hash, the Midnight network, the backend-deployed Midnight contract address, and a version string.
Epoch System
Veil scoring events carry an eventEpochsupplied by the issuer/backend. The contract enforces monotonicity so a user's event stream cannot be backdated. Epochs are used to:
- Enforce event monotonicity (events must not be backdated)
- Compute
epochsActive(tenure contribution to score) - Keep score updates ordered across issuer submissions
Privacy Invariants
The Veil scoring model is designed so that no participant — not even the Veil backend — can reconstruct a user's score from public data:
- Raw score never disclosed: Only ZK commitments (Merkle tree roots) are published on-chain. The actual numeric value stays in private state.
- Decision returns a band: Integrators learn only the decision band, LTV limit, risk premium, and validity timestamp — not the raw score or private inputs.
- Replay-protected challenges: Each verification call requires a fresh challenge that expires in 60 seconds and cannot be reused.
- Wallet authorization: The user signs the canonical credit decision message with their CKB wallet. Their Midnight secret key never leaves their machine and is never sent to a backend endpoint.