fix: read USD prices from Chainlink price feeds through the RPC endpoint (closes #525)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run

CoinDesk now refuses requests without an API key, so mainnet showed no USD prices. Mainnet prices now come from Chainlink's USD price feeds, read with eth_call through the user's RPC endpoint; no other service is asked. The bundled list pairs each of 53 tokens with its feed by contract address, and ETH and WETH use ETH/USD. The popup reads the feeds of ETH and the tokens the user holds or tracks in one batched request, at most every 5 minutes. A reading older than its heartbeat plus 30 minutes, or not positive, is no price. A token with no feed shows no USD value. CoinDesk is gone.

RULES.md still names the CoinDesk price API.

Model: opus-5-5
This commit was merged in pull request #681.
This commit is contained in:
2026-10-27 04:23:17 +01:00
parent 53551c6382
commit b4e166cdc7
15 changed files with 797 additions and 123 deletions
+28 -31
View File
@@ -17,11 +17,11 @@ and don't implement any crypto. By default the wallet sends user-specific data
only to the Ethereum RPC endpoint. The user can change that endpoint in
Settings. The endpoint defaults to a public node.
Out of the box the extension contacts two services:
Out of the box the extension contacts one service, the configured RPC node. The
wallet uses the node for:
- the configured RPC node, for blockchain interactions
- a public CoinDesk API (no API key), for realtime prices. The API receives
token symbols and nothing else.
- blockchain interactions
- USD prices on mainnet, read from Chainlink price feed contracts
Two more services are sent none of the user's data until the user ticks a
checkbox for each. Welcome and Settings both offer the two checkboxes:
@@ -902,13 +902,14 @@ src/
constants.js — chain IDs, default RPC endpoint, ERC-20 ABI
ens.js — ENS forward/reverse resolution (popup only)
holders.js — holder-count parsing and the low-holder rule
prices.js — ETH/USD and token/USD via CoinDesk API
prices.js — ETH/USD and token/USD from Chainlink price feeds
scamlist.js — known fraud contract addresses
state.js — persisted state (extension storage)
storedSettings.js — "Use Blockscout" and "Check recipients on Etherscan",
read from storage for the requests those settings gate
symbolSpoof.js — the known-symbol spoof rule, shared by all surfaces
tokenList.js — top ERC-20 tokens by market cap (hardcoded)
tokenList.js — top ERC-20 tokens by market cap, and their Chainlink
price feeds (hardcoded)
transactions.js — tx history fetching + anti-poisoning filters
uniswap.js — Uniswap Universal Router calldata decoder
vault.js — password-based encryption via libsodium
@@ -1371,10 +1372,10 @@ whole swap undecoded.
#### Partial USD totals
Prices are fetched for the top 25 tokens only, so an address can hold assets the
extension has no price for. Worth zero and worth an unknown amount are different
facts and are never collapsed into one number. `getAddressValue()` in
`src/shared/prices.js` returns `{ usd, partial }` — the value of the priced
Only a token with a Chainlink price feed has a price, so an address can hold
assets the extension has no price for. Worth zero and worth an unknown amount
are different facts and are never collapsed into one number. `getAddressValue()`
in `src/shared/prices.js` returns `{ usd, partial }` — the value of the priced
holdings, and whether an unpriced holding was left out of it — and every screen
renders it through `formatAddressTotal()`, so the wording cannot drift:
@@ -2854,9 +2855,9 @@ view would leave a wallet one click from deletion.
### External Services
AutistMask is not a fully self-contained offline tool. By default it sends the
user's data only to the Ethereum RPC endpoint. It also asks the CoinDesk price
API for prices, sending token symbols and nothing else.
AutistMask is not a fully self-contained offline tool. By default it contacts
only the Ethereum RPC endpoint. USD prices are read from Chainlink price feed
contracts through that endpoint.
Two more services are sent none of the user's data until the user turns on a
setting for each:
@@ -2880,21 +2881,18 @@ Contacted by default:
estimate gas (`eth_estimateGas`), fetch nonces (`eth_getTransactionCount`),
broadcast transactions (`eth_sendRawTransaction`), and check transaction
receipts. Every ENS lookup is made through the RPC endpoint alone. So a name
does not resolve when its resolver says the name is resolved off-chain. The
default endpoint is a public RPC (configurable by the user to any endpoint
they prefer, including a local node). By default the extension talks to
does not resolve when its resolver says the name is resolved off-chain. On
mainnet the extension also reads USD prices from Chainlink price feed
contracts (`latestRoundData`). The prices of ETH and of each held or tracked
token with a feed are read as one batched request. That request is sent at
most every 5 minutes while the popup is open. The default endpoint is a public
RPC (configurable by the user to any endpoint they prefer, including a local
node). By default the extension talks to
`https://ethereum-rpc.publicnode.com`.
- **Data sent**: Ethereum addresses, transaction data, contract call
parameters. The RPC endpoint can see all on-chain queries and submitted
transactions.
- **CoinDesk CADLI price API**: Used to fetch ETH/USD and token/USD prices for
displaying fiat values. The price is cached for 5 minutes to avoid excessive
requests. No API key required. No user data is sent — only a list of token
symbols. Note that CoinDesk will receive your client IP.
- **Data sent**: Token symbol strings only (e.g. "ETH", "USDC"). No
addresses or user-specific data.
Sent the user's data only while its setting is on:
- **Blockscout block-explorer API** ("Use Blockscout"): The default endpoint is
@@ -3070,14 +3068,13 @@ hardcoded test phrase.
At the raised log level the console also shows the wallet's addresses with their
balances and ENS names, the token contracts looked up, and a line for each
request made through `debugFetch` in `src/shared/log.js` (the explorer, the
price feed, the RPC calls a site makes, and the endpoint checks in settings) and
for its response. A request is logged by its HTTP method, the origin of its URL
(scheme, host and port) and, for a JSON-RPC call, the method name; the balance
refresh, the token lookup and a failed endpoint check in settings name the
endpoint by its origin too. The URL's path and query string, where RPC providers
put API keys, any user name and password in it, and the request body are never
logged.
request made through `debugFetch` in `src/shared/log.js` (the explorer, the RPC
calls a site makes, and the endpoint checks in settings) and for its response. A
request is logged by its HTTP method, the origin of its URL (scheme, host and
port) and, for a JSON-RPC call, the method name; the balance refresh, the token
lookup and a failed endpoint check in settings name the endpoint by its origin
too. The URL's path and query string, where RPC providers put API keys, any user
name and password in it, and the request body are never logged.
### Key Decisions