Compare commits

..

1 Commits

Author SHA1 Message Date
user
f290298244 feat: expand confirm-tx warnings for contracts, burn addrs, scam list
All checks were successful
check / check (push) Successful in 10s
- Expand scam address list from 7 to 652 addresses using MEW darklist
- Add contract address warning when sending directly to a smart contract
- Add null/burn address warning (0x0000...0000, 0xdead, etc.)
- All warnings use visibility:hidden/visible pattern (no layout shift)
- Scam and burn address checks are synchronous; contract check is async

closes #114
2026-02-28 16:07:38 -08:00
24 changed files with 865 additions and 666 deletions

View File

@@ -437,10 +437,6 @@ transitions.
- **When**: User tapped a transaction row from AddressDetail or AddressToken.
- **Elements**:
- "Transaction" heading, "Back" button
- Type: transaction classification — one of: Native ETH Transfer, ERC-20
Token Transfer, Swap, Token Approval, Contract Call, Contract Creation
- Token contract: shown for ERC-20 transfers — color dot + full contract
address (tap to copy) + etherscan token link
- Status: "Success" or "Failed"
- Time: ISO datetime + relative age in parentheses
- Amount: value + symbol (bold)
@@ -449,11 +445,6 @@ transitions.
- To: blockie + color dot + full address (tap to copy) + etherscan link
- ENS name if available
- Transaction hash: full hash (tap to copy) + etherscan link
- Block: block number (tap to copy) + etherscan block link
- Nonce: transaction nonce (tap to copy)
- Transaction fee: ETH amount (tap to copy)
- Gas price: value in Gwei (tap to copy)
- Gas used: integer (tap to copy)
- **Transitions**:
- "Back" → **AddressToken** (if `selectedToken` set) or **AddressDetail**

View File

@@ -107,8 +107,7 @@
</div>
<div
id="add-wallet-phrase-warning"
class="text-xs mb-2 border border-border border-dashed p-2"
style="visibility: hidden"
class="text-xs mb-2 border border-border border-dashed p-2 hidden"
>
Write these words down and keep them safe. Anyone with
them can take your funds; if you lose them, your wallet
@@ -185,7 +184,7 @@
<!-- active address headline -->
<div
id="total-value"
class="text-2xl font-bold min-h-[2rem] text-fg"
class="text-2xl font-bold min-h-[2rem]"
></div>
<div
id="total-value-sub"
@@ -376,8 +375,7 @@
</p>
<div
id="export-privkey-flash"
class="text-xs mb-2 min-h-[1.25rem]"
style="visibility: hidden"
class="text-xs mb-2 hidden"
></div>
<div id="export-privkey-password-section" class="mb-2">
<label class="block mb-1">Password</label>
@@ -581,17 +579,13 @@
<div class="text-xs text-muted mb-1">Your balance</div>
<div id="confirm-balance" class="text-xs"></div>
</div>
<div id="confirm-fee" class="mb-3" style="visibility: hidden">
<div id="confirm-fee" class="mb-3 hidden">
<div class="text-xs text-muted mb-1">
Estimated network fee
</div>
<div id="confirm-fee-amount" class="text-xs"></div>
</div>
<div
id="confirm-warnings"
class="mb-2"
style="visibility: hidden"
></div>
<div id="confirm-warnings" class="mb-2 hidden"></div>
<div
id="confirm-recipient-warning"
class="mb-2"
@@ -605,10 +599,34 @@
Double-check the address before sending.
</div>
</div>
<div
id="confirm-contract-warning"
class="mb-2"
style="visibility: hidden"
>
<div
class="border border-red-500 border-dashed p-2 text-xs font-bold text-red-500"
>
WARNING: The recipient is a smart contract. Sending ETH
or tokens directly to a contract may result in permanent
loss of funds.
</div>
</div>
<div
id="confirm-burn-warning"
class="mb-2"
style="visibility: hidden"
>
<div
class="border border-red-500 border-dashed p-2 text-xs font-bold text-red-500"
>
WARNING: This is a known null/burn address. Funds sent
here are permanently destroyed and cannot be recovered.
</div>
</div>
<div
id="confirm-errors"
class="mb-2 border border-border border-dashed p-2"
style="visibility: hidden; min-height: 1.25rem"
class="mb-2 border border-border border-dashed p-2 hidden"
></div>
<div class="mb-2">
<label class="block mb-1 text-xs">Password</label>
@@ -621,7 +639,6 @@
<div
id="confirm-tx-password-error"
class="text-xs mb-2 min-h-[1.25rem]"
style="visibility: hidden"
></div>
<button
id="btn-confirm-send"
@@ -736,8 +753,7 @@
</button>
<div
id="receive-erc20-warning"
class="text-xs border border-border border-dashed p-2 mt-3"
style="visibility: hidden"
class="text-xs border border-border border-dashed p-2 mt-3 hidden"
></div>
</div>
@@ -765,8 +781,7 @@
</div>
<div
id="add-token-info"
class="text-xs text-muted mb-2 min-h-[1.25rem]"
style="visibility: hidden"
class="text-xs text-muted mb-2 hidden"
></div>
<div class="mb-2">
<label class="block mb-1 text-xs text-muted"
@@ -824,7 +839,7 @@
<div class="bg-well p-3 mx-1 mb-3">
<h3 class="font-bold mb-1">Display</h3>
<label
class="text-xs flex items-center gap-1 cursor-pointer mb-2"
class="text-xs flex items-center gap-1 cursor-pointer"
>
<input
type="checkbox"
@@ -832,17 +847,6 @@
/>
Show tracked tokens with zero balance
</label>
<div class="text-xs flex items-center gap-1">
<label for="settings-theme">Theme:</label>
<select
id="settings-theme"
class="border border-border p-1 bg-bg text-fg text-xs cursor-pointer"
>
<option value="system">System</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
</div>
<div class="bg-well p-3 mx-1 mb-3">
@@ -924,12 +928,6 @@
/>
<span class="text-xs text-muted">gwei</span>
</div>
<label
class="text-xs flex items-center gap-1 cursor-pointer mb-1"
>
<input type="checkbox" id="settings-utc-timestamps" />
UTC Timestamps
</label>
</div>
<div class="bg-well p-3 mx-1 mb-3">
@@ -965,8 +963,7 @@
</p>
<div
id="delete-wallet-flash"
class="text-xs text-red-500 mb-2 min-h-[1.25rem]"
style="visibility: hidden"
class="text-xs text-red-500 mb-2 hidden"
></div>
<div class="mb-2">
<label class="block mb-1">Password</label>
@@ -1041,8 +1038,7 @@
/>
<div
id="settings-addtoken-info"
class="text-xs text-muted mt-1 min-h-[1.25rem]"
style="visibility: hidden"
class="text-xs text-muted mt-1 hidden"
></div>
<button
id="btn-settings-addtoken-manual"
@@ -1064,134 +1060,60 @@
<h2 id="tx-detail-heading" class="font-bold mb-2">
Transaction
</h2>
<!-- ── Identity ── -->
<div class="tx-detail-group mb-1">
<div class="mb-3">
<div class="text-xs text-muted mb-1">
Transaction hash
</div>
<div
id="tx-detail-hash"
class="text-xs break-all"
></div>
</div>
<div id="tx-detail-type-section" class="mb-3 hidden">
<div class="text-xs text-muted mb-1">Type</div>
<div
id="tx-detail-type"
class="text-xs font-bold"
></div>
</div>
<div class="mb-3">
<div class="text-xs text-muted mb-1">Status</div>
<div id="tx-detail-status" class="text-xs"></div>
</div>
<div class="mb-1">
<div class="text-xs text-muted mb-1">Time</div>
<div id="tx-detail-time" class="text-xs"></div>
</div>
<div id="tx-detail-type-section" class="mb-4 hidden">
<div class="text-xs text-muted mb-1">Type</div>
<div id="tx-detail-type" class="text-xs font-bold"></div>
</div>
<!-- ── Value ── -->
<div class="tx-detail-group mb-1">
<div class="mb-3">
<div class="text-xs text-muted mb-1">Amount</div>
<div id="tx-detail-value" class="text-xs"></div>
</div>
<div class="mb-3 hidden">
<div class="text-xs text-muted mb-1">
Native quantity
</div>
<div id="tx-detail-native" class="text-xs"></div>
</div>
<div class="mb-4">
<div class="text-xs text-muted mb-1">Status</div>
<div id="tx-detail-status" class="text-xs"></div>
</div>
<div class="mb-4">
<div class="text-xs text-muted mb-1">Time</div>
<div id="tx-detail-time" class="text-xs"></div>
</div>
<div class="mb-4">
<div class="text-xs text-muted mb-1">Amount</div>
<div id="tx-detail-value" class="text-xs"></div>
</div>
<div class="mb-4 hidden">
<div class="text-xs text-muted mb-1">Native quantity</div>
<div id="tx-detail-native" class="text-xs"></div>
</div>
<div class="mb-4">
<div class="text-xs text-muted mb-1">From</div>
<div id="tx-detail-from" class="text-xs break-all"></div>
</div>
<div class="mb-4">
<div class="text-xs text-muted mb-1">To</div>
<div id="tx-detail-to" class="text-xs break-all"></div>
</div>
<div id="tx-detail-calldata-section" class="mb-4 hidden">
<div
id="tx-detail-token-contract-section"
class="mb-1 hidden"
id="tx-detail-calldata-well"
class="mb-3 border border-border border-dashed p-2"
>
<div class="text-xs text-muted mb-1">
Token contract
</div>
<div class="text-xs text-muted mb-1">Action</div>
<div
id="tx-detail-token-contract"
class="text-xs break-all"
id="tx-detail-calldata-action"
class="text-xs font-bold mb-2"
></div>
<div
id="tx-detail-calldata-details"
class="text-xs"
></div>
</div>
</div>
<!-- ── Parties ── -->
<div class="tx-detail-group mb-1">
<div class="mb-3">
<div class="text-xs text-muted mb-1">From</div>
<div
id="tx-detail-from"
class="text-xs break-all"
></div>
</div>
<div class="mb-1">
<div class="text-xs text-muted mb-1">To</div>
<div id="tx-detail-to" class="text-xs break-all"></div>
</div>
<div class="mb-4">
<div class="text-xs text-muted mb-1">Transaction hash</div>
<div id="tx-detail-hash" class="text-xs break-all"></div>
</div>
<!-- ── Protocol ── -->
<div id="tx-detail-calldata-section" class="mb-1 hidden">
<div class="tx-detail-group mb-1">
<div
id="tx-detail-calldata-well"
class="border border-border border-dashed p-2"
>
<div class="text-xs text-muted mb-1">Action</div>
<div
id="tx-detail-calldata-action"
class="text-xs font-bold mb-2"
></div>
<div
id="tx-detail-calldata-details"
class="text-xs"
></div>
</div>
</div>
</div>
<!-- ── On-chain details ── -->
<div
id="tx-detail-onchain-group"
class="tx-detail-group mb-1 hidden"
>
<div id="tx-detail-block-section" class="mb-3 hidden">
<div class="text-xs text-muted mb-1">Block</div>
<div id="tx-detail-block" class="text-xs"></div>
</div>
<div id="tx-detail-nonce-section" class="mb-3 hidden">
<div class="text-xs text-muted mb-1">Nonce</div>
<div id="tx-detail-nonce" class="text-xs"></div>
</div>
<div id="tx-detail-fee-section" class="mb-3 hidden">
<div class="text-xs text-muted mb-1">
Transaction fee
</div>
<div id="tx-detail-fee" class="text-xs"></div>
</div>
<div id="tx-detail-gasprice-section" class="mb-3 hidden">
<div class="text-xs text-muted mb-1">Gas price</div>
<div id="tx-detail-gasprice" class="text-xs"></div>
</div>
<div id="tx-detail-gasused-section" class="mb-1 hidden">
<div class="text-xs text-muted mb-1">Gas used</div>
<div id="tx-detail-gasused" class="text-xs"></div>
</div>
</div>
<!-- ── Raw data ── -->
<div id="tx-detail-rawdata-section" class="mb-4 hidden">
<div class="tx-detail-group">
<div class="text-xs text-muted mb-1">Raw data</div>
<div
id="tx-detail-rawdata"
class="text-xs break-all font-mono border border-border border-dashed p-2"
></div>
</div>
<div class="text-xs text-muted mb-1">Raw data</div>
<div
id="tx-detail-rawdata"
class="text-xs break-all font-mono border border-border border-dashed p-2"
></div>
</div>
</div>
@@ -1242,8 +1164,7 @@
</div>
<div
id="approve-tx-error"
class="text-xs mb-2 border border-border border-dashed p-1 min-h-[1.25rem]"
style="visibility: hidden"
class="text-xs mb-2 border border-border border-dashed p-1 min-h-[1.25rem] hidden"
></div>
<div class="flex justify-between">
<button
@@ -1271,10 +1192,8 @@
<div
id="approve-sign-danger-warning"
class="mb-3 p-2 text-xs font-bold"
class="hidden mb-3 p-2 text-xs font-bold"
style="
visibility: hidden;
min-height: 1.25rem;
background: #fee2e2;
color: #991b1b;
border: 2px solid #dc2626;
@@ -1311,8 +1230,7 @@
</div>
<div
id="approve-sign-error"
class="text-xs mb-2 border border-border border-dashed p-1 min-h-[1.25rem]"
style="visibility: hidden"
class="text-xs mb-2 border border-border border-dashed p-1 min-h-[1.25rem] hidden"
></div>
<div class="flex justify-between">
<button

View File

@@ -6,7 +6,6 @@ const { state, saveState, loadState } = require("../shared/state");
const { refreshPrices } = require("../shared/prices");
const { refreshBalances } = require("../shared/balances");
const { $, showView } = require("./views/helpers");
const { applyTheme } = require("./theme");
const home = require("./views/home");
const welcome = require("./views/welcome");
@@ -177,7 +176,6 @@ async function init() {
}
await loadState();
applyTheme(state.theme);
// Auto-default active address
if (

View File

@@ -15,40 +15,7 @@
--color-section: #dddddd;
}
html.dark {
--color-bg: #000000;
--color-fg: #ffffff;
--color-muted: #aaaaaa;
--color-border: #ffffff;
--color-border-light: #444444;
--color-hover: #222222;
--color-well: #1a1a1a;
--color-danger-well: #2a0a0a;
--color-section: #2a2a2a;
}
body {
width: 396px;
overflow-x: hidden;
}
/* Copy-flash feedback: inverts colors then fades back */
.copy-flash-active {
background-color: var(--color-fg) !important;
color: var(--color-bg) !important;
transition: none;
}
.copy-flash-fade {
transition:
background-color 225ms ease-out,
color 225ms ease-out;
}
/* Transaction detail view — visual grouping of related fields */
.tx-detail-group {
border-bottom: 1px solid var(--color-border-light);
padding-bottom: 0.5rem;
margin-bottom: 0.5rem;
padding-top: 0.25rem;
}

View File

@@ -1,33 +0,0 @@
// Theme management: applies light/dark class to <html> based on preference.
let mediaQuery = null;
let mediaHandler = null;
function applyTheme(theme) {
// Clean up previous system listener
if (mediaQuery && mediaHandler) {
mediaQuery.removeEventListener("change", mediaHandler);
mediaHandler = null;
}
if (theme === "dark") {
document.documentElement.classList.add("dark");
} else if (theme === "light") {
document.documentElement.classList.remove("dark");
} else {
// system
mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const update = () => {
if (mediaQuery.matches) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
};
mediaHandler = update;
mediaQuery.addEventListener("change", update);
update();
}
}
module.exports = { applyTheme };

View File

@@ -7,8 +7,7 @@ const { log } = require("../../shared/log");
function show() {
$("add-token-address").value = "";
$("add-token-info").textContent = "";
$("add-token-info").style.visibility = "hidden";
$("add-token-info").classList.add("hidden");
const list = $("common-token-list");
list.innerHTML = getTopTokens(25)
.map(
@@ -46,7 +45,7 @@ function init(ctx) {
}
const infoEl = $("add-token-info");
infoEl.textContent = "Looking up token...";
infoEl.style.visibility = "visible";
infoEl.classList.remove("hidden");
log.debugf("Looking up token contract", contractAddr);
try {
const info = await lookupTokenInfo(contractAddr, state.rpcUrl);
@@ -64,8 +63,7 @@ function init(ctx) {
const detail = e.shortMessage || e.message || String(e);
log.errorf("Token lookup failed for", contractAddr, detail);
showFlash(detail);
infoEl.textContent = "";
infoEl.style.visibility = "hidden";
infoEl.classList.add("hidden");
}
});

View File

@@ -11,25 +11,6 @@ const { encryptWithPassword } = require("../../shared/vault");
const { state, saveState } = require("../../shared/state");
const { scanForAddresses } = require("../../shared/balances");
/**
* Check if an address already exists in ANY wallet (hd, xprv, or key).
* Returns the wallet object if found, or undefined.
*/
function findWalletByAddress(addr) {
const lower = addr.toLowerCase();
return state.wallets.find((w) =>
w.addresses.some((a) => a.address.toLowerCase() === lower),
);
}
/**
* Check if an xpub already exists in any HD-type wallet (hd or xprv).
* Returns the wallet object if found, or undefined.
*/
function findWalletByXpub(xpub) {
return state.wallets.find((w) => w.xpub && w.xpub === xpub);
}
let currentMode = "mnemonic";
const MODES = ["mnemonic", "privkey", "xprv"];
@@ -71,7 +52,7 @@ function show() {
$("import-xprv-key").value = "";
$("add-wallet-password").value = "";
$("add-wallet-password-confirm").value = "";
$("add-wallet-phrase-warning").style.visibility = "hidden";
$("add-wallet-phrase-warning").classList.add("hidden");
switchMode("mnemonic");
showView("add-wallet");
}
@@ -116,18 +97,18 @@ async function importMnemonic(ctx) {
const pw = validatePassword();
if (!pw) return;
const { xpub, firstAddress } = hdWalletFromMnemonic(mnemonic);
const xpubDup = findWalletByXpub(xpub);
if (xpubDup) {
const duplicate = state.wallets.find(
(w) =>
w.type === "hd" &&
w.addresses[0] &&
w.addresses[0].address.toLowerCase() === firstAddress.toLowerCase(),
);
if (duplicate) {
showFlash(
"This recovery phrase is already added (" + xpubDup.name + ").",
"This recovery phrase is already added (" + duplicate.name + ").",
);
return;
}
const addrDup = findWalletByAddress(firstAddress);
if (addrDup) {
showFlash("Address already exists in wallet (" + addrDup.name + ").");
return;
}
const encrypted = await encryptWithPassword(mnemonic, pw);
const walletNum = state.wallets.length + 1;
const wallet = {
@@ -181,10 +162,15 @@ async function importPrivateKey(ctx) {
}
const pw = validatePassword();
if (!pw) return;
const duplicate = findWalletByAddress(addr);
const duplicate = state.wallets.find(
(w) =>
w.type === "key" &&
w.addresses[0] &&
w.addresses[0].address.toLowerCase() === addr.toLowerCase(),
);
if (duplicate) {
showFlash(
"This address already exists in wallet (" + duplicate.name + ").",
"This private key is already added (" + duplicate.name + ").",
);
return;
}
@@ -222,14 +208,14 @@ async function importXprvKey(ctx) {
return;
}
const { xpub, firstAddress } = result;
const xpubDup = findWalletByXpub(xpub);
if (xpubDup) {
showFlash("This key is already added (" + xpubDup.name + ").");
return;
}
const addrDup = findWalletByAddress(firstAddress);
if (addrDup) {
showFlash("Address already exists in wallet (" + addrDup.name + ").");
const duplicate = state.wallets.find(
(w) =>
(w.type === "hd" || w.type === "xprv") &&
w.addresses[0] &&
w.addresses[0].address.toLowerCase() === firstAddress.toLowerCase(),
);
if (duplicate) {
showFlash("This key is already added (" + duplicate.name + ").");
return;
}
const pw = validatePassword();
@@ -281,7 +267,7 @@ function init(ctx) {
// Generate mnemonic
$("btn-generate-phrase").addEventListener("click", () => {
$("wallet-mnemonic").value = generateMnemonic();
$("add-wallet-phrase-warning").style.visibility = "visible";
$("add-wallet-phrase-warning").classList.remove("hidden");
});
// Import / confirm

View File

@@ -2,7 +2,6 @@ const {
$,
showView,
showFlash,
flashCopyFeedback,
balanceLinesForAddress,
addressDotHtml,
addressTitle,
@@ -95,39 +94,18 @@ function show() {
function isoDate(timestamp) {
const d = new Date(timestamp * 1000);
const pad = (n) => String(n).padStart(2, "0");
if (state.utcTimestamps) {
return (
d.getUTCFullYear() +
"-" +
pad(d.getUTCMonth() + 1) +
"-" +
pad(d.getUTCDate()) +
"T" +
pad(d.getUTCHours()) +
":" +
pad(d.getUTCMinutes()) +
":" +
pad(d.getUTCSeconds()) +
"Z"
);
}
const offsetMin = -d.getTimezoneOffset();
const sign = offsetMin >= 0 ? "+" : "-";
const absOff = Math.abs(offsetMin);
const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60);
return (
d.getFullYear() +
"-" +
pad(d.getMonth() + 1) +
"-" +
pad(d.getDate()) +
"T" +
" " +
pad(d.getHours()) +
":" +
pad(d.getMinutes()) +
":" +
pad(d.getSeconds()) +
tzStr
pad(d.getSeconds())
);
}
@@ -263,7 +241,6 @@ function init(_ctx) {
if (addr) {
navigator.clipboard.writeText(addr);
showFlash("Copied!");
flashCopyFeedback($("address-full"));
}
});
@@ -333,8 +310,8 @@ function init(_ctx) {
$("export-privkey-address").textContent = addr.address;
$("export-privkey-address").dataset.full = addr.address;
$("export-privkey-password").value = "";
$("export-privkey-flash").classList.add("hidden");
$("export-privkey-flash").textContent = "";
$("export-privkey-flash").style.visibility = "hidden";
$("export-privkey-password-section").classList.remove("hidden");
$("export-privkey-result").classList.add("hidden");
$("export-privkey-value").textContent = "";
@@ -345,7 +322,7 @@ function init(_ctx) {
const password = $("export-privkey-password").value;
if (!password) {
$("export-privkey-flash").textContent = "Password is required.";
$("export-privkey-flash").style.visibility = "visible";
$("export-privkey-flash").classList.remove("hidden");
return;
}
const btn = $("btn-export-privkey-confirm");
@@ -366,10 +343,10 @@ function init(_ctx) {
$("export-privkey-password-section").classList.add("hidden");
$("export-privkey-value").textContent = privateKey;
$("export-privkey-result").classList.remove("hidden");
$("export-privkey-flash").style.visibility = "hidden";
$("export-privkey-flash").classList.add("hidden");
} catch {
$("export-privkey-flash").textContent = "Wrong password.";
$("export-privkey-flash").style.visibility = "visible";
$("export-privkey-flash").classList.remove("hidden");
} finally {
btn.disabled = false;
btn.classList.remove("text-muted");
@@ -381,7 +358,6 @@ function init(_ctx) {
if (key) {
navigator.clipboard.writeText(key);
showFlash("Copied!");
flashCopyFeedback($("export-privkey-value"));
}
});
@@ -390,7 +366,6 @@ function init(_ctx) {
if (full) {
navigator.clipboard.writeText(full);
showFlash("Copied!");
flashCopyFeedback($("export-privkey-address"));
}
});

View File

@@ -5,7 +5,6 @@ const {
$,
showView,
showFlash,
flashCopyFeedback,
addressDotHtml,
addressTitle,
escapeHtml,
@@ -48,39 +47,18 @@ function etherscanAddressLink(address) {
function isoDate(timestamp) {
const d = new Date(timestamp * 1000);
const pad = (n) => String(n).padStart(2, "0");
if (state.utcTimestamps) {
return (
d.getUTCFullYear() +
"-" +
pad(d.getUTCMonth() + 1) +
"-" +
pad(d.getUTCDate()) +
"T" +
pad(d.getUTCHours()) +
":" +
pad(d.getUTCMinutes()) +
":" +
pad(d.getUTCSeconds()) +
"Z"
);
}
const offsetMin = -d.getTimezoneOffset();
const sign = offsetMin >= 0 ? "+" : "-";
const absOff = Math.abs(offsetMin);
const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60);
return (
d.getFullYear() +
"-" +
pad(d.getMonth() + 1) +
"-" +
pad(d.getDate()) +
"T" +
" " +
pad(d.getHours()) +
":" +
pad(d.getMinutes()) +
":" +
pad(d.getSeconds()) +
tzStr
pad(d.getSeconds())
);
}
@@ -339,7 +317,6 @@ function init(_ctx) {
if (addr) {
navigator.clipboard.writeText(addr);
showFlash("Copied!");
flashCopyFeedback($("address-token-full"));
}
});
@@ -348,7 +325,6 @@ function init(_ctx) {
if (copyEl) {
navigator.clipboard.writeText(copyEl.dataset.copy);
showFlash("Copied!");
flashCopyFeedback(copyEl);
}
});
@@ -397,7 +373,6 @@ function init(_ctx) {
copyEl.addEventListener("click", () => {
navigator.clipboard.writeText(copyEl.dataset.copy);
showFlash("Copied!");
flashCopyFeedback(copyEl);
});
}
updateSendBalance();

View File

@@ -269,7 +269,7 @@ function showTxApproval(details) {
}
$("approve-tx-password").value = "";
hideError("approve-tx-error");
$("approve-tx-error").classList.add("hidden");
showView("approve-tx");
}
@@ -351,10 +351,10 @@ function showSignApproval(details) {
if (warningEl) {
if (sp.dangerWarning) {
warningEl.textContent = sp.dangerWarning;
warningEl.style.visibility = "visible";
warningEl.classList.remove("hidden");
} else {
warningEl.textContent = "";
warningEl.style.visibility = "hidden";
warningEl.classList.add("hidden");
}
}

View File

@@ -15,7 +15,6 @@ const {
hideError,
showView,
showFlash,
flashCopyFeedback,
addressTitle,
addressDotHtml,
escapeHtml,
@@ -26,7 +25,7 @@ const { decryptWithPassword } = require("../../shared/vault");
const { formatUsd, getPrice } = require("../../shared/prices");
const { getProvider } = require("../../shared/balances");
const { isScamAddress } = require("../../shared/scamlist");
const { ERC20_ABI } = require("../../shared/constants");
const { ERC20_ABI, isBurnAddress } = require("../../shared/constants");
const { log } = require("../../shared/log");
const makeBlockie = require("ethereum-blockies-base64");
const txStatus = require("./txStatus");
@@ -118,7 +117,6 @@ function show(txInfo) {
copyEl.onclick = () => {
navigator.clipboard.writeText(copyEl.dataset.copy);
showFlash("Copied!");
flashCopyFeedback(copyEl);
};
}
} else {
@@ -167,13 +165,18 @@ function show(txInfo) {
$("confirm-balance").textContent = valueWithUsd(bal + " ETH", balUsd);
}
// Check for warnings
// Check for warnings (synchronous checks)
const warnings = [];
if (isScamAddress(txInfo.to)) {
warnings.push(
"This address is on a known scam/fraud list. Do not send funds to this address.",
);
}
if (isBurnAddress(txInfo.to)) {
warnings.push(
"This is a known null/burn address. Funds sent here are permanently destroyed and cannot be recovered.",
);
}
if (txInfo.to.toLowerCase() === txInfo.from.toLowerCase()) {
warnings.push("You are sending to your own address.");
}
@@ -186,10 +189,9 @@ function show(txInfo) {
`<div class="border border-border border-dashed p-2 mb-1 text-xs font-bold">WARNING: ${w}</div>`,
)
.join("");
warningsEl.style.visibility = "visible";
warningsEl.classList.remove("hidden");
} else {
warningsEl.innerHTML = "";
warningsEl.style.visibility = "hidden";
warningsEl.classList.add("hidden");
}
// Check for errors
@@ -227,12 +229,11 @@ function show(txInfo) {
errorsEl.innerHTML = errors
.map((e) => `<div class="text-xs">${e}</div>`)
.join("");
errorsEl.style.visibility = "visible";
errorsEl.classList.remove("hidden");
sendBtn.disabled = true;
sendBtn.classList.add("text-muted");
} else {
errorsEl.innerHTML = "";
errorsEl.style.visibility = "hidden";
errorsEl.classList.add("hidden");
sendBtn.disabled = false;
sendBtn.classList.remove("text-muted");
}
@@ -242,13 +243,20 @@ function show(txInfo) {
hideError("confirm-tx-password-error");
// Gas estimate — show placeholder then fetch async
$("confirm-fee").style.visibility = "visible";
$("confirm-fee").classList.remove("hidden");
$("confirm-fee-amount").textContent = "Estimating...";
state.viewData = { pendingTx: txInfo };
showView("confirm-tx");
// Reset recipient warning to hidden (space always reserved, no layout shift)
// Reset async warnings to hidden (space always reserved, no layout shift)
$("confirm-recipient-warning").style.visibility = "hidden";
$("confirm-contract-warning").style.visibility = "hidden";
$("confirm-burn-warning").style.visibility = "hidden";
// Show burn warning via reserved element (in addition to inline warning)
if (isBurnAddress(txInfo.to)) {
$("confirm-burn-warning").style.visibility = "visible";
}
estimateGas(txInfo);
checkRecipientHistory(txInfo);
@@ -295,19 +303,17 @@ async function estimateGas(txInfo) {
}
async function checkRecipientHistory(txInfo) {
const el = $("confirm-recipient-warning");
try {
const provider = getProvider(state.rpcUrl);
// Skip warning for contract addresses — they may legitimately
// have zero outgoing transactions (getTransactionCount returns
// the nonce, i.e. sent-tx count only).
const code = await provider.getCode(txInfo.to);
if (code && code !== "0x") {
// Recipient is a contract — warn the user
$("confirm-contract-warning").style.visibility = "visible";
return;
}
const txCount = await provider.getTransactionCount(txInfo.to);
if (txCount === 0) {
el.style.visibility = "visible";
$("confirm-recipient-warning").style.visibility = "visible";
}
} catch (e) {
log.errorf("recipient history check failed:", e.message);

View File

@@ -12,7 +12,7 @@ function show(walletIdx) {
wallet.name || "Wallet " + (walletIdx + 1);
$("delete-wallet-password").value = "";
$("delete-wallet-flash").textContent = "";
$("delete-wallet-flash").style.visibility = "hidden";
$("delete-wallet-flash").classList.add("hidden");
showView("delete-wallet-confirm");
}
@@ -29,14 +29,14 @@ function init(_ctx) {
if (!pw) {
$("delete-wallet-flash").textContent =
"Please enter your password.";
$("delete-wallet-flash").style.visibility = "visible";
$("delete-wallet-flash").classList.remove("hidden");
return;
}
if (deleteWalletIndex === null) {
$("delete-wallet-flash").textContent =
"No wallet selected for deletion.";
$("delete-wallet-flash").style.visibility = "visible";
$("delete-wallet-flash").classList.remove("hidden");
return;
}
@@ -52,7 +52,7 @@ function init(_ctx) {
await decryptWithPassword(wallet.encryptedSecret, pw);
} catch (_e) {
$("delete-wallet-flash").textContent = "Wrong password.";
$("delete-wallet-flash").style.visibility = "visible";
$("delete-wallet-flash").classList.remove("hidden");
btn.disabled = false;
btn.classList.remove("text-muted");
return;

View File

@@ -40,13 +40,11 @@ function $(id) {
function showError(id, msg) {
const el = $(id);
el.textContent = msg;
el.style.visibility = "visible";
el.classList.remove("hidden");
}
function hideError(id) {
const el = $(id);
el.textContent = "";
el.style.visibility = "hidden";
$(id).classList.add("hidden");
}
function showView(name) {
@@ -228,39 +226,18 @@ function formatAddressHtml(address, ensName, maxLen, title) {
function isoDate(timestamp) {
const d = new Date(timestamp * 1000);
const pad = (n) => String(n).padStart(2, "0");
if (state.utcTimestamps) {
return (
d.getUTCFullYear() +
"-" +
pad(d.getUTCMonth() + 1) +
"-" +
pad(d.getUTCDate()) +
"T" +
pad(d.getUTCHours()) +
":" +
pad(d.getUTCMinutes()) +
":" +
pad(d.getUTCSeconds()) +
"Z"
);
}
const offsetMin = -d.getTimezoneOffset();
const sign = offsetMin >= 0 ? "+" : "-";
const absOff = Math.abs(offsetMin);
const tzStr = sign + pad(Math.floor(absOff / 60)) + ":" + pad(absOff % 60);
return (
d.getFullYear() +
"-" +
pad(d.getMonth() + 1) +
"-" +
pad(d.getDate()) +
"T" +
" " +
pad(d.getHours()) +
":" +
pad(d.getMinutes()) +
":" +
pad(d.getSeconds()) +
tzStr
pad(d.getSeconds())
);
}
@@ -281,26 +258,12 @@ function timeAgo(timestamp) {
return years + " year" + (years !== 1 ? "s" : "") + " ago";
}
function flashCopyFeedback(el) {
if (!el) return;
el.classList.remove("copy-flash-fade");
el.classList.add("copy-flash-active");
setTimeout(() => {
el.classList.remove("copy-flash-active");
el.classList.add("copy-flash-fade");
setTimeout(() => {
el.classList.remove("copy-flash-fade");
}, 275);
}, 75);
}
module.exports = {
$,
showError,
hideError,
showView,
showFlash,
flashCopyFeedback,
balanceLine,
balanceLinesForAddress,
addressColor,

View File

@@ -2,7 +2,6 @@ const {
$,
showView,
showFlash,
flashCopyFeedback,
balanceLinesForAddress,
isoDate,
timeAgo,
@@ -86,10 +85,9 @@ function renderActiveAddress() {
el.innerHTML =
`<span class="underline decoration-dashed cursor-pointer" id="active-addr-copy">${dot}${escapeHtml(addr)}</span>` +
`<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`;
$("active-addr-copy").addEventListener("click", (e) => {
$("active-addr-copy").addEventListener("click", () => {
navigator.clipboard.writeText(addr);
showFlash("Copied!");
flashCopyFeedback(e.currentTarget);
});
} else {
el.textContent = "";

View File

@@ -2,7 +2,6 @@ const {
$,
showView,
showFlash,
flashCopyFeedback,
formatAddressHtml,
addressTitle,
} = require("./helpers");
@@ -53,21 +52,19 @@ function show() {
"This is an ERC-20 token. Only send " +
symbol +
" on the Ethereum network to this address. Sending tokens on other networks will result in permanent loss.";
warningEl.style.visibility = "visible";
warningEl.classList.remove("hidden");
} else {
warningEl.textContent = "";
warningEl.style.visibility = "hidden";
warningEl.classList.add("hidden");
}
showView("receive");
}
function init(ctx) {
$("receive-address-block").addEventListener("click", (e) => {
$("receive-address-block").addEventListener("click", () => {
const addr = $("receive-address-block").dataset.full;
if (addr) {
navigator.clipboard.writeText(addr);
showFlash("Copied!");
flashCopyFeedback(e.currentTarget);
}
});
@@ -76,7 +73,6 @@ function init(ctx) {
if (addr) {
navigator.clipboard.writeText(addr);
showFlash("Copied!");
flashCopyFeedback($("receive-address-block"));
}
});

View File

@@ -1,5 +1,4 @@
const { $, showView, showFlash, escapeHtml } = require("./helpers");
const { applyTheme } = require("../theme");
const { state, saveState } = require("../../shared/state");
const { ETHEREUM_MAINNET_CHAIN_ID } = require("../../shared/constants");
const { log, debugFetch } = require("../../shared/log");
@@ -215,13 +214,6 @@ function init(ctx) {
await saveState();
});
$("settings-theme").value = state.theme;
$("settings-theme").addEventListener("change", async () => {
state.theme = $("settings-theme").value;
await saveState();
applyTheme(state.theme);
});
$("settings-hide-low-holders").checked = state.hideLowHolderTokens;
$("settings-hide-low-holders").addEventListener("change", async () => {
state.hideLowHolderTokens = $("settings-hide-low-holders").checked;
@@ -249,12 +241,6 @@ function init(ctx) {
}
});
$("settings-utc-timestamps").checked = state.utcTimestamps;
$("settings-utc-timestamps").addEventListener("change", async () => {
state.utcTimestamps = $("settings-utc-timestamps").checked;
await saveState();
});
$("btn-main-add-wallet").addEventListener("click", ctx.showAddWalletView);
$("btn-settings-add-token").addEventListener(

View File

@@ -73,8 +73,7 @@ function renderDropdown() {
function show() {
$("settings-addtoken-address").value = "";
$("settings-addtoken-info").textContent = "";
$("settings-addtoken-info").style.visibility = "hidden";
$("settings-addtoken-info").classList.add("hidden");
renderTop10();
renderDropdown();
showView("settings-addtoken");
@@ -130,7 +129,7 @@ function init(_ctx) {
}
const infoEl = $("settings-addtoken-info");
infoEl.textContent = "Looking up token...";
infoEl.style.visibility = "visible";
infoEl.classList.remove("hidden");
log.debugf("Looking up token contract", addr);
try {
const info = await lookupTokenInfo(addr, state.rpcUrl);
@@ -144,8 +143,7 @@ function init(_ctx) {
await saveState();
showFlash("Added " + info.symbol);
$("settings-addtoken-address").value = "";
infoEl.textContent = "";
infoEl.style.visibility = "hidden";
infoEl.classList.add("hidden");
renderTop10();
renderDropdown();
ctx.doRefreshAndRender();
@@ -153,8 +151,7 @@ function init(_ctx) {
const detail = e.shortMessage || e.message || String(e);
log.errorf("Token lookup failed for", addr, detail);
showFlash(detail);
infoEl.textContent = "";
infoEl.style.visibility = "hidden";
infoEl.classList.add("hidden");
}
});
}

View File

@@ -5,7 +5,6 @@ const {
$,
showView,
showFlash,
flashCopyFeedback,
addressDotHtml,
addressTitle,
escapeHtml,
@@ -13,7 +12,6 @@ const {
timeAgo,
} = require("./helpers");
const { state } = require("../../shared/state");
const { formatEther, formatUnits } = require("ethers");
const makeBlockie = require("ethereum-blockies-base64");
const { log, debugFetch } = require("../../shared/log");
const { decodeCalldata } = require("./approval");
@@ -27,25 +25,6 @@ const EXT_ICON =
let ctx;
/**
* Determine a human-readable transaction type string from tx fields.
*/
function getTransactionType(tx) {
if (!tx.to) return "Contract Creation";
if (tx.direction === "contract") {
if (tx.directionLabel === "Swap") return "Swap";
if (
tx.method === "approve" ||
tx.directionLabel === "Approve" ||
tx.method === "setApprovalForAll"
)
return "Token Approval";
return "Contract Call";
}
if (tx.symbol && tx.symbol !== "ETH") return "ERC-20 Token Transfer";
return "Native ETH Transfer";
}
function copyableHtml(text, extraClass) {
const cls =
"underline decoration-dashed cursor-pointer" +
@@ -119,7 +98,6 @@ function show(tx) {
direction: tx.direction || null,
isContractCall: tx.isContractCall || false,
method: tx.method || null,
contractAddress: tx.contractAddress || null,
},
};
render();
@@ -156,56 +134,30 @@ function render() {
nativeEl.parentElement.classList.add("hidden");
}
// Always show transaction type as the first field
// Show type label for contract interactions (Swap, Execute, etc.)
const typeSection = $("tx-detail-type-section");
const typeEl = $("tx-detail-type");
const headingEl = $("tx-detail-heading");
if (typeSection && typeEl) {
typeEl.textContent = getTransactionType(tx);
typeSection.classList.remove("hidden");
if (tx.direction === "contract" && tx.directionLabel) {
if (typeSection) {
typeEl.textContent = tx.directionLabel;
typeSection.classList.remove("hidden");
}
} else {
if (typeSection) typeSection.classList.add("hidden");
}
if (headingEl) headingEl.textContent = "Transaction";
// Token contract address (for ERC-20 transfers)
const tokenContractSection = $("tx-detail-token-contract-section");
const tokenContractEl = $("tx-detail-token-contract");
if (tokenContractSection && tokenContractEl) {
if (tx.contractAddress) {
const dot = addressDotHtml(tx.contractAddress);
const link = `https://etherscan.io/token/${tx.contractAddress}`;
tokenContractEl.innerHTML =
`<div class="flex items-center">${dot}` +
copyableHtml(tx.contractAddress, "break-all") +
etherscanLinkHtml(link) +
`</div>`;
tokenContractSection.classList.remove("hidden");
} else {
tokenContractSection.classList.add("hidden");
}
}
// Hide calldata and raw data sections; always fetch full tx details
// Hide calldata and raw data sections; re-fetch if this is a contract call
const calldataSection = $("tx-detail-calldata-section");
if (calldataSection) calldataSection.classList.add("hidden");
const rawDataSection = $("tx-detail-rawdata-section");
if (rawDataSection) rawDataSection.classList.add("hidden");
// Hide on-chain detail sections (and their group wrapper) until populated
const onchainGroup = $("tx-detail-onchain-group");
if (onchainGroup) onchainGroup.classList.add("hidden");
for (const id of [
"tx-detail-block-section",
"tx-detail-nonce-section",
"tx-detail-fee-section",
"tx-detail-gasprice-section",
"tx-detail-gasused-section",
]) {
const el = $(id);
if (el) el.classList.add("hidden");
if (tx.isContractCall || tx.direction === "contract") {
loadCalldata(tx.hash, tx.to);
}
loadFullTxDetails(tx.hash, tx.to, tx.isContractCall);
const isoStr = isoDate(tx.timestamp);
$("tx-detail-time").innerHTML =
copyableHtml(isoStr) + " (" + escapeHtml(timeAgo(tx.timestamp)) + ")";
@@ -219,113 +171,11 @@ function render() {
el.onclick = () => {
navigator.clipboard.writeText(el.dataset.copy);
showFlash("Copied!");
flashCopyFeedback(el);
};
});
}
function showDetailField(sectionId, contentId, value) {
const section = $(sectionId);
const el = $(contentId);
if (!section || !el) return;
el.innerHTML = copyableHtml(value, "");
section.classList.remove("hidden");
}
function populateOnChainDetails(txData) {
// Block number
if (txData.block_number != null) {
const blockLink = `https://etherscan.io/block/${txData.block_number}`;
const blockSection = $("tx-detail-block-section");
const blockEl = $("tx-detail-block");
if (blockSection && blockEl) {
blockEl.innerHTML =
copyableHtml(String(txData.block_number), "") +
etherscanLinkHtml(blockLink);
blockSection.classList.remove("hidden");
}
}
// Nonce
if (txData.nonce != null) {
showDetailField(
"tx-detail-nonce-section",
"tx-detail-nonce",
String(txData.nonce),
);
}
// Transaction fee
const feeWei = txData.fee?.value || txData.tx_fee;
if (feeWei) {
const feeEth = formatEther(String(feeWei));
showDetailField(
"tx-detail-fee-section",
"tx-detail-fee",
feeEth + " ETH",
);
}
// Gas price
const gasPrice = txData.gas_price;
if (gasPrice) {
const gwei = formatUnits(String(gasPrice), "gwei");
showDetailField(
"tx-detail-gasprice-section",
"tx-detail-gasprice",
gwei + " Gwei",
);
}
// Gas used
const gasUsed = txData.gas_used;
if (gasUsed) {
showDetailField(
"tx-detail-gasused-section",
"tx-detail-gasused",
String(gasUsed),
);
}
// Show the on-chain details group if any child section is visible
const onchainGroup = $("tx-detail-onchain-group");
if (onchainGroup) {
const hasVisible = [
"tx-detail-block-section",
"tx-detail-nonce-section",
"tx-detail-fee-section",
"tx-detail-gasprice-section",
"tx-detail-gasused-section",
].some((id) => {
const el = $(id);
return el && !el.classList.contains("hidden");
});
if (hasVisible) {
onchainGroup.classList.remove("hidden");
}
}
// Bind copy handlers for newly added elements
for (const id of [
"tx-detail-block-section",
"tx-detail-nonce-section",
"tx-detail-fee-section",
"tx-detail-gasprice-section",
"tx-detail-gasused-section",
]) {
const section = $(id);
if (!section) continue;
section.querySelectorAll("[data-copy]").forEach((el) => {
el.onclick = () => {
navigator.clipboard.writeText(el.dataset.copy);
showFlash("Copied!");
flashCopyFeedback(el);
};
});
}
}
async function loadFullTxDetails(txHash, toAddress, isContractCall) {
async function loadCalldata(txHash, toAddress) {
const section = $("tx-detail-calldata-section");
const actionEl = $("tx-detail-calldata-action");
const detailsEl = $("tx-detail-calldata-details");
@@ -340,10 +190,6 @@ async function loadFullTxDetails(txHash, toAddress, isContractCall) {
);
if (!resp.ok) return;
const txData = await resp.json();
// Populate on-chain detail fields (block, nonce, gas, fee)
populateOnChainDetails(txData);
const inputData = txData.raw_input || txData.input || null;
if (!inputData || inputData === "0x") return;
@@ -402,7 +248,6 @@ async function loadFullTxDetails(txHash, toAddress, isContractCall) {
el.onclick = () => {
navigator.clipboard.writeText(el.dataset.copy);
showFlash("Copied!");
flashCopyFeedback(el);
};
});
}

View File

@@ -4,7 +4,6 @@ const {
$,
showView,
showFlash,
flashCopyFeedback,
addressDotHtml,
addressTitle,
escapeHtml,
@@ -78,7 +77,6 @@ function attachCopyHandlers(viewId) {
el.onclick = () => {
navigator.clipboard.writeText(el.dataset.copy);
showFlash("Copied!");
flashCopyFeedback(el);
};
});
}

View File

@@ -20,6 +20,19 @@ const ERC20_ABI = [
"function approve(address spender, uint256 amount) returns (bool)",
];
// Known null/burn addresses that permanently destroy funds.
const BURN_ADDRESSES = new Set([
"0x0000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000001",
"0x000000000000000000000000000000000000dead",
"0xdead000000000000000000000000000000000000",
"0x00000000000000000000000000000000deadbeef",
]);
function isBurnAddress(address) {
return BURN_ADDRESSES.has(address.toLowerCase());
}
module.exports = {
DEBUG,
DEBUG_MNEMONIC,
@@ -28,4 +41,6 @@ module.exports = {
DEFAULT_BLOCKSCOUT_URL,
BIP44_ETH_PATH,
ERC20_ABI,
BURN_ADDRESSES,
isBurnAddress,
};

View File

@@ -8,23 +8,666 @@
// and does not enforce jurisdiction-specific sanctions.
//
// Sources:
// - MyEtherWallet/ethereum-lists addresses-darklist.json (MIT license)
// https://github.com/MyEtherWallet/ethereum-lists
// - Known wallet-drainer contracts identified via Etherscan labels,
// MistTrack alerts, and community incident reports (e.g. address-
// poisoning campaigns, phishing kit deployments).
// MistTrack alerts, and community incident reports.
//
// All addresses lowercased for comparison.
const SCAM_ADDRESSES = new Set([
// Fake Uniswap phishing
"0x0000000000000000000000000000000000000001",
// Common address poisoning targets
"0x0000000000000000000000000000000000000000",
// Known drainer contracts (examples — expand as needed)
"0x00000000a991c429ee2ec6df19d40fe0c80088b8",
"0xae0ee0a63a2ce6baeeffe56e7714fb4efe48d419",
"0x3ee18b2214aff97000d974cf647e7c347e8fa585",
"0x55fe002aeff02f77364de339a1292923a15844b8",
"0x7f268357a8c2552623316e2562d90e642bb538e5",
"0x0059b14e35dab1b4eee1e2926c7a5660da66f747",
"0x008f3db10374099a11ec263415cb88c952abeedc",
"0x00e01a648ff41346cdeb873182383333d2184dd1",
"0x0153775362c3071c1860e8dbfd53ccc82fa226f5",
"0x02673ebfbfaed5891f6b14248b0e2753a8758fbf",
"0x02f4a464eebb46a50dd087074d7a2cf3f5a3598b",
"0x0310c4385311d225cbcaca26fefc9ab45d3eba3e",
"0x0387cfc283ffcf25a4a6a61e832545bbeb7c8fda",
"0x03f034fb47965123ea4148e3147e2cfdc5b1f7a5",
"0x052f585fa599bfb4bed290ff30c057627ccd8059",
"0x05dd62c007cde143b402fa5da3937c40c70b4b14",
"0x05e3650bfc907cc1b3c774f0c053c8b990ce1a4b",
"0x05e5061133f752ea565f40f1e755d9604c313bc9",
"0x069a6761e9897ced3f5d13f4aa9ad745e526c913",
"0x071cf579507dd0661845694f709e68cf7ea506a4",
"0x07425527076b4f73b5616716dda54fb14cf1219e",
"0x0755e59d505c9b473aec50b476df3ea2045739aa",
"0x07a065bbc565002740caccb22b452fdd029ba988",
"0x07faacbe52a8b5d3d22f49206bdd9d9c1f71991d",
"0x08389b19ad52f0d983609ab785b3a43a0e90355f",
"0x09750ad360fdb7a2ee23669c4503c974d86d8694",
"0x09cc69f6b484cada8e152d2002adfca496d723f3",
"0x09faf25e57abd0a401bb5a2341d7f926c389f8d1",
"0x0a00fb2e074ffaaf6c561164c6458b5c448120fc",
"0x0a4392f623faf54de72cbf6f567e1a7808251cd3",
"0x0a78fa6dab70385dd46fbdd20da5d868eb43f904",
"0x0a9f58ee19a7131ed031ea66a032c05c7efe965a",
"0x0ae775637e63fa95855246fd82e96802d05883fc",
"0x0af16719353eddc122eaf834638406c499197860",
"0x0b1f025593acd090a6c9b754e6e46acf88e89646",
"0x0b21fd643aaaf5af800af67e14ebf4886be20164",
"0x0b591a58137f154f59aea1284166d6a7b49821fd",
"0x0b7f284d74f549731499c44aed2a10adcc9e9cc0",
"0x0bdd81d2a676166f2a28691e8af64ebeeca67fae",
"0x0c8b0b3e963becf16cda410fe1eb4b5d64022c94",
"0x0c9d1b3ca852c67bbeabeeb51e9482a845d56868",
"0x0d502f90c0a740b9c069ddec1c436d483ed7ab8e",
"0x0d87ca679a7ab95598b34eb54dafc356b0373388",
"0x0d9786138578fcdcce04435592545ef08422d755",
"0x0d979f9ffdd579d67c29531cccba568d2172d0b0",
"0x0dac7a548551dd2e0f7293005f14f9261d7e0196",
"0x0dc04198cd942b55a7d63f5c3891122b6fa277ea",
"0x0dcc89f3091d3ef66e00fb595ed0fafef4434192",
"0x0dea8a35f4e39cdf65d3cc11f10612e9e0278b60",
"0x0e04298d9eaffaf538fe04b4cd525b6d1b64c3ca",
"0x0ef2724a6d8be9f72f3d35b62e1e8a37ceaf721e",
"0x0f7a4c36b5ee28e581504e8dfa62fa83a11ca7a9",
"0x101c55c14bbde713d4c718b67256133bac8126c6",
"0x1086feeb031e45e8fcd770662aa8302d7c23ba1f",
"0x116574a18659f0063150e0064e4ea00349a1492c",
"0x119240ef17333e8dcb19a0dd4f5fc848981b0ff4",
"0x122c7f492c51c247e293b0f996fa63de61474959",
"0x12736c6b02381c3c50e41db3a69d7bb651a77d57",
"0x1363077895b20ae90f80794ce4e575559517d033",
"0x14130d36b36887620c37a404f859d11e293ec06a",
"0x143494359bbacc878308075c1c9fa05fcda96651",
"0x1447b1cb205158d98fdbd312b37ed9dd1481fb62",
"0x1494403137159bb0dc545d11963fdf797ea1ecab",
"0x15577e328dd6e17b7721c9ca4d00610d3ce3762f",
"0x15847dcd428033c2cc2cb70edf2cbd6c84afc146",
"0x15a99893dc47bfae5b06e9b4c8941aaca60f8af5",
"0x15f4a5d5bbc071fc20bd60b9b7d81aaa8a1142ab",
"0x16112015d50fac2d084e096feea0863800517f94",
"0x169b6c7ed548e45ba9d87b2d32ff27e2d00051ef",
"0x16b9c4333559256ca7d87b28c6fb2e8415b4711f",
"0x175e0d5f3757b066a6f0cf7634b45f3085cb490b",
"0x175f6dbf87215a6da7107977b4680bc3db31b2de",
"0x17bd6f815fb71a77dc20e12177c4d763a3f67632",
"0x181c71726f12ce2514e8b93019eb22645a79f966",
"0x18345118bd04c405b4d74941563a21b5a2bf06b7",
"0x18e81898ac31a43850fb8d0224477349de6d8d9a",
"0x18ec1d727320cbed7c0c63cd655ade997b292c5e",
"0x18f3489d959fd0e3fac366646e08f9aeabea4d75",
"0x1982b0e96a0375e5b570e8e466d423a37af34b43",
"0x199eee5b6ca1aaf030c77f0b5c50e39908fd2072",
"0x19fb7020381fe9197bf841d5034d5467fe5a3c93",
"0x1a2c53d132ff8a6e26f6b32c0abdc179b94f0721",
"0x1aef1e14108786e0b049f37758529d866cc3c7e6",
"0x1b42bf30cd988ad20500ce47cce52098576ec2a2",
"0x1c0dffa23f342a62ace37bd4eede6180c40ccbec",
"0x1c0e294310091654beac3d191089d3c376be123f",
"0x1c2fb401d3dc4fd8131ccfc4589f281d58921402",
"0x1c39d6e0278d1a28ce21dbd73826559b010224e5",
"0x1c50139c266559b29d7cb27635e0da13bef76a09",
"0x1ccc9b2769741ab0e1620721df7cf8ff1d70716b",
"0x1ccd65d573057c388ea96cc8be30c18a6d21185d",
"0x1cea7999fdafe5d156952ebf1816e8aa67a5485e",
"0x1d60606d8a09b5015d773a80b0c660bb8d91809c",
"0x1d64ea27764164debb4e891eb04d524f42904b08",
"0x1d82886f997d0b124dfab342558bc463299c8a6b",
"0x1de23f02e185fdc4f6bacc98d6c6419370e38538",
"0x1dea82c5628ae89572a153a556312832ee33fda9",
"0x1e12436889e2459202437077549e35b56f1299cb",
"0x1e3c07ce10973fcaebc81468af1d3f390d2a4c71",
"0x1e67f055367bcbce045e1211f09355eb7411236f",
"0x1e75f03ff928bca6c1692179679afc0223d7824f",
"0x1e80dad60d19fb8159af3f440a8ceaa0e5581847",
"0x2056d2e97cc9ad78cf527b382495a8b9704ce011",
"0x21918461c6aeca5eaec825b4746d64a0d4028df6",
"0x22374a2e9e19bcea56e64f9de3329670cd1a458c",
"0x2268751eafc860781074d25f4bd10ded480310b9",
"0x22764de8f82f2d2a90e0ccca4556a2a5114b6461",
"0x22eb0b23e5ad7ef827aff13b2e40ed63e277844e",
"0x2315314c5cadb895c2c9982b847fd6f6a32d6129",
"0x23d56797f64640e96feaf57b7874c897b53ef0e4",
"0x240e125c20a4cc84bd6e7f8d1fd07aff4c06d43d",
"0x247733af8b2bb8e63e3a2a419761c0fd71d40499",
"0x24aa18952e80707dad3ab4c9c97e787f2af337ca",
"0x24c00035b050bec633287c2babfbb9f4fe3c8a83",
"0x259efb559a5d9ed64ccf44bd5188b325dc4738c8",
"0x25dc5894db8ef7f7ff7096ea73b890c1a188d549",
"0x25f8b4acd2c37d7ebae0066fdf9f13f327c0c4eb",
"0x26f7eb488661c33c43089ad61944f3231222b32f",
"0x2789b7b3557f39643198433daebf6b43512be65b",
"0x2804e8b4c04c655376d18ada84d46fc345240e0a",
"0x285cfe87f4ad22228a625dcd1f64b2bc6b27ecff",
"0x2903e9f1d81fac8ccc313b5b496f2db7db6750b6",
"0x29e225d888cf11c5e67613bffd30bcf071eb3d4a",
"0x2a6d8021861f27ab992572d8689017b7a83c989d",
"0x2a7d04018d7295d8069ec9721ee415c4bdc57909",
"0x2b065809f6ec6df32878bcd26711a0e2bcf59c26",
"0x2b3f15a55a68c4c81ae8331c2fe8e90008993f51",
"0x2b5268fde5041f6b1afd77166de4e9ea5d9e967a",
"0x2bcc8aefa2fc9d8e52dad098778bb0a8d08a4aa3",
"0x2d146aa23645950fdefbb23f636a5d1674fe1047",
"0x2d4498a2cf71f5b0946f0ebcfff97ca9d0cd5d04",
"0x2da8703d18afed53b303119e4ff06cf035a9fadb",
"0x2f141d0cd81011436df8fa04bdac01cb6cf51a65",
"0x2f77f4523a13138b472ac6faef624cce68da2c46",
"0x2f80a6700e4bea478ac027f019f04a78a7538d06",
"0x3007a0db9caad557cf4589cfd550c21ce9399b15",
"0x300d4fbdb7ef38488eb9338982686e04ba426715",
"0x3018a3eab048a0a1c15003b383a181f089a039cc",
"0x303f41f4e6a1e1fc9cc2738c722b73b3d9fe54cb",
"0x31537238bb5b237254b629bf226b566c617f182b",
"0x316b3b83c33b20249b61ded124f2620d8e823793",
"0x33bf8bde242216da572d6486d641ae0a18137306",
"0x33f6ee23ee223c4558a95bf4c0e9f5ed21e06319",
"0x33f74739ce6be3b2c38af76f5adb3866cb4784c4",
"0x34743ffa1f93e745668725e44efeb3c50a237796",
"0x35ad1cb9a39de486cebcce3cf51661fd6c848193",
"0x362291375bd3fc2ff0f928184bd6f373f92dea57",
"0x3639b8b8eda816b2cb62b65159a183972a3cf863",
"0x3681828da105fc3c44e212f6c3dc51a0a5a6f5c6",
"0x36db23fd8e0fabb300ba95bfdf4a0f20b05eebfb",
"0x3715b1ed9bbd5e165af97619b5c5e13e39f55504",
"0x371850498d1bd60d67110d1d046ffabc10f957ec",
"0x379ce20c018fb6301c1872c429ec7270ffa4dc5b",
"0x37c81cc9ff71d0f97849a947bf22e8e529b72230",
"0x37c85a7523b1832b4d0380b2f513e88b66e8c850",
"0x37d0909cc7ec38f2d966092ea3607f77bc6bc008",
"0x3810bd434103f1d0618b909ba14e4d2d707d79f7",
"0x3853ba76ec6ae97818e2d0e0839c9eda6c396690",
"0x3884eb0ae2a04bce65b5b0ca9c1bd069cbd52c66",
"0x38b7ca3b78c51364095bc56146d25f55aee0af21",
"0x38c36d3faab64c0770af26cc6ee95d91312bc53c",
"0x394f59e83d88469d3ca3b8da15f31b73f27eada7",
"0x395b53a4ef9736d698e86607047267f7f640fe34",
"0x39a7c26ada0e575d69f05dd6b8086f0ddd99e207",
"0x39ac3008be15cd3e3f23786faa0550c3e0b92ff2",
"0x39d7c7acec5bb282a4fb6e998d3d0099ff7780cf",
"0x39f3e7fa18d342de467ad9d7065a46c8385589f7",
"0x3a3999e6501e2a36dd3c0b8fc2bd165fc4a22e54",
"0x3aa65f17cde339df49afd2f88b3c8495842d5fb0",
"0x3ad44a16451d65d97394ac793b0a2d90c8530499",
"0x3b0009071a0a9983e9aab537fd8c9ad478310aeb",
"0x3b5744c7f340e0d2dcf7a072a4c963b9a43c982b",
"0x3c3b85b2ae785a8cc16c3d4df12cb27c6983dff5",
"0x3cf71871d2f56a6af21bf17ca2652cbc12e8ba1f",
"0x3d11b2189ad65e0e91e2f2b0e07cbc05ddc75b7a",
"0x3de08b35c0e7f255f37a5dace9bd8eb78e99e985",
"0x3dfe9f7af8864df0b7cc2a20430006fd1af8da1a",
"0x3e2b9f6b67fc972419af5e9818281b745a6bf83b",
"0x3f4d77cdabf58f5bfd2ef20784cafd58334542f8",
"0x3fcb2d173389b7cd8079ef8b439dbd92e7e0ae28",
"0x40b942240fec55473388cbf7dbcc5482e64b4367",
"0x4114fb8b1879f61b18f7d2e623569a847a03e15a",
"0x414bca672494b8f078112c52ae258f9e8de1a4a0",
"0x41dfc5797c8043a2f4f7e7abf71291064e091fc6",
"0x42a777e0f24390e7ed461a7af325526e53ac57d9",
"0x42c5459911ae51d1d005cbe39749bd8d8e533c22",
"0x42ff5fff0e369ef6880875efcacb3b7c77974abc",
"0x434e9f6a1ac134fda3e7ceb3fd67c3d9b3518737",
"0x439cb5628e64677c540a8635c86e41d83c1170d5",
"0x43ac6aae3c5dd21855c3aabe3cadb7dfdcf2e5be",
"0x43f030a549f780d1af59a782eaadce4dec7ba183",
"0x4411db7be552b487e3681e890ed14de9a9b24f7b",
"0x4423b4d5174dd6fca701c053bf48faeaaaee59f0",
"0x44a7ff01f7d38c73530c279e19d31527bdcf8c78",
"0x45029af827c652f47b1f678456b2cd009647c8ad",
"0x4541c7745c82df8c10bd4a58e28161534b353064",
"0x4566b8b849dcdbe04f64bf1909db313cf60d6e41",
"0x45750cd6a3bb2206dbeb9cba5e68bf909ac945e3",
"0x474aa9c6f46dc6379c638690fd8f5ade22df5205",
"0x47dc6f08214f891cc910d6d2abfb504c2584f14f",
"0x484aa220812a5ed3d1162686bc8592eb39276348",
"0x4899f3371fb9f8e68a0b639bf1fd75220a089c42",
"0x48d0a447b1d7b9a89112578db4536032d3047b2e",
"0x49311a81f5f5df6c109599ed5d7413d85b8bca18",
"0x495a8b1fdba38d726e514e95b4f1657c69fefa0a",
"0x4999924fd714092fe92ca9a792a8549f798c1dc2",
"0x4a0d27a1044dd871a93275de5109e5f5efc4d46e",
"0x4a500bf6818ba31bb2b1dc90a354ab64ad3301dd",
"0x4bb1c210e3921e0f051ed00e898b9605c902114e",
"0x4bde78ead56a30055d33bf84f52e420b0f6070ba",
"0x4bf722014e54aeab05fcf1519e6e4c0c3f742e43",
"0x4c191dca6cecfa1e55caa3cf178eea52728b13e7",
"0x4c569022016eba68ea10dee702670dc82d2749af",
"0x4cdc1cba0aeb5539f2e0ba158281e67e0e54a9b1",
"0x4da9608f15c504a2b5c46491d9ccf257c5e2ff71",
"0x4de76b3dfd38292ba71cf2465ca3a1d526dcb567",
"0x4e556877e96fdf3bef67c81ef72e20ab29a9aebf",
"0x4e790c545478029d20816a7212c46adde058d63e",
"0x4fafc9291a0112a0ee0928361c02848ff2fdf6bd",
"0x501577cd4865d7d50664a1519a79f1c0e7069154",
"0x504f1e10da987490db4f0d92931497f635e240e8",
"0x508e1101755a4a6ae228b0e18002a816ce3418ce",
"0x514e9ca5406f112aba902b0cba87395b914d861e",
"0x5167052b83f36952d1a9901e0de2b2038c3dd1a3",
"0x51dcd13361c5d921d2c4818e419011301e7be34e",
"0x52005b77fbebf53cfd9527a388f58d0431aaaa3e",
"0x52b34625687d6266a3b98fd5d214828f0670d7ed",
"0x52b7c8857be3853fa68e09d9ba7a9adf075040ea",
"0x545f5e5c54d8ac72af1c7de8c070387b73841a24",
"0x55456cbd1f11298b80a53c896f4b1dc9bc16c731",
"0x55c4dfc965b911afee18a7d2d94a245b867395b6",
"0x560644f0980300b31dcde7b5f78d0d53e375371f",
"0x560833296ef7a7f41b30c1065225fa9b33830c19",
"0x56f0e244ea07e894170731285daf8cf4864e368e",
"0x570ac27d1048dcba1a4c58fe80109b9cf9b0e2ef",
"0x57b818a1070373e21fcedf48d4368e1703c75852",
"0x57d02550f47dd932d2fb84e0aa883c9d8d53c313",
"0x57d4e4ea0a207074d7e45fe60c939d2f4d3ed06b",
"0x57dcf20135b0cb9d167e8ebfe13b84bfd67645ed",
"0x581cc257051a34972641d008c3915a75771be274",
"0x58a166f9a6ff996e2349eea90d42cc198529a037",
"0x5b67a30108e1a5f3d5a809d57e76cd16fdfde7a7",
"0x5b6d3d66e18dfd31ed9a753c406963c401f356c0",
"0x5ba53eb789c7f017fe3cd8027fb01c5eeb81f697",
"0x5bc989f38ec9b913b0fbd8b702858214d428a1bd",
"0x5d1bcbde56db05bead0ff7c87c9dc85baf98ab32",
"0x5d3f32f4b2e99fb79d2f6a1cbf3aa7390f8fc751",
"0x5d82db63cf0c54d47006d416bdc7dab09ea2f3f1",
"0x5d840db230c397acc22ab28053d1a1ff7f14583e",
"0x5ddd20ac4bacff3f148af4c8c24194a1c102cfe5",
"0x5e0f56a3e977318b69303c12369b2a797dfa2b0b",
"0x5e5d6f6706420f932d796c0f28a83b26dabcfd8d",
"0x5e6dcab4f8edbfc1537b804803d831e5f42d8f3b",
"0x5efb7d2ab258b18c8166b0c74fc4117716e52515",
"0x5f58fe612db5b3e97faf4a2e8f38b4ae295288aa",
"0x5f6ab160206bc6a5d663ca5d0f237d82c572272b",
"0x602026bd56bf9ca9d86926669e4353035aca2a88",
"0x6122ee683dbee50fd432fe5bdd8ac055aa3d42b1",
"0x619f18aec5f8eef483fbfe654a269f1186dda915",
"0x62404bfb3e1c464f17e3af0c139e39d0703ae4a1",
"0x629b2aa7fdda8f7527d6f8ea742c1d5774e567ea",
"0x62e8aaffb7568cec94b0e15e7b4d859302d65ee9",
"0x62ee4a3c690c9660762c52bf1a232365c86c879b",
"0x636e5292882e2342aaf82d651322aea09a3e5392",
"0x63cfa80bbbee233a4257857dcdc9d78cbc8efe37",
"0x64c5971dd27ff063ee4bc5e4c231febd9fc228cd",
"0x64f3502c4b6b7e7f106e7ab1f175a4e2c2fde45f",
"0x6506827d91659e871fc7267160ba1c3407195270",
"0x65af8c81291fb60a20235d9e4de80851f8f845d2",
"0x660d30d1fbafdc88a1951370cbe2b3094dbf224b",
"0x6642cec9d02e4e669103a3ed4f3505f437b8fe73",
"0x6678bb2a94097f5046ff179a9dcec5be0985745b",
"0x66817272d39da7fd4c552f430fc0b694e357c157",
"0x66e88b42922916d01b9aec71dd334d7fa5fb526d",
"0x6721b09f4fb6fe69a14add6d1f2c1b94562e7801",
"0x6834d3096793d0731348f1581ae1b20c9b79e26a",
"0x685445fe51b31790538890a6468851afbf7a0519",
"0x68a91eba9c82b475091077e16357bb2dded479f4",
"0x68b0e0db7918c0211ea1fb78292a879839137dd0",
"0x68d41d0c11bf5536d6d8c186ccfaf3de1a022d67",
"0x693d47204555361d4d51311f97102a1507559802",
"0x6973c24fadd0bcab33ee5cb325c8a70e81c67c20",
"0x6a14e385fff2f21abe425a07ce29842b7037a80d",
"0x6a164122d5cf7c840d26e829b46dcc4ed6c0ae48",
"0x6af114154a8850b1c54d48bd9103bdcdb420611b",
"0x6b5c35d525d2d94c68ab5c5af9729092fc8771dd",
"0x6cf481c7090d88ca6f1fa3c9ffd0911ef5359808",
"0x6d0b90ea2951ca5ad3abdb606e4146e9765f1ee4",
"0x6d64486a5c99139b53239297e0b653d196d05777",
"0x6d8a1bc0ae09ae5cfd4a7a2a59194598734ceea5",
"0x6ddfef85ecf643628254e5af7064e05b3c6b221e",
"0x6e3e57e536aab05950774945d9cdbf506865c52f",
"0x6fcf42fcd9c6a54b64d1b52083700142952a2805",
"0x702d965332e3082dc976c3b4013cfe0e2c540bcb",
"0x703f0116bf2ef4c80efac751a319f097fd2dfe6c",
"0x7098c7113c2ecaf48baad07e598c74c2689a5795",
"0x71108be9c232722dd6839a6fbad171cf44d3ffaf",
"0x717d2041c516573a0e27f552cdb6c5c3ec7b4e09",
"0x71b7767a9464ea1ad82db434f06d9330d34a9fe5",
"0x72a66bba491f0c696c4c4e3154d07b241d371944",
"0x72b59c7ff61130f4923e2da6af27d562dd64191c",
"0x738a4daae8d4a640172fac4f45b41707bd2197be",
"0x73bb594368ddb778972c6f30b41437c897419c37",
"0x74104e8c7f546c7398d198f6678310ce9d1b8814",
"0x75dbe6e21e38a05a82ad64281a9e28a627619273",
"0x7613a475b7fe61775f579bc148300c8171eccae9",
"0x76c88cd1ec2442c4f929b0f87280be006d7ba725",
"0x774148e22f021972bfe082e1548e5d9dc6e1d76e",
"0x7822b7b190604eaeb4dcaabd2dd5e1daafcb9d46",
"0x784205acd9423a2044d09e41642a71e087162861",
"0x785a751b2e36b2b0fdb80100b7ca14889a768a22",
"0x788431a9fed9fae11871353cd6e92f3b724e7d63",
"0x79a9e902cfbe87fa5065905293992c15de7f6095",
"0x7a18cfc4a36e48163034e51a9ed19d4bdea05f6f",
"0x7b3a132ad35b6138f2dd148bfac2e790e4869723",
"0x7ba85e2e55c7598378fe927ad2aef2dac6c45218",
"0x7bb386c33486fe345168d0af94bef03897e16022",
"0x7c203685291149d5dad4308781f42a6b945df4e1",
"0x7c57981af4cee87567774b53f9da1bfceb8b944b",
"0x7cbc4e0d168744912c5afc081499145bbdc51e69",
"0x7da6955457b72fdd0e80709d704520ff85d79e39",
"0x7dc66f5b59b34df403b96fec7e749703c157ebf8",
"0x7dcfd020ad161fbd4c45894ab352582018ea9197",
"0x7e160f5cf87e44cbe6b1337bb883f453445391de",
"0x7e3d7d8e31b3f472fcd3c1552a9f009131c50c6c",
"0x7eb28833082eb28fd423ab30df20afde98c47dbf",
"0x7f2b4801c338a7b3cc322bfaf151afea8708e8e0",
"0x7f85a82a2da50540412f6e526f1d00a0690a77b8",
"0x7f85d875eaf39525004e8ac2bc1e83786d7dfd77",
"0x7fbf068316c5806a62d549441ae82b00fdb10de1",
"0x801d03f4d053242afbc2949c141bab4270ca6707",
"0x8202b590eca4662446102b3a97e3536aac8ab941",
"0x82334551e71b48387a122ebb256b188bf5868f44",
"0x8369f6723c4792b41d4af4190662269431ee5b0b",
"0x8374aa52bf36eabca06e33c4163a081f53e39aa3",
"0x8444173280e5fdc0525494638123b014cffcc521",
"0x854b62b692ac8411c3a45463c44cbdabac2d6913",
"0x85704b506e10b10d714ead28cde947baa58f0ea4",
"0x858457daa7e087ad74cdeeceab8419079bc2ca03",
"0x8645ec394f7af95316639dce6f99c01476b0d888",
"0x8651e8e218597e781e9a6d8ceb53ec5a236ae75b",
"0x8676f2dfd06ec780ea3f8fba65a29110a56dd830",
"0x86d38dbde80fe6a947565967f41cf958cc271548",
"0x86ddf5b305b9081fb5208e903edeb013510997cd",
"0x86e00684ea92cb87f40d74c5ebde97ef9a17753e",
"0x878cce43a3b9d8a872a33d2d6ccd561e033707f4",
"0x88436c2a2f427f2aa641dfd8c6763facf2bad7bf",
"0x88698a2ff09506596a88a1af9d103a420ba46a3e",
"0x893676c4a94f6ecdc33a18438bbdcfe9e6c68a9a",
"0x897757cba67eadb66d40e45c6748e27d0e67b52a",
"0x89c98cc6d9917b615257e5704e83906402f0f91f",
"0x89e5124b5162a0ceec512e8ba97cd5e6f065fd13",
"0x8a46aa04725b2e2029bdcb924d671fd6a9c11dab",
"0x8a6fcf189437928a6a07f6899b675d11f3d3f778",
"0x8abe0a9b8a1c8a003354e61f3ed8befdeb7d2cec",
"0x8b3b831d767fad1ab0ad52a38777acb98630f8a7",
"0x8c38a4e5104a2925d9e23d5f269afd1c2e09b8f9",
"0x8c9d299cc3c2111499416cf4e3677328486cfac2",
"0x8cf893f11f6037b14f0f827b9ed6aeef38931fb4",
"0x8dc2c980d25b586c5c110ec3c477e985b9acad8b",
"0x8dcdb7fc9f541825ba57fb47d5204317fc609590",
"0x8e6be4cc78557cf3087b15963bf7139b0154e0e0",
"0x8ee2ba41ef59b5773e5909451e0b8bad4e59a81b",
"0x8f0bca6b1b2b522082d46f3eec31340f113b029a",
"0x902d0d5bb6a34307bbd1da33e61ae1654b6cbe1e",
"0x905475cbca398e8fee6e35bcb2f530a3474b78ce",
"0x90a16ebcf1bc0da0347134f707da93c40bb8a4c5",
"0x915c95415d3449212fd0991ccf5eb42864118ec9",
"0x91619a3456fdbac90bd93f51f2917981f0ed3097",
"0x91ef0754d270e67f2ca92595f5a1c0459ec7df89",
"0x920adc9a060cda345fdec2fdebad6ebf38edf83d",
"0x9327cb34984c3992ec1ea0eae98ccf80a74f95b9",
"0x93574d7c405717d80520944ee1cb72d327b01df5",
"0x93aebe50a59dfd4c1f7ed56f5177c6ced54a33af",
"0x93d8d5bb1f1e4822f3c614d9a09e7c5e3fabc13a",
"0x943db403251105195f56ce69c7c48a6320475ab1",
"0x9441a81fb67f44ce7b31c311317e136e7067f8ca",
"0x944a3f5641d6faf7a6abde4ec31e40d0929955ed",
"0x94763846b1fcb75fccfcb51a9636ddc26af281b1",
"0x94b40ecc7b38f5a9316b03dac4fc663e4d10cd15",
"0x94d55b6f8b53903026399ab45294140eafbfbf44",
"0x9546748f47a77408dfbff0dc22505b2e9a75fda4",
"0x955427a36b5c92edee90a1448bfc7e854e9caef5",
"0x956729a9e9b2ff42a30c8bd8fbe380b2c714825b",
"0x983bda798a24720bb4fe3dba287ec352e7b440fc",
"0x99f93d05059f074e893ab369f71adb4569a3da12",
"0x9a95f2ca24d1dcb01048e0623f63f9d174237deb",
"0x9aa722c9b6e63325797e1b2f8ff6873e44dd17c1",
"0x9b521529d04329c2b0a9f9b7db18ed775c6ca8fc",
"0x9beb086842a6c61ad929995f7060e3ee2c4a94ce",
"0x9cdfc5b0aca4527a0916412e9dbd6ad85556a494",
"0x9d0b8447f16d6bc1de2b52d63e9c487bd778a91d",
"0x9e009c57b36ac7e3382697b4c9d3e7092a1cdb42",
"0x9eb041af96d44583110565abec79aedf22eb60b5",
"0xa05554fa940043c7ba191b16cbbe404c3f898f48",
"0xa0d67bf1ae91b6b705abd4f695cc13edaacd0d02",
"0xa0db4acb24e92167341320fcd882bbfb641cd12d",
"0xa153a6ef80fb5d60de18688bdc82684d48fc8de1",
"0xa1b70dc70fb74767cd380985cf93c4fb132fc4f7",
"0xa2896b45f8bd313b777f1027724da1b5f2205a58",
"0xa2a3a8e4a1877da1c913df7591ac0d72f27d4510",
"0xa2a6ceadcb7db611f618ebabfe6de5763953b7c7",
"0xa35e3534917e471540345733b8f23e7bad54681b",
"0xa3e52a23060ccdfe186822b52bf60a07451e094e",
"0xa46311dc4d3bc52b66d45e74682042bb143a9aa3",
"0xa490730ce2a97ec9f81f08fdb0eede36d6319bbd",
"0xa4fcbc4a0508b11d5ef34d41d72020a5def1c712",
"0xa5797ea738abf85db7b3f2e04a4a40a5180044a8",
"0xa5b254aea2e59ab3ce3bec470fe1882403c41be0",
"0xa62b5f23f4b3ddc20f879c3ff58d4b1ef2952d97",
"0xa6b60dd3be491aafe5aba8622b35c0ead608d3fd",
"0xa6ba0996684fcff6167128a13c8b0a1648310e6e",
"0xa7a020ae798a0a026c57ed6cbe48b21d3dbbec5b",
"0xa7ce02b8195fe8e3116a7e1248f2725eaac86fec",
"0xa82c7c0ef05080463e4ac55db8b8531007f3a66c",
"0xa8454096f4a18e0252b411f670b3dd9d21465e75",
"0xa8eb559eeb38c6cb6a8bc6c39badd86b113cd875",
"0xaa1886de3f70a3ef502ea1379a311c5b4e05f3fd",
"0xaa2498d85ba7559006f90110a4bc33c10a06c1af",
"0xab1c30cd2d9964b3b845ef744e2e4043739430e5",
"0xab215957873deadd9512fbde29e6c12bea1dd1a1",
"0xabc490af011c8e354b7d2112648883fe9e511695",
"0xac1a4f4d49715e31f54cb8e0bf867bb9170c10cb",
"0xac3800002e45ed2e1a55dedfa2aca137f6dba61e",
"0xac513396ee50091972ee6fc07d120b6ad360b233",
"0xac82537fae701b2046f62bb718c4857787b0b647",
"0xac9093198de62e5289163e85bacb53ead596b9f3",
"0xaca33c43bd8deac8e1d144baa51fe9c7dd7b010f",
"0xad0c88f3313abbce2185597e87a15f764e948a46",
"0xadf5b0c2103598fb66a61714152f1d1717d49fe0",
"0xaf313b5cf52d7a2ad7785d008660ab68421db2d1",
"0xaf31bf4fa017768e6426308cf17e16d633700265",
"0xaf70168cdac454fa9ff94e5458d450ae7eeb7ae8",
"0xaff9e40f9b245b15a1d1bb45516ca213e682fa81",
"0xb0dba64e6a353a74cc78e8a98914c4dff517ba7d",
"0xb113a2c20471ff1efdf2735206c630707263f41c",
"0xb116d83c919d6661dc20246d312b702f0cdf297c",
"0xb177fc9a7caaa9c50d82c5d2533750f1d72aac86",
"0xb1f873cb2cd0c818ce8b0a7d971f062808a47425",
"0xb2172027cb233198a3945a2c864f1ec5b7e3b3da",
"0xb23fb6cc17026d1ce9cd543cb69a023569503e4c",
"0xb2a3fcf38979898e695c88947e3373bf1c2e9b37",
"0xb2ac8e363ea34201df532a01522a006fcaa389ee",
"0xb3764761e297d6f121e79c32a65829cd1ddb4d32",
"0xb37722e14fcfc78a56312a9a746c5474822002f2",
"0xb37da5c9179345341fe50d37cfc961ba47c01f9a",
"0xb4c9d8a5812a024bdb177991af256da144776033",
"0xb5879bf4a37c6ed9d86a6f2d8aab7939b21687cd",
"0xb65db9f684f3bfa45ad3fb4cdc4120618ad81f00",
"0xb71df0dbbb9754c1aa9115253f4ebb7ef3f8a57c",
"0xb76a4770587f76d6c644deec275a2c76069b5f14",
"0xb7c4a4341c0a03460b6cec4fa997dd2d748ce281",
"0xb82bfd79d5a8c0cc0d4c045633288b48a2beddcb",
"0xb8689c29bdc7c84f33706ff0f96eafc222ba6d86",
"0xb9ae74beafe4025cc0fcfb5e190169f2d7f0b563",
"0xb9cfb36060785f6800b1ab9ccc9b4f341d097399",
"0xba83e9ce38b10522e3d6061a12779b7526839eda",
"0xbb53be0690ae7256833099a1f01125eb33445692",
"0xbbdef8b12babd3ab6018566bc17ec3aa302b8348",
"0xbbf84f9b823c42896c9723c0be4d5f5ede257b52",
"0xbc210ec2efada9df4897dc9b23c58e96c01ef711",
"0xbc66e774ce25000950786241b8c5ee3275311bd7",
"0xbc83d48dd0cd9c3f47bab6436defeb334b563f4c",
"0xbc85a12364c9e375801c00aad17b893fc4c8f5b6",
"0xbc8b85b1515e45fb2d74333310a1d37b879732c0",
"0xbca6294a6c80ee0f20173547b8d85d4948a0cb39",
"0xbcc6c0fef89b87a12773db7a9a8ecbccccdb7aff",
"0xbe11d0d38ef4857224032afc9c96647492f726ce",
"0xbe3364dfb8158a3e452ff96aeb247bf3f5f019b5",
"0xbeebe66b6a511dd2accbff7f258554dbe97f1294",
"0xbf0c85867fcdd4064d22b0dfd91561a52134e035",
"0xc0ded1d24c071ca13a905ec5e54a6ffdd0c4df68",
"0xc0fed35b43f59c2bfb1eb544bd8921bfb18140c2",
"0xc14777c94229582e5758c5a79b83dde876b9be98",
"0xc169a0e826f22fc7d37a48aaa346ffb4521c35cd",
"0xc1dd8ac971129c8c08333924f58ff40a50b8bb9a",
"0xc28f50625cfe028ab1a3458c7cebcf9657ff1438",
"0xc315f677f6a8f7b42d8fb32500e790cbf3dfbce8",
"0xc31fb4c352cb68c847715d46d97c1fa2aa2d0f00",
"0xc36454ed2b40adef7c75b9cef95b2f8010d3e0d2",
"0xc38d14bf89e0001260bb349a006d7e491e5f43ad",
"0xc4625787be3fccff6ac2971945806acc167c8cbd",
"0xc4b51a247514901faf1b6b1da9f0836066e64407",
"0xc4dca8f7d121ad79057a8382fc9fa9898727ddd8",
"0xc57f1148855e67763a694f7f2c0e68230adc686e",
"0xc593e92ed9fd3c2f9db972860047affd2df83ae7",
"0xc5d65deff5d1373f8b19aa8be528e9c3599013ba",
"0xc5f6690f7b60fe739908e02aec46ae07718cec5f",
"0xc616d99df3bf92e3111090f94694bd662b72ab1d",
"0xc7308d5a7220dc021ccf417445cb28cbb8fece66",
"0xc8ac47854a3fed2b444beb83f4c95ba092a3a735",
"0xc8c3234aea55a5f746b2ae585a849ba0bfa57785",
"0xc8ce1c3ac9549d479a90a6a89155b9b94e54d9ba",
"0xc915ec7f4cfd1c0a8aba090f03bfaab588aef9b4",
"0xc94a6e7776bade5da316cf6fd8c751fb0d5c3c5e",
"0xc9a08162b95915edf6570f0439757e1f655de576",
"0xc9f32ce1127e44c51cbd182d6364f3d707fd0d47",
"0xca4da1753336aa8340710e0e1a8c0bee2bfbf56c",
"0xcae77a05ab518492144944fb50572ebfa3595870",
"0xcb142defc16c9b409272447d87c74b722b767f1f",
"0xcb89576d5e1627f4a0c64c2bb4d51798e92e8b8f",
"0xcbcd4b518890429c0ffef3d782ed99b3adff009b",
"0xcc02b920ae227f1be7d01fc241c27e5f74d40436",
"0xcc49d1f23f01decd4e18b6aaacccb038c9648e30",
"0xcc66ea7ef61a67df02afb18706c68a5f30b22300",
"0xccdf72de615b5158bb37931bbce645aa69221520",
"0xccffdb4bd0abb6fd105c1c4e03b4e919a5b57ab9",
"0xcd63bb3586e871611cc60befcadf8e56bc7aeea3",
"0xcda5dd8e13fdae006b270769b1a18fa6c5524ce0",
"0xcdd1c19dcc3f473eaef6edb0a28e1e796d6e1767",
"0xce52d38539e1e557f184f1073a59f69c9ba95f28",
"0xce686d019e6f692af646a8df3bfe789da97a484f",
"0xcec962222291e09a07c0bd0a110d2211e0358b28",
"0xcee9ee01d48050415f1b104277bd493c5dbe645e",
"0xcff5e190a3d92e480a8bc5b414362c0d1afeb54e",
"0xd055610c2d5151adb3eaf994e08abe45dee936e0",
"0xd06f80a4b932d7247aba4a85decce6c2458c0654",
"0xd0ac32ab01d9d3ec145ef63f73bf4e222dbcc0fb",
"0xd0b473271e9d38dfd11925d62ef8c0a2cf033a9c",
"0xd0cc2b24980cbcca47ef755da88b220a82291407",
"0xd1091f9c7bb48651edb17d255b2824de0ebd2074",
"0xd1bdd067f5f1cbd358e2dde444f8d9f41de8ae76",
"0xd22167d083a5cfc2f1ccadeb842a8093b2174c5f",
"0xd2f4ea2f9d4c627c957305e28877081da9296d47",
"0xd33441a44da0c1dcee596abb7ebdcdba77c7ddbd",
"0xd354cf84a4cfd096120e2d4bf0a0cc8866d4efe1",
"0xd362c189cb0056159d2fe22bd8203e26579f620d",
"0xd3d26c61401924939bee67dffdb7aa7a0138844c",
"0xd3d7c71e4c5b1ff2f42fbb9b4a21f3373aa8b3b0",
"0xd43f2111e4055120196285ec094aa6e96998668b",
"0xd50fcd07007255599a9a8cedd881f863bcd65eb1",
"0xd5ce086a9d4987adf088889a520de98299e10bb5",
"0xd5edb088b7382b397238f5010d14cf10dd395007",
"0xd6f8668e96951e4108a79bc45ccef022544c73b3",
"0xd72ebdf278c092d9cdaa12c58f8bd9438f96b83e",
"0xd754b5bf41b84bcb49cb952f858318640abc60e0",
"0xd77b95800f67deb52e0e0988dbf01a311a3ccd80",
"0xd79eb898431ea62636f08f738d10eb00446403de",
"0xd821dfb705333d2c16e0c0c0be75ce360cca566a",
"0xd84af8766bd29b988f3426cc48cac5a6646f1f90",
"0xd868a76a1dcc34854903376e734e3cfe461dd08c",
"0xd912289c9f079b0655737a55fd5d745501ecefc7",
"0xd9204db19a18e051e861576a462c0cf83a2b6237",
"0xd92559e1f1d2e84f4964f45256693d03833d7d44",
"0xd9932453997ddf36fe114c9f22bebb9ee77b2921",
"0xd9cd7461f960e56364a294f124aac77b25e2b784",
"0xd9d25dce4c3765855c76f3d2baccc0755c4d0bc5",
"0xda1cc010259496d407a764c381d0e82182d68d89",
"0xda657e9fa116900fab8178e5580a3a6cedd89f3c",
"0xda917961872ae8e0c8b96f6925a4d7cc7b27aea3",
"0xdaa29859836d97c810c7f9d350d4a1b3e8cafc9a",
"0xdafc2a2d4e070f863cfcb5887bc2516e05e18877",
"0xdb21509547c82b69e714bf3a761e6d64f0f91cd0",
"0xdb82af76f9ccddfe9e8f7996492f4c5bd5f9d53d",
"0xdbe6dba965afc52f5b3d51e4b15e372f45d4106a",
"0xdc3fd5cce7b17e461c692ff34f22fbd4b780b151",
"0xdcd2fb1c1b103c5a591e76798704cfaa27baf6b9",
"0xddd6854a002a6fbcdf695385cd5ed630c9e27c3e",
"0xddff022e4befa69cbb5262446a8ae564700bea24",
"0xde5886e65cbf1a9d21267f5ef7d5ed444cc63938",
"0xdef05b512d405f4fb930e252c6c11f054832c93b",
"0xdefb014b9e2f3bd81cdb084821f99b681cfca695",
"0xdf04e5007cb8fd65831310ceda40f5642c1b39c3",
"0xdf1ec2e44a8b1774b068ecfc5ef1c937a86baf3e",
"0xdf3e77cd5e563f93fc7eca905d3302e95f9bb150",
"0xdf967ff95cd5ff35a27786dbd906fbb9ded116af",
"0xe009321b26350c2cff5db607068c1f6813df40d7",
"0xe02f2921742a6eec7ae44a18b1cca4277874bc74",
"0xe051ee9bd6270ff52d85ffe09685ee3a9755f04a",
"0xe06ae5c5ab3eda3a62918bf86532c7d87dbbc66d",
"0xe0b13c073e8173b06062c69a160ebb54e2af86c3",
"0xe10270bfb1ed82e120bfc392efb3c94a1604ded6",
"0xe10c24ca7bf18640fcb35e059919348891922a3b",
"0xe112784753273ebede055968ecb78dcad8ff6da2",
"0xe1358ae1a9130bc458c8021fcbb7ef8aedd51487",
"0xe1632e7e0dc135f3bc8991b917e8b682ab340fe1",
"0xe276c66af8853cdd91c1a25608b4e8b599f4da0e",
"0xe344e4b209e8eabaa2a6ddd1b0aa120b7599af25",
"0xe350160e3c8a07e92ec58ddcb8df81a73aedc6f9",
"0xe41031233301370491d47477e1171dab212d5352",
"0xe4fa5149306b12d51dc0d04e5e95bc9704ccaad7",
"0xe4faeef4002c200d9d4f1d381ccb5fb679d1c437",
"0xe54659243f8542d6abfd15bbff69f62465786756",
"0xe5b913f91f2b90c5cd04d711e1eb3214c56dba98",
"0xe61df1f5b8dd4e4e2a874157c2c97daf7314b795",
"0xe6b39dad6d7a50b233da23e510697422e9d6351a",
"0xe6c51d563f92a23dee9a7093bb1be33bd35c05d8",
"0xe6d3486a5fe2742e313a3266af8ff4f43e597d27",
"0xe74b02131ed2184eb94fd357b4f303e6935367f5",
"0xe7cc576611c2df800862db667140a90cf9ec4b72",
"0xe824ec0f8384600f3187967f8b098014e31892f2",
"0xe88379631857c0d8174efefdf8dca25b29610f08",
"0xe977419ef28e71ed541ff6318cea9a6392709a48",
"0xe9aa3a74e3d62274f221eca42736cadc14ccffaf",
"0xe9ab68e4aa12bf290529626e5f32725cf5abfba8",
"0xe9e837881c89e943c4c66e26031922bf8fc3f4f3",
"0xe9ee84b3b818b2af67c4536248fbfc5c7a0ccfad",
"0xea6e18fd3c301e4300bc99b6db792fcff376abb4",
"0xea7380369af53598a55b4622909cd9633e0ae7fa",
"0xeab1d82e96541718e645fdef4a5ee9df78605b76",
"0xeaceb8e777dc8b1c11b8730904d13fbda070846f",
"0xeb8b629df028f5353649460f1c06bf394de8641f",
"0xec2d5cc008ba6d2237d33ca9cf74b684737fcae8",
"0xec970bbf167beee6fb536f1839418f3d19ce2a69",
"0xecb6ffac05d8b4660b99b475b359fe454c77d153",
"0xed2d26eed06ecfbfd796d1ec551d2a649a31e576",
"0xed44fc770eaa76db9ade24d86ce3b409f4aed009",
"0xedf202629bb7e9f72d4c62c325d198513fa7a3d3",
"0xee2c2c2dcd952429147b62fd4dcd7f565313e419",
"0xeecc46a74cea6133a12672bd62d5167877b4d521",
"0xeed48722238b98317d849fa591d96b7efbe9b06f",
"0xeed7072fcf46733833249d1e979c44fe4f2a23a2",
"0xeef2a09be2a136bba76f04cf056e36947dbf0b0c",
"0xef0683bef79b7ad85573415c781edfde8bec65b1",
"0xef57aa4b57587600fc209f345fe0a2687bc26985",
"0xef6805af393c34fe772f8d827a5d26685215612a",
"0xf021428381172ef96988b31f241f4000259488f7",
"0xf0a924661b0263e5ce12756d07f45b8668c53380",
"0xf0fc2d4d550253d8c165c599e0cccf221c7eb7b5",
"0xf1014a29614b196d19d8ecd5ff05f3cef5efdbe2",
"0xf193a9afb00715aacf7ca9ebffafe02c77517c2e",
"0xf1e250f2a27ab7ec5b82b287f2799260448cd51d",
"0xf23f9dda63ed0628609272dc0544c7a2f7189f51",
"0xf245e09a1b42f847b120558f0c6e08f821be23f7",
"0xf2601c5a063c6a6ca86d8b6bd0543158f83a10ce",
"0xf2dcfa51f83e41c0e988477f939c7a5e5b9a6905",
"0xf33068d5e798f6519349ce32669d1ec940db1193",
"0xf394d807795ff57373f1fe903046596cb0f69acc",
"0xf3ae45d5a0b8efb41444101267b56e6795051fac",
"0xf4710562c804087af20b7396ce7cba38b43fb61f",
"0xf4c87c851d772b0f9e72cca0690327f2bc505015",
"0xf5c86c5ba5291da78a2b4119dd7aef99e3508362",
"0xf689bb4b063683a9ffd7a9295106f1e47c095809",
"0xf73ef0650415656b51be39bd5d37cf5ce03f4033",
"0xf747726200c7fc14ef4ce267b1f070128c2260dd",
"0xf855fee50a8915634a105385cb6cb9e442d15457",
"0xf8e676094628776690dbf83fa31f08aa14fd3fb8",
"0xf902aa3a62f9e28878129e1a8f1eb71e4fd7a88c",
"0xf962933e44b1dfc771738a034c80d87bdcd96ecc",
"0xf9a5139e42c3ca13d5b24d23ab2b18c278805dc4",
"0xf9fe85e19bd533c69067c6e2ff02727d40a54100",
"0xfa1f0b67b9b48b2e87467d72f09722e19ed601bd",
"0xfa956f10da5e16f120eb299df963c1f7563bf66b",
"0xfad17c9da2bebf758e3bc0c99d1abe8af7e96ed1",
"0xfb1a7ccf5bcd436dcc0acb49ba1fb9f57bb4d064",
"0xfb6e71e0800bccc0db8a9cf326fe3213ca1a0ea0",
"0xfb990112f51931a2834fcf574a860b7419ecd76f",
"0xfbc79a80ffa84253611e8f9a82f18e9e93ceee81",
"0xfbf6f29c126382cf15795bb209ee506a174cc709",
"0xfc74fb0d5df08ec546a3da18ccc7fbcbe031f5d8",
"0xfcdd417b449c982fc9b6d7b117e417682e81b627",
"0xfd477bf560e59941796b398cea662b393298abc0",
"0xfddbfa1b0b93612b95e3296690b63b74d019370c",
"0xfe68de56a07cd3af0ec40c22b0193115ecdd0501",
"0xfe821f5509fdea3f72362d001697ad8c11f0c111",
"0xfe9b7db8d9d57e9ad9341bcf51b110ba5d27b48b",
"0xff35866acb80ce4b169d1460cd48108955c1c445",
]);
function isScamAddress(address) {

View File

@@ -23,10 +23,8 @@ const DEFAULT_STATE = {
hideFraudContracts: true,
hideDustTransactions: true,
dustThresholdGwei: 100000,
utcTimestamps: false,
fraudContracts: [],
tokenHolderCache: {},
theme: "system",
};
const state = {
@@ -55,10 +53,8 @@ async function saveState() {
hideFraudContracts: state.hideFraudContracts,
hideDustTransactions: state.hideDustTransactions,
dustThresholdGwei: state.dustThresholdGwei,
utcTimestamps: state.utcTimestamps,
fraudContracts: state.fraudContracts,
tokenHolderCache: state.tokenHolderCache,
theme: state.theme,
currentView: state.currentView,
selectedWallet: state.selectedWallet,
selectedAddress: state.selectedAddress,
@@ -112,11 +108,8 @@ async function loadState() {
saved.dustThresholdGwei !== undefined
? saved.dustThresholdGwei
: 100000;
state.utcTimestamps =
saved.utcTimestamps !== undefined ? saved.utcTimestamps : false;
state.fraudContracts = saved.fraudContracts || [];
state.tokenHolderCache = saved.tokenHolderCache || {};
state.theme = saved.theme || "system";
state.currentView = saved.currentView || null;
state.selectedWallet =
saved.selectedWallet !== undefined ? saved.selectedWallet : null;

View File

@@ -153,38 +153,24 @@ async function fetchRecentTransactions(address, blockscoutUrl, count = 25) {
// When a token transfer shares a hash with a normal tx, the normal tx
// is the contract call (0 ETH) and the token transfer has the real
// amount and symbol. For contract calls (swaps), a single transaction
// can produce multiple token transfers (input, intermediates, output).
// We consolidate these into the original tx entry using the token
// transfer where the user *receives* tokens (the swap output), so
// the transaction list shows the final result rather than confusing
// intermediate hops. We preserve the original tx's from/to so the
// user sees their own address, not a router or Permit2 contract.
// amount and symbol. A single transaction (e.g. a swap) can produce
// multiple token transfers (one per token involved), so we key token
// transfers by hash + contract address to keep all of them. We also
// preserve contract-call metadata (direction, label, method) from the
// matching normal tx so swaps display correctly.
for (const tt of ttJson.items || []) {
const parsed = parseTokenTransfer(tt, addrLower);
const existing = txsByHash.get(parsed.hash);
if (existing && existing.direction === "contract") {
// For contract calls (swaps), consolidate into the original
// tx entry. Prefer the "received" transfer (swap output)
// for the display amount. If no received transfer exists,
// fall back to the first "sent" transfer (swap input).
const isReceived = parsed.direction === "received";
const needsAmount = !existing.exactValue;
if (isReceived || needsAmount) {
existing.value = parsed.value;
existing.exactValue = parsed.exactValue;
existing.rawAmount = parsed.rawAmount;
existing.rawUnit = parsed.rawUnit;
existing.symbol = parsed.symbol;
existing.contractAddress = parsed.contractAddress;
existing.holders = parsed.holders;
}
// Keep the original tx's from/to (the user's address and the
// contract they called), not the token transfer's from/to
// which may be a router or Permit2 contract.
continue;
parsed.direction = "contract";
parsed.directionLabel = existing.directionLabel;
parsed.isContractCall = true;
parsed.method = existing.method;
// Remove the bare-hash normal tx so it doesn't appear as a
// duplicate with empty value; token transfers replace it.
txsByHash.delete(parsed.hash);
}
// Non-contract token transfers get their own entries.
// Use composite key so multiple token transfers per tx are kept.
const ttKey = parsed.hash + ":" + (parsed.contractAddress || "");
txsByHash.set(ttKey, parsed);
}

View File

@@ -359,12 +359,9 @@ function decode(data, toAddress) {
const s = decodeV3SwapExactIn(inputs[i]);
if (s) {
if (!inputToken) inputToken = s.tokenIn;
if (!outputToken) outputToken = s.tokenOut;
if (!inputAmount) inputAmount = s.amountIn;
// Always update output: in multi-step swaps (V3 → V4),
// the last swap step determines the final output token
// and minimum received amount.
outputToken = s.tokenOut;
minOutput = s.amountOutMin;
if (!minOutput) minOutput = s.amountOutMin;
}
}
@@ -372,9 +369,9 @@ function decode(data, toAddress) {
const s = decodeV2SwapExactIn(inputs[i]);
if (s) {
if (!inputToken) inputToken = s.tokenIn;
if (!outputToken) outputToken = s.tokenOut;
if (!inputAmount) inputAmount = s.amountIn;
outputToken = s.tokenOut;
minOutput = s.amountOutMin;
if (!minOutput) minOutput = s.amountOutMin;
}
}
@@ -391,11 +388,12 @@ function decode(data, toAddress) {
const v4 = decodeV4Swap(inputs[i]);
if (v4) {
if (!inputToken && v4.tokenIn) inputToken = v4.tokenIn;
if (!outputToken && v4.tokenOut)
outputToken = v4.tokenOut;
if (!inputAmount && v4.amountIn)
inputAmount = v4.amountIn;
// Always update output: last swap step wins
if (v4.tokenOut) outputToken = v4.tokenOut;
if (v4.amountOutMin) minOutput = v4.amountOutMin;
if (!minOutput && v4.amountOutMin)
minOutput = v4.amountOutMin;
}
}