Repository navigation
Show swap token logos from the token contracts on NEAR - #695
Conversation
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
left a comment
There was a problem hiding this comment.
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.
Why
1Click's
/v0/tokenshas 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 anicon.What was tested with curl / RPC before writing code
ft_metadataon NEP-141 contracts (nep141:…): returnsiconas 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_idon the NEP-245 HOT bridge contract (nep245:v2_1.omni.hot.tg:<token_id>, 36 listings, one batched call): returnsiconas 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 stringtodo.1cs_v1:…listings (25) are chain-native ids with no NEAR contract.~/play/quantus-network/.agent-tmp/claude/token-icons/.What changed
NearRpcService: read-onlyviewcalls over JSON-RPC againsthttps://free.rpc.fastnear.com(AppConstants.nearRpcEndpoint).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 returnsSwapTokenIcon(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.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.TokenIconwatchesswapTokenIconProvider(token)and rendersSvgPicture.memoryorImage.memorywith the monogram as fallback (loading, none, decode error). The network badge stays a monogram.Tests
iconAssetIdfor own, sibling and none.