Wallets: expected-days drain, copy/paste, three-column selectable balances, rename and destroy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
783f73989d
commit
2b781acd82
10 files changed
+178
-22
No files matched your search
@@ -13,6 +13,11 @@
|
|||||||
- 多钱包:每个硬件钱包的余额、污点、成本、冻结各算各的。终端新增"钱包"页:列出全部钱包(名称、地址、估值、污点),"打开"弹出这个钱包自己的 PLEDGER 窗口(几台可以同时开),"连接"设为当前钱包。Web3 页的交易、黑市、混币器用当前钱包;用币付款的顾客、挖矿收益进当前钱包;订单退款、出售回款、混币提回回到当初付款的钱包。账单可以用任何一台钱包付。钱包之间转账走签名,污点按比例跟着走。
|
- 多钱包:每个硬件钱包的余额、污点、成本、冻结各算各的。终端新增"钱包"页:列出全部钱包(名称、地址、估值、污点),"打开"弹出这个钱包自己的 PLEDGER 窗口(几台可以同时开),"连接"设为当前钱包。Web3 页的交易、黑市、混币器用当前钱包;用币付款的顾客、挖矿收益进当前钱包;订单退款、出售回款、混币提回回到当初付款的钱包。账单可以用任何一台钱包付。钱包之间转账走签名,污点按比例跟着走。
|
||||||
- Poinbase 拆成托管账户:银行存入进 Poinbase,出金审查只看 Poinbase 账户的污点;Poinbase 页"提到当前钱包"直接转,不用签名;钱包充值要转到 Poinbase 的充值地址(页面上显示),只收 CRC 和 PSC。
|
- Poinbase 拆成托管账户:银行存入进 Poinbase,出金审查只看 Poinbase 账户的污点;Poinbase 页"提到当前钱包"直接转,不用签名;钱包充值要转到 Poinbase 的充值地址(页面上显示),只收 CRC 和 PSC。
|
||||||
- PLEDGER 硬件钱包(物品):和游戏的升级材料一样,拖到交易终端上用掉,钱包 +1,最多 5 个。杰克逊博士进货时(钱包没满)50% 上一个,价值 240,正品;Web3 黑市平台现货每周 50% 上 1–2 个,150–180,其中 20% 被投毒。外观和说明完全一样。被投毒的钱包币的总价值到 3000 起,每晚有概率被转空(1/7 起,按价值线性升到 20000 时当晚必转),晨间日志只写"钱包 n:币被转走了"。
|
- PLEDGER 硬件钱包(物品):和游戏的升级材料一样,拖到交易终端上用掉,钱包 +1,最多 5 个。杰克逊博士进货时(钱包没满)50% 上一个,价值 240,正品;Web3 黑市平台现货每周 50% 上 1–2 个,150–180,其中 20% 被投毒。外观和说明完全一样。被投毒的钱包币的总价值到 3000 起,每晚有概率被转空(1/7 起,按价值线性升到 20000 时当晚必转),晨间日志只写"钱包 n:币被转走了"。
|
||||||
|
- 被投毒的钱包按期望天数算:门槛(3000)处期望 7 天,按价值线性降到 20000 处 1 天,每晚概率 = 1 / 期望天数(中间值期望 4 天、每晚 25%,模拟 4000 次平均 4 天)。
|
||||||
|
- 复制粘贴:终端账单的收款地址、Poinbase 充值地址、钱包页每个钱包、PLEDGER 主页的本钱包地址都有"复制";PLEDGER 发送页收款地址旁有"粘贴"。用的是系统剪贴板。
|
||||||
|
- PLEDGER 主页余额改成三列(币种 | 数量 | 估值)右对齐,五种币全部显示(含 0,冻结的另注);点一行选中,整行反色高亮,发送时默认用选中的币。
|
||||||
|
- 钱包页:每个钱包可以改名(留空恢复默认,最长 16 字),名字在终端和 PLEDGER 上都用;非主钱包可以销毁(点两次确认,钱包里还有币时不让销毁)。钱包编号固定,销毁别的钱包后名字不会错位。
|
||||||
|
- 游戏内验证(这一轮):复制钱包 3 地址后在主钱包发送页粘贴,地址正确;选中 CRT 行高亮,进发送页默认 CRT;向钱包 3 转 1 CRT 后点销毁被拦下;钱包 2 二次确认后销毁,钱包 3 仍叫"钱包 3";钱包 2 改名为 cold,终端与 PLEDGER 都显示新名字;三列余额对齐。PLEDGER 上名字被标题样式转成大写,已改成普通粗体(编译通过,没重进游戏看)。没有验证:游戏输入框里直接 Ctrl+V。
|
||||||
- 存档不兼容旧版:链上的币归到新建的主钱包,Poinbase 账户从 0 开始。
|
- 存档不兼容旧版:链上的币归到新建的主钱包,Poinbase 账户从 0 开始。
|
||||||
- 游戏内验证(多钱包):两件硬件钱包(一正一投毒)作用于终端,钱包页出现钱包 2、钱包 3,标记看不出来;同时打开两台 PLEDGER;主钱包向钱包 3 转 100 PSC,核对页显示"自己的钱包:钱包 3",污点 5000 按 10000/16000 带走 3125;过一夜钱包 3 被转空(约 9393),晨间日志有记录;银行存入 500 进 Poinbase,提 200 CRC 到主钱包,再从主钱包向充值地址转 100 CRC,Poinbase 300 → 400,带过去污点 31。商城平台现货出现硬件钱包(158)。没有在游戏里验证:用鼠标拖拽(用探查通道执行了同一个"物品作用于物品"接口)、杰克逊柜台上的钱包和它的标价、Poinbase 空账户和"账户污点比例"两处改字(已编译,没重进游戏看)。
|
- 游戏内验证(多钱包):两件硬件钱包(一正一投毒)作用于终端,钱包页出现钱包 2、钱包 3,标记看不出来;同时打开两台 PLEDGER;主钱包向钱包 3 转 100 PSC,核对页显示"自己的钱包:钱包 3",污点 5000 按 10000/16000 带走 3125;过一夜钱包 3 被转空(约 9393),晨间日志有记录;银行存入 500 进 Poinbase,提 200 CRC 到主钱包,再从主钱包向充值地址转 100 CRC,Poinbase 300 → 400,带过去污点 31。商城平台现货出现硬件钱包(158)。没有在游戏里验证:用鼠标拖拽(用探查通道执行了同一个"物品作用于物品"接口)、杰克逊柜台上的钱包和它的标价、Poinbase 空账户和"账户污点比例"两处改字(已编译,没重进游戏看)。
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ namespace Decentralized.Domain
|
|||||||
/// <summary>硬件钱包:最多几个(含主钱包);杰克逊博士的售价与每次进货上架的概率;Web3 商城的价格范围、每周平台现货上架的概率与件数上限;商城货里被投毒的比例。</summary>
|
/// <summary>硬件钱包:最多几个(含主钱包);杰克逊博士的售价与每次进货上架的概率;Web3 商城的价格范围、每周平台现货上架的概率与件数上限;商城货里被投毒的比例。</summary>
|
||||||
public const int WalletMax = 5, WalletJacksonPrice = 240, WalletMallMin = 150, WalletMallMax = 180, WalletMallQtyMax = 2;
|
public const int WalletMax = 5, WalletJacksonPrice = 240, WalletMallMin = 150, WalletMallMax = 180, WalletMallQtyMax = 2;
|
||||||
public const double WalletJacksonChance = 0.5, WalletMallChance = 0.5, WalletPoisonShare = 0.2;
|
public const double WalletJacksonChance = 0.5, WalletMallChance = 0.5, WalletPoisonShare = 0.2;
|
||||||
/// <summary>被投毒的钱包:币的总价值达到 DrainMin 起,每晚有概率被转空;价值越高越快:每晚概率从门槛处的 1 / SlowDays(平均 7 天)按价值线性升到 DrainFull 处的 1(当晚就转走)。</summary>
|
/// <summary>被投毒的钱包:币的总价值达到 DrainMin 起,每晚有概率被转空;价值越高越快:期望天数从门槛处的 SlowDays(7 天)按价值线性降到 DrainFull 处的 1 天,每晚概率 = 1 / 期望天数。</summary>
|
||||||
public const double WalletDrainMin = 3000, WalletDrainFull = 20000, WalletDrainSlowDays = 7;
|
public const double WalletDrainMin = 3000, WalletDrainFull = 20000, WalletDrainSlowDays = 7;
|
||||||
|
|
||||||
/// <summary>主交易对(信用点一侧)的深度随天数增长。</summary>
|
/// <summary>主交易对(信用点一侧)的深度随天数增长。</summary>
|
||||||
|
|||||||
@@ -18,19 +18,23 @@ namespace Decentralized.Domain
|
|||||||
public double Basis;
|
public double Basis;
|
||||||
/// <summary>被执法标记。</summary>
|
/// <summary>被执法标记。</summary>
|
||||||
public bool Flagged;
|
public bool Flagged;
|
||||||
/// <summary>显示名(终端钱包页、硬件钱包标题)。</summary>
|
/// <summary>钱包编号("钱包 n"),添加时定下,销毁别的钱包也不变。</summary>
|
||||||
public string Name;
|
public string Name;
|
||||||
|
/// <summary>玩家起的名字;空时用默认名。</summary>
|
||||||
|
public string Label;
|
||||||
/// <summary>硬件钱包被动过手脚(供应链投毒):别人手里有助记词。玩家看不到。</summary>
|
/// <summary>硬件钱包被动过手脚(供应链投毒):别人手里有助记词。玩家看不到。</summary>
|
||||||
public bool Poisoned;
|
public bool Poisoned;
|
||||||
|
|
||||||
/// <summary>被投毒的钱包今晚被转空的概率:总价值不到门槛时为 0,越多越高(1–7 天)。</summary>
|
/// <summary>被投毒的钱包被转空前的期望天数:门槛处 7 天,按价值线性降到满额处 1 天;不到门槛不会被转。</summary>
|
||||||
public static double DrainChance(double value)
|
public static double DrainDays(double value)
|
||||||
{
|
{
|
||||||
if (value < Tuning.WalletDrainMin) return 0;
|
double t = Math.Min(1, (value - Tuning.WalletDrainMin) / (Tuning.WalletDrainFull - Tuning.WalletDrainMin));
|
||||||
double slow = 1 / Tuning.WalletDrainSlowDays, t = Math.Min(1, (value - Tuning.WalletDrainMin) / (Tuning.WalletDrainFull - Tuning.WalletDrainMin));
|
return Tuning.WalletDrainSlowDays - (Tuning.WalletDrainSlowDays - 1) * t;
|
||||||
return slow + (1 - slow) * t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>被投毒的钱包今晚被转空的概率 = 1 / 期望天数(每晚独立掷一次)。</summary>
|
||||||
|
public static double DrainChance(double value) => value < Tuning.WalletDrainMin ? 0 : 1 / DrainDays(value);
|
||||||
|
|
||||||
/// <summary>转空:全部余额(含冻结的)连同污点、成本一起转走,返回转走的价值(信用点)。</summary>
|
/// <summary>转空:全部余额(含冻结的)连同污点、成本一起转走,返回转走的价值(信用点)。</summary>
|
||||||
public double Drain(Func<string, double> price)
|
public double Drain(Func<string, double> price)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ namespace Decentralized
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>系统剪贴板(游戏里的输入框也能 Ctrl+V)。</summary>
|
||||||
|
public static class Clipboard
|
||||||
|
{
|
||||||
|
public static void Copy(string s) => GUIUtility.systemCopyBuffer = s ?? "";
|
||||||
|
public static string Paste() => (GUIUtility.systemCopyBuffer ?? "").Trim();
|
||||||
|
}
|
||||||
|
|
||||||
public static class Fmt
|
public static class Fmt
|
||||||
{
|
{
|
||||||
public static string Num(double v) => Math.Abs(v) >= 100 ? v.ToString("N0") : Math.Abs(v) >= 1 ? v.ToString("0.##") : v.ToString("0.####");
|
public static string Num(double v) => Math.Abs(v) >= 100 ? v.ToString("N0") : Math.Abs(v) >= 1 ? v.ToString("0.##") : v.ToString("0.####");
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ black.kind.Contraband.3,Contraband (high)
|
|||||||
black.kind.Tech,High-tech (markup)
|
black.kind.Tech,High-tech (markup)
|
||||||
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
|
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
|
||||||
item.web3guide.name,Web3 Handbook
|
item.web3guide.name,Web3 Handbook
|
||||||
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometimes turns into enforcement at your door: signing logs the crime on the spot and seizes the goods, refusing only costs the payment. Selling: on the black market's Sell tab, list contraband, high-tech goods and GPUs from storage at 0.6x to 1.8x the reference price. The higher the multiplier, the slower the sale (below 0.9x about a day, 1.0x about two, 1.8x about fourteen), and the likelier and larger the taint on the payout, which also grows with how much you sold in the last 7 days; at most 10 listings at a time, 5% listing fee, repricing a listing costs 2% and the sale speed follows the new multiplier, 8% commission, payout held 3 days before reaching your wallet; a fake card may be spotted and returned, hurting your rating; if the platform rugs, listed goods and unreleased payouts are gone. Anonymous-coin swap (Web3 trade page, needs black-market standing): Ponero is a privacy coin and swapping in wipes the taint on that value; 6% fee each way with a daily limit; coin swapped out of Ponero has no origin and cash-out screening gets stricter."
|
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page once its coins are moved out. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometimes turns into enforcement at your door: signing logs the crime on the spot and seizes the goods, refusing only costs the payment. Selling: on the black market's Sell tab, list contraband, high-tech goods and GPUs from storage at 0.6x to 1.8x the reference price. The higher the multiplier, the slower the sale (below 0.9x about a day, 1.0x about two, 1.8x about fourteen), and the likelier and larger the taint on the payout, which also grows with how much you sold in the last 7 days; at most 10 listings at a time, 5% listing fee, repricing a listing costs 2% and the sale speed follows the new multiplier, 8% commission, payout held 3 days before reaching your wallet; a fake card may be spotted and returned, hurting your rating; if the platform rugs, listed goods and unreleased payouts are gone. Anonymous-coin swap (Web3 trade page, needs black-market standing): Ponero is a privacy coin and swapping in wipes the taint on that value; 6% fee each way with a daily limit; coin swapped out of Ponero has no origin and cash-out screening gets stricter."
|
||||||
courier.odd1,...Package is here.
|
courier.odd1,...Package is here.
|
||||||
courier.odd2,"(His hand stays in his pocket.) Sign here."
|
courier.odd2,"(His hand stays in his pocket.) Sign here."
|
||||||
courier.sign,Sign
|
courier.sign,Sign
|
||||||
@@ -355,7 +355,6 @@ black.desk.pair,"PONERO [anonymous coin] price {0}"
|
|||||||
black.pay.amount,"Amount: {0} {1} (about {2} credits)"
|
black.pay.amount,"Amount: {0} {1} (about {2} credits)"
|
||||||
black.pay.wallet,Pay from wallet
|
black.pay.wallet,Pay from wallet
|
||||||
hw.wallet.main,Main wallet
|
hw.wallet.main,Main wallet
|
||||||
hw.balance,"{0} {1} ≈ {2}"
|
|
||||||
hw.total,"Total ≈ {0} credits"
|
hw.total,"Total ≈ {0} credits"
|
||||||
hw.pending.bill,"Unpaid: {0}"
|
hw.pending.bill,"Unpaid: {0}"
|
||||||
hw.pending.sign,"To sign: {0}"
|
hw.pending.sign,"To sign: {0}"
|
||||||
@@ -415,3 +414,16 @@ nightlog.drained,"The coins in {0} were moved out, about {1} credits."
|
|||||||
item.hwwallet.name,PLEDGER hardware wallet
|
item.hwwallet.name,PLEDGER hardware wallet
|
||||||
item.hwwallet.desc,Large-screen hardware wallet. Drag it onto the trading terminal to add a wallet (up to 5).
|
item.hwwallet.desc,Large-screen hardware wallet. Drag it onto the trading terminal to add a wallet (up to 5).
|
||||||
poin.empty,The account is empty.
|
poin.empty,The account is empty.
|
||||||
|
hw.copy,Copy
|
||||||
|
hw.paste,Paste
|
||||||
|
hw.copied,Address copied.
|
||||||
|
hw.copied.addr,"Copied {0}"
|
||||||
|
hw.balance.frozen," (frozen {0})"
|
||||||
|
wallets.destroy,Destroy
|
||||||
|
wallets.destroy.sure,Confirm
|
||||||
|
wallets.destroy.notempty,"{0} still holds coins. Move them out first."
|
||||||
|
wallets.destroyed,"Destroyed {0}: {1}"
|
||||||
|
hw.balance.value,"≈ {0}"
|
||||||
|
wallets.rename,Rename
|
||||||
|
wallets.rename.h,"New name for {0}"
|
||||||
|
wallets.rename.ok,OK
|
||||||
|
@@ -267,7 +267,7 @@ black.kind.Contraband.3,违禁品(高)
|
|||||||
black.kind.Tech,高科技物品(加价)
|
black.kind.Tech,高科技物品(加价)
|
||||||
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
|
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
|
||||||
item.web3guide.name,Web3 手册
|
item.web3guide.name,Web3 手册
|
||||||
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了。匿名币兑换(Web3 交易页,要黑市声望):Ponero 是隐私币,兑入时这部分价值的污点被清除;兑入、兑出各收 6% 手续费,每天有额度;从 Ponero 兑出的币来路不明,出金审查会加重。"
|
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。不要的钱包可以在钱包页销毁,要先把币转走。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了。匿名币兑换(Web3 交易页,要黑市声望):Ponero 是隐私币,兑入时这部分价值的污点被清除;兑入、兑出各收 6% 手续费,每天有额度;从 Ponero 兑出的币来路不明,出金审查会加重。"
|
||||||
courier.odd1,……东西到了。
|
courier.odd1,……东西到了。
|
||||||
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
|
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
|
||||||
courier.sign,签收
|
courier.sign,签收
|
||||||
@@ -354,7 +354,6 @@ black.desk.pair,"PONERO [匿名币] 价格 {0}"
|
|||||||
black.pay.amount,"金额:{0} {1}(约 {2} 信用点)"
|
black.pay.amount,"金额:{0} {1}(约 {2} 信用点)"
|
||||||
black.pay.wallet,打开钱包转账
|
black.pay.wallet,打开钱包转账
|
||||||
hw.wallet.main,主钱包
|
hw.wallet.main,主钱包
|
||||||
hw.balance,"{0} {1} ≈ {2}"
|
|
||||||
hw.total,"合计 ≈ {0} 信用点"
|
hw.total,"合计 ≈ {0} 信用点"
|
||||||
hw.pending.bill,"待付:{0}"
|
hw.pending.bill,"待付:{0}"
|
||||||
hw.pending.sign,"待签名:{0}"
|
hw.pending.sign,"待签名:{0}"
|
||||||
@@ -414,3 +413,16 @@ nightlog.drained,"{0}:币被转走了,约 {1} 信用点。"
|
|||||||
item.hwwallet.name,PLEDGER 硬件钱包
|
item.hwwallet.name,PLEDGER 硬件钱包
|
||||||
item.hwwallet.desc,大屏硬件钱包。拖到交易终端上,添加一个钱包(最多 5 个)。
|
item.hwwallet.desc,大屏硬件钱包。拖到交易终端上,添加一个钱包(最多 5 个)。
|
||||||
poin.empty,账户为空。
|
poin.empty,账户为空。
|
||||||
|
hw.copy,复制
|
||||||
|
hw.paste,粘贴
|
||||||
|
hw.copied,地址已复制。
|
||||||
|
hw.copied.addr,"已复制 {0}"
|
||||||
|
hw.balance.frozen,"(冻结 {0})"
|
||||||
|
wallets.destroy,销毁
|
||||||
|
wallets.destroy.sure,确认销毁
|
||||||
|
wallets.destroy.notempty,"{0}:还有币,先转走。"
|
||||||
|
wallets.destroyed,"已销毁{0}:{1}"
|
||||||
|
hw.balance.value,"≈ {0}"
|
||||||
|
wallets.rename,改名
|
||||||
|
wallets.rename.h,"{0} 新名字"
|
||||||
|
wallets.rename.ok,确定
|
||||||
|
@@ -37,9 +37,12 @@ namespace Decentralized
|
|||||||
|
|
||||||
public Wallet Own(string address) => Wallets.Find(w => ChainAddress.Same(w.Address, address));
|
public Wallet Own(string address) => Wallets.Find(w => ChainAddress.Same(w.Address, address));
|
||||||
|
|
||||||
|
/// <summary>钱包编号:每添加一个加一,销毁后不回收,名字不会错位。</summary>
|
||||||
|
public int WalletSeq;
|
||||||
|
|
||||||
public Wallet AddWallet(bool poisoned)
|
public Wallet AddWallet(bool poisoned)
|
||||||
{
|
{
|
||||||
var w = new Wallet { Address = ChainAddress.Make(Rng.NextUInt()), Poisoned = poisoned };
|
var w = new Wallet { Address = ChainAddress.Make(Rng.NextUInt()), Poisoned = poisoned, Name = (++WalletSeq).ToString() };
|
||||||
Wallets.Add(w);
|
Wallets.Add(w);
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,10 @@ namespace Decentralized
|
|||||||
Holdings(l, w, a => true);
|
Holdings(l, w, a => true);
|
||||||
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
|
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
|
||||||
l.AddSeparator();
|
l.AddSeparator();
|
||||||
|
l.BeginRow(8f, TextAnchor.MiddleLeft);
|
||||||
l.AddLabel(L.T("poin.deposit", d.Address("poinbase")));
|
l.AddLabel(L.T("poin.deposit", d.Address("poinbase")));
|
||||||
|
l.AddButton(L.T("hw.copy"), () => { Clipboard.Copy(d.Address("poinbase")); _message = L.T("hw.copied.addr", d.Address("poinbase")); Open(); });
|
||||||
|
l.End();
|
||||||
}, r =>
|
}, r =>
|
||||||
{
|
{
|
||||||
r.AddHeader(L.T("trade.h"));
|
r.AddHeader(L.T("trade.h"));
|
||||||
@@ -276,7 +279,7 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
b.AddLabel(L.T("wallets.count", d.Wallets.Count, Tuning.WalletMax));
|
b.AddLabel(L.T("wallets.count", d.Wallets.Count, Tuning.WalletMax));
|
||||||
var cols = new[] { 130f, 130f, 120f, 110f, 220f };
|
var cols = new[] { 100f, 110f, 85f, 70f, 375f };
|
||||||
TableHeader(b, cols, L.T("wallets.col.name"), L.T("wallets.col.addr"), L.T("col.value"), L.T("wallets.col.taint"), "");
|
TableHeader(b, cols, L.T("wallets.col.name"), L.T("wallets.col.addr"), L.T("col.value"), L.T("wallets.col.taint"), "");
|
||||||
for (int i = 0; i < d.Wallets.Count; i++)
|
for (int i = 0; i < d.Wallets.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -290,12 +293,62 @@ namespace Decentralized
|
|||||||
b.BeginPanel(cols[4], 30f, TextAnchor.MiddleLeft, 2f, 0f);
|
b.BeginPanel(cols[4], 30f, TextAnchor.MiddleLeft, 2f, 0f);
|
||||||
b.BeginRow(4f);
|
b.BeginRow(4f);
|
||||||
b.AddButton(L.T("wallets.open"), () => WalletWindow.Open(w));
|
b.AddButton(L.T("wallets.open"), () => WalletWindow.Open(w));
|
||||||
|
b.AddButton(L.T("hw.copy"), () => { Clipboard.Copy(w.Address); _message = L.T("hw.copied.addr", w.Address); Open(); });
|
||||||
|
b.AddButton(L.T("wallets.rename"), () => { _renamePick = w.Address; _renameText = w.Label ?? ""; _message = null; Open(); });
|
||||||
if (idx != d.Current) b.AddButton(L.T("wallets.use"), () => { d.Current = idx; Session.Save(); Open(); });
|
if (idx != d.Current) b.AddButton(L.T("wallets.use"), () => { d.Current = idx; Session.Save(); Open(); });
|
||||||
else b.AddLabel(L.T("wallets.current"));
|
else b.AddLabel(L.T("wallets.current"));
|
||||||
|
if (idx > 0) b.AddButton(_destroyPick == w.Address ? L.T("wallets.destroy.sure") : L.T("wallets.destroy"), () => DestroyWallet(w));
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
}
|
}
|
||||||
|
if (_renamePick != null) RenameRow(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string _destroyPick, _renamePick, _renameText = "";
|
||||||
|
|
||||||
|
/// <summary>改名:表格下方一行输入框;留空恢复默认名。</summary>
|
||||||
|
private static void RenameRow(CustomUIBuilder b)
|
||||||
|
{
|
||||||
|
var w = Session.Data.Own(_renamePick);
|
||||||
|
if (w == null) { _renamePick = null; return; }
|
||||||
|
b.AddSeparator();
|
||||||
|
b.BeginRow(8f, TextAnchor.MiddleLeft);
|
||||||
|
b.AddLabel(L.T("wallets.rename.h", w.Address));
|
||||||
|
b.BeginPanel(220f, -1f, TextAnchor.MiddleLeft, 0f, 0f);
|
||||||
|
b.AddInput(WalletWindow.NameOf(w), _renameText, s => _renameText = s, "rename", s => _renameText = s);
|
||||||
|
b.End();
|
||||||
|
b.AddButton(L.T("wallets.rename.ok"), () =>
|
||||||
|
{
|
||||||
|
string name = (_renameText ?? "").Trim();
|
||||||
|
w.Label = name.Length == 0 ? null : name.Length > 16 ? name.Substring(0, 16) : name;
|
||||||
|
_renamePick = null;
|
||||||
|
Session.Save();
|
||||||
|
WalletWindow.RefreshDevice(w, true);
|
||||||
|
Open();
|
||||||
|
});
|
||||||
|
b.AddButton(L.T("black.edit.cancel"), () => { _renamePick = null; Open(); });
|
||||||
|
b.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>销毁钱包(主钱包不能销毁):要先把币转走;点两次确认。之后回到这个地址的退款、回款、混币提回改进当前钱包。</summary>
|
||||||
|
private static void DestroyWallet(Wallet w)
|
||||||
|
{
|
||||||
|
var d = Session.Data;
|
||||||
|
int idx = d.Wallets.IndexOf(w);
|
||||||
|
if (idx <= 0) return;
|
||||||
|
if (w.Balances.Values.Any(v => v > 1e-9) || w.Frozen.Values.Any(v => v > 1e-9)) { _destroyPick = null; _message = L.T("wallets.destroy.notempty", WalletWindow.NameOf(w)); Open(); return; }
|
||||||
|
if (_destroyPick != w.Address) { _destroyPick = w.Address; _message = null; Open(); return; }
|
||||||
|
_destroyPick = null;
|
||||||
|
string name = WalletWindow.NameOf(w);
|
||||||
|
WalletWindow.Close(w);
|
||||||
|
var current = d.Hot;
|
||||||
|
d.Wallets.RemoveAt(idx);
|
||||||
|
d.Current = Math.Max(0, d.Wallets.IndexOf(current));
|
||||||
|
Log.Info($"销毁硬件钱包 {w.Address}{(w.Poisoned ? "(被投毒)" : "")}");
|
||||||
|
Session.Save();
|
||||||
|
_message = L.T("wallets.destroyed", name, w.Address);
|
||||||
|
Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>硬件钱包拖到终端上:添加一个钱包(被投毒的钱包看起来一样)。</summary>
|
/// <summary>硬件钱包拖到终端上:添加一个钱包(被投毒的钱包看起来一样)。</summary>
|
||||||
@@ -618,7 +671,10 @@ namespace Decentralized
|
|||||||
var p = WalletWindow.Pending;
|
var p = WalletWindow.Pending;
|
||||||
b.AddHeader(L.T("black.pay.confirm"));
|
b.AddHeader(L.T("black.pay.confirm"));
|
||||||
b.AddLabel(p.Summary);
|
b.AddLabel(p.Summary);
|
||||||
|
b.BeginRow(8f, TextAnchor.MiddleLeft);
|
||||||
b.AddLabel(L.T("black.pay.to", p.Address));
|
b.AddLabel(L.T("black.pay.to", p.Address));
|
||||||
|
b.AddButton(L.T("hw.copy"), () => { Clipboard.Copy(p.Address); _message = L.T("hw.copied.addr", p.Address); Open(); });
|
||||||
|
b.End();
|
||||||
b.AddLabel(L.T("black.pay.amount", WalletWindow.Amt(p.Amount), p.Asset, Fmt.Credits(p.Amount * Session.Price(p.Asset))));
|
b.AddLabel(L.T("black.pay.amount", WalletWindow.Amt(p.Amount), p.Asset, Fmt.Credits(p.Amount * Session.Price(p.Asset))));
|
||||||
b.BeginRow();
|
b.BeginRow();
|
||||||
b.AddButton(L.T("black.pay.wallet"), () => WalletWindow.OpenForBill(p));
|
b.AddButton(L.T("black.pay.wallet"), () => WalletWindow.OpenForBill(p));
|
||||||
|
|||||||
@@ -68,8 +68,9 @@ namespace Decentralized
|
|||||||
|
|
||||||
public static string NameOf(Wallet w)
|
public static string NameOf(Wallet w)
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(w.Label)) return w.Label;
|
||||||
int i = Session.Data.Wallets.IndexOf(w);
|
int i = Session.Data.Wallets.IndexOf(w);
|
||||||
return i == 0 ? L.T("hw.wallet.main") : L.T("hw.wallet.n", i + 1);
|
return i == 0 ? L.T("hw.wallet.main") : L.T("hw.wallet.n", w.Name ?? (i + 1).ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Device Dev(Wallet w)
|
private static Device Dev(Wallet w)
|
||||||
@@ -80,6 +81,14 @@ namespace Decentralized
|
|||||||
|
|
||||||
private static bool IsOpen(Device dev) => CustomUIManager.Instance != null && CustomUIManager.Instance.IsOpen(dev.Id);
|
private static bool IsOpen(Device dev) => CustomUIManager.Instance != null && CustomUIManager.Instance.IsOpen(dev.Id);
|
||||||
|
|
||||||
|
/// <summary>钱包被销毁:关掉它的窗口,丢掉设备状态。</summary>
|
||||||
|
public static void Close(Wallet w)
|
||||||
|
{
|
||||||
|
if (w == null || !Devices.TryGetValue(w.Address, out var dev)) return;
|
||||||
|
if (IsOpen(dev)) CustomUIManager.Instance.CloseWindow(dev.Id);
|
||||||
|
Devices.Remove(w.Address);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>读档 / 新游戏后清掉上一局的设备状态并关窗。</summary>
|
/// <summary>读档 / 新游戏后清掉上一局的设备状态并关窗。</summary>
|
||||||
public static void Reset()
|
public static void Reset()
|
||||||
{
|
{
|
||||||
@@ -115,6 +124,7 @@ namespace Decentralized
|
|||||||
WalletDriver.Ensure();
|
WalletDriver.Ensure();
|
||||||
var b = CustomUIManager.Window(dev.Id, "PLEDGER");
|
var b = CustomUIManager.Window(dev.Id, "PLEDGER");
|
||||||
if (b == null) return;
|
if (b == null) return;
|
||||||
|
_rows.Clear(); _clear.Clear();
|
||||||
int idx = Session.Data.Wallets.IndexOf(dev.Wallet);
|
int idx = Session.Data.Wallets.IndexOf(dev.Wallet);
|
||||||
b.SetSize(WinW, WinH).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
|
b.SetSize(WinW, WinH).SetDraggable(true).SetCloseOnEscape(true).UseMachineryStyle();
|
||||||
b.SetPosition(new Vector2(600f + 28f * idx, -24f * idx));
|
b.SetPosition(new Vector2(600f + 28f * idx, -24f * idx));
|
||||||
@@ -181,6 +191,21 @@ namespace Decentralized
|
|||||||
if (img != null) img.color = Key;
|
if (img != null) img.color = Key;
|
||||||
foreach (var t in btn.GetComponentsInChildren<TextMeshProUGUI>(true)) t.color = KeyText;
|
foreach (var t in btn.GetComponentsInChildren<TextMeshProUGUI>(true)) t.color = KeyText;
|
||||||
}
|
}
|
||||||
|
foreach (var img in _clear) if (img != null) img.color = new Color(0, 0, 0, 0);
|
||||||
|
// 余额行:整行可点,选中的一行反色高亮
|
||||||
|
foreach (var (root, img, asset) in _rows)
|
||||||
|
{
|
||||||
|
if (root == null || img == null) continue;
|
||||||
|
bool on = asset == dev.Asset;
|
||||||
|
img.sprite = MachineryStyle.White; img.type = Image.Type.Simple; img.raycastTarget = true;
|
||||||
|
img.color = on ? Ink : new Color(0, 0, 0, 0.04f);
|
||||||
|
foreach (var t in root.GetComponentsInChildren<TextMeshProUGUI>(true)) t.color = on ? KeyText : Ink;
|
||||||
|
var btn = root.GetComponent<Button>() ?? root.AddComponent<Button>();
|
||||||
|
btn.transition = Selectable.Transition.None;
|
||||||
|
btn.onClick.RemoveAllListeners();
|
||||||
|
string a = asset;
|
||||||
|
btn.onClick.AddListener(() => { dev.Asset = a; dev.Note = null; Open(dev); });
|
||||||
|
}
|
||||||
// 签名要按住 ✓:在按键上挂一个按住计时
|
// 签名要按住 ✓:在按键上挂一个按住计时
|
||||||
if (ok != null && dev.Screen == Screen.Review && ok.GetComponent<HoldToSign>() == null) ok.AddComponent<HoldToSign>().Device = dev.Address;
|
if (ok != null && dev.Screen == Screen.Review && ok.GetComponent<HoldToSign>() == null) ok.AddComponent<HoldToSign>().Device = dev.Address;
|
||||||
}
|
}
|
||||||
@@ -203,6 +228,9 @@ namespace Decentralized
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<string, Sprite> Sliced = new Dictionary<string, Sprite>();
|
private static readonly Dictionary<string, Sprite> Sliced = new Dictionary<string, Sprite>();
|
||||||
|
private const float RowW = ScreenW - 28f;
|
||||||
|
private static readonly List<(GameObject root, Image image, string asset)> _rows = new List<(GameObject, Image, string)>();
|
||||||
|
private static readonly List<Image> _clear = new List<Image>();
|
||||||
|
|
||||||
public static string Amt(double v) => v.ToString(v >= 100 ? "0.##" : "0.####", CultureInfo.InvariantCulture);
|
public static string Amt(double v) => v.ToString(v >= 100 ? "0.##" : "0.####", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
@@ -214,16 +242,34 @@ namespace Decentralized
|
|||||||
private static void HomeScreen(CustomUIBuilder b, Device dev)
|
private static void HomeScreen(CustomUIBuilder b, Device dev)
|
||||||
{
|
{
|
||||||
var w = dev.Wallet;
|
var w = dev.Wallet;
|
||||||
b.AddHeader(NameOf(w));
|
b.AddLabel("<size=22><b>" + NameOf(w) + "</b></size>");
|
||||||
|
b.BeginRow(8f, TextAnchor.MiddleLeft);
|
||||||
b.AddLabel(w.Address, "addr_dim");
|
b.AddLabel(w.Address, "addr_dim");
|
||||||
|
b.AddButton(L.T("hw.copy"), () => { Clipboard.Copy(w.Address); dev.Note = L.T("hw.copied"); Open(dev); });
|
||||||
|
b.End();
|
||||||
b.AddSeparator();
|
b.AddSeparator();
|
||||||
bool any = false;
|
// 余额:每个币一行,左边币种、右边数量与估值,全部显示(含 0);点一行选中,发送时默认用它
|
||||||
foreach (var kv in w.Balances.OrderBy(k => k.Key))
|
foreach (var a in SendAssets)
|
||||||
{
|
{
|
||||||
any = true;
|
double q = w.Get(a);
|
||||||
b.AddLabel(L.T("hw.balance", kv.Key, Amt(kv.Value), Fmt.Credits(kv.Value * Session.Price(kv.Key))));
|
b.BeginPanel(RowW, 32f, TextAnchor.MiddleLeft, 6f, 0f);
|
||||||
|
_rows.Add((b.Last.root, b.Last.image, a));
|
||||||
|
b.BeginRow(0f, TextAnchor.MiddleLeft);
|
||||||
|
b.BeginPanel(90f, 26f, TextAnchor.MiddleLeft, 0f, 0f);
|
||||||
|
_clear.Add(b.Last.image);
|
||||||
|
b.AddLabel(a);
|
||||||
|
b.End();
|
||||||
|
b.BeginPanel(130f, 26f, TextAnchor.MiddleRight, 0f, 0f);
|
||||||
|
_clear.Add(b.Last.image);
|
||||||
|
b.AddLabel(Amt(q) + (w.Frozen.TryGetValue(a, out var fz) && fz > 0 ? L.T("hw.balance.frozen", Amt(fz)) : ""));
|
||||||
|
b.End();
|
||||||
|
b.BeginPanel(RowW - 12f - 90f - 130f, 26f, TextAnchor.MiddleRight, 0f, 0f);
|
||||||
|
_clear.Add(b.Last.image);
|
||||||
|
b.AddLabel(L.T("hw.balance.value", Fmt.Credits(q * Session.Price(a))));
|
||||||
|
b.End();
|
||||||
|
b.End();
|
||||||
|
b.End();
|
||||||
}
|
}
|
||||||
if (!any) b.AddLabel(L.T("wallet.empty"));
|
|
||||||
b.AddSeparator();
|
b.AddSeparator();
|
||||||
b.AddLabel(L.T("hw.total", Fmt.Credits(w.Value(Session.Price))));
|
b.AddLabel(L.T("hw.total", Fmt.Credits(w.Value(Session.Price))));
|
||||||
if (w.Taint > 0.5) b.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
|
if (w.Taint > 0.5) b.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)));
|
||||||
@@ -240,7 +286,13 @@ namespace Decentralized
|
|||||||
var w = dev.Wallet;
|
var w = dev.Wallet;
|
||||||
b.AddHeader(L.T("hw.send"));
|
b.AddHeader(L.T("hw.send"));
|
||||||
b.AddLabel(L.T("hw.to"));
|
b.AddLabel(L.T("hw.to"));
|
||||||
|
b.BeginRow(6f, TextAnchor.MiddleLeft);
|
||||||
|
b.BeginPanel(RowW - 70f, -1f, TextAnchor.MiddleLeft, 0f, 0f);
|
||||||
|
_clear.Add(b.Last.image);
|
||||||
b.AddInput("0x", dev.To, s => dev.To = s, "to", s => dev.To = s);
|
b.AddInput("0x", dev.To, s => dev.To = s, "to", s => dev.To = s);
|
||||||
|
b.End();
|
||||||
|
b.AddButton(L.T("hw.paste"), () => { dev.To = Clipboard.Paste(); dev.Note = null; Open(dev); });
|
||||||
|
b.End();
|
||||||
b.BeginRow();
|
b.BeginRow();
|
||||||
foreach (var a in SendAssets)
|
foreach (var a in SendAssets)
|
||||||
{
|
{
|
||||||
@@ -452,10 +504,10 @@ namespace Decentralized
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>别的交易让这个钱包的余额变了:开着的话刷新主页。</summary>
|
/// <summary>别的交易让这个钱包的余额变了:开着的话刷新主页。</summary>
|
||||||
public static void RefreshDevice(Wallet w)
|
public static void RefreshDevice(Wallet w, bool any = false)
|
||||||
{
|
{
|
||||||
if (w == null || !Devices.TryGetValue(w.Address, out var dev) || !IsOpen(dev)) return;
|
if (w == null || !Devices.TryGetValue(w.Address, out var dev) || !IsOpen(dev)) return;
|
||||||
if (dev.Screen == Screen.Home || dev.Screen == Screen.Send) Open(dev);
|
if (any || dev.Screen == Screen.Home || dev.Screen == Screen.Send) Open(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>每帧推进出块动画:只改状态文字和进度条,不重建窗口。</summary>
|
/// <summary>每帧推进出块动画:只改状态文字和进度条,不重建窗口。</summary>
|
||||||
|
|||||||
@@ -814,6 +814,11 @@ internal static class Program
|
|||||||
Check(Math.Abs(Wallet.DrainChance(Tuning.WalletDrainMin) - 1 / 7.0) < 1e-9, "刚到门槛:每晚 1/7(平均 7 天)");
|
Check(Math.Abs(Wallet.DrainChance(Tuning.WalletDrainMin) - 1 / 7.0) < 1e-9, "刚到门槛:每晚 1/7(平均 7 天)");
|
||||||
Check(Wallet.DrainChance(Tuning.WalletDrainFull) == 1 && Wallet.DrainChance(Tuning.WalletDrainFull * 3) == 1, "到满额当晚就转走");
|
Check(Wallet.DrainChance(Tuning.WalletDrainFull) == 1 && Wallet.DrainChance(Tuning.WalletDrainFull * 3) == 1, "到满额当晚就转走");
|
||||||
Check(Wallet.DrainChance(10000) > Wallet.DrainChance(5000), "钱越多越快");
|
Check(Wallet.DrainChance(10000) > Wallet.DrainChance(5000), "钱越多越快");
|
||||||
|
double mid = (Tuning.WalletDrainMin + Tuning.WalletDrainFull) / 2;
|
||||||
|
Check(Math.Abs(Wallet.DrainDays(mid) - 4) < 1e-9 && Math.Abs(Wallet.DrainChance(mid) - 0.25) < 1e-9, "中间值:期望 4 天,每晚 25%");
|
||||||
|
var rng = new Rng(9); double sum = 0; int n = 4000;
|
||||||
|
for (int i = 0; i < n; i++) { int d = 1; while (!rng.Chance(Wallet.DrainChance(mid))) d++; sum += d; }
|
||||||
|
Check(Math.Abs(sum / n - 4) < 0.25, $"模拟平均 {sum / n:0.00} 天被转走(期望 4)");
|
||||||
var w = new Wallet { Address = "a" };
|
var w = new Wallet { Address = "a" };
|
||||||
w.Add(Assets.PSC, 50); w.Add(Assets.CRT, 1000); w.Taint = 300; w.Basis = 900; w.Freeze(Assets.CRT, 0.5, 9);
|
w.Add(Assets.PSC, 50); w.Add(Assets.CRT, 1000); w.Taint = 300; w.Basis = 900; w.Freeze(Assets.CRT, 0.5, 9);
|
||||||
double v = w.Drain(a => a == Assets.PSC ? 100 : 1);
|
double v = w.Drain(a => a == Assets.PSC ? 100 : 1);
|
||||||
|
|||||||
Reference in new issue
Block a user