Skip to content

feat: st0x-fixed-spread-v6 asserts the signed vault NAV ratio (RAI-1721) - #54

Merged
hardyjosh merged 5 commits into
mainfrom
2026-08-13-st0x-fixed-spread-v6
Aug 25, 2026
Merged

hardyjosh merged 5 commits into
mainfrom
2026-08-13-st0x-fixed-spread-v6

Conversation

@hardyjosh

@hardyjosh hardyjosh commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

What

Adds src/st0x-fixed-spread-v6.rain (+ companion src/st0x-fixed-spread-v6.md) — v6 of the ST0x fixed-spread strategy, implementing the strategy half of RAI-1721 (RAI-1479: vault NAV ratio asserted at settlement). The v5 strategy is untouched and remains live until v6 deploys; versions live side by side.

How it works

  • The order's oracle-url moves to https://oracle.t0trade.com/context/v6 and oracle-schema-version to 6. Slots 0–8 are identical to v5; slot 9 carries the vault NAV ratio as the lossless Rain Float packing, at 18 decimals, of the vault's raw convertToAssets(1e18) — the exact value the pricing model priced the quote against.
  • At settlement the strategy reads the wt vault's live value via erc4626-convert-to-assets from rainlanguage/rain.erc4626.words, deployed on Base at 0xd69dC3d58a7C875117f9c7cecF4F1A7f3CA47254 and bound as erc4626-subparser in both scenarios. The share amount is the Float literal 1 (one whole share), which at the vault's 18 share decimals converts losslessly to the raw 1e18 probe; the word returns the vault's answer Float-packed at the underlying asset's 18 decimals.
  • The comparison is numeric equal-to, and that is exact: lossless packing of the same raw value at the same decimals is injective on values, so numeric equality of the two Floats is equivalent to bit-for-bit equality of the raw ratios — while staying robust to any normalization difference between the server's Rust packing and Solidity's LibDecimalFloat. A fill can therefore never straddle a NAV step (e.g. a dividend deposit).
  • The NAV assertion is unconditional — the vault read and the equality run on every fill. This strategy only ever deploys on wt pairs (the sell/buy scenario split exists precisely because the wt side is known at deploy time), and a real vault's live convertToAssets is never zero, so a zero signed ratio fails the equality and the fill reverts. An upstream that stops publishing ratios halts fills on this strategy rather than letting them settle unprotected — that is intended. v5 covers pairs without a vault side (documented in the strat and the .md).
  • The vault address is wired from the order's own IO, dia-limit style: two deployments, sell shares (wt-vault: ${order.outputs.0.token.address}) and buy shares (wt-vault: ${order.inputs.0.token.address}). No manually-entered vault address.
  • Registry index updated: all pins bumped to the content commit and the st0x-fixed-spread-v6 line added, matching the pin convention of Point st0x fixed-spread and oracle-limit at 0trade v5 oracle #52/Add SFT HyperEVM subgraph URL #53.

Dependencies

Deploy sequencing: RAI-1754 + RAI-1755 before RAI-1757. This strategy is only viable once both upstreams are live:

  • RAI-1754 — st0x-oracle-server serves /context/v6 (Mirror the 14 Base-only tokens onto the Ethereum and HyperEVM token lists #64, branch feat/context-v6-nav-ratio, signing slot 9 as the lossless 18-decimal Float packing). Until then the oracle URL serves nothing.
  • RAI-1755 — st0x.pricing publishes real vault NAV ratios. Because the NAV assertion is unconditional, a v6 context with a Float-zero slot 9 makes every fill revert — the strat halts rather than settling unprotected.

Only then does RAI-1757 (deploying this strategy) go ahead.

Validation

  • Frontmatter YAML parses; binding declarations and uses are consistent; erc4626-subparser bound to the deployed address in both scenarios.
  • Word semantics verified against rain.erc4626.words source (LibERC4626.convertToAssets: shares Float → toFixedDecimalLossless at share decimals; output fromFixedDecimalLosslessPacked at asset decimals) and its parse tests, which call erc4626-convert-to-assets(<vault> 1) for one share; on-chain code confirmed at the Base address.
  • scripts/build-public-registry-data-uri.sh and scripts/build-private-registry-artifact.sh both build green with the new registry line (the repo has no PR-triggered CI; both workflows are workflow_dispatch).

🤖 Generated with Claude Code

https://claude.ai/code/session_01TQ3QV1QrwmaS7ACSf7cds7

Closes RAI-1721

@linear-code

linear-code Bot commented Aug 13, 2026

Copy link
Copy Markdown

RAI-1721

@hardyjosh
hardyjosh force-pushed the 2026-08-13-st0x-fixed-spread-v6 branch from be54c72 to a08b253 Compare August 13, 2026 11:03
@hardyjosh
hardyjosh force-pushed the 2026-08-13-st0x-fixed-spread-v6 branch from a08b253 to 8a425c5 Compare August 20, 2026 13:29
Josh Hardy and others added 3 commits August 22, 2026 11:02
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ3QV1QrwmaS7ACSf7cds7
v6 of the ST0x fixed-spread strategy consumes /context/v6, which signs
the vault NAV ratio at slot 9 as the lossless Rain Float packing (18
decimals) of the wt vault's raw convertToAssets(1e18). At settlement
the strategy reads the vault's live value via erc4626-convert-to-assets
(rainlanguage/rain.erc4626.words, 0xd69dC3d58a7C875117f9c7cecF4F1A7f3CA47254
on Base) and requires numeric equality - exact, since lossless packing
at fixed decimals is injective on values - so a fill can never straddle
a NAV step. The assertion is unconditional: this strategy only deploys
on wt pairs, a real vault NAV ratio is never zero, so a zero signed
ratio (upstream not publishing) fails the equality and halts fills
rather than settling unprotected.

Two deployments (sell shares / buy shares) wire the vault address from
the order's own IO tokens per scenario, dia-limit style. v5 remains
live alongside.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQ3QV1QrwmaS7ACSf7cds7
@hardyjosh
hardyjosh force-pushed the 2026-08-13-st0x-fixed-spread-v6 branch from 8a425c5 to 3e4f44e Compare August 22, 2026 11:05
Adds a workflow that checks out rainlanguage/raindex (feat/st0x-v6-nav-fork-test)
and runs RaindexV6St0xNavForkTest using PRIVATE_BASE_RPC_URLS. Covers the
quote+take happy path and NAV-step revert for RAI-1721 / PR #54.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Siddharth2207

Copy link
Copy Markdown
Collaborator

NAV fork validation (RAI-1721)

Added CI so this PR’s vault NAV bind can be exercised on a Base fork without bolting Foundry into the registry itself.

What runs

  • Workflow: st0x-v6-nav-fork.yml
  • Harness: RaindexV6St0xNavForkTest on rainlanguage/raindex@feat/st0x-v6-nav-fork-test
  • Asserts:
    1. quote2 + takeOrders4 succeed when live NAV matches signed slot 9
    2. after donating underlying into the wt vault, takeOrders4 reverts with Vault NAV ratio mismatch

How to re-run

  • Actions → st0x-v6 NAV fork test → Run workflow (optional raindex_ref)
  • Or any PR that touches src/st0x-fixed-spread-v6.*

Local equivalent:

BASE_MAINNET_RPC_URL=<rpc> forge test --match-contract RaindexV6St0xNavForkTest -vvv

(from a raindex checkout on that branch)

PRIVATE_BASE_RPC_URLS may include non-eth endpoints; pick the first
candidate that answers eth_blockNumber, falling back to mainnet.base.org.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Siddharth2207

Copy link
Copy Markdown
Collaborator

✅ NAV fork CI green

Latest run: https://github.com/ST0x-Technology/st0x.registry/actions/runs/32760813301

Both RaindexV6St0xNavForkTest cases passed on Base fork (quote+take when NAV matches; take reverts after NAV step).

@Siddharth2207

Siddharth2207 commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

@hardyjosh
hardyjosh merged commit 2f2c09d into main Aug 25, 2026
1 check passed
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