// AutistMask background service worker // Handles EIP-1193 RPC requests from content scripts and proxies // non-sensitive calls to the configured Ethereum JSON-RPC endpoint. const CHAIN_ID = "0x1"; const DEFAULT_RPC = "https://eth.llamarpc.com"; const storageApi = typeof browser !== "undefined" ? browser.storage.local : chrome.storage.local; const runtime = typeof browser !== "undefined" ? browser.runtime : chrome.runtime; // Connected sites: { origin: [address, ...] } const connectedSites = {}; async function getState() { const result = await storageApi.get("autistmask"); return result.autistmask || { wallets: [], rpcUrl: DEFAULT_RPC }; } async function getAccounts() { const state = await getState(); const accounts = []; for (const wallet of state.wallets) { for (const addr of wallet.addresses) { accounts.push(addr.address); } } return accounts; } async function getRpcUrl() { const state = await getState(); return state.rpcUrl || DEFAULT_RPC; } // Proxy an RPC call to the Ethereum node async function proxyRpc(method, params) { const rpcUrl = await getRpcUrl(); const resp = await fetch(rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params, }), }); const json = await resp.json(); if (json.error) { throw new Error(json.error.message || "RPC error"); } return json.result; } // Methods that are safe to proxy directly to the RPC node const PROXY_METHODS = [ "eth_blockNumber", "eth_call", "eth_chainId", "eth_estimateGas", "eth_gasPrice", "eth_getBalance", "eth_getBlockByHash", "eth_getBlockByNumber", "eth_getCode", "eth_getLogs", "eth_getStorageAt", "eth_getTransactionByHash", "eth_getTransactionCount", "eth_getTransactionReceipt", "eth_maxPriorityFeePerGas", "eth_sendRawTransaction", "net_version", "web3_clientVersion", "eth_feeHistory", "eth_getBlockTransactionCountByHash", "eth_getBlockTransactionCountByNumber", ]; async function handleRpc(method, params, origin) { // Methods that need wallet involvement if (method === "eth_requestAccounts" || method === "eth_accounts") { const accounts = await getAccounts(); if (accounts.length === 0) { return { error: { message: "No accounts available" } }; } // Auto-connect for now (approval flow is a future TODO) connectedSites[origin] = accounts; return { result: accounts }; } if (method === "eth_chainId") { return { result: CHAIN_ID }; } if (method === "net_version") { return { result: "1" }; } if (method === "wallet_switchEthereumChain") { const chainId = params?.[0]?.chainId; if (chainId === CHAIN_ID) { return { result: null }; } return { error: { code: 4902, message: "AutistMask only supports Ethereum mainnet.", }, }; } if (method === "wallet_addEthereumChain") { return { error: { code: 4902, message: "AutistMask only supports Ethereum mainnet.", }, }; } if (method === "wallet_requestPermissions") { const accounts = await getAccounts(); if (accounts.length === 0) { return { error: { message: "No accounts available" } }; } connectedSites[origin] = accounts; return { result: [ { parentCapability: "eth_accounts", caveats: [ { type: "restrictReturnedAccounts", value: accounts, }, ], }, ], }; } if (method === "wallet_getPermissions") { const accounts = connectedSites[origin] || []; if (accounts.length === 0) { return { result: [] }; } return { result: [ { parentCapability: "eth_accounts", caveats: [ { type: "restrictReturnedAccounts", value: accounts, }, ], }, ], }; } if (method === "personal_sign" || method === "eth_sign") { // TODO: implement signature approval flow return { error: { message: "Signing not yet implemented in AutistMask." }, }; } if (method === "eth_signTypedData_v4" || method === "eth_signTypedData") { // TODO: implement typed data signing return { error: { message: "Typed data signing not yet implemented in AutistMask.", }, }; } if (method === "eth_sendTransaction") { // TODO: implement transaction signing approval flow // For now, return an error directing the user to use the popup return { error: { message: "Transaction signing via dApps not yet implemented. Use the AutistMask popup to send transactions.", }, }; } // Proxy safe read-only methods to the RPC node if (PROXY_METHODS.includes(method)) { try { const result = await proxyRpc(method, params); return { result }; } catch (e) { return { error: { message: e.message } }; } } return { error: { message: "Unsupported method: " + method } }; } // Listen for messages from content scripts runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.type !== "AUTISTMASK_RPC") return; handleRpc(msg.method, msg.params, msg.origin).then((response) => { sendResponse(response); }); // Return true to indicate async response return true; });