Repository navigation
Price & token sources: list, order and extend where Sage reads prices - #897
Open
awizardxch wants to merge 4 commits into
Open
awizardxch wants to merge 4 commits into
awizardxch wants to merge 4 commits into
Conversation
A new Settings > Network > Price & Token Sources section lists the sources Sage already uses, with what each provides and a switch: Dexie (CAT prices; its names and icons are saved by the wallet's own sync) and CoinGecko (the XCH/USD rate). The user can add custom token indexes (one https JSON document, version 1, listing CATs with a name, ticker, https icon and price in XCH) and reorder all sources with up/down buttons. For each value, the first enabled source that has it wins and later sources only fill gaps: a CAT's price in XCH, the XCH/USD rate, and a display-only label for a CAT with no name. Index labels never replace a name the wallet has (from the user, Dexie or chia_updateCat) and are never written to it. Indexes are fetched on the existing 60-second price refresh, validated (version, network, text bounds matching chia_updateCat, https icons, finite positive prices), and a failed fetch keeps the last good copy. Nothing is stored until the user changes the list, and the defaults (Dexie then CoinGecko, both on, no custom sources) make exactly the requests Sage made before. The single tokenIndexUrl key from an earlier build of this branch is read once into the list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
A note on the red checks: the two iOS jobs and Android fail at the signing step, because GitHub doesn't pass this repo's signing secrets to a PR from a fork. The iOS log shows |
…oken page A token the wallet already named (for example through chia_updateCat) kept "CAT" and no icon, because a suggestion was made only for a token with no name. Each of name, ticker and icon is now suggested where the token has none, and an existing value is still never replaced. The token page's card also shows the suggested ticker and icon; its edit dialog keeps starting from, and saving, the wallet's own record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Sage prices CATs from Dexie's
v3/prices/tickers, takes the XCH/USD rate from CoinGecko, and names CATs from Dexie'sv1/assets. None of this is visible or adjustable. A CAT that Dexie has no price for (it trades elsewhere, such as an AMM, or it is not registered with Dexie) shows no value, and an unregistered CAT shows as "Unknown CAT", however actively it trades. The user can't see where a number came from, can't turn a source off, and can't add another one.Change
A new section, Settings → Network → Price & Token Sources, lists the sources in their order of priority:
Each row has a switch, up/down buttons to change its priority (plain buttons, keyboard accessible, with labels such as "Move Dexie up"), and custom rows have a remove button. An input below the list adds a custom token index: one https JSON document (format below).
Priority and fallback. For each value, the first enabled source that has it wins; later sources only fill gaps:
last_price, an index'sprice_xch).xch_usd).chia_updateCat, is never replaced, and nothing is written to the wallet's database: turn the index off and the suggestion is gone. To keep a suggested name, edit the token.Defaults reproduce today's behavior exactly. Dexie on and first, CoinGecko on, no custom sources. Nothing is stored until the user changes the list, so a user who never opens the section makes exactly the requests Sage makes on
main, with no new default host. Turning Dexie off stops the price request only. Dexie's names and icons come from the Rust CAT queue (crates/sage-wallet/src/queues/cat_queue.rs) and are not affected; the row says so.Fetching. Custom indexes are fetched on the existing 60-second price refresh, beside Dexie and CoinGecko. Reordering doesn't refetch. A document whose
versionis not 1, or whosenetworkdoes not match the wallet's (mainnet/testnet11), is ignored. Within it, each entry and field is checked on its own and a bad one is dropped: the asset id must be 64 lowercase hex characters (not the native coin), the name 1–64 characters, the ticker 1–16, the icon https and at most 512 characters (the same bounds aschia_updateCat), and prices finite and positive. A failed fetch keeps the last good copy.What's not listed. Spacescan isn't in the list. Sage uses it only for explorer links (
spacescanCoinUrl/spacescanNftUrl/spacescanCollectionUrlinsrc/lib/urls.ts, used by coin, NFT, collection and option pages). It doesn't fetch prices or token data from it. Its per-token API doesn't fit a one-call index either.Files:
src/lib/priceSources.ts: the built-in sources, the stored list (normalizePriceSources, add, move, the legacy migration),activePriceSourcesfor the network, and the priority rules (firstCatPriceXch,firstXchUsd,firstLabel).src/lib/priceSources.test.ts: defaults, ordering, fallback, enablement, network matching and migration.src/lib/tokenIndex.ts,src/lib/tokenIndex.test.ts: the index format,parseTokenIndex(validation),withSuggestedLabel,isValidTokenIndexUrl.src/hooks/usePriceSources.ts: the setting, in local storage like the other preferences (priceSources). It reads once the singletokenIndexUrlkey that an earlier build of this branch stored.src/contexts/PriceContext.tsx: fetches only the enabled sources, applies the order, and exposesgetSuggestedLabel.src/pages/Settings.tsx: the section.src/pages/TokenList.tsx,src/pages/Token.tsx: the display-only label suggestion.src/locales/*/messages.po:pnpm extractfor the 14 new strings (a separate "Extract" commit; the rest of that diff is reference lines whose line numbers moved). Without it a production build shows the strings' message ids. Translations for de-DE, es-MX and zh-CN are left empty for translators.No Rust changes, no new dependency, no new default host.
Side finding, out of scope: Sage's hourly Dexie refresh upserts every Dexie asset with
name = COALESCE(excluded.name, name)(crates/sage-database/src/tables/assets/asset.rs). So for a CAT Dexie names, a name the user typed (or set throughchia_updateCat) is overwritten by Dexie's within the hour. This PR doesn't change that. It only means the "never overwrite" rule above holds for index suggestions but not, today, for the user's own edits against Dexie.Token index format (version 1)
Any provider can serve it: an exchange, an indexer, a community list. Nothing in it is specific to one provider.
{ "version": 1, "provider": { "name": "Example Index", "url": "https://index.example" }, "network": "mainnet", "generated_at": "2026-10-17T07:24:54.208Z", "ttl_seconds": 300, "xch_usd": 1.62, "tokens": [ { "asset_id": "ab558b1b841365a24d1ff2264c55982e55664a8b6e45bc107446b7e667bb463b", "name": "SPROUT", "ticker": "🌱", "icon_url": "https://index.example/icons/sprout.webp", "decimals": 3, "price_xch": 0.000016181075, "price_usd": 0.0000262133, "liquidity_xch": 91.857983, "source": "amm" } ] }version1. Any other value: the document is ignored.provider{ name, url }, informational.networkmainnetortestnet11; must match the wallet's network.generated_atttl_secondsxch_usdtokens[].asset_idtokens[].nametokens[].tickertokens[].icon_urlhttps://, at most 512 characters.tokens[].descriptiontokens[].decimalstokens[].price_xchtokens[].price_usdtokens[].liquidity_xchtokens[].sourceammororderbook.A provider should serve it like this: a GET over https with
Access-Control-Allow-Origin: *and no key or cookie, one document per network at a stable URL, andCache-Controlwiths-maxageequal tottl_secondsso that a CDN answers wallets' polls. It should list a price only for markets deep enough that a few mojos can't move it.How to test
pnpm test:unit(src/lib/priceSources.test.ts,src/lib/tokenIndex.test.ts),pnpm lint,pnpm tsc -b,pnpm prettier --checkon the touched files.main: only Dexie and CoinGecko are fetched, every 60 s.http://example.comand press Add: it's refused with a message. Add an https index for the wallet's network (such as the example above): it appears third, on.api.dexie.space/v3/prices/tickers, and CATs are priced by the index only. Turn CoinGecko off with an index that hasxch_usd: the index's rate is used."version": 2: it's ignored (a console warning), and Sage shows what it shows without it. Remove it.🤖 Generated with Claude Code