Skip to content

Commit d2e8943

Browse files
committed
Merge branch 'feature/reserve-derived-collateral-quality'
2 parents 20f52f5 + 4e50309 commit d2e8943

File tree

6 files changed

+522
-242
lines changed

6 files changed

+522
-242
lines changed
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# Reserve Tier Migration Audit Log
2+
3+
> Generated 2026-02-27 as part of the 3→5 tier ReserveRisk migration.
4+
5+
**Total slices:** 374
6+
**Changed:** 197
7+
**Unchanged:** 177
8+
9+
## low → very-low (103 slices)
10+
11+
| Coin | Slice Name |
12+
|------|-----------|
13+
| AEUR | Euro deposits at Swissquote Bank SA |
14+
| AUSD | Cash (USD deposits at State Street) |
15+
| AUSD | Short-dated U.S. Treasury Bills |
16+
| BUIDL | Cash |
17+
| BUIDL | Overnight Repos |
18+
| BUIDL | U.S. Treasury Bills |
19+
| CASH | Bank deposit accounts |
20+
| CASH | Reverse repos (overnight, UST-collateralized) |
21+
| CASH | U.S. Treasury bills (<=3mo maturity) |
22+
| CASH | U.S. government money-market funds |
23+
| CGO | Physical gold (24K 999.9, Transguard vaults UAE) |
24+
| DGLD | Allocated LBMA Good Delivery PAMP gold (Swiss vaults) |
25+
| EURAU | Euro bank deposits (CRR credit institutions, EU) |
26+
| EURC | Deposits at Systemically Important Institutions (EUR) |
27+
| EURC | Other Bank Deposits (EUR) |
28+
| EURCV | Euro cash deposits at Societe Generale |
29+
| EURE | EUR bank deposits (Arion Bank, LHV Bank) |
30+
| EURE | State Street EUR Liquidity LVNAV Fund (AAA-rated) |
31+
| EURI | Euro cash in segregated fiduciary accounts |
32+
| EURI | Low-risk liquid assets (EU government securities) |
33+
| EUROP | Euro cash and cash equivalents (EU banks incl. Societe Generale) |
34+
| EURQ | Cash deposits (Tier 1 European banks) |
35+
| EURQ | Government bonds (NL, DE, US) |
36+
| EURR | Fiat reserves (euro cash at regulated financial institutions) |
37+
| FDUSD | Bank Deposits |
38+
| FDUSD | Cash |
39+
| FDUSD | Overnight Reverse Repos |
40+
| FDUSD | U.S. Treasury Bills |
41+
| FIDD | Cash and cash equivalents |
42+
| FIDD | U.S. Treasury securities (short-term) |
43+
| GUSD | Cash deposits (State Street, Western Alliance Bank) |
44+
| GUSD | Short-term U.S. Treasury bonds |
45+
| GUSD | U.S. Treasury bills (<=3 month maturity) |
46+
| KAG | Physical silver bullion (999 fine, ABX global vaults) |
47+
| KAU | Physical gold bullion (LBMA-approved, ABX/Brink's/Loomis vaults) |
48+
| M | Short-term U.S. Treasury Bills (30-90 day) |
49+
| MNEE | U.S. Treasury bills (<=90-day duration) |
50+
| MNEE | USD cash at qualified custodian |
51+
| MUSD | Cash in bankruptcy-remote custody |
52+
| MUSD | U.S. Treasury bills (0-180 day maturity) |
53+
| PAXG | Physical gold bars (LBMA Good Delivery, Brink's London vaults) |
54+
| PYUSD | Cash Deposits |
55+
| PYUSD | U.S. Treasury Reverse Repos |
56+
| RLUSD | Cash Deposits |
57+
| RLUSD | Government Money Market Funds |
58+
| RLUSD | U.S. Treasury Bills |
59+
| SBC | Cash and cash equivalents |
60+
| SBC | Short-duration U.S. Treasury securities |
61+
| TBILL | Short-dated U.S. Treasury bills (WAM <3mo) |
62+
| TBILL | USD cash buffer |
63+
| TUSD | Cash at depository institutions |
64+
| U | Fiat USD / U.S. Treasury Bills |
65+
| USAT | Cash deposits |
66+
| USAT | U.S. Treasury bills |
67+
| USD1 | Cash Deposits |
68+
| USD1 | U.S. Government Money Market Funds |
69+
| USD1 | U.S. Treasury Bills |
70+
| USDA | U.S. Treasury securities |
71+
| USDA | USD cash deposits |
72+
| USDC | Cash Deposits |
73+
| USDC | Overnight Repos |
74+
| USDCV | USD cash deposits at BNY Mellon (segregated) |
75+
| USDG | Cash Deposits (DBS/StanChart) |
76+
| USDG | Cash Equivalents |
77+
| USDG | U.S. Government Securities |
78+
| USDGO | Cash and cash equivalents |
79+
| USDGO | U.S. Treasury bills |
80+
| USDH | Cash deposits (JP Morgan, Lead Bank) |
81+
| USDH | Repo agreements (UST-collateralized) |
82+
| USDH | Short-term U.S. Treasuries (BlackRock-managed, JP Morgan custody) |
83+
| USDH | Treasury-focused funds (BlackRock TTTXX) |
84+
| USDM | Money market funds (Fidelity / Amundi) |
85+
| USDM | USD bank deposits |
86+
| USDN | Short-term U.S. Treasury Bills (via M0 SPVs) |
87+
| USDP | Cash in FDIC-insured bank accounts |
88+
| USDP | Overnight reverse repos (secured by Treasuries) |
89+
| USDP | U.S. Treasury bills (<90 day maturity) |
90+
| USDQ | Cash deposits at Tier 1 European banks |
91+
| USDQ | Government bonds (NL, DE, US) |
92+
| USDR | Cash and short-term government bonds (EU financial institutions) |
93+
| USDT | Reverse Repos |
94+
| USDT | U.S. Treasury Bills |
95+
| USDX | Cash and cash equivalents |
96+
| USDX | U.S. Treasury bills (1-3 month maturity) |
97+
| USDY | Bank Demand Deposits |
98+
| USDY | iShares Short Treasury Bond ETF |
99+
| USDz | Cash and cash equivalents |
100+
| USN | U.S. Treasury Bills |
101+
| USYC | Reverse Repo Agreements |
102+
| USYC | U.S. Treasury Bills |
103+
| VEUR | Euro cash and cash equivalents (bank/custody accounts) |
104+
| VRO | Physical gold (LBMA 999.9, Geneva Free Ports) |
105+
| WUSD | Cash and cash equivalents |
106+
| WUSD | Short-term U.S. Treasury bills |
107+
| XAUT | Physical gold bars (LBMA Good Delivery, Swiss vaults) |
108+
| XAUm | Physical gold bars (LBMA-certified 99.99%, Brink's & Malca-Amit, Singapore & Hong Kong) |
109+
| XUSD | Cash deposits at Tier 1 banks (DBS, Standard Chartered, UOB) |
110+
| XUSD | Short-term U.S. / MAS government securities |
111+
| YLDS | Money market funds |
112+
| YLDS | Overnight Treasury repo agreements (UMB Bank) |
113+
| cgUSD | Accrued interest |
114+
| cgUSD | Short-term U.S. Treasury bills (off-chain) |
115+
| reUSD | Short-duration U.S. Treasury bills |
116+
117+
## medium → very-low (6 slices)
118+
119+
| Coin | Slice Name |
120+
|------|-----------|
121+
| CEUR | ETH |
122+
| HOLLAR | ETH |
123+
| LUSD | ETH |
124+
| USDe | BTC |
125+
| cUSD | ETH |
126+
| crvUSD | ETH |
127+
128+
## medium → low (72 slices)
129+
130+
| Coin | Slice Name |
131+
|------|-----------|
132+
| BOLD | rETH (Rocket Pool) |
133+
| BOLD | wstETH (Lido) |
134+
| BUCK | ETH & LSTs (via CDPs) |
135+
| BUCK | USDC/USDT (via PSM) |
136+
| CASH | Tokenized treasuries (BUIDL, USTB) |
137+
| CEUR | EURC (Circle euro stablecoin) |
138+
| CEUR | USDC |
139+
| CEUR | USDGLO (Glo Dollar) |
140+
| CEUR | USDT |
141+
| CEUR | sUSDS (Sky savings USDS) |
142+
| CEUR | stETH (Lido staked ETH) |
143+
| CUSD | BENJI (Franklin Templeton fund) |
144+
| CUSD | BUIDL (BlackRock tokenized MMF) |
145+
| CUSD | USDC (Circle) |
146+
| CUSD | USDT (Tether) |
147+
| CUSD | pyUSD (PayPal) |
148+
| DAI | ETH / wstETH |
149+
| DOLA | wstETH (Lido) |
150+
| EUSD | Aave V3 USDC (aUSDCv3) |
151+
| EUSD | Compound V3 USDC (cUSDCv3) |
152+
| EUSD | Compound V3 USDT (cUSDTv3) |
153+
| GHO | USDC / USDT (GSM) |
154+
| GHO | sDAI |
155+
| GHO | wstETH |
156+
| GUSD | USDT/USDC deposits (minting collateral) |
157+
| GYD | USDC (via Aave/Flux) |
158+
| GYD | sDAI (DAI Savings Rate) |
159+
| HOLLAR | USDT / USDC (stablecoins) |
160+
| HONEY | USDC (Circle) |
161+
| HONEY | USDT0 (Tether via LayerZero) |
162+
| HONEY | pyUSD / BYUSD (PayPal) |
163+
| IUSD | Aave USDC (liquid money market) |
164+
| IUSD | Fluid / Euler USDC (money markets) |
165+
| LISUSD | USDT / USDC / FDUSD (via PSM) |
166+
| MIM | wstETH / stETH |
167+
| MSUSD | DAI |
168+
| MSUSD | USDC (1:1 backing) |
169+
| NUSD | Liquid stablecoin reserves (USDC, USDT, USDS, USDe) |
170+
| PUSD | USDT deposits (1:1 minting collateral) |
171+
| SUSD | ETH and LSTs (V3 multi-collateral) |
172+
| SUSD | USDC / stataUSDC (V3 Base pool collateral) |
173+
| U | USDC |
174+
| U | USDT |
175+
| USDA | USDT (1:1 minted deposits) |
176+
| USDB | DAI / sDAI (via MakerDAO DSR) |
177+
| USDB | USDC (bridged) |
178+
| USDB | USDT (bridged) |
179+
| USDD | USDT (PSM vaults) |
180+
| USDF | USDT (held in Ceffu custody) |
181+
| USDH | Tokenized treasuries (BUIDL, USTB) |
182+
| USDO | BlackRock BUIDL (tokenized money market fund) |
183+
| USDO | Franklin Templeton BENJI (tokenized govt money fund) |
184+
| USDO | USDC cash buffer |
185+
| USDS | ETH / wstETH |
186+
| USDTB | USDC (redemption reserve) |
187+
| USDe | ETH / stETH |
188+
| USN | USDC/USDT (1:1 backing) |
189+
| USR | USD stablecoins (USDC/USDT) |
190+
| USX | USDC |
191+
| USX | USDT |
192+
| UTY | USDC deposits (initial user deposits) |
193+
| UUSD | USDt (Tether on Tezos) |
194+
| YLDS | Digital assets (USDC + USDT, operational) |
195+
| YZUSD | USDT0 / stablecoin liquidity buffer |
196+
| ZCHF | ETH / wstETH / LsETH |
197+
| ZeUSD | Tokenized U.S. T-Bills & MMFs (USYC, STBT, TBILL, ZTLN-P) |
198+
| cUSD | USDGLO (Glo Dollar) |
199+
| cUSD | sUSDS (Sky/Maker yield-bearing stablecoin) |
200+
| cUSD | stETH (Lido staked ETH) |
201+
| cgUSD | On-chain stablecoins (USDC/USDT) |
202+
| crvUSD | wstETH / sfrxETH / weETH |
203+
| fxUSD | wstETH (Lido) |
204+
205+
## medium → high (5 slices)
206+
207+
| Coin | Slice Name |
208+
|------|-----------|
209+
| AZND | Asian sovereign bonds (BBB+ min) |
210+
| AZND | Investment-grade Asian corporate/bank bonds (BBB- min) |
211+
| FEUSD | SOL (bridged) |
212+
| HYUSD | JitoSOL |
213+
| HYUSD | Other Solana LSTs (mSOL, bSOL, JupSOL) |
214+
215+
## high → very-high (11 slices)
216+
217+
| Coin | Slice Name |
218+
|------|-----------|
219+
| A7A5 | Russian ruble deposits at Promsvyazbank (sanctioned) |
220+
| DEURO | UNI / ZCHF (governance / other) |
221+
| DOLA | Other (sFRAX, INV, st-yETH) |
222+
| FEUSD | HYPE / kHYPE / wstHYPE (Hyperliquid native + LSTs) |
223+
| HOLLAR | GIGADOT / GIGAETH |
224+
| MIM | GM tokens (GMX V2 LP) |
225+
| MIM | Other exotic collateral (xSUSHI, Super OETH, etc.) |
226+
| SUSD | SNX (via legacy V2 420 pool, 200% C-ratio) |
227+
| TUSD | First Digital Trust fund investments (at cost, opaque) |
228+
| USP | BMMF Turkey FX arbitrage (Balsa Technologies, delta-neutral TRY/USD) |
229+
| ZCHF | Other (CRV, GNO, governance tokens) |
230+

src/components/reserve-treemap.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@ interface ReserveTreemapProps {
1010
}
1111

1212
const RISK_COLORS: Record<ReserveRisk, string> = {
13+
"very-low": "#16a34a",
1314
low: "#22c55e",
1415
medium: "#f59e0b",
15-
high: "#ef4444",
16+
high: "#f97316",
17+
"very-high": "#ef4444",
1618
};
1719

1820
const RISK_LABELS: Record<ReserveRisk, string> = {
21+
"very-low": "Very Low Risk",
1922
low: "Low Risk",
2023
medium: "Medium Risk",
2124
high: "High Risk",
25+
"very-high": "Very High Risk",
2226
};
2327

2428
function TreemapCell({

src/lib/report-cards.ts

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ import type {
1919
CustodyModel,
2020
ReportCard,
2121
DependencyType,
22+
ReserveRisk,
23+
ReserveSlice,
2224
} from "./types";
2325

2426
// ---------------------------------------------------------------------------
2527
// Constants
2628
// ---------------------------------------------------------------------------
2729

28-
export const METHODOLOGY_VERSION = "3.2";
30+
export const METHODOLOGY_VERSION = "3.3";
2931

3032
export const DIMENSION_WEIGHTS: Record<DimensionKey, number> = {
3133
pegStability: 0.25,
@@ -216,6 +218,63 @@ const COLLATERAL_QUALITY_SCORE: Record<CollateralQuality, number> = {
216218
exotic: 0,
217219
};
218220

221+
const RESERVE_QUALITY_SCORE: Record<ReserveRisk, number> = {
222+
"very-low": 95,
223+
low: 75,
224+
medium: 50,
225+
high: 25,
226+
"very-high": 5,
227+
};
228+
229+
const COLLATERAL_QUALITY_DISPLAY: [number, string][] = [
230+
[85, "Very low risk"],
231+
[62, "Low risk"],
232+
[37, "Medium risk"],
233+
[15, "High risk"],
234+
[0, "Very high risk"],
235+
];
236+
237+
export function computeCollateralQualityFromReserves(reserves: ReserveSlice[]): number {
238+
const totalPct = reserves.reduce((s, r) => s + r.pct, 0);
239+
if (totalPct === 0) return 0;
240+
const weighted = reserves.reduce(
241+
(s, r) => s + r.pct * RESERVE_QUALITY_SCORE[r.risk],
242+
0,
243+
);
244+
return Math.round(weighted / totalPct);
245+
}
246+
247+
function collateralScoreLabel(score: number): string {
248+
for (const [threshold, label] of COLLATERAL_QUALITY_DISPLAY) {
249+
if (score >= threshold) return label;
250+
}
251+
return "Very high risk";
252+
}
253+
254+
/**
255+
* Detect drift between computed reserve quality and enum classification.
256+
* Returns warnings for coins where the two disagree by more than 30 points.
257+
* Run during build or as a manual audit.
258+
*/
259+
export function validateCollateralQualityDrift(
260+
coins: StablecoinMeta[],
261+
): string[] {
262+
const warnings: string[] = [];
263+
for (const coin of coins) {
264+
if (!coin.reserves || coin.reserves.length === 0) continue;
265+
const computed = computeCollateralQualityFromReserves(coin.reserves);
266+
const factors = resolveResilienceFactors(coin);
267+
const enumScore = COLLATERAL_QUALITY_SCORE[factors.collateralQuality];
268+
const drift = Math.abs(computed - enumScore);
269+
if (drift > 30) {
270+
warnings.push(
271+
`${coin.symbol} (${coin.id}): reserve-derived=${computed}, enum=${enumScore}, drift=${drift}`,
272+
);
273+
}
274+
}
275+
return warnings;
276+
}
277+
219278
const CUSTODY_MODEL_SCORE: Record<CustodyModel, number> = {
220279
onchain: 100,
221280
institutional: 50,
@@ -302,17 +361,24 @@ export function scoreResilience(
302361
const blacklistLabel = canBeBlacklisted === true ? "Yes" : canBeBlacklisted === "possible" ? "Possible (mutable contract)" : "No";
303362

304363
const chainScore = CHAIN_RISK_SCORE[factors.chainRisk];
305-
const collateralScore = COLLATERAL_QUALITY_SCORE[factors.collateralQuality];
306364
const custodyScore = CUSTODY_MODEL_SCORE[factors.custodyModel];
307365

366+
// Prefer computed score from curated reserves; fall back to enum
367+
const hasReserves = meta.reserves && meta.reserves.length > 0;
368+
const collateralScore = hasReserves
369+
? computeCollateralQualityFromReserves(meta.reserves!)
370+
: COLLATERAL_QUALITY_SCORE[factors.collateralQuality];
371+
const collateralLabel = hasReserves
372+
? collateralScoreLabel(collateralScore)
373+
: COLLATERAL_QUALITY_LABEL[factors.collateralQuality];
374+
308375
const score = Math.round(
309376
(chainScore + collateralScore + custodyScore + blacklistScore) / 4,
310377
);
311378

312-
// Build detail: list each sub-factor
313379
const parts = [
314380
`Chain: ${CHAIN_RISK_LABEL[factors.chainRisk]} (${chainScore})`,
315-
`Collateral: ${COLLATERAL_QUALITY_LABEL[factors.collateralQuality]} (${collateralScore})`,
381+
`Collateral: ${collateralLabel} (${collateralScore})`,
316382
`Custody: ${CUSTODY_MODEL_LABEL[factors.custodyModel]} (${custodyScore})`,
317383
`Blacklist: ${blacklistLabel} (${blacklistScore})`,
318384
];

0 commit comments

Comments
 (0)