Add transaction confirmation screen and password modal
All checks were successful
check / check (push) Successful in 13s
All checks were successful
check / check (push) Successful in 13s
New send flow: Send → Confirm → Password → Broadcast. Send view: collects To (with ENS resolution), Amount, Token. "Review" button advances to confirmation. No password field. Confirm Transaction view: shows From, To (with ENS name), Amount (with USD value), and runs pre-send checks: - Scam address warning (checked against local blocklist) - Self-send warning - Insufficient balance error (disables Send button) Password modal: full-screen overlay, appears only after user clicks Send on the confirmation screen. Decrypts the wallet secret, signs and broadcasts the transaction. Wrong password is caught inline. scamlist.js: hardcoded set of known scam/fraud addresses (Tornado Cash sanctioned, drainer contracts, address poisoning). Checked locally, no external API.
This commit is contained in:
@@ -298,27 +298,12 @@
|
|||||||
placeholder="0.0"
|
placeholder="0.0"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
id="send-fee-estimate"
|
|
||||||
class="text-xs text-muted mb-2 hidden"
|
|
||||||
></div>
|
|
||||||
<div class="mb-2">
|
|
||||||
<label class="block mb-1">Password</label>
|
|
||||||
<p class="text-xs text-muted mb-1">
|
|
||||||
Required to authorize the transaction.
|
|
||||||
</p>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="send-password"
|
|
||||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
id="btn-send-confirm"
|
id="btn-send-review"
|
||||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||||
>
|
>
|
||||||
Send
|
Review
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="btn-send-back"
|
id="btn-send-back"
|
||||||
@@ -328,11 +313,99 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="send-status"
|
id="send-error"
|
||||||
|
class="mt-2 border border-border border-dashed p-1 hidden"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ============ CONFIRM TRANSACTION ============ -->
|
||||||
|
<div id="view-confirm-tx" class="view hidden">
|
||||||
|
<h2 class="font-bold mb-2">Confirm Transaction</h2>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="text-xs text-muted">From</div>
|
||||||
|
<div id="confirm-from" class="text-xs break-all"></div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="text-xs text-muted">To</div>
|
||||||
|
<div id="confirm-to" class="text-xs break-all"></div>
|
||||||
|
<div
|
||||||
|
id="confirm-to-ens"
|
||||||
|
class="text-xs text-muted hidden"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="text-xs text-muted">Amount</div>
|
||||||
|
<div id="confirm-amount" class="font-bold"></div>
|
||||||
|
<div
|
||||||
|
id="confirm-amount-usd"
|
||||||
|
class="text-xs text-muted"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="confirm-fee"
|
||||||
|
class="mb-2 text-xs text-muted hidden"
|
||||||
|
></div>
|
||||||
|
<div id="confirm-warnings" class="mb-2 hidden"></div>
|
||||||
|
<div
|
||||||
|
id="confirm-errors"
|
||||||
|
class="mb-2 border border-border border-dashed p-2 hidden"
|
||||||
|
></div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button
|
||||||
|
id="btn-confirm-send"
|
||||||
|
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="btn-confirm-back"
|
||||||
|
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="confirm-status"
|
||||||
class="mt-2 border border-border p-1 hidden"
|
class="mt-2 border border-border p-1 hidden"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ============ PASSWORD MODAL ============ -->
|
||||||
|
<div
|
||||||
|
id="password-modal"
|
||||||
|
class="hidden fixed inset-0 bg-bg flex items-center justify-center z-50"
|
||||||
|
>
|
||||||
|
<div class="border border-border p-4 bg-bg w-80">
|
||||||
|
<h2 class="font-bold mb-2">Enter Password</h2>
|
||||||
|
<p class="text-xs text-muted mb-2">
|
||||||
|
Your password is needed to authorize this transaction.
|
||||||
|
</p>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="modal-password"
|
||||||
|
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg mb-2"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
id="modal-password-error"
|
||||||
|
class="text-xs mb-2 border border-border border-dashed p-1 hidden"
|
||||||
|
></div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button
|
||||||
|
id="btn-modal-confirm"
|
||||||
|
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="btn-modal-cancel"
|
||||||
|
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ============ RECEIVE ============ -->
|
<!-- ============ RECEIVE ============ -->
|
||||||
<div id="view-receive" class="view hidden">
|
<div id="view-receive" class="view hidden">
|
||||||
<h2 class="font-bold mb-2">Receive</h2>
|
<h2 class="font-bold mb-2">Receive</h2>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const addWallet = require("./views/addWallet");
|
|||||||
const importKey = require("./views/importKey");
|
const importKey = require("./views/importKey");
|
||||||
const addressDetail = require("./views/addressDetail");
|
const addressDetail = require("./views/addressDetail");
|
||||||
const send = require("./views/send");
|
const send = require("./views/send");
|
||||||
|
const confirmTx = require("./views/confirmTx");
|
||||||
const receive = require("./views/receive");
|
const receive = require("./views/receive");
|
||||||
const addToken = require("./views/addToken");
|
const addToken = require("./views/addToken");
|
||||||
const settings = require("./views/settings");
|
const settings = require("./views/settings");
|
||||||
@@ -38,6 +39,7 @@ const ctx = {
|
|||||||
showImportKeyView: () => importKey.show(),
|
showImportKeyView: () => importKey.show(),
|
||||||
showAddressDetail: () => addressDetail.show(),
|
showAddressDetail: () => addressDetail.show(),
|
||||||
showAddTokenView: () => addToken.show(),
|
showAddTokenView: () => addToken.show(),
|
||||||
|
showConfirmTx: (txInfo) => confirmTx.show(txInfo),
|
||||||
};
|
};
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
@@ -52,13 +54,13 @@ async function init() {
|
|||||||
|
|
||||||
await loadState();
|
await loadState();
|
||||||
|
|
||||||
// Initialize all view event handlers
|
|
||||||
welcome.init(ctx);
|
welcome.init(ctx);
|
||||||
addWallet.init(ctx);
|
addWallet.init(ctx);
|
||||||
importKey.init(ctx);
|
importKey.init(ctx);
|
||||||
home.init(ctx);
|
home.init(ctx);
|
||||||
addressDetail.init(ctx);
|
addressDetail.init(ctx);
|
||||||
send.init(ctx);
|
send.init(ctx);
|
||||||
|
confirmTx.init(ctx);
|
||||||
receive.init(ctx);
|
receive.init(ctx);
|
||||||
addToken.init(ctx);
|
addToken.init(ctx);
|
||||||
settings.init(ctx);
|
settings.init(ctx);
|
||||||
|
|||||||
179
src/popup/views/confirmTx.js
Normal file
179
src/popup/views/confirmTx.js
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Transaction confirmation view + password modal.
|
||||||
|
// Shows transaction details, warnings, errors. On proceed, opens
|
||||||
|
// password modal, decrypts secret, signs and broadcasts.
|
||||||
|
|
||||||
|
const { parseEther } = require("ethers");
|
||||||
|
const { $, showError, hideError, showView } = require("./helpers");
|
||||||
|
const { state } = require("../../shared/state");
|
||||||
|
const { getSignerForAddress } = require("../../shared/wallet");
|
||||||
|
const { decryptWithPassword } = require("../../shared/vault");
|
||||||
|
const { formatUsd, getPrice } = require("../../shared/prices");
|
||||||
|
const {
|
||||||
|
getProvider,
|
||||||
|
invalidateBalanceCache,
|
||||||
|
} = require("../../shared/balances");
|
||||||
|
const { isScamAddress } = require("../../shared/scamlist");
|
||||||
|
|
||||||
|
let pendingTx = null;
|
||||||
|
|
||||||
|
function show(txInfo) {
|
||||||
|
pendingTx = txInfo;
|
||||||
|
|
||||||
|
$("confirm-from").textContent = txInfo.from;
|
||||||
|
$("confirm-to").textContent = txInfo.to;
|
||||||
|
|
||||||
|
const ensEl = $("confirm-to-ens");
|
||||||
|
if (txInfo.ensName) {
|
||||||
|
ensEl.textContent = "(" + txInfo.ensName + ")";
|
||||||
|
ensEl.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
ensEl.classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("confirm-amount").textContent = txInfo.amount + " " + txInfo.token;
|
||||||
|
|
||||||
|
const ethPrice = getPrice("ETH");
|
||||||
|
if (txInfo.token === "ETH" && ethPrice) {
|
||||||
|
const usd = parseFloat(txInfo.amount) * ethPrice;
|
||||||
|
$("confirm-amount-usd").textContent = formatUsd(usd);
|
||||||
|
} else {
|
||||||
|
$("confirm-amount-usd").textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for warnings
|
||||||
|
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 (txInfo.to.toLowerCase() === txInfo.from.toLowerCase()) {
|
||||||
|
warnings.push("You are sending to your own address.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const warningsEl = $("confirm-warnings");
|
||||||
|
if (warnings.length > 0) {
|
||||||
|
warningsEl.innerHTML = warnings
|
||||||
|
.map(
|
||||||
|
(w) =>
|
||||||
|
`<div class="border border-border border-dashed p-2 mb-1 text-xs font-bold">WARNING: ${w}</div>`,
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
warningsEl.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
warningsEl.classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
const errors = [];
|
||||||
|
if (
|
||||||
|
txInfo.token === "ETH" &&
|
||||||
|
parseFloat(txInfo.amount) > parseFloat(txInfo.balance)
|
||||||
|
) {
|
||||||
|
errors.push(
|
||||||
|
"Insufficient balance. You have " +
|
||||||
|
txInfo.balance +
|
||||||
|
" ETH but are trying to send " +
|
||||||
|
txInfo.amount +
|
||||||
|
" ETH.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorsEl = $("confirm-errors");
|
||||||
|
const sendBtn = $("btn-confirm-send");
|
||||||
|
if (errors.length > 0) {
|
||||||
|
errorsEl.innerHTML = errors
|
||||||
|
.map((e) => `<div class="text-xs">${e}</div>`)
|
||||||
|
.join("");
|
||||||
|
errorsEl.classList.remove("hidden");
|
||||||
|
sendBtn.disabled = true;
|
||||||
|
sendBtn.classList.add("text-muted");
|
||||||
|
} else {
|
||||||
|
errorsEl.classList.add("hidden");
|
||||||
|
sendBtn.disabled = false;
|
||||||
|
sendBtn.classList.remove("text-muted");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("confirm-fee").classList.add("hidden");
|
||||||
|
$("confirm-status").classList.add("hidden");
|
||||||
|
showView("confirm-tx");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPasswordModal() {
|
||||||
|
$("modal-password").value = "";
|
||||||
|
hideError("modal-password-error");
|
||||||
|
$("password-modal").classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function hidePasswordModal() {
|
||||||
|
$("password-modal").classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(ctx) {
|
||||||
|
$("btn-confirm-send").addEventListener("click", () => {
|
||||||
|
showPasswordModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("btn-confirm-back").addEventListener("click", () => {
|
||||||
|
showView("send");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("btn-modal-cancel").addEventListener("click", () => {
|
||||||
|
hidePasswordModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("btn-modal-confirm").addEventListener("click", async () => {
|
||||||
|
const password = $("modal-password").value;
|
||||||
|
if (!password) {
|
||||||
|
showError("modal-password-error", "Please enter your password.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallet = state.wallets[state.selectedWallet];
|
||||||
|
let decryptedSecret;
|
||||||
|
hideError("modal-password-error");
|
||||||
|
|
||||||
|
try {
|
||||||
|
decryptedSecret = await decryptWithPassword(
|
||||||
|
wallet.encryptedSecret,
|
||||||
|
password,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
showError("modal-password-error", "Wrong password.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hidePasswordModal();
|
||||||
|
|
||||||
|
const statusEl = $("confirm-status");
|
||||||
|
statusEl.textContent = "Sending...";
|
||||||
|
statusEl.classList.remove("hidden");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const signer = getSignerForAddress(
|
||||||
|
wallet,
|
||||||
|
state.selectedAddress,
|
||||||
|
decryptedSecret,
|
||||||
|
);
|
||||||
|
const provider = getProvider(state.rpcUrl);
|
||||||
|
const connectedSigner = signer.connect(provider);
|
||||||
|
const tx = await connectedSigner.sendTransaction({
|
||||||
|
to: pendingTx.to,
|
||||||
|
value: parseEther(pendingTx.amount),
|
||||||
|
});
|
||||||
|
statusEl.textContent = "Sent. Waiting for confirmation...";
|
||||||
|
const receipt = await tx.wait();
|
||||||
|
statusEl.textContent =
|
||||||
|
"Confirmed in block " +
|
||||||
|
receipt.blockNumber +
|
||||||
|
". Tx: " +
|
||||||
|
receipt.hash;
|
||||||
|
invalidateBalanceCache();
|
||||||
|
ctx.doRefreshAndRender();
|
||||||
|
} catch (e) {
|
||||||
|
statusEl.textContent = "Failed: " + (e.shortMessage || e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { init, show };
|
||||||
@@ -9,6 +9,7 @@ const VIEWS = [
|
|||||||
"main",
|
"main",
|
||||||
"address",
|
"address",
|
||||||
"send",
|
"send",
|
||||||
|
"confirm-tx",
|
||||||
"receive",
|
"receive",
|
||||||
"add-token",
|
"add-token",
|
||||||
"settings",
|
"settings",
|
||||||
|
|||||||
@@ -1,90 +1,53 @@
|
|||||||
const { parseEther } = require("ethers");
|
// Send view: collect To, Amount, Token. Then go to confirmation.
|
||||||
const { $, showError } = require("./helpers");
|
|
||||||
const { state } = require("../../shared/state");
|
const { $, showError, hideError } = require("./helpers");
|
||||||
const { getSignerForAddress } = require("../../shared/wallet");
|
const { state, currentAddress } = require("../../shared/state");
|
||||||
const { decryptWithPassword } = require("../../shared/vault");
|
const { getProvider } = require("../../shared/balances");
|
||||||
const {
|
|
||||||
getProvider,
|
|
||||||
invalidateBalanceCache,
|
|
||||||
} = require("../../shared/balances");
|
|
||||||
|
|
||||||
function init(ctx) {
|
function init(ctx) {
|
||||||
$("btn-send-confirm").addEventListener("click", async () => {
|
$("btn-send-review").addEventListener("click", async () => {
|
||||||
const to = $("send-to").value.trim();
|
const to = $("send-to").value.trim();
|
||||||
const amount = $("send-amount").value.trim();
|
const amount = $("send-amount").value.trim();
|
||||||
if (!to) {
|
if (!to) {
|
||||||
showError("send-status", "Please enter a recipient address.");
|
showError("send-error", "Please enter a recipient address.");
|
||||||
$("send-status").classList.remove("hidden");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
|
if (!amount || isNaN(parseFloat(amount)) || parseFloat(amount) <= 0) {
|
||||||
showError("send-status", "Please enter a valid amount.");
|
showError("send-error", "Please enter a valid amount.");
|
||||||
$("send-status").classList.remove("hidden");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
hideError("send-error");
|
||||||
|
|
||||||
|
// Resolve ENS if needed
|
||||||
let resolvedTo = to;
|
let resolvedTo = to;
|
||||||
|
let ensName = null;
|
||||||
if (to.includes(".") && !to.startsWith("0x")) {
|
if (to.includes(".") && !to.startsWith("0x")) {
|
||||||
const statusEl = $("send-status");
|
|
||||||
statusEl.textContent = "Resolving " + to + "...";
|
|
||||||
statusEl.classList.remove("hidden");
|
|
||||||
try {
|
try {
|
||||||
const provider = getProvider(state.rpcUrl);
|
const provider = getProvider(state.rpcUrl);
|
||||||
const resolved = await provider.resolveName(to);
|
const resolved = await provider.resolveName(to);
|
||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
showError("send-status", "Could not resolve " + to);
|
showError("send-error", "Could not resolve " + to);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolvedTo = resolved;
|
resolvedTo = resolved;
|
||||||
|
ensName = to;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showError("send-status", "Failed to resolve ENS name.");
|
showError("send-error", "Failed to resolve ENS name.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const password = $("send-password").value;
|
|
||||||
if (!password) {
|
const token = $("send-token").value;
|
||||||
showError("send-status", "Please enter your password.");
|
const addr = currentAddress();
|
||||||
$("send-status").classList.remove("hidden");
|
|
||||||
return;
|
ctx.showConfirmTx({
|
||||||
}
|
from: addr.address,
|
||||||
const wallet = state.wallets[state.selectedWallet];
|
to: resolvedTo,
|
||||||
let decryptedSecret;
|
ensName: ensName,
|
||||||
const statusEl = $("send-status");
|
amount: amount,
|
||||||
statusEl.textContent = "Decrypting...";
|
token: token,
|
||||||
statusEl.classList.remove("hidden");
|
balance: addr.balance,
|
||||||
try {
|
});
|
||||||
decryptedSecret = await decryptWithPassword(
|
|
||||||
wallet.encryptedSecret,
|
|
||||||
password,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
showError("send-status", "Wrong password.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
statusEl.textContent = "Sending...";
|
|
||||||
try {
|
|
||||||
const signer = getSignerForAddress(
|
|
||||||
wallet,
|
|
||||||
state.selectedAddress,
|
|
||||||
decryptedSecret,
|
|
||||||
);
|
|
||||||
const provider = getProvider(state.rpcUrl);
|
|
||||||
const connectedSigner = signer.connect(provider);
|
|
||||||
const tx = await connectedSigner.sendTransaction({
|
|
||||||
to: resolvedTo,
|
|
||||||
value: parseEther(amount),
|
|
||||||
});
|
|
||||||
statusEl.textContent = "Sent. Waiting for confirmation...";
|
|
||||||
const receipt = await tx.wait();
|
|
||||||
statusEl.textContent =
|
|
||||||
"Confirmed in block " +
|
|
||||||
receipt.blockNumber +
|
|
||||||
". Tx: " +
|
|
||||||
receipt.hash;
|
|
||||||
invalidateBalanceCache();
|
|
||||||
ctx.doRefreshAndRender();
|
|
||||||
} catch (e) {
|
|
||||||
statusEl.textContent = "Failed: " + (e.shortMessage || e.message);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("btn-send-back").addEventListener("click", ctx.showAddressDetail);
|
$("btn-send-back").addEventListener("click", ctx.showAddressDetail);
|
||||||
|
|||||||
48
src/shared/scamlist.js
Normal file
48
src/shared/scamlist.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Known scam/fraud addresses. Checked locally before sending.
|
||||||
|
// This is a best-effort blocklist — it does not replace due diligence.
|
||||||
|
// Sources: Etherscan labels, MistTrack, community 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",
|
||||||
|
// Tornado Cash sanctioned addresses (OFAC)
|
||||||
|
"0x722122df12d4e14e13ac3b6895a86e84145b6967",
|
||||||
|
"0xdd4c48c0b24039969fc16d1cdf626eab821d3384",
|
||||||
|
"0xd90e2f925da726b50c4ed8d0fb90ad053324f31b",
|
||||||
|
"0xd96f2b1ab14cd8ab753fa0357fee5cd7d512c838",
|
||||||
|
"0x4736dcf1b7a3d580672cce6e7c65cd5cc9cfbfa9",
|
||||||
|
"0xd4b88df4d29f5cedd6857912842cff3b20c8cfa3",
|
||||||
|
"0x910cbd523d972eb0a6f4cae4618ad62622b39dbf",
|
||||||
|
"0xa160cdab225685da1d56aa342ad8841c3b53f291",
|
||||||
|
"0xfd8610d20aa15b7b2e3be39b396a1bc3516c7144",
|
||||||
|
"0xf60dd140cff0706bae9cd734ac3683731eb5bb31",
|
||||||
|
"0x22aaa7720ddd5388a3c0a3333430953c68f1849b",
|
||||||
|
"0xba214c1c1928a32bffe790263e38b4af9bfcd659",
|
||||||
|
"0xb1c8094b234dce6e03f10a5b673c1d8c69739a00",
|
||||||
|
"0x527653ea119f3e6a1f5bd18fbf4714081d7b31ce",
|
||||||
|
"0x58e8dcc13be9780fc42e8723d8ead4cf46943df2",
|
||||||
|
"0xd691f27f38b395864ea86cfc7253969b409c362d",
|
||||||
|
"0xaeaac358560e11f52454d997aaff2c5731b6f8a6",
|
||||||
|
"0x1356c899d8c9467c7f71c195612f8a395abf2f0a",
|
||||||
|
"0xa60c772958a3ed56c1f15dd055ba37ac8e523a0d",
|
||||||
|
"0x169ad27a470d064dede56a2d3ff727986b15d52b",
|
||||||
|
"0x0836222f2b2b24a3f36f98668ed8f0b38d1a872f",
|
||||||
|
"0x178169b423a011fff22b9e3f3abea13414ddd0f1",
|
||||||
|
"0x610b717796ad172b316957a19699d4b58edca1e0",
|
||||||
|
"0xbb93e510bbcd0b7beb5a853875f9ec60275cf498",
|
||||||
|
]);
|
||||||
|
|
||||||
|
function isScamAddress(address) {
|
||||||
|
return SCAM_ADDRESSES.has(address.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { isScamAddress, SCAM_ADDRESSES };
|
||||||
Reference in New Issue
Block a user