-
Notifications
You must be signed in to change notification settings - Fork 3
Dynamic on-chain tokens #1014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic on-chain tokens #1014
Conversation
✅ Deploy Preview for vortex-sandbox ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for vortexfi ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| "react-hook-form": "^7.65.0", | ||
| "react-i18next": "^15.4.1", | ||
| "react-toastify": "^11.0.5", | ||
| "react-window": "^2.2.5", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…rtex into dynamic-onchain-tokens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds support for dynamically loaded on-chain (EVM) tokens (logo + USD price) sourced from SquidRouter, and updates the frontend token UX to display those tokens efficiently.
Changes:
- Introduces a shared “dynamic EVM tokens” module that fetches tokens/prices from SquidRouter with static-config fallback.
- Updates frontend token selection/display to use dynamic token logos, compute/display USD balances, and virtualize long token lists.
- Cleans up/adjusts various UI pieces and token icon handling (including removing many bundled token SVG assets).
Reviewed changes
Copilot reviewed 43 out of 68 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/tokens/utils/typeGuards.ts | Extends on-chain balance types to include USD balance. |
| packages/shared/src/tokens/utils/helpers.ts | Adds optional dynamic EVM token config lookup in token detail helpers. |
| packages/shared/src/tokens/types/evm.ts | Adds logoURI/usdPrice to EVM token details and USD balance field to balance type. |
| packages/shared/src/tokens/types/assethub.ts | Adds USD balance field to AssetHub balance type. |
| packages/shared/src/tokens/index.ts | Exports dynamic EVM token module from shared tokens index. |
| packages/shared/src/tokens/evm/dynamicEvmTokens.ts | New dynamic token fetch/store layer (SquidRouter) with fallback to static config. |
| packages/shared/src/tokens/constants/misc.ts | Minor constant string literal change. |
| packages/shared/src/services/squidrouter/onramp.ts | Adjusts import path. |
| packages/shared/src/services/pendulum/apiManager.ts | Minor refactor (let → const). |
| packages/shared/.prettierignore | Attempts to ignore shared tokens index for Prettier runs. |
| package.json | Adds react-window dependency. |
| apps/rebalancer/src/rebalance/brla-to-axlusdc/index.ts | Minor TS default param style tweak. |
| apps/frontend/src/stories/RampFollowUpRedirectStep.stories.tsx | Minor TS default param style tweak. |
| apps/frontend/src/stories/AveniaLivenessStep.stories.tsx | Minor TS default param style tweak. |
| apps/frontend/src/services/tokens/index.ts | New frontend re-export wrapper for shared dynamic token APIs. |
| apps/frontend/src/sections/individuals/PopularTokens/index.tsx | Restricts “popular tokens” display to fiat tokens and updates icon validation. |
| apps/frontend/src/main.tsx | Initializes dynamic EVM tokens at app startup. |
| apps/frontend/src/hooks/useTokensSortedByBalance.ts | Simplifies sorting logic to reorder definitions based on computed balance order. |
| apps/frontend/src/hooks/useOnchainTokenBalancesSorted.ts | Switches sorting metric to USD balance. |
| apps/frontend/src/hooks/useOnchainTokenBalances.ts | Reworks EVM balance fetching to use Alchemy and adds USD balance computation. |
| apps/frontend/src/hooks/useGetNetworkIcon.tsx | Adds default icon fallback and constrains typed icon map. |
| apps/frontend/src/hooks/useGetAssetIcon.tsx | Refactors to fiat-only icon lookup with placeholder fallback. |
| apps/frontend/src/hooks/ramp/useRampValidation.ts | Uses dynamic EVM token config when resolving token details. |
| apps/frontend/src/components/widget-steps/SummaryStep/TransactionTokensDisplay.tsx | Uses logoURI for on-chain token icons with fiat fallback. |
| apps/frontend/src/components/buttons/AssetButton/index.tsx | Supports logoURI override for token icon rendering. |
| apps/frontend/src/components/UserBalance/index.tsx | Improves balance formatting and hides zero balances. |
| apps/frontend/src/components/TokenSelection/TokenSelectionList/hooks/useTokenSelection.tsx | Adds optional logoURI to token definitions. |
| apps/frontend/src/components/TokenSelection/TokenSelectionList/helpers.tsx | Switches token definitions to use dynamic config and external icon URLs. |
| apps/frontend/src/components/TokenSelection/TokenSelectionList/components/SelectionTokenList.tsx | Virtualizes token list rendering with @tanstack/react-virtual. |
| apps/frontend/src/components/TokenIconWithNetwork/index.tsx | New component to render token icon with optional network overlay. |
| apps/frontend/src/components/Ramp/Onramp/index.tsx | Passes logoURI to asset input for dynamic token icons. |
| apps/frontend/src/components/Ramp/Offramp/index.tsx | Passes logoURI to asset input for dynamic token icons. |
| apps/frontend/src/components/ListItem/index.tsx | Uses new icon component + memoization; shows balances only for on-chain items. |
| apps/frontend/src/components/ComparisonSlider/index.tsx | Classname formatting changes only. |
| apps/frontend/src/components/Avenia/AveniaField/index.tsx | Classname formatting changes only. |
| apps/frontend/src/components/AssetNumericInput/index.tsx | Threads logoURI through to button. |
| apps/frontend/src/components/Accordion/index.tsx | Classname formatting changes only. |
| apps/frontend/src/assets/coins/placeholder.svg | Adds placeholder token icon asset. |
| apps/frontend/src/assets/coins/eurc.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT_POLYGON.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT_ETHEREUM.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT_BSC.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT_BASE.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT_AVALANCHE.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT_ASSETHUB.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT_ARBITRUM.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDT.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDC_POLYGON.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDC_ETHEREUM.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDC_BSC.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDC_BASE.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDC_AVALANCHE.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDC_ASSETHUB.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/USDC_ARBITRUM.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/ETH_ETHEREUM.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/ETH_BSC.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/ETH_BASE.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/ETH_ARBITRUM.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/ETH.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/src/assets/coins/DOT_ASSETHUB.svg | Removes bundled token icon (now using new icon approach). |
| apps/frontend/package.json | Adds @tanstack/react-virtual and react-window. |
| apps/api/src/index.ts | Initializes dynamic EVM tokens at API startup. |
| apps/api/src/api/services/quote/core/squidrouter.ts | Adds a debug console.log around token detail lookup. |
| apps/api/src/api/services/priceFeed.service.ts | Uses dynamic token USD prices (when available) before falling back to CoinGecko. |
| apps/api/src/api/services/phases/handlers/distribute-fees-handler.ts | Minor string literal tweak. |
| apps/api/src/api/controllers/metrics.controller.ts | Minor string literal tweak. |
Comments suppressed due to low confidence (1)
apps/frontend/src/hooks/useOnchainTokenBalances.ts:121
nativeTokenis memoized with dependency[tokensForNetwork.find], which is a stable function reference and won't update whentokensForNetworkchanges (e.g., network switch). This can leavenativeTokenstale. Depend ontokensForNetwork(or compute inline) instead.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const ASSETHUB_ICON_URLS: Record<AssetHubToken, string> = { | ||
| [AssetHubToken.USDC]: "https://raw.githubusercontent.com/0xsquid/assets/main/images/tokens/usdc.svg", | ||
| [AssetHubToken.USDT]: "https://raw.githubusercontent.com/0xsquid/assets/main/images/tokens/usdt.svg", | ||
| [AssetHubToken.DOT]: "https://raw.githubusercontent.com/0xsquid/assets/main/images/tokens/dot.svg" | ||
| }; | ||
|
|
||
| import { useMemo, useRef } from "react"; | ||
| import { getEvmTokenConfig } from "../../../services/tokens"; |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { useMemo, useRef } from "react"; appears after executable code (const ASSETHUB_ICON_URLS ...). In ESM/TypeScript, all import statements must be at the top level before other statements, so this file will fail to compile. Move the ASSETHUB_ICON_URLS constant below the imports (or convert it to a separate module).
| // Global cache to persist balances across hook instances and app lifecycle | ||
| const globalBalanceCache = new Map<string, Map<string, string>>(); | ||
|
|
||
| import { useBalance, useReadContracts } from "wagmi"; | ||
| import { useNetwork } from "../contexts/network"; | ||
| import { useAssetHubNode } from "../contexts/polkadotNode"; | ||
| import erc20ABI from "../contracts/ERC20"; | ||
| import { multiplyByPowerOfTen } from "../helpers/contracts"; | ||
| import { getEvmTokensForNetwork } from "../services/tokens"; | ||
| import { useVortexAccount } from "./useVortexAccount"; |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file declares globalBalanceCache before importing from wagmi, and also has an import statement after code. ESM requires imports to come before other statements, so this will be a syntax/compile error. Move the globalBalanceCache declaration below all imports (or move it to a separate module).
| import { useEffect, useMemo, useState } from "react"; | ||
| import { Abi } from "viem"; | ||
| import { Abi, hexToBigInt } from "viem"; | ||
|
|
||
| // Global cache to persist balances across hook instances and app lifecycle | ||
| const globalBalanceCache = new Map<string, Map<string, string>>(); | ||
|
|
||
| import { useBalance, useReadContracts } from "wagmi"; | ||
| import { useNetwork } from "../contexts/network"; | ||
| import { useAssetHubNode } from "../contexts/polkadotNode"; | ||
| import erc20ABI from "../contracts/ERC20"; | ||
| import { multiplyByPowerOfTen } from "../helpers/contracts"; |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useReadContracts, erc20ABI, and Abi are imported but no longer referenced in this file. This will fail lint/typecheck in many setups; please remove unused imports (or reintroduce usage if intended).
| }); | ||
| } | ||
|
|
||
| console.log("Getting token details for:", finalOutputCurrency, "on network:", network); |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the console.log in this server-side path. It will spam production logs and bypasses the configured logger (which also controls log levels/format). Use logger.debug(...) (or remove entirely) instead.
| console.log("Getting token details for:", finalOutputCurrency, "on network:", network); | |
| logger.debug("Getting token details for EVM destination", { | |
| finalOutputCurrency, | |
| network | |
| }); |
| @@ -0,0 +1 @@ | |||
| ./packages/shared/src/tokens/index.ts No newline at end of file | |||
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ignore entry ./packages/shared/src/tokens/index.ts is unlikely to match when running prettier . from within packages/shared (paths are relative to that directory). Use src/tokens/index.ts (or verify the configured Prettier ignore path base) so the file is actually ignored.
| for (const token of tokens) { | ||
| if (isNetworkEVM(token.network)) { | ||
| const network = token.network as EvmNetworks; | ||
| if (!grouped[network]) { | ||
| grouped[network] = {}; | ||
| } | ||
| grouped[network][token.assetSymbol] = token; | ||
| } |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dynamic tokens are keyed by token.assetSymbol only. On many EVM chains there are multiple distinct tokens sharing a symbol (e.g., multiple USDC variants), so this will silently overwrite entries and can return incorrect token details. Consider keying by a stable identifier (address) and/or storing a list per symbol with explicit disambiguation logic.
| export interface EvmTokenDetailsWithBalance extends EvmTokenDetails { | ||
| balance: string; | ||
| balanceUsd?: string; | ||
| } |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
balanceUsd is required on OnChainTokenDetailsWithBalance, but EvmTokenDetailsWithBalance defines balanceUsd as optional. This creates inconsistent typing for EVM balances (and forces extra casting). Align the types by making balanceUsd consistently required or consistently optional across balance-bearing token types.
| "dependencies": { | ||
| "big.js": "^7.0.1", | ||
| "husky": "^9.1.7", | ||
| "lint-staged": "^16.1.0" | ||
| "lint-staged": "^16.1.0", | ||
| "react-window": "^2.2.5" | ||
| }, |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
react-window was added as a dependency but there are no imports/usages in the repo (search shows none). If virtualization is now handled by @tanstack/react-virtual, please remove react-window to avoid extra bundle/dependency surface.
| "react-hook-form": "^7.65.0", | ||
| "react-i18next": "^15.4.1", | ||
| "react-toastify": "^11.0.5", | ||
| "react-window": "^2.2.5", |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
react-window is listed in dependencies, but the code uses @tanstack/react-virtual and there are no react-window imports. Consider removing react-window here to keep dependencies minimal.
| "react-window": "^2.2.5", |
| import { stellarTokenConfig } from "../stellar/config"; | ||
| import { AssetHubToken, FiatToken, OnChainToken, RampCurrency } from "../types/base"; | ||
| import { EvmToken } from "../types/evm"; | ||
| import { EvmToken, EvmTokenDetails } from "../types/evm"; |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import EvmToken.
No description provided.