Skip to content

feat: deterministic getTokenBalanceAcrossChains (no new tool) - #34

Merged
Aliiiu merged 16 commits into
mainfrom
feat/token-balance-across-chains
Jun 28, 2026
Merged

Aliiiu merged 16 commits into
mainfrom
feat/token-balance-across-chains

Conversation

@Aliiiu

@Aliiiu Aliiiu commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

closes #36

Adds a host-side aggregate debank.user.getTokenBalanceAcrossChains (exposed via TOOL_METADATA, no new MCP tool — the code-mode surface stays execute + search_docs) so multichain token-balance computation is deterministic instead of LLM-authored guest arithmetic.

What it does

Given a wallet + a named token (name or symbol), it fans out across all chains (reusing getUserTokensAcrossChains), matches holdings via a shared pure matcher, and returns per-chain balances + a combined total — reading each holding's human-readable amount (no 10**decimals math). Pass chain to restrict to one chain (single-chain fetch).

Changes

  • src/lib/token-matcher.ts — pure matchesTokenReference (exact name/symbol/display_symbol/optimized_symbol, or 0x address → holding id).
  • src/services/user.service.ts — extracted _getUserTokensWithSkippedChains (records skipped chains, preserves abort plumbing; public getUserTokensAcrossChainsRaw keeps its flat-array contract) + new getTokenBalanceAcrossChainsRaw.
  • src/mcp/legacy/response-schemas.ts + tool-metadata.tsTokenBalanceAcrossChainsSchema + a TOOL_METADATA entry (timeoutMs: 45_000); search_docs index regenerated.
  • Tests (vitest) for the matcher and the method (multi-chain, partial, single-chain, resolve-miss, null amount); entry-count assertions bumped 35 → 36.
  • Changeset (minor).

Notes

  • Bridged-suffix variants (USDC.e, USDC (PoS)) are out of scope for v1 (documented in the tool description).
  • partial / chains_skipped surface when a chain's fetch is skipped, so totals aren't silently under-counted.

Spec + plan: docs/superpowers/specs/2026-06-27-deterministic-token-balance-across-chains-design.md, docs/superpowers/plans/2026-06-27-deterministic-token-balance-across-chains.md. Driven by IQAIcom/aiden#105.

Aliiiu added 13 commits June 27, 2026 16:02
Design for a host-side aggregate (getTokenBalanceAcrossChains) that moves
multichain token-balance computation off LLM-authored guest code into
deterministic package code, exposed via TOOL_METADATA (no new MCP tool).
Driven by BrainDAO/aiden#105.
- min_usd_value: 0 (complete sweep; a balance query must not under-report).
- chain-restricted path uses single-chain getUserTokenListRaw, not
  aggregate-then-filter.
- add partial / chains_skipped observability (extend the aggregate to report
  skipped chains).
- matcher: consult name/symbol/display_symbol/optimized_symbol + 0x-address ->
  id; bridged-suffix variants explicitly out of scope; concrete normalization
  examples; home at src/lib/token-matcher.ts.
- responseSchema amount is z.number().nullable(); fix garbled missing-amount
  bullet; note total precision limit.
- tests use vitest (not node:test); bump tool-metadata length 35 -> 36.
- cross-repo gating: reverts must not ship before the package is published+pinned.
- single response shape with optional `error` field (resolveChain miss returns
  it with other fields zeroed; infra failures still throw) — resolves the
  two-shape + single-chain `partial` conflation findings.
- skipped-chain observability via an internal `_getUserTokensWithSkippedChains`
  helper returning { tokens, skipped }; public aggregate keeps its flat-array
  contract (only tool-metadata + embedded-index register it).
- matcher: empty-reference guard, null-safe fields (display_symbol), canonical
  /^0x[a-f0-9]{40}$/i with malformed-address fallback.
- bump this method's timeoutMs to 45_000 (min_usd_value:0 sweeps more chains);
  verify with a whale wallet during dev-link.
- name TokenBalanceAcrossChainsSchema; add a search-docs embedded-index entry
  (resolves search_docs discoverability); drop the Open-details section.
- Decision log marked non-normative to avoid drift.
- embedded-index.ts is AUTO-GENERATED from TOOL_METADATA via build:docs (run by
  prebuild + prepare); the TOOL_METADATA entry alone suffices. Do NOT hand-edit
  the generated file.
- helper keeps options?: RequestOptions and the doc block; abort plumbing
  preserved end-to-end.
- record skipped chains via closure-and-push AFTER the abort re-throw check (a
  cancellation is not a skip); no allSettled.
- initial getUserTotalBalanceRaw lookup throws (like the single-chain per-call);
  only per-chain fan-out failures become chains_skipped.
- clarify _ prefix is convention only; method is TS-public (sandbox exposure is
  gated by TOOL_METADATA registration, not visibility).
- new method signature includes options?: RequestOptions (mandatory — the
  sandbox bridge calls rawFn(args, {signal, timeout})); thread it into both
  fetch paths so abort/timeout reach upstream calls.
- cross-repo PR 1 checklist no longer says "embedded-index entries"
  (regenerates via build:docs).
- resolve the embedded-index count hedge: no fixed-count test exists (only
  results.length > 0), so only tool-metadata.test.ts needs the 35->36 bump.
Match the call sites to the options-threading mandate so a literal copy
preserves the abort signal.
Cross-repo plan: PR 1 (debank-mcp — matcher, helper, method, schema,
TOOL_METADATA, tests, changeset) and PR 2 (aiden — wire + revert #105 bullets +
bump), gated so reverts don't ship before publish.
- TOOL_METADATA entry includes the required name field
  (debank_get_token_balance_across_chains).
- build:docs regenerates only embedded-index.ts; instructions.generated.ts comes
  from instructions.md via build:instructions and is unaffected — dropped from
  the git add list.
- Task 1 ships the correct failing test (no self-correcting buggy assertion);
  add an intentional post-normalize match case.
- Task 2 shows the full refactored helper (early-return shape change, try/catch,
  throwIfAborted) + an explicit change checklist.
- drop unnecessary (userService as any) casts (helper is TS-public).
- Task 7 quotes the exact three aiden bullets to delete.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request implements a deterministic host-side token balance aggregation method across chains (debank.user.getTokenBalanceAcrossChains) to replace brittle LLM-authored arithmetic. It introduces a pure token matcher with robust normalization rules, refactors the user service to track skipped chains during fan-out, and registers the new method in the tool metadata and schemas. Feedback on the changes suggests adding defensive checks, specifically a null/undefined check for the holding parameter in the token matcher and a fallback empty array when fetching token lists in the user service.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib/token-matcher.ts
Comment thread src/services/user.service.ts
…crossChains

Live verification showed the agent sometimes read result.total.amount (nested)
instead of the flat result.total. Make the description + exampleCall state the
result shape and demonstrate flat-field access so guest code reads it correctly.
@Aliiiu

Aliiiu commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a deterministic, host-side method debank.user.getTokenBalanceAcrossChains to calculate multichain token balances, moving arithmetic logic out of LLM-authored guest code. It adds a pure token matcher with normalization rules, refactors the user service to support skipped-chain observability, and registers the new method in the tool metadata. The review feedback suggests two robust improvements: normalizing consecutive whitespace characters to a single space in the token matcher, and normalizing token names before checking for unique representations to avoid false positives.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/lib/token-matcher.ts
Comment thread src/services/user.service.ts Outdated
Compare matched holdings' names case-insensitively (trim + lower) so case-only
differences don't surface a misleading mixed_representations flag to the agent.
The matcher itself already compares case-insensitively, so this aligns the
final aggregate with the matcher's intent.
@Aliiiu

Aliiiu commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a deterministic host-side token balance aggregation method, debank.user.getTokenBalanceAcrossChains, to replace brittle LLM-authored arithmetic in guest code. It adds a pure token matcher, refactors existing fan-out logic to track skipped chains, registers the new method in TOOL_METADATA, and updates corresponding schemas, types, and tests. Feedback on the changes identifies a potential TypeError if h.name is null or undefined when calling h.name.trim(), recommending defensive fallback handling to prevent runtime crashes and schema validation failures.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/services/user.service.ts Outdated
DeBank occasionally returns null name/symbol on custom or newly-deployed
tokens. The matcher already handles this (typed string-guard in
matchesTokenReference), but the new aggregate's downstream string ops
(.trim() in mixed_representations, the matches array return) trusted the
declared non-null types and would TypeError at runtime on a matched-via-symbol
holding with null name. Coalesce to "" at the use sites — matches the
cookbook's defensive p.name precedent (commit b0b12f1). Add a regression test.
@Aliiiu

Aliiiu commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request implements a deterministic, host-side token balance aggregation method across multiple chains (debank.user.getTokenBalanceAcrossChains) to replace brittle LLM-authored arithmetic. The changes introduce a pure token matcher (matchesTokenReference) with robust normalization rules, refactor the existing user service to support skipped-chain observability via _getUserTokensWithSkippedChains, and expose the new aggregate method via TOOL_METADATA with comprehensive test coverage. There are no review comments, so no additional feedback is provided.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@Aliiiu
Aliiiu merged commit bc79839 into main Jun 28, 2026
3 checks passed
@Aliiiu Aliiiu self-assigned this Jun 29, 2026
@Aliiiu Aliiiu added the enhancement New feature or request label Jun 29, 2026
@Aliiiu

Aliiiu commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Tracking issue: #36 (deterministic multichain token balance). Driven by IQAIcom/aiden#105 cross-repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deterministic multichain token balance (no new MCP tool)

1 participant