// The approval screen for a site's Sepolia transfer of the contract at USDC's // mainnet address, while Sepolia is active. // // The bundled token list holds Ethereum Mainnet contracts. The contract at the // same address on Sepolia is another token, but the screen named it USDC and // scaled its amount at USDC's decimals // (https://git.eeqj.de/sneak/AutistMask/issues/632). The bundled list now names // a token only on Ethereum Mainnet. The same screen for a mainnet transfer of // USDC, while Sepolia is active, still names USDC. // // Driven through the real approval screen, against the minimal DOM stub // tests/approvalTokenNetwork.test.js uses. globalThis.chrome = { storage: { local: { get: async () => ({}), set: async () => {} } }, }; const { Interface } = require("ethers"); const { state } = require("../src/shared/state"); const { NETWORKS } = require("../src/shared/networks"); const approval = require("../src/popup/views/approval"); const FROM = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; const RECIPIENT = "0x711e691962E5c054b78071C789A0C8CE657377aD"; // USDC's address on Ethereum Mainnet, where the bundled list names it. const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; // FROM sends 1234500000 base units to RECIPIENT. const TRANSFER_DATA = new Interface([ "function transfer(address to, uint256 amount)", ]).encodeFunctionData("transfer", [RECIPIENT, 1234500000n]); function makeElement(id) { const classes = new Set(); const el = { id, textContent: "", value: "", innerHTML: "", disabled: false, style: {}, dataset: {}, listeners: {}, classList: { add: (...names) => names.forEach((n) => classes.add(n)), remove: (...names) => names.forEach((n) => classes.delete(n)), contains: (n) => classes.has(n), toggle: (n, force) => { const on = force === undefined ? !classes.has(n) : force; if (on) classes.add(n); else classes.delete(n); return on; }, }, addEventListener: (name, fn) => { el.listeners[name] = el.listeners[name] || []; el.listeners[name].push(fn); }, querySelectorAll: () => [], appendChild: () => {}, }; // Views reach for .parentElement to hide whole sections. Object.defineProperty(el, "parentElement", { get: () => node(id + "-parent"), }); return el; } function makeDocument() { const els = new Map(); return { getElementById(id) { // The debug banner is created on demand by helpers.js; absent // is the state a non-debug, non-testnet popup is in. if (id === "debug-banner") return null; if (!els.has(id)) els.set(id, makeElement(id)); return els.get(id); }, createElement: () => makeElement("created"), body: { prepend: () => {} }, }; } function node(id) { return globalThis.document.getElementById(id); } // One line of the decoded details: its label and what is drawn under it. function decodedLine(label) { return node("approve-tx-decoded-details") .innerHTML.split('