poc: Token list - #263
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR introduces a proof-of-concept “token graph” module and UI for graph-backed token discovery, route resolution, and balance-based source token loading across a small set of chains.
Changes:
- Add a token graph library that merges token sources (native/manual/Uniswap list/LiFi registry) and protocol/LiFi mapping edges.
- Add API routes to search source tokens, resolve destination tokens (with optional LiFi swap fallback), and fetch wallet balances via multicall.
- Add a standalone
/token-graph-pocpage + client UI and a Vitest test suite for destination routing behavior.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/app/src/lib/token-graph-poc/types.ts | Adds shared type definitions for token variants, edges, and API responses. |
| packages/app/src/lib/token-graph-poc/graph.ts | Core token graph build/cache logic plus public functions for search, destination resolution, and balances. |
| packages/app/src/lib/token-graph-poc/graph.test.ts | Adds tests covering destination token routing and swap fallback behavior. |
| packages/app/src/app/api/token-graph-poc/tokens/route.ts | API endpoint to search/filter source tokens (optionally by destination). |
| packages/app/src/app/api/token-graph-poc/destination/route.ts | API endpoint to resolve destination tokens/routes for a selected source token. |
| packages/app/src/app/api/token-graph-poc/balances/route.ts | API endpoint to load wallet balances (optionally filtered by destination reachability). |
| packages/app/src/app/(with-sidebar)/token-graph-poc/page.tsx | Registers the PoC page route and metadata. |
| packages/app/src/app/(with-sidebar)/token-graph-poc/TokenGraphPocClient.tsx | Client UI to explore source tokens (balances/search) and destination routes (direct + on-demand swap fallback). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const [nativeBalance, tokenBalances] = await Promise.all([ | ||
| nativeToken ? client.getBalance({ address: walletAddress }) : null, | ||
| client.multicall({ |
There was a problem hiding this comment.
walletAddress is validated with isAddress, but it remains typed as string while viem expects an Address for getBalance. In strict TS this won’t typecheck, and it also misses an opportunity to normalize the address once (e.g. checksummed) before use.
|
|
||
| const rpcUrl = rpcURLs[chainId]; | ||
| if (!rpcUrl) { | ||
| throw new Error(`No RPC URL configured for chain ${chainId}`); | ||
| } |
There was a problem hiding this comment.
supportedChainIds/chains include ChainId.ApeChain, but rpcURLs (from @/bridge/util/networks) does not include an entry for ApeChain by default. This makes GET /api/token-graph-poc/balances return No RPC URL configured... whenever ApeChain is selected. Either remove ApeChain from the PoC-supported set or source an RPC URL for it (e.g. from orbit chain config) so balances work consistently.
| abi: erc20Abi, | ||
| functionName: 'balanceOf' as const, | ||
| args: [walletAddress], | ||
| })), | ||
| }), |
There was a problem hiding this comment.
The balanceOf call should receive a validated/normalized Address as its argument. Here walletAddress is still a plain string, which is likely to fail strict viem typing and can lead to inconsistent formatting. Consider normalizing once after validation (e.g. checksummed Address) and reusing it for both native + ERC20 reads.
| describe('token graph destination tokens', () => { | ||
| beforeEach(() => { | ||
| mockLifiRegistry.tokensByChain = {}; | ||
| mockLifiRegistry.tokensByChainAndCoinKey = {}; | ||
|
|
There was a problem hiding this comment.
graph.ts caches cachedTokens/cachedEdges/graphPromise at module scope, but these tests mutate mockLifiRegistry between cases. Once the first test populates the cache, later tests can reuse the cached graph and never observe the updated registry/fetch stubs, making the suite order-dependent (and likely failing). Reset the graph module state between tests (e.g. vi.resetModules() + re-import, or expose a test-only reset function).
| if (!graphPromise) { | ||
| graphPromise = Promise.all([fetchUniswapTokens(), getLifiGraphData()]).then( | ||
| ([uniswapTokens, lifiGraphData]) => { | ||
| const tokenById = new Map<string, TokenVariant>(); | ||
|
|
There was a problem hiding this comment.
If fetchUniswapTokens() / getLifiGraphData() rejects, the assigned graphPromise will stay rejected and never be cleared, so subsequent calls to getGraphData() will keep failing until the process restarts. Add error handling (e.g. a .catch) that resets graphPromise/caches on failure to allow retries after transient errors.
Summary
Steps to test