Skip to content

🐛 fix(sdk): recognize long-form SUI in history classifier (correct principal asset)#101

Open
ngna3007 wants to merge 1 commit into
mainfrom
fix/history-longform-sui-classify
Open

🐛 fix(sdk): recognize long-form SUI in history classifier (correct principal asset)#101
ngna3007 wants to merge 1 commit into
mainfrom
fix/history-longform-sui-classify

Conversation

@ngna3007

Copy link
Copy Markdown
Collaborator

Problem

Transaction history mis-identifies the principal asset/amount of a tx.

History reads now use the Sui GraphQL transport, which returns SUI's coin type fully expanded (0x000…0002::sui::SUI). But the shared classifier (wallet/classify.ts) excludes SUI gas by strict-comparing the short 0x2::sui::SUI:

  • extractTransferDetails (line ~248) — userNonSui = filter(coinType !== SUI_TYPE) → runs on every history tx
  • refineLendingLabel (lines ~168/173) — same compare

So the long-form SUI delta passes through as a "non-SUI" change. extractTransferDetails then picks the largest-by-raw-units change as the principal — and since SUI is 9 decimals vs stablecoins' 6, a normal gas spend (~0.001–0.005 SUI) often out-magnitudes a small stablecoin transfer.

Impact (display-only, no funds at risk): a "$1 USDC send" renders in history as "−0.003 SUI" — wrong asset, amount, and direction. Hits a common everyday case (small stablecoin sends/swaps). Surfaces in t2 history and any SDK consumer reading history. JSON-RPC emitted short-form SUI, so this only appeared after the GraphQL migration.

Fix

Compare canonical struct tags instead of raw strings — new isSuiCoinType(coinType) using normalizeStructTag, replacing the three coinType !== SUI_TYPE checks. Form-insensitive (detects SUI in either address form) without false-matching an impostor 0xother::sui::SUI (the package address is part of the normalized tag). Aligns with token-data-architecture.mdc (never raw-string-compare coin types).

Verification

  • Regression test (classify.test.ts): a larger-raw long-form SUI gas delta + a smaller USDC inflow must resolve to USDC / 1 / in. Fails without the fix (SUI / 5 / out), passes with it — confirmed by running before/after.
  • Full SDK suite: 424 tests pass, typecheck clean, lint 0 errors.

Notes

Found while reconciling an (abandoned, now-superseded) gRPC read-migration branch against current main — this is the one real bug that survived; it's live on v5.6.0.

…incipal asset)

History reads on the Sui GraphQL transport return SUI's coin type fully
expanded (0x000…0002::sui::SUI). The shared classifier excluded SUI gas by
strict-comparing the SHORT 0x2::sui::SUI, so the long-form SUI delta passed
through as a "non-SUI" change and extractTransferDetails could pick it as the
transaction's principal — rendering e.g. a $1 USDC send as "-0.003 SUI" (wrong
asset, amount, and direction). Triggers on common small stablecoin transfers /
swaps where the SUI gas raw (9 decimals) out-magnitudes the stable raw (6
decimals). Display-only — no funds at risk.

Fix: compare canonical struct tags (normalizeStructTag) via a new isSuiCoinType
helper, replacing the three raw `coinType !== SUI_TYPE` checks
(extractTransferDetails + both refineLendingLabel finds). Form-insensitive,
without false-matching an impostor 0xother::sui::SUI. Per
token-data-architecture.mdc — never raw-string-compare coin types.

Regression test: a larger-raw long-form SUI gas delta + a smaller USDC inflow
must resolve to USDC/1/in (fails without the fix → SUI/5/out).

Verified: 424 SDK tests, typecheck, lint (0 errors).
@vercel

vercel Bot commented Jun 18, 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 18, 2026 3:00am
t2000-gateway Ready Ready Preview, Comment Jun 18, 2026 3:00am

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