Compare commits
4 Commits
33070cae75
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a22f33d511 | |||
| 39db06c83d | |||
| df031fd07d | |||
| a138a36710 |
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
const { DEFAULT_RPC_URL } = require("../shared/constants");
|
const { DEFAULT_RPC_URL } = require("../shared/constants");
|
||||||
const { SUPPORTED_CHAIN_IDS, networkByChainId } = require("../shared/networks");
|
const { SUPPORTED_CHAIN_IDS, networkByChainId } = require("../shared/networks");
|
||||||
|
const { onChainSwitch } = require("../shared/chainSwitch");
|
||||||
const { getBytes } = require("ethers");
|
const { getBytes } = require("ethers");
|
||||||
const {
|
const {
|
||||||
state,
|
state,
|
||||||
@@ -345,12 +346,8 @@ async function handleRpc(method, params, origin) {
|
|||||||
return { result: null };
|
return { result: null };
|
||||||
}
|
}
|
||||||
if (SUPPORTED_CHAIN_IDS.has(chainId)) {
|
if (SUPPORTED_CHAIN_IDS.has(chainId)) {
|
||||||
// Switch to the requested network
|
|
||||||
const target = networkByChainId(chainId);
|
const target = networkByChainId(chainId);
|
||||||
state.networkId = target.id;
|
await onChainSwitch(target.id);
|
||||||
state.rpcUrl = target.defaultRpcUrl;
|
|
||||||
state.blockscoutUrl = target.defaultBlockscoutUrl;
|
|
||||||
await saveState();
|
|
||||||
broadcastChainChanged(target.chainId);
|
broadcastChainChanged(target.chainId);
|
||||||
return { result: null };
|
return { result: null };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,22 @@
|
|||||||
// Loads state, initializes views, triggers first render.
|
// Loads state, initializes views, triggers first render.
|
||||||
|
|
||||||
const { DEBUG } = require("../shared/constants");
|
const { DEBUG } = require("../shared/constants");
|
||||||
const { state, saveState, loadState } = require("../shared/state");
|
const {
|
||||||
|
state,
|
||||||
|
saveState,
|
||||||
|
loadState,
|
||||||
|
currentNetwork,
|
||||||
|
} = require("../shared/state");
|
||||||
const { refreshPrices } = require("../shared/prices");
|
const { refreshPrices } = require("../shared/prices");
|
||||||
const { refreshBalances } = require("../shared/balances");
|
const { refreshBalances } = require("../shared/balances");
|
||||||
const { $, showView } = require("./views/helpers");
|
const {
|
||||||
|
$,
|
||||||
|
showView,
|
||||||
|
setRenderMain,
|
||||||
|
pushCurrentView,
|
||||||
|
goBack,
|
||||||
|
clearViewStack,
|
||||||
|
} = require("./views/helpers");
|
||||||
const { applyTheme } = require("./theme");
|
const { applyTheme } = require("./theme");
|
||||||
|
|
||||||
const home = require("./views/home");
|
const home = require("./views/home");
|
||||||
@@ -53,15 +65,42 @@ async function doRefreshAndRender() {
|
|||||||
const ctx = {
|
const ctx = {
|
||||||
renderWalletList,
|
renderWalletList,
|
||||||
doRefreshAndRender,
|
doRefreshAndRender,
|
||||||
showAddWalletView: () => addWallet.show(),
|
showAddWalletView: () => {
|
||||||
showAddressDetail: () => addressDetail.show(),
|
pushCurrentView();
|
||||||
showAddressToken: () => addressToken.show(),
|
addWallet.show();
|
||||||
showAddTokenView: () => addToken.show(),
|
},
|
||||||
showConfirmTx: (txInfo) => confirmTx.show(txInfo),
|
showAddressDetail: () => {
|
||||||
showReceive: () => receive.show(),
|
pushCurrentView();
|
||||||
showTransactionDetail: (tx) => transactionDetail.show(tx),
|
addressDetail.show();
|
||||||
showSettingsView: () => settings.show(),
|
},
|
||||||
showSettingsAddTokenView: () => settingsAddToken.show(),
|
showAddressToken: () => {
|
||||||
|
pushCurrentView();
|
||||||
|
addressToken.show();
|
||||||
|
},
|
||||||
|
showAddTokenView: () => {
|
||||||
|
pushCurrentView();
|
||||||
|
addToken.show();
|
||||||
|
},
|
||||||
|
showConfirmTx: (txInfo) => {
|
||||||
|
pushCurrentView();
|
||||||
|
confirmTx.show(txInfo);
|
||||||
|
},
|
||||||
|
showReceive: () => {
|
||||||
|
pushCurrentView();
|
||||||
|
receive.show();
|
||||||
|
},
|
||||||
|
showTransactionDetail: (tx) => {
|
||||||
|
pushCurrentView();
|
||||||
|
transactionDetail.show(tx);
|
||||||
|
},
|
||||||
|
showSettingsView: () => {
|
||||||
|
pushCurrentView();
|
||||||
|
settings.show();
|
||||||
|
},
|
||||||
|
showSettingsAddTokenView: () => {
|
||||||
|
pushCurrentView();
|
||||||
|
settingsAddToken.show();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Views that can be fully re-rendered from persisted state.
|
// Views that can be fully re-rendered from persisted state.
|
||||||
@@ -167,18 +206,25 @@ function fallbackView() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
if (DEBUG) {
|
await loadState();
|
||||||
|
applyTheme(state.theme);
|
||||||
|
|
||||||
|
const net = currentNetwork();
|
||||||
|
if (DEBUG || net.isTestnet) {
|
||||||
const banner = document.createElement("div");
|
const banner = document.createElement("div");
|
||||||
banner.id = "debug-banner";
|
banner.id = "debug-banner";
|
||||||
|
if (DEBUG && net.isTestnet) {
|
||||||
|
banner.textContent = "DEBUG / INSECURE [TESTNET]";
|
||||||
|
} else if (net.isTestnet) {
|
||||||
|
banner.textContent = "[TESTNET]";
|
||||||
|
} else {
|
||||||
banner.textContent = "DEBUG / INSECURE";
|
banner.textContent = "DEBUG / INSECURE";
|
||||||
|
}
|
||||||
banner.style.cssText =
|
banner.style.cssText =
|
||||||
"background:#c00;color:#fff;text-align:center;font-size:10px;padding:1px 0;font-family:monospace;position:sticky;top:0;z-index:9999;";
|
"background:#c00;color:#fff;text-align:center;font-size:10px;padding:1px 0;font-family:monospace;position:sticky;top:0;z-index:9999;";
|
||||||
document.body.prepend(banner);
|
document.body.prepend(banner);
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadState();
|
|
||||||
applyTheme(state.theme);
|
|
||||||
|
|
||||||
// Auto-default active address
|
// Auto-default active address
|
||||||
if (
|
if (
|
||||||
state.activeAddress === null &&
|
state.activeAddress === null &&
|
||||||
@@ -208,13 +254,15 @@ async function init() {
|
|||||||
.getElementById("view-settings")
|
.getElementById("view-settings")
|
||||||
.classList.contains("hidden")
|
.classList.contains("hidden")
|
||||||
) {
|
) {
|
||||||
renderWalletList();
|
goBack();
|
||||||
showView("main");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
pushCurrentView();
|
||||||
settings.show();
|
settings.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setRenderMain(renderWalletList);
|
||||||
|
|
||||||
welcome.init(ctx);
|
welcome.init(ctx);
|
||||||
addWallet.init(ctx);
|
addWallet.init(ctx);
|
||||||
home.init(ctx);
|
home.init(ctx);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { $, showView, showFlash } = require("./helpers");
|
const { $, showFlash, goBack } = require("./helpers");
|
||||||
const { getTopTokens } = require("../../shared/tokenList");
|
const { getTopTokens } = require("../../shared/tokenList");
|
||||||
const { state, saveState } = require("../../shared/state");
|
const { state, saveState } = require("../../shared/state");
|
||||||
const { lookupTokenInfo } = require("../../shared/balances");
|
const { lookupTokenInfo } = require("../../shared/balances");
|
||||||
@@ -59,7 +59,12 @@ function init(ctx) {
|
|||||||
});
|
});
|
||||||
await saveState();
|
await saveState();
|
||||||
ctx.doRefreshAndRender();
|
ctx.doRefreshAndRender();
|
||||||
ctx.showAddressDetail();
|
// Pop the stack (back to address detail) and re-render it
|
||||||
|
// so the newly added token is visible immediately.
|
||||||
|
if (state.viewStack.length > 0) {
|
||||||
|
state.viewStack.pop();
|
||||||
|
}
|
||||||
|
require("./addressDetail").show();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const detail = e.shortMessage || e.message || String(e);
|
const detail = e.shortMessage || e.message || String(e);
|
||||||
log.errorf("Token lookup failed for", contractAddr, detail);
|
log.errorf("Token lookup failed for", contractAddr, detail);
|
||||||
@@ -69,7 +74,9 @@ function init(ctx) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("btn-add-token-back").addEventListener("click", ctx.showAddressDetail);
|
$("btn-add-token-back").addEventListener("click", () => {
|
||||||
|
goBack();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { init, show };
|
module.exports = { init, show };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { $, showView, showFlash } = require("./helpers");
|
const { $, showView, showFlash, goBack, clearViewStack } = require("./helpers");
|
||||||
const {
|
const {
|
||||||
generateMnemonic,
|
generateMnemonic,
|
||||||
hdWalletFromMnemonic,
|
hdWalletFromMnemonic,
|
||||||
@@ -143,6 +143,7 @@ async function importMnemonic(ctx) {
|
|||||||
state.wallets.push(wallet);
|
state.wallets.push(wallet);
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
await saveState();
|
await saveState();
|
||||||
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
showView("main");
|
showView("main");
|
||||||
|
|
||||||
@@ -198,6 +199,7 @@ async function importPrivateKey(ctx) {
|
|||||||
});
|
});
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
await saveState();
|
await saveState();
|
||||||
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
showView("main");
|
showView("main");
|
||||||
|
|
||||||
@@ -249,6 +251,7 @@ async function importXprvKey(ctx) {
|
|||||||
state.wallets.push(wallet);
|
state.wallets.push(wallet);
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
await saveState();
|
await saveState();
|
||||||
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
showView("main");
|
showView("main");
|
||||||
|
|
||||||
@@ -297,12 +300,7 @@ function init(ctx) {
|
|||||||
|
|
||||||
// Back button
|
// Back button
|
||||||
$("btn-add-wallet-back").addEventListener("click", () => {
|
$("btn-add-wallet-back").addEventListener("click", () => {
|
||||||
if (!state.hasWallet) {
|
goBack();
|
||||||
showView("welcome");
|
|
||||||
} else {
|
|
||||||
ctx.renderWalletList();
|
|
||||||
showView("main");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ const {
|
|||||||
truncateMiddle,
|
truncateMiddle,
|
||||||
renderAddressHtml,
|
renderAddressHtml,
|
||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
|
goBack,
|
||||||
|
pushCurrentView,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, currentAddress, saveState } = require("../../shared/state");
|
const { state, currentAddress, saveState } = require("../../shared/state");
|
||||||
const { formatUsd, getAddressValueUsd } = require("../../shared/prices");
|
const { formatUsd, getAddressValueUsd } = require("../../shared/prices");
|
||||||
@@ -247,8 +249,7 @@ function init(_ctx) {
|
|||||||
ctx = _ctx;
|
ctx = _ctx;
|
||||||
|
|
||||||
$("btn-address-back").addEventListener("click", () => {
|
$("btn-address-back").addEventListener("click", () => {
|
||||||
ctx.renderWalletList();
|
goBack();
|
||||||
showView("main");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("btn-send").addEventListener("click", () => {
|
$("btn-send").addEventListener("click", () => {
|
||||||
@@ -266,6 +267,7 @@ function init(_ctx) {
|
|||||||
$("send-token-static").classList.add("hidden");
|
$("send-token-static").classList.add("hidden");
|
||||||
updateSendBalance();
|
updateSendBalance();
|
||||||
resetSendValidation();
|
resetSendValidation();
|
||||||
|
pushCurrentView();
|
||||||
showView("send");
|
showView("send");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -295,6 +297,7 @@ function init(_ctx) {
|
|||||||
$("btn-export-privkey").addEventListener("click", () => {
|
$("btn-export-privkey").addEventListener("click", () => {
|
||||||
moreDropdown.classList.add("hidden");
|
moreDropdown.classList.add("hidden");
|
||||||
moreBtn.classList.remove("bg-fg", "text-bg");
|
moreBtn.classList.remove("bg-fg", "text-bg");
|
||||||
|
pushCurrentView();
|
||||||
const wallet = state.wallets[state.selectedWallet];
|
const wallet = state.wallets[state.selectedWallet];
|
||||||
const addr = wallet.addresses[state.selectedAddress];
|
const addr = wallet.addresses[state.selectedAddress];
|
||||||
const blockieEl = $("export-privkey-jazzicon");
|
const blockieEl = $("export-privkey-jazzicon");
|
||||||
@@ -367,7 +370,7 @@ function init(_ctx) {
|
|||||||
$("btn-export-privkey-back").addEventListener("click", () => {
|
$("btn-export-privkey-back").addEventListener("click", () => {
|
||||||
$("export-privkey-value").textContent = "";
|
$("export-privkey-value").textContent = "";
|
||||||
$("export-privkey-password").value = "";
|
$("export-privkey-password").value = "";
|
||||||
show();
|
goBack();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ const {
|
|||||||
balanceLine,
|
balanceLine,
|
||||||
renderAddressHtml,
|
renderAddressHtml,
|
||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
|
goBack,
|
||||||
|
pushCurrentView,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, currentAddress, saveState } = require("../../shared/state");
|
const { state, currentAddress, saveState } = require("../../shared/state");
|
||||||
const { TOKEN_BY_ADDRESS, resolveSymbol } = require("../../shared/tokenList");
|
const { TOKEN_BY_ADDRESS, resolveSymbol } = require("../../shared/tokenList");
|
||||||
@@ -148,7 +150,7 @@ function show() {
|
|||||||
attachCopyHandlers($("address-token-line"));
|
attachCopyHandlers($("address-token-line"));
|
||||||
|
|
||||||
// USD total for this token only
|
// USD total for this token only
|
||||||
const usdVal = price ? amount * price : 0;
|
const usdVal = price ? amount * price : null;
|
||||||
const usdStr = formatUsd(usdVal);
|
const usdStr = formatUsd(usdVal);
|
||||||
$("address-token-usd-total").innerHTML = usdStr || " ";
|
$("address-token-usd-total").innerHTML = usdStr || " ";
|
||||||
|
|
||||||
@@ -331,7 +333,7 @@ function init(_ctx) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("btn-address-token-back").addEventListener("click", () => {
|
$("btn-address-token-back").addEventListener("click", () => {
|
||||||
ctx.showAddressDetail();
|
goBack();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("btn-address-token-send").addEventListener("click", () => {
|
$("btn-address-token-send").addEventListener("click", () => {
|
||||||
@@ -365,6 +367,7 @@ function init(_ctx) {
|
|||||||
attachCopyHandlers($("send-token-static"));
|
attachCopyHandlers($("send-token-static"));
|
||||||
updateSendBalance();
|
updateSendBalance();
|
||||||
resetSendValidation();
|
resetSendValidation();
|
||||||
|
pushCurrentView();
|
||||||
showView("send");
|
showView("send");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const {
|
|||||||
escapeHtml,
|
escapeHtml,
|
||||||
renderAddressHtml,
|
renderAddressHtml,
|
||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
|
goBack,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, currentNetwork } = require("../../shared/state");
|
const { state, currentNetwork } = require("../../shared/state");
|
||||||
const { getSignerForAddress } = require("../../shared/wallet");
|
const { getSignerForAddress } = require("../../shared/wallet");
|
||||||
@@ -355,7 +356,7 @@ function init(ctx) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("btn-confirm-back").addEventListener("click", () => {
|
$("btn-confirm-back").addEventListener("click", () => {
|
||||||
showView("send");
|
goBack();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { $, showView, showFlash } = require("./helpers");
|
const { $, showView, showFlash, goBack, clearViewStack } = require("./helpers");
|
||||||
const { state, saveState } = require("../../shared/state");
|
const { state, saveState } = require("../../shared/state");
|
||||||
const { decryptWithPassword } = require("../../shared/vault");
|
const { decryptWithPassword } = require("../../shared/vault");
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ function init(_ctx) {
|
|||||||
|
|
||||||
$("btn-delete-wallet-back").addEventListener("click", () => {
|
$("btn-delete-wallet-back").addEventListener("click", () => {
|
||||||
deleteWalletIndex = null;
|
deleteWalletIndex = null;
|
||||||
ctx.showSettingsView();
|
goBack();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("btn-delete-wallet-confirm").addEventListener("click", async () => {
|
$("btn-delete-wallet-confirm").addEventListener("click", async () => {
|
||||||
@@ -77,6 +77,7 @@ function init(_ctx) {
|
|||||||
state.selectedWallet = null;
|
state.selectedWallet = null;
|
||||||
state.selectedAddress = null;
|
state.selectedAddress = null;
|
||||||
state.activeAddress = null;
|
state.activeAddress = null;
|
||||||
|
clearViewStack();
|
||||||
await saveState();
|
await saveState();
|
||||||
showView("welcome");
|
showView("welcome");
|
||||||
} else {
|
} else {
|
||||||
@@ -86,8 +87,14 @@ function init(_ctx) {
|
|||||||
state.activeAddress =
|
state.activeAddress =
|
||||||
state.wallets[0].addresses[0]?.address || null;
|
state.wallets[0].addresses[0]?.address || null;
|
||||||
await saveState();
|
await saveState();
|
||||||
|
// Reset stack to [main] so Settings back goes home.
|
||||||
|
// Use require() lazily to avoid circular dependency
|
||||||
|
// (settings.js requires deleteWallet.js).
|
||||||
|
clearViewStack();
|
||||||
|
state.viewStack.push("main");
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
ctx.showSettingsView();
|
const settings = require("./settings");
|
||||||
|
settings.show();
|
||||||
showFlash("Wallet deleted.");
|
showFlash("Wallet deleted.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -59,13 +59,58 @@ function showView(name) {
|
|||||||
clearFlash();
|
clearFlash();
|
||||||
state.currentView = name;
|
state.currentView = name;
|
||||||
saveState();
|
saveState();
|
||||||
if (DEBUG) {
|
const net = currentNetwork();
|
||||||
|
if (DEBUG || net.isTestnet) {
|
||||||
const banner = document.getElementById("debug-banner");
|
const banner = document.getElementById("debug-banner");
|
||||||
if (banner) {
|
if (banner) {
|
||||||
|
if (DEBUG && net.isTestnet) {
|
||||||
|
banner.textContent =
|
||||||
|
"DEBUG / INSECURE [TESTNET] (" + name + ")";
|
||||||
|
} else if (net.isTestnet) {
|
||||||
|
banner.textContent = "[TESTNET]";
|
||||||
|
} else {
|
||||||
banner.textContent = "DEBUG / INSECURE (" + name + ")";
|
banner.textContent = "DEBUG / INSECURE (" + name + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback to re-render the main/home view when navigating back to it.
|
||||||
|
// Set once by index.js via setRenderMain().
|
||||||
|
let _renderMain = null;
|
||||||
|
|
||||||
|
function setRenderMain(fn) {
|
||||||
|
_renderMain = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the current view onto the navigation stack so goBack() can
|
||||||
|
// return to it. Call this before any forward navigation.
|
||||||
|
function pushCurrentView() {
|
||||||
|
if (state.currentView) {
|
||||||
|
state.viewStack.push(state.currentView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop the navigation stack and show the previous view. If the stack
|
||||||
|
// is empty, fall back to the main (home) view.
|
||||||
|
function goBack() {
|
||||||
|
let target;
|
||||||
|
if (state.viewStack.length > 0) {
|
||||||
|
target = state.viewStack.pop();
|
||||||
|
} else {
|
||||||
|
target = "main";
|
||||||
|
}
|
||||||
|
if (target === "main" && _renderMain) {
|
||||||
|
_renderMain();
|
||||||
|
}
|
||||||
|
showView(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the entire navigation stack (used when resetting to root,
|
||||||
|
// e.g. after adding or deleting a wallet).
|
||||||
|
function clearViewStack() {
|
||||||
|
state.viewStack = [];
|
||||||
|
}
|
||||||
|
|
||||||
let flashTimer = null;
|
let flashTimer = null;
|
||||||
|
|
||||||
@@ -372,6 +417,10 @@ module.exports = {
|
|||||||
showError,
|
showError,
|
||||||
hideError,
|
hideError,
|
||||||
showView,
|
showView,
|
||||||
|
setRenderMain,
|
||||||
|
pushCurrentView,
|
||||||
|
goBack,
|
||||||
|
clearViewStack,
|
||||||
showFlash,
|
showFlash,
|
||||||
flashCopyFeedback,
|
flashCopyFeedback,
|
||||||
balanceLine,
|
balanceLine,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const {
|
|||||||
truncateMiddle,
|
truncateMiddle,
|
||||||
renderAddressHtml,
|
renderAddressHtml,
|
||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
|
pushCurrentView,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, saveState, currentAddress } = require("../../shared/state");
|
const { state, saveState, currentAddress } = require("../../shared/state");
|
||||||
const {
|
const {
|
||||||
@@ -381,6 +382,7 @@ function init(ctx) {
|
|||||||
renderSendTokenSelect(addr);
|
renderSendTokenSelect(addr);
|
||||||
updateSendBalance();
|
updateSendBalance();
|
||||||
resetSendValidation();
|
resetSendValidation();
|
||||||
|
pushCurrentView();
|
||||||
showView("send");
|
showView("send");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const {
|
|||||||
formatAddressHtml,
|
formatAddressHtml,
|
||||||
addressTitle,
|
addressTitle,
|
||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
|
goBack,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, currentAddress, currentNetwork } = require("../../shared/state");
|
const { state, currentAddress, currentNetwork } = require("../../shared/state");
|
||||||
const QRCode = require("qrcode");
|
const QRCode = require("qrcode");
|
||||||
@@ -67,11 +68,7 @@ function init(ctx) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("btn-receive-back").addEventListener("click", () => {
|
$("btn-receive-back").addEventListener("click", () => {
|
||||||
if (state.selectedToken) {
|
goBack();
|
||||||
ctx.showAddressToken();
|
|
||||||
} else {
|
|
||||||
ctx.showAddressDetail();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const {
|
|||||||
escapeHtml,
|
escapeHtml,
|
||||||
renderAddressHtml,
|
renderAddressHtml,
|
||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
|
goBack,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, currentAddress } = require("../../shared/state");
|
const { state, currentAddress } = require("../../shared/state");
|
||||||
let ctx;
|
let ctx;
|
||||||
@@ -250,11 +251,7 @@ function init(_ctx) {
|
|||||||
$("btn-send-back").addEventListener("click", () => {
|
$("btn-send-back").addEventListener("click", () => {
|
||||||
$("send-token").classList.remove("hidden");
|
$("send-token").classList.remove("hidden");
|
||||||
$("send-token-static").classList.add("hidden");
|
$("send-token-static").classList.add("hidden");
|
||||||
if (state.selectedToken) {
|
goBack();
|
||||||
ctx.showAddressToken();
|
|
||||||
} else {
|
|
||||||
ctx.showAddressDetail();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
const { $, showView, showFlash, escapeHtml } = require("./helpers");
|
const {
|
||||||
|
$,
|
||||||
|
showView,
|
||||||
|
showFlash,
|
||||||
|
escapeHtml,
|
||||||
|
goBack,
|
||||||
|
pushCurrentView,
|
||||||
|
} = require("./helpers");
|
||||||
const { applyTheme } = require("../theme");
|
const { applyTheme } = require("../theme");
|
||||||
const { state, saveState, currentNetwork } = require("../../shared/state");
|
const { state, saveState, currentNetwork } = require("../../shared/state");
|
||||||
const { NETWORKS, SUPPORTED_CHAIN_IDS } = require("../../shared/networks");
|
const { NETWORKS, SUPPORTED_CHAIN_IDS } = require("../../shared/networks");
|
||||||
|
const { onChainSwitch } = require("../../shared/chainSwitch");
|
||||||
const { log, debugFetch } = require("../../shared/log");
|
const { log, debugFetch } = require("../../shared/log");
|
||||||
const deleteWallet = require("./deleteWallet");
|
const deleteWallet = require("./deleteWallet");
|
||||||
|
|
||||||
@@ -85,6 +93,7 @@ function renderWalletListSettings() {
|
|||||||
container.querySelectorAll(".btn-delete-wallet").forEach((btn) => {
|
container.querySelectorAll(".btn-delete-wallet").forEach((btn) => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
const idx = parseInt(btn.dataset.idx, 10);
|
const idx = parseInt(btn.dataset.idx, 10);
|
||||||
|
pushCurrentView();
|
||||||
deleteWallet.show(idx);
|
deleteWallet.show(idx);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -220,14 +229,9 @@ function init(ctx) {
|
|||||||
if (networkSelect) {
|
if (networkSelect) {
|
||||||
networkSelect.addEventListener("change", async () => {
|
networkSelect.addEventListener("change", async () => {
|
||||||
const newId = networkSelect.value;
|
const newId = networkSelect.value;
|
||||||
const net = NETWORKS[newId];
|
const net = await onChainSwitch(newId);
|
||||||
if (!net) return;
|
|
||||||
state.networkId = newId;
|
|
||||||
state.rpcUrl = net.defaultRpcUrl;
|
|
||||||
state.blockscoutUrl = net.defaultBlockscoutUrl;
|
|
||||||
$("settings-rpc").value = state.rpcUrl;
|
$("settings-rpc").value = state.rpcUrl;
|
||||||
$("settings-blockscout").value = state.blockscoutUrl;
|
$("settings-blockscout").value = state.blockscoutUrl;
|
||||||
await saveState();
|
|
||||||
showFlash("Switched to " + net.name + ".");
|
showFlash("Switched to " + net.name + ".");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -286,8 +290,7 @@ function init(ctx) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
$("btn-settings-back").addEventListener("click", () => {
|
$("btn-settings-back").addEventListener("click", () => {
|
||||||
ctx.renderWalletList();
|
goBack();
|
||||||
showView("main");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { $, showView, showFlash } = require("./helpers");
|
const { $, showView, showFlash, goBack } = require("./helpers");
|
||||||
const { getTopTokens } = require("../../shared/tokenList");
|
const { getTopTokens } = require("../../shared/tokenList");
|
||||||
const { state, saveState } = require("../../shared/state");
|
const { state, saveState } = require("../../shared/state");
|
||||||
const { lookupTokenInfo } = require("../../shared/balances");
|
const { lookupTokenInfo } = require("../../shared/balances");
|
||||||
@@ -84,7 +84,7 @@ function init(_ctx) {
|
|||||||
ctx = _ctx;
|
ctx = _ctx;
|
||||||
|
|
||||||
$("btn-settings-addtoken-back").addEventListener("click", () => {
|
$("btn-settings-addtoken-back").addEventListener("click", () => {
|
||||||
ctx.showSettingsView();
|
goBack();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("btn-settings-addtoken-select").addEventListener("click", async () => {
|
$("btn-settings-addtoken-select").addEventListener("click", async () => {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const {
|
|||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
copyableHtml,
|
copyableHtml,
|
||||||
etherscanLinkHtml,
|
etherscanLinkHtml,
|
||||||
|
goBack,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { state, currentNetwork } = require("../../shared/state");
|
const { state, currentNetwork } = require("../../shared/state");
|
||||||
const { formatEther, formatUnits } = require("ethers");
|
const { formatEther, formatUnits } = require("ethers");
|
||||||
@@ -350,11 +351,7 @@ async function loadFullTxDetails(txHash, toAddress, isContractCall) {
|
|||||||
function init(_ctx) {
|
function init(_ctx) {
|
||||||
ctx = _ctx;
|
ctx = _ctx;
|
||||||
$("btn-tx-back").addEventListener("click", () => {
|
$("btn-tx-back").addEventListener("click", () => {
|
||||||
if (state.selectedToken) {
|
goBack();
|
||||||
ctx.showAddressToken();
|
|
||||||
} else {
|
|
||||||
ctx.showAddressDetail();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const {
|
|||||||
attachCopyHandlers,
|
attachCopyHandlers,
|
||||||
copyableHtml,
|
copyableHtml,
|
||||||
etherscanLinkHtml,
|
etherscanLinkHtml,
|
||||||
|
clearViewStack,
|
||||||
} = require("./helpers");
|
} = require("./helpers");
|
||||||
const { TOKEN_BY_ADDRESS } = require("../../shared/tokenList");
|
const { TOKEN_BY_ADDRESS } = require("../../shared/tokenList");
|
||||||
const { state, saveState, currentNetwork } = require("../../shared/state");
|
const { state, saveState, currentNetwork } = require("../../shared/state");
|
||||||
@@ -221,10 +222,16 @@ function navigateBack() {
|
|||||||
window.close();
|
window.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// After a completed transaction, reset the navigation stack
|
||||||
|
// and go directly to the address view (token or detail).
|
||||||
|
// Use require() lazily to call show() without the ctx push wrapper.
|
||||||
|
clearViewStack();
|
||||||
|
state.viewStack.push("main");
|
||||||
if (state.selectedToken) {
|
if (state.selectedToken) {
|
||||||
ctx.showAddressToken();
|
state.viewStack.push("address");
|
||||||
|
require("./addressToken").show();
|
||||||
} else {
|
} else {
|
||||||
ctx.showAddressDetail();
|
require("./addressDetail").show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
src/shared/chainSwitch.js
Normal file
57
src/shared/chainSwitch.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Consolidated chain-switch handler.
|
||||||
|
//
|
||||||
|
// Every state change required when the active network changes is
|
||||||
|
// performed here so that callers (settings UI, background
|
||||||
|
// wallet_switchEthereumChain, future chain additions) all go
|
||||||
|
// through a single code path.
|
||||||
|
//
|
||||||
|
// Adding a new chain (e.g. ETC) requires only a new entry in
|
||||||
|
// networks.js — no per-caller wiring is needed.
|
||||||
|
|
||||||
|
const { networkById } = require("./networks");
|
||||||
|
const { clearPrices } = require("./prices");
|
||||||
|
|
||||||
|
// Switch the active chain and reset all chain-specific cached state.
|
||||||
|
// Returns the network configuration object for the new chain.
|
||||||
|
async function onChainSwitch(newNetworkId) {
|
||||||
|
const { state, saveState } = require("./state");
|
||||||
|
|
||||||
|
const net = networkById(newNetworkId);
|
||||||
|
|
||||||
|
// --- core identity ---
|
||||||
|
state.networkId = net.id;
|
||||||
|
state.rpcUrl = net.defaultRpcUrl;
|
||||||
|
state.blockscoutUrl = net.defaultBlockscoutUrl;
|
||||||
|
|
||||||
|
// --- price cache ---
|
||||||
|
// Prices are chain-specific (testnet tokens are worthless,
|
||||||
|
// ETC has different pricing, etc.).
|
||||||
|
clearPrices();
|
||||||
|
|
||||||
|
// --- balance / refresh state ---
|
||||||
|
// Reset last-refresh timestamp so the next polling cycle
|
||||||
|
// triggers an immediate balance refresh on the new chain.
|
||||||
|
state.lastBalanceRefresh = 0;
|
||||||
|
|
||||||
|
// Clear per-address balances and token balances so stale data
|
||||||
|
// from the previous chain is never displayed while the first
|
||||||
|
// refresh on the new chain is in flight.
|
||||||
|
for (const wallet of state.wallets) {
|
||||||
|
for (const addr of wallet.addresses) {
|
||||||
|
addr.balance = "0";
|
||||||
|
addr.tokenBalances = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- chain-specific caches ---
|
||||||
|
// Token holder counts and fraud contract lists are
|
||||||
|
// chain-specific and must not carry over.
|
||||||
|
state.tokenHolderCache = {};
|
||||||
|
state.fraudContracts = [];
|
||||||
|
|
||||||
|
await saveState();
|
||||||
|
|
||||||
|
return net;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { onChainSwitch };
|
||||||
@@ -8,9 +8,13 @@ const prices = {};
|
|||||||
let lastFetchedAt = 0;
|
let lastFetchedAt = 0;
|
||||||
|
|
||||||
async function refreshPrices() {
|
async function refreshPrices() {
|
||||||
// Testnet tokens have no real market value — skip price fetching.
|
// Testnet tokens have no real market value — skip price fetching
|
||||||
|
// and clear any stale mainnet prices so the UI shows no USD values.
|
||||||
const { currentNetwork } = require("./state");
|
const { currentNetwork } = require("./state");
|
||||||
if (currentNetwork().isTestnet) return;
|
if (currentNetwork().isTestnet) {
|
||||||
|
clearPrices();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - lastFetchedAt < PRICE_CACHE_TTL) return;
|
if (now - lastFetchedAt < PRICE_CACHE_TTL) return;
|
||||||
try {
|
try {
|
||||||
@@ -22,7 +26,19 @@ async function refreshPrices() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear all cached prices and reset the fetch timestamp so the
|
||||||
|
// next refreshPrices() call will fetch fresh data.
|
||||||
|
function clearPrices() {
|
||||||
|
for (const key of Object.keys(prices)) {
|
||||||
|
delete prices[key];
|
||||||
|
}
|
||||||
|
lastFetchedAt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the USD price for a symbol, or null on testnet / unknown.
|
||||||
function getPrice(symbol) {
|
function getPrice(symbol) {
|
||||||
|
const { currentNetwork } = require("./state");
|
||||||
|
if (currentNetwork().isTestnet) return null;
|
||||||
return prices[symbol] || null;
|
return prices[symbol] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +56,8 @@ function formatUsd(amount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getAddressValueUsd(addr) {
|
function getAddressValueUsd(addr) {
|
||||||
|
const { currentNetwork } = require("./state");
|
||||||
|
if (currentNetwork().isTestnet) return null;
|
||||||
if (!prices.ETH) return null;
|
if (!prices.ETH) return null;
|
||||||
let total = 0;
|
let total = 0;
|
||||||
const ethBal = parseFloat(addr.balance || "0");
|
const ethBal = parseFloat(addr.balance || "0");
|
||||||
@@ -54,6 +72,8 @@ function getAddressValueUsd(addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWalletValueUsd(wallet) {
|
function getWalletValueUsd(wallet) {
|
||||||
|
const { currentNetwork } = require("./state");
|
||||||
|
if (currentNetwork().isTestnet) return null;
|
||||||
if (!prices.ETH) return null;
|
if (!prices.ETH) return null;
|
||||||
let total = 0;
|
let total = 0;
|
||||||
for (const addr of wallet.addresses) {
|
for (const addr of wallet.addresses) {
|
||||||
@@ -63,6 +83,8 @@ function getWalletValueUsd(wallet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTotalValueUsd(wallets) {
|
function getTotalValueUsd(wallets) {
|
||||||
|
const { currentNetwork } = require("./state");
|
||||||
|
if (currentNetwork().isTestnet) return null;
|
||||||
if (!prices.ETH) return null;
|
if (!prices.ETH) return null;
|
||||||
let total = 0;
|
let total = 0;
|
||||||
for (const wallet of wallets) {
|
for (const wallet of wallets) {
|
||||||
@@ -74,6 +96,7 @@ function getTotalValueUsd(wallets) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
prices,
|
prices,
|
||||||
refreshPrices,
|
refreshPrices,
|
||||||
|
clearPrices,
|
||||||
getPrice,
|
getPrice,
|
||||||
formatUsd,
|
formatUsd,
|
||||||
getAddressValueUsd,
|
getAddressValueUsd,
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const state = {
|
|||||||
selectedAddress: null,
|
selectedAddress: null,
|
||||||
selectedToken: null,
|
selectedToken: null,
|
||||||
viewData: {},
|
viewData: {},
|
||||||
|
viewStack: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return the network configuration for the currently selected network.
|
// Return the network configuration for the currently selected network.
|
||||||
@@ -72,6 +73,7 @@ async function saveState() {
|
|||||||
selectedAddress: state.selectedAddress,
|
selectedAddress: state.selectedAddress,
|
||||||
selectedToken: state.selectedToken,
|
selectedToken: state.selectedToken,
|
||||||
viewData: state.viewData,
|
viewData: state.viewData,
|
||||||
|
viewStack: state.viewStack,
|
||||||
};
|
};
|
||||||
await storageApi.set({ autistmask: persisted });
|
await storageApi.set({ autistmask: persisted });
|
||||||
}
|
}
|
||||||
@@ -133,6 +135,7 @@ async function loadState() {
|
|||||||
saved.selectedAddress !== undefined ? saved.selectedAddress : null;
|
saved.selectedAddress !== undefined ? saved.selectedAddress : null;
|
||||||
state.selectedToken = saved.selectedToken || null;
|
state.selectedToken = saved.selectedToken || null;
|
||||||
state.viewData = saved.viewData || {};
|
state.viewData = saved.viewData || {};
|
||||||
|
state.viewStack = Array.isArray(saved.viewStack) ? saved.viewStack : [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user