Skip to content

Multichain trading vault: Sui hub / Robinhood spoke (full plan implementation) - #527

Open
ewitulsk wants to merge 23 commits into
stagingfrom
claude/multichain-trading-vault-wer9q0
Open

Multichain trading vault: Sui hub / Robinhood spoke (full plan implementation)#527
ewitulsk wants to merge 23 commits into
stagingfrom
claude/multichain-trading-vault-wer9q0

Conversation

@ewitulsk

@ewitulsk ewitulsk commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Implements docs/multichain-vault-plan.md end to end: Sui hub / Robinhood spoke multichain trading vault with hub-master accounting, a dumb spoke vault, transport-agnostic messaging (LayerZero primary, CCIP secondary, dev relayer for CI), and no hub↔spoke fund movement (rebalance integration is a mainnet launch gate).

What's in here

Wire format (phase 1)rust-backend/crates/vault-messages: canonical big-endian fixed-width layout for the 7 hub↔spoke messages + committed golden fixtures. The Move and Solidity codecs are pinned to those fixtures byte-for-byte (tests in all three languages).

Hub Move (phase 2)contracts/trading-vault-v2 gains wire, spoke, endpoint (+ shared EndpointRegistry), endpoint_relayer (dev/CI transport), multichain (spoke binding, ordered-lane handlers, ConfigSync), asset_markers. Key semantics:

  • Event-sourced hub books: deposit notices mint ledger shares into the same TrancheBook as wallet positions (tranches supported; entry/exit haircuts, lockups, exit-crystallized fees identical to hub deposits); StateSync is a freshness proof + reconciliation cross-check, never a value source.
  • Withdrawals burn in full at ACK; payables are netted out of NAV (§5.1); protocol fee cut mints into an admin-claimable escrow position (cash lives on the spoke).
  • Relayer-side faults abort (lane frozen, retryable); spoke-side conditions reject with explicit codes (lane advances) — a bad request can never wedge the lane.
  • Appraisals require a fresh spoke leg per bound spoke (record_spoke_state); a dark spoke blocks NAV vault-wide (fail-safe). Close is blocked until spokes are drained + unbound.
  • Layout-incompatible (TradingVault.spokes, Appraisal spoke fields): ships via fresh publish, per API_DELTA.md.
  • 141/141 Move tests (115 pre-existing + 26 new), and the pinned deploy-compiler build test passes (now also building both endpoint packages).

EVM spoke (phase 3) — new evm-contracts/ Foundry workspace: SpokeVault (pending/active/reserved fund states, per-chain depositor whitelist, deposit/reclaim, per-tranche withdraw + FIFO payout queue, native fee pot, endpoint-gated handleMessage with strict seq, ConfigSync-driven curator/endpoint/integration-set — none of those are local roles, stale-heartbeat freeze, OZ AccessControlDefaultAdminRules two-step admin), Relayer/LayerZero/CCIP endpoints, TUSDG faucet mock, vendored OZ v5.4.0 (contracts only). 63/63 forge tests incl. golden fixtures, an endpoint-switch drill, and the deploy-script tests below.

Transports (phases 5–6)contracts/endpoint-layerzero (real OApp integration: lz_receive/lz_send/confirm, peer + eid mapping) and contracts/endpoint-ccip (receiver-registry + Any2SuiMessage consume, OnRamp ccip_send), both building against pinned upstream LayerZero-v2 / chainlink-sui packages, both in the Move CI matrix.

Off-chain (phase 4)rust-backend/services/vault-messenger: ordered per-lane delivery queues in Postgres, hub PTB construction (appraisal legs + attestations + handler + send), EVM delivery, syncState/ConfigSync cranks, alerting per tx-alerting convention (tx-failed-vault-messenger, queue-stalled, payout-queue-aged, fee-pot-low), small HTTP API, deployment registration mirroring cctp-relay. 24/24 unit tests.

Frontend (phase 7)/spoke screen (viem-only injected wallet): deposit/reclaim with pending→active status, per-tranche withdraw + payout-queue view, status strip (pause/risk-off/heartbeat/fee pot), testnet TUSDG faucet. Typecheck + production build pass.

One place to write addresses (follow-up in this same PR)

The redeploy pipeline is now the single writer of every multichain address, and token-info the single server:

  • Schemacrates/deployments gains endpointLayerzero / endpointCcip / multichain { endpointRegistryId, hubChainId, spokes. } blocks on PackageInfo (all optional, absent-not-null; serde round-trip tests). token-info serves PackageInfo verbatim, so /package-info picks these up with zero service changes; token-info-client gains typed accessors.
  • Writerdeployment-manager gains --deploy-endpoints (publishes both Sui transport packages, extracts LzTransport/OApp/CcipTransport shared objects, refreshes multichain.endpointRegistryId from the trading-vault publish, cctp-style carry-forward), --hub-chain-id, and --record-evm-spoke (validating read-merge-write of the forge deploy artifact into multichain.spokes).
  • EVM deploys in redeploy-contract.yml — new dispatch inputs deploy_endpoints and deploy_evm_spoke; the latter runs evm-contracts/script/DeploySpoke.s.sol (foundry-toolchain, repo secrets SPOKE_RPC_URL + EVM_DEPLOYER_KEY, params in repo variable EVM_SPOKE_PARAMS) and folds the artifact into deployments.json before the existing commit step — EVM addresses ride the same commit, bundle, and token-info restart as Sui ids. Proven end-to-end against anvil (script deploy → cast checks → --record-evm-spoke merge in the exact reader shape).
  • Consumers — vault-messenger resolves every package/object id and spoke address from token-info at boot (TOML fields are now break-glass overrides; vault_id, RPCs, intervals stay config); the frontend populates the spoke config from /package-info's multichain block (static map reduced to rpc/explorer metadata). deployment-manager 12/12, deployments 7/7, vault-messenger 24/24, forge 63/63.

Not done in this PR (honest gaps)

  • No live-lane verification: LayerZero/CCIP transport packages compile against pinned upstream and pass config-surface tests, but their receive/send paths need a real testnet endpoint to exercise; same for publish-effects extraction shapes in --deploy-endpoints and the messenger's PTB shapes (each fails loudly if wrong).
  • Registry seeding + transport wiring + bind + fee-pot funding remain ceremony steps (runbook §2/4/5); the pipeline covers publishes, EVM deploys, and address recording.
  • Per the locked plan: no bridging, no Hyperliquid, no rebalance integration (mainnet launch gate), spoke shares non-transferable.
  • Branch note (resolved): a session credential outage forced the last few commits through the GitHub API, which initially left rust-backend/Cargo.lock one line short (the file exceeded the API payload limit). Fixed on-branch: a temporary push-triggered workflow regenerated the lock on a runner (commit f0856e2, byte-identical to the local tree) and was then removed (fbf5fa4/f9fe1e8 add it, a9580fe deletes it). cargo build --locked works on the branch.

Verification

  • sui move test (testnet-v1.78.1): trading-vault-v2 141/141; endpoint-layerzero 2/2; endpoint-ccip 2/2.
  • cargo test -p deployment-manager --test deploy_build (pinned deploy compiler): pass, incl. both endpoint packages.
  • forge test: 63/63; anvil deploy-script proof run green.
  • cargo check workspace; cargo test for vault-messages, vault-messenger (24/24), deployments (7/7), deployment-manager (12/12), token-info-client (6/6).
  • Frontend typecheck + vite build: pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776

claude added 10 commits August 28, 2026 22:15
Hub-master accounting, dumb spoke vaults, transport-agnostic messaging
with a first-party attestor endpoint (required for Robinhood Chain),
CCTP as a curator-executed vault integration, and vault-custodied
HyperCore trading via CoreWriter. Captures the 2026-08-28 design Q&A,
external-dependency checks, build order, and open questions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Per-spoke depositor whitelists, USDG on the Robinhood spoke, tranche
support with hub-only accounting, no bridge amount limits, flip-a-switch
network config, and a bridge-adapter abstraction (CCTP + Across) with the
Robinhood lane selection left open pending bridge validation. Adds the
attestor-key rationale and the raw-facts-only spoke messaging principle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Cut the Hyperliquid spoke (HyperCore/CoreWriter, Wormhole lane) and all
vault-level bridge integrations (CCTP, Across). Keep cross-chain
deposits/withdrawals and a curator-controlled spoke integration
interface with zero registered integrations. Add the spoke solvency cap
for withdrawals, since no hub-to-spoke funding path exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Withdrawals burn in full at hub ACK with a spoke-side FIFO payout queue
(rebalance integration becomes a mainnet launch gate); drop the separate
attestor keypair for a relayer-sender-gated endpoint; dual-oracle USDG
feed (Switchboard on-demand guaranteed, Pyth when cataloged) switchable
via the OracleRegistry pin; faucet-mintable TUSDG testnet mock; role-based
governance with transferable admin on both chains.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Correct the wrong claim that no third-party messaging serves Robinhood
Chain: both LayerZero V2 and Chainlink CCIP cover the Sui<->Robinhood
lane end-to-end, so the protocol ships both as endpoint modules (one
active per spoke, admin-switchable) and the relayer-gated endpoint
becomes dev/test-only. Make hub-tracked payables and NAV-net-of-queue
accounting explicit; governance proposal confirmed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Lock the last two decisions (vault-held message-fee pot; LayerZero
primary / CCIP secondary transport) and consolidate all review rounds
into one final design doc: locked decision list, fee-pot mechanics,
transport roles in the build order, and a closing section of
implementation-time checks and accepted limitations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Phase 1-3 of docs/multichain-vault-plan.md:
- rust-backend/crates/vault-messages: canonical wire format + golden
  fixtures (source of truth for the Move and Solidity codecs).
- trading-vault-v2: wire codec, spoke state, endpoint layer with dev
  relayer transport, multichain message handlers (deposit notice /
  withdraw request / payout receipt / state sync / config sync),
  appraisal spoke legs with payables netted from NAV, protocol-fee
  escrow, close/unbind drain guards. 141/141 Move tests green
  (115 existing + 26 new); layout-incompatible change, fresh publish.
- evm-contracts: Foundry workspace with SpokeVault (fund states,
  whitelist, payout queue, fee pot, roles), Relayer/LayerZero/CCIP
  endpoints, TUSDG mock, vendored OZ v5.4.0 (contracts only). 61/61
  forge tests green incl. byte-exact golden fixtures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
- contracts/endpoint-layerzero: real OApp integration (lz_receive/
  lz_send/confirm, peer + eid-mapping admin) against LayerZero-v2 Sui
  packages, pinned rev; builds + config-surface tests green.
- contracts/endpoint-ccip: CCIP receiver-registry integration
  (ccip_receive via Any2SuiMessage consume, onramp ccip_send) against
  chainlink-sui packages, pinned rev; builds + tests green.
- frontend: /spoke screen (viem-only EVM wallet, deposit/reclaim/
  withdraw/payout-queue/faucet panels, per-network spoke config);
  typecheck + production build pass.
- evm-contracts: stop tracking forge build artifacts.
- API_DELTA.md + plan doc status updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Phase 4 of docs/multichain-vault-plan.md: ordered per-lane message
queues in Postgres, hub PTB construction (appraisal + spoke legs +
attestations + handlers + send), EVM delivery and syncState/ConfigSync
cranks, tx-alerting-convention alerts (tx-failed-vault-messenger,
queue-stalled, payout-queue-aged, fee-pot-low), health/messages/lanes
API, and full deployment registration mirroring cctp-relay (ecr.tf
left for a targeted apply, per its README). Workspace cargo check
clean; 20/20 unit tests over mocked chain traits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
exchange-dashboard Ready Ready Preview Aug 30, 2026 11:16am
sui-options Ready Ready Preview Aug 30, 2026 11:16am

Request Review

The session's git push credentials are down; this dispatch-only workflow
regenerates rust-backend/Cargo.lock (missing vault-messenger's
token-info-client entry) on a runner and commits it back. Removed once
the lock commit lands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
Two red checks on the PR head:

- affected-filter: deployment/resolve_network.py fails closed on
  vault-messenger declaring hub.network in a table. Move `network` to
  the top level (config struct + all three profiles), and flip the prod
  profile to the testnet set it would actually run today (prod's env is
  testnet until the mainnet migration, same as cctp-relay documents;
  a mainnet declaration there breaks the guard's unanimity for every
  service). The mainnet flip stays documented in the file header.

- move (endpoint-layerzero) / move (endpoint-ccip): the pinned upstream
  LayerZero-v2 / chainlink-sui deps need a newer VM at test runtime than
  the matrix default (verifier error 2017 / MISSING_DEPENDENCY under
  mainnet-1.75.2 — the SO-335 false-positive class). Per-package
  sui_version override to testnet-1.78.1, which these packages' tests
  pass on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EHimiGNoVjEP65m8dch776
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