Skip to content

Show swap token logos from the token contracts on NEAR - #695

Merged
n13 merged 2 commits into
mainfrom
n13/swap-token-icons
Oct 9, 2026
Merged

n13 merged 2 commits into
mainfrom
n13/swap-token-icons

Conversation

@n13

@n13 n13 commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Why

1Click's /v0/tokens has no image field (verified against the live API and its OpenAPI spec), and the Defuse console API is the same list. The logos live on NEAR itself: every listed token is, or has a sibling that is, a NEAR token contract whose metadata carries an icon.

What was tested with curl / RPC before writing code

  • ft_metadata on NEP-141 contracts (nep141:…): returns icon as a data URI: SVG (USDC, ETH, FRAX, AURORA, mpDAO, ZEC…), PNG (JAMBO, CFI), JPEG (BLACKDRAGON), WebP (SHITZU). Some have none (wrap.near, qtc.omft.near, WBTC, HAPI…).
  • mt_metadata_base_by_token_id on the NEP-245 HOT bridge contract (nep245:v2_1.omni.hot.tg:<token_id>, 36 listings, one batched call): returns icon as an https URL (storage.herewallet.app, coin-images.coingecko.com, bscscan). Every URL downloads with HTTP 200 and an image content type. Three carry the literal string todo.
  • 1cs_v1:… listings (25) are chain-native ids with no NEAR contract.
  • Every image the app's top-20 list would show was downloaded for inspection to ~/play/quantus-network/.agent-tmp/claude/token-icons/.

What changed

  • SDK NearRpcService: read-only view calls over JSON-RPC against https://free.rpc.fastnear.com (AppConstants.nearRpcEndpoint).
  • SDK SwapTokenIconService: iconFor(token) reads the metadata icon (NEP-141 or NEP-245 by asset id), decodes a data URI or downloads an https URL, and returns SwapTokenIcon(bytes, mimeType). Each listing is fetched once; a failed fetch is forgotten so it is retried next time; a junk icon value is logged and treated as none.
  • SwapToken.iconAssetId: the listing whose metadata carries the symbol's logo. The token's own asset when it has a NEAR contract, else the first listing of the same symbol that has one (so ZEC on Solana would still get Zcash's logo), else null.
  • Native chain first: a listing whose chain is the coin's own (symbol equals chain, or W + chain) now beats the main-network order. Before, ZEC resolved to the Solana SPL wrapper and wNEAR to a Solana wrapper, neither with a NEAR contract, and arguably not the asset a user means.
  • TokenIcon watches swapTokenIconProvider(token) and renders SvgPicture.memory or Image.memory with the monogram as fallback (loading, none, decode error). The network badge stays a monogram.

Tests

  • SDK: NEP-141 data URI → bytes; NEP-245 token id in the call and the URL downloaded with its content type; sibling listing supplies the logo; no logo without a NEAR listing, with a null icon, or with a junk icon; one fetch per listing and a failed fetch retried. Swap service: own chain wins (ZEC, wNEAR), iconAssetId for own, sibling and none.
  • App: monogram without a logo, SVG logo, raster logo. Swap flow tests pass.

1Click's listing carries no images, so a token's logo now comes from
its contract on NEAR: ft_metadata for a NEP-141 token and
mt_metadata_base_by_token_id for a NEP-245 one, read over JSON-RPC. The
icon is a data URI (SVG or raster bytes) or an https URL, which is
downloaded; the picker renders either and keeps the monogram where
there is none. A listing without a NEAR contract borrows the logo of
another listing of its symbol. A coin's own chain now wins over the
main-network order, so ZEC and wNEAR are the native assets rather than
their Solana wrappers.
@n13 n13 added the bot-review Request automated review from review-bot label Oct 9, 2026
@n13
n13 changed the base branch from n13/qtc-price-under-balance to main October 9, 2026 04:05

@n13 n13 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewer model: GPT-6.1 Sol

Verdict (advisory): Approve

Reviewed d06992576d13911b377b0b02cafe0b8555447d9b against base e7de1a708bcf6a72d6e530170294720b7ca89602. No blocking findings.

The metadata lookup, data URI/HTTPS image handling, shared per-listing cache, failure eviction, and monogram fallback are consistent with the intended behavior. I also traced the native-chain preference through listing selection, quote asset IDs, deposit deadlines, and network-specific address handling.

Validation:

  • SDK: flutter test --no-pub --reporter=expanded test/services/swap_service_test.dart test/services/swap_token_icon_service_test.dart — 46 passed.
  • Mobile: flutter test --no-pub --reporter=expanded test/unit/token_icon_test.dart test/screens/swap_flow_test.dart test/unit/swap_math_test.dart — 30 passed. Both test commands ran through repository-pinned Melos from the worktree root.
  • Formatting check on all 11 changed Dart files and git diff --check — passed without changing source.
  • Read-only live checks: fetched the current 1Click listing and top-20 NEP-141 metadata, covering SVG, PNG, JPEG, WebP, and absent icons; verified a NEP-245 metadata call and its HTTPS image response. The JSON-RPC request/byte decoding agrees with NEAR's contract-call documentation.

Validation limits: melos run analyze was stopped at the mandatory 10-second cap after the unaffected cold-wallet and miner packages passed; SDK/mobile analysis did not complete. The affected files compiled in the passing focused tests. GitHub currently reports three successful dependency-cooldown checks; those are separate from local test validation.

@n13 n13 removed the bot-review Request automated review from review-bot label Oct 9, 2026
@n13 n13 closed this Oct 9, 2026
@n13 n13 reopened this Oct 9, 2026
@n13
n13 merged commit bf32ff5 into main Oct 9, 2026
2 checks 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.

1 participant