Skip to content

Risk Assessment: USDat (Saturn)#225

Open
spalen0 wants to merge 11 commits into
masterfrom
usdat
Open

Risk Assessment: USDat (Saturn)#225
spalen0 wants to merge 11 commits into
masterfrom
usdat

Conversation

@spalen0

@spalen0 spalen0 commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #135.

Adds reports/report/saturn-usdat.md — a risk assessment of USDat, Saturn's non-yielding stablecoin (assessment subject per the issue: "USDat as collateral").

Assess USDat per issue #135. USDat is an M0 extension token backed 1:1
by $M (tokenized US Treasuries); STRC/BTC credit exposure sits in the
sUSDat yield layer. Final score 3.0/5.0 (Medium Risk).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel

vercel Bot commented May 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
risk-score Ready Ready Preview, Comment Jun 10, 2026 2:12pm

Request Review

Ingested the issue's deeper sources (Notion DD FAQ/Ops&Risk/STRC pages
via Notion's public API, and the Google Doc contract spec via text
export). Adds verified detail: auditors (Three Sigma + Certora), the
two-leg redemption path (USDat->wM->USDC, 1bps), legal structure
(Cayman foundation / BVI Saturn Capital & Fund / Panama), off-chain
providers (Galaxy, Clear Street, Securitize, Fireblocks), PoR cadence,
and a note that the Ops doc's "Copper custodial / 3-of-5" design is
stale vs the live on-chain M0 design. Score unchanged (~3.0, Medium).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a Tools subsection on pulling content from JS-rendered/doc-host
links (Notion public API loadCachedPageChunkV2 with the double-nested
block gotcha; Google Docs export?format=txt for link-shared docs),
since WebFetch only sees their static shell and Playwright lacks
Chromium system libs in the sandbox.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add the GitBook tricks to the doc-fetching subsection: append .md for
clean markdown, and the built-in ?ask= Q&A endpoint for filling gaps.
Also firm up the USDat report's bug-bounty finding (confirmed none via
the docs Q&A endpoint).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Read the four published audit PDFs (Three Sigma Audit #1; Certora
Audit #2/#3/Formal Verification). Key finding: all four cover a
pre-pivot self-issued USDat ERC20 (PROCESSOR mint + blacklist), not
the M0 JMIExtension actually deployed on 2026-03-10 — so the live
token leans on M0's separate m-extensions audits plus an un-audited
Saturn whitelist wrapper. Adds audit table, findings context, the
coverage-gap as a key/critical risk, and records the GitHub repos.
Audits subscore 2.5->3.0; final score 3.0/5.0 (Medium) unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comment thread reports/report/saturn-usdat.md Outdated
Comment thread reports/report/saturn-usdat.md Outdated
Comment thread reports/report/saturn-usdat.md Outdated
Comment thread reports/report/saturn-usdat.md Outdated
Comment thread reports/report/saturn-usdat.md Outdated
Comment thread reports/report/saturn-usdat.md Outdated
Comment thread reports/report/saturn-usdat.md Outdated
@ctmotox2

ctmotox2 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

I flagged some minor changes, the rest LGTM. I agree with the risk rating being Medium, however we need to be cautious due to sUSDat's STRC exposure and offchain custody/execution, NAV/oracle reliance, and withdrawal queue liquidity risk. The yield is not "stablecoin yield", it is packaged STRC credit/dividend exposure.

- Certora Audit #3 (Saturn Dollar M0 Extensions, Apr 30 2026) recorded;
  remove "deployed token not covered by audits" caveat
- Record live Accountable-backed Chainlink "Saturn sUSDat NAV" feed
  (0x73B8…D318), verified on-chain June 7 2026
- ASSET_CAP_MANAGER_ROLE moved to Saturn Timelock (5-day delay)
- Adjust Audits score 3.0->2.5 and final 3.02->2.97 (~3.0)

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

ctmotox2 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

LGTM

@spalen0 spalen0 marked this pull request as ready for review June 10, 2026 12:01
- Past security incidents: none known (none expected given age).
- Peg history: USDat trades near $1; the Curve USDC/USDat pool is roughly balanced (see Liquidity). No depeg events observed. **TODO: pull historical peg/price series for a fuller picture.**
- Concentration risk from large depositors / holder distribution: **TODO** (holder list requires Etherscan Pro). A large fraction of supply is staked into sUSDat (sUSDat supply ≈ 106.5M shares).
- Funding: seed round (Jan 2026) led by **YZi Labs** and **Sora Ventures** plus angels. Reported amount conflicts across sources ($800K vs $2M) — **TODO: confirm**.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Rounds:

  • Pre-Seed: 800k lead by YZI and Sora
  • Seed: $2 mil lead by Spartan

```

- The protocol delegates backing entirely to **M0** (the `$M` token). There is no other delegation for USDat itself. The collateral asset is held as `$M` on the USDat contract.
- **Monitoring delegation changes:** the `ASSET_CAP_MANAGER_ROLE` can authorize additional backing assets (the M0 "JMI / Just Mint It" model supports collateral assets beyond `$M`). Today `totalAssets()` ≈ 0 (backing is effectively all `$M`), but this is a parameter to watch — see Monitoring.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The protocol support caps on assets used to mint. The limits the maximum exposure a USDat can have to a minting asset. Currently USDC is the only allowed asset to mint with a cap of $10 mil


- **Who can mint/redeem:** only **whitelisted ("onboarded") addresses**. The whitelist is enforced on `wrap` (mint) and `unwrap` (redeem) via `_revertIfNotWhitelisted` (verified in source). `isWhitelistEnabled()` = **true** on-chain.
- **Regular transfers are NOT whitelist-gated** — verified: the whitelist hooks fire only on `_beforeWrap`/`_beforeUnwrap`, not on `transfer`/`transferFrom`. This is why the Curve pool (not whitelisted) trades freely. **Implication for Yearn: a non-onboarded holder can hold and transfer USDat but cannot mint or redeem directly — its only exit is the secondary market (Curve/Pancake) unless Yearn is whitelisted.**
- **Atomicity:** the on-chain wrap (M → USDat) and unwrap (USDat → M) are atomic. The USDC↔M leg runs through the M0 Swap Facility in the same user flow. USDat→USDC redemption for onboarded users is effectively 1:1 and prompt (Treasury-backed, no queue). The **sUSDat** layer has a withdrawal queue (STRC liquidation); USDat itself does not.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This might be to in the weeds but wanted to make sure the flows are clear.

wrap:

  • Takes m/usdc and wraps to USDat atomically
  • solvers can swap the USDC to $M asynchronously. The contract enforces that the amount of USDC leaving is equal to the amount of $M being deposited.

unwrap

  • Converts USDat to wM which can then be swapped to USDC on uniswap. Liquidity on uniswap for wM/USDC is maintained by M0.
  • wM acts as the M0 maintained stablecoin and is equivalent to USDat but maintained by M0.
  • M0 has also built a limit order protocol which acts as an OTC desk where "solvers" can fill orders partially or fully. This flow is async unlike the the uniswap pool.

| Address | Can Mint | Can Burn | Role / Mechanism | Notes |
|---------|:--------:|:--------:|------------------|-------|
| [`0xB6807116b3B1B321a390594e31ECD6e0076f6278`](https://etherscan.io/address/0xB6807116b3B1B321a390594e31ECD6e0076f6278) | ✓ | ✓ | `onlySwapFacility` (`wrap`/`unwrap`) | M0 Swap Facility ("Mint and Redeem Contract"). Sole mint/burn path; mint pulls `$M` 1:1 first. Caller must be whitelisted. |
| [`0x10D59F776db12b4B271b2609CB8b7Ddd0A82703B`](https://etherscan.io/address/0x10D59F776db12b4B271b2609CB8b7Ddd0A82703B) | — | (seize) | `FORCED_TRANSFER_MANAGER_ROLE` | Compliance (Fireblocks 2/3 MPC). Cannot mint; can `forceTransfer` tokens out of **frozen** accounts. Also holds `FREEZE_MANAGER_ROLE`, `PAUSER_ROLE`, `WHITELIST_MANAGER_ROLE`. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Would you recommend the FORCED_TRANSFER_MANAGER_ROLE is held by the Admin Timelock?

| `ASSET_CAP_MANAGER_ROLE` | `0x7d34…78f7` (Saturn Timelock) | Timelock contract (`getMinDelay()` = 5 days) | Authorize/cap additional backing assets |

- **Can governance pause, freeze, or seize user funds? Yes** — freeze + forced transfer + pause are all live and held by the Compliance MPC. These are standard regulated-stablecoin compliance controls (cf. USDG, USDC) but represent real holder risk and a notable centralization signal.
- **Documentation discrepancy (resolved in favour of on-chain):** Saturn's internal Ops/Risk doc describes an *earlier* design in which "funds that back USDat are held in a Copper custodial multisig wallet" (not in the contract) and the admin is a "3-of-5 multisig." The **live, on-chain design supersedes this**: USDat is an M0 extension, the `$M` backing sits **in the token contract** (verified: `M.balanceOf(USDat)` = $126M), and the main admin/compliance roles are held by **Fireblocks 2/3 MPC** addresses (per the key-addresses page), while **admin and asset-cap management now sit behind timelocks** (5-day delay each). The "off-chain custody / 3-of-5" framing is stale. The exact MPC signer threshold cannot be verified on-chain (MPC is off-chain) — taken from docs.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I can work to update our docs. This is a good find.

### Programmability

- Core mint/redeem and accounting are **programmatic and on-chain**: USDat is 1:1 non-rebasing; the index/exchange rate is read from M0 (`currentIndex()`); backing is verifiable on-chain.
- Off-chain dependencies: user **onboarding/KYC** (whitelist), the USDC↔`$M` swap routing in Saturn's app, and the sUSDat STRC management (off-chain). For USDat-as-collateral the critical accounting is on-chain.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

what do you mean by

user onboarding/KYC (whitelist), the USDC↔$M swap routing in Saturn's app

USDat is an M0 extension. The fund flow is:

```
USDC ──(Saturn app, onboarded user)──▶ M0 Swap Facility ──swap──▶ $M ──wrap──▶ USDat (1:1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

For correctness there is a solver in this flow. USDat minting flow of funds:

  1. USDC deposited through swapFacility and USDat minted 1:1
  2. solver asynchronously swaps USDC to $M 1:1
  3. USDat now backed by $M.

Minting fails if the contract has more than $10 mil in USDC exposure.

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.

Risk Assessment: USDat

3 participants