Skip to content

feat(sdk): route balance reads through transport-agnostic Core API (gRPC migration Stage 1)#98

Open
ngna3007 wants to merge 1 commit into
mainfrom
feat/grpc-stage1-balance-reads
Open

feat(sdk): route balance reads through transport-agnostic Core API (gRPC migration Stage 1)#98
ngna3007 wants to merge 1 commit into
mainfrom
feat/grpc-stage1-balance-reads

Conversation

@ngna3007

@ngna3007 ngna3007 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

What

gRPC migration Stage 1 (SPEC_FULL_GRPC_MIGRATION). Mysten deactivates the public Sui JSON-RPC fullnode on 2026-07-31. Both SuiJsonRpcClient and SuiGrpcClient expose the same .core CoreClient, so this routes the balance read path onto client.core.*. It soaks on JSON-RPC today and flips to gRPC via a single env flag — a near-no-op.

Scope is deliberately one read path. Writes, execution, and history are untouched (Stages 2 and 4).

Changes

  • balance.tsfetchSuiPrice + queryBalance use client.core.getBalance / getObject (shape drift: .totalBalance.balance.balance; .data.content.fields.object.json). Param widened to the transport-agnostic ClientWithCoreApi. Adds resetSuiPriceCache() (test/probe seam for the module-level price cache).
  • utils/sui.tsgetSuiReadClient() flip seam keyed on T2000_TRANSPORT (default jsonrpc, fail-safe on unrecognized values). A custom rpcUrl does not carry to the gRPC branch, so a non-default fullnode needs T2000_GRPC_URL (documented inline). Adds getSuiGraphQLClient() stub for Stage 2 history.ts + DEFAULT_GRAPHQL_URL.
  • t2000.ts — new readClient field routes the two queryBalance call sites; writes/execution stay on this.client (JSON-RPC) until Stage 4.
  • index.ts — export queryBalance (canonical SDK balance reader), resetSuiPriceCache, getSuiGraphQLClient.
  • Testsbalance.test.ts (Core response shapes) + getSuiReadClient transport-selector tests in sui.test.ts.
  • grpc-parity-probe.mjs — Stage 1 gate: parity across both transports, multiple wallet shapes, and both call orders. Resets the price cache before each read so each transport exercises its own core.getObject; compares the full BalanceResponse (usdEquiv within a small tolerance, not stripped); checks the Cetus pool json shape head-on; non-zero exit on failure for CI/canary gating.

How to flip

Off by default. Set T2000_TRANSPORT=grpc to route balance reads over gRPC; unset to roll back instantly (no redeploy).

Verification

  • pnpm --filter @t2000/sdk typecheck — clean
  • pnpm --filter @t2000/engine typecheck — clean
  • SDK tests — 639 passed
  • SDK build (CJS/ESM/DTS) — success
  • SDK lint — 0 errors
  • Live mainnet parity probe — PASS across 3 wallet shapes × both call orders; pool json shape byte-identical across transports

Reviewed

Two independent Codex review rounds: a price-cache gate blind-spot (gRPC getObject skipped via shared cache) and a probe exit-code gap were both found and fixed; no outstanding code issues.

Not in this PR (ops)

Production soak / canary — deploy + flip T2000_TRANSPORT=grpc on a canary surface, watch parity + latency, then widen.

Note: live benchmark showed gRPC not faster than JSON-RPC on a getBalance fan-out (periodic ~220ms spikes vs steady ~45ms). Doesn't block Stage 1 (reads default to JSON-RPC); re-validate at Stage 3.

gRPC migration Stage 1 (SPEC_FULL_GRPC_MIGRATION). Mysten deactivates the
public Sui JSON-RPC fullnode on 2026-07-31. Both SuiJsonRpcClient and
SuiGrpcClient expose the same `.core` CoreClient, so balance reads move onto
`client.core.*` and soak on JSON-RPC before flipping T2000_TRANSPORT=grpc.

- balance.ts: fetchSuiPrice + queryBalance use client.core.getBalance /
  getObject; param widened to the transport-agnostic ClientWithCoreApi.
  Adds resetSuiPriceCache() (test/probe seam for the module-level price cache).
- utils/sui.ts: getSuiReadClient() flip seam keyed on T2000_TRANSPORT
  (default jsonrpc, fail-safe); a custom rpcUrl does not carry to the gRPC
  branch, so a non-default fullnode needs T2000_GRPC_URL (documented inline).
  getSuiGraphQLClient() stub for Stage 2 history.ts; DEFAULT_GRAPHQL_URL.
- t2000.ts: readClient field routes the two queryBalance call sites;
  writes and execution stay on the JSON-RPC client until Stage 4.
- index.ts: export queryBalance (canonical SDK balance reader),
  resetSuiPriceCache, and getSuiGraphQLClient.
- tests: balance.test.ts (Core response shapes) + getSuiReadClient
  transport-selector tests in sui.test.ts.
- grpc-parity-probe.mjs: Stage 1 gate proves parity across both transports,
  multiple wallet shapes, AND both call orders. Resets the price cache before
  each read so each transport exercises its own core.getObject, compares the
  full BalanceResponse (usdEquiv within a small tolerance, not stripped), and
  checks the Cetus pool json shape head-on. Sets a non-zero exit code on
  failure so CI / a canary guard can gate on it. Verified byte-identical on
  mainnet.

Writes and history reads are intentionally untouched (Stages 2 and 4).
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
t2000 Ready Ready Preview, Comment Jun 9, 2026 1:00pm
t2000-gateway Ready Ready Preview, Comment Jun 9, 2026 1:00pm

Request Review

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.

1 participant