Files
probably-decentralized/src/Decentralized.Domain/Wallet.cs
T

73 lines
3.4 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
namespace Decentralized.Domain
{
/// <summary>
/// 一个链上地址:各币种余额,加上整个地址的污点额与成本(信用点等值)。
/// 污点和成本按转出的价值比例随币走。
/// </summary>
public sealed class Wallet
{
public string Address;
public Dictionary<string, double> Balances = new Dictionary<string, double>();
/// <summary>污点额(信用点等值)。</summary>
public double Taint;
/// <summary>成本(信用点):出金时只对超出成本的部分征税。</summary>
public double Basis;
/// <summary>被执法标记。</summary>
public bool Flagged;
/// <summary>买入记录:每种币买入的数量与花费(信用点等值),用来显示买入均价。卖出或转出时按比例减少。</summary>
public Dictionary<string, double> BuyQty = new Dictionary<string, double>(), BuyCost = new Dictionary<string, double>();
public void RecordBuy(string asset, double qty, double costCredits)
{
if (qty <= 0) return;
BuyQty[asset] = (BuyQty.TryGetValue(asset, out var q) ? q : 0) + qty;
BuyCost[asset] = (BuyCost.TryGetValue(asset, out var c) ? c : 0) + Math.Max(0, costCredits);
}
/// <summary>买入均价(信用点);没有买入记录(例如全是挖出来的)返回 null。</summary>
public double? AvgBuy(string asset) => BuyQty.TryGetValue(asset, out var q) && q > 1e-9 ? BuyCost[asset] / q : (double?)null;
public double Get(string asset) => Balances.TryGetValue(asset, out var v) ? v : 0;
public void Add(string asset, double amount)
{
if (amount == 0) return;
if (amount < 0 && BuyQty.TryGetValue(asset, out var bq))
{
double before = Get(asset), keep = before > 1e-12 ? Math.Max(0, 1 + amount / before) : 0;
BuyQty[asset] = bq * keep; BuyCost[asset] *= keep;
if (BuyQty[asset] < 1e-9) { BuyQty.Remove(asset); BuyCost.Remove(asset); }
}
double v = Get(asset) + amount;
if (v < -1e-9) throw new InvalidOperationException($"{Address}: {asset} 余额不足");
if (Math.Abs(v) < 1e-12) Balances.Remove(asset);
else Balances[asset] = v;
}
/// <summary>按价格表估值(信用点)。</summary>
public double Value(Func<string, double> price) => Balances.Sum(kv => kv.Value * price(kv.Key));
/// <summary>
/// 转出:taint 和 basis 按转出价值占地址总价值的比例带走。Ponero 等不可追踪资产转入时,污点不随之进入目标地址。
/// </summary>
public static void Transfer(Wallet from, Wallet to, string asset, double amount, Func<string, double> price)
{
if (amount <= 0) return;
double total = from.Value(price);
double share = total > 0 ? Math.Min(1.0, amount * price(asset) / total) : 0;
double taint = from.Taint * share, basis = from.Basis * share;
from.Add(asset, -amount);
from.Taint -= taint;
from.Basis -= basis;
to.Add(asset, amount);
var spec = Assets.Get(asset);
if (spec == null || spec.Traceable) to.Taint += taint;
to.Basis += basis;
}
}
}