Stage 7.3: NPC scams (wicks, phishing airdrops, rug pools, fake support, forged transfers)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Opus 5.5 committed 2026-11-04 08:28:43 +08:00
1 parent 4279d48374
commit 6c5db17758
13 files changed
+691 -11

No files matched your search

+135 -1
View File
@@ -349,6 +349,138 @@ namespace Decentralized
});
}
private static readonly float[] PoolW = { 128f, 50f, 64f, 64f, 50f, 60f };
/// <summary>第三方收益池:日息、储备、时间锁、审计、我的存款,存入 / 取出(合约调用,签名)。是不是骗局看不见。</summary>
private static void YieldPools(CustomUIBuilder l, Wallet w, int day)
{
var sc = Session.Data.Scams;
var pools = sc.Pools.Where(p => !p.Closed || p.Pending.Any(q => ChainAddress.Same(q.Wallet, w.Address))).ToList();
l.AddSeparator();
l.AddHeader(L.T("pool.h"));
if (pools.Count == 0) { l.AddLabel(L.T("pool.none")); return; }
TableHeader(l, PoolW, L.T("pool.col.name"), L.T("pool.col.rate"), L.T("pool.col.reserve"), L.T("pool.col.lock"), L.T("pool.col.audit"), L.T("pool.col.mine"));
foreach (var p in pools)
{
var pp = p;
string mine = Fmt.Num(p.Of(w.Address)) + (p.Pending.Any(q => ChainAddress.Same(q.Wallet, w.Address)) ? "+" + L.T("pool.pending") : "");
TableRow(l, PoolW, p.Name + (p.Closed ? " " + L.T("pool.closed") : ""), (p.Rate * 100).ToString("0", CultureInfo.InvariantCulture) + "%", Fmt.Num(p.Reserve), L.T(p.Timelock ? "pool.yes" : "pool.no"), L.T(p.Audited ? "pool.yes" : "pool.no"), mine);
if (p.Closed) continue;
l.BeginRow(4f);
l.AddButton(L.T("pool.deposit"), () => PoolCall(pp, true));
if (p.Of(w.Address) > 0) l.AddButton(L.T(p.Timelock ? "pool.withdraw.lock" : "pool.withdraw", Tuning.YieldLockDays), () => PoolCall(pp, false));
l.End();
}
}
private static void PoolCall(YieldPool p, bool deposit)
{
var d = Session.Data;
var w = d.Hot;
double v = 0;
if (deposit)
{
if (!ReadAmount(out v)) return;
if (w.Avail(Assets.CRC) + 1e-9 < v) { _message = L.T("msg.balance"); Open(); return; }
if (p.Total + v > p.Cap + 1e-9) { _message = L.T("pool.err.cap", Fmt.Num(Math.Max(0, p.Cap - p.Total))); Open(); return; }
}
_message = L.T("hw.wait");
Open();
WalletWindow.OpenForBill(new WalletWindow.Bill
{
Contract = true, Asset = Assets.CRC, Amount = v, Spend = deposit ? v : 0, FeeAsset = deposit ? null : BestFeeAsset(w),
Summary = L.T(deposit ? "pool.sum.in" : "pool.sum.out", p.Name, Fmt.Num(deposit ? v : p.Of(w.Address))),
Execute = pw =>
{
int day = StoreStation.GetDayCounter();
if (deposit) _message = d.Scams.Deposit(p, pw, v, Session.Price) ? L.T("pool.done.in", Fmt.Num(v), p.Name) : L.T("msg.balance");
else
{
double amt = p.Of(pw.Address), got = d.Scams.Withdraw(p, pw, day);
_message = amt <= 0 ? L.T("pool.err.none") : got > 0 ? L.T("pool.done.out", Fmt.Num(got)) : L.T("pool.done.lock", Fmt.Num(amt), day + Tuning.YieldLockDays);
}
Session.Save();
Open();
},
});
}
/// <summary>空投页:可领取的空投(名字、奖励、要求的授权)和当前钱包的授权(可撤销)。领取和撤销都是合约调用,签名时 PLEDGER 上会写明授权范围。</summary>
private static void AirdropPage(CustomUIBuilder b)
{
var d = Session.Data;
var w = d.Hot;
int day = StoreStation.GetDayCounter();
Columns(b, l =>
{
l.AddHeader(L.T("air.h"));
var list = d.Scams.Airdrops.Where(a => !a.Claimed && a.Expire >= day).ToList();
if (list.Count == 0) l.AddLabel(L.T("air.none"));
foreach (var a in list)
{
var ad = a;
l.AddLabel(L.T("air.row", a.Name, Fmt.Num(a.Reward), ApproveText(a.Approve, a.Asset), a.Expire));
l.AddButton(L.T("air.claim"), () => ClaimAirdrop(ad));
}
}, r =>
{
r.AddHeader(L.T("air.approvals", WalletWindow.NameOf(w)));
var aps = d.Scams.Approvals.Where(x => ChainAddress.Same(x.Wallet, w.Address)).ToList();
if (aps.Count == 0) r.AddLabel(L.T("air.approvals.none"));
foreach (var ap in aps)
{
int id = ap.Id;
r.AddLabel(L.T("air.approval", ap.Spender, ApproveText(ap.Limit, ap.Asset)));
r.AddButton(L.T("air.revoke"), () => RevokeApproval(id));
}
});
}
private static string ApproveText(double limit, string asset) =>
limit <= 0 ? L.T("air.noapprove") : double.IsPositiveInfinity(limit) ? L.T("air.unlimited", asset) : L.T("air.limited", asset, Fmt.Num(limit));
private static void ClaimAirdrop(Airdrop a)
{
var d = Session.Data;
_message = L.T("hw.wait");
Open();
var w = d.Hot;
WalletWindow.OpenForBill(new WalletWindow.Bill
{
Contract = true, Asset = Assets.CRC, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
Summary = L.T("air.sum", a.Name, ApproveText(a.Approve, a.Asset)),
Execute = pw =>
{
var ap = d.Scams.Claim(a, pw, StoreStation.GetDayCounter(), d.Rng, out double got);
_message = L.T("air.claimed", Fmt.Num(got));
Log.Info($"领取空投 {a.Name}:得 {got},授权 {(ap == null ? "无" : ap.Asset + " " + ap.Limit)},骗局 {a.Scam}");
Session.Save();
Open();
},
});
}
private static void RevokeApproval(int id)
{
var d = Session.Data;
var ap = d.Scams.Approvals.Find(x => x.Id == id);
if (ap == null) return;
var w = d.Own(ap.Wallet) ?? d.Hot;
_message = L.T("hw.wait");
Open();
WalletWindow.OpenForBill(new WalletWindow.Bill
{
Contract = true, Asset = Assets.CRC, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
Summary = L.T("air.revoke.sum", ap.Spender, ap.Asset),
Execute = pw =>
{
_message = ChainAddress.Same(pw.Address, ap.Wallet) && d.Scams.Revoke(id) ? L.T("air.revoked") : L.T("air.err.wallet");
Session.Save();
Open();
},
});
}
private static bool PonziVisible => Session.Data != null && (Session.Data.Ponzi.Open || Session.Data.Ponzi.Done || StoreStation.GetDayCounter() >= Tuning.PonziMinDay);
private static double _ponziRate = 0.04;
private static bool _ponziConfirm;
@@ -843,6 +975,7 @@ namespace Decentralized
if (_web3Tab == Web3Tab.Stake) { StakePage(b); return; }
if (_web3Tab == Web3Tab.Predict) { PredictPage(b); return; }
if (_web3Tab == Web3Tab.Ponzi) { PonziPage(b); return; }
if (_web3Tab == Web3Tab.Airdrop) { AirdropPage(b); return; }
if (_web3Tab == Web3Tab.Mining) { ReportPage(b); return; }
if (_web3Tab == Web3Tab.Mixer) { MixerPage(b); return; }
if (_web3Tab == Web3Tab.Black) { BlackPage(b); return; }
@@ -1307,7 +1440,7 @@ namespace Decentralized
Open();
}
private enum Web3Tab { Trade, Stake, Predict, Ponzi, Mining, Mixer, Black }
private enum Web3Tab { Trade, Stake, Airdrop, Predict, Ponzi, Mining, Mixer, Black }
private static Web3Tab _web3Tab = Web3Tab.Trade;
private static string _mixAsset = Assets.PSC;
private static int _mixRounds = 2;
@@ -1406,6 +1539,7 @@ namespace Decentralized
}
l.AddSeparator();
l.AddLabel(L.T("spark.price", Fmt.Num(st.Spark)));
YieldPools(l, w, day);
}, r =>
{
r.AddHeader(L.T("stake.ops", WalletWindow.NameOf(w)));