Skip to content

feat: DEFI-2845: add FDUSD as an independent third USD stablecoin anchor#318

Draft
mbjorkqvist wants to merge 4 commits into
mainfrom
mathias/DEFI-2845-add-fdusd
Draft

feat: DEFI-2845: add FDUSD as an independent third USD stablecoin anchor#318
mbjorkqvist wants to merge 4 commits into
mainfrom
mathias/DEFI-2845-add-fdusd

Conversation

@mbjorkqvist

Copy link
Copy Markdown
Contributor

Purpose

The XRC converts crypto/USDT rates to crypto/USD using USD-pegged stablecoins as proxies. Until now only two stablecoin symbols were used — USDS and USDC — which has two structural weaknesses:

  • With exactly two symbols, the median-of-medians selector has no true "middle", so a single off/stale stablecoin can be selected and bias every crypto/USD (and crypto/fiat) rate.
  • USDS is effectively a single point of failure: if it returns no usable rate across all exchanges, the whole request fails (MIN_NUM_STABLECOIN_RATES = 2). USDS is also partly USDC-collateralized, so it offers little independence from a USDC de-peg.

This adds FDUSD (First Digital USD) as a third base:

  • With three symbols, median_in_set returns a genuine middle and an outlier (stale-high or stale-low) can never be selected.
  • Losing any one symbol still leaves two, removing the single-point-of-failure.
  • FDUSD is independently reserved (not USDC-collateralized), giving real diversification.
  • FDUSD is queried only on MEXC and Gate.io, the two configured exchanges that list a liquid FDUSD-USDT market (a survey of all nine XRC exchanges found no other liquid venue). MIN_NUM_STABLECOIN_RATES is unchanged at 2.

Per the analysis in DEFI-2845. The e2e rate/standard-deviation expectations were recomputed against the three-symbol set; the metrics e2e test now also asserts the FDUSD per-symbol counter.

🤖 Generated with Claude Code

mbjorkqvist and others added 2 commits June 1, 2026 14:28
The XRC anchors USDT->USD using stablecoin proxies. Until now only two
symbols (USDS, USDC) were used, which has two weaknesses: with exactly
two symbols the median-of-medians selector has no true middle, so a
single off/stale symbol can be selected and bias every crypto/USD rate;
and USDS is effectively a single point of failure (if it is unavailable
across all exchanges the whole request errors). USDS is also partly
USDC-collateralized, so it does not provide real independence from a
USDC de-peg.

Add FDUSD (First Digital USD) as a third base. With three symbols the
selector returns a true middle and rejects a single outlier, and losing
any one symbol still leaves two. FDUSD is independently reserved (not
USDC-collateralized), and is queried only on MEXC and Gate.io - the two
configured exchanges with a liquid FDUSD-USDT market.
MIN_NUM_STABLECOIN_RATES is unchanged (2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adding FDUSD to STABLECOIN_BASES means the canister now also fetches an
FDUSD/USDT rate, so the api unit-test mocks and cache fixtures must
supply it: add an FDUSD entry (rate = RATE_UNIT, matching USDS/USDC) to
each get_stablecoin_rates mock map, and insert FDUSD into the cache in
the 'asset and stablecoins in cache' scenario so it stays a cache hit.
Because FDUSD = 1.0 the selected stablecoin multiplier is unchanged, so
no rate assertions change. All xrc unit tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands XRC’s USD-stablecoin anchoring set by adding FDUSD as a third independent USD-pegged proxy, improving robustness of the stablecoin median selection and reducing single-point-of-failure risk when deriving crypto/USD (and downstream crypto/fiat) rates.

Changes:

  • Introduces the FDUSD symbol constant and includes it in the stablecoin base set used for USD anchoring.
  • Queries FDUSD/USDT only on Gate.io and MEXC via exchange-specific supported_stablecoin_pairs overrides.
  • Updates unit/integration tests and metrics e2e assertions to reflect the new three-stablecoin behavior and updated expected outputs.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/xrc/src/lib.rs Adds the FDUSD stablecoin symbol constant.
src/xrc/src/exchanges.rs Extends Gate.io/MEXC stablecoin pair support to include FDUSD/USDT and updates related tests.
src/xrc/src/api.rs Adds FDUSD to STABLECOIN_BASES, enabling it as an anchor in stablecoin selection.
src/xrc/src/api/test.rs Updates API unit tests/mocks to provide FDUSD stablecoin responses and cached entries where required.
src/xrc-tests/src/tests/misbehavior.rs Updates expected e2e outputs impacted by the third stablecoin anchor.
src/xrc-tests/src/tests/metrics_endpoint.rs Asserts metrics exposure for the FDUSD per-symbol stablecoin counter.
src/xrc-tests/src/tests/get_icp_xdr_rate.rs Updates expected e2e values reflecting the revised anchoring set.
src/xrc-tests/src/tests/basic_exchange_rates.rs Updates expected e2e values reflecting the revised anchoring set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/xrc/src/api.rs
mbjorkqvist and others added 2 commits June 3, 2026 14:18
…et refs

Order the stablecoin symbol arrays as USDC, USDS, FDUSD everywhere
(STABLECOIN_BASES, the per-exchange supported_stablecoin_pairs, and the
test walk-throughs). For the two-symbol exchanges this makes the deep,
robust USDC the tie-winner of median_in_set, so a USDS depeg is avoided
on the common path; with three symbols a true middle exists and the
order is otherwise cosmetic but kept consistent.

Also drop the "(see DEFI-2845)" references from the FDUSD code comments
now that the rationale lives in the comments themselves.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
get_stablecoin_rate built the cached QueriedExchangeRate with
num_queried_sources = exchanges.len(), but a symbol is only queried on
the exchanges that list a supported pair for it. With FDUSD added to a
subset of exchanges this overstated *_num_queried_sources and could
mislead monitoring. Use the number of futures actually issued instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mbjorkqvist mbjorkqvist marked this pull request as ready for review June 3, 2026 14:20
@mbjorkqvist mbjorkqvist requested a review from a team as a code owner June 3, 2026 14:20
mbjorkqvist added a commit that referenced this pull request Jun 4, 2026
…S]) (#320)

## Purpose

A zero-cost interim mitigation for the two-symbol stablecoin selection
(DEFI-2845).

With exactly two stablecoin symbols, `median_in_set` has no true middle:
the two per-symbol medians are equidistant from their midpoint, so the
(almost-always) tie is broken toward the **first** entry. Listing `USDS`
first therefore made the thin, depeg-prone USDS the **default USD
anchor** — and a USDS depeg would be silently baked into every
crypto/USD rate (see the worked 15% scenario in DEFI-2845).

Reordering to `[USDC, USDS]` makes the robust, deep USDC the default
tie-winner, so a USDS depeg is avoided on the common path.

## Caveats / scope

- This only **shifts** which coin's depeg propagates (USDC-first means a
USDC depeg is always selected); it's an improvement only because USDC is
far more reliable and liquid than USDS. It is **not** a fix.
- It is **superseded by the FDUSD PR (#318)**: with an odd set (3
symbols) `median_in_set` returns the true middle by value and order no
longer matters, so this change becomes inert.

## Merge order

Merge this **before** #318 if there's any gap before FDUSD ships —
that's the only window in which it adds value. The two touch the same
line (`STABLECOIN_BASES`), so #318 should rebase on top to `[USDC, USDS,
FDUSD]`. If #318 is going out imminently, this PR can simply be closed
as superseded.

No behaviour change in the e2e scenarios (their median sums are odd, so
the lower value already wins regardless of order); all unit and e2e
tests pass unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mbjorkqvist mbjorkqvist marked this pull request as draft June 16, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants