Add dust transaction filter to catch native ETH poisoning
Some checks failed
check / check (push) Has been cancelled

Address poisoning attacks also use real native ETH dust transfers
(e.g. 1 gwei) from look-alike addresses. Token-level filters cannot
catch these. Add a configurable dust threshold (default 100,000 gwei
/ 0.0001 ETH) that hides transactions below the threshold from
history. The threshold is editable in Settings and the filter can be
disabled entirely. Document the specific attack tx in the README.
This commit is contained in:
2026-02-26 15:29:48 +07:00
parent b5b4f75968
commit 9a6d1f6255
6 changed files with 73 additions and 5 deletions

View File

@@ -565,6 +565,22 @@
/>
Hide transactions from detected fraud contracts
</label>
<label
class="text-xs flex items-center gap-1 cursor-pointer mt-1"
>
<input type="checkbox" id="settings-hide-dust" />
Hide dust transactions below
</label>
<div class="flex items-center gap-1 mt-1">
<input
type="number"
id="settings-dust-threshold"
class="border border-border p-1 text-xs bg-bg text-fg"
style="width: 10ch"
min="0"
/>
<span class="text-xs text-muted">gwei</span>
</div>
</div>
<div class="bg-well p-3 mx-1 mb-3">

View File

@@ -94,6 +94,8 @@ async function loadTransactions(address) {
const result = filterTransactions(rawTxs, {
hideLowHolderTokens: state.hideLowHolderTokens,
hideFraudContracts: state.hideFraudContracts,
hideDustTransactions: state.hideDustTransactions,
dustThresholdGwei: state.dustThresholdGwei,
fraudContracts: state.fraudContracts,
});
const txs = result.transactions;

View File

@@ -125,6 +125,21 @@ function init(ctx) {
await saveState();
});
$("settings-hide-dust").checked = state.hideDustTransactions;
$("settings-hide-dust").addEventListener("change", async () => {
state.hideDustTransactions = $("settings-hide-dust").checked;
await saveState();
});
$("settings-dust-threshold").value = state.dustThresholdGwei;
$("settings-dust-threshold").addEventListener("change", async () => {
const val = parseInt($("settings-dust-threshold").value, 10);
if (!isNaN(val) && val >= 0) {
state.dustThresholdGwei = val;
await saveState();
}
});
$("btn-main-add-wallet").addEventListener("click", ctx.showAddWalletView);
$("btn-settings-back").addEventListener("click", () => {

View File

@@ -20,6 +20,8 @@ const DEFAULT_STATE = {
rememberSiteChoice: true,
hideLowHolderTokens: true,
hideFraudContracts: true,
hideDustTransactions: true,
dustThresholdGwei: 100000,
fraudContracts: [],
tokenHolderCache: {},
};
@@ -44,6 +46,8 @@ async function saveState() {
rememberSiteChoice: state.rememberSiteChoice,
hideLowHolderTokens: state.hideLowHolderTokens,
hideFraudContracts: state.hideFraudContracts,
hideDustTransactions: state.hideDustTransactions,
dustThresholdGwei: state.dustThresholdGwei,
fraudContracts: state.fraudContracts,
tokenHolderCache: state.tokenHolderCache,
};
@@ -82,6 +86,14 @@ async function loadState() {
saved.hideFraudContracts !== undefined
? saved.hideFraudContracts
: true;
state.hideDustTransactions =
saved.hideDustTransactions !== undefined
? saved.hideDustTransactions
: true;
state.dustThresholdGwei =
saved.dustThresholdGwei !== undefined
? saved.dustThresholdGwei
: 100000;
state.fraudContracts = saved.fraudContracts || [];
state.tokenHolderCache = saved.tokenHolderCache || {};
}

View File

@@ -20,13 +20,15 @@ function formatTxValue(val) {
function parseTx(tx, addrLower) {
const from = tx.from?.hash || "";
const to = tx.to?.hash || "";
const rawWei = tx.value || "0";
return {
hash: tx.hash,
blockNumber: tx.block_number,
timestamp: Math.floor(new Date(tx.timestamp).getTime() / 1000),
from: from,
to: to,
value: formatTxValue(formatEther(tx.value || "0")),
value: formatTxValue(formatEther(rawWei)),
valueGwei: Math.floor(Number(BigInt(rawWei) / BigInt(1000000000))),
symbol: "ETH",
direction: from.toLowerCase() === addrLower ? "sent" : "received",
isError: tx.status !== "ok",
@@ -47,6 +49,7 @@ function parseTokenTransfer(tt, addrLower) {
from: from,
to: to,
value: formatTxValue(formatUnits(rawValue, decimals)),
valueGwei: null,
symbol: tt.token?.symbol || "?",
direction: from.toLowerCase() === addrLower ? "sent" : "received",
isError: false,
@@ -160,6 +163,15 @@ function filterTransactions(txs, filters = {}) {
continue;
}
// Filter dust transactions (below gwei threshold) if setting is on
if (
filters.hideDustTransactions &&
tx.valueGwei !== null &&
tx.valueGwei < (filters.dustThresholdGwei || 100000)
) {
continue;
}
filtered.push(tx);
}