fix: read USD prices from Chainlink price feeds through the RPC endpoint (closes #525)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s

CoinDesk now refuses every request without an API key, so mainnet showed no
USD prices. On mainnet the wallet now reads latestRoundData() from Chainlink's
USD price feeds through the configured RPC endpoint. ETH and the held or
tracked tokens with a feed are read as one batched request, at most every 5
minutes. src/shared/tokenList.js lists the feeds of 53 bundled tokens, keyed by
contract address, and the ETH/USD feed. A reading older than its feed's
heartbeat plus 30 minutes, or not positive, is no price. A failed read leaves
no price and is logged. getPrice() takes a contract address instead of a
symbol. The CoinDesk request is removed.

Model: opus-5-5
This commit is contained in:
2026-10-27 02:49:24 +00:00
parent 53551c6382
commit 8ba30f873c
15 changed files with 796 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