污点按币种记脏枚数、只在 Web3 钱包里;Poinbase 只收干净的币;删除出金审查;永续操纵只记罪证;泄露记罪证;剧情替换;存档迁移到数据版本 5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f0a6051591
commit
34b70023c9
37 files changed
+744
-647
No files matched your search
@@ -1,76 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
public enum AmlOutcome { Clear, Light, Heavy }
|
||||
|
||||
/// <summary>一次合规出金的结果。罪证由接入层记进游戏的罪证账本(销赃)。</summary>
|
||||
/// <summary>一次出金到银行的结果。</summary>
|
||||
public sealed class CashoutResult
|
||||
{
|
||||
public double Requested;
|
||||
public double Received;
|
||||
public double Confiscated;
|
||||
public double Crime;
|
||||
public double Tax;
|
||||
public double HitChance;
|
||||
public AmlOutcome Outcome;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合规交易所出金:CRC 兑回信用点,过 AML。审查按单笔金额、7 天累计金额、污点比例、是否来自混币器或被标记地址判定。
|
||||
/// 轻度命中(污点 < 30%)没收污点部分、不记罪证;重度命中没收污点部分,并把污点额记为销赃罪证。
|
||||
/// 合规交易所 Poinbase:只收干净的币(钱包往充值地址最多转“价值 − 污点”,见 <see cref="Wallet.TransferClean"/>),账户没有污点。
|
||||
/// 出金到银行把 CRC 兑回信用点,扣手续费,只对超出成本的部分计税,不做审查。
|
||||
/// </summary>
|
||||
public sealed class Exchange
|
||||
{
|
||||
/// <summary>Poinbase 上架的币:只有这些能充值、能在现货交易。黑市的 CRT、革命军的 CRA 合规交易所不收。</summary>
|
||||
public static readonly string[] Listed = { Assets.PSC, Assets.CRC, Assets.SPARK };
|
||||
|
||||
/// <summary>最近的出金记录:(天, 金额)。</summary>
|
||||
public List<KeyValuePair<int, double>> History = new List<KeyValuePair<int, double>>();
|
||||
|
||||
public static double HitChance(double amount, double taint, double recentTotal, double rent, bool fromMixerOrFlagged)
|
||||
/// <summary>存档迁移(数据版本 5):旧存档里 Poinbase 账户和永续仓位带的污点额(信用点),清零后返回总数,由调用方挪到 1 号钱包一起摊。</summary>
|
||||
public static double TakeLegacyTaint(Wallet account, System.Collections.Generic.IEnumerable<PerpPosition> positions)
|
||||
{
|
||||
if (amount <= 0) return 0;
|
||||
double ratio = taint / amount;
|
||||
double big = Tuning.AmlBigRents * rent, cum = Tuning.AmlCumRents * rent;
|
||||
double p = 0;
|
||||
if (ratio >= Tuning.AmlTaintRatio) p += ratio * Tuning.AmlTaintMult;
|
||||
p += Math.Max(0, (amount - big) / big) * Tuning.AmlExcessWeight;
|
||||
p += Math.Max(0, (recentTotal + amount - cum) / cum) * Tuning.AmlExcessWeight;
|
||||
if (fromMixerOrFlagged) p += Tuning.AmlMixerBonus;
|
||||
return Math.Min(Tuning.AmlMaxChance, p);
|
||||
double t = account.LegacyTaint;
|
||||
account.LegacyTaint = 0;
|
||||
foreach (var p in positions) { t += p.LegacyTaint; p.LegacyTaint = 0; }
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从地址里取出 amount 个 CRC 兑成信用点(按 CRC 当前价格,平时为 1;脱锚期间按市价),扣手续费。污点与成本按比例随取出部分计算;
|
||||
/// 从账户里取出 amount 个 CRC 兑成信用点(按 CRC 当前价格,平时为 1;脱锚期间按市价),扣手续费。成本按取出部分的价值比例带走,
|
||||
/// 只对超出成本的部分计税。
|
||||
/// </summary>
|
||||
public CashoutResult Cashout(Wallet wallet, double amount, Func<string, double> price, int day, double rent, bool fromMixer, Rng rng)
|
||||
public CashoutResult Cashout(Wallet account, double amount, Func<string, double> price)
|
||||
{
|
||||
amount = Math.Min(amount, wallet.Get(Assets.CRC));
|
||||
amount = Math.Min(amount, account.Get(Assets.CRC));
|
||||
var r = new CashoutResult { Requested = amount };
|
||||
if (amount <= 0) return r;
|
||||
double total = wallet.Value(price);
|
||||
double share = total > 0 ? Math.Min(1, amount * price(Assets.CRC) / total) : 0;
|
||||
double taint = wallet.Taint * share, basis = wallet.Basis * share;
|
||||
History.RemoveAll(h => h.Key <= day - Tuning.AmlWindowDays);
|
||||
double recent = History.Sum(h => h.Value);
|
||||
double total = account.Value(price);
|
||||
double value = amount * price(Assets.CRC);
|
||||
r.HitChance = Math.Min(1, HitChance(value, taint, recent, rent, fromMixer || wallet.Flagged) * StoryMods.Current.Aml(day));
|
||||
wallet.Add(Assets.CRC, -amount);
|
||||
wallet.Taint -= taint;
|
||||
wallet.Basis -= basis;
|
||||
History.Add(new KeyValuePair<int, double>(day, value));
|
||||
double share = total > 0 ? Math.Min(1, value / total) : 0;
|
||||
double basis = account.Basis * share;
|
||||
account.Add(Assets.CRC, -amount);
|
||||
account.Basis -= basis;
|
||||
double got = value * (1 - Tuning.ExchangeFee);
|
||||
if (rng.Chance(r.HitChance))
|
||||
{
|
||||
r.Outcome = taint / value >= Tuning.AmlHeavyRatio ? AmlOutcome.Heavy : AmlOutcome.Light;
|
||||
r.Confiscated = Math.Min(got, taint);
|
||||
got -= r.Confiscated;
|
||||
if (r.Outcome == AmlOutcome.Heavy) r.Crime = taint;
|
||||
}
|
||||
r.Tax = Math.Max(0, got - basis) * Tuning.TaxRate;
|
||||
r.Received = got;
|
||||
return r;
|
||||
|
||||
Reference in new issue
Block a user