diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..a5895370 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +scripts/check_bridges.mjs text diff diff --git a/reports/report/across-protocol.md b/reports/report/across-protocol.md index fdd9569a..3cb27c37 100644 --- a/reports/report/across-protocol.md +++ b/reports/report/across-protocol.md @@ -181,6 +181,7 @@ So fund delegation risk is limited to (a) the HubPool's own custody and (b) ever - **UMA Optimistic Oracle V3:** [`0xfb55F43fB9F48F63f9269DB7Dde3BbBe1ebDC0dE`](https://etherscan.io/address/0xfb55F43fB9F48F63f9269DB7Dde3BbBe1ebDC0dE). Disputes route through UMA's DVM. The OO uses identifier `"ACROSS-V2"` (verified onchain via `HubPool.identifier()`). UMA itself is governed by a separate UMA-holder DAO, which is also Risk Labs–affiliated. - **Canonical L1↔L2 bridges (Arbitrum, Optimism, Base, Polygon, zkSync, Linea, Blast, World Chain, etc.):** Critical for repatriating `utilizedReserves` to the HubPool. Failure of any of these (a frozen rollup, a halted canonical bridge) isolates that chain's SpokePool inventory until the underlying issue resolves — or until the multisig writes down the loss via `haircutReserves`. +- **LayerZero V2 / USDT0 OFT:** The deployed Ethereum Arbitrum adapter (`0x5eC9…B57C`) uses AdapterStore `0x42df…D63b`; its live `(OFT_MESSENGER, eid 30110, USDT)` entry resolves to USDT0 messenger `0x6C96…1dee`. That route moves USDT from the HubPool to the Arbitrum SpokePool through the peer Arbitrum OApp [`0x14E4…8D92`](https://arbiscan.io/address/0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92). The Ethereum→Arbitrum receive config was verified onchain at **3-of-3** (USDT0, LayerZero Labs, Canary), 65 confirmations. This is a per-asset transport dependency; it does not describe other Across assets or routes. - **Ethereum mainnet:** Full L1 dependency. - **No oracle pricing dependency for LP NAV:** Unlike Reserve/RSR, Across does NOT need Chainlink or any price oracle to value LP reserves; the underlying token itself is what's owed. - **Fallback mechanisms:** If UMA DVM fails to resolve, root bundle execution is blocked until the proposal is replaced. Liquidity remains in the HubPool / SpokePools but cannot be rebalanced. The multisig can `emergencyDeleteProposal` to clear the queue. diff --git a/reports/report/cap-stcusd.md b/reports/report/cap-stcusd.md index 17d0cf45..ef610396 100644 --- a/reports/report/cap-stcusd.md +++ b/reports/report/cap-stcusd.md @@ -299,6 +299,7 @@ Cap's governance flows through a **3-of-5 Gnosis Safe multisig** → **24-hour T | **RedStone** | High | cUSD price oracle (0.05% deviation threshold). Stale prices disable minting/burning | | **wWTGXX (WisdomTree)** | Low | ~$5.08M tokenized gov money market fund. Minimal DeFi adoption and few holders | | **USDC (Circle)** | High | Primary reserve asset (~95% of cUSD backing) | +| **LayerZero V2** | High | The Ethereum OFT Adapter [`0x983a…4137`](https://etherscan.io/address/0x983aeaaa0d0426839158435c43725ea7f45d4137) escrows 25,311,191 stcUSD, **51.99% of the 48.68M supply**, backing the native Katana OFT. The adapter cannot mint canonical stcUSD, so compromise risk is bounded by the remote supply and locked collateral, but the integration affects a majority of current supply | | **USDT, pyUSD, BENJI, BUIDL** | Low | Listed in docs as potential reserve assets but **not currently whitelisted onchain** (`Vault.assets()` returns only USDC and wWTGXX) | | **Institutional Operators** | High | IMC Trading, Edge Capital, Susquehanna Crypto generate yield via offchain strategies. Counterparty risk mitigated by Symbiotic restaking | @@ -426,12 +427,13 @@ Cap's governance flows through a **3-of-5 Gnosis Safe multisig** → **24-hour T | Factor | Assessment | |--------|-----------| -| Protocol count | Morpho (critical), Symbiotic (critical), RedStone (high), USDC/Circle (high), wWTGXX/WisdomTree (low). Aave V3 is wired in but no longer holds reserves | +| Protocol count | Morpho (critical), Symbiotic (critical), RedStone (high), USDC/Circle (high), LayerZero V2 (high), wWTGXX/WisdomTree (low). Aave V3 is wired in but no longer holds reserves | | Morpho concentration | ~$48.9M USDC — **100%** of deployed USDC reserves are in Morpho (Steakhouse Prime + Gauntlet Prime). Concentration on a single underlying lending protocol increased materially vs. March's 67/33 Morpho/Aave split | | Symbiotic | Novel restaking infrastructure, less battle-tested than established alternatives | +| LayerZero concentration | The OFT Adapter escrows 25.31M stcUSD (**51.99% of supply**) for Katana. This is a lock-and-mint representation, not a canonical-token mint authority, but bridge failure or compromise can affect the majority escrowed share | | Operator counterparties | Institutional firms (IMC, Susquehanna, Edge) — blue-chip but opaque | -**Dependencies Score: 3.0/5** — The total dependency count is similar, but reserve concentration has shifted: 100% of the deployed USDC reserve now sits in Morpho (across two MetaMorpho curators), removing the Aave V3 diversification leg. This raises Morpho-specific risk while keeping cross-protocol risk count flat. Symbiotic integration adds complexity. Multiple oracle dependencies (RedStone). The operator model introduces counterparty risk with institutional firms. Score remains 3.0 — diversification across two curators (Steakhouse, Gauntlet) partially offsets the loss of the Aave leg, and Morpho Blue itself is battle-tested. +**Dependencies Score: 3.0/5** — Reserve concentration has shifted: 100% of the deployed USDC reserve now sits in Morpho (across two MetaMorpho curators), removing the Aave V3 diversification leg. The live LayerZero integration also escrows 51.99% of stcUSD supply for Katana. Its lock-and-mint model cannot dilute canonical stcUSD, but it adds a material availability and escrow dependency that must be monitored. Symbiotic integration, RedStone oracles, and opaque institutional operator strategies add further complexity. Score remains 3.0 because the bridge blast radius is bounded by its locked collateral and the category already reflects several high-impact dependencies; the newly documented LayerZero concentration reinforces rather than changes that assessment. **Centralization Score = (2.0 + 2.5 + 3.0) / 3 = 2.5** diff --git a/reports/report/centrifuge-jaaa.md b/reports/report/centrifuge-jaaa.md index 567538e6..97fd659b 100644 --- a/reports/report/centrifuge-jaaa.md +++ b/reports/report/centrifuge-jaaa.md @@ -257,7 +257,7 @@ Other governance / infra addresses checked and confirmed **not wards** on the JA ### External Dependencies -- **Cross-chain messaging — 2/2 threshold with LayerZero V2 + Chainlink CCIP.** The `MultiAdapter` ([`0x35C8…73BE`](https://etherscan.io/address/0x35C837F0A54B715a23D193E1476BFC9BC30073BE)) is now configured with two adapters for JAAA across destination chains: `0xD517BC7b…` (LayerZero V2, confirmed by `endpoint() = 0x1a44…`, the canonical Ethereum LayerZero V2 Endpoint) and `0x34e9…` (Chainlink CCIP per Centrifuge attestation). `quorum() = 2` verified onchain — **both** adapters must sign any non-Ethereum-chain message. Wormhole is no longer in the active adapter array for JAAA. LayerZero side reportedly uses 5 DVNs. +- **Cross-chain messaging — 2/2 threshold with LayerZero V2 + Chainlink CCIP.** The `MultiAdapter` ([`0x35C8…73BE`](https://etherscan.io/address/0x35C837F0A54B715a23D193E1476BFC9BC30073BE)) is now configured with two adapters for JAAA across destination chains: [`0xD517BC7b…`](https://etherscan.io/address/0xD517BC7ba17271A8D87bE7355B2523bf5c750295) (LayerZero V2, confirmed by `endpoint() = 0x1a44…`, the canonical Ethereum LayerZero V2 Endpoint) and `0x34e9…` (Chainlink CCIP per Centrifuge attestation). `quorum() = 2` verified onchain — **both** adapters must sign any non-Ethereum-chain message. Wormhole is no longer in the active adapter array for JAAA. The LayerZero adapter's six live inbound routes all use the same 15-confirmation ULN policy: Deutsche Telekom and Canary are required, plus 2-of-3 optional P2P, Nansen, and Nethermind DVNs (**4-of-5 effective quorum**). - **Stablecoin settlement (USDC, USDT, USDS)** — JAAA accepts multiple subscription / redemption assets per Centrifuge. USDC inherits Circle's freeze list and reserve risk; USDT inherits Tether's; USDS inherits Sky's. Multi-asset settlement reduces single-issuer concentration. - **Anemoy Capital SPC Limited (BVI)** — fund issuer of record. The legal wrapper that holds the underlying CLOs. BVI insolvency or regulatory action against Anemoy would constitute an existential risk for token holders. - **Janus Henderson** — sub-advisor; selects and manages the CLO portfolio. @@ -643,7 +643,7 @@ MADISON PARK FUNDING — three separate tranches (2018-30A 6.95%, 2025-65A 4.52% **Subcategory C: External Dependencies** -- **Cross-chain:** **2-of-2 MultiAdapter quorum** — LayerZero V2 + Chainlink CCIP. Verified onchain via `MultiAdapter.adapters(...)` and `quorum() = 2`; LayerZero adapter `0xD517BC7b…` confirmed by `endpoint() = 0x1a44…` (canonical LayerZero V2 Endpoint), second adapter `0x34e9…` is per Centrifuge attestation Chainlink CCIP. Both must sign any non-Ethereum-chain message — materially stronger than the prior single-Wormhole setup. +- **Cross-chain:** **2-of-2 MultiAdapter quorum** — LayerZero V2 + Chainlink CCIP. Verified onchain via `MultiAdapter.adapters(...)` and `quorum() = 2`; LayerZero adapter [`0xD517BC7b…`](https://etherscan.io/address/0xD517BC7ba17271A8D87bE7355B2523bf5c750295) confirmed by `endpoint() = 0x1a44…` (canonical LayerZero V2 Endpoint), second adapter `0x34e9…` is per Centrifuge attestation Chainlink CCIP. Both must sign any non-Ethereum-chain message. All six live LayerZero inbound routes use 15 confirmations and require Deutsche Telekom + Canary plus 2-of-3 P2P/Nansen/Nethermind (4-of-5 effective), materially stronger than the prior single-Wormhole setup. - **Stablecoin settlement:** USDC, USDT and USDS are all supported subscription / redemption assets (per Centrifuge team). - **Offchain stack:** Janus Henderson sub-advisor, Anemoy issuer, StoneX custody, Trident Trust admin+KYC (dual role), MHA Cayman audit. StoneX and Janus Henderson are battle-tested TradFi entities. diff --git a/reports/report/fx-fxusd.md b/reports/report/fx-fxusd.md index 1561c0c6..ce51cb4b 100644 --- a/reports/report/fx-fxusd.md +++ b/reports/report/fx-fxusd.md @@ -256,7 +256,9 @@ In V2, all fxUSD position collateral is custodied by the PoolManager contract ([ - **Curve:** stETH/ETH EMA oracle + fxUSD/USDC pool for peg monitoring - **Aave:** Stability Pool deploys USDC to Aave Core (up to 80% cap) and wstETH to Aave Prime (up to 80%) - **Lido:** wstETH is the primary ETH collateral in V2 (~$12.38M) -- **LayerZero:** Used for omnichain fxUSD bridging (cross-chain OFT) +- **LayerZero:** **Not a fxUSD dependency** (corrected July 15, 2026). f(x) uses LayerZero `ProxyOFT` for *other* tokens (fETH, xETH, FXN, arUSD), but **fxUSD itself is not bridged**: the docs list fxUSD with a single Ethereum address and no bridging entry, [DeFiLlama](https://api.llama.fi/protocol/fx-protocol) reports f(x) Protocol on Ethereum only, fxUSD is not a native OFT (`endpoint()` and `oftVersion()` revert on [`0x0857…d8f6`](https://etherscan.io/address/0x085780639CC2cACd35E474e71f4d000e2405d8f6)), it is absent from [LayerZero's OFT registry](https://metadata.layerzero-api.com/v1/metadata/experiment/ofts/list), and it has no Chainlink CCIP token pool (`TokenAdminRegistry.getPool` returns the zero address). An earlier revision of this report described fxUSD as omnichain-bridged; that claim was unverified and is withdrawn. + +- **Katana deployments are locally issued, not identified bridge representations.** Two `fxUSD` / "f(x) USD" ERC-20s exist on Katana ([`0x4c03…FDF9`](https://explorer.katanarpc.com/address/0x4c03ff0f44A55e7098a09016E02a01d3cdC2FDF9), supply ~12,008; [`0x1364…9f86`](https://explorer.katanarpc.com/address/0x1364b238C668A2dec1294174e4798E8c09979f86), supply ~1,000,018). Each token exposes a local `poolManager()` (`0x27b3…f96a` and `0xFae3…3C68`, respectively), and each PoolManager's `fxUSD()` points back to its corresponding token. The verified `0x1364…9f86` implementation restricts minting to its PoolManager; local mint events, including a 10,000-token genesis mint for `0x4c03…FDF9`, corroborate local issuance. Neither token is a LayerZero OFT (`endpoint()` reverts), and neither is the canonical AggLayer/LxLy wrapper of mainnet fxUSD (`PolygonZkEVMBridgeV2.getTokenWrappedAddress(0, fxUSD)` returns the zero address on Katana). This positive architecture evidence does not reveal a bridge path from Ethereum fxUSD to either Katana deployment. Because the `0x4c03…FDF9` implementation is not source-verified, a separate custom conversion path cannot be ruled out absolutely; reassess if one is identified rather than inferring a bridge from the shared name and symbol. The protocol depends on multiple well-established DeFi protocols. Chainlink is the most critical dependency — oracle failure would impair pricing and liquidations. Aave exposure is capped and non-critical to core fxUSD backing. diff --git a/reports/report/infinifi.md b/reports/report/infinifi.md index c7fad161..a9a4bb7e 100644 --- a/reports/report/infinifi.md +++ b/reports/report/infinifi.md @@ -323,6 +323,14 @@ The governance system is split into three branches to check and balance power: - **Top dependencies (by deployed value)**: **Midas** (mGLOBAL tokenization layer over Fasanara Capital) ~47%, **Unidentified RWA escrow counterparties** (three separate escrows; one routes to the team multisig, two to external EOAs — TODO identify) ~31%, **PYUSD / Paxos + Sentora PRIME** (via maturing CoW-swap basket) ~7%, **Cap Protocol** (stcUSD) ~6%, **Aave (V4) / Global Dollar (USDG)** ~5%, **Steakhouse-curated Morpho MetaMorpho** ~3%. The Spark/MakerDAO, Aave Horizon, Maple Finance, and f(x) Protocol farms currently hold $0. The CoW-Protocol solver set is a settlement dependency for the maturing swap baskets. - **Stablecoin dependencies**: USDC and USDT enabled as deposit assets (verified onchain). The protocol also takes indirect exposure to PYUSD (via the maturing swap basket), USDG / Global Dollar (via the Aave V4 market), cUSD/stcUSD (Cap), and to T-Bill-backed / hedge-fund RWAs (via Midas mGLOBAL and the three RWA escrow counterparties). USDe and sUSDe remain not enabled as deposit assets on FarmRegistry. +- **Cross-chain / bridge dependency (verified July 15, 2026): LayerZero, lock-and-mint.** Both receipt tokens bridge to **Katana** via LayerZero V2 OFT Adapters that **escrow the canonical token on Ethereum** — they hold no mint authority, so a bridge compromise cannot mint native iUSD/siUSD: + | Token | Ethereum OFT Adapter | Escrowed | Katana native OFT | + |---|---|---:|---| + | siUSD | [`0x5f21…c3c0`](https://etherscan.io/address/0x5f2106bb2a5aba6a783dbf29c8d3b09c175bc3c0) | 29,340.24 siUSD | [`0x6894…F92D`](https://explorer.katanarpc.com/address/0x68943c066747690ecDAEB027fa722B090ee6F92D) | + | iUSD | [`0xdd1c…3005`](https://etherscan.io/address/0xdd1cb2e1aa483e1d94e3e22e70cfbb634fcb3005) | 4.33 iUSD | [`0x9Fa1…1C10`](https://explorer.katanarpc.com/address/0x9Fa1202516916534Ade66962Ee91410d559f1C10) | + + Each adapter's `token()` returns the corresponding mainnet token and its `endpoint()` is the canonical LayerZero V2 `EndpointV2` [`0x1a44…728c`](https://etherscan.io/address/0x1a44076050125825900e736c501f859c50fE728c); the Katana side exposes `oftVersion()` and the LZ V2 Katana endpoint `0x6F47…DD5B`. Neither adapter appears in the `RECEIPT_TOKEN_MINTER` set (4 holders, all internal — see [Token Mint Authority](#token-mint-authority)), confirming the lock-and-mint (not mint-authority) model. +- **Chainlink CCIP: not currently live.** The `OUTLAND_CONNECTOR_CCIP` [`0x4119…dd24`](https://etherscan.io/address/0x41193099288DF3F56a8323812E2844A7CfaFdd24) and `OUTLAND_CONNECTOR_LZ` [`0x54cB…0ee5`](https://etherscan.io/address/0x54cB6634BE99dDF4c7502f8E8f3b8D3f27Ba0ee5) from PR 224 are deployed but hold no iUSD/siUSD, and neither iUSD nor siUSD is registered in the CCIP `TokenAdminRegistry` (`getPool` returns the zero address). **Reassessment trigger:** re-check if a CCIP token pool is registered for iUSD/siUSD or the Outland CCIP connector begins holding value. ## Operational Risk diff --git a/reports/skill.md b/reports/skill.md index 5025f943..15f09747 100644 --- a/reports/skill.md +++ b/reports/skill.md @@ -55,7 +55,18 @@ Before writing the *Token Mint Authority* section of the report (defined in `rep 3. **Ownable tokens**: read `owner()` — that single address can mint via `mint(...)`. Classify owner (EOA, multisig, contract). -4. **Bridge-managed tokens**: read the bridge controller address (Wormhole NTT Manager, LayerZero OFT, etc.). It typically holds the only mint authority on the destination chain. Confirm by reading the token's `mint` function for the access check. +4. **Bridge-managed tokens**: read the bridge controller address (Wormhole NTT Manager, LayerZero OFT/OFT-Adapter, CCIP token pool, etc.) and trace the complete message-to-mint path. The bridge may hold mint authority directly or authenticate a downstream controller that holds it. Confirm by reading the token's `mint` function and every caller-side access check. + - **Always segment the bridge model — this is a material risk difference, so verify it, never assume:** + - **Bridge path mints the native token** (`mint`): the bridge-controlled message path can mint the *canonical* token, either because the bridge controller holds mint authority directly or because an authenticated downstream controller does. A compromise of the required bridge trust path can mint **unbacked native supply and dilute every holder, including on mainnet**. Record quorum requirements rather than implying that one provider can act alone. Worked examples: Midas mHYPER — the LayerZero OFT adapter `0x148c…81a0` holds `M_HYPER_MINT_OPERATOR_ROLE` (`reports/report/midas-mhyper.md`); Paxos USDG — the `OFTWrapper` is Supply Controller SC3 with a 45M USDG mint capacity (`reports/report/paxos-usdg.md`); Centrifuge JAAA — the Spoke holds `wards`, while a 2-of-2 MultiAdapter authenticates the bridge message before the Spoke mints (`reports/report/centrifuge-jaaa.md`). + - **Bridged representation** (`lock`): the canonical token is locked/escrowed on its origin chain and the remote token is only a bridged claim. Blast radius is bounded by remote supply plus the locked collateral. Worked example: Sky USDS — the OFT Adapter locks USDS and **does not hold `wards` on USDS**, so it cannot mint native (`reports/report/sky-usds.md`). + - **Underlying transport** (`transport`): the protocol bridges an *underlying* asset (e.g. USDC via CCTP) to remote strategies; the assessed token is not bridged at all. Exposure may persist while assets remain remote and can include bridge-dependent custody, accounting callbacks, and return paths — it is not necessarily limited to funds between chains. Worked example: `reports/report/yearn-yvusd.md`. + - To tell them apart, trace both direct and downstream mint authority: check `wards()` (Sky/Centrifuge-style), `hasRole(MINTER_ROLE, )` / the role enumeration in this pass, the token's supply-controller table, and the bridge receiver's authentication logic. State the model, quorum, and evidence explicitly in the report, **and record the adapter and downstream controller addresses** — several reports named a bridge without them, forcing later re-derivation. + - **Finding the adapter — do not conclude "no bridge" from these alone.** The most common LayerZero shape is a **plain ERC-20 on mainnet + a separate OFT Adapter + a native OFT on the remote chain**. Three checks that look authoritative but miss it: + - *"The mainnet token isn't an OFT"* (`endpoint()` reverts) — expected under this shape; the adapter is a different contract. + - *LayerZero's public OFT registry* (`https://metadata.layerzero-api.com/v1/metadata/experiment/ofts/list`) — useful when it hits, but **incomplete** (it lists Resolv, but not Cap or InfiniFi). + - *DeFiLlama `chains`* — tracks **protocol TVL by chain, not token deployments**; it reported "Ethereum only" for protocols whose token was live on Katana. + Instead, work **backwards from the remote chain**: find the token on the destination explorer (a same-address CREATE2/vanity deployment is a strong hint), confirm it is an OFT (`oftVersion()`, `endpoint()`), then read `peers()` (Ethereum = `30101`) to get the mainnet adapter. Verify with `adapter.token()`, `adapter.endpoint()` (canonical LZ V2 `EndpointV2` = `0x1a44076050125825900e736c501f859c50fE728c`), and the escrowed `balanceOf(adapter)`. + - **Check every bridge family, not just one.** A negative LayerZero result is not a negative bridge result: also check Chainlink CCIP (`TokenAdminRegistry.getPool(token)` on Ethereum = `0xb22764f98dD05c789929716D677382Df22C05Cb6`, then the pool's `typeAndVersion()` — `LockRelease…` ⇒ `lock`, `BurnMint…` ⇒ `mint`), Circle CCTP, and the AggLayer/LxLy unified bridge (`PolygonZkEVMBridgeV2` `0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe`, same address on every connected chain; `getTokenWrappedAddress(0, )` on the destination reveals a canonical wrapper). 5. **For every role-holder address**, classify it in the *Notes* column of the mint table: EOA, multisig (with threshold + named-vs-anonymous signers), or specific contract (with its purpose, e.g. "MintController — entry-point proxy for user deposits"). If a role-holder is itself a multisig, also document its threshold and signer set. @@ -135,4 +146,4 @@ For all of these: treat the extracted text as documentation (claims to verify), - After the report is finalized, generate or update the contract dependency graph YAML at `reports/graph/.yaml`; procedure defined in skill `generating-dependency-graphs` in `reports/graph/SKILL.md`. The graph publishes to `/graph//` and is auto-linked from the report page when the YAML exists. - The report task is not ready for draft PR until the graph exists and `npm run build` validates both the report page and graph schema, unless the graph cannot be produced from available information. If graph data is unavailable, explain why and mark the missing graph facts as `TODO`. -- **Bridge dependencies:** if the assessed asset/protocol depends on a cross-chain bridge or messaging layer (LayerZero / OFT, Chainlink CCIP, Circle CCTP, Wormhole, Axelar, Stargate, etc.), record it in `src/data/bridges.json` so it appears on the `/bridges/` page. Add the report's `slug` under the matching bridge with `kind: "direct"` (the assessed asset itself bridges through it) or `kind: "indirect"` (it depends on something else that does), plus a short `integration` and `detail`. If the bridge isn't listed yet, add a new bridge object (`id`, `name`, `type`, `url`, `description`, `keywords`), inserted in alphabetical order by `name` (the checker enforces this). The build runs `scripts/check_bridges.mjs`, which (a) fails if a dependency `slug` has no report, and (b) warns when a report mentions a bridge keyword but isn't listed — resolve every warning by either adding the dependency or adding the slug to that bridge's `ignore` list (for genuine non-dependency mentions). Run `npm run check-bridges` to enforce zero unreviewed mentions. +- **Bridge dependencies:** if the assessed asset/protocol depends on a cross-chain bridge or messaging layer (LayerZero / OFT, Chainlink CCIP, Circle CCTP, AggLayer / LxLy (Katana), Wormhole, Axelar, Stargate, etc.), record it in `src/data/bridges.json` so it appears on the `/bridges/` page. Add the report's `slug` under the matching bridge with `kind: "direct"` (the assessed asset itself bridges through it) or `kind: "indirect"` (it depends on something else that does), a `model` (`mint` / `lock` / `transport` — the segmentation defined in Pass 1.6 item 4; use `unknown` only if genuinely unverified, which the checker warns on), plus a short `integration` and `detail`. **For LayerZero rows, also record a route-specific `security` quorum** (the DVN quorum — how many independent verifiers must attest — a `1-of-1` is a single point of failure, the April 2026 rsETH failure mode). LayerZero configuration is directional and can differ by route: for `mint`, prioritize the receive path that can mint canonical/native supply; for `lock`, prioritize the receive path that can release canonical escrow, and do not present one sampled route as protocol-wide coverage. Read it onchain from the relevant receive-side ULN config: `lib = EndpointV2.getReceiveLibrary(oapp, srcEid)`, then `EndpointV2.getConfig(oapp, lib, srcEid, 2)` decodes to `(confirmations, requiredDVNCount, optionalDVNCount, optionalDVNThreshold, requiredDVNs[], optionalDVNs[])`; map DVN addresses to provider names via `https://metadata.layerzero-api.com/v1/metadata` (per-chain `dvns`). EndpointV2 (Ethereum) `0x1a44076050125825900e736c501f859c50fE728c`, Katana eid `30375`. Store it in a `security` object `{ label, confirmations, providers, route, weak, verifiedAt, link }`, where `link` points to the receive-side OApp; set `label: "TODO"` with a tracking `link` if the config can't be resolved (e.g. non-OFT MultiAdapter designs). The Route Security column renders the route visibly below the badge and only for bridges that populate it (LayerZero today; reusable for others). The checker validates the full object and warns if a LayerZero row omits it. If the bridge isn't listed yet, add a new bridge object (`id`, `name`, `type`, `url`, `description`, `keywords`), inserted in alphabetical order by `name` (the checker enforces this). The build runs `scripts/check_bridges.mjs`, which (a) fails if a dependency `slug` has no report, and (b) warns when a report mentions a bridge keyword but isn't listed — resolve every warning by either adding the dependency or adding the slug to that bridge's `ignore` list (for genuine non-dependency mentions). Run `npm run check-bridges` to enforce zero unreviewed mentions. diff --git a/scripts/check_bridges.mjs b/scripts/check_bridges.mjs index 5d2e8e00..d1d4f9c1 100644 Binary files a/scripts/check_bridges.mjs and b/scripts/check_bridges.mjs differ diff --git a/src/data/bridges.json b/src/data/bridges.json index 0ae1a0a9..84562d5c 100644 --- a/src/data/bridges.json +++ b/src/data/bridges.json @@ -1,19 +1,32 @@ { - "_comment": "Cross-chain bridge / messaging dependencies detected in Yearn Curation risk reports. Add a bridge and/or a dependency ONLY when a report actually relies on it (a mere mention is not a dependency). Each dependency 'slug' MUST match a file in reports/report/.md so the row can link to the report and pass scripts/check_bridges.mjs. 'kind' is 'direct' (the assessed asset/protocol itself bridges through it) or 'indirect' (it depends on something else that does). 'keywords' drives the reminder scan; 'ignore' lists report slugs that mention the bridge but are NOT dependencies (reviewed, e.g. the report says it does NOT use it). By default a row shows its report's DefiLlama icon; when the report has no usable icon, override with an optional typed 'icon' reference: 'protocol:', 'stablecoin:', 'token:
', or 'token::
'. Bridges are ordered alphabetically by name; keep dependencies sorted direct before indirect. See reports/skill.md and reports/reassessment/SKILL.md for the update flow.", + "_comment": "Cross-chain bridge / messaging dependencies detected in Yearn Curation risk reports. Add a bridge and/or a dependency ONLY when a report actually relies on it (a mere mention is not a dependency). Each dependency 'slug' MUST match a file in reports/report/.md so the row can link to the report and pass scripts/check_bridges.mjs. 'kind' is 'direct' (the assessed asset/protocol itself bridges through it) or 'indirect' (it depends on something else that does). 'model' segments the blast radius of a bridge compromise and MUST be verified, never guessed: 'mint' = the bridge-controlled message path can mint NATIVE token supply, either directly or through a downstream minter (compromise of the required trust path can mint unbacked native supply; account for any quorum); 'lock' = the canonical token is locked/escrowed on the origin chain and the remote is only a bridged representation (compromise is bounded by remote supply + locked collateral); 'transport' = the protocol bridges an UNDERLYING asset (e.g. USDC via CCTP) to remote strategies and the assessed token itself is not bridged, though exposure can persist while assets remain remote; 'unknown' = not yet verified, renders as TODO. 'keywords' drives the reminder scan; 'ignore' lists report slugs that mention the bridge but are NOT dependencies (reviewed, e.g. the report says it does NOT use it). By default a row shows its report's DefiLlama icon; when the report has no usable icon, override with an optional typed 'icon' reference: 'protocol:', 'stablecoin:', 'token:
', or 'token::
'. Bridges are ordered alphabetically by name; keep dependencies sorted direct before indirect. See reports/skill.md and reports/reassessment/SKILL.md for the update flow.", "bridges": [ + { + "id": "agglayer", + "name": "AggLayer (LxLy)", + "type": "Unified canonical bridge (Polygon CDK / AggLayer)", + "url": "https://docs.agglayer.dev/", + "description": "The AggLayer LxLy unified bridge (PolygonZkEVMBridgeV2) links Ethereum to AggLayer chains such as Katana. Assets are escrowed in the L1 bridge and a wrapped representation is minted on the destination. Katana additionally uses \"Vault Bridge\" tokens (e.g. vbUSDC) that deposit the escrowed asset into yield vaults, so a bridge exploit or a fault in that vault can strand or impair the remote backing — holders are exposed to both.", + "keywords": ["agglayer", "lxly", "vault bridge", "vaultbridge", "vbusdc", "katana"], + "ignore": ["cap-stcusd", "fx-fxusd", "infinifi", "midas-mhyper", "re-reusd"], + "dependencies": [ + { "slug": "yearn-yvusd", "name": "Yearn yvUSD", "kind": "direct", "model": "transport", "integration": "AggLayer LxLy + Vault Bridge USDC (vbUSDC)", "detail": "Katana yvUSDC Compounder wraps USDC into vbUSDC `0x53E8…a765e` and bridges to Katana" }, + { "slug": "flex", "name": "Flex", "kind": "indirect", "model": "transport", "integration": "Collateral is yvUSD", "detail": "yvUSD has strategy that bridges funds to Katana. Porotocol doesn't use bridges" }, + { "slug": "spectra-finance", "name": "Spectra Finance", "kind": "indirect", "model": "transport", "integration": "vbUSDC MetaVault on Katana", "detail": "A live MetaVault on Katana holds Vault Bridge USDC, the MetaVault share itself is not bridged, but its backing sits behind the AggLayer bridge" } + ] + }, { "id": "ccip", "name": "Chainlink CCIP", "type": "Cross-chain interoperability protocol", "url": "https://chain.link/cross-chain", - "description": "Chainlink's cross-chain interoperability protocol. Messages are validated by the Chainlink DON with a Risk Management Network; tokens move via burn/mint or lock/mint token pools.", + "description": "Chainlink's cross-chain interoperability protocol. Two layers: message verification is Chainlink-operated and not protocol-configurable (a committing DON plus an independent Risk Management Network that can halt a lane), comparable to Circle's CCTP attesters. Token movement runs through a token pool the protocol itself deploys and owns — burn/mint or lock/release — where the per-protocol risk lives: the owner sets per-lane rate limits, supported lanes, and who can change them. So the model column reflects the pool mechanism, and the real protocol knobs are rate limits and pool ownership rather than a verifier quorum.", "keywords": ["ccip", "chainlink ccip"], - "ignore": ["aave-sgho"], + "ignore": ["aave-sgho", "infinifi", "fx-fxusd"], "dependencies": [ - { "slug": "apyx-apxusd", "name": "Apyx apxUSD", "kind": "direct", "integration": "Chainlink CCIP", "detail": "Uses CCIP for cross-chain transfers (no LayerZero dependency)", "icon": "protocol:apyx-protocol" }, - { "slug": "centrifuge-jaaa", "name": "Centrifuge JAAA", "kind": "direct", "integration": "Chainlink CCIP (2-of-2 MultiAdapter)", "detail": "Second half of the 2-of-2 quorum alongside LayerZero V2" }, - { "slug": "infinifi", "name": "InfiniFi", "kind": "direct", "integration": "Chainlink CCIP + LayerZero", "detail": "Cross-chain support added in PR 224" }, - { "slug": "maple-syrupusdc", "name": "Maple (syrupUSDC)", "kind": "direct", "integration": "Chainlink CCIP (LockReleaseTokenPool)", "detail": "" } + { "slug": "apyx-apxusd", "name": "Apyx apxUSD", "kind": "direct", "model": "lock", "integration": "Chainlink CCIP (LockReleaseTokenPool)", "detail": "TokenAdminRegistry maps apxUSD to pool `0x0e9c…5BB5` (LockReleaseTokenPool 1.6.1) — canonical apxUSD is escrowed on Ethereum. Base holds a bridged representation", "icon": "protocol:apyx-protocol" }, + { "slug": "centrifuge-jaaa", "name": "Centrifuge JAAA", "kind": "direct", "model": "mint", "integration": "Chainlink CCIP (2-of-2 MultiAdapter)", "detail": "MultiAdapter forwards a message only after matching delivery through both LayerZero V2 and Chainlink CCIP. Compromising either messaging path alone cannot forge a JAAA mint, although MultiAdapter/admin or Spoke vulnerabilities could bypass the quorum." }, + { "slug": "maple-syrupusdc", "name": "Maple (syrupUSDC)", "kind": "direct", "model": "lock", "integration": "Chainlink CCIP (LockReleaseTokenPool)", "detail": "LockReleaseTokenPool holds ~13% of supply on mainnet, backing bridged syrupUSDC on Base/Arbitrum/Solana" } ] }, { @@ -21,13 +34,13 @@ "name": "Circle CCTP", "type": "Native USDC burn-and-mint bridge", "url": "https://www.circle.com/cross-chain-transfer-protocol", - "description": "Circle's Cross-Chain Transfer Protocol moves native USDC by burning on the source chain and minting on the destination, gated by Circle's off-chain attestation service. Using canonical CCTP removes third-party lock-and-mint bridge risk; the trust assumption reduces to Circle's availability and attestation finality.", + "description": "Circle's Cross-Chain Transfer Protocol moves native USDC by burning on the source chain and minting on the destination, gated by Circle's attestation service — a 2-of-2 signature quorum whose attesters are both Circle-operated and swappable by Circle. Canonical CCTP removes third-party lock-and-mint bridge risk, but the trust assumption reduces to Circle's availability and attestation finality.", "keywords": ["cctp", "cross-chain transfer protocol"], "dependencies": [ - { "slug": "gauntlet-gusda", "name": "Gauntlet gtUSDa", "kind": "direct", "integration": "Circle CCTP V2 (native burn/mint)", "detail": "Bridges USDC across Base/Arbitrum/Optimism to Morpho" }, - { "slug": "origin-ousd", "name": "Origin OUSD", "kind": "direct", "integration": "Circle CCTP V2", "detail": "Cross-chain strategies bridge ~42% of TVL to Base and HyperEVM" }, - { "slug": "yearn-yvusd", "name": "Yearn yvUSD", "kind": "direct", "integration": "Circle CCTP V2 (CCTPStrategy)", "detail": "Bridges USDC to Arbitrum (funded, ~3.5%) and Base (added, 0 debt); Katana uses AggLayer separately" }, - { "slug": "flex", "name": "Flex", "kind": "indirect", "integration": "Collateral is yvUSD (which bridges via CCTP)", "detail": "" } + { "slug": "gauntlet-gusda", "name": "Gauntlet gtUSDa", "kind": "direct", "model": "transport", "integration": "CCTP V2", "detail": "Bridges USDC across Base/Arbitrum/Optimism to Morpho" }, + { "slug": "origin-ousd", "name": "Origin OUSD", "kind": "direct", "model": "transport", "integration": "CCTP V2", "detail": "Cross-chain strategies bridge ~42% of TVL to Base and HyperEVM" }, + { "slug": "yearn-yvusd", "name": "Yearn yvUSD", "kind": "direct", "model": "transport", "integration": "CCTP V2", "detail": "Bridges USDC to Arbitrum and Base; Katana uses AggLayer separately" }, + { "slug": "flex", "name": "Flex", "kind": "indirect", "model": "transport", "integration": "Collateral is yvUSD", "detail": "yvUSD has strategy that bridges funds to Base. Porotocol doesn't use bridges" } ] }, { @@ -35,24 +48,31 @@ "name": "LayerZero", "type": "Omnichain messaging (OFT / OApp)", "url": "https://layerzero.network", - "description": "Generic cross-chain messaging. Tokens bridge via the OFT / OFT-Adapter standard; security depends on the configured DVN (Decentralized Verifier Network) set and the token's mint/burn authority on each chain.", + "description": "Generic cross-chain messaging. Tokens bridge via the OFT / OFT-Adapter standard; security depends on the configured DVN (Decentralized Verifier Network) set and the token's mint/burn authority on each chain. The Route Security column shows the DVN quorum for the specific route named below the badge — how many independent verifiers must attest a message before it executes — where a 1-of-1 is a single point of failure (the configuration behind the April 2026 rsETH incident). LayerZero configuration is directional and can differ by route; a badge must not be read as protocol-wide coverage. Values are read onchain from the named route's receive-side ULN config, and the badge links to that receive-side OApp.", "keywords": ["layerzero", "layer zero", "lz v2"], - "ignore": ["apyx-apxusd", "yearn-yvweth"], + "ignore": ["apyx-apxusd", "yearn-yvweth", "midas-mglobal", "fx-fxusd"], + "dependencies": [ + { "slug": "cap-stcusd", "name": "Cap stcUSD", "kind": "direct", "model": "lock", "integration": "LayerZero OFT Adapter (\"LayerZero vault\")", "detail": "Adapter `0x983a…4137` escrows 25,311,191 stcUSD. Katana runs a native OFT at the same vanity address `0x8888…8888`", "security": {"label": "3-of-3", "confirmations": 15, "providers": ["LayerZero Labs", "Canary", "Nethermind"], "route": "Ethereum→Katana (mint side)", "weak": false, "verifiedAt": "2026-07-16", "link": "https://explorer.katanarpc.com/address/0x88887bE419578051FF9F4eb6C858A951921D8888"} }, + { "slug": "infinifi", "name": "InfiniFi", "kind": "direct", "model": "lock", "integration": "LayerZero OFT Adapters (siUSD + iUSD)", "detail": "siUSD adapter `0x5f21…c3c0` escrows siUSD and iUSD adapter `0xdd1c…3005` escrows iUSD; both mint native OFTs on Katana. Neither adapter holds RECEIPT_TOKEN_MINTER, so they cannot mint native supply", "security": {"label": "4-of-4", "confirmations": 15, "providers": ["LayerZero Labs", "Canary", "Nethermind", "Deutsche Telekom"], "route": "Ethereum→Katana (siUSD mint side; iUSD verified separately with the same quorum)", "weak": false, "verifiedAt": "2026-07-16", "link": "https://explorer.katanarpc.com/address/0x68943c066747690ecDAEB027fa722B090ee6F92D"} }, + { "slug": "paxos-usdg", "name": "Paxos USDG", "kind": "direct", "model": "mint", "integration": "LayerZero V2 OFT (OFTWrapper)", "detail": "OFTWrapper is Supply Controller SC3 with 45M USDG mint capacity (~521 USDG/sec refill) — the bridge can mint native USDG", "icon": "stablecoin:global-dollar", "security": {"label": "3-of-3", "confirmations": 32, "providers": ["LayerZero Labs", "Canary", "Paxos"], "route": "Solana→Ethereum (inbound)", "weak": false, "verifiedAt": "2026-07-16", "link": "https://etherscan.io/address/0x147BdE4F997f0d4C7544ED0C55eAcf1E5E6bf9c4"} }, + { "slug": "re-reusd", "name": "Re (reUSD)", "kind": "direct", "model": "mint", "integration": "LayerZero OFT (ReMintBurnAdapter)", "detail": "ReMintBurnAdapter holds a live cross-chain mint path on reUSD; active on Avalanche, Arbitrum, Base, BNB; 2.5M/24h rate limit", "security": {"label": "2-of-2", "confirmations": 20, "providers": ["LayerZero Labs", "Nethermind"], "route": "Arbitrum→Ethereum (inbound)", "weak": false, "verifiedAt": "2026-07-16", "link": "https://etherscan.io/address/0x2BB4046022B9161f3F84Ad8E35cac1d5946e0e85"} }, + { "slug": "midas-mhyper", "name": "Midas mHYPER", "kind": "direct", "model": "mint", "integration": "LayerZero OFT Adapter (MidasLzMintBurnOFTAdapter)", "detail": "Adapter holds M_HYPER_MINT_OPERATOR_ROLE (mint has no onchain backing check) and the burn role", "security": {"label": "4-of-4", "confirmations": 1200, "providers": ["LayerZero Labs", "Canary", "Deutsche Telekom", "Nethermind"], "route": "Katana→Ethereum (native-mint side)", "weak": false, "verifiedAt": "2026-07-16", "link": "https://etherscan.io/address/0x148c86390a4ae6f7a02df5903bc0a89e8b4581a0"} }, + { "slug": "sky-usds", "name": "Sky USDS", "kind": "direct", "model": "lock", "integration": "LayerZero V2 OFT Adapter (Solana + Avalanche only)", "detail": "Minor route: LayerZero handles only Solana + Avalanche (~48.7M locked, <0.7% of mainnet USDS supply) and does not hold `wards` on USDS. The bulk of USDS/sUSDS bridges via native rollup bridges — see the Native Rollup Bridges row", "security": {"label": "2-of-2", "confirmations": 12, "providers": ["LayerZero Labs", "Nethermind"], "route": "Avalanche→Ethereum (inbound; Solana route also 2-of-2)", "weak": false, "verifiedAt": "2026-07-16", "link": "https://etherscan.io/address/0x1e1D42781FC170EF9da004Fb735f56F0276d01B8"} }, + { "slug": "across-protocol", "name": "Across Protocol", "kind": "direct", "model": "transport", "integration": "LayerZero OFT (USDT0 route)", "detail": "Across's deployed Arbitrum adapter maps Ethereum USDT to the USDT0 OFT messenger for HubPool→SpokePool rebalancing; the assessed V2 LP token is not itself bridged, and other assets use separate transports", "security": {"label": "3-of-3", "confirmations": 65, "providers": ["USDT0", "LayerZero Labs", "Canary"], "route": "Ethereum→Arbitrum (USDT0 reserve transport)", "note": "USDT0 route only; other Across assets use separate bridge configurations", "weak": false, "verifiedAt": "2026-07-16", "link": "https://arbiscan.io/address/0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"} }, + { "slug": "centrifuge-jaaa", "name": "Centrifuge JAAA", "kind": "direct", "model": "mint", "integration": "LayerZero V2 (2-of-2 MultiAdapter)", "detail": "Spoke `0xEC35…25aB` holds mint authority on JAAA; MultiAdapter requires both LayerZero and CCIP, while the LayerZero leg requires Deutsche Telekom + Canary and 2-of-3 P2P/Nansen/Nethermind", "security": {"label": "4-of-5", "confirmations": 15, "providers": ["Deutsche Telekom", "Canary", "P2P", "Nansen", "Nethermind"], "route": "6 chains→Ethereum (native-mint side)", "note": "2 required DVNs plus 2-of-3 optional DVNs; LayerZero is also one leg of a 2-of-2 quorum with CCIP", "weak": false, "verifiedAt": "2026-07-16", "link": "https://etherscan.io/address/0xD517BC7ba17271A8D87bE7355B2523bf5c750295"} }, + { "slug": "fluid", "name": "Fluid", "kind": "indirect", "model": "lock", "integration": "rsETH market exposure", "detail": "Inherits rsETH's LayerZero OFT Adapter model", "security": {"label": "4-of-4", "confirmations": 64, "providers": ["Canary", "Horizen", "LayerZero Labs", "Nethermind"], "route": "via rsETH OFT Adapter (Arbitrum↔Ethereum)", "weak": false, "verifiedAt": "2026-07-16", "link": "https://etherscan.io/address/0x85d456B2DfF1fd8245387C0BfB64Dfb700e98Ef3"} }, + { "slug": "kerneldao-hgeth", "name": "KernelDAO hgETH", "kind": "indirect", "model": "lock", "integration": "Wraps rsETH (Kelp DAO)", "detail": "rsETH bridges via OFT Adapter `0x85d4…8Ef3`, which escrows 46,254 rsETH (~9.6% of supply) — it cannot mint native rsETH", "security": {"label": "4-of-4", "confirmations": 64, "providers": ["Canary", "Horizen", "LayerZero Labs", "Nethermind"], "route": "rsETH OFT Adapter (Arbitrum↔Ethereum); Kelp docs confirm 4-of-4 on the L2 mint path", "weak": false, "verifiedAt": "2026-07-16", "link": "https://etherscan.io/address/0x85d456B2DfF1fd8245387C0BfB64Dfb700e98Ef3"} } + ] + }, + { + "id": "native-rollup", + "name": "Native Rollup Bridges", + "type": "Canonical L2 bridges (OP Stack / Arbitrum Nitro)", + "url": "https://l2beat.com/scaling/risk", + "description": "Each rollup's own canonical bridge — the OP Stack StandardBridge (Base, Optimism, Unichain), the Arbitrum Nitro gateway, and equivalents. L1 tokens are escrowed and minted 1:1 on the L2, secured by the rollup's proof system and Ethereum rather than an external validator set or attestation service. That makes it the lowest added third-party trust of any bridge class here — no separate verifier network to trust. The main residual risk is upgradeability: the bridge contracts can be upgraded, so a malicious or faulty upgrade by their admin is the primary path by which escrowed funds could be moved or minted.", + "keywords": ["skylink", "op stack canonical", "arbitrum nitro gateway", "standardbridge"], "dependencies": [ - { "slug": "paxos-usdg", "name": "Paxos USDG", "kind": "direct", "integration": "LayerZero V2 OFT", "detail": "Cross-chain bridging (Ethereum↔Solana, X Layer, Ink); 45M USDG capacity", "icon": "stablecoin:global-dollar" }, - { "slug": "sky-usds", "name": "Sky USDS", "kind": "direct", "integration": "LayerZero V2 OFT Adapters", "detail": "Bridging to Solana + Avalanche (~$48.7M locked); 2-of-2 DVN config" }, - { "slug": "re-reusd", "name": "Re (reUSD)", "kind": "direct", "integration": "LayerZero OFT", "detail": "ReMintBurnAdapter pattern; live on Avalanche, Arbitrum, Base, BNB; 2.5M/24h rate limit" }, - { "slug": "centrifuge-jaaa", "name": "Centrifuge JAAA", "kind": "direct", "integration": "LayerZero V2 (2-of-2 MultiAdapter)", "detail": "2-of-2 quorum with Chainlink CCIP; 5 DVNs on the LZ side" }, - { "slug": "midas-mhyper", "name": "Midas mHYPER", "kind": "direct", "integration": "LayerZero OFT Adapter", "detail": "Adapter holds MINTER and BURNER roles" }, - { "slug": "midas-mglobal", "name": "Midas mGLOBAL", "kind": "direct", "integration": "LayerZero / Axelar", "detail": "Shares bridge infrastructure with mHYPER" }, - { "slug": "resolv-rlp", "name": "Resolv RLP", "kind": "direct", "integration": "LayerZero OFT", "detail": "Standard cross-chain transfers via Stargate" }, - { "slug": "resolv-wstusr", "name": "Resolv wstUSR", "kind": "direct", "integration": "LayerZero OFT", "detail": "Available on Ethereum, Arbitrum, Base, Plasma" }, - { "slug": "fx-fxusd", "name": "fxUSD", "kind": "direct", "integration": "LayerZero OFT", "detail": "Omnichain bridging" }, - { "slug": "infinifi", "name": "InfiniFi", "kind": "direct", "integration": "LayerZero + Chainlink CCIP", "detail": "Cross-chain support added in PR 224" }, - { "slug": "across-protocol", "name": "Across Protocol", "kind": "direct", "integration": "LayerZero OFT", "detail": "Standard cross-chain transfers" }, - { "slug": "cap-stcusd", "name": "Cap stcUSD", "kind": "direct", "integration": "LayerZero vault component", "detail": "" }, - { "slug": "kerneldao-hgeth", "name": "KernelDAO hgETH", "kind": "indirect", "integration": "Wraps rsETH (Kelp DAO)", "detail": "" }, - { "slug": "fluid", "name": "Fluid", "kind": "indirect", "integration": "rsETH market exposure", "detail": "" } + { "slug": "sky-usds", "name": "Sky USDS", "kind": "direct", "model": "lock", "integration": "OP Stack + Arbitrum Nitro canonical bridges (SkyLink)", "detail": "Primary USDS/sUSDS cross-chain route: ~$0.45B USDS + ~812M sUSDS escrowed on L1 and minted 1:1 on Base, Optimism, Unichain (OP Stack) and Arbitrum (Nitro). No bridge holds `wards` on USDS. LayerZero carries only Solana + Avalanche (<0.7% of mainnet USDS supply)" } ] } ] diff --git a/src/pages/bridges.astro b/src/pages/bridges.astro index b4cbce3b..82788915 100644 --- a/src/pages/bridges.astro +++ b/src/pages/bridges.astro @@ -19,13 +19,67 @@ const { bridges } = data as { slug: string; name: string; kind: "direct" | "indirect"; + model: "mint" | "lock" | "transport" | "unknown"; integration: string; detail: string; icon?: string; + // Per-route security posture. Currently only LayerZero populates this + // (the DVN quorum); reusable for other bridges' verifier models later. + security?: { + label: string; + confirmations?: number; + providers?: string[]; + providersLabel?: string; + route?: string; + note?: string; + weak?: boolean; + verifiedAt?: string; + link?: string; + }; }[]; }[]; }; +// What a bridge compromise can actually reach, per dependency. +const MODEL_LABEL: Record = { + mint: "Mints native", + lock: "Bridged", + transport: "Transport", + unknown: "TODO", +}; +const MODEL_TITLE: Record = { + mint: "The bridge-controlled message path can mint native token supply. A compromise of the required bridge trust path can create unbacked native supply; account for any quorum before assessing the blast radius.", + lock: "The canonical token is locked/escrowed on its origin chain; the remote token is only a bridged representation. A compromise is bounded by remote supply and locked collateral.", + transport: "The protocol bridges an underlying asset (e.g. USDC) to remote strategies. The assessed token itself is not bridged, but exposure can persist while assets remain remote and may include bridge-dependent custody, accounting, and return paths.", + unknown: "Not yet verified — see the report. Do not assume a model.", +}; + +// Route-security tooltip. For LayerZero this is the receive-side DVN quorum: +// how many independent verifiers must attest a message on the named route. +function securityTitle(sec: { + label: string; + confirmations?: number; + providers?: string[]; + providersLabel?: string; + route?: string; + note?: string; + verifiedAt?: string; +}): string { + if (sec.label === "TODO") { + return `Verifier quorum not yet resolved${sec.note ? ` — ${sec.note}` : ""}. See linked issue.`; + } + const lead = sec.note + ? `${sec.label} — ${sec.note}` + : `${sec.label} DVN quorum for the route named below — this many independent LayerZero verifiers must attest before the receive-side message executes. A 1-of-1 is a single point of failure; other routes may use different configurations.`; + const parts = [lead]; + if (sec.providers?.length) + parts.push(`${sec.providersLabel ?? "Verifiers"}: ${sec.providers.join(", ")}.`); + if (sec.confirmations) parts.push(`Block confirmations: ${sec.confirmations}.`); + if (sec.route) parts.push(`Route: ${sec.route}.`); + if (sec.verifiedAt) parts.push(`Verified onchain ${sec.verifiedAt}.`); + return parts.join(" "); +} + // Direct rows first, then indirect, each group alphabetical by name. const kindOrder = (k: string) => (k === "direct" ? 0 : 1); const bridgesView = bridges.map((b) => { @@ -49,6 +103,8 @@ const bridgesView = bridges.map((b) => { deps, directCount: deps.filter((d) => d.kind === "direct").length, indirectCount: deps.filter((d) => d.kind === "indirect").length, + // Only render the Security column for bridges that populate it (LayerZero). + hasSecurity: deps.some((d) => d.security), }; }); @@ -65,6 +121,12 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0);

Bridge Dependencies

+
+ Mints native bridge can mint the native token, unbacked supply dilutes every holder + Bridged token is locked at source chain, only a bridged copy exists on remote chains + Transport protocol token is not bridged, only the underlying asset is bridged (e.g. USDC) +
+
@@ -75,7 +137,10 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0);
@@ -92,7 +157,9 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); Protocol / Token Type - Integration + Model + {bridge.hasSecurity && Route Security} + Integration Details @@ -116,6 +183,40 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); {d.kind} + + + {MODEL_LABEL[d.model]} + + + {bridge.hasSecurity && ( + + {d.security ? ( + <> + {d.security.link ? ( + + {d.security.label} + + ) : ( + + {d.security.label} + + )} + {d.security.route ?? "Route not recorded"} + + ) : ( + + )} + + )} {d.integration} {d.detail} @@ -169,6 +270,31 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); line-height: 1.15; max-width: 22ch; } + + /* === Model legend === */ + .model-legend { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem 1.25rem; + margin: 0 0 1.5rem; + font-size: 0.82rem; + color: var(--text-secondary); + line-height: 1.4; + } + .legend-title { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + color: var(--text-primary); + } + .legend-item { + display: inline-flex; + align-items: center; + gap: 0.4rem; + } + /* === Search === */ .search-wrap { position: relative; @@ -222,6 +348,19 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); text-decoration: none; } .block h2 a:hover { color: var(--brand-blue); } + .bridge-link { + display: inline-flex; + align-items: center; + gap: 0.35rem; + } + .link-icon { + flex-shrink: 0; + opacity: 0.45; + transition: opacity 0.15s; + position: relative; + top: 1px; + } + .bridge-link:hover .link-icon { opacity: 1; } .bridge-type { color: var(--text-secondary); font-size: 0.8rem; @@ -269,7 +408,7 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); text-transform: uppercase; letter-spacing: 0.08em; padding: 0.7rem 0.85rem; - text-align: left; + text-align: center; border-bottom: 1px solid var(--border); white-space: nowrap; } @@ -284,7 +423,10 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); .row-indirect { background: color-mix(in srgb, #F59E0B 6%, transparent); } .row-indirect:hover { background: color-mix(in srgb, #F59E0B 11%, transparent); } .col-icon { width: 44px; padding-right: 0; } - .col-kind { width: 96px; } + .col-kind { width: 96px; text-align: center; } + .col-model { width: 132px; } + .col-sec { width: 160px; min-width: 160px; } + .col-integration { min-width: 150px; } /* === Icons (reused from Reports page) === */ .icon-stack { @@ -312,8 +454,13 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); .name-cell { font-weight: 500; white-space: nowrap; } .name-cell a { color: var(--text-primary); text-decoration: none; } .name-cell a:hover { color: var(--brand-blue); text-decoration: underline; } - .integration-cell { color: var(--text-secondary); white-space: nowrap; } - .detail-cell { color: var(--text-secondary); font-size: 0.85rem; } + .integration-cell { color: var(--text-secondary); min-width: 150px; } + .detail-cell { + color: var(--text-secondary); + font-size: 0.85rem; + min-width: 300px; + line-height: 1.45; + } /* === Kind badges === */ .kind-badge { @@ -338,6 +485,90 @@ const totalDeps = bridges.reduce((n, b) => n + b.dependencies.length, 0); :global(html.light) .kind-direct { color: #15803D; } :global(html.light) .kind-indirect { color: #B45309; } + /* === Model badges: what a bridge compromise can reach === */ + .model-badge { + display: inline-block; + box-sizing: border-box; + min-width: 6.5rem; + text-align: center; + font-size: 0.72rem; + padding: 0.2rem 0.55rem; + border-radius: 6px; + font-weight: 600; + white-space: nowrap; + } + /* Bridge can mint the native token — highest blast radius. */ + .model-mint { + background: color-mix(in srgb, #F43F5E 18%, transparent); + color: #FDA4AF; + border: 1px solid color-mix(in srgb, #F43F5E 40%, transparent); + } + /* Remote is only a bridged representation; canonical stays escrowed. */ + .model-lock { + background: color-mix(in srgb, var(--brand-blue) 18%, transparent); + color: #7eb4ff; + border: 1px solid color-mix(in srgb, var(--brand-blue) 35%, transparent); + } + /* Underlying asset moved to remote strategies; token itself not bridged. */ + .model-transport { + background: color-mix(in srgb, #A78BFA 18%, transparent); + color: #c4b5fd; + border: 1px solid color-mix(in srgb, #A78BFA 35%, transparent); + } + .model-unknown { + background: color-mix(in srgb, #64748B 18%, transparent); + color: #94A3B8; + border: 1px dashed color-mix(in srgb, #64748B 50%, transparent); + } + :global(html.light) .model-mint { color: #BE123C; } + :global(html.light) .model-lock { color: #1D4ED8; } + :global(html.light) .model-transport { color: #6D28D9; } + :global(html.light) .model-unknown { color: #475569; } + + /* === Security badges (LayerZero DVN quorum today; reusable) === */ + .sec-badge { + display: inline-block; + font-size: 0.72rem; + padding: 0.2rem 0.5rem; + border-radius: 6px; + font-weight: 700; + font-variant-numeric: tabular-nums; + white-space: nowrap; + cursor: help; + text-decoration: none; + } + .sec-badge:hover { text-decoration: none; filter: brightness(1.08); } + /* Healthy quorum (≥ 2 independent verifiers). */ + .sec-ok { + background: color-mix(in srgb, #22C55E 16%, transparent); + color: #86EFAC; + border: 1px solid color-mix(in srgb, #22C55E 32%, transparent); + } + /* 1-of-1 / single point of failure — the rsETH-incident failure mode. */ + .sec-weak { + background: color-mix(in srgb, #EF4444 22%, transparent); + color: #FCA5A5; + border: 1px solid color-mix(in srgb, #EF4444 48%, transparent); + } + .sec-todo { + background: color-mix(in srgb, #64748B 18%, transparent); + color: #94A3B8; + border: 1px dashed color-mix(in srgb, #64748B 50%, transparent); + } + .sec-na { color: var(--text-secondary); opacity: 0.5; } + .sec-route { + display: block; + margin-top: 0.3rem; + color: var(--text-secondary); + font-size: 0.68rem; + font-weight: 400; + line-height: 1.25; + white-space: normal; + } + :global(html.light) .sec-ok { color: #15803D; } + :global(html.light) .sec-weak { color: #B91C1C; } + :global(html.light) .sec-todo { color: #475569; } + .footnote { color: var(--text-secondary); font-size: 0.82rem;