Stage 6.1: CRA staking pool, SPARK balance token with mint/burn

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-04 00:06:16 +08:00
1 parent 58dd93eca6
commit 98e80412e6
12 files changed
+371 -9

No files matched your search

+37 -1
View File
@@ -15,6 +15,8 @@ namespace Decentralized.Domain
public double CraSubsidy = Tuning.CraSubsidy0;
public int CraWarnDay = -1, CraSpiralDay = -1;
public double RevPower = 50;
/// <summary>平衡代币 SPARK 的价格(信用点);SparkPre 是螺旋开始前一刻的价格,螺旋三天按它的倍数走。</summary>
public double Spark = Tuning.SparkStart, SparkPre = -1;
public List<double> RevHistory = new List<double>();
public double Price(string asset)
@@ -24,6 +26,7 @@ namespace Decentralized.Domain
case Assets.CRC: return Crc;
case Assets.CRT: return Crt;
case Assets.CRA: return Cra;
case Assets.SPARK: return Spark;
default: return 1;
}
}
@@ -42,6 +45,8 @@ namespace Decentralized.Domain
Crt = rng.Range(Tuning.CrtDepegMin, Tuning.CrtDepegMax);
CrtDepegEnd = day + rng.Range(Tuning.CrtDepegDaysMin, Tuning.CrtDepegDaysMax);
}
if (ev == MarketEvent.RevRaidUpper) Spark *= Tuning.SparkRaidShock;
if (ev == MarketEvent.LowerUnrest) Spark *= Tuning.SparkUnrestShock;
return 1;
}
@@ -52,6 +57,26 @@ namespace Decentralized.Domain
/// <summary>镇压开始时是否冻结玩家的 CRT:按黑市声望,声望越低越容易被黑市"出卖"。</summary>
public static bool CrtFrozenByCrackdown(double blackMarketRep, Rng rng) => rng.Chance(CrtFreezeChance(blackMarketRep));
/// <summary>铸造:用 CRA 按当前价换 SPARK,收手续费。余额不足返回 -1。</summary>
public double Mint(Wallet w, double cra)
{
if (cra <= 0 || Spark <= 0 || w.Avail(Assets.CRA) + 1e-9 < cra) return -1;
double got = cra * Cra / Spark * (1 - Tuning.SparkFee);
w.Add(Assets.CRA, -cra); w.Add(Assets.SPARK, got);
w.RecordBuy(Assets.SPARK, got, cra * Cra);
return got;
}
/// <summary>销毁:用 SPARK 按当前价换 CRA,收手续费。余额不足返回 -1。</summary>
public double Burn(Wallet w, double spark)
{
if (spark <= 0 || Cra <= 0 || w.Avail(Assets.SPARK) + 1e-9 < spark) return -1;
double got = spark * Spark / Cra * (1 - Tuning.SparkFee);
w.Add(Assets.SPARK, -spark); w.Add(Assets.CRA, got);
w.RecordBuy(Assets.CRA, got, spark * Spark);
return got;
}
public bool CraSubsidyLow => CraSubsidy <= Tuning.CraSubsidy0 * Tuning.CraSubsidyWarn;
/// <summary>CRA 的预兆:外生螺旋的新闻已出,或补贴池告急(利率下调)。螺旋开始后不再算预兆。</summary>
@@ -95,7 +120,18 @@ namespace Decentralized.Domain
}
}
if (CraSpiralDay > 0 && day >= CraSpiralDay)
Cra = Tuning.CraSpiralPath[Math.Min(day - CraSpiralDay, Tuning.CraSpiralPath.Length - 1)];
{
int k = Math.Min(day - CraSpiralDay, Tuning.CraSpiralPath.Length - 1);
Cra = Tuning.CraSpiralPath[k];
if (SparkPre < 0) SparkPre = Spark;
Spark = SparkPre * Tuning.SparkSpiralPath[Math.Min(day - CraSpiralDay, Tuning.SparkSpiralPath.Length - 1)];
}
else
{
Spark *= Math.Exp(rng.Gauss() * Tuning.SparkVol);
Spark += (Tuning.SparkStart - Spark) * Tuning.SparkRevert;
Spark = Math.Max(Tuning.SparkStart * Tuning.SparkMinMult, Math.Min(Tuning.SparkStart * Tuning.SparkMaxMult, Spark));
}
return crcFactor;
}
}
+90
View File
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Decentralized.Domain
{
/// <summary>一个钱包在革命军质押池里的仓位:计息的本金、排队等解锁的部分,以及存入时带来的污点与成本。</summary>
public sealed class StakeLot
{
public string Wallet;
public double Amount;
public List<KeyValuePair<int, double>> Unlocking = new List<KeyValuePair<int, double>>();
public double Taint, Basis;
public double Total => Amount + Unlocking.Sum(u => u.Value);
}
/// <summary>一次到期或利息的结果,供夜间日志使用。</summary>
public sealed class StakeEvent
{
public string Wallet;
public double Interest, Released;
}
/// <summary>
/// 革命军 CRA 质押池:质押的 CRA 每晚按日息计息(利息并入本金),取出要先申请,等 <see cref="Tuning.CraUnlockDays"/> 天,期间不计息、
/// 也不能提前取。到期自动回到质押时的钱包。质押的币不会被冻结,但仓位里仍是 CRA:螺旋来临时到期取回的币只值当时的价格。
/// 污点与成本随币存入、按比例随币取出。
/// </summary>
public sealed class Staking
{
public List<StakeLot> Lots = new List<StakeLot>();
public StakeLot Of(string wallet) => Lots.Find(l => ChainAddress.Same(l.Wallet, wallet));
/// <summary>全部计息中的质押量,补贴池按它算支出。</summary>
public double Earning => Lots.Sum(l => l.Amount);
/// <summary>质押:从钱包取出 CRA,污点与成本按价值比例随币留在仓位里。余额不足返回 false。</summary>
public bool Stake(Wallet w, double amount, Func<string, double> price)
{
if (amount <= 0 || w.Avail(Assets.CRA) + 1e-9 < amount) return false;
double total = w.Value(price);
double share = total > 0 ? Math.Min(1, amount * price(Assets.CRA) / total) : 0;
double taint = w.Taint * share, basis = w.Basis * share;
w.Add(Assets.CRA, -amount); w.Taint -= taint; w.Basis -= basis;
var lot = Of(w.Address) ?? AddLot(w.Address);
lot.Amount += amount; lot.Taint += taint; lot.Basis += basis;
return true;
}
private StakeLot AddLot(string wallet) { var l = new StakeLot { Wallet = wallet }; Lots.Add(l); return l; }
/// <summary>申请取出:这部分从计息本金里挪到排队区,day + 解锁天数 到期。额度不足返回 false。</summary>
public bool RequestUnstake(string wallet, double amount, int day)
{
var lot = Of(wallet);
if (lot == null || amount <= 0 || lot.Amount + 1e-9 < amount) return false;
lot.Amount -= amount;
lot.Unlocking.Add(new KeyValuePair<int, double>(day + Tuning.CraUnlockDays, amount));
return true;
}
/// <summary>每晚:计息(日息 rate,利息并入本金),到期的部分回到钱包。返回有变化的仓位。</summary>
public List<StakeEvent> Night(int day, double rate, Func<string, Wallet> wallet)
{
var evs = new List<StakeEvent>();
foreach (var lot in Lots.ToList())
{
var ev = new StakeEvent { Wallet = lot.Wallet };
ev.Interest = lot.Amount * rate;
lot.Amount += ev.Interest;
foreach (var u in lot.Unlocking.Where(x => day >= x.Key).ToList())
{
double total = lot.Total;
double share = total > 0 ? Math.Min(1, u.Value / total) : 0;
double taint = lot.Taint * share, basis = lot.Basis * share;
lot.Taint -= taint; lot.Basis -= basis;
lot.Unlocking.Remove(u);
var w = wallet(lot.Wallet);
w.Add(Assets.CRA, u.Value); w.Taint += taint; w.Basis += basis;
ev.Released += u.Value;
}
if (lot.Total < 1e-9) Lots.Remove(lot);
if (ev.Interest > 0 || ev.Released > 0) evs.Add(ev);
}
return evs;
}
}
}
+5
View File
@@ -55,6 +55,11 @@ namespace Decentralized.Domain
public const double CraSpiralWeekly = 0.02;
public const int CraWarnLeadMin = 4, CraWarnLeadMax = 7;
public static readonly double[] CraSpiralPath = { 0.85, 0.30, 0.02 };
/// <summary>平衡代币 SPARK:起始价与长期锚(信用点)、日波动率、每晚向锚回归的比例、价格上下限(锚的倍数);铸销手续费;
/// 革命军袭击上层 / 下层动荡开始时的冲击;CRA 死亡螺旋三天里相对螺旋前价格的倍数。</summary>
public const double SparkStart = 10, SparkVol = 0.08, SparkRevert = 0.03, SparkMinMult = 0.3, SparkMaxMult = 3, SparkFee = 0.003;
public const double SparkRaidShock = 1.15, SparkUnrestShock = 0.90;
public static readonly double[] SparkSpiralPath = { 0.8, 0.3, 0.05 };
// 合规出金与 AML(阈值以当前房租为单位)
public const double AmlBigRents = 5, AmlCumRents = 15;