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 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 03:13:00 +00:00
parent 53551c6382
commit d8ed061b6c
15 changed files with 797 additions and 123 deletions
+13 -1
View File
@@ -43,12 +43,15 @@ const GAS_PRICE_WEI = BASE_FEE_WEI + PRIORITY_FEE_WEI;
const STUB_BLOCK_NUMBER = 21000000;
// A 32-byte zero word, returned for every eth_call. It is what makes ethers'
// A 32-byte zero word, returned for every eth_call but a price feed's
// latestRoundData() (see rpcResult() below). It is what makes ethers'
// ENS reverse lookup resolve to "no resolver set" instead of throwing, and a
// throw there reaches the console through src/shared/log.js, which fails the
// run on its own.
const ZERO_WORD = "0x" + "0".repeat(64);
const SELECTOR_LATEST_ROUND_DATA = "0xfeaf968c";
// One ETH, so the popup's balance lines render something and the wallet does
// not look empty on the approval screen.
const STUB_BALANCE_WEI = 10n ** 18n;
@@ -111,6 +114,15 @@ function rpcResult(req, state) {
return latestBlock();
}
// A price feed's latestRoundData() gets five zero words, as in network.js:
// a price of zero, which the wallet counts as no price.
const data = String(
(req.params && req.params[0] && req.params[0].data) || "",
);
if (method === "eth_call" && data.startsWith(SELECTOR_LATEST_ROUND_DATA)) {
return "0x" + "0".repeat(64 * 5);
}
if (Object.prototype.hasOwnProperty.call(RPC_RESULTS, method)) {
return RPC_RESULTS[method];
}