16 Commits
Author SHA1 Message Date
ZHU MINCHAOandClaude Opus 5.5 685762d110 2.0.0:版本号、CHANGELOG 整理成发布版
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:54:31 +08:00
ZHU MINCHAOandClaude Opus 5.5 67df561a5f 兑换、黑市自动换币、铸 CRA、CRA 排队订单按脏币比例带过去;持仓表列宽;中文统一叫“脏币”
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:12:15 +08:00
ZHU MINCHAOandClaude Opus 5.5 34b70023c9 污点按币种记脏枚数、只在 Web3 钱包里;Poinbase 只收干净的币;删除出金审查;永续操纵只记罪证;泄露记罪证;剧情替换;存档迁移到数据版本 5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:04:09 +08:00
ZHU MINCHAOandClaude Opus 5.5 f0a6051591 补测:钓鱼混币器提回时日志只说地址被标记(不写制裁);测试按钮:钱包加 CRC、自营混币器风声拉满、立刻换成钓鱼混币器、联系人 + 明天镇压
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 19:03:14 +08:00
ZHU MINCHAOandClaude Opus 5.5 8afdfa606c CHANGELOG:Tumbler 外观修正
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 18:46:08 +08:00
ZHU MINCHAOandClaude Opus 5.5 e88fab3f1b Tumbler 扩展实测修正:没有出款可还原时单独一句日志;每次上门是同一个外观;测试按钮“剧情重来”
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 18:45:54 +08:00
ZHU MINCHAOandClaude Opus 5.5 a37dd4ca7c Tumbler 扩展:治安部探员与搜查、密钥泄露还原出款污点、审判预测盘、黑市跑路、报复、长期联系人、混币器换皮重开
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 18:30:28 +08:00
ZHU MINCHAOandClaude Opus 5.5 0f26d88c03 自营混币器取回:照页面上四舍五入的可取回数量填写时按全部取回
实测发现:可取回显示 25.75,实际 25.749…,照填被判超出。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 17:57:13 +08:00
ZHU MINCHAOandClaude Opus 5.5 64f330ef25 自营混币器:大池子更容易被盯上(藏身半值 40,000 → 160,000)
- 查封概率随本金上升(8 万约是 5 千的 4 倍),净收益仍随本金增长、增速递减(mixsim out_v3)
- 手册改为"池子大能藏得深一点,但客流也大,总体更容易被盯上",8 种语言

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 16:36:12 +08:00
ZHU MINCHAOandClaude Opus 5.5 c2a6d8832a 自营混币器调整:利润翻倍、查封减半以上、大池子更难追查
- 基础需求 ×2(OwnMixDemand0 40,000、OwnMixDemandPerDay 1,400)
- 查封系数 0.01 → 0.002;风声累积除以 1 + 匿名集 / OwnMixHeatAnon0(40,000),本金加大时查封概率不再跟着翻倍
- mixsim out_v2:注资 1 万 / 2 万 / 8 万第 61–90 天手续费每天约 317 / 580 / 1,000,90 天被查封 0.08 / 0.13 / 0.19 次(原 0.31 / 0.50 / 1.14),净收益随本金单调增长
- 手册补"池子越大越难追查",8 种语言

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 16:31:27 +08:00
ZHU MINCHAOandClaude Opus 5.5 71002cbced 第十一阶段 stage 3:自营混币器
- Domain/OwnMixer:注资开张、NPC 客户存入、按匿名集洗污点出款(和普通混币器同一套抽象,匿名集主要是本金和收入)、手续费收入取回全额带污点、本金带自己的污点;客流按"费率 + 出款剩下的污点"和 Tumbler 比,乘黑市声望;风声累积按概率查封(全部没收、记 FENCING、停业)、便衣存入(记 FENCING)、卷款跑路(全额污点、记 FRAUD、黑市声望下降、永久关闭)
- 灰网页"自营混币器"标签(买下场外换币台就有;TerminalWindow 拆成 partial,页面在 TerminalWindow.OwnMix.cs):状态、费率、口碑、风声、匿名集、各币种本金/收入/客户在途;费率、开张/暂停、注资(账单转账)、取回、卷款跑路(合约签名)
- 夜间结算、日志、报纸新闻;浏览器显示"自营混币器"地址名
- Tuning.OwnMix*(research/mixsim 标定),单元测试,手册与 8 种语言
- 实测:注资 20 PSC 开张,三晚各一笔存入、一次便衣

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 16:14:28 +08:00
ZHU MINCHAOandClaude Opus 5.5 9d95e1f760 换币客户改在交易终端成交:按比例部分成交、买币随机出价、弹出钱包
- 对话只剩"到终端上谈 / 这单我不做":交易终端打开柜台交易页(标签"柜台",只在有客户时出现),客户在柜台前等着
- 柜台交易页:客户来意、数量、市值、出价/要价、单价、地址(卖币可"查地址"进浏览器),25/50/75/100% 或手输数量部分成交(Otc.Part),成交 / 放弃
- 买币客户随机出价(OtcBidMin −5% … OtcBidMax +30%),去掉三档报价与成交概率(OtcPremium、OtcAccept);成交后弹出 PLEDGER,账单 Prefill 填好客户地址
- 卖币:成交后付现金、币进已连接的钱包;客户说最后一句离开(DialogUIManager.StartDialogue)
- 客户被打发或离开时交易作废;不再有"当晚未付作废"
- 增量翻译:只是和已删旧键同文的新键照常翻译
- 实测:买币客户 25% 部分成交(先在 Web3 换出 CRA),PLEDGER 预填地址签名,到账 +668,客户离开;涡轮增压器作用于矿架立刻挖一轮

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:46:14 +08:00
ZHU MINCHAOandClaude Opus 5.5 2badc45a5a 涡轮增压器可以作用于矿架和矿机:立刻挖一轮
- 原来挡掉增压器(白天多扣一次电、挖矿不多);改为照原版机器规则多跑一轮:增压器作用于矿机时打标记,矿架、矿机的夜间回调据此立刻结算一轮(TurboMine)——扣一晚的电、按全网份额出块发到收款地址、算一次损耗和暴毙
- 增压器的判断、消耗、充电走原版;夜间按机器发放抽成 Night.PayMachine 供两处共用
- 手册矿机一章、夜间日志 nightlog.turbo,8 种语言

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:19:36 +08:00
ZHU MINCHAOandClaude Opus 5.5 7269c253af 场外换币台实测修正:三个选项、换币账单不独占终端
- 原版对话最多三个选项:买币对话去掉"不卖"(第四个选项会越界),不想卖就打发客户;选项那句话带上报价和溢价,删除 otc.tier
- 换币账单单独挂着(OtcClient.Standing),WalletWindow.Pending 没有终端账单时回落到它:不独占 Web3、灰网页,换币、混币开的账单不会把它覆盖;签名后清掉,被挤出内存池时放回
- 到账后刷新终端;手册补一句"不想卖就把客户打发走"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:10:03 +08:00
ZHU MINCHAOandClaude Opus 5.5 ec578a39d9 第十一阶段 stage 2:场外换币台(暗灯招换币客户)
- 新人脉升级“场外换币台”(OtcUnlockPrice 500),附赠一盏暗灯
- 包装原版暗灯工厂:切换顺序末尾加目标 pd_otc(只接管进出这一步,其余交给原切换函数);顾客工厂写进公开的 StoreClientListDict.storeClientDict,顾客面板与概率走原版逻辑
- 换色:底图换成原版熄灭的暗灯,叠加层画黄绿色光晕(otc_lamp_on),不进存档
- 换币客户(OtcClient)一半卖币、一半买币:卖币并入原收黑币(Domain/Otc.cs 取代 HotCoins.cs,HotCoinSeller 删除),污点比例随机、越脏越便宜、地址来历写进账本;买币三档报价,PLEDGER 转账确认后现金到账,当晚未付作废
- 只在生成顾客的回调窗口里生成报价、写账本(原版取名字也会调用工厂)
- 存档数据版本 4:旧存档出块奖励、NPC 算力按 MiningScale 放大
- 8 种语言文字、手册“换币客户”一节;单元测试

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 14:29:49 +08:00
ZHU MINCHAOandClaude Opus 5.5 bc23ae056b 第十一阶段 stage 1:污点代替没收、模块不设上限、出块 ×10
- 预测市场内幕单改为确定性记污点:结算或卖出时照常兑付,内幕份额对应的兑付记收款钱包污点;删除 PredictInsiderShare、PredictInsiderCatch
- 操纵检出被查后照常兑付,预测兑付记钱包污点、永续盈利记 Poinbase 账户污点;删除 ManipFreezeDays
- Wallet.AddTaintCapped:污点不超过钱包当时的价值,超出部分按 PredictFraudShare / PredictFraudCap 记诈骗罪证(TaintCrime.Commit)
- 模块效果不设上限,只留算力、耗电、损耗乘数的下限(ModuleHashFloor / ModuleKjFloor / ModuleWearFloor)
- BlockReward 100 → 1000,NpcHash0 360 → 3600
- 修正内幕卖出日志"只退本金"的错误说法;8 种语言文字与手册更新

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 14:17:41 +08:00
54 changed files with 3704 additions and 965 deletions

No files matched your search

+63
View File
@@ -1,5 +1,68 @@
# Changelog
## 2.0.0
污点规则重做(按币种记脏币,只有干净的币能进 Poinbase 和银行,出金审查删除)、OTC Desk 场外换币台、自营混币器、Tumbler 支线扩展、挖矿去模块上限。存档兼容,读旧存档时自动升级。
### 内幕与操纵、挖矿
- 预测市场的内幕单不再按概率查处、没收:结算或卖出时照常兑付,内幕份额对应的兑付算脏币,夜间日志写明“警方怀疑这是内幕交易”。原来“内幕持仓达到上限的 40%、70% 概率被查”的规则(`PredictInsiderShare`、`PredictInsiderCatch`)删除,每一份内幕份额都算脏币,先买后卖也一样。
- 操纵检出:被查的概率不变,被查后照常兑付,预测盘的这笔兑付算脏币,永续的这笔盈利记诈骗罪证(见下面的污点一节);永续账户不再冻结 3 天(`ManipFreezeDays` 删除)。报纸的调查新闻改为“涉事账户已被标记”。
- 矿架、矿机的模块效果不设上限(原来算力最多 +20%、省电最多 25%、损耗最多 −30%,以及耗电最多 ×2 的隐含限制):9 个无限制神经核心算力 ×5.5、耗电 ×3.25、暴毙率 ×5.5。只保留防出错的下限:算力乘数 ≥ 0、耗电乘数 ≥ 0.2、损耗乘数 ≥ 0。
- 出块奖励 100 → 1000 PSC,NPC 初始算力 360 → 3600:均衡时每 H 收入不变(开局 1 H 每晚仍约 26.9 信用点),全网算力放大,玩家算力占比变低。模拟:普通玩法第 30 天挖矿占比 19% → 21%,第 120 天玩家算力占比 33% → 约 2%;大矿工收入明显变高。
- 修正:内幕卖出的日志原来写“只退本金”,实际是全部没收。
- 手册对应段落 8 种语言更新。
- 涡轮增压器可以对矿架和矿机用(原来被挡掉),规则和原版机器一样:用一次就多跑一轮,也就是立刻挖一晚——扣一晚的电,产出按当前全网算力计算、发到这台机器的收款地址,同样算一次损耗和暴毙判定,不影响当晚照常挖矿。增压器本身的消耗、充电、可用次数照原版。
### OTC Desk 场外换币台
- 新人脉升级“场外换币台”(500,前提是 Web3 治理社区),附赠一盏暗灯。原版暗灯双击切换目标时多一个“换币客户”(解锁后才有),放进展示柜亮黄绿色的光;招客概率、顾客面板、灯的提示都沿用原版暗灯(每盏 15% + 黑市声望的一半,封顶 95%)。
- 换币客户一半来卖币、一半来买币,都用现金。客户说明来意后选“到终端上谈”,交易终端打开柜台交易页(标签“柜台”),客户在柜台前等着;可以按 25% / 50% / 75% / 100% 或手输数量只成交一部分,现金按比例折算,客户要的数量随房租增长。选“放弃”或把客户打发走就作废。
- 卖币(原来的收黑币并进来,不再单独上门):成交后付现金,币直接进已连接的钱包。帮派火并、镇压期间多是 CRT,袭击上层、下层动荡期间多是 PSC,这类币全额污点;平时在 PSC、CRT、CRA 里随机,污点占市值 20%–90%,要价从市值的 80% 降到 45%。交易页上有客户地址,“查地址”打开浏览器看它的来历(混币器、黑市平台、骗局地址或 Poinbase、DEX)。Ponero 兑换处开放后偶尔有人出手 Ponero;便衣规则不变。
- 买币:什么币都可能要,数量按房租 2–8 倍,出价是市价加一个随机溢价(−5% 到 +30%),成不成交由玩家决定。成交后 PLEDGER 弹出,收款地址和数量已填好,签名确认后客户付现金离开。卖出的币不追查污点。换币账单不独占终端的 Web3、灰网页,也不会被换币、混币这些终端账单覆盖,可以先在 DEX 换出客户要的币再成交。
- 浏览器里换币客户的地址显示为“换币客户”。
- 存档数据版本 4:读旧存档时出块奖励和 NPC 算力按 stage 1 的新数值放大 10 倍(保留减半进度)。
- 手册“收黑币”一节改写为“换币客户”,8 种语言。
### 自营混币器
- 买下场外换币台就开放,灰网页多一个“自营混币器”标签。注资(PSC、CRC、CRT、CRA,账单转账)后开张,NPC 客户把带污点的币存进来,几天后出款,你收手续费;费率可选 1% / 2% / 3% / 5% / 8% / 12%,可以暂停营业。
- 和普通混币器一样,每一轮按匿名集洗掉一部分污点;匿名集主要是你的本金和没取走的收入(客户在途的币按 25% 算),每轮清洗率 = 35% × 匿名集 / (匿名集 + 20,000)。客户拿“费率 + 出款剩下的污点 × 0.1”和 Tumbler 的混币器比,便宜就多来,黑市声望越高客人越多(Tumbler 关停或被制裁时客人更多)。
- 手续费收入留在池子里,取回时先取收入(全额带污点),再取本金(带本金自己的污点)。客户在途的币不能取。
- 风险:风声随客流累积(池子大能藏得深一点:累积量除以 1 + 匿名集 / 160,000,但客流也大,总体大池子更容易被盯上)、每晚衰减,按风声有概率被治安部查封(本金、收入和客户的币全部没收,记销赃罪证 = 池子价值的 1%、封顶 400,停业 7 天,上报纸);营业的晚上有 3% 概率来一笔便衣存入(记销赃罪证)。随时可以卷款跑路(点两次确认、签名):连同客户的币全部卷走、全额带污点,记诈骗罪证 400、黑市声望 −25,混币器永久关闭。
- 模拟(research/mixsim out_v3,费率 3%,第 61–90 天):注资 1 万每天手续费约 316、2 万约 574、4 万约 807、8 万约 991;90 天被查封 1 万 0.12 次、2 万 0.21 次、4 万和 8 万 0.35 次,净收益随本金增长、增速递减(1 万约 2.8 万,8 万约 6.3 万)。
- 手册“换币客户”一节加一段自营混币器,8 种语言;夜间日志和报纸新闻。
### Tumbler 支线扩展
- 治安部探员:Tumbler 第 2 次上门之后的第二天起来店里问一次,他来过之后 Tumbler 才来第 3 次。承认 Tumbler 来过:治安部声望 +5;否认:没有影响。承认过又帮他藏了密钥盘的,探员在 Tumbler 来取的前一天上门搜查,搜到就没收、记违禁品罪证 300,Tumbler 第 4 次来时说他听说了。
- 密钥落到治安部(举报、被搜走、Tumbler 来取时盘不在手上):混币记录被还原,按最近 30 天混币器出款洗掉的污点记诈骗罪证(× 50%,单次最多 400);夜间日志和报纸。卖给黑市不算。
- 举报之后有审判:第 2 天开一个“Tumbler 被判有罪”的预测盘(先验 70%),第 7 天宣判;报纸每天登一条庭审进展,偏向控方或辩方,七成偏向真实判决,盘口的价格跟着新闻走,线索列出两边各占上风几天。无罪获释的话,3 天后他会报复。
- 卖给黑市的:黑市在 10–20 天后卷款跑路,混币器里在途的币全部丢失,混币器关停;3 天后 Tumbler 报复。
- 报复:门缝里塞进来一张纸条,之后一半可能是当前钱包被植入木马(和商城的投毒钱包一样:剪贴板劫持,钱够多以后被转空),否则每个钱包都会收到伪装成最近转账对象的 0 币转入(地址投毒)。
- 帮他藏好并还给他:Tumbler 成为长期联系人,治安部镇压开始的前一晚在夜间日志里报信。
- 混币器关停 15 天后换名字(Vortex)、换地址重新上线,费率和跑路概率回到原样;有 25% 是治安部的钓鱼混币器,从它提回的币整笔算脏币,费率只有一半。
- 卖给黑市那笔 CRT 的污点比例收进配置(`TumblerSellTaint`)。
- 修正:Tumbler 每次上门的外观原来是随机的,现在第一次上门定下后一直不变。
- 手册剧情联动一节补一段,8 种语言。
### 污点:按币种记脏币,删除出金审查
- 污点只在 Web3 钱包里,每种币分别记脏币数量,钱包页、PLEDGER 的余额里能看到;钱包总表的“脏币”列按现价显示。原来整个钱包记一笔信用点的污点不再使用。
- 花掉、转出、付手续费时按这种币的脏币比例带走脏币;兑换成别的币时脏币比例跟过去(60% 是脏币的 CRT 换成 PSC,PSC 也是 60% 脏币);兑入 Ponero 时脏币消失,Ponero 是干净的。
- Poinbase 只收干净的币:钱包往充值地址每种币最多转干净的部分,超出时 PLEDGER 拦下并写明能充多少;资金页“从钱包入金”也列出每种币能充多少。Poinbase 账户和银行没有污点。
- 出金审查整套删除:出金到银行不再有被查概率、没收和销赃罪证,只收手续费 0.1%、对超出成本的部分征税 8%。从 Ponero 兑出、刚从混币器提出的币也不再“出金加重”。
- 带污点的收入都改成“收到的这批币里有多少脏币”:换币客户卖来的币、用币付款的顾客(按赃物热度)、灰网卖赃物的货款、预测盘内幕单和操纵的兑付、理财卷款、自营混币器的收入和卷款、卖密钥盘换来的 CRT。“加污点不能超过钱包价值、超出部分记诈骗罪证”的规则随之删除。
- 永续被判操纵:盈利照常留在账户,只记诈骗罪证(盈利 × 50%,单次最多 400),不再加污点。
- 混币器:存入时带走这种币的脏币,每轮减少;制裁期间到期的出款、从钓鱼混币器提出的币不洗,整笔算脏币。
- Tumbler 的密钥泄露:不再往钱包里加回污点,改为按最近 30 天混币器出款洗掉的污点记诈骗罪证(× 50%,单次最多 400)。
- 剧情联动:塞尔皮科的证据交给治安部,原来的“出金审查更严”换成 30 天内混币器每轮清洗率 ×0.7;伦警官被捕,原来的“出金审查更严”换成 14 天内换币客户里的便衣 ×1.3。
- 存档数据版本 5:读旧档时,每个钱包原来的污点按当前价格摊成各币的脏币;Poinbase 账户和永续仓位的污点挪到 1 号钱包一起摊(超出的作废);混币器、黑市订单、质押、空投池、预测持仓、自营混币器本金里托管的污点换成对应币的脏币。
- 模拟(research/launder):混币器小额 1–3 轮洗钱成本 10%–15%,Ponero 约 12%、每天约 500,买币的换币客户是负成本但受客流限制;数值都不用改。
- 手册“出金审查”一节改写为“污点”,相关段落 8 种语言同步改。
## 1.1.0
市场与洗钱的平衡调整:Poinbase 改为订单簿、DEX 稳定币对改为稳定币池、保险基金和爆仓新闻、预测市场流动性、收黑币、浏览器记录钱包的每一笔进出。存档兼容,读旧存档时自动升级。
+1 -1
View File
@@ -1,6 +1,6 @@
# Probably Decentralized
A mod for *Probably Stolen*: crypto mining, a fictional coin market, stablecoins, laundering and its risks, a Web3 black market, built on the game's own power, events and law systems. Current version: 1.1.0.
A mod for *Probably Stolen*: crypto mining, a fictional coin market, stablecoins, laundering and its risks, a Web3 black market, built on the game's own power, events and law systems. Current version: 2.0.0.
All coins, exchanges and events are fictional and have no real-world value.
Binary file not shown.
+29 -2
View File
@@ -44,6 +44,9 @@ C = {
'P': (0xD8, 0xDA, 0xD0), # 墨水屏
'K': (0x3C, 0x3F, 0x48), # 石墨机身
'E': (0x5A, 0x5E, 0x6A), # 机身受光边
# 换币灯的黄绿色霓虹(带透明度:光晕由外向内 20% / 40% / 60% / 80%,灯芯不透明)
'N1': (0xB6, 0xF0, 0x5A, 52), 'N2': (0xB6, 0xF0, 0x5A, 103), 'N3': (0xB6, 0xF0, 0x5A, 153), 'N4': (0xB6, 0xF0, 0x5A, 204),
'N5': (0xD8, 0xFF, 0x7A), 'N6': (0x6E, 0xA8, 0x2E), 'N7': (0x3E, 0x6A, 0x1E),
}
@@ -73,7 +76,7 @@ class Canvas:
for row in self.px:
line = bytearray()
for c in row:
r, g, b, a = (0, 0, 0, 0) if c is None else C[c] + (255,)
r, g, b, a = (0, 0, 0, 0) if c is None else (C[c] + (255,))[:4]
if bg is not None:
r, g, b = (round(v * a / 255 + w * (255 - a) / 255) for v, w in zip((r, g, b), bg))
a = 255
@@ -665,8 +668,31 @@ def key_drive():
return c
def otc_lamp_on():
"""换币灯的点亮叠加图(32×48,和原版暗灯同尺寸,(0,0) 对齐):只画灯罩下面的光晕和灯芯,叠在熄灭的暗灯上。
光晕是灯罩下沿的椭圆(第 25–40 行),越靠中心越不透明;灯芯是中间一根竖条,底下一圈灯座光。"""
c = Canvas(32, 48)
rows = { # 每行光晕的左右边界(含)
25: (5, 26), 26: (4, 27), 27: (4, 27), 28: (3, 28), 29: (3, 28), 30: (3, 28), 31: (2, 29), 32: (2, 29),
33: (2, 29), 34: (3, 28), 35: (3, 28), 36: (4, 27), 37: (5, 26), 38: (6, 25), 39: (9, 22), 40: (13, 18),
}
for y, (x0, x1) in rows.items():
for x in range(x0, x1 + 1):
if y == 25 and 7 <= x <= 24:
continue # 灯罩下沿正中还是灯罩
d = (((x - 15.5) / 14) ** 2 + ((y - 32) / 8.5) ** 2) ** 0.5
c.put(x, y, 'N4' if d < 0.45 else 'N3' if d < 0.65 else 'N2' if d < 0.85 else 'N1')
for y in range(27, 37): # 灯芯
c.put(15, y, 'N5'); c.put(16, y, 'N6')
for y, (x0, x1) in {37: (13, 18), 38: (11, 20), 39: (11, 20), 40: (13, 18)}.items():
for x in range(x0, x1 + 1): # 灯座反光
c.put(x, y, 'N6' if y < 40 else 'N7')
for x in range(14, 18):
c.put(x, 38, 'N5')
return c
SPRITES = {
'gpu_guide': guide,
'gpu_burnt_f': lambda: gpu_front('e', 'c', burnt=True),
'gpu_burnt_b': lambda: gpu_back('e', 'c', burnt=True),
'gpu_probidia_b': lambda: gpu_back('G', 'g2'),
@@ -779,6 +805,7 @@ OVERLAYS = {
'gpu_ov_fin1': lambda: ov_fingers(1), 'gpu_ov_fin2': lambda: ov_fingers(2),
'gpu_ov_brk1': lambda: ov_bracket(1),
'gpu_ov_stk2': lambda: ov_sticker(2),
'otc_lamp_on': otc_lamp_on,
}
for _n in (1, 2, 3):
OVERLAYS['gpu_ov_fan1_%d' % _n] = (lambda n=_n: ov_fan(1, n))
+2 -6
View File
@@ -116,7 +116,7 @@ namespace Decentralized.Domain
return true;
}
/// <summary>买一件:付 asset 币。钱包里按价值比例带走的污点和成本留在卖家那边。成功返回 true,库存减一。</summary>
/// <summary>买一件:付 asset 币。付出的币按它的脏比例带走脏枚数,成本按价值比例留在卖家那边。成功返回 true,库存减一。</summary>
public bool Buy(BmOffer o, Wallet w, string asset, Func<string, double> price)
{
if (o == null || o.Qty <= 0 || asset == Assets.CRC) return false;
@@ -126,7 +126,6 @@ namespace Decentralized.Domain
if (w.Avail(asset) + 1e-9 < qty) return false;
double total = w.Value(price);
double share = total > 0 ? Math.Min(1, o.Price / total) : 0;
w.Taint -= w.Taint * share;
w.Basis -= w.Basis * share;
w.Add(asset, -qty);
o.Qty--;
@@ -139,14 +138,11 @@ namespace Decentralized.Domain
public double DeskLeft(int day) => Math.Max(0, Tuning.PoneroDailyCap - (DeskDay == day ? DeskToday : 0));
/// <summary>兑入 Ponero:from 币 amount 个,扣手续费后按 Ponero 价到账;这部分价值带的污点被清除(不跟着 Ponero)。返回到账数量,额度或余额不足返回 -1。</summary>
/// <summary>兑入 Ponero:from 币 amount 个,扣手续费后按 Ponero 价到账;兑掉的币连同其中的脏枚数一起消失,换来的 Ponero 是干净的。返回到账数量,额度或余额不足返回 -1。</summary>
public double ToPonero(Wallet w, string from, double amount, int day, Func<string, double> price)
{
double value = amount * price(from);
if (from == Assets.Ponero || amount <= 0 || value <= 0 || w.Avail(from) + 1e-9 < amount || value > DeskLeft(day) + 1e-9) return -1;
double total = w.Value(price);
double share = total > 0 ? Math.Min(1, value / total) : 0;
w.Taint -= w.Taint * share;
w.Add(from, -amount);
double got = value * (1 - Tuning.PoneroFee) / price(Assets.Ponero);
w.Add(Assets.Ponero, got);
+11 -8
View File
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
@@ -46,8 +47,10 @@ namespace Decentralized.Domain
public double Qty;
/// <summary>付款的钱包地址:退款回这里。</summary>
public string Wallet;
/// <summary>付款时留在卖家那边的污点与成本:退款时一并还回,免得"付脏币再退款"洗钱。</summary>
public double Taint, Basis;
/// <summary>付款的币里脏的枚数和成本:退款时一并还回,免得"付脏币再退款"洗钱。</summary>
public double Dirty, Basis;
/// <summary>旧存档(数据版本 5 之前)的污点额(信用点),读档迁移时换成 <see cref="Dirty"/>。</summary>
[JsonProperty("Taint")] public double LegacyTaint;
public int PlacedDay, DueDay;
/// <summary>被截获后执法选择了上门:到货时快递员是执法人员,签收即记罪证。</summary>
public bool Controlled;
@@ -238,7 +241,7 @@ namespace Decentralized.Domain
/// <summary>目录里全部货按订单价算的总值(信用点),用来核对每周周转量。</summary>
public double CatalogValue => Catalog.Sum(l => (double)l.Price * l.Qty);
/// <summary>下单:钱包里的币按实付折算扣除,污点与成本按比例留在卖家那边(记在订单上)。成功返回订单。</summary>
/// <summary>下单:钱包里的币按实付折算扣除,脏枚数与成本按比例留在卖家那边(记在订单上)。成功返回订单。</summary>
public BmOrder Place(BmListing l, int count, PayMode mode, Wallet w, string asset, int day, Func<string, double> price)
{
if (l == null || count < 1 || count > l.Qty || asset == Assets.CRC || Closed(day)) return null;
@@ -251,13 +254,13 @@ namespace Decentralized.Domain
if (w.Avail(asset) + 1e-9 < qty) return null;
double total = w.Value(price);
double share = total > 0 ? Math.Min(1, credits / total) : 0;
double taint = w.Taint * share, basis = w.Basis * share;
w.Taint -= taint; w.Basis -= basis;
double dirty = qty * w.DirtyRatio(asset), basis = w.Basis * share;
w.Basis -= basis;
w.Add(asset, -qty);
var o = new BmOrder
{
Id = NextOrderId++, Item = l.Item, Seller = seller.Name, SellerScam = seller.Scam, Mode = mode, Count = count, Model = l.Model, FakeOf = l.FakeOf, Health = l.Health, Price = credits, Asset = asset, Qty = qty,
Taint = taint, Basis = basis, PlacedDay = day, DueDay = day + seller.Days, Wallet = w.Address,
Dirty = dirty, Basis = basis, PlacedDay = day, DueDay = day + seller.Days, Wallet = w.Address,
};
l.Qty -= count;
if (l.Qty <= 0) Catalog.Remove(l);
@@ -268,8 +271,8 @@ namespace Decentralized.Domain
private static void Refund(Wallet w, BmOrder o)
{
w.Add(o.Asset, o.Qty);
w.Taint += o.Taint; w.Basis += o.Basis;
w.AddDirty(o.Asset, o.Qty, o.Dirty);
w.Basis += o.Basis;
}
/// <summary>每晚推进订单:平台跑路、到期的订单(卖家跑路、被截获、死投被挖走)、转入可取状态。镇压期间到期顺延一天。</summary>
+4 -3
View File
@@ -115,7 +115,7 @@ namespace Decentralized.Domain
if (w.Avail(feeAsset) + 1e-9 < qty) return null;
double total = w.Value(price);
double share = total > 0 ? Math.Min(1, ListFee(listPrice) / total) : 0;
w.Taint -= w.Taint * share; w.Basis -= w.Basis * share;
w.Basis -= w.Basis * share;
w.Add(feeAsset, -qty);
var s = new BmSale { Item = item, Model = model, FakeOf = fakeOf, Health = health, Ref = refValue, Mult = m, ListPrice = listPrice, ListedDay = day, Wallet = w.Address, Heat = Math.Max(0, Math.Min(100, heat)) };
Sales.Add(s);
@@ -163,8 +163,9 @@ namespace Decentralized.Domain
double p = price(pa);
if (p <= 0) continue;
var w = wallet(s.Wallet);
w.Add(pa, s.Proceeds / p);
w.Taint += s.Taint;
// 回款的币按 污点 / 回款额 的比例是脏的
double got = s.Proceeds / p;
w.AddDirty(pa, got, s.Proceeds > 0 ? got * Math.Min(1, s.Taint / s.Proceeds) : 0);
w.Basis += s.Proceeds;
s.State = SaleState.Paid;
ev.Add(new BmEvent { Kind = "sale_paid", Sale = s, Amount = s.Proceeds });
+2 -3
View File
@@ -33,13 +33,12 @@ namespace Decentralized.Domain
}
}
/// <summary>收款:返回收到的币数。heatPct 是物品的赃物热度(0–100)。多付的溢价是顾客给的额外收入,不带污点。</summary>
/// <summary>收款:返回收到的币数。heatPct 是物品的赃物热度(0–100):按货值 × 热度那部分的币是脏的;多付的溢价是顾客给的额外收入,干净。</summary>
public static double Receive(Wallet w, string asset, double credits, double price, double heatPct)
{
if (credits <= 0 || price <= 0) return 0;
double qty = credits * (1 + Tuning.CoinPayPremium) / price; // 溢价只加在币数上,污点与成本仍按货值算
w.Add(asset, qty);
w.Taint += credits * Math.Max(0, Math.Min(100, heatPct)) / 100.0;
w.AddDirty(asset, qty, credits * Math.Max(0, Math.Min(100, heatPct)) / 100.0 / price);
w.Basis += credits;
return qty;
}
+17 -44
View File
@@ -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 天累计金额、污点比例、是否来自混币器或被标记地址判定。
/// 轻度命中(污点 &lt; 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;
-55
View File
@@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
namespace Decentralized.Domain
{
/// <summary>一笔黑币报价:卖家手里的币、按现价的市值和他要的现金。Sting 是钓鱼执法(收下就记罪证,币不会到)。</summary>
public sealed class HotOffer
{
public string Asset;
public double Qty, Value;
public int Cash;
public bool Sting;
}
/// <summary>
/// 收黑币:游戏里出了事,手里有来路不明的币的人上门,折价换现金。
/// 帮派火并、镇压期间黑市的人出手 CRT;革命军袭击上层、下层动荡期间有人出手抢来的 PSC;黑市声望够了以后偶尔有人出手勒索得来的 Ponero。
/// PSC、CRT 链上可追,收下的币按市值全额带污点;Ponero 不可追、不带污点,折扣也小,出口只有 Ponero 兑换处。
/// </summary>
public static class HotCoins
{
/// <summary>今天哪种黑币可能有人上门(按游戏当前的事件),没有返回 null。poneroOpen:Ponero 兑换处已开放。</summary>
public static string Pick(ICollection<MarketEvent> active, bool poneroOpen, Rng rng)
{
var c = new List<string>();
if (active.Contains(MarketEvent.GangWar) || active.Contains(MarketEvent.Crackdown)) c.Add(Assets.CRT);
if (active.Contains(MarketEvent.RevRaidUpper) || active.Contains(MarketEvent.LowerUnrest)) c.Add(Assets.PSC);
if (c.Count > 0 && rng.Chance(Tuning.HotEventChance)) return c[rng.Range(0, c.Count - 1)];
if (poneroOpen && rng.Chance(Tuning.HotPoneroChance)) return Assets.Ponero;
return null;
}
/// <summary>开价:市值 = 房租 × 区间,现金要价 = 市值 × 折扣(Ponero 折扣小)。crackdown:镇压期间钓鱼执法更多。</summary>
public static HotOffer Make(string asset, double coinPrice, double rent, bool crackdown, Rng rng)
{
if (coinPrice <= 0 || rent <= 0) return null;
double value = rent * rng.Range(Tuning.HotValueMinRents, Tuning.HotValueMaxRents);
bool ponero = asset == Assets.Ponero;
double cut = ponero ? rng.Range(Tuning.HotPoneroCutMin, Tuning.HotPoneroCutMax) : rng.Range(Tuning.HotCutMin, Tuning.HotCutMax);
return new HotOffer
{
Asset = asset, Qty = value / coinPrice, Value = value, Cash = (int)Math.Round(value * cut),
Sting = rng.Chance(Tuning.HotSting + (crackdown ? Tuning.HotStingCrackdown : 0)),
};
}
/// <summary>收下:币进钱包,成本是付出的现金;PSC、CRT 按市值记污点,Ponero 不记。钓鱼执法不在这里处理。</summary>
public static void Receive(Wallet w, HotOffer o)
{
w.Add(o.Asset, o.Qty);
w.Basis += o.Cash;
if (o.Asset != Assets.Ponero) w.Taint += o.Value;
}
}
}
+10 -8
View File
@@ -68,9 +68,8 @@ namespace Decentralized.Domain
}
/// <summary>
/// 矿架上游戏模块的效果。机器汇总的三项百分比(性能、效率、品质)各除以 <see cref="Tuning.ModuleRatio"/> 后生效,
/// 并有上限,避免模块把挖矿收益放大到破坏经济:算力最多 +20%,省电最多 25%,损耗最多降 30%。
/// 性能加成同时按比例提高损耗与暴毙率。负数(例如节能模块的负性能、超频模块的负效率)照常生效,耗电增加不设上限。
/// 矿架上游戏模块的效果。机器汇总的三项百分比(性能、效率、品质)各除以 <see cref="Tuning.ModuleRatio"/> 后生效,不设上限(强力模块是玩家自己拿到的)。
/// 性能加成同时按比例提高损耗与暴毙率。三个乘数各有防出错的下限:算力不低于 <see cref="Tuning.ModuleHashFloor"/>,耗电不低于 <see cref="Tuning.ModuleKjFloor"/>,损耗不低于 <see cref="Tuning.ModuleWearFloor"/>。
/// </summary>
public struct ModuleBonus
{
@@ -84,14 +83,17 @@ namespace Decentralized.Domain
public static ModuleBonus From(int performance, int efficiency, int quality)
{
double r = Tuning.ModuleRatio * 100.0;
double hash = Clamp(performance / r, -0.5, Tuning.ModuleHashCap);
double save = Clamp(efficiency / r, -1.0, Tuning.ModuleKjCap);
double wearCut = Clamp(quality / r, -1.0, Tuning.ModuleWearCap);
double hash = performance / r, save = efficiency / r, wearCut = quality / r;
double heat = 1 + Math.Max(0, hash);
return new ModuleBonus { Hash = 1 + hash, Kj = 1 - save, Wear = (1 - wearCut) * heat, Death = heat };
return new ModuleBonus
{
Hash = Math.Max(Tuning.ModuleHashFloor, 1 + hash),
Kj = Math.Max(Tuning.ModuleKjFloor, 1 - save),
Wear = Math.Max(Tuning.ModuleWearFloor, (1 - wearCut) * heat),
Death = heat,
};
}
private static double Clamp(double v, double lo, double hi) => Math.Max(lo, Math.Min(hi, v));
}
/// <summary>一张显卡或一台矿机的实例状态(存进物品标签)。</summary>
+61 -20
View File
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
@@ -9,20 +10,38 @@ namespace Decentralized.Domain
{
public string Asset;
public double Amount;
/// <summary>存入时一并带走的污点与成本(信用点)。</summary>
public double Taint, Basis;
/// <summary>存入的币里脏的枚数,和一并带走的成本(信用点)。</summary>
public double Dirty, Basis;
/// <summary>旧存档(数据版本 5 之前)的污点额(信用点),读档迁移时换成 <see cref="Dirty"/>。</summary>
[JsonProperty("Taint")] public double LegacyTaint;
public int Rounds;
public int ReadyDay;
/// <summary>存入的钱包地址:到期提回这里。</summary>
public string Wallet;
}
/// <summary>一笔到期提回的出款记录(泄露时按它记罪证):Cleaned 是这笔洗掉的污点,按提回时的价格折成信用点。</summary>
public sealed class MixReturn
{
public int Day;
public string Wallet, Asset;
public double Amount, Cleaned;
}
/// <summary>泄露的结果:最近几笔出款,一共洗掉了多少污点(信用点),折成的诈骗罪证。</summary>
public sealed class MixLeak
{
public int Count;
public double Cleaned, Fraud;
}
/// <summary>一笔混币结束时的结果,供夜间日志使用。</summary>
public sealed class MixResult
{
public bool Rugged;
public string Asset;
public double Amount, TaintBefore, TaintAfter;
/// <summary>被标记(制裁期间提出、从钓鱼混币器提出):不洗,整笔都是脏的。</summary>
public bool Flagged;
}
@@ -35,21 +54,44 @@ namespace Decentralized.Domain
{
public List<MixDeposit> Pending = new List<MixDeposit>();
public int SanctionedUntil = -1;
/// <summary>提回的币最近一次从混币器出来的天数,用来给出金审查加重。</summary>
public int LastReturnDay = -999;
/// <summary>最近的出款记录(Tumbler 的密钥落到治安部时用来还原)。</summary>
public List<MixReturn> Returns = new List<MixReturn>();
/// <summary>
/// 泄露:最近 <see cref="Tuning.TumblerLeakDays"/> 天的出款被还原,按这些出款洗掉的污点记诈骗罪证(<see cref="Wallet.FraudFor"/>)。记录清空。
/// </summary>
public MixLeak Leak(int day)
{
var recent = Returns.Where(x => day - x.Day <= Tuning.TumblerLeakDays).ToList();
double cleaned = recent.Sum(x => x.Cleaned);
Returns.Clear();
return new MixLeak { Count = recent.Count, Cleaned = cleaned, Fraud = Wallet.FraudFor(cleaned) };
}
private void Remember(MixReturn ret)
{
Returns.Add(ret);
Returns.RemoveAll(x => ret.Day - x.Day > Tuning.TumblerLeakDays);
while (Returns.Count > Tuning.TumblerReturnsMax) Returns.RemoveAt(0);
}
/// <summary>黑市卷款跑路:在途的存入全部丢失。</summary>
public List<MixResult> RugAll()
{
var list = Pending.Select(p => new MixResult { Rugged = true, Asset = p.Asset, Amount = p.Amount }).ToList();
Pending.Clear();
return list;
}
public bool Sanctioned(int day) => day <= SanctionedUntil;
/// <summary>资金来自混币器:最近 AML 窗口内提回过币,或者被标记。</summary>
public bool RecentlyMixed(int day) => day - LastReturnDay <= Tuning.AmlWindowDays;
/// <summary>一轮的清洗率:基准 × 匿名集系数。</summary>
/// <summary>每轮手续费(剧情可能改动)。</summary>
public static double Fee => Math.Min(0.5, Tuning.MixerFee * StoryMods.Current.MixerFee);
public static double RoundRate(double depositValue) => Tuning.MixerCleanRate * Tuning.MixerNpcVolume * StoryMods.Current.MixerVolume / (Tuning.MixerNpcVolume * StoryMods.Current.MixerVolume + Math.Max(0, depositValue));
public static double RoundRate(double depositValue, int day = -1) => Tuning.MixerCleanRate * (day >= 0 ? StoryMods.Current.MixClean(day) : 1) * Tuning.MixerNpcVolume * StoryMods.Current.MixerVolume / (Tuning.MixerNpcVolume * StoryMods.Current.MixerVolume + Math.Max(0, depositValue));
/// <summary>存入:从钱包按价值比例带走污点与成本。轮数超出范围或余额不足返回 null。</summary>
/// <summary>存入:按这种币的脏比例带走脏枚数,按价值比例带走成本。轮数超出范围或余额不足返回 null。</summary>
public MixDeposit Deposit(Wallet w, string asset, double amount, int rounds, int day, Func<string, double> price)
{
if (rounds < 1 || rounds > Tuning.MixerMaxRounds || amount <= 0 || w.Avail(asset) + 1e-9 < amount) return null;
@@ -57,11 +99,10 @@ namespace Decentralized.Domain
if (value < Tuning.MixerMin) return null;
double total = w.Value(price);
double share = total > 0 ? Math.Min(1, value / total) : 0;
double taint = w.Taint * share, basis = w.Basis * share;
double dirty = amount * w.DirtyRatio(asset), basis = w.Basis * share;
w.Add(asset, -amount);
w.Taint -= taint;
w.Basis -= basis;
var d = new MixDeposit { Asset = asset, Amount = amount, Taint = taint, Basis = basis, Rounds = rounds, ReadyDay = day + rounds, Wallet = w.Address };
var d = new MixDeposit { Asset = asset, Amount = amount, Dirty = dirty, Basis = basis, Rounds = rounds, ReadyDay = day + rounds, Wallet = w.Address };
Pending.Add(d);
return d;
}
@@ -89,17 +130,17 @@ namespace Decentralized.Domain
}
if (day < d.ReadyDay) continue;
Pending.Remove(d);
double rate = RoundRate(d.Amount * price(d.Asset));
double taint = d.Taint * Math.Pow(1 - rate, d.Rounds);
// 制裁期间提出的、从治安部的钓鱼混币器提出的,都被标记:不洗,这笔币按全额污点带回
bool flagged = Sanctioned(day) || StoryMods.Current.MixerHoneypot;
double rate = flagged ? 0 : RoundRate(d.Amount * price(d.Asset), day);
double amount = d.Amount * Math.Pow(1 - Fee, d.Rounds);
bool flagged = Sanctioned(day);
double dirty = flagged ? amount : Math.Min(amount, d.Dirty * Math.Pow(1 - rate, d.Rounds));
var w = wallet(d.Wallet);
w.Add(d.Asset, amount);
w.Taint += taint;
w.AddDirty(d.Asset, amount, dirty);
w.Basis += d.Basis;
if (flagged) w.Flagged = true;
LastReturnDay = day;
results.Add(new MixResult { Asset = d.Asset, Amount = amount, TaintBefore = d.Taint, TaintAfter = taint, Flagged = flagged });
double p = price(d.Asset);
Remember(new MixReturn { Day = day, Wallet = d.Wallet, Asset = d.Asset, Amount = amount, Cleaned = Math.Max(0, d.Dirty - dirty) * p });
results.Add(new MixResult { Asset = d.Asset, Amount = amount, TaintBefore = d.Dirty * p, TaintAfter = dirty * p, Flagged = flagged });
}
return results;
}
+131
View File
@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
namespace Decentralized.Domain
{
/// <summary>换币客户的来意:卖币给玩家(玩家付现金收币),或者向玩家买币(玩家转币收现金)。</summary>
public enum OtcSide { Sell, Buy }
/// <summary>
/// 一位换币客户的报价。Sell:客户手里的币(Qty、按现价的市值 Value、污点比例 TaintRatio)和他要的现金 Cash;Sting 是便衣(收下就记罪证,币不会到)。
/// Buy:客户要买的币和数量,Value 是按现价的市值,Cash 是他出的现金(市价加一个随机的溢价,可能低于市价)。
/// 玩家可以只成交一部分(<see cref="Otc.Part"/>),现金、市值按数量比例折算。
/// </summary>
public sealed class OtcOffer
{
public OtcSide Side;
public string Asset;
public double Qty, Value, TaintRatio;
public int Cash;
public bool Sting;
}
/// <summary>换币台的累计统计(存档):成交额(信用点)、笔数、客户地址编号;混币器提议是否已经发过(第三阶段用)。</summary>
public sealed class OtcDesk
{
public double Volume;
public int Deals, Seq;
public bool MixerOffered;
public void Record(double cash) { Volume += Math.Max(0, cash); Deals++; }
}
/// <summary>
/// 换币客户(场外换币台):小偷灯切到"换币客户"后招来,一半来卖币、一半来买币。
/// 卖币沿用原来的收黑币:帮派火并、镇压期间多是 CRT,革命军袭击上层、下层动荡期间多是 PSC,这类币全额带污点;
/// 其他时候在 PSC、CRT、CRA 里随机,污点比例随机,现金价按污点比例打折;Ponero 兑换处开放后偶尔有人出手 Ponero(不带污点)。
/// 买币:什么币都可能要,出价是市价加一个随机溢价,玩家自己决定成不成交;卖出的币不追查污点。两边都可以只成交一部分。
/// </summary>
public static class Otc
{
/// <summary>客户卖币时可能出手的币(没有相关事件时随机)。</summary>
public static readonly string[] SellAssets = { Assets.PSC, Assets.CRT, Assets.CRA };
/// <summary>客户买币时可能要的币。</summary>
public static readonly string[] BuyAssets = { Assets.PSC, Assets.CRC, Assets.CRT, Assets.CRA, Assets.SPARK };
public static OtcSide PickSide(Rng rng) => rng.Chance(Tuning.OtcSellShare) ? OtcSide.Sell : OtcSide.Buy;
/// <summary>和当前事件有关的币(帮派火并、镇压 → CRT;袭击上层、下层动荡 → PSC),没有返回 null。</summary>
public static string EventAsset(ICollection<MarketEvent> active, Rng rng)
{
var c = new List<string>();
if (active.Contains(MarketEvent.GangWar) || active.Contains(MarketEvent.Crackdown)) c.Add(Assets.CRT);
if (active.Contains(MarketEvent.RevRaidUpper) || active.Contains(MarketEvent.LowerUnrest)) c.Add(Assets.PSC);
return c.Count == 0 ? null : c[rng.Range(0, c.Count - 1)];
}
/// <summary>
/// 卖币客户的报价。有相关事件时按 OtcEventChance 出手事件里来的币(全额污点);Ponero 兑换处开放时按 OtcPoneroChance 出手 Ponero;
/// 否则在 PSC、CRT、CRA 里随机,污点比例在 OtcTaintMin–OtcTaintMax 之间。市值 = 房租 × 区间;现金价 = 市值 × 折扣,折扣按污点比例从 OtcCutClean 线性降到 OtcCutDirty。
/// crackdown:镇压期间便衣更多。
/// </summary>
public static OtcOffer MakeSell(ICollection<MarketEvent> active, bool poneroOpen, Func<string, double> price, double rent, Rng rng, double stingMult = 1)
{
if (rent <= 0) return null;
string asset = null; double ratio;
string ev = EventAsset(active, rng);
if (ev != null && rng.Chance(Tuning.OtcEventChance)) { asset = ev; ratio = 1; }
else if (poneroOpen && rng.Chance(Tuning.OtcPoneroChance)) { asset = Assets.Ponero; ratio = 0; }
else { asset = SellAssets[rng.Range(0, SellAssets.Length - 1)]; ratio = rng.Range(Tuning.OtcTaintMin, Tuning.OtcTaintMax); }
double p = price(asset);
if (p <= 0) return null;
double value = rent * rng.Range(Tuning.OtcValueMinRents, Tuning.OtcValueMaxRents);
double cut = asset == Assets.Ponero ? rng.Range(Tuning.OtcPoneroCutMin, Tuning.OtcPoneroCutMax) : CutFor(ratio);
bool crackdown = active.Contains(MarketEvent.Crackdown);
return new OtcOffer
{
Side = OtcSide.Sell, Asset = asset, Qty = value / p, Value = value, TaintRatio = ratio, Cash = (int)Math.Round(value * cut),
Sting = rng.Chance(Math.Min(1, (Tuning.OtcSting + (crackdown ? Tuning.OtcStingCrackdown : 0)) * stingMult)),
};
}
/// <summary>现金价占市值的比例:污点比例 0 时 OtcCutClean,1 时 OtcCutDirty,之间线性。</summary>
public static double CutFor(double taintRatio) => Tuning.OtcCutClean + (Tuning.OtcCutDirty - Tuning.OtcCutClean) * Math.Max(0, Math.Min(1, taintRatio));
/// <summary>买币客户的来意:随机一种币,市值 = 房租 × OtcBuyMinRents–OtcBuyMaxRents,出价 = 市值 × (1 + OtcBidMin–OtcBidMax 之间的随机溢价)。</summary>
public static OtcOffer MakeBuy(Func<string, double> price, double rent, Rng rng)
{
if (rent <= 0) return null;
string asset = BuyAssets[rng.Range(0, BuyAssets.Length - 1)];
double p = price(asset);
if (p <= 0) return null;
double value = rent * rng.Range(Tuning.OtcBuyMinRents, Tuning.OtcBuyMaxRents);
return new OtcOffer { Side = OtcSide.Buy, Asset = asset, Qty = value / p, Value = value, Cash = (int)Math.Round(value * (1 + rng.Range(Tuning.OtcBidMin, Tuning.OtcBidMax))) };
}
/// <summary>只成交 qty(不超过报价的数量):现金、市值按数量比例折算,污点比例和便衣不变。qty 不合法返回 null。</summary>
public static OtcOffer Part(OtcOffer o, double qty)
{
if (o == null || qty <= 0 || o.Qty <= 0) return null;
double f = Math.Min(1, qty / o.Qty);
return new OtcOffer { Side = o.Side, Asset = o.Asset, Qty = o.Qty * f, Value = o.Value * f, TaintRatio = o.TaintRatio, Cash = (int)Math.Round(o.Cash * f), Sting = o.Sting };
}
/// <summary>收下卖币客户的币:币进钱包,其中 数量 × 污点比例 枚是脏的;成本是付出的现金。</summary>
public static void Receive(Wallet w, OtcOffer o)
{
w.AddDirty(o.Asset, o.Qty, o.Qty * o.TaintRatio);
w.Basis += o.Cash;
}
/// <summary>
/// 客户地址的来历(写进链上账本,玩家在浏览器里能查到):1–OtcHistoryMax 笔转入,每一笔按污点比例的概率来自"脏"的来源(混币器、黑市平台、骗局地址),否则来自 Poinbase 或 DEX。
/// 返回 (来源地址, 数量占比) 列表;dirty 和 clean 由接入层给出地址。
/// </summary>
public static List<KeyValuePair<string, double>> History(OtcOffer o, IList<string> dirty, IList<string> clean, Rng rng)
{
var list = new List<KeyValuePair<string, double>>();
int n = rng.Range(1, Tuning.OtcHistoryMax);
double left = 1;
for (int i = 0; i < n; i++)
{
bool isDirty = dirty.Count > 0 && rng.Chance(o.TaintRatio);
var src = isDirty ? dirty[rng.Range(0, dirty.Count - 1)] : clean[rng.Range(0, clean.Count - 1)];
double share = i == n - 1 ? left : left * rng.Range(0.3, 0.7);
left -= share;
list.Add(new KeyValuePair<string, double>(src, share));
}
return list;
}
}
}
+226
View File
@@ -0,0 +1,226 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace Decentralized.Domain
{
/// <summary>自营混币器里一种币:玩家的本金(数量、其中脏的枚数)和还没取走的手续费收入(取走时整笔是脏的)。</summary>
public sealed class MixPool
{
public double Capital, CapDirty, Earned;
/// <summary>旧存档(数据版本 5 之前)本金的污点额(信用点),读档迁移时换成 <see cref="CapDirty"/>。</summary>
[JsonProperty("CapTaint")] public double LegacyCapTaint;
}
/// <summary>NPC 客户的一笔存入:扣掉手续费后的数量、存入时的污点额(信用点)、轮数、到期日。</summary>
public sealed class OwnMixDeposit
{
public string Asset;
public double Amount, Taint;
public int Rounds, ReadyDay;
}
/// <summary>一晚发生的事。Kind:deposit(今晚存入合计)/ payout(今晚出款合计,Ratio 是出款剩下的污点比例)/ sting(便衣存入)/ seized(被查封)。</summary>
public sealed class OwnMixEvent
{
public string Kind;
public double Value, Ratio, Crime;
public int Count;
}
/// <summary>
/// 自营混币器:玩家注资开张,NPC 客户把带污点的币存进来,几天后出款,玩家收手续费。和普通混币器一样,每一轮按匿名集洗掉一部分污点:
/// 匿名集主要是玩家的本金和没取走的收入(客户在途的币按一个比例折算),注资越多洗得越干净,客户越愿意来。
/// 手续费是玩家的收入,留在池子里,取走时全额带污点;本金取回时带着它自己的污点(注资用脏币,取回的也脏)。
/// 客流 = 基础需求(天) × 黑市声望 × 和 Tumbler 比的吸引力(费率加上出款剩下的污点,近期出款的滑动平均就是口碑)。
/// 风声随客流累积(池子越大越难追查,累积得越慢)、每晚衰减,按风声有概率被查封:池子和在途的存入全部没收,记销赃罪证,停业若干天。营业的晚上可能有便衣存入,记销赃罪证。
/// 玩家可以随时卷款跑路:池子连同客户的币全部进钱包、全额带污点,记诈骗罪证,混币器永久关闭。
/// </summary>
public sealed class OwnMixer
{
public static readonly string[] AssetsList = { Assets.PSC, Assets.CRC, Assets.CRT, Assets.CRA };
public bool Open;
public bool Closed;
public int SeizedUntil = -1;
public double Fee = Tuning.OwnMixFeeDefault;
/// <summary>口碑:近期出款剩下的污点比例(滑动平均),越低越好;开张时按当时的匿名集估。</summary>
public double Rating = 1;
public double Heat;
public Dictionary<string, MixPool> Pools = new Dictionary<string, MixPool>();
public List<OwnMixDeposit> Pending = new List<OwnMixDeposit>();
/// <summary>累计手续费收入(信用点,按入账时的价格),统计用。</summary>
public double FeesTotal;
public bool Seized(int day) => day <= SeizedUntil;
public bool Running(int day) => Open && !Closed && !Seized(day);
public MixPool Pool(string asset)
{
if (!Pools.TryGetValue(asset, out var p)) Pools[asset] = p = new MixPool();
return p;
}
/// <summary>一种币在池子里的总数:本金、收入、客户在途的币。</summary>
public double Total(string asset) => Pool(asset).Capital + Pool(asset).Earned + Pending.Where(x => x.Asset == asset).Sum(x => x.Amount);
/// <summary>匿名集(信用点):玩家的本金和收入,加上客户在途的币按 OwnMixPendingWeight 折算(主要靠注资撑起来)。</summary>
public double Anonymity(Func<string, double> price) => AssetsList.Sum(a => (Free(a) + Tuning.OwnMixPendingWeight * Pending.Where(x => x.Asset == a).Sum(x => x.Amount)) * price(a));
/// <summary>每一轮洗掉的污点比例:基准清洗率 × 匿名集 / (匿名集 + OwnMixAnon0)。</summary>
public static double RoundRate(double anonymity) => Tuning.MixerCleanRate * anonymity / (anonymity + Tuning.OwnMixAnon0);
/// <summary>按平均轮数出款时剩下的污点比例。</summary>
public static double Remain(double rate) => Math.Pow(1 - rate, Tuning.OwnMixAvgRounds);
/// <summary>玩家能取走的:本金加收入(客户在途的币不能动)。</summary>
public double Free(string asset) => Pool(asset).Capital + Pool(asset).Earned;
/// <summary>开张(或停业后重新开张):口碑按现在的匿名集估。</summary>
public void Start(Func<string, double> price)
{
Open = true;
Rating = Remain(RoundRate(Anonymity(price)));
}
/// <summary>注资:币从钱包进本金,按这种币的脏比例带上脏枚数。</summary>
public bool Inject(Wallet w, string asset, double amount, Func<string, double> price)
{
if (amount <= 0 || w.Avail(asset) + 1e-9 < amount || !AssetsList.Contains(asset)) return false;
double total = w.Value(price), share = total > 0 ? Math.Min(1, amount * price(asset) / total) : 0;
double dirty = amount * w.DirtyRatio(asset), basis = w.Basis * share;
w.Add(asset, -amount); w.Basis -= basis;
var p = Pool(asset);
p.Capital += amount; p.CapDirty += dirty;
return true;
}
/// <summary>取回:不超过 <see cref="Free"/>。先取手续费收入(整笔是脏的),再取本金(按本金的脏比例)。</summary>
public bool Withdraw(Wallet w, string asset, double amount)
{
var p = Pool(asset);
if (amount <= 0 || amount > Free(asset) + 1e-9) return false;
double fromFees = Math.Min(amount, p.Earned), fromCap = Math.Min(p.Capital, amount - fromFees);
double capDirty = p.Capital > 1e-12 ? p.CapDirty * fromCap / p.Capital : 0;
p.Earned -= fromFees; p.Capital -= fromCap; p.CapDirty -= capDirty;
w.AddDirty(asset, fromFees + fromCap, fromFees + capDirty);
return true;
}
/// <summary>卷款跑路:本金、收入和客户在途的币全部进钱包,整笔是脏的,永久关闭。返回 (币种, 数量)。</summary>
public List<KeyValuePair<string, double>> Rug(Wallet w)
{
var got = new List<KeyValuePair<string, double>>();
foreach (var a in AssetsList)
{
double amt = Total(a);
if (amt <= 1e-9) continue;
w.AddDirty(a, amt, amt);
got.Add(new KeyValuePair<string, double>(a, amt));
}
Pools.Clear(); Pending.Clear();
Open = false; Closed = true;
return got;
}
/// <summary>今晚的基础需求(信用点):随天数增长。</summary>
public static double Demand(int day) => Tuning.OwnMixDemand0 + Tuning.OwnMixDemandPerDay * day;
/// <summary>
/// 和 Tumbler 的混币器比,分到的客流(0–1):客户的成本 = 费率 + 出款剩下的污点比例 × OwnMixTaintWeight,比对手便宜就多分。
/// rivalRemain 是对手出款剩下的污点比例;对手关停时客户的另一条路是不混(成本再加 OwnMixRivalGone)。
/// </summary>
public double Share(double rivalFee, double rivalRemain, bool rivalOpen)
{
double mine = Fee + Rating * Tuning.OwnMixTaintWeight;
double rival = rivalFee + rivalRemain * Tuning.OwnMixTaintWeight + (rivalOpen ? 0 : Tuning.OwnMixRivalGone);
return 1 / (1 + Math.Exp((mine - rival) / Tuning.OwnMixFeeScale));
}
/// <summary>
/// 夜间:到期的存入出款(按匿名集洗掉污点),口碑跟着出款走;营业中接新的存入;风声累积、按风声查封。
/// bmRep:黑市声望(0–100);rivalFee、rivalRemain、rivalOpen:Tumbler 混币器的费率、出款剩下的污点比例、是否在营业;enforce:剧情带来的执法倍数;crackdown:镇压期间。
/// </summary>
public List<OwnMixEvent> Night(int day, Func<string, double> price, int bmRep, double rivalFee, double rivalRemain, bool rivalOpen, double enforce, bool crackdown, Rng rng)
{
var evs = new List<OwnMixEvent>();
if (Closed || Seized(day)) return evs;
// 出款(客户的币回到客户手里,污点按匿名集洗掉一部分)
double rate = RoundRate(Anonymity(price));
double paid = 0; int paidN = 0;
foreach (var dep in Pending.Where(x => x.ReadyDay <= day).ToList())
{
Pending.Remove(dep);
paid += dep.Amount * price(dep.Asset);
paidN++;
}
if (paidN > 0)
{
double remain = Remain(rate);
Rating += Tuning.OwnMixRatingSpeed * (remain - Rating);
evs.Add(new OwnMixEvent { Kind = "payout", Value = paid, Ratio = remain, Count = paidN });
}
// 存入
double inValue = 0; int n = 0;
if (Open)
{
double repF = Tuning.OwnMixRepMin + (1 - Tuning.OwnMixRepMin) * Math.Max(0, Math.Min(100, bmRep)) / 100.0;
double demand = Demand(day) * repF * Share(rivalFee, rivalRemain, rivalOpen);
double left = demand * rng.Range(1 - Tuning.OwnMixNoise, 1 + Tuning.OwnMixNoise);
while (left > Tuning.MixerMin)
{
double v = Math.Min(left, Tuning.OwnMixTicket * rng.Range(0.5, 1.5));
left -= v;
string asset = Pick(rng);
double px = price(asset);
if (px <= 0) continue;
double amt = v / px;
Pool(asset).Earned += amt * Fee;
FeesTotal += v * Fee;
int rounds = rng.Range(1, Tuning.MixerMaxRounds);
Pending.Add(new OwnMixDeposit { Asset = asset, Amount = amt * (1 - Fee), Taint = v * rng.Range(Tuning.OwnMixInTaintMin, Tuning.OwnMixInTaintMax), Rounds = rounds, ReadyDay = day + rounds });
inValue += v; n++;
}
if (n > 0)
{
evs.Add(new OwnMixEvent { Kind = "deposit", Value = inValue, Count = n });
// 便衣:营业的晚上按概率有一笔存入来自治安部
if (rng.Chance(Tuning.OwnMixSting))
{
double v = inValue / n;
evs.Add(new OwnMixEvent { Kind = "sting", Value = v, Crime = Math.Min(Tuning.CrimeEventCap, v * Tuning.OwnMixStingCrime) });
}
}
}
// 风声与查封
// 池子越大,客户的币越好藏,同样的客流引起的风声越小
Heat = Heat * Tuning.OwnMixHeatKeep + inValue / Tuning.OwnMixHeatScale / (1 + Anonymity(price) / Tuning.OwnMixHeatAnon0);
if (rng.Chance(SeizeChance(enforce, crackdown)))
{
double lost = AssetsList.Sum(a => Total(a) * price(a));
evs.Add(new OwnMixEvent { Kind = "seized", Value = lost, Crime = Math.Min(Tuning.CrimeEventCap, lost * Tuning.OwnMixSeizeCrime) });
Pools.Clear(); Pending.Clear();
SeizedUntil = day + Tuning.OwnMixSeizeDays;
Heat = 0; Open = false;
}
return evs;
}
/// <summary>今晚被查封的概率:风声 × 系数 × 执法倍数(镇压期间再乘),封顶。</summary>
public double SeizeChance(double enforce, bool crackdown) => Math.Min(Tuning.OwnMixSeizeMax, Tuning.OwnMixSeizeRate * Heat * enforce * (crackdown ? Tuning.OwnMixSeizeCrackdown : 1));
/// <summary>风声的三档提示:0 低、1 中、2 高(按今晚的查封概率)。</summary>
public int HeatLevel(double enforce) { double s = SeizeChance(enforce, false); return s < Tuning.OwnMixHeatMid ? 0 : s < Tuning.OwnMixHeatHigh ? 1 : 2; }
private static string Pick(Rng rng)
{
double r = rng.NextDouble() * Tuning.OwnMixAssetWeights.Sum();
for (int i = 0; i < AssetsList.Length; i++) { r -= Tuning.OwnMixAssetWeights[i]; if (r <= 0) return AssetsList[i]; }
return AssetsList[AssetsList.Length - 1];
}
}
}
+14 -11
View File
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
@@ -9,7 +10,9 @@ namespace Decentralized.Domain
{
public string Product;
public bool Long;
public double Size, Entry, Margin, Taint, Basis;
public double Size, Entry, Margin, Basis;
/// <summary>旧存档(数据版本 5 之前)保证金带的污点额:读档迁移时挪到 1 号钱包,之后为 0(Poinbase 没有污点)。</summary>
[JsonProperty("Taint")] public double LegacyTaint;
public double Notional(double price) => Size * price;
public double Pnl(double price) => (Long ? 1 : -1) * Size * (price - Entry);
@@ -97,13 +100,13 @@ namespace Decentralized.Domain
double eq0 = (pos?.Margin ?? 0) + margin + (isLong ? 1 : -1) * sizeAll * (spot - entryAll);
if (eq0 < (Tuning.PerpMaint + Tuning.PerpOpenBuffer) * sizeAll * spot) return PerpResult.Thin;
double share = Share(acct, margin, pr);
double taint = acct.Taint * share, basis = acct.Basis * share;
acct.Add(Assets.CRC, -(margin + fee)); acct.Taint -= taint; acct.Basis -= basis;
double basis = acct.Basis * share;
acct.Add(Assets.CRC, -(margin + fee)); acct.Basis -= basis;
InsuranceFund += fee * Tuning.PerpFeeToFund;
if (pos == null) Positions.Add(pos = new PerpPosition { Product = product, Long = isLong, Entry = price });
double size = notional / price;
pos.Entry = (pos.Size * pos.Entry + size * price) / (pos.Size + size);
pos.Size += size; pos.Margin += margin; pos.Taint += taint; pos.Basis += basis;
pos.Size += size; pos.Margin += margin; pos.Basis += basis;
return PerpResult.Ok;
}
@@ -115,13 +118,13 @@ namespace Decentralized.Domain
if (amount <= 0) return PerpResult.Margin;
if (acct.Avail(Assets.CRC) + 1e-9 < amount) return PerpResult.Funds;
double share = Share(acct, amount, pr);
double taint = acct.Taint * share, basis = acct.Basis * share;
acct.Add(Assets.CRC, -amount); acct.Taint -= taint; acct.Basis -= basis;
pos.Margin += amount; pos.Taint += taint; pos.Basis += basis;
double basis = acct.Basis * share;
acct.Add(Assets.CRC, -amount); acct.Basis -= basis;
pos.Margin += amount; pos.Basis += basis;
return PerpResult.Ok;
}
/// <summary>平仓(按白天的现价):权益减手续费回到账户,污点按剩下的比例退回,盈利是干净的新币。权益不够付时缺口按穿仓处理。返回回到账户的 CRC。</summary>
/// <summary>平仓(按白天的现价):权益减手续费回到账户,成本按剩下的比例退回。权益不够付时缺口按穿仓处理。返回回到账户的 CRC。</summary>
public double Close(Wallet acct, string product, double price, int day, out double deficit)
{
deficit = 0;
@@ -140,7 +143,7 @@ namespace Decentralized.Domain
private void Return(Wallet acct, PerpPosition pos, double payout)
{
double keep = pos.Margin > 0 ? Math.Min(1, payout / pos.Margin) : 0;
acct.Add(Assets.CRC, payout); acct.Taint += pos.Taint * keep; acct.Basis += pos.Basis * keep;
acct.Add(Assets.CRC, payout); acct.Basis += pos.Basis * keep;
}
/// <summary>穿仓缺口:保险基金先垫,不够的记成欠款。</summary>
@@ -223,8 +226,8 @@ namespace Decentralized.Domain
if (repay > 1e-9)
{
double share = Share(acct, repay, pr);
double taint = acct.Taint * share, basis = acct.Basis * share;
acct.Add(Assets.CRC, -repay); acct.Taint -= taint; acct.Basis -= basis;
double basis = acct.Basis * share;
acct.Add(Assets.CRC, -repay); acct.Basis -= basis;
Debt -= repay;
evs.Add(new PerpEvent { Kind = "repaid", Amount = repay });
}
+1 -1
View File
@@ -75,7 +75,7 @@ namespace Decentralized.Domain
{
if (!Open) return 0;
double take = Pool;
acct.Add(Assets.CRC, take); acct.Taint += take;
acct.AddDirty(Assets.CRC, take, take);
Open = false; Done = true; Pool = 0;
return take;
}
+54 -44
View File
@@ -1,10 +1,11 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace Decentralized.Domain
{
public enum PredictKind { Event, Faction, Purity, Price, Gas, Cra, Era }
public enum PredictKind { Event, Faction, Purity, Price, Gas, Cra, Era, Trial }
public enum PredictResult { Ok, Closed, Amount, Funds, Cap, Opposite, NoPosition }
@@ -77,7 +78,10 @@ namespace Decentralized.Domain
/// <summary>持仓所在的钱包地址(下注签名的钱包,结算和卖出都回到它)。</summary>
public string Wallet;
public bool Yes;
public double Shares, Cost, Taint, Basis, Insider;
/// <summary>Dirty:下注付出的 CRC 里脏的枚数(兑付时按比例带回)。</summary>
public double Shares, Cost, Dirty, Basis, Insider;
/// <summary>旧存档(数据版本 5 之前)的污点额(信用点),读档迁移时换成 <see cref="Dirty"/>。</summary>
[JsonProperty("Taint")] public double LegacyTaint;
}
/// <summary>情报贩子卖的内幕消息:对某个盘口给出"是 / 否"的说法,三档准确率,可能是假的(与真相相反)。</summary>
@@ -113,12 +117,13 @@ namespace Decentralized.Domain
public bool Held;
}
/// <summary>夜间结算发生的事。Kind:win / lose / void / caught。</summary>
/// <summary>夜间结算发生的事。Kind:win / lose / void / insider(内幕份额对应的兑付记成脏币)/ manip(被判操纵,整笔兑付记成脏币)。
/// Amount 是兑付或本金;Taint 是因此记成脏的 CRC 枚数。</summary>
public sealed class PredictEvent
{
public string Kind, Title;
public PredictMarket Market;
public double Amount, Fraud;
public double Amount, Taint;
}
/// <summary>候选盘口(接入层按游戏当前状态给出):先验概率按窗口算。</summary>
@@ -156,7 +161,7 @@ namespace Decentralized.Domain
}
/// <summary>
/// 买入:花 spend(含手续费)的 CRC。污点与成本按花掉的价值比例随资金进持仓。insider:这笔单下单时玩家已经知道答案(传闻纸条、派系特权)。
/// 买入:花 spend(含手续费)的 CRC。CRC 的脏枚数按比例、成本按花掉的价值比例随资金进持仓。insider:这笔单下单时玩家已经知道答案(传闻纸条、派系特权)。
/// </summary>
public PredictResult Buy(Wallet acct, PredictMarket m, bool yes, double spend, int day, Func<string, double> pr, bool insider, out double shares)
{
@@ -171,53 +176,64 @@ namespace Decentralized.Domain
shares = m.SharesFor(yes, cost);
if (all.Sum(x => x.Shares) + shares > m.ShareCap + 1e-9) { shares = 0; return PredictResult.Cap; }
double share = Share(acct, spend, pr);
double taint = acct.Taint * share, basis = acct.Basis * share;
acct.Add(Assets.CRC, -spend); acct.Taint -= taint; acct.Basis -= basis;
double dirty = spend * acct.DirtyRatio(Assets.CRC), basis = acct.Basis * share;
acct.Add(Assets.CRC, -spend); acct.Basis -= basis;
if (yes) m.QYes += shares; else m.QNo += shares;
if (pos == null) Positions.Add(pos = new PredictPos { Market = m.Id, Yes = yes, Wallet = acct.Address });
pos.Shares += shares; pos.Cost += cost; pos.Taint += taint; pos.Basis += basis;
pos.Shares += shares; pos.Cost += cost; pos.Dirty += dirty; pos.Basis += basis;
if (insider) pos.Insider += shares;
return PredictResult.Ok;
}
/// <summary>全部卖出这个钱包在盘口里的持仓(盘口关闭前):收回 LMSR 的卖价减手续费,污点按比例退回,赚的部分是干净的。返回收回的 CRC。</summary>
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, out double got) => SellAll(acct, m, day, null, out got, out _);
/// <summary>全部卖出这个钱包在盘口里的持仓(盘口关闭前):收回 LMSR 的卖价减手续费,脏枚数按比例带回,赚的部分是干净的。返回收回的 CRC。</summary>
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, Func<string, double> price, out double got) => SellAll(acct, m, day, price, out got, out _);
/// <summary>
/// 全部卖出;内幕持仓(这个盘口合计)达到查处线、而且卖出有赚,按查处概率被查:本金和收益全部没收,收益的一半记罪证(fraud &gt; 0)。
/// 先买后卖、赶在结算前离场,同样会被查。
/// 全部卖出;持仓里的内幕份额(下单时已经知道答案)对应的那部分卖出所得记成脏的 CRC,taint 是记成脏的枚数。先买后卖、赶在结算前离场也一样。
/// </summary>
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, Rng rng, out double got, out double fraud)
public PredictResult SellAll(Wallet acct, PredictMarket m, int day, Func<string, double> price, out double got, out double taint)
{
got = 0; fraud = 0;
got = 0; taint = 0;
var pos = PosOf(m.Id, acct.Address);
if (pos == null) return PredictResult.NoPosition;
if (!m.IsOpen(day)) return PredictResult.Closed;
double value = m.SellValue(pos.Yes, pos.Shares);
got = value * (1 - Tuning.PredictFee);
bool insider = PosAll(m.Id).Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap;
if (pos.Yes) m.QYes -= pos.Shares; else m.QNo -= pos.Shares;
Positions.Remove(pos);
if (rng != null && insider && pos.Insider > 0 && got > pos.Cost && rng.Chance(Math.Min(1, Tuning.PredictInsiderCatch * StoryMods.Current.Enforce(day))))
{
fraud = Math.Min(Tuning.PredictFraudCap, (got - pos.Cost) * Tuning.PredictFraudShare);
got = 0;
}
Pay(acct, pos, got);
double back = Pay(acct, pos, got);
taint = MarkInsider(acct, pos, got, back);
return PredictResult.Ok;
}
private static void Pay(Wallet acct, PredictPos pos, double payout)
/// <summary>兑付进钱包:下注时带进来的脏枚数按兑付占成本的比例带回(不超过兑付)。返回带回的脏枚数。</summary>
private static double Pay(Wallet acct, PredictPos pos, double payout)
{
double keep = pos.Cost > 0 ? Math.Min(1, payout / pos.Cost) : 0;
acct.Add(Assets.CRC, payout); acct.Taint += pos.Taint * keep; acct.Basis += pos.Basis * keep;
double dirty = Math.Min(payout, pos.Dirty * keep);
acct.AddDirty(Assets.CRC, payout, dirty); acct.Basis += pos.Basis * keep;
return dirty;
}
/// <summary>这笔兑付里还干净的部分(兑付 − 已经带回的脏枚数)里,再记 qty 枚为脏的。返回记上的枚数。</summary>
private static double MarkPayout(Wallet acct, double payout, double alreadyDirty, double qty)
{
double mark = Math.Max(0, Math.Min(qty, payout - alreadyDirty));
return mark - acct.MarkDirty(Assets.CRC, mark);
}
/// <summary>内幕份额对应的兑付记成脏的 CRC。返回记上的枚数。</summary>
private static double MarkInsider(Wallet acct, PredictPos pos, double payout, double alreadyDirty)
{
if (pos.Insider <= 0 || pos.Shares <= 0 || payout <= 0) return 0;
return MarkPayout(acct, payout, alreadyDirty, payout * Math.Min(1, pos.Insider / pos.Shares));
}
/// <summary>
/// 夜间:先登记今晚的观测,再结算 ToDay 到了的盘口(赢的钱回到下注的钱包,wallet 按地址找)。outcome 由接入层给出(事件 / 派系读观测,纯度和行情读游戏数值),null 表示拿不到数据。
/// 赢家每份兑付 1 信用点;内幕持仓达到份额上限的 <see cref="Tuning.PredictInsiderShare"/> 后,有概率被查:本金和收益全部没收,收益的一半记罪证。
/// 赢家每份兑付 1 信用点,照常兑付;内幕份额对应的兑付记成脏的 CRC,行情盘被判操纵时整笔兑付都记成脏的。
/// </summary>
public List<PredictEvent> Night(int day, IEnumerable<string> activeNow, Func<PredictMarket, bool?> outcome, Func<string, Wallet> wallet, Rng rng, double push = 0)
public List<PredictEvent> Night(int day, IEnumerable<string> activeNow, Func<PredictMarket, bool?> outcome, Func<string, Wallet> wallet, Func<string, double> price, Rng rng, double push = 0)
{
var now = activeNow.Distinct().ToList();
if (Prev != null) Started[day] = now.Where(x => !Prev.Contains(x)).ToList();
@@ -243,14 +259,6 @@ namespace Decentralized.Domain
if (!Stats.TryGetValue(sk, out var st)) Stats[sk] = st = new PredictStat();
st.Count++; st.SumP0 += m.P0; if (res.Value) st.Hits++;
var winners = all.Where(x => x.Yes == res.Value).ToList();
// 操纵:行情盘上,赢家的方向和玩家推价的方向一致时按推价幅度被查——本金和收益都没收,收益的一半记罪证
if (m.Kind == PredictKind.Price && winners.Count > 0 && winners.Any(x => x.Yes ? push > 0 : push < 0) && rng.Chance(Math.Min(1, Market.ManipChance(push) * StoryMods.Current.Enforce(day))))
{
double profitM = winners.Sum(x => x.Shares) - cost;
evs.Add(new PredictEvent { Kind = "manip", Title = m.Title, Market = m, Amount = cost, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profitM) * Tuning.PredictFraudShare) });
Record(day, m, m.Outcome, -cost, true);
continue;
}
if (all.Count > 0 && winners.Count == 0)
{
evs.Add(new PredictEvent { Kind = "lose", Title = m.Title, Market = m, Amount = cost });
@@ -259,19 +267,21 @@ namespace Decentralized.Domain
else if (winners.Count > 0)
{
double payout = winners.Sum(x => x.Shares), profit = payout - cost;
// 内幕持仓按盘口合计(拆到几个钱包里也一样)
if (all.Sum(x => x.Insider) >= Tuning.PredictInsiderShare * m.ShareCap && rng.Chance(Math.Min(1, Tuning.PredictInsiderCatch * StoryMods.Current.Enforce(day))))
// 操纵:行情盘上,赢家的方向和玩家推价的方向一致时按推价幅度被查——照常兑付,整笔兑付记成脏的
bool manip = m.Kind == PredictKind.Price && winners.Any(x => x.Yes ? push > 0 : push < 0) && rng.Chance(Math.Min(1, Market.ManipChance(push) * StoryMods.Current.Enforce(day)));
double taint = 0;
bool insider = false;
foreach (var pos in winners)
{
// 查实:本金和收益全部没收
evs.Add(new PredictEvent { Kind = "caught", Title = m.Title, Market = m, Amount = payout, Fraud = Math.Min(Tuning.PredictFraudCap, Math.Max(0, profit) * Tuning.PredictFraudShare) });
Record(day, m, m.Outcome, -cost, true);
}
else
{
foreach (var pos in winners) Pay(wallet(pos.Wallet), pos, pos.Shares);
evs.Add(new PredictEvent { Kind = "win", Title = m.Title, Market = m, Amount = payout });
Record(day, m, m.Outcome, profit, true);
var w = wallet(pos.Wallet);
double back = Pay(w, pos, pos.Shares);
// 内幕份额(下单时已经知道答案)对应的兑付记成脏的,每个钱包各算各的
if (manip) taint += MarkPayout(w, pos.Shares, back, pos.Shares);
else if (pos.Insider > 0) { insider = true; taint += MarkInsider(w, pos, pos.Shares, back); }
}
string kind = manip ? "manip" : insider ? "insider" : "win";
evs.Add(new PredictEvent { Kind = kind, Title = m.Title, Market = m, Amount = payout, Taint = taint });
Record(day, m, m.Outcome, profit, true);
}
else Record(day, m, m.Outcome, 0, false);
}
+22 -13
View File
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
@@ -34,14 +35,22 @@ namespace Decentralized.Domain
public double Rate, Reserve, Cap;
public bool Timelock, Audited, Scam, Closed;
public Dictionary<string, double> Deposits = new Dictionary<string, double>();
public Dictionary<string, double> Taint = new Dictionary<string, double>(), Basis = new Dictionary<string, double>();
/// <summary>每个钱包存入的 CRC 里脏的枚数、成本:取出时带回。</summary>
public Dictionary<string, double> Dirty = new Dictionary<string, double>(), Basis = new Dictionary<string, double>();
/// <summary>旧存档(数据版本 5 之前)的污点额(信用点),读档迁移时换成 <see cref="Dirty"/>。</summary>
[JsonProperty("Taint")] public Dictionary<string, double> LegacyTaint = new Dictionary<string, double>();
/// <summary>时间锁池子的取款队列:钱包 → (到账日, 数额)。</summary>
public List<YieldWithdraw> Pending = new List<YieldWithdraw>();
public double Total => Deposits.Values.Sum();
public double Of(string wallet) => Deposits.TryGetValue(wallet, out var v) ? v : 0;
}
public sealed class YieldWithdraw { public string Wallet; public int Day; public double Amount, Taint, Basis; }
public sealed class YieldWithdraw
{
public string Wallet; public int Day; public double Amount, Dirty, Basis;
/// <summary>旧存档(数据版本 5 之前)的污点额(信用点),读档迁移时换成 <see cref="Dirty"/>。</summary>
[JsonProperty("Taint")] public double LegacyTaint;
}
/// <summary>夜间发生的事。Kind:drain(钓鱼授权转走)/ rug(收益池跑路)/ dry(正规池储备耗尽,退本金)/ paid(取款到账)/ wickback(插针回去了)。</summary>
public sealed class ScamEvent
@@ -151,10 +160,10 @@ namespace Decentralized.Domain
{
if (p.Closed || amount <= 0 || w.Avail(Assets.CRC) + 1e-9 < amount || p.Total + amount > p.Cap + 1e-9) return false;
double total = w.Value(pr), share = total > 0 ? Math.Min(1, amount * pr(Assets.CRC) / total) : 0;
double taint = w.Taint * share, basis = w.Basis * share;
w.Add(Assets.CRC, -amount); w.Taint -= taint; w.Basis -= basis;
double dirty = amount * w.DirtyRatio(Assets.CRC), basis = w.Basis * share;
w.Add(Assets.CRC, -amount); w.Basis -= basis;
p.Deposits[w.Address] = p.Of(w.Address) + amount;
p.Taint[w.Address] = (p.Taint.TryGetValue(w.Address, out var t) ? t : 0) + taint;
p.Dirty[w.Address] = (p.Dirty.TryGetValue(w.Address, out var t) ? t : 0) + dirty;
p.Basis[w.Address] = (p.Basis.TryGetValue(w.Address, out var b) ? b : 0) + basis;
return true;
}
@@ -164,16 +173,16 @@ namespace Decentralized.Domain
{
double amt = p.Of(w.Address);
if (amt <= 0 || p.Closed) return 0;
double taint = p.Taint.TryGetValue(w.Address, out var t) ? t : 0, basis = p.Basis.TryGetValue(w.Address, out var b) ? b : 0;
p.Deposits.Remove(w.Address); p.Taint.Remove(w.Address); p.Basis.Remove(w.Address);
if (p.Timelock) { p.Pending.Add(new YieldWithdraw { Wallet = w.Address, Day = day + Tuning.YieldLockDays, Amount = amt, Taint = taint, Basis = basis }); return 0; }
Pay(w, amt, taint, basis);
double dirty = p.Dirty.TryGetValue(w.Address, out var t) ? t : 0, basis = p.Basis.TryGetValue(w.Address, out var b) ? b : 0;
p.Deposits.Remove(w.Address); p.Dirty.Remove(w.Address); p.Basis.Remove(w.Address);
if (p.Timelock) { p.Pending.Add(new YieldWithdraw { Wallet = w.Address, Day = day + Tuning.YieldLockDays, Amount = amt, Dirty = dirty, Basis = basis }); return 0; }
Pay(w, amt, dirty, basis);
return amt;
}
private static void Pay(Wallet w, double amt, double taint, double basis)
private static void Pay(Wallet w, double amt, double dirty, double basis)
{
w.Add(Assets.CRC, amt); w.Taint += taint; w.Basis += basis;
w.AddDirty(Assets.CRC, amt, dirty); w.Basis += basis;
}
// ——— 夜间 ———
@@ -209,7 +218,7 @@ namespace Decentralized.Domain
foreach (var kv in p.Deposits.ToList())
{
var w = own(kv.Key);
if (w != null) Pay(w, kv.Value, p.Taint.TryGetValue(kv.Key, out var t) ? t : 0, p.Basis.TryGetValue(kv.Key, out var b) ? b : 0);
if (w != null) Pay(w, kv.Value, p.Dirty.TryGetValue(kv.Key, out var t) ? t : 0, p.Basis.TryGetValue(kv.Key, out var b) ? b : 0);
evs.Add(new ScamEvent { Kind = "dry", Name = p.Name, Wallet = kv.Key, Asset = Assets.CRC, Amount = kv.Value });
}
p.Deposits.Clear(); p.Closed = true;
@@ -223,7 +232,7 @@ namespace Decentralized.Domain
{
p.Pending.Remove(q);
var w = own(q.Wallet);
if (w != null) Pay(w, q.Amount, q.Taint, q.Basis);
if (w != null) Pay(w, q.Amount, q.Dirty, q.Basis);
evs.Add(new ScamEvent { Kind = "paid", Name = p.Name, Wallet = q.Wallet, Asset = Assets.CRC, Amount = q.Amount });
}
}
+7 -2
View File
@@ -18,6 +18,8 @@ namespace Decentralized.Domain
public string Wallet;
public bool Mint;
public double Cra, Crc, Spark, C;
/// <summary>付出去的币的脏比例(按价值加权):到期换回的币按这个比例是脏的。</summary>
public double DirtyRatio;
}
public sealed class CraOrderEvent
@@ -114,8 +116,10 @@ namespace Decentralized.Domain
if (cra <= 0) return null;
Collateral(cra, out double crc, out double spark);
if (w.Avail(Assets.CRC) + 1e-9 < crc || w.Avail(Assets.SPARK) + 1e-9 < spark) return null;
double sv = spark * Spark, total = crc + sv;
double ratio = total > 0 ? (crc * w.DirtyRatio(Assets.CRC) + sv * w.DirtyRatio(Assets.SPARK)) / total : 0;
w.Add(Assets.CRC, -crc); w.Add(Assets.SPARK, -spark);
var o = new CraOrder { Id = ++OrderSeq, Wallet = w.Address, Mint = true, Cra = cra, Crc = crc, Spark = spark, C = C, Due = day + rng.Range(Tuning.CraMintDaysMin, Tuning.CraMintDaysMax) };
var o = new CraOrder { Id = ++OrderSeq, Wallet = w.Address, Mint = true, Cra = cra, Crc = crc, Spark = spark, C = C, DirtyRatio = ratio, Due = day + rng.Range(Tuning.CraMintDaysMin, Tuning.CraMintDaysMax) };
Orders.Add(o);
return o;
}
@@ -124,8 +128,9 @@ namespace Decentralized.Domain
public CraOrder RequestBurn(Wallet w, double cra, int day)
{
if (cra <= 0 || w.Avail(Assets.CRA) + 1e-9 < cra) return null;
double ratio = w.DirtyRatio(Assets.CRA);
w.Add(Assets.CRA, -cra);
var o = new CraOrder { Id = ++OrderSeq, Wallet = w.Address, Mint = false, Cra = cra, C = C, Due = day + Tuning.CraUnlockDays };
var o = new CraOrder { Id = ++OrderSeq, Wallet = w.Address, Mint = false, Cra = cra, C = C, DirtyRatio = ratio, Due = day + Tuning.CraUnlockDays };
Orders.Add(o);
return o;
}
+12 -9
View File
@@ -1,16 +1,19 @@
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace Decentralized.Domain
{
/// <summary>一个钱包在革命军质押池里的仓位:计息的本金、排队等解锁的部分,以及存入时带来的污点与成本。</summary>
/// <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 Dirty, Basis;
/// <summary>旧存档(数据版本 5 之前)的污点额(信用点),读档迁移时换成 <see cref="Dirty"/>。</summary>
[JsonProperty("Taint")] public double LegacyTaint;
public double Total => Amount + Unlocking.Sum(u => u.Value);
}
@@ -36,16 +39,16 @@ namespace Decentralized.Domain
/// <summary>全部计息中的质押量,补贴池按它算支出。</summary>
public double Earning => Lots.Sum(l => l.Amount);
/// <summary>质押:从钱包取出 CRA,污点与成本按价值比例随币留在仓位里。余额不足返回 false。</summary>
/// <summary>质押:从钱包取出 CRA,脏枚数按 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;
double dirty = amount * w.DirtyRatio(Assets.CRA), basis = w.Basis * share;
w.Add(Assets.CRA, -amount); w.Basis -= basis;
var lot = Of(w.Address) ?? AddLot(w.Address);
lot.Amount += amount; lot.Taint += taint; lot.Basis += basis;
lot.Amount += amount; lot.Dirty += dirty; lot.Basis += basis;
return true;
}
@@ -74,11 +77,11 @@ namespace Decentralized.Domain
{
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;
double dirty = lot.Dirty * share, basis = lot.Basis * share;
lot.Dirty -= dirty; lot.Basis -= basis;
lot.Unlocking.Remove(u);
var w = wallet(lot.Wallet);
w.Add(Assets.CRA, u.Value); w.Taint += taint; w.Basis += basis;
w.AddDirty(Assets.CRA, u.Value, dirty); w.Basis += basis;
ev.Released += u.Value;
}
if (lot.Total < 1e-9) Lots.Remove(lot);
+123 -5
View File
@@ -17,9 +17,9 @@ namespace Decentralized.Domain
public double CrcDepeg = 1, CrcHaircut = 1, CrtDepeg = 1, CrtFreeze = 1;
/// <summary>到这一天之前 CRA 螺旋概率减半。</summary>
public int CraSafeUntil = -1;
/// <summary>治安部整顿到这一天:出金审查命中率、混币器制裁概率、操纵与内幕查处概率上调。</summary>
/// <summary>治安部整顿到这一天:混币器清洗变差,制裁概率、操纵查处概率上调。</summary>
public int SecUntil = -1;
/// <summary>伦警官被捕后的立威期到这一天:出金审查、操纵、内幕查处概率上调。</summary>
/// <summary>伦警官被捕后的立威期到这一天:操纵查处概率、换币客户里便衣的概率上调。</summary>
public int LunUntil = -1;
/// <summary>SPARK 每晚额外乘的倍数,还剩几晚。</summary>
public double SparkPush = 1;
@@ -27,13 +27,20 @@ namespace Decentralized.Domain
/// <summary>混币器:手续费、匿名集(NPC 混币量)、跑路概率的倍率;被举报后永久关闭(在途的照常提回,不能再存)。</summary>
public double MixerFee = 1, MixerVolume = 1, MixerRug = 1;
public bool MixerClosed;
/// <summary>混币器换皮重开过(换了名字、地址);重开的是治安部的钓鱼混币器(提回的币一律被标记)。</summary>
public bool MixerRebranded, MixerHoneypot;
/// <summary>本 mod 自己的剧情线:混币器运营者 Tumbler。</summary>
public Tumbler Tumbler = new Tumbler();
/// <summary>Tumbler 四次上门之后的剧情:探员、泄露、审判、黑市跑路、报复、长期联系人、换皮重开。</summary>
public TumblerAfter After = new TumblerAfter();
public bool Has(string key) => Applied.Contains(key);
public double CraSpiral(int day) => day < CraSafeUntil ? Tuning.StoryCraSpiralMult : 1;
public double Aml(int day) => (day < SecUntil ? Tuning.StorySecAmlMult : 1) * (day < LunUntil ? Tuning.StoryLunMult : 1);
/// <summary>混币器每轮清洗率的倍率(治安部监控资金流向期间洗得更差)。</summary>
public double MixClean(int day) => day < SecUntil ? Tuning.StorySecMixClean : 1;
/// <summary>换币客户里便衣概率的倍率(伦警官被捕后的立威期)。</summary>
public double OtcSting(int day) => day < LunUntil ? Tuning.StoryLunMult : 1;
public double Sanction(int day) => day < SecUntil ? Tuning.StorySecSanctionMult : 1;
/// <summary>操纵检出、内幕查处的概率倍率。</summary>
public double Enforce(int day) => (day < SecUntil ? Tuning.StorySecEnforceMult : 1) * (day < LunUntil ? Tuning.StoryLunMult : 1);
@@ -85,10 +92,23 @@ namespace Decentralized.Domain
case "tumbler_sold":
MixerFee *= Tuning.TumblerSoldFee; MixerRug *= Tuning.TumblerSoldRug;
break;
case "tumbler_bm_rug":
MixerClosed = true;
break;
}
return true;
}
/// <summary>混币器换皮重开:重新开放,费率、匿名集、跑路倍率回到 1;钓鱼混币器的费率打折(引人上钩)。</summary>
public void Rebrand(bool honeypot)
{
MixerClosed = false;
MixerRebranded = true;
MixerHoneypot = honeypot;
MixerFee = honeypot ? Tuning.TumblerHoneypotFee : 1;
MixerVolume = 1; MixerRug = 1;
}
/// <summary>SPARK 在接下来几晚里一共涨到 total 倍(每晚乘相同的倍数)。</summary>
private void PushSpark(double total)
{
@@ -110,6 +130,8 @@ namespace Decentralized.Domain
public const int Hide = 1, Report = 2, Sell = 3;
public int StartDay = -1, Stage, Choice, VisitedDay = -1;
public bool Done;
/// <summary>Tumbler 的外观(第一次上门时随机定下,之后每次都一样)。</summary>
public string Sprite;
/// <summary>第几次上门在哪一天(相对开始日)。</summary>
public static readonly int[] Offsets = { 0, 3, 5, 9 };
@@ -124,11 +146,16 @@ namespace Decentralized.Domain
return true;
}
/// <summary>今天该上门的是第几次(1–4),没有就返回 0(第 4 次举报过的话由接入层改成新闻:他被抓了)。</summary>
public int Due(int day)
/// <summary>
/// 今天该上门的是第几次(1–4),没有就返回 0(第 4 次举报过的话由接入层改成新闻:他被抓了)。
/// after 不为空时:第 3 次要等治安部探员来过(第二天起),配合过探员又选了藏的,第 4 次要等搜查过(第二天起)。
/// </summary>
public int Due(int day, TumblerAfter after = null)
{
if (Done || StartDay < 0 || Stage >= Offsets.Length || VisitedDay == day) return 0;
if (day < NextVisitDay) return 0;
if (after != null && Stage == 2 && (after.Agent == 0 || day <= after.AgentDay)) return 0;
if (after != null && Stage == 3 && after.NeedSearch(this) && (!after.Searched || day <= after.SearchDay)) return 0;
return Stage + 1;
}
@@ -140,4 +167,95 @@ namespace Decentralized.Domain
Done = Stage >= Offsets.Length;
}
}
/// <summary>
/// Tumbler 四次上门之后的剧情(原来的进度类不动):
/// 治安部探员在第 2、3 次上门之间来一次(配合 / 否认);配合过又选了藏的,第 4 次前一天来搜查;
/// 举报、被搜走、第 4 次盘不在手上 → 密钥落到治安部,混币记录泄露;举报 → 审判盘;卖给黑市 → 黑市跑路;
/// 无罪获释、黑市跑路 → Tumbler 报复;藏好归还 → 长期联系人;混币器关停若干天后换皮重开。只记进度,对话、物品、夜间效果在接入层。
/// </summary>
public sealed class TumblerAfter
{
public const int Cooperate = 1, Deny = 2;
/// <summary>探员上门的结果(0 = 还没来)和那一天。</summary>
public int Agent, AgentDay = -1;
/// <summary>探员的外观(问话和搜查是同一个人)。</summary>
public string AgentSprite;
/// <summary>搜查过(不管有没有搜到)、搜走了密钥盘、哪一天。</summary>
public bool Searched, Seized;
public int SearchDay = -1;
/// <summary>哪一晚泄露(-1 = 没安排);已经泄露过(整局一次)。</summary>
public int LeakDay = -1;
public bool Leaked;
/// <summary>藏好并归还:成为长期联系人;已经提示过的镇压开始日。</summary>
public bool Contact;
public List<int> Tipped = new List<int>();
/// <summary>审判:开盘日、宣判日、判决(1 定罪,0 无罪,-1 没有审判);每晚的庭审新闻偏向(true = 控方占上风);最后一条新闻是哪晚。</summary>
public int TrialOpenDay = -1, TrialDay = -1, Verdict = -1, HintDay = -1;
public List<bool> Hints = new List<bool>();
public bool TrialMarket, TrialDone;
/// <summary>报复在哪一晚(-1 = 没有)、已经报复过。</summary>
public int RevengeDay = -1;
public bool Revenged;
/// <summary>黑市卷款跑路在哪一晚(-1 = 没有)、已经跑路。</summary>
public int BmRugDay = -1;
public bool BmRugged;
/// <summary>混币器关停的那天(-1 = 没关停);换皮重开过。</summary>
public int ClosedDay = -1;
public bool Rebranded;
/// <summary>探员今天该不该来:第 2 次上门之后的第二天起,还没来过。</summary>
public bool AgentDue(Tumbler t, int day) => !t.Done && t.Stage == 2 && Agent == 0 && day > t.VisitedDay;
/// <summary>配合过探员、又选了藏,第 4 次上门前要先来搜查。</summary>
public bool NeedSearch(Tumbler t) => Agent == Cooperate && t.Choice == Tumbler.Hide;
/// <summary>搜查今天该不该来:第 4 次上门的前一天起(第 3 次之后的第二天起)。</summary>
public bool SearchDue(Tumbler t, int day) => !t.Done && t.Stage == 3 && NeedSearch(t) && !Searched && day >= t.NextVisitDay - 1 && day > t.VisitedDay;
/// <summary>安排今晚泄露(整局一次)。</summary>
public void LeakTonight(int day)
{
if (!Leaked && LeakDay < 0) LeakDay = day;
}
/// <summary>举报:掷出判决,安排开盘和宣判;混币器从今天起关停。</summary>
public void StartTrial(int day, Rng rng)
{
if (Verdict >= 0) return;
Verdict = rng.Chance(Tuning.TumblerTrialConvict) ? 1 : 0;
TrialOpenDay = day + Tuning.TumblerTrialOpen;
TrialDay = day + Tuning.TumblerTrialDays;
ClosedDay = day;
}
/// <summary>今晚的庭审新闻(开盘前一晚起到宣判前一晚,每晚一条):偏向真实判决的概率 HintAcc。没有就返回 null。</summary>
public bool? Hint(int day, Rng rng)
{
if (Verdict < 0 || TrialDone || day < TrialOpenDay - 1 || day >= TrialDay || HintDay >= day) return null;
HintDay = day;
bool truth = Verdict == 1;
bool h = rng.Chance(Tuning.TumblerTrialHintAcc) ? truth : !truth;
Hints.Add(h);
return h;
}
/// <summary>公开信息下的定罪概率:先验按已经登出的庭审新闻做贝叶斯更新。</summary>
public double TrialPublic()
{
double acc = Tuning.TumblerTrialHintAcc, lr = Math.Log(acc / (1 - acc)), p0 = Tuning.TumblerTrialConvict;
double l = Math.Log(p0 / (1 - p0));
foreach (var h in Hints) l += h ? lr : -lr;
return 1 / (1 + Math.Exp(-l));
}
/// <summary>卖给黑市:安排黑市卷款跑路的那一晚。</summary>
public void StartSold(int day, Rng rng)
{
if (BmRugDay < 0) BmRugDay = day + rng.Range(Tuning.TumblerBmRugMin, Tuning.TumblerBmRugMax);
}
/// <summary>今晚该不该换皮重开:关停满 RebrandDays 天,只一次。</summary>
public bool RebrandDue(int day) => !Rebranded && ClosedDay >= 0 && day >= ClosedDay + Tuning.TumblerRebrandDays;
}
}
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Decentralized.Domain
{
/// <summary>
/// 存档迁移(数据版本 5):旧存档的污点是一笔信用点(整个钱包、托管里各一笔),换成按币种记的脏枚数。
/// 钱包:按当前价格摊到各种可追踪的币上(<see cref="Wallet.MigrateTaint"/>);Poinbase 账户和永续仓位的污点先挪到 1 号钱包一起摊(超出的作废)。
/// 托管(混币器在途、黑市订单、质押、空投池、预测持仓、自营混币器本金):按当前价格换成那种币的脏枚数,不超过数量。旧字段清零。
/// </summary>
public static class TaintMigration
{
private static double Qty(Func<string, double> price, string asset, double amount, double legacy)
{
double p = price(asset);
return legacy > 0 && p > 0 ? Math.Min(Math.Max(0, amount), legacy / p) : 0;
}
/// <summary>迁移全部,返回旧存档里的污点总额(信用点,日志用)。</summary>
public static double Run(IList<Wallet> wallets, Wallet poinbase, IEnumerable<PerpPosition> perps, Mixer mixer, BlackMarket black,
Staking staking, Scams scams, PredictBook predict, OwnMixer own, Func<string, double> price)
{
double total = wallets.Sum(w => w.LegacyTaint) + poinbase.LegacyTaint + perps.Sum(p => p.LegacyTaint);
double extra = Exchange.TakeLegacyTaint(poinbase, perps);
for (int i = 0; i < wallets.Count; i++) wallets[i].MigrateTaint(price, i == 0 ? extra : 0);
foreach (var d in mixer.Pending) { total += d.LegacyTaint; d.Dirty += Qty(price, d.Asset, d.Amount, d.LegacyTaint); d.LegacyTaint = 0; }
foreach (var o in black.Orders) { total += o.LegacyTaint; o.Dirty += Qty(price, o.Asset, o.Qty, o.LegacyTaint); o.LegacyTaint = 0; }
foreach (var l in staking.Lots) { total += l.LegacyTaint; l.Dirty += Qty(price, Assets.CRA, l.Total, l.LegacyTaint); l.LegacyTaint = 0; }
foreach (var p in scams.Pools)
{
foreach (var kv in p.LegacyTaint)
{
total += kv.Value;
p.Dirty[kv.Key] = (p.Dirty.TryGetValue(kv.Key, out var d) ? d : 0) + Qty(price, Assets.CRC, p.Of(kv.Key), kv.Value);
}
p.LegacyTaint.Clear();
foreach (var q in p.Pending) { total += q.LegacyTaint; q.Dirty += Qty(price, Assets.CRC, q.Amount, q.LegacyTaint); q.LegacyTaint = 0; }
}
foreach (var p in predict.Positions) { total += p.LegacyTaint; p.Dirty += Qty(price, Assets.CRC, p.Cost * (1 + Tuning.PredictFee), p.LegacyTaint); p.LegacyTaint = 0; }
foreach (var kv in own.Pools) { total += kv.Value.LegacyCapTaint; kv.Value.CapDirty += Qty(price, kv.Key, kv.Value.Capital, kv.Value.LegacyCapTaint); kv.Value.LegacyCapTaint = 0; }
return total;
}
}
}
+58 -21
View File
@@ -10,7 +10,9 @@ namespace Decentralized.Domain
// 挖矿
public const double PscStartPrice = 100;
public const double BlockReward = 100;
/// <summary>第十一阶段把出块奖励和 NPC 初始算力同乘的倍数(100 → 1000、360 → 3600);读旧存档时按它放大全网。</summary>
public const double MiningScale = 10;
public const double BlockReward = 1000;
public const int HalvingDays = 90;
/// <summary>减半对币价的传导:每次减半在区间内随机一个指数,价格锚上移 2^指数 倍(0.15–0.85 即约 +11% 到 +80%)。
/// 参考比特币:每次减半后的涨幅差别很大,市场也已部分预期到,所以既不固定也不翻倍。</summary>
@@ -20,15 +22,15 @@ namespace Decentralized.Domain
public const double HalvingRamp = 0.08;
/// <summary>价格锚向目标的逐晚靠拢比例(约 7 天完成大半)。</summary>
public const double SupplyAdjust = 0.15;
public const double NpcHash0 = 360;
public const double NpcHash0 = 3600;
public const double NpcKjPerH = 2.4;
public const double ElecPrice = 1.5;
public const double NpcTargetRatio = 3.5;
public const double NpcAdj = 0.05;
public const double PoolFee = 0.03;
public const int RigPrice = 400, RigKj = 2;
/// <summary>矿架上模块百分比的折算系数(原版机器用 2–4),以及各项上限。</summary>
public const double ModuleRatio = 4, ModuleHashCap = 0.20, ModuleKjCap = 0.25, ModuleWearCap = 0.30;
/// <summary>矿架上模块百分比的折算系数(原版机器用 2–4)。模块效果不设上限,只有防出错的下限:算力、耗电、每日损耗三个乘数各自不低于下面的值。</summary>
public const double ModuleRatio = 4, ModuleHashFloor = 0, ModuleKjFloor = 0.2, ModuleWearFloor = 0;
public const double DeathBase = 0.001, DeathWorn = 0.03, FakeDeathFactor = 2;
public const double RefurbishHealth = 30;
@@ -81,12 +83,7 @@ namespace Decentralized.Domain
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;
/// <summary>出金审查命中概率:污点占比达标后每占一分加 AmlTaintMult 倍;超出单笔 / 累计额度的部分按 AmlExcessWeight 加。</summary>
public const double AmlTaintMult = 1.5, AmlExcessWeight = 0.2;
public const int AmlWindowDays = 7;
public const double AmlTaintRatio = 0.10, AmlHeavyRatio = 0.30, AmlMixerBonus = 0.30, AmlMaxChance = 0.9;
/// <summary>出金到银行:超出成本的部分征税。</summary>
public const double TaxRate = 0.08;
/// <summary>混币器:每轮手续费、基准清洗率、NPC 背景量(信用点)、最大轮数、最小存入、制裁概率与天数、每晚跑路概率。</summary>
public const double MixerFee = 0.03, MixerCleanRate = 0.35, MixerNpcVolume = 20000, MixerMin = 100, MixerSanctionChance = 0.5, MixerRugChance = 0.004;
@@ -244,14 +241,14 @@ namespace Decentralized.Domain
public const double EraKjMult = 0.5;
/// <summary>预测市场:游戏第几天起开放;手续费;单盘额度 = 基础 + 每天增量 × 天数;所有钱包在单盘合计的份额上限(额度的倍数);
/// 同时开放的盘口数量范围、每天最多新开几个;内幕持仓达到上限份额的多少比例后结算被查的概率,查实后收益记罪证的比例与上限;
/// 同时开放的盘口数量范围、每天最多新开几个;按信用点额折诈骗罪证的比例与单次上限(永续被判操纵、Tumbler 泄露用);
/// 派系行动的日均开始概率(先验);先验概率的夹紧范围;结算记录保留条数。</summary>
public const int PredictUnlockDay = 8;
public const double PredictFee = 0.01, PredictLoss0 = 800, PredictLossPerDay = 25, PredictCap = 2;
/// <summary>预测盘的流动性:LMSR 的 b = 倍数 × 单盘额度 / ln 2,与先验无关(低先验盘不再更浅)。单盘额度只决定份额上限和消息价。</summary>
public const double PredictDepth = 1.8;
public const int PredictOpenMin = 3, PredictOpenMax = 6, PredictNewPerDay = 2;
public const double PredictInsiderShare = 0.4, PredictInsiderCatch = 0.7, PredictFraudShare = 0.5, PredictFraudCap = 400;
public const double PredictFraudShare = 0.5, PredictFraudCap = 400;
public const double PredictFactionDaily = 0.06, PredictPMin = 0.03, PredictPMax = 0.97;
public const int PredictHistory = 8;
/// <summary>gas 盘的公开价里"不知道相位"的权重:越大,数清七天周期的玩家越赚。金融审查(agy_fin/subsim)发现 0.5 时 gas 盘胜率 96%、每周净赚约 1,400,调到 0.15。</summary>
@@ -265,9 +262,8 @@ namespace Decentralized.Domain
public static readonly double[] IntelTipPrice = { 0.02, 0.05, 0.12 }, IntelTipAcc = { 0.60, 0.80, 0.95 };
public const int IntelRep = 20;
/// <summary>操纵检出:玩家自己的大单把价格推离"没有你时的水平"的对数幅度(取绝对值)低于 ManipFree 不查;
/// 超过后被查概率 = ManipBase + ManipSlope × (幅度 − ManipFree),封顶 ManipMax;被查后永续账户冻结几天。</summary>
/// 超过后被查概率 = ManipBase + ManipSlope × (幅度 − ManipFree),封顶 ManipMax;被查后照常兑付,赚到的钱记污点。</summary>
public const double ManipFree = 0.16, ManipBase = 0.25, ManipSlope = 3, ManipMax = 0.95;
public const int ManipFreezeDays = 3;
/// <summary>高息理财(庞氏):第几天起开放、开张的宣传费(CRC)、可选日息、每晚新存入的基数(随天数)和对日息的敏感指数、
/// 池子上限(随天数)、每晚被取走的比例、每晚被举报概率(基础 + 日息系数 + 池子占上限的比例系数)、卷走或崩盘时记的罪证。</summary>
@@ -290,10 +286,35 @@ namespace Decentralized.Domain
public const double YieldPoolChance = 0.25, YieldRugNightly = 0.18, YieldCap0 = 4000, YieldCapPerDay = 120, YieldReserveDays = 25;
public static readonly double[] YieldRates = { 0.01, 0.02, 0.03 }, YieldScamByRate = { 0.15, 0.40, 0.75 };
public const double YieldScamTimelock = 0.25, YieldScamAudit = 0.20;
/// <summary>收黑币:相关事件期间每天有人上门的概率(PSC、CRT),Ponero 兑换处开放后每天的概率;币的市值(房租的倍数);
/// 现金要价占市值的比例(PSC、CRT 与 Ponero 分开);钓鱼执法的概率与镇压期间的加成。</summary>
public const double HotEventChance = 0.35, HotPoneroChance = 0.05, HotValueMinRents = 4, HotValueMaxRents = 15;
public const double HotCutMin = 0.45, HotCutMax = 0.60, HotPoneroCutMin = 0.70, HotPoneroCutMax = 0.80, HotSting = 0.05, HotStingCrackdown = 0.10;
/// <summary>换币客户(场外换币台):人脉升级价格;来卖币的比例(其余来买币)。</summary>
public const int OtcUnlockPrice = 500;
public const double OtcSellShare = 0.5;
/// <summary>卖币客户:有相关事件时出手事件里来的币(全额污点)的概率,Ponero 兑换处开放后出手 Ponero 的概率;币的市值(房租的倍数);
/// 其他币的污点比例范围;现金价占市值的比例(污点比例 0 时、1 时,之间线性;Ponero 单独一档);便衣的概率与镇压期间的加成;客户地址来历的最多笔数。</summary>
public const double OtcEventChance = 0.7, OtcPoneroChance = 0.15, OtcValueMinRents = 4, OtcValueMaxRents = 15;
public const double OtcTaintMin = 0.2, OtcTaintMax = 0.9, OtcCutClean = 0.80, OtcCutDirty = 0.45, OtcPoneroCutMin = 0.70, OtcPoneroCutMax = 0.80;
public const double OtcSting = 0.05, OtcStingCrackdown = 0.10;
public const int OtcHistoryMax = 3;
/// <summary>买币客户:要买的币的市值(房租的倍数);出价相对市价的随机溢价范围(可能低于市价,成不成交由玩家决定)。</summary>
public const double OtcBuyMinRents = 2, OtcBuyMaxRents = 8;
public const double OtcBidMin = -0.05, OtcBidMax = 0.30;
/// <summary>
/// 自营混币器(买下场外换币台就开放):默认费率、可选费率;口碑(近期出款剩下的污点比例)跟出款走的速度、客户把剩下的污点比例折成费率的权重、Tumbler 关停时客户不混的额外成本、匿名集的半饱和值(每轮清洗率 = 基准 × 匿名集 /(匿名集 + 它))、平均轮数、客户在途的币计入匿名集的比例;
/// 基础需求(信用点,随天数增长)、黑市声望 0 时的需求系数、客流对成本差的灵敏度、每晚需求的随机幅度、单笔存入的平均额;
/// 客户存入的币的污点比例范围;各币种的需求权重(PSC、CRC、CRT、CRA);营业时每晚有便衣存入的概率和记罪证的比例(按一笔存入的额);
/// 风声每晚保留的比例和累积的标度、池子越大越难追查的半值(风声累积 = 存入 / 标度 / (1 + 匿名集 / 它))、按风声的查封概率系数与上限、镇压期间的倍数、查封记罪证的比例、停业天数、风声提示的两档界线;卷款跑路记的诈骗罪证和黑市声望损失。
/// </summary>
public const double OwnMixFeeDefault = 0.03;
public static readonly double[] OwnMixFees = { 0.01, 0.02, 0.03, 0.05, 0.08, 0.12 };
public const double OwnMixRatingSpeed = 0.2, OwnMixTaintWeight = 0.1, OwnMixRivalGone = 0.03, OwnMixAnon0 = 20000, OwnMixAvgRounds = 3, OwnMixPendingWeight = 0.25;
public const double OwnMixDemand0 = 40000, OwnMixDemandPerDay = 1400, OwnMixRepMin = 0.3, OwnMixFeeScale = 0.015, OwnMixNoise = 0.3, OwnMixTicket = 800;
public const double OwnMixInTaintMin = 0.4, OwnMixInTaintMax = 1.0;
public static readonly double[] OwnMixAssetWeights = { 0.3, 0.1, 0.4, 0.2 };
public const double OwnMixSting = 0.03, OwnMixStingCrime = 0.1;
public const double OwnMixHeatKeep = 0.9, OwnMixHeatScale = 100000, OwnMixHeatAnon0 = 160000, OwnMixSeizeRate = 0.002, OwnMixSeizeMax = 0.5, OwnMixSeizeCrackdown = 2, OwnMixSeizeCrime = 0.01, OwnMixHeatMid = 0.01, OwnMixHeatHigh = 0.03;
public const int OwnMixSeizeDays = 7, OwnMixRugRep = 25;
public const double OwnMixRugCrime = 400;
/// <summary>假客服:每天上门的概率;伪造转账:用币付款的顾客里是骗子的比例;两者都从第几天起。</summary>
public const double FakeSupportChance = 0.04, FakeTransferShare = 0.08;
public const int ScamMinDay = 10;
@@ -302,12 +323,12 @@ namespace Decentralized.Domain
public const double BmAutoSwapSlip = 0.08;
/// <summary>剧情联动:塞尔皮科证据交给革命军(CRA 补贴池注资、螺旋概率倍率与期限、SPARK 几晚内涨到的倍数)、交给黑市(CRT 脱锚与被出卖的倍率)、
/// 交给治安部(期限内出金审查命中、混币器制裁、操纵与内幕查处的倍率);埃莉诺泄露数据盘(CRC 托管减记倍率、SPARK 涨幅)、交回(CRC 脱锚倍率);
/// 伦警官被捕(期限与查处倍率);SPARK 推涨分几晚走完。</summary>
/// 交给治安部(期限内混币器每轮清洗率、制裁、操纵查处的倍率);埃莉诺泄露数据盘(CRC 托管减记倍率、SPARK 涨幅)、交回(CRC 脱锚倍率);
/// 伦警官被捕(期限与倍率:操纵查处、换币客户里的便衣);SPARK 推涨分几晚走完。</summary>
public const double StoryRevSubsidy = 10000, StoryCraSpiralMult = 0.5, StorySerpicoSpark = 1.2;
public const double StoryBmCrtMult = 0.5;
public const int StorySecDays = 30;
public const double StorySecAmlMult = 1.5, StorySecSanctionMult = 1.5, StorySecEnforceMult = 1.3;
public const double StorySecMixClean = 0.7, StorySecSanctionMult = 1.5, StorySecEnforceMult = 1.3;
public const double StoryLeakHaircutMult = 2, StoryLeakSpark = 1.15, StoryReturnCrcMult = 0.5;
public const int StoryLunDays = 14, StorySparkNights = 5;
/// <summary>混币器运营者 Tumbler:第几天起开始;帮他藏好密钥盘后的混币器手续费、匿名集倍率和谢礼(CRT);举报的赏金(信用点)和治安部声望;
@@ -317,6 +338,22 @@ namespace Decentralized.Domain
public const int TumblerBounty = 1200, TumblerSecRep = 8, TumblerBmRep = 5;
public const double TumblerSellCrt = 3000, TumblerSoldFee = 1.5, TumblerSoldRug = 4;
public const int TumblerDriveValue = 600;
/// <summary>卖给黑市得的 CRT 里带污点的比例。</summary>
public const double TumblerSellTaint = 0.5;
/// <summary>Tumbler 的后续剧情:混币器出款记录保留几天、最多几条(泄露时这些出款洗掉的污点加回原钱包);
/// 治安部探员:配合时的治安部声望,之后搜走密钥盘记的违禁品罪证。</summary>
public const int TumblerLeakDays = 30, TumblerReturnsMax = 300, TumblerAgentSecRep = 5, TumblerSearchCrime = 300;
/// <summary>审判盘:举报后第几天开盘、第几天宣判;定罪的概率(举报时掷出);每晚庭审新闻偏向真实判决的概率。</summary>
public const int TumblerTrialOpen = 2, TumblerTrialDays = 7;
public const double TumblerTrialConvict = 0.7, TumblerTrialHintAcc = 0.7;
/// <summary>报复:获释或黑市跑路后几天动手;给当前钱包植入木马(否则地址投毒)的概率;地址投毒每个钱包最多几笔。</summary>
public const int TumblerRevengeDelay = 3, TumblerRevengePoison = 3;
public const double TumblerRevengeWalletShare = 0.5;
/// <summary>卖给黑市后,黑市卷款跑路在第几天(随机,含两端)。</summary>
public const int TumblerBmRugMin = 10, TumblerBmRugMax = 20;
/// <summary>换皮重开:混币器关停后几天;是治安部钓鱼混币器的概率;钓鱼混币器的费率倍率。</summary>
public const int TumblerRebrandDays = 15;
public const double TumblerHoneypot = 0.25, TumblerHoneypotFee = 0.5;
public const double StoryLunMult = 1.3;
/// <summary>行情图的 NPC 成交量:每天的基础量占主池深度的比例(按币种),价格波动越大成交越多(|涨跌幅| × 倍数),再乘 0.6–1.4 的随机。</summary>
+72 -26
View File
@@ -16,15 +16,6 @@ namespace Decentralized.Domain
["AirdropRewardMax"] = Tuning.AirdropRewardMax,
["AirdropRewardMin"] = Tuning.AirdropRewardMin,
["AirdropScam"] = Tuning.AirdropScam,
["AmlBigRents"] = Tuning.AmlBigRents,
["AmlCumRents"] = Tuning.AmlCumRents,
["AmlExcessWeight"] = Tuning.AmlExcessWeight,
["AmlHeavyRatio"] = Tuning.AmlHeavyRatio,
["AmlMaxChance"] = Tuning.AmlMaxChance,
["AmlMixerBonus"] = Tuning.AmlMixerBonus,
["AmlTaintMult"] = Tuning.AmlTaintMult,
["AmlTaintRatio"] = Tuning.AmlTaintRatio,
["AmlWindowDays"] = Tuning.AmlWindowDays,
["AsicChance"] = Tuning.AsicChance,
["AsicDeadValue"] = Tuning.AsicDeadValue,
["AsicNewShare"] = Tuning.AsicNewShare,
@@ -249,16 +240,6 @@ namespace Decentralized.Domain
["HalvingPassMin"] = Tuning.HalvingPassMin,
["HalvingRamp"] = Tuning.HalvingRamp,
["HalvingRampDays"] = Tuning.HalvingRampDays,
["HotCutMax"] = Tuning.HotCutMax,
["HotCutMin"] = Tuning.HotCutMin,
["HotEventChance"] = Tuning.HotEventChance,
["HotPoneroChance"] = Tuning.HotPoneroChance,
["HotPoneroCutMax"] = Tuning.HotPoneroCutMax,
["HotPoneroCutMin"] = Tuning.HotPoneroCutMin,
["HotSting"] = Tuning.HotSting,
["HotStingCrackdown"] = Tuning.HotStingCrackdown,
["HotValueMaxRents"] = Tuning.HotValueMaxRents,
["HotValueMinRents"] = Tuning.HotValueMinRents,
["IntelBriefPrice"] = Tuning.IntelBriefPrice,
["IntelFake"] = Tuning.IntelFake,
["IntelRep"] = Tuning.IntelRep,
@@ -266,9 +247,9 @@ namespace Decentralized.Domain
["LedgerShow"] = Tuning.LedgerShow,
["ManipBase"] = Tuning.ManipBase,
["ManipFree"] = Tuning.ManipFree,
["ManipFreezeDays"] = Tuning.ManipFreezeDays,
["ManipMax"] = Tuning.ManipMax,
["ManipSlope"] = Tuning.ManipSlope,
["MiningScale"] = Tuning.MiningScale,
["MixerCleanRate"] = Tuning.MixerCleanRate,
["MixerFee"] = Tuning.MixerFee,
["MixerMaxRounds"] = Tuning.MixerMaxRounds,
@@ -278,14 +259,62 @@ namespace Decentralized.Domain
["MixerSanctionChance"] = Tuning.MixerSanctionChance,
["MixerSanctionDays"] = Tuning.MixerSanctionDays,
["MixerUnlockPrice"] = Tuning.MixerUnlockPrice,
["ModuleHashCap"] = Tuning.ModuleHashCap,
["ModuleKjCap"] = Tuning.ModuleKjCap,
["ModuleHashFloor"] = Tuning.ModuleHashFloor,
["ModuleKjFloor"] = Tuning.ModuleKjFloor,
["ModuleRatio"] = Tuning.ModuleRatio,
["ModuleWearCap"] = Tuning.ModuleWearCap,
["ModuleWearFloor"] = Tuning.ModuleWearFloor,
["NpcAdj"] = Tuning.NpcAdj,
["NpcHash0"] = Tuning.NpcHash0,
["NpcKjPerH"] = Tuning.NpcKjPerH,
["NpcTargetRatio"] = Tuning.NpcTargetRatio,
["OtcBidMax"] = Tuning.OtcBidMax,
["OtcBidMin"] = Tuning.OtcBidMin,
["OtcBuyMaxRents"] = Tuning.OtcBuyMaxRents,
["OtcBuyMinRents"] = Tuning.OtcBuyMinRents,
["OtcCutClean"] = Tuning.OtcCutClean,
["OtcCutDirty"] = Tuning.OtcCutDirty,
["OtcEventChance"] = Tuning.OtcEventChance,
["OtcHistoryMax"] = Tuning.OtcHistoryMax,
["OtcPoneroChance"] = Tuning.OtcPoneroChance,
["OtcPoneroCutMax"] = Tuning.OtcPoneroCutMax,
["OtcPoneroCutMin"] = Tuning.OtcPoneroCutMin,
["OtcSellShare"] = Tuning.OtcSellShare,
["OtcSting"] = Tuning.OtcSting,
["OtcStingCrackdown"] = Tuning.OtcStingCrackdown,
["OtcTaintMax"] = Tuning.OtcTaintMax,
["OtcTaintMin"] = Tuning.OtcTaintMin,
["OtcUnlockPrice"] = Tuning.OtcUnlockPrice,
["OtcValueMaxRents"] = Tuning.OtcValueMaxRents,
["OtcValueMinRents"] = Tuning.OtcValueMinRents,
["OwnMixAnon0"] = Tuning.OwnMixAnon0,
["OwnMixAvgRounds"] = Tuning.OwnMixAvgRounds,
["OwnMixDemand0"] = Tuning.OwnMixDemand0,
["OwnMixDemandPerDay"] = Tuning.OwnMixDemandPerDay,
["OwnMixFeeDefault"] = Tuning.OwnMixFeeDefault,
["OwnMixFeeScale"] = Tuning.OwnMixFeeScale,
["OwnMixHeatAnon0"] = Tuning.OwnMixHeatAnon0,
["OwnMixHeatHigh"] = Tuning.OwnMixHeatHigh,
["OwnMixHeatKeep"] = Tuning.OwnMixHeatKeep,
["OwnMixHeatMid"] = Tuning.OwnMixHeatMid,
["OwnMixHeatScale"] = Tuning.OwnMixHeatScale,
["OwnMixInTaintMax"] = Tuning.OwnMixInTaintMax,
["OwnMixInTaintMin"] = Tuning.OwnMixInTaintMin,
["OwnMixNoise"] = Tuning.OwnMixNoise,
["OwnMixPendingWeight"] = Tuning.OwnMixPendingWeight,
["OwnMixRatingSpeed"] = Tuning.OwnMixRatingSpeed,
["OwnMixRepMin"] = Tuning.OwnMixRepMin,
["OwnMixRivalGone"] = Tuning.OwnMixRivalGone,
["OwnMixRugCrime"] = Tuning.OwnMixRugCrime,
["OwnMixRugRep"] = Tuning.OwnMixRugRep,
["OwnMixSeizeCrackdown"] = Tuning.OwnMixSeizeCrackdown,
["OwnMixSeizeCrime"] = Tuning.OwnMixSeizeCrime,
["OwnMixSeizeDays"] = Tuning.OwnMixSeizeDays,
["OwnMixSeizeMax"] = Tuning.OwnMixSeizeMax,
["OwnMixSeizeRate"] = Tuning.OwnMixSeizeRate,
["OwnMixSting"] = Tuning.OwnMixSting,
["OwnMixStingCrime"] = Tuning.OwnMixStingCrime,
["OwnMixTaintWeight"] = Tuning.OwnMixTaintWeight,
["OwnMixTicket"] = Tuning.OwnMixTicket,
["PerpAdlHaircut"] = Tuning.PerpAdlHaircut,
["PerpBanDays"] = Tuning.PerpBanDays,
["PerpDebtDays"] = Tuning.PerpDebtDays,
@@ -342,8 +371,6 @@ namespace Decentralized.Domain
["PredictFraudShare"] = Tuning.PredictFraudShare,
["PredictGasBlind"] = Tuning.PredictGasBlind,
["PredictHistory"] = Tuning.PredictHistory,
["PredictInsiderCatch"] = Tuning.PredictInsiderCatch,
["PredictInsiderShare"] = Tuning.PredictInsiderShare,
["PredictLoss0"] = Tuning.PredictLoss0,
["PredictLossPerDay"] = Tuning.PredictLossPerDay,
["PredictMoveLog"] = Tuning.PredictMoveLog,
@@ -392,25 +419,42 @@ namespace Decentralized.Domain
["StoryLunMult"] = Tuning.StoryLunMult,
["StoryReturnCrcMult"] = Tuning.StoryReturnCrcMult,
["StoryRevSubsidy"] = Tuning.StoryRevSubsidy,
["StorySecAmlMult"] = Tuning.StorySecAmlMult,
["StorySecDays"] = Tuning.StorySecDays,
["StorySecEnforceMult"] = Tuning.StorySecEnforceMult,
["StorySecMixClean"] = Tuning.StorySecMixClean,
["StorySecSanctionMult"] = Tuning.StorySecSanctionMult,
["StorySerpicoSpark"] = Tuning.StorySerpicoSpark,
["StorySparkNights"] = Tuning.StorySparkNights,
["SupplyAdjust"] = Tuning.SupplyAdjust,
["TaxRate"] = Tuning.TaxRate,
["TumblerAgentSecRep"] = Tuning.TumblerAgentSecRep,
["TumblerBmRep"] = Tuning.TumblerBmRep,
["TumblerBmRugMax"] = Tuning.TumblerBmRugMax,
["TumblerBmRugMin"] = Tuning.TumblerBmRugMin,
["TumblerBounty"] = Tuning.TumblerBounty,
["TumblerDriveValue"] = Tuning.TumblerDriveValue,
["TumblerGift"] = Tuning.TumblerGift,
["TumblerHoneypot"] = Tuning.TumblerHoneypot,
["TumblerHoneypotFee"] = Tuning.TumblerHoneypotFee,
["TumblerKeptFee"] = Tuning.TumblerKeptFee,
["TumblerKeptVolume"] = Tuning.TumblerKeptVolume,
["TumblerLeakDays"] = Tuning.TumblerLeakDays,
["TumblerMinDay"] = Tuning.TumblerMinDay,
["TumblerRebrandDays"] = Tuning.TumblerRebrandDays,
["TumblerReturnsMax"] = Tuning.TumblerReturnsMax,
["TumblerRevengeDelay"] = Tuning.TumblerRevengeDelay,
["TumblerRevengePoison"] = Tuning.TumblerRevengePoison,
["TumblerRevengeWalletShare"] = Tuning.TumblerRevengeWalletShare,
["TumblerSearchCrime"] = Tuning.TumblerSearchCrime,
["TumblerSecRep"] = Tuning.TumblerSecRep,
["TumblerSellCrt"] = Tuning.TumblerSellCrt,
["TumblerSellTaint"] = Tuning.TumblerSellTaint,
["TumblerSoldFee"] = Tuning.TumblerSoldFee,
["TumblerSoldRug"] = Tuning.TumblerSoldRug,
["TumblerTrialConvict"] = Tuning.TumblerTrialConvict,
["TumblerTrialDays"] = Tuning.TumblerTrialDays,
["TumblerTrialHintAcc"] = Tuning.TumblerTrialHintAcc,
["TumblerTrialOpen"] = Tuning.TumblerTrialOpen,
["TxConfirmBase"] = Tuning.TxConfirmBase,
["TxConfirmBig"] = Tuning.TxConfirmBig,
["TxConfirmBigValue"] = Tuning.TxConfirmBigValue,
@@ -483,6 +527,8 @@ namespace Decentralized.Domain
["CraSpiralPath"] = Array.ConvertAll(Tuning.CraSpiralPath, v => (double)v),
["IntelTipAcc"] = Array.ConvertAll(Tuning.IntelTipAcc, v => (double)v),
["IntelTipPrice"] = Array.ConvertAll(Tuning.IntelTipPrice, v => (double)v),
["OwnMixAssetWeights"] = Array.ConvertAll(Tuning.OwnMixAssetWeights, v => (double)v),
["OwnMixFees"] = Array.ConvertAll(Tuning.OwnMixFees, v => (double)v),
["PonziRates"] = Array.ConvertAll(Tuning.PonziRates, v => (double)v),
["SparkSpiralPath"] = Array.ConvertAll(Tuning.SparkSpiralPath, v => (double)v),
["UsedHealthMax"] = Array.ConvertAll(Tuning.UsedHealthMax, v => (double)v),
+111 -18
View File
@@ -1,23 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace Decentralized.Domain
{
/// <summary>
/// 一个链上地址:各币种余额,加上整个地址的污点额与成本(信用点等值)。
/// 污点和成本按转出的价值比例随币走。
/// 一个链上地址:各币种余额,每种币里有几枚是脏的(<see cref="Dirty"/>),加上整个地址的成本(信用点)。
/// 污点随具体的币进出:收到带污点的币时记下其中几枚脏;花掉、转出这种币时按它的脏比例带走;兑换成别的币时脏比例跟过去。
/// 成本按转出的价值比例随币走。
/// </summary>
public sealed class Wallet
{
public string Address;
public Dictionary<string, double> Balances = new Dictionary<string, double>();
/// <summary>污点额(信用点等值)。</summary>
public double Taint;
/// <summary>每种币里脏的枚数(0 ≤ 脏 ≤ 余额)。</summary>
public Dictionary<string, double> Dirty = new Dictionary<string, double>();
/// <summary>旧存档(数据版本 5 之前)整个钱包的污点额(信用点):只在读档迁移时读一次,之后为 0。</summary>
[JsonProperty("Taint")] public double LegacyTaint;
/// <summary>成本(信用点):出金时只对超出成本的部分征税。</summary>
public double Basis;
/// <summary>被执法标记。</summary>
public bool Flagged;
/// <summary>钱包编号("钱包 n"),添加时定下,销毁别的钱包也不变。</summary>
public string Name;
/// <summary>玩家起的名字;空时用默认名。</summary>
@@ -43,7 +45,7 @@ namespace Decentralized.Domain
{
double v = Value(price);
Balances.Clear(); Frozen.Clear(); FrozenUntil.Clear(); BuyQty.Clear(); BuyCost.Clear();
Taint = 0; Basis = 0;
Dirty.Clear(); Basis = 0;
return v;
}
@@ -101,40 +103,131 @@ namespace Decentralized.Domain
BuyQty[asset] = bq * keep; BuyCost[asset] *= keep;
if (BuyQty[asset] < 1e-9) { BuyQty.Remove(asset); BuyCost.Remove(asset); }
}
double v = Get(asset) + amount;
double had = Get(asset), v = had + amount;
if (v < -1e-9) throw new InvalidOperationException($"{Address}: {asset} 余额不足");
if (Math.Abs(v) < 1e-12) Balances.Remove(asset);
else Balances[asset] = v;
// 拿走的币按这种币的脏比例带走脏枚数
if (amount < 0 && Dirty.TryGetValue(asset, out var d)) SetDirty(asset, had > 1e-12 ? d * Math.Max(0, v) / had : 0);
}
/// <summary>按价格表估值(信用点)。</summary>
/// <summary>花掉(销毁)一笔币:污点与成本按这笔币占钱包总价值的比例一起带走。</summary>
/// <summary>这种币里脏的枚数(不超过余额)。</summary>
public double DirtyOf(string asset) => Dirty.TryGetValue(asset, out var d) ? Math.Max(0, Math.Min(d, Get(asset))) : 0;
/// <summary>这种币的脏比例(0–1)。</summary>
public double DirtyRatio(string asset) { double g = Get(asset); return g > 1e-12 ? DirtyOf(asset) / g : 0; }
/// <summary>这种币里干净的、能动用的枚数(不超过可动用余额)。钱包往 Poinbase 最多只能充这么多。</summary>
public double CleanAvail(string asset) => Math.Max(0, Math.Min(Avail(asset), Get(asset) - DirtyOf(asset)));
/// <summary>按现价算的脏币总价值(信用点):钱包总表、日志用。</summary>
public double Taint(Func<string, double> price) => Balances.Keys.Sum(a => DirtyOf(a) * price(a));
private void SetDirty(string asset, double d)
{
d = Math.Max(0, Math.Min(d, Get(asset)));
if (d < 1e-9) Dirty.Remove(asset); else Dirty[asset] = d;
}
/// <summary>收到 amount 枚币,其中 dirty 枚是脏的(不超过 amount)。</summary>
public void AddDirty(string asset, double amount, double dirty)
{
if (amount <= 0) return;
Add(asset, amount);
if (dirty > 0) SetDirty(asset, DirtyOf(asset) + Math.Min(dirty, amount));
}
/// <summary>把这种币里干净的 qty 枚记成脏的(刚收到的兑付被判内幕、操纵时用);不够的部分不记,返回没记上的枚数。</summary>
public double MarkDirty(string asset, double qty)
{
if (qty <= 0) return 0;
double clean = Math.Max(0, Get(asset) - DirtyOf(asset)), mark = Math.Min(qty, clean);
SetDirty(asset, DirtyOf(asset) + mark);
return qty - mark;
}
/// <summary>余额按比例缩水(CRC 托管银行减记):脏枚数同比例缩水。</summary>
public void Scale(string asset, double factor)
{
double g = Get(asset);
if (g <= 0) return;
double d = DirtyOf(asset);
Add(asset, g * Math.Max(0, factor) - g);
SetDirty(asset, d * Math.Max(0, factor));
}
/// <summary>兑换:付出 amount 枚 from(按它的脏比例带走脏枚数),收到 got 枚 to,脏比例跟过去;clean 为真时(兑入 Ponero)收到的是干净的。</summary>
public void Convert(string from, double amount, string to, double got, bool clean = false)
{
double r = DirtyRatio(from);
Add(from, -amount);
AddDirty(to, got, clean ? 0 : got * r);
}
/// <summary>花掉(销毁)一笔币:脏枚数按这种币的脏比例带走,成本按这笔币占钱包总价值的比例带走。</summary>
public void Spend(string asset, double amount, Func<string, double> price)
{
double total = Value(price), share = total > 0 ? Math.Min(1, amount * price(asset) / total) : 0;
double taint = Taint * share, basis = Basis * share;
Add(asset, -amount); Taint -= taint; Basis -= basis;
double basis = Basis * share;
Add(asset, -amount); Basis -= basis;
}
/// <summary>按价格表估值(信用点)。</summary>
public double Value(Func<string, double> price) => Balances.Sum(kv => kv.Value * price(kv.Key));
/// <summary>按信用点额折成的诈骗罪证:额 × <see cref="Tuning.PredictFraudShare"/>,单次封顶 <see cref="Tuning.PredictFraudCap"/>(永续被判操纵、Tumbler 泄露用)。</summary>
public static double FraudFor(double credits) => Math.Min(Tuning.PredictFraudCap, Math.Max(0, credits) * Tuning.PredictFraudShare);
/// <summary>
/// 转出:taint 和 basis 按转出价值占地址总价值的比例带走;被标记的地址转出可追踪的币,收款地址也被标记。
/// Ponero 等不可追踪资产转入时,污点和标记都不随之进入目标地址。
/// 只转干净的币(往 Poinbase 充值):脏枚数全部留在原钱包,成本按价值比例带走。数量超过 <see cref="CleanAvail"/> 的部分不转,返回实际转的数量。
/// </summary>
public static double TransferClean(Wallet from, Wallet to, string asset, double amount, Func<string, double> price)
{
amount = Math.Min(amount, from.CleanAvail(asset));
if (amount <= 0) return 0;
double total = from.Value(price);
double share = total > 0 ? Math.Min(1.0, amount * price(asset) / total) : 0;
double basis = from.Basis * share, dirty = from.DirtyOf(asset);
from.Add(asset, -amount);
from.SetDirty(asset, dirty);
from.Basis -= basis;
to.Add(asset, amount);
to.Basis += basis;
return amount;
}
/// <summary>
/// 转出:这种币按它的脏比例把脏枚数带过去,成本按转出价值占地址总价值的比例带走。
/// 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;
double basis = from.Basis * share, dirty = amount * from.DirtyRatio(asset);
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; if (from.Flagged) to.Flagged = true; }
to.AddDirty(asset, amount, spec == null || spec.Traceable ? dirty : 0);
to.Basis += basis;
}
/// <summary>
/// 存档迁移:旧的整钱包污点额(信用点)按当前价格摊成各币的脏枚数:脏比例 = min(1, 污点 / 可追踪币的价值),每种可追踪的币按这个比例记脏。
/// extra 是另外挪进来的旧污点(Poinbase 账户、永续仓位),一起摊;超出的部分作废。旧字段清零。
/// </summary>
public void MigrateTaint(Func<string, double> price, double extra = 0)
{
double t = LegacyTaint + extra;
LegacyTaint = 0;
if (t <= 0) return;
var traceable = Balances.Keys.Where(a => Assets.Get(a) == null || Assets.Get(a).Traceable).ToList();
double v = traceable.Sum(a => Get(a) * price(a));
if (v <= 0) return;
double r = Math.Min(1, t / v);
foreach (var a in traceable) SetDirty(a, DirtyOf(a) + Get(a) * r);
}
}
}
+13
View File
@@ -137,6 +137,7 @@ namespace Decentralized
private static void NightDraw(GameItem m, GameSlotInventory battery, GameInventory modules)
{
if (TurboMine.Boosting) { Boost(m, battery, modules); return; }
if (!Session.TonightDrawn.Add(m.uniqueId)) return;
var u = Items.ToUnit(m);
Session.TonightRigs++;
@@ -151,6 +152,18 @@ namespace Decentralized
Update(m, modules);
}
/// <summary>涡轮增压:扣一晚的电,立刻挖一轮(不影响今晚照常挖矿)。</summary>
private static void Boost(GameItem m, GameSlotInventory battery, GameInventory modules)
{
var u = Items.ToUnit(m);
if (u.Dead) return;
var b = Items.Bonus(m, modules);
int kj = (int)Math.Ceiling(u.Kj(b));
if (battery.childItems.Count == 0 || !PowerHelper.DrawPowerSource(battery.childItem, kj)) { Log.Info("涡轮增压:矿机电池电量不够,这一轮没挖"); return; }
TurboMine.Round(m, new List<KeyValuePair<GameItem, ModuleBonus>> { new KeyValuePair<GameItem, ModuleBonus>(m, b) });
Update(m, modules);
}
/// <summary>造一台矿机:按健康度写标签。</summary>
public static GameItem New(string model, double health)
{
+3 -3
View File
@@ -44,8 +44,8 @@ namespace Decentralized
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tainted wallet (test)", () => Log.Guard("带污点的钱包(测试)", () =>
{
var w = Session.Data.Hot;
w.Add(Assets.PSC, 100); w.Add(Assets.CRT, 1000); w.Taint += 3000; w.Basis += 2000;
Log.Info($"钱包:PSC {w.Get(Assets.PSC):0.##},污点 {w.Taint:0}");
w.AddDirty(Assets.PSC, 100, 30); w.AddDirty(Assets.CRT, 1000, 1000); w.Basis += 2000;
Log.Info($"钱包:PSC {w.Get(Assets.PSC):0.##}(脏 {w.DirtyOf(Assets.PSC):0.##}),CRT {w.Get(Assets.CRT):0}(脏 {w.DirtyOf(Assets.CRT):0})");
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Terminal (test)", () => Log.Guard("终端(测试)", TerminalWindow.Open));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Black market +50 rep, restock (test)", () => Log.Guard("黑市(测试)", () =>
@@ -55,7 +55,7 @@ namespace Decentralized
if (!Unlock.BlackUnlocked) ModHelper.GetNetworkUpgrade(Unlock.BlackId)?.Unlock();
if (!Unlock.MixerUnlocked) ModHelper.GetNetworkUpgrade(Unlock.MixerId)?.Unlock();
var d = Session.Data;
d.Hot.Add(Assets.PSC, 20); d.Hot.Add(Assets.CRT, 3000); d.Hot.Taint += 2000; d.Hot.Basis += 1000;
d.Hot.Add(Assets.PSC, 20); d.Hot.AddDirty(Assets.CRT, 3000, 2000); d.Hot.Basis += 1000;
BlackDelivery.Restock(d, StoreStation.GetDayCounter(), true);
Log.Info($"黑市:声望 {StoreReputation.GetBMReputation()},上架 {d.Black.Offers.Count} 种:{string.Join(", ", d.Black.Offers.Select(o => o.Item + "×" + o.Qty + "@" + o.Price))},解锁 {Unlock.BlackUnlocked}");
}));
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Decentralized
{
public const string Id = "probably_decentralized";
public const string Name = "Probably Decentralized";
public const string Version = "1.1.0";
public const string Version = "2.0.0";
}
/// <summary>本地化:游戏的 mod 本地化表(ModFiles/Localization/*.csv),当前语言缺失时回退英文。</summary>
+63
View File
@@ -144,11 +144,74 @@ namespace Decentralized
var t = Session.Data.Story.Tumbler; int day = StoreStation.GetDayCounter();
if (t.StartDay < 0) t.StartDay = day;
t.StartDay = day - Tumbler.Offsets[System.Math.Min(t.Stage, Tumbler.Offsets.Length - 1)]; t.VisitedDay = -1;
var af = Session.Data.Story.After;
if (af.AgentDay >= day) af.AgentDay = day - 1;
if (af.SearchDay >= day) af.SearchDay = day - 1;
PlayerStore.Instance.currentClientInstance = null;
var m = PlayerStore.Instance.storeClientManager;
TumblerStory.ForceVisit(m);
PlayerStore.Instance.TryCallNextClient();
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tumbler: restart story (test)", () =>
{
var st = Session.Data.Story;
Log.Info($"测试:Tumbler 剧情重来(原进度 {st.Tumbler.Stage},选择 {st.Tumbler.Choice},结束 {st.Tumbler.Done})");
st.Tumbler = new Tumbler(); st.After = new TumblerAfter();
st.Applied.RemoveAll(k => k.StartsWith("tumbler_"));
st.MixerClosed = st.MixerRebranded = st.MixerHoneypot = false; st.MixerFee = st.MixerVolume = st.MixerRug = 1;
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Hot wallet +2000 CRC (test)", () =>
{
var w = Session.Data.Hot; w.Add(Assets.CRC, 2000); w.Basis += 2000;
Log.Info($"测试:钱包 CRC {w.Get(Assets.CRC):0},污点 {w.Taint:0}");
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "My mixer: max heat (test)", () =>
{
var m = Session.Data.OwnMix; m.Heat = 1e4;
Log.Info($"测试:自营混币器风声拉满,今晚查封概率 {m.SeizeChance(1, false):P0}");
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Mixer: honeypot now (test)", () =>
{
var d = Session.Data;
d.Story.Rebrand(true); d.Story.After.Rebranded = true;
d.Addresses["mixer_old"] = d.Address("mixer"); d.Addresses["mixer"] = ChainAddress.Make(d.Rng.NextUInt());
Log.Info($"测试:混币器换成钓鱼混币器,费率 {Mixer.Fee:P1}");
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tumbler: contact, crackdown tomorrow (test)", () => Log.Guard("联系人测试", () =>
{
Session.Data.Story.After.Contact = true;
StoreStation.Instance.storeEventManager.QueueFuturEvent(OperationListSec.Crackdown(), 1, false);
Log.Info("测试:Tumbler 成为联系人,镇压排到明天(玩家不知道)");
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tumbler: leak tonight (test)", () =>
{
var a = Session.Data.Story.After; a.Leaked = false; a.LeakDay = StoreStation.GetDayCounter();
Log.Info($"测试:今晚泄露,出款记录 {Session.Data.Mixer.Returns.Count} 条");
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tumbler: trial market now (test)", () => Log.Guard("审判盘测试", () =>
{
var d = Session.Data; var a = d.Story.After; int day = StoreStation.GetDayCounter();
a.Verdict = -1; a.TrialMarket = a.TrialDone = false; a.Hints.Clear(); a.HintDay = -1;
a.StartTrial(day - Tuning.TumblerTrialOpen, d.Rng);
PredictGame.OpenTrial(d, day);
Log.Info($"测试:审判盘开盘,第 {a.TrialDay} 天宣判,判决 {a.Verdict}");
Session.Save();
}));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tumbler: revenge tonight (test)", () =>
{
var a = Session.Data.Story.After; a.Revenged = false; a.RevengeDay = StoreStation.GetDayCounter();
Log.Info("测试:今晚报复");
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Tumbler: BM rug tonight (test)", () =>
{
var a = Session.Data.Story.After; a.BmRugged = false; a.BmRugDay = StoreStation.GetDayCounter();
Log.Info($"测试:今晚黑市跑路,在途 {Session.Data.Mixer.Pending.Count} 笔");
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Mixer: rebrand tonight (test)", () =>
{
var a = Session.Data.Story.After; a.Rebranded = false; a.ClosedDay = StoreStation.GetDayCounter() - Tuning.TumblerRebrandDays;
Log.Info("测试:今晚换皮重开");
});
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Lower-level rep +30 (test)", () => StoreReputation.ModReputation(StoreReputation.FACTION_LOWER_LEVEL, 30));
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Dump crimes and reputation (test)", () => Log.Guard("罪证与声望", () =>
{
-107
View File
@@ -1,107 +0,0 @@
using System;
using Decentralized.Domain;
namespace Decentralized
{
/// <summary>
/// 收黑币的上门顾客(游戏的对话顾客):游戏里出了事,有人拿着来路不明的币折价换现金,见 <see cref="HotCoins"/>。
/// 一天最多一位。收下:付现金,币进已连接的钱包(PSC、CRT 全额带污点);钓鱼执法:现金被没收并记销赃罪证,币不会到。
/// </summary>
public static class HotCoinSeller
{
private static int seenDay = -1;
public static void Subscribe()
{
ModHook.OnGenerateCustomerLate += OnGenerate;
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "Hot coin seller (test)", () => Log.Guard("收黑币(测试)", () =>
{
var d = Session.Data;
string[] assets = { Assets.CRT, Assets.PSC, Assets.Ponero };
var c = Create(assets[d.Rng.Range(0, assets.Length - 1)]);
if (c == null) return;
PlayerStore.Instance.currentClientInstance = null;
PlayerStore.Instance.storeClientManager.clientStack.Insert(0, c);
PlayerStore.Instance.TryCallNextClient();
}));
}
public static void Unsubscribe() => ModHook.OnGenerateCustomerLate -= OnGenerate;
private static void OnGenerate(StoreClientManager manager)
{
var d = Session.Data;
if (d == null || !Unlock.IsUnlocked || !d.Connected) return;
Log.Guard("收黑币", () =>
{
int day = StoreStation.GetDayCounter();
if (day == seenDay) return;
seenDay = day;
bool ponero = Unlock.BlackUnlocked && BlackMarket.DeskOpen(StoreReputation.GetBMReputation());
string asset = HotCoins.Pick(Night.ActiveEvents(), ponero, d.Rng);
if (asset == null) return;
var c = Create(asset);
if (c != null) manager.AddClient(c);
});
}
private static StoreClient Create(string asset)
{
var d = Session.Data;
bool crackdown = Night.ActiveEvents().Contains(MarketEvent.Crackdown);
var o = HotCoins.Make(asset, Session.Price(asset), Session.Rent(), crackdown, d.Rng);
if (o == null) return null;
var c = new StoreClient
{
identifier = "pd_hot_coins", displayName = L.T("hot.name"),
spriteName = asset == Assets.CRT ? SpriteDict.GetRandomBMMaleSpriteName() : asset == Assets.PSC ? SpriteDict.GetRandomRevSpriteName() : SpriteDict.GetRandomShadySpriteName(),
clientIntent = StoreClient.ClientIntent.DIALOGUE,
};
// 选项的动作先于下一句执行:在动作里结算,再把卖家的回话换成结果
var yes = new Dialogue();
var reply = yes.SetText("PLAYER", L.T("hot.say.yes")).NextDialogue();
reply.SetText(c.displayName, L.T("hot.refused")).SetEndAction(Leave);
var no = new Dialogue();
no.SetText("PLAYER", L.T("hot.say.no")).NextDialogue().SetText(c.displayName, L.T("hot.refused")).SetEndAction(Leave);
c.mainDialogue.SetText(c.displayName, L.T("hot.intro." + asset)).NextDialogue()
.SetText(c.displayName, L.T("hot.offer", Fmt.Num(o.Qty), asset, Fmt.Credits(o.Value), Fmt.Credits(o.Cash)))
.AddChoice(DirectoryMaster.Item("yes"), L.T("hot.choice.yes"), "", yes, () => reply.SetText(c.displayName, L.T(Accept(o))))
.AddChoice(DirectoryMaster.Item("cancel"), L.T("hot.choice.no"), "", no, null);
ModHelper.SetClientSource(c, ModInfo.Id);
c.AddBasicDialog();
c.CompleteClientCreation();
Log.Info($"收黑币上门:{o.Qty:0.##} {asset},市值 {o.Value:0},要价 {o.Cash}{(o.Sting ? "(钓鱼执法)" : "")}");
return c;
}
/// <summary>选了收下:现金不够或没连钱包就作罢;钓鱼执法没收现金、记罪证;否则付现金、币进钱包。返回卖家回话的键。</summary>
private static string Accept(HotOffer o)
{
string line = "hot.refused";
Log.Guard("收黑币成交", () =>
{
var d = Session.Data;
if (!d.Connected) { line = "hot.nowallet"; return; }
if (PlayerStore.Instance.playerCash < o.Cash) { line = "hot.nocash"; return; }
ModHelper.AddCredit(-o.Cash, false);
if (o.Sting)
{
PlayerStore.Instance.secData.CommitCrime("FENCING", (int)Math.Min(Tuning.CrimeEventCap, o.Cash));
line = "hot.sting";
Log.Info($"收黑币:钓鱼执法,现金 {o.Cash} 没收,记销赃罪证");
}
else
{
HotCoins.Receive(d.Hot, o);
d.Chain(ChainAddress.Make(d.Rng.NextUInt()), d.Hot.Address, o.Asset, o.Qty);
line = "hot.done";
Log.Info($"收黑币:{o.Qty:0.##} {o.Asset} 进 {WalletWindow.NameOf(d.Hot)},付现金 {o.Cash},钱包污点 {d.Hot.Taint:0}");
}
Session.Save();
});
return line;
}
private static void Leave() => DialogUIManager.Instance.CurrentClientLeave();
}
}
+23 -6
View File
@@ -58,6 +58,7 @@ namespace Decentralized
dir.Add(Rig, CreateRig);
WrapElectronics(dir);
WrapTurbos(dir);
OtcLamp.Wrap(dir);
dir.Add(Guide, CreateGuide);
dir.Add(HwWallet, CreateHwWallet);
dir.Add(KeyDrive, CreateKeyDrive);
@@ -540,8 +541,8 @@ namespace Decentralized
}
/// <summary>
/// 涡轮增压器不能作用于矿架和 ASIC 矿机:它们带"标准机器"标签,原版涡轮只看这个标签,还会手动触发目标的夜间结算(等于白天多扣一次电)。
/// 在原版涡轮工厂外包一层,对这两类目标返回否;是替换判断函数而不是追加(追加是"或",原来的"是"还会生效)。
/// 涡轮增压器作用于矿架和 ASIC 矿机:照原版机器的规则多跑一轮,这一轮是立刻挖一晚(见 <see cref="TurboMine"/>)。
/// 原版增压器调用机器的夜间周期回调,所以在它的作用函数外包一层,作用于矿机时打上标记,回调据此改为立刻结算;判断、消耗、充电都照原版。
/// </summary>
private static void WrapTurbos(ModItemDirectory dir)
{
@@ -552,10 +553,13 @@ namespace Decentralized
dir.Set(id, () =>
{
var it = make();
var may = it.mayThisTargetItemFunc;
var can = it.canThisTargetItemFunc;
it.mayThisTargetItemFunc = (s, t) => !IsMiner(t) && (may?.Invoke(s, t) ?? false);
it.canThisTargetItemFunc = (s, t) => !IsMiner(t) && (can?.Invoke(s, t) ?? false);
var on = it.onThisTargetItemFunc;
it.onThisTargetItemFunc = (s, t) =>
{
if (!IsMiner(t)) { on?.Invoke(s, t); return; }
TurboMine.Boosting = true;
try { on?.Invoke(s, t); } finally { TurboMine.Boosting = false; }
};
return it;
});
}
@@ -640,6 +644,7 @@ namespace Decentralized
/// <summary>夜间早段:按矿架上的显卡总耗电(含模块效果)从电池扣电;扣电成功,登记今晚参与挖矿的显卡和模块效果。</summary>
private static void NightDraw(GameItem rig, GameSlotInventory battery, GameInventory modules, GameInventory cards)
{
if (TurboMine.Boosting) { Boost(rig, battery, modules, cards); return; }
if (!Session.TonightDrawn.Add(rig.uniqueId)) return;
var list = Cards(cards).Where(c => !c.IsTag(DeadTag)).ToList();
Session.TonightRigs++;
@@ -653,6 +658,18 @@ namespace Decentralized
UpdateRig(rig, modules, cards);
}
/// <summary>涡轮增压:扣一晚的电,立刻挖一轮(不影响今晚照常挖矿)。</summary>
private static void Boost(GameItem rig, GameSlotInventory battery, GameInventory modules, GameInventory cards)
{
var list = Cards(cards).Where(c => !c.IsTag(DeadTag)).ToList();
if (list.Count == 0) return;
var b = Bonus(rig, modules);
int kj = RigKj(cards, b);
if (battery.childItems.Count == 0 || !PowerHelper.DrawPowerSource(battery.childItem, kj)) { Log.Info("涡轮增压:矿架电池电量不够,这一轮没挖"); return; }
TurboMine.Round(rig, list.Select(c => new KeyValuePair<GameItem, ModuleBonus>(c, b)).ToList());
UpdateRig(rig, modules, cards);
}
/// <summary>
/// 放进储藏区(游戏 EmporiumTryAdd 的默认目标,放不下时游戏自己找位置)。
/// 不放柜台:测试中柜台上的物品过夜后会消失(未在正常流程中确认);ModHelper.MoveItemTo* 放满时会静默失败。
+2 -2
View File
@@ -22,7 +22,7 @@ namespace Decentralized
DealerStock.Subscribe();
UsedDealerClient.Subscribe();
CoinCustomers.Subscribe();
HotCoinSeller.Subscribe();
OtcClient.Subscribe();
BlackDelivery.Subscribe();
TumblerStory.Subscribe();
GpuInspectView.Ensure();
@@ -38,7 +38,7 @@ namespace Decentralized
DealerStock.Unsubscribe();
UsedDealerClient.Unsubscribe();
CoinCustomers.Unsubscribe();
HotCoinSeller.Unsubscribe();
OtcClient.Unsubscribe();
BlackDelivery.Unsubscribe();
TumblerStory.Unsubscribe();
GpuInspectView.Remove();
+141 -31
View File
@@ -22,7 +22,7 @@ tab.report,Bericht
wallet.line,{0}: {1} (ca. {2} Credits)
wallet.empty,Die Wallet ist leer.
wallet.total,Gesamtwert: {0} Credits
wallet.taint,Kontaminierter Wert: {0} Credits
wallet.taint,Kontaminierte Coins: {0} Credits
trade.amount,Betrag
trade.deposit,Credits als CRC einzahlen
trade.withdraw,CRC als Credits auszahlen
@@ -38,8 +38,6 @@ msg.balance,Nicht genügend Guthaben.
msg.trade,"{0} {1} gezahlt, {2} {3} erhalten."
msg.deposit,{0} Credits als CRC eingezahlt.
msg.cashout,{0} Credits erhalten. Steuer: {1} Credits.
msg.aml_light,Compliance-Prüfung: {0} Credits an kontaminierten Geldern wurden einbehalten.
msg.aml_heavy,Compliance-Prüfung: {0} Credits an kontaminierten Geldern wurden beschlagnahmt und an Security gemeldet.
report.none,Noch kein Mining-Bericht vorhanden.
report.day,Nacht von Tag {0}
report.rigs,Rigs mit Strom: {0} von {1}
@@ -50,6 +48,7 @@ report.burnt,Durchgebrannte Karten: {0}
report.price,PSC-Preis: {0} Credits
nightlog.mined,Mining-Rigs haben {0} PSC geschürft.
nightlog.burnt,{0} ist durchgebrannt.
nightlog.turbo,{0} per Turbo-Booster beschleunigt: sofort {1} PSC geschürft.
coinpay.line,Abgemacht. {0} {1}.
nightlog.coinpay,Zahlungen in Coins ergaben {0} Credits; die Coins wurden an die Hot-Wallet übertragen.
nightlog.unpowered,Mining-Rigs oder Miner ohne Strom: {0}.
@@ -140,6 +139,7 @@ col.asset,Asset
col.amount,Betrag
col.value,Wert
col.avg,Ø-Kauf
col.dirty,Kontaminiert
bank.cash.k,Credits
bank.rent.k,Miete
bank.rent.v,"{0}, fällig in {1} Tg."
@@ -151,13 +151,12 @@ stable.ok,Gekoppelt
stable.depeg,Entkoppelt
stable.warn,Warnung: Staking-Reservedeckung knapp
stable.spiral,Kollaps
aml.taint,Konto-Kontaminationsanteil: {0}%
mixer.h,Mixer
mixer.ok,Status: Normalbetrieb
mixer.sanctioned,Status: sanktioniert (noch {0} Tag(e)). Währenddessen abgehobene Coins werden markiert.
mixer.sanctioned,Status: Sanktioniert (noch {0} T.). Währenddessen fällige Auszahlungen werden nicht gewaschen und kommen vollständig kontaminiert zurück.
mixer.pending,Ausstehende Einzahlungen
mixer.none,Keine ausstehenden Einzahlungen.
mixer.col.taint,Kontamination
mixer.col.taint,Kontaminiert
mixer.col.ready,Fällig
mixer.day,Tag {0}
mixer.deposit,Einzahlung
@@ -168,11 +167,12 @@ mixer.go,In Mixer einzahlen
mixer.min,Einzahlungen müssen mindestens {0} Credits wert sein.
mixer.done,{0} {1} eingezahlt; Rückerhalt an Tag {2}.
mixer.upgrade.title,Mixer
mixer.upgrade.desc,Schaltet den Mixer auf der Graunetz-Seite des Terminals frei: Kontaminierte Coins einzahlen und Tage später mit reduzierter Kontamination zurückerhalten. Auszahlungen frisch gemixter Coins werden strenger geprüft; bei Razzien kann der Mixer sanktioniert werden und währenddessen abgehobene Coins werden markiert.
mixer.upgrade.desc,"Schaltet den Mixer auf der Graunetz-Seite des Terminals frei: Kontaminierte Coins einzahlen und Tage später zurückerhalten, wobei weniger davon kontaminiert sind. Während Razzien kann der Mixer sanktioniert werden; in dieser Zeit fällige Auszahlungen kommen vollständig kontaminiert zurück."
news.sanction.title,Mixer auf Sanktionsliste gesetzt
news.sanction.desc,Security hat eine Reihe von Mixing-Diensten sanktioniert. In den nächsten Tagen aus Mixern abgehobene Gelder gelten als verdächtig.
news.sanction.desc,Security hat eine Reihe von Mixing-Diensten sanktioniert. In den kommenden Tagen aus Mixern abgehobene Gelder werden als kontaminierte Gelder behandelt.
nightlog.mixer,Mixer zahlte {0} {1} zurück; Kontamination {2} -> {3}.
nightlog.mixer_flag," (während Sanktionen abgehoben; Adresse markiert)"
nightlog.mixer_flag," (während der Sanktionen abgehoben; vollständig als kontaminiert markiert)"
nightlog.mixer_flag.trap," (Security markiert die abgehobenen Coins als kontaminiert)"
nightlog.mixer_rug,Der Mixer-Betreiber ist untergetaucht: {0} {1} kehrten nie zurück.
web3.sub.trade,Handel
gray.sub.mixer,Mixer
@@ -247,7 +247,7 @@ black.kind.Contraband.3,Schmuggelware (hoch)
black.kind.Tech,Hightech (Aufpreis)
nightlog.bm.ready,"Schwarzmarkt: {0} eingetroffen; sobald du öffnest, kommt ein Kurier vorbei."
guide.web3.title,Web3-Handbuch
guide.web3.text,"Bank und Handel: Die Bankseite ist nur lesbar und zeigt Bargeld, Miete und das CRC auf dem Poinbase-Konto. Die Seite „Guthaben“ von Poinbase regelt Ein- und Auszahlungen; zuerst wird der Coin gewählt: CRC geht über das Bankkonto oder die Wallet, alle anderen Coins nur über die Wallet. Eine Einzahlung von der Bank wandelt Credits in CRC auf deinem Poinbase-Konto um; eine Auszahlung auf die Bank wandelt CRC zurück in Credits um und durchläuft eine Compliance-Prüfung, die die Kontamination des Poinbase-Kontos bewertet. Eine Auszahlung auf eine Wallet braucht die Zieladresse und mehrere Bestätigungen bis zur Gutschrift; die Gasgebühr ist fest auf Normal gesetzt und wird vom abgehenden Coin abgezogen. Ist das Adressformat oder die Prüfziffer falsch, wird abgelehnt; bei einer formal gültigen Adresse, die niemand nutzt, sind die Coins nach der Überweisung weg.¶Handel: PSC steht für Probably Stolen Coin, den durch Grafikkarten und Miner geschürften Coin. Der Poinbase-Spotmarkt hat zwei Paare, PSC/CRC und SPARK/CRC, je nach dem im Chart gewählten Coin. Käufe und Verkäufe werden beide als Menge des gehandelten Coins eingegeben; die Vorschau zeigt den gezahlten oder erhaltenen Notierungs-Coin, der Durchschnittspreis enthält die Gebühr, die Slippage nicht.¶Auf der Web3-Handelsseite lassen sich beliebige zwei Coins tauschen, stets über CRC als Zwischenschritt: Ist CRC eine Seite, läuft der Tausch über einen Pool, sonst über zwei Pools mit zwei Gebühren und sich addierender Slippage; die Vorschau zeigt Menge, Anzahl der Pools und Verlust. Stablecoin-Tausch (CRT oder CRA gegen CRC) läuft über Stable-Pools: {StableFee:%} Gebühr und kaum Slippage nahe der Bindung; beim Depeg wird die Liquidität dünner, beim Zusammenbruch wird sie abgezogen und die Slippage springt.§Hardware-Wallets: On-Chain-Coins liegen auf PLEDGER-Hardware-Wallets, jede mit eigenem Guthaben und eigener Kontamination. Eine Wallet ist ein Gegenstand: Ziehe sie auf das Handelsterminal, um sie zu laden. Öffne sie auf der Wallets-Seite des Terminals; verbinde und trenne sie oben rechts im Terminal. Der Handel der Web3-Seite sowie Shop und Mixer der Graunetz-Seite nutzen die verbundene Wallet; ohne Wallet oder ohne Verbindung sind sie nur lesbar und alle Guthaben zeigen 0. Kunden, die in Coins zahlen, zahlen auf die verbundene Wallet.¶Zahlungen (Shop, Mixer, Ponero-Schalter, Einstellgebühren) beginnen als Rechnung auf dem Handelsterminal; gib die Empfängeradresse auf der Wallet ein und halte zum Signieren ✓ gedrückt. Die letzten beiden Zeichen einer Adresse bilden eine Prüfsumme, sodass ein einzelner Tippfehler meist abgefangen wird; gelegentlich ist die Eingabe dennoch gültig und die Coins gehen an eine ungenutzte Adresse. Auch DEX-Swaps erfordern eine Signatur auf der Wallet.¶Poinbase ist ein Verwahrkonto; Einzahlungen erfolgen von der Wallet an die Einzahlungsadresse von Poinbase (nur PSC, CRC und SPARK; die regulierte Börse nimmt weder das CRT des Schwarzmarkts noch das CRA der Revolution, und die Wallet blockiert Überweisungen dieser Coins an die Einzahlungsadresse), Auszahlungen werden auf der Seite „Guthaben“ von Poinbase gestartet. Der Explorer auf der Wallets-Seite zeigt Überweisungen nach Adresse, ausschließlich tatsächlich erfolgte: jede Bewegung in und aus deinen Wallets (Überweisungen, DEX-Tausch und andere Vertragsaufrufe, Miner-Erträge, Auszahlungen, Coins, die andere dir schicken); Ponero ist ein Privacy Coin und erscheint nie. Weitere Hardware-Wallets verkauft Dr. Jackson; ziehe eine auf das Handelsterminal, um sie hinzuzufügen, bis zu {WalletMax}. Im Shop gibt es günstigere Exemplare. Eine nicht mehr benötigte Wallet kann auf der Wallets-Seite zerstört werden; ihre Coins sind dauerhaft verloren, ebenso alles, was danach an ihre Adresse gesendet wird.¶Überweisungen kosten Gasgebühren: langsam, normal oder schnell, und die Gas-Preise steigen und fallen im Wochenverlauf; bei Netzwerküberlastung steigen die Gasgebühren und langsame Transaktionen können aus dem Mempool fliegen. Vor dem Senden die vollständige Adresse prüfen.§Staking: Im Revolution-Pool gestakte CRA erzielen einen Tageszinssatz, der dem Einsatz zugeschlagen wird; eine Auszahlung erfordert einen vorherigen Antrag und vier Tage Wartezeit – ohne Zinsen während des Wartens und ohne vorzeitige Entnahme. CRA ist teilbesichert: Jedes CRA wird von CRC-Reserven der Revolution und vom Ausgleichstoken SPARK gedeckt; Reservedeckung und Besicherungsquote stehen auf der Staking-Seite. Zinsen werden in neuem, unbesichertem CRA gezahlt, das die Reservedeckung verwässert, umso schneller, je mehr gestakt ist; bei zu niedriger Deckung senkt die Revolution den Zinssatz. Die Revolution greift manchmal in die Reserve; zuvor gibt es Gerüchte, das ist das Vorzeichen. Fällt die Deckung danach unter {CraCrashRatio:%}, bricht das System zusammen: CRA ist binnen drei Tagen wertlos und vier Tage Entsperrung sind zu langLine truncated
guide.web3.text,"Bank und Handel: Die Bank-Seite ist schreibgeschützt und zeigt Bargeld, Miete und die CRC auf deinem Poinbase-Konto an. Die Guthaben-Seite von Poinbase wickelt Ein- und Auszahlungen ab; wähle zuerst den Coin: CRC kann über das Bankkonto oder eine Wallet laufen, jeder andere Coin nur über eine Wallet. Einzahlungen von der Bank wandeln Credits in CRC auf deinem Poinbase-Konto um; Auszahlungen auf das Bankkonto wandeln CRC ohne Prüfung wieder in Credits um (das Poinbase-Konto hält ausschließlich sauberes Geld). Auszahlungen an eine Wallet erfordern eine Empfängeradresse und einige Bestätigungen, bevor sie ankommen; die Gas-Gebühr ist fest auf der normalen Stufe und wird vom gesendeten Coin abgezogen. Ein falsches Adressformat oder eine fehlerhafte Prüfsumme wird abgelehnt; eine korrekt formatierte Adresse, die niemand nutzt, bedeutet, dass die Coins verloren sind.¶Handel: PSC steht für Probably Stolen Coin, den von Grafikkarten und Minern geschürften Coin. Der Poinbase-Spotmarkt hat zwei Handelspaare, PSC/CRC und SPARK/CRC, die dem im Chart ausgewählten Coin folgen. Käufe und Verkäufe werden jeweils als Betrag des gehandelten Coins eingegeben; die Vorschau zeigt die gezahlte oder erhaltene Notierungswährung, der Durchschnittspreis enthält die Gebühr, die Slippage nicht.¶Die Web3-Handelsseite kann zwei beliebige Coins tauschen, immer über CRC: Liegt CRC auf einer Seite, nutzt sie einen Pool, andernfalls zwei Pools mit doppelten Gebühren und gestapelter Slippage; die Vorschau zeigt den erhaltenen Betrag, die durchlaufenen Pools und den Verlust. Stablecoin-Swaps (CRT oder CRA gegen CRC) nutzen Stable-Pools: eine Gebühr von {StableFee:%} und nahezu keine Slippage nahe der Bindung; bei einer Entkopplung wird die Liquidität dünner, und bei einem Kollaps wird sie abgezogen und die Slippage springt in die Höhe.§Hardware-Wallets: On-Chain-Coins liegen in PLEDGER-Hardware-Wallets, jede mit eigenen Guthaben und kontaminierten Coins (bei jedem Coin wird erfasst, wie viele davon kontaminiert sind; siehe Kontamination). Eine Wallet ist ein Gegenstand: Ziehe sie auf das Handelsterminal, um sie zu laden. Öffne sie auf der Wallets-Seite des Terminals; das Verbinden und Trennen erfolgt oben rechts am Terminal. Der Handel auf der Web3-Seite sowie der Shop und Mixer auf der Graunetz-Seite nutzen die verbundene Wallet; ohne Wallet oder Verbindung sind sie schreibgeschützt und jedes Guthaben steht bei 0. Kunden, die mit Coins zahlen, zahlen auf die verbundene Wallet ein.¶Zahlungen (Shop, Mixer, Ponero-Schalter, Listungsgebühren) beginnen als Rechnung auf dem Terminal; tippe die Empfängeradresse auf der Wallet ein und halte ✓ gedrückt, um zu signieren. Die letzten beiden Zeichen einer Adresse sind eine Prüfsumme, sodass ein Tippfehler um ein Zeichen meistens abgefangen wird; gelegentlich ist sie zufällig gültig und die Coins gehen an eine Adresse, die niemand nutzt. DEX-Swaps erfordern ebenfalls eine Signatur auf der Wallet.¶Poinbase ist ein verwahrtes Konto; Einzahlungen werden von einer Wallet an die Einzahlungsadresse von Poinbase gesendet (nur PSC, CRC und SPARK; die konforme Börse akzeptiert weder das CRT des Schwarzmarkts noch das CRA der Revolution, und die Wallet blockiert das Senden an die Einzahlungsadresse; es können nur saubere Coins eingezahlt werden, siehe Kontamination) und Auszahlungen werden auf der Guthaben-Seite von Poinbase veranlasst. Der Explorer auf der Wallets-Seite sucht Überweisungen nach Adresse und zeigt nur tatsächlich erfolgte Überweisungen an: jede Bewegung in und aus deinen Wallets (Überweisungen, DEX-Swaps und andere Contract-Aufrufe, Miner-Auszahlungen, Auszahlungen, von anderen gesendete Coins); Ponero ist ein Privacy Coin und wird nie angezeigt. Weitere Hardware-Wallets werden von Dr. Jackson verkauft; ziehe eine auf das Handelsterminal, um sie hinzuzufügen, bis zu maximal {WalletMax}. Der Shop bietet günstigere an. Eine Wallet, die du nicht mehr benötigst, kann auf der Wallets-Seite zerstört werden; ihre Coins sind unwiderruflich verloren, und alles, was danach an ihre Adresse gesendet wird, ist ebenfalls verloren.¶Überweisungen zahlen eine Gas-Gebühr: langsam, normal oder schnell, und die Gas-Preise steigen und fallen über etwa eine Woche hinweg; bei Netzwerküberlastung steigen die Gas-Gebühren und langsame Transaktionen können aus dem Mempool verworfen werden. Prüfe vor dem Absenden die vollständige Adresse.§Staking: Im Revolution-Pool gestakte CRA erzielen einen Tageszinssatz, der dem Einsatz zugeschlagen wird; eine Auszahlung erfordert einen vorherigen Antrag und vier Tage Wartezeit – ohne Zinsen während des Wartens und ohne vorzeitige Entnahme. CRA ist teilbesichert: Jedes CRA wird von CRC-Reserven der Revolution und vom Ausgleichstoken SPARK gedeckt; Reservedeckung und Besicherungsquote stehen auf der Staking-Seite. Zinsen werden in neuem, unbesichertem CRA gezahlt, das die Reservedeckung verwässert, umso schnLine truncated
courier.odd1,...Paket ist da.
courier.odd2,(Seine Hand bleibt in der Tasche.) Hier unterschreiben.
courier.sign,Unterschreiben
@@ -360,10 +360,11 @@ hw.wallet.n,Wallet {0}
hw.review.own,Eigene Wallet: {0}
hw.review.poinbase,Poinbase-Einzahlungsadresse
poin.deposit,Einzahlungsadresse: {0}
poin.deposit.clean,In {0} einzahlbare saubere Coins: {1}
wallets.count,Wallets {0}/{1}
wallets.col.name,Name
wallets.col.addr,Adresse
wallets.col.taint,Kontamination
wallets.col.taint,Kontaminiert
wallets.open,Öffnen
wallets.use,Verbinden
wallets.current,Verbunden
@@ -377,6 +378,7 @@ hw.paste,Einfügen
hw.copied,Adresse kopiert.
hw.copied.addr,{0} kopiert
hw.balance.frozen," (gefroren {0})"
hw.balance.dirty," ({0} kontaminiert)"
wallets.destroy,Zerstören
wallets.destroy.sure,Bestätigen
wallets.destroyed,{0} zerstört: {1}
@@ -548,7 +550,7 @@ predict.purity.rust,Wasser (Obere Ebene)
nightlog.predict.win,"Prognose richtig: „{0}“, {1} ausgezahlt."
nightlog.predict.lose,"Prognose falsch: „{0}“, Einsatz {1} verloren."
nightlog.predict.void,"Prognose ungültig: „{0}“, Einsatz {1} erstattet."
nightlog.predict.caught,"Insider-Wette aufgeflogen: „{0}“, Einsatz und Gewinn {1} beschlagnahmt, Betrug vermerkt."
nightlog.predict.insider,"""{0}"" für {1} abgerechnet. Die Polizei vermutet Insiderhandel; {2} CRC davon werden als kontaminiert markiert."
predict.tab.markets,Märkte
predict.tab.intel,Informationen
predict.acct.crt,{0}: CRT {1}
@@ -591,10 +593,10 @@ predict.brief.queued,{0}: Operation geplant
predict.brief.none,{0}: Keine Operation möglich
predict.brief.can,{0}: Kann {1} starten
predict.clue.push,Preis um {0}% bewegt: hohes Manipulationsrisiko
nightlog.predict.manip,"Prognose als Marktmanipulation gewertet: „{0}“, Einsatz {1} beschlagnahmt, Betrug vermerkt."
nightlog.predict.manip,"""{0}"" für {1} abgerechnet. Die Aufsichtsbehörden vermuten, dass du den Preis manipuliert hast; {2} CRC davon werden als kontaminiert markiert."
news.manip.title,Börse untersucht ungewöhnlichen Handel
news.manip.desc,Regulierer melden: Jemand bewegte den Preis mit großen Spot-Orders und profitierte bei Derivaten und Prognosen; Gelder wurden beschlagnahmt.
perp.manip,Schließungsgewinn {0} als Marktmanipulation gewertet und beschlagnahmt; Perpetual-Konto eingefroren (Tage: {1}).
news.manip.desc,"Die Aufsichtsbehörden melden, dass jemand den Preis mit großen Spot-Orders bewegt und aus Derivaten sowie Prognosen profitiert hat; die betroffenen Konten wurden markiert."
perp.manip,Mit Gewinn von {0} geschlossen. Die Aufsichtsbehörden vermuten Marktmanipulation; Betrugsbeweise in deiner Akte: {1}.
web3.sub.ponzi,Rendite
ponzi.h,Schneeballsystem
ponzi.acct,Emittenten-Wallet {0}: CRC {1}
@@ -685,7 +687,7 @@ scam.support.name,Poinbase-Support
scam.support.1,"Hallo, hier ist der Poinbase-Support. Wir haben eine ungewöhnliche Anmeldung in Ihrem Konto festgestellt und es wurde markiert."
scam.support.2,Bitte senden Sie heute {0} CRC zur Verifizierung an die sichere Adresse {1}; der Betrag wird direkt im Anschluss zurückerstattet.
scam.support.3,"Andernfalls wird das Konto dauerhaft gefroren. Ich muss los, denken Sie an die Überweisung."
predict.sold.caught,"Insiderhandel beim Verkauf aufgeflogen: Einsatz und Gewinn beschlagnahmt, Betrug vermerkt."
predict.sold.caught,"Verkauft, {0} erhalten. Die Polizei vermutet Insiderhandel; {1} CRC davon werden als kontaminiert markiert."
predict.row.move," (letzte Nacht {0} {1})"
predict.stats.h,Verlauf (durchschnittl. Eröffnungskurs / tatsächliche Quote)
predict.stats.row,"{0}: {1} Märkte, Eröffnungsschnitt {2}, eingetreten {3}%"
@@ -696,6 +698,7 @@ predict.kind.Price,PSC-Preis
predict.kind.Gas,Gas
predict.kind.Cra,CRA-Warnung
predict.kind.Era,Miner-Ära
predict.kind.Trial,Tumbler-Prozess
nightlog.predict.move,Prognose „{0}“ geändert: {1} -> {2}
nightlog.predict.why," ({0})"
chart.empty,{0}-Tageschart: Noch keine Historie; Aufzeichnung beginnt heute.
@@ -707,7 +710,7 @@ news.story.serpico_rev.desc,"Die Revolution hat Berichten zufolge Beweise für i
news.story.serpico_bm.title,Schwarzmarkt trifft stille Vereinbarung
news.story.serpico_bm.desc,"Ein internes Security-Dokument wechselte auf dem Schwarzmarkt den Besitzer. Die Abrechnung im Darknet läuft seither spürbar stabiler, und CRT-Einlösungen liefen ausnahmslos reibungslos."
news.story.serpico_sec.title,Security leitet interne Säuberung ein
news.story.serpico_sec.desc,"Die Security kündigte eine umfassende Untersuchung interner Korruption und eine strengere Überwachung von Geldflüssen an. Poinbase verschärft Auszahlungsprüfungen, und Mixer-Dienste wurden namentlich genannt."
news.story.serpico_sec.desc,Security kündigte eine umfassende Untersuchung interner Korruption und eine strengere Überwachung der Geldströme an; Mixer-Dienste wurden namentlich genannt.
news.story.eleanor_leak.title,Zuchtdaten der Nährfrucht durchgesickert
news.story.eleanor_leak.desc,"Die Zeitung der Unteren Ebene veröffentlichte Forschungsergebnisse, mit denen die Nährfrucht wieder Samen bilden kann; Pastelle-Aktien stürzten ab. Die Depotbank für Gelder der Oberen Ebene meldet faule Kredite, und die Revolution gewinnt an Auftrieb."
news.story.eleanor_return.title,Pastelle stellt gestohlene Daten sicher
@@ -752,6 +755,47 @@ news.tumbler.caught.desc,Der als Tumbler bekannte Mixer-Betreiber wurde in der U
news.tumbler.sold.title,Mixer-Dienst wechselt den Besitzer
news.tumbler.sold.desc,"Es heißt, die Signaturschlüssel eines Mixer-Dienstes der Unteren Ebene seien in die Hände des Schwarzmarkts gefallen, und die Gebühren stiegen sofort. Nutzer fürchten um ihre Gelder."
nightlog.tumbler.gift,Tumbler nahm das Key-Drive zurück und hinterließ {0} CRT als Dank (gesendet an {1}); Mixer-Gebühren halbiert.
agent.name,Sicherheitsagent
agent.v1.1,Security. Entschuldigung für die Störung.
agent.v1.2,Wir suchen jemanden namens Tumbler. Er betreibt einen On-Chain-Mixerdienst.
agent.v1.3,Er wurde in dieser Gegend gesehen.
agent.ask,War er in deinem Laden?
agent.choice.yes,[Sagen: Ein paar Mal]
agent.choice.no,[Sagen: Nie gesehen]
agent.yes1,Danke für die Kooperation. Die Security wird sich das merken.
agent.no1,"So ist das also. Falls dir noch etwas einfällt, weißt du, wo du uns findest."
agent.search.1,"Wir sehen uns wieder. Wir haben gehört, dass du etwas für diesen Mann aufbewahrst."
agent.search.2,Routinedurchsuchung. Bitte kooperieren.
agent.search.found,Gefunden. Das nehmen wir mit.
agent.search.none,... Nichts. Entschuldigung für die Störung.
tumbler.v4.seized.1,"Ich habe gehört, die Security hat deinen Laden durchsucht."
tumbler.v4.seized.2,Sie haben jetzt den Schlüssel. Die Aufzeichnungen aller Nutzer werden aufgedeckt.
tumbler.v4.seized.3,"Was auch immer du ihnen erzählt hast, ich will es gar nicht mehr wissen."
nightlog.tumbler.leak,Security nutzte Tumblers Signaturschlüssel zur Rekonstruktion der Mixer-Aufzeichnungen: Auszahlungen: {0} ({1} Kontamination reingewaschen); Betrugsbeweise in deiner Akte: {2}.
nightlog.tumbler.leak.none,"Security hat Tumblers Signaturschlüssel verwendet, um die Mixer-Aufzeichnungen zu rekonstruieren. Du hast in letzter Zeit nichts aus dem Mixer abgehoben."
nightlog.tumbler.note,"Ein Zettel wurde unter der Tür durchgeschoben: ""Du schuldest mir was, und ich hole es mir zurück. —T"""
nightlog.tumbler.tip,Nachricht von Tumbler: Die Security geht morgen womöglich gegen die Mixer-Dienste vor.
news.tumbler.leak.title,Security rekonstruiert Mixer-Aufzeichnungen
news.tumbler.leak.desc,"Die Security gibt an, den Signaturschlüssel eines Mixer-Dienstes erhalten zu haben, und will damit Gelder zurückverfolgen, die im vergangenen Monat über diesen Dienst gewaschen wurden."
news.trial.pro.title,"Tumbler-Prozess, Tag {0}: Anklage gewinnt an Boden"
news.trial.def.title,"Tumbler-Prozess, Tag {0}: Verteidigung gewinnt an Boden"
news.trial.pro.desc1,"Die Anklage legte Server-Logs des Mixer-Knotens vor und gab an, sie stimmten eins zu eins mit den Signaturen des Angeklagten überein."
news.trial.pro.desc2,"Ein ehemaliger Partner sagte aus, der Angeklagte habe gewusst, dass die Kundengelder vom Schwarzmarkt stammten."
news.trial.pro.desc3,Die Anklage zeigte Überweisungen zwischen der Wallet des Angeklagten und mehreren Betrugsadressen.
news.trial.def.desc1,Die Verteidigung verwies auf Mängel bei der Beweissicherung während der Beschlagnahmung des Knotens; einige Logs könnten manipuliert worden sein.
news.trial.def.desc2,"Ein Gutachter der Verteidigung sagte aus, der Mixer-Contract sei Open-Source-Code und der Angeklagte könne nicht kontrollieren, wer ihn nutze."
news.trial.def.desc3,"Der Hauptzeuge der Anklage änderte seine Aussage im Zeugenstand und erklärte, er könne sich nicht erinnern, ob der Angeklagte davon wusste."
news.trial.convict.title,Tumbler schuldig gesprochen
news.trial.convict.desc,"Das Gericht sprach den als Tumbler bekannten Mixer-Betreiber der Geldwäsche schuldig. Die Security erklärte, der Mixer-Dienst werde nicht wieder öffnen."
news.trial.acquit.title,Tumbler freigesprochen
news.trial.acquit.desc,"Aus Mangel an Beweisen sprach das Gericht den als Tumbler bekannten Mixer-Betreiber frei und entließ ihn. Er verließ das Gerichtsgebäude, ohne Fragen von Reportern zu beantworten."
news.tumbler.bmrug.title,Mixer-Dienst verschwindet mit Geldern
news.tumbler.bmrug.desc,"Ein vom Schwarzmarkt übernommener Mixer-Dienst stoppte plötzlich die Auszahlungen, und die Gelder im Contract wurden abgezogen. Nutzer in den Foren berichten von einem Totalverlust."
mixer.brand,Vortex
news.mixer.rebrand.title,Neuer Mixer-Dienst {0} geht live
news.mixer.rebrand.desc,"Ein Mixer-Dienst namens {0} ging kürzlich live und behauptet, keine Logs und keine Hintertüren zu haben. Einige haben bemerkt, dass sein Contract dem zuvor geschlossenen Dienst stark ähnelt."
predict.q.trial,{0}: Tumbler schuldig gesprochen
predict.clue.trial,"Prozessberichte: Anklage im Vorteil ({0} d), Verteidigung im Vorteil ({1} d)"
mixer.status.closed,Status: Abgeschaltet
yield.sub.pools,Rendite-Pools
yield.sub.mine,System auflegen
@@ -963,18 +1007,84 @@ news.wipe.down.title,"{0} fällt an einem Tag um {1}%, {2} Trader liquidiert"
news.wipe.down.desc,"Am vergangenen Tag wurden {1} Long-Positionen auf {0}-Perps liquidiert, insgesamt {2} Credits. Der Versicherungsfonds von Poinbase steht bei {3} Credits."
news.wipe.up.title,"{0} steigt an einem Tag um {1}%, {2} Trader liquidiert"
news.wipe.up.desc,"Am vergangenen Tag wurden {1} Short-Positionen auf {0}-Perps liquidiert, insgesamt {2} Credits. Der Versicherungsfonds von Poinbase steht bei {3} Credits."
hot.name,Verkäufer
hot.intro.CRT,"Gerade ist die Lage angespannt, und dieses CRT ist mir zu heiß. Ich brauche schnell Bargeld."
hot.intro.PSC,"Die Wallet einer Mining-Farm hat letzte Nacht den Besitzer gewechselt. Das PSC liegt jetzt bei mir, und ich nehme nur Bargeld."
hot.intro.PONERO,"Lösegeld, in Ponero. Niemand kann es zurückverfolgen. Nur Bargeld."
hot.offer,"{0} {1}, zum heutigen Kurs {2} wert. Gib mir {3} in bar, und die Coins gehören sofort dir."
hot.choice.yes,Annehmen
hot.choice.no,Ablehnen
hot.say.yes,Abgemacht.
hot.say.no,Davon lasse ich die Finger.
hot.done,Coins sind raus. Wir haben uns nie gesehen.
hot.refused,Andere nehmen es gern.
hot.nocash,Du hast nicht mal das Bargeld? Vergiss es.
hot.nowallet,"Keine Wallet verbunden, wohin sollen die Coins? Vergiss es."
hot.sting,"Keine Bewegung. Security. Ankauf gestohlener Gelder: Das Bargeld wird beschlagnahmt, und das kommt in deine Akte."
otc.name,OTC-Kunde
otc.intro.CRT,"Gerade ist die Lage angespannt, und dieses CRT ist mir zu heiß. Ich brauche schnell Bargeld."
otc.intro.PSC,"Die Wallet einer Mining-Farm hat letzte Nacht den Besitzer gewechselt. Das PSC liegt jetzt bei mir, und ich nehme nur Bargeld."
otc.intro.PONERO,"Lösegeld, in Ponero. Niemand kann es zurückverfolgen. Nur Bargeld."
otc.offer,"{0} {1}, zum heutigen Kurs {2} wert, gesendet von {4}. {3} in bar für alles, oder nimm nur einen Teil."
otc.intro.sell,"Habe etwas {0}, das ich zu Bargeld machen will. Prüf die Herkunft, wenn du willst."
otc.buy.ask,"Ich möchte {0} {1}, zum heutigen Kurs {2} wert. {3} in bar für alles, oder verkauf mir einen Teil."
otc.intro.buy,"Habe gehört, du tauschst hier Coins. Ich kaufe, gegen bar."
otc.say.trade,Lass uns das am Terminal machen.
otc.wait,"Gut, ich warte."
otc.paid,Angekommen. Hier ist dein Bargeld.
tab.otc,Schalter
otc.h,Schalterhandel
otc.k.side,Kunde
otc.side.sell,Verkauft {0}
otc.side.buy,Kauft {0}
otc.k.qty,Betrag
otc.k.value,Marktwert
otc.k.ask,Forderung (gesamt)
otc.k.bid,Gebot (gesamt)
otc.k.unit,Einzelpreis
otc.k.addr,Kundenadresse: {0}
otc.lookup,Prüfen
otc.preview.take,"{0} Bargeld zahlen, {1} {2} erhalten"
otc.preview.give,"{0} {1} senden, {2} Bargeld erhalten"
otc.deal,Abschließen
otc.cancel,Passen
otc.msg.gone,Der Kunde ist gegangen.
otc.msg.cash,Nicht genug Bargeld.
otc.msg.sting,"Der Kunde war ein verdeckter Ermittler: {0} Bargeld beschlagnahmt, Hehlerei erfasst."
otc.msg.bought,"{0} {1} erhalten ({3} davon kontaminiert), {2} in bar gezahlt."
otc.msg.sold,"{0} {1} gesendet, {2} Bargeld erhalten."
gray.sub.own,Eigener Mixer
ownmix.h,Eigener Mixer
ownmix.status.closed,Geschlossen (Rug Pull)
ownmix.status.seized,"Beschlagnahmt, geschlossen bis Tag {0}"
ownmix.status.open,Geöffnet
ownmix.status.paused,Pausiert
ownmix.k.fee,Gebühr
ownmix.k.rating,Ansehen (Restkontamination)
ownmix.k.heat,Fahndungsdruck
ownmix.heat.0,Niedrig
ownmix.heat.1,Mittel
ownmix.heat.2,Hoch
ownmix.k.anon,Anonymitätsset
ownmix.k.fees,Erzielte Gebühren
ownmix.col.capital,Kapital
ownmix.col.earned,Einnahmen
ownmix.col.pending,Kunden-Coins
ownmix.open,Öffnen
ownmix.pause,Pausieren
ownmix.inject,Einzahlung
ownmix.withdraw,Auszahlung
ownmix.free,Auszahlbar: {0} {1}
ownmix.err,Übersteigt den auszahlbaren Betrag.
ownmix.injected,{0} {1} eingezahlt.
ownmix.withdrawn,{0} {1} ausgezahlt.
ownmix.withdraw.sum,{0} {1} aus eigenem Mixer auszahlen
pay.ownmix,Einzahlung eigener Mixer: {0} {1}
ownmix.rug,Rug Pull ({0})
ownmix.rug.confirm,"Erneut klicken, um {0} einzustecken"
ownmix.rug.sum,Alle Gelder aus eigenem Mixer einstecken
ownmix.rugged,{0} eingesteckt; der Mixer ist geschlossen.
nightlog.ownmix.in,"Eigener Mixer: Einzahlungen: {0}, gesamt {1}, Gebühren: {2}."
nightlog.ownmix.sting,Eine Einzahlung im eigenen Mixer stammte von einem verdeckten Ermittler; Hehlerei erfasst.
nightlog.ownmix.seized,"Security hat den eigenen Mixer beschlagnahmt: Der Pool von {0} wurde konfisziert, Hehlerei erfasst."
news.ownmix.seized.title,Security schließt Mixing-Dienst
news.ownmix.seized.desc,Security hat einen auf der Unteren Ebene betriebenen Mixing-Dienst geschlossen und sämtliche Gelder beschlagnahmt. Der Betreiber wurde bisher nicht identifiziert.
news.ownmix.rug.title,Mixing-Dienst verschwindet mit Geldern
news.ownmix.rug.desc,"Ein Mixing-Dienst auf der Unteren Ebene hat über Nacht den Betrieb eingestellt, und alle Nutzergelder im Transit sind verschwunden."
pay.otc,OTC-Kunde {0} {1} (Bargeld {2})
otc.upgrade.title,OTC-Schalter
otc.upgrade.desc,"Fügt der Schwarzen Lampe ein neues Ziel hinzu: OTC-Kunden. Einige verkaufen Coins unbekannter Herkunft, andere kaufen Coins; alle Geschäfte werden in bar abgewickelt. Enthält eine Schwarze Lampe."
otc.say.no,Diesmal passe ich.
otc.done,Coins sind raus. Wir haben uns nie gesehen.
otc.refused,Dann suche ich mir jemand anderen.
otc.nocash,Du hast nicht mal das Bargeld? Vergiss es.
otc.nowallet,"Keine Wallet verbunden, wohin sollen die Coins? Vergiss es."
otc.sting,"Keine Bewegung. Security. Ankauf gestohlener Gelder: Das Bargeld wird beschlagnahmt, und das kommt in deine Akte."
hw.err.poinbase,Poinbase nimmt kein {0} an.
hw.err.tainted,{0} der {1} dieser Wallet sind kontaminiert und Poinbase akzeptiert nur saubere Coins: maximal {2} {1}.
+141 -31
View File
@@ -22,7 +22,7 @@ tab.report,Report
wallet.line,{0}: {1} (about {2} credits)
wallet.empty,The wallet is empty.
wallet.total,Total value: {0} credits
wallet.taint,Tainted value: {0} credits
wallet.taint,Dirty coins: {0} credits
trade.amount,Amount
trade.deposit,Deposit credits as CRC
trade.withdraw,Withdraw CRC as credits
@@ -38,8 +38,6 @@ msg.balance,Not enough balance.
msg.trade,"Paid {0} {1}, received {2} {3}."
msg.deposit,Deposited {0} credits as CRC.
msg.cashout,Received {0} credits. Tax {1} credits.
msg.aml_light,Compliance review: {0} credits of tainted funds were withheld.
msg.aml_heavy,Compliance review: {0} credits of tainted funds were seized and reported to Security.
report.none,No mining report yet.
report.day,Night of day {0}
report.rigs,Rigs with power: {0} of {1}
@@ -50,6 +48,7 @@ report.burnt,Cards burnt out: {0}
report.price,PSC price: {0} credits
nightlog.mined,Mining rigs produced {0} PSC.
nightlog.burnt,A {0} burnt out.
nightlog.turbo,{0} turbo-boosted: mined {1} PSC right away.
coinpay.line,Deal. {0} {1}.
nightlog.coinpay,Customers paying in coin settled {0} credits; the coins went to the hot wallet.
nightlog.unpowered,{0} mining rig(s) or miner(s) had no power.
@@ -140,6 +139,7 @@ col.asset,Asset
col.amount,Amount
col.value,Value
col.avg,Avg. buy
col.dirty,Dirty
bank.cash.k,Credits
bank.rent.k,Rent
bank.rent.v,"{0}, due in {1} days"
@@ -151,13 +151,12 @@ stable.ok,Pegged
stable.depeg,Depegged
stable.warn,Warning: staking reserve coverage low
stable.spiral,Collapsing
aml.taint,Account taint share: {0}%
mixer.h,Mixer
mixer.ok,Status: operating normally
mixer.sanctioned,Status: sanctioned ({0} more day(s)). Coins withdrawn meanwhile are flagged.
mixer.sanctioned,Status: sanctioned ({0} more day(s)). Payouts due meanwhile are not cleaned and come back all dirty.
mixer.pending,Pending deposits
mixer.none,No pending deposits.
mixer.col.taint,Taint
mixer.col.taint,Dirty
mixer.col.ready,Ready
mixer.day,Day {0}
mixer.deposit,Deposit
@@ -168,11 +167,12 @@ mixer.go,Deposit to mixer
mixer.min,Deposits must be worth at least {0} credits.
mixer.done,Deposited {0} {1}; back on day {2}.
mixer.upgrade.title,Mixer
mixer.upgrade.desc,"Unlocks the mixer in the terminal's Gray Net page: deposit tainted coins, get them back days later with less taint. Cash-outs of freshly mixed coins are screened harder; during crackdowns the mixer may be sanctioned and coins withdrawn meanwhile are flagged."
mixer.upgrade.desc,"Unlocks the mixer in the terminal's Gray Net page: deposit dirty coins, get them back days later with fewer of them dirty. During crackdowns the mixer may be sanctioned, and payouts due meanwhile come back all dirty."
news.sanction.title,Mixer put on sanctions list
news.sanction.desc,Security has sanctioned a batch of mixing services. Funds withdrawn from mixers in the next few days will be treated as suspicious.
news.sanction.desc,Security has sanctioned a batch of mixing services. Funds withdrawn from mixers in the next few days will be treated as dirty money.
nightlog.mixer,Mixer returned {0} {1}; taint {2} -> {3}.
nightlog.mixer_flag," (withdrawn during sanctions; address flagged)"
nightlog.mixer_flag," (withdrawn during sanctions; all of it marked dirty)"
nightlog.mixer_flag.trap," (Security marks the withdrawn coins as dirty)"
nightlog.mixer_rug,The mixer's operator ran off with it: {0} {1} never came back.
web3.sub.trade,Trade
gray.sub.mixer,Mixer
@@ -247,7 +247,7 @@ black.kind.Contraband.3,Contraband (high)
black.kind.Tech,High-tech (markup)
nightlog.bm.ready,Black market: {0} arrived; a courier will come by once you open.
guide.web3.title,Web3 Handbook
guide.web3.text,"Bank and trading: the Bank page is read-only and shows cash, rent and the CRC in your Poinbase account. Poinbase's Funds page handles deposits and withdrawals; pick the coin first: CRC can go through the bank account or a wallet, every other coin only through a wallet. Depositing from the bank turns credits into CRC in your Poinbase account; withdrawing to the bank turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Withdrawing to a wallet needs a payee address and a few confirmations before it arrives; the gas fee is fixed at the normal tier and taken from the coin sent. A wrong address format or checksum is rejected; a correctly formatted address nobody uses means the coins are gone.¶Trading: PSC stands for Probably Stolen Coin, the coin mined by GPUs and miners. Poinbase spot has two pairs, PSC/CRC and SPARK/CRC, following the coin picked on the chart. Buys and sells are both entered as an amount of the traded coin; the preview shows the quote coin paid or received, the average price includes the fee and the slippage does not.¶The Web3 trade page can swap any two coins, always through CRC: with CRC on one side it uses one pool, otherwise two pools, with two fees and stacked slippage; the preview shows the amount received, the pools crossed and the loss. Stablecoin swaps (CRT or CRA against CRC) use stable pools: a {StableFee:%} fee and almost no slippage near the peg; during a depeg liquidity thins, and in a collapse it is pulled and slippage jumps.§Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint. A wallet is an item: drag it onto the trading terminal to load it. Open it on the terminal's Wallets page; connect and disconnect at the top right of the terminal. Trading on the Web3 page and the shop and mixer on the Gray Net page use the connected wallet; with no wallet or no connection they are view-only and every balance reads 0. Coin-paying customers pay into the connected wallet.¶Payments (shop, 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; deposits are sent from a wallet to Poinbase's deposit address (only PSC, CRC and SPARK; the compliant exchange takes neither the black market's CRT nor the Revolution's CRA, and the wallet blocks sending them to the deposit address) and withdrawals are started on Poinbase's Funds page. The Explorer on the Wallets page looks up transfers by address and shows only transfers that really happened: every movement in and out of your wallets (transfers, DEX swaps and other contract calls, miner payouts, withdrawals, coins others send you); Ponero is a privacy coin and never shows. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to {WalletMax}. The shop has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page; its coins are lost for good, and anything sent to its address afterwards is lost too.¶Transfers pay a gas fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested gas fees rise and slow transactions may be dropped from the mempool. Check the full address before sending.§Staking: CRA staked in the Revolution pool earns a daily rate that compounds into the stake; withdrawing means requesting first and waiting four days, with no interest while waiting and no way to take it early. CRA is partly collateralized: each CRA is backed by the CRC reserve the Revolution holds and by the balance token SPARK; reserve coverage and the collateral ratio are shown on the staking page. Interest is paid in new uncollateralized CRA, which dilutes reserve coverage, faster the more is staked; when coverage is too low the Revolution cuts the rate. The Revolution sometimes diverts the reserve, preceded by rumors: that is the warning. If coverage then falls below {CraCrashRatio:%} it collapses: CRA is worthless within three days and four days of unlocking is too slow; above that there is only a discount, and money from raiding the upper class slowly refills the reserve.¶Official minting: deposit CRC and SPARK (ratio per the current collateral ratio; the preview shows what is needed) to mint CRA, queued {CraMintDaysMin}-{CraMintDaysMax} days; the minted CRA goes straight into staking. Burning turns CRA back into CRC and SPARK, waiting as long as the unlock period; when the reserve is short the CRC share is cut by coverage. Queued mints and burns may be dishonored in full by the Revolution: the nightly chance is how far the CRA price is below 1 times {CraRugPerDepeg}; if the price is not below 1 it cLine truncated
guide.web3.text,"Bank and trading: the Bank page is read-only and shows cash, rent and the CRC in your Poinbase account. Poinbase's Funds page handles deposits and withdrawals; pick the coin first: CRC can go through the bank account or a wallet, every other coin only through a wallet. Depositing from the bank turns credits into CRC in your Poinbase account; withdrawing to the bank turns CRC back into credits with no screening (the Poinbase account only ever holds clean money). Withdrawing to a wallet needs a payee address and a few confirmations before it arrives; the gas fee is fixed at the normal tier and taken from the coin sent. A wrong address format or checksum is rejected; a correctly formatted address nobody uses means the coins are gone.¶Trading: PSC stands for Probably Stolen Coin, the coin mined by GPUs and miners. Poinbase spot has two pairs, PSC/CRC and SPARK/CRC, following the coin picked on the chart. Buys and sells are both entered as an amount of the traded coin; the preview shows the quote coin paid or received, the average price includes the fee and the slippage does not.¶The Web3 trade page can swap any two coins, always through CRC: with CRC on one side it uses one pool, otherwise two pools, with two fees and stacked slippage; the preview shows the amount received, the pools crossed and the loss. Stablecoin swaps (CRT or CRA against CRC) use stable pools: a {StableFee:%} fee and almost no slippage near the peg; during a depeg liquidity thins, and in a collapse it is pulled and slippage jumps.§Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and dirty coins (each coin tracks how many of it are dirty; see Taint). A wallet is an item: drag it onto the trading terminal to load it. Open it on the terminal's Wallets page; connect and disconnect at the top right of the terminal. Trading on the Web3 page and the shop and mixer on the Gray Net page use the connected wallet; with no wallet or no connection they are view-only and every balance reads 0. Coin-paying customers pay into the connected wallet.¶Payments (shop, 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; deposits are sent from a wallet to Poinbase's deposit address (only PSC, CRC and SPARK; the compliant exchange takes neither the black market's CRT nor the Revolution's CRA, and the wallet blocks sending them to the deposit address; only clean coins can be deposited, see Taint) and withdrawals are started on Poinbase's Funds page. The Explorer on the Wallets page looks up transfers by address and shows only transfers that really happened: every movement in and out of your wallets (transfers, DEX swaps and other contract calls, miner payouts, withdrawals, coins others send you); Ponero is a privacy coin and never shows. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to {WalletMax}. The shop has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page; its coins are lost for good, and anything sent to its address afterwards is lost too.¶Transfers pay a gas fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested gas fees rise and slow transactions may be dropped from the mempool. Check the full address before sending.§Staking: CRA staked in the Revolution pool earns a daily rate that compounds into the stake; withdrawing means requesting first and waiting four days, with no interest while waiting and no way to take it early. CRA is partly collateralized: each CRA is backed by the CRC reserve the Revolution holds and by the balance token SPARK; reserve coverage and the collateral ratio are shown on the staking page. Interest is paid in new uncollateralized CRA, which dilutes reserve coverage, faster the more is staked; when coverage is too low the Revolution cuts the rate. The Revolution sometimes diverts the reserve, preceded by rumors: that is the warning. If coverage then falls below {CraCrashRatio:%} it collapses: CRA is worthless within three days and four days of unlocking is too slow; above that there is only a discount, and money from raiding the upper class slowly refills the reserve.¶Official minting: deposit CRC and SPARK (ratio per the current collateral ratio; the preview shows what is needed) to mint CRA, queued {CraMintDaysMin}-{CraMintDaysMax} days; the minted CRA goes straight into staking. Burning turns CRA back into CRC and SPARK, waiting as long as the unlock period; when the reserve is short the CRC share is cut by coverage. Queued mints and burns may be dishonored in full by the Revolution: the nightly chanceLine truncated
courier.odd1,...Package is here.
courier.odd2,(His hand stays in his pocket.) Sign here.
courier.sign,Sign
@@ -360,10 +360,11 @@ hw.wallet.n,Wallet {0}
hw.review.own,Own wallet: {0}
hw.review.poinbase,Poinbase deposit address
poin.deposit,Deposit address: {0}
poin.deposit.clean,Clean coins {0} can deposit: {1}
wallets.count,Wallets {0}/{1}
wallets.col.name,Name
wallets.col.addr,Address
wallets.col.taint,Taint
wallets.col.taint,Dirty
wallets.open,Open
wallets.use,Connect
wallets.current,Connected
@@ -377,6 +378,7 @@ hw.paste,Paste
hw.copied,Address copied.
hw.copied.addr,Copied {0}
hw.balance.frozen," (frozen {0})"
hw.balance.dirty," ({0} dirty)"
wallets.destroy,Destroy
wallets.destroy.sure,Confirm
wallets.destroyed,Destroyed {0}: {1}
@@ -548,7 +550,7 @@ predict.purity.rust,Upper water
nightlog.predict.win,"Prediction right: ""{0}"", paid out {1}."
nightlog.predict.lose,"Prediction wrong: ""{0}"", stake {1} lost."
nightlog.predict.void,"Prediction void: ""{0}"", stake {1} refunded."
nightlog.predict.caught,"Insider bet caught: ""{0}"", stake and winnings {1} confiscated, fraud on record."
nightlog.predict.insider,"""{0}"" settled for {1}. Police suspect insider trading; {2} CRC of it is marked as dirty."
predict.tab.markets,Markets
predict.tab.intel,Intel
predict.acct.crt,{0}: CRT {1}
@@ -591,10 +593,10 @@ predict.brief.queued,{0}: operation scheduled
predict.brief.none,{0}: nothing to launch
predict.brief.can,{0}: can launch {1}
predict.clue.push,You moved the price {0}%: risky
nightlog.predict.manip,"Prediction ruled manipulation: ""{0}"", stake {1} confiscated, fraud on record."
nightlog.predict.manip,"""{0}"" settled for {1}. Regulators suspect you manipulated the price; {2} CRC of it is marked as dirty."
news.manip.title,Exchange probes unusual trading
news.manip.desc,Regulators report that someone moved the price with large spot orders and profited from derivatives and predictions; the funds were seized.
perp.manip,Profit {0} on close ruled price manipulation and seized; perp account frozen for {1} days.
news.manip.desc,Regulators report that someone moved the price with large spot orders and profited from derivatives and predictions; the accounts involved have been flagged.
perp.manip,Closed with profit {0}. Regulators suspect price manipulation; {1} fraud goes on your record.
web3.sub.ponzi,Yield
ponzi.h,High-yield scheme
ponzi.acct,Issuer wallet {0}: CRC {1}
@@ -685,7 +687,7 @@ scam.support.name,Poinbase Support
scam.support.1,"Hello, Poinbase support here. We detected an unusual login on your account and it has been flagged."
scam.support.2,Please send {0} CRC to the safe address {1} today for verification; it will be returned right after.
scam.support.3,"Otherwise the account will be frozen for good. I have to go, remember to send it."
predict.sold.caught,"Insider trading caught on sale: stake and profit confiscated, fraud on record."
predict.sold.caught,"Sold, received {0}. Police suspect insider trading; {1} CRC of it is marked as dirty."
predict.row.move," (last night {0} {1})"
predict.stats.h,History (avg opening price / actual rate)
predict.stats.row,"{0}: {1} markets, avg open {2}, happened {3}%"
@@ -696,6 +698,7 @@ predict.kind.Price,PSC price
predict.kind.Gas,Gas
predict.kind.Cra,CRA warning
predict.kind.Era,ASIC era
predict.kind.Trial,Tumbler trial
nightlog.predict.move,"Prediction ""{0}"" moved {1} -> {2}"
nightlog.predict.why," ({0})"
chart.empty,{0} daily chart: no history yet; recording from today.
@@ -707,7 +710,7 @@ news.story.serpico_rev.desc,"The Revolution reportedly obtained evidence of corr
news.story.serpico_bm.title,Black market strikes a quiet deal
news.story.serpico_bm.desc,"An internal Security document changed hands on the black market. Darknet settlement has been noticeably steadier since, and CRT redemptions have not slipped once."
news.story.serpico_sec.title,Security launches internal purge
news.story.serpico_sec.desc,Security announced a full investigation into internal corruption and tighter monitoring of money flows. Poinbase is tightening cash-out screening and mixing services have been named.
news.story.serpico_sec.desc,Security announced a full investigation into internal corruption and tighter monitoring of money flows; mixing services have been named.
news.story.eleanor_leak.title,Nutrifruit breeding data leaked
news.story.eleanor_leak.desc,"The lower-level paper published research that lets nutrifruit set seed again; Pastelle shares plunged. The bank custodying upper-level funds reports bad debts, and the Revolution is gaining ground."
news.story.eleanor_return.title,Pastelle recovers stolen data
@@ -752,6 +755,47 @@ news.tumbler.caught.desc,The mixer operator known as Tumbler was arrested in the
news.tumbler.sold.title,Mixing service changes hands
news.tumbler.sold.desc,"Word is the signing keys of a lower-level mixing service have fallen into black-market hands, and fees went up at once. Users fear for their funds."
nightlog.tumbler.gift,Tumbler took back the key drive and left {0} CRT as thanks (sent to {1}); mixer fees halved.
agent.name,Security Agent
agent.v1.1,Security. Sorry to bother you.
agent.v1.2,We're looking for someone who goes by Tumbler. He runs a mixing service on chain.
agent.v1.3,He's been seen around this area.
agent.ask,Has he been to your shop?
agent.choice.yes,[Tell him: a few times]
agent.choice.no,[Tell him: never seen him]
agent.yes1,Thanks for your cooperation. Security will remember it.
agent.no1,"Is that so. If anything comes to mind, you know where to find us."
agent.search.1,We meet again. We hear you're keeping something for that man.
agent.search.2,Routine search. Please cooperate.
agent.search.found,Found it. We're taking this.
agent.search.none,...Nothing. Sorry for the trouble.
tumbler.v4.seized.1,I heard Security searched your shop.
tumbler.v4.seized.2,They have the key now. Every user's records will be dug up.
tumbler.v4.seized.3,"Whatever you told them, I don't want to know anymore."
nightlog.tumbler.leak,Security used Tumbler's signing key to rebuild the mixing records: {0} payouts had {1} of taint washed off; {2} fraud goes on your record.
nightlog.tumbler.leak.none,Security used Tumbler's signing key to rebuild the mixing records. You have not withdrawn anything from the mixer recently.
nightlog.tumbler.note,"A note was slipped under the door: ""You owe me, and I'll take it back. —T"""
nightlog.tumbler.tip,Message from Tumbler: Security may go after the mixing services tomorrow.
news.tumbler.leak.title,Security rebuilds mixer records
news.tumbler.leak.desc,Security says it has obtained the signing key of a mixing service and will use it to trace funds laundered through the service over the past month.
news.trial.pro.title,"Tumbler trial, day {0}: prosecution gains ground"
news.trial.def.title,"Tumbler trial, day {0}: defense gains ground"
news.trial.pro.desc1,"The prosecution presented server logs from the mixing node, saying they match the defendant's signatures one by one."
news.trial.pro.desc2,A former partner testified that the defendant knew clients' funds came from the black market.
news.trial.pro.desc3,The prosecution showed transfers between the defendant's wallet and several scam addresses.
news.trial.def.desc1,"The defense pointed to flaws in how evidence was collected when the node was seized, saying some logs may have been altered."
news.trial.def.desc2,A defense expert testified that the mixing contract is open-source code and the defendant cannot control who uses it.
news.trial.def.desc3,"The prosecution's key witness changed his story on the stand, saying he could not remember whether the defendant knew."
news.trial.convict.title,Tumbler found guilty
news.trial.convict.desc,The court found the mixer operator known as Tumbler guilty of money laundering. Security says the mixing service will not reopen.
news.trial.acquit.title,Tumbler acquitted
news.trial.acquit.desc,"Citing insufficient evidence, the court acquitted the mixer operator known as Tumbler and released him. He left the courthouse without answering reporters' questions."
news.tumbler.bmrug.title,Mixing service vanishes with funds
news.tumbler.bmrug.desc,"A mixing service taken over by the black market suddenly stopped paying out, and the funds in its contract were moved away. Users on the forums say they lost everything."
mixer.brand,Vortex
news.mixer.rebrand.title,New mixing service {0} goes live
news.mixer.rebrand.desc,"A mixing service called {0} went live recently, claiming no logs and no backdoors. Some have noticed that its contract looks very much like the service shut down earlier."
predict.q.trial,{0}: Tumbler found guilty
predict.clue.trial,"Trial reports: prosecution ahead {0} days, defense ahead {1} days"
mixer.status.closed,Status: shut down
yield.sub.pools,Yield pools
yield.sub.mine,Issue scheme
@@ -963,18 +1007,84 @@ news.wipe.down.title,"{0} falls {1}% in a day, {2} traders liquidated"
news.wipe.down.desc,"Over the past day {1} long positions on {0} perps were liquidated, {2} credits in total. The Poinbase insurance fund stands at {3} credits."
news.wipe.up.title,"{0} jumps {1}% in a day, {2} traders liquidated"
news.wipe.up.desc,"Over the past day {1} short positions on {0} perps were liquidated, {2} credits in total. The Poinbase insurance fund stands at {3} credits."
hot.name,Seller
hot.intro.CRT,"Things are tense lately and this CRT is too hot to hold. I need cash, fast."
hot.intro.PSC,"A mining farm's wallet changed hands last night. The PSC is with me now, and I only take cash."
hot.intro.PONERO,"Ransom money, in Ponero. Nobody can trace it. Cash only."
hot.offer,"{0} {1}, worth {2} at today's price. Give me {3} in cash and the coins are yours right now."
hot.choice.yes,Take it
hot.choice.no,Pass
hot.say.yes,Deal.
hot.say.no,I'm not touching that.
hot.done,Coins sent. We never met.
hot.refused,Plenty of others will take it.
hot.nocash,You can't even put up the cash? Forget it.
hot.nowallet,"No wallet connected, so where would the coins go? Forget it."
hot.sting,Don't move. Security. Buying stolen funds: the cash is confiscated and this goes on your record.
otc.name,OTC client
otc.intro.CRT,"Things are tense lately and this CRT is too hot to hold. I need cash, fast."
otc.intro.PSC,"A mining farm's wallet changed hands last night. The PSC is with me now, and I only take cash."
otc.intro.PONERO,"Ransom money, in Ponero. Nobody can trace it. Cash only."
otc.offer,"{0} {1}, worth {2} at today's price, sent from {4}. {3} in cash for all of it, or take part of it."
otc.intro.sell,Got some {0} I want to turn into cash. Check where it came from if you like.
otc.buy.ask,"I want {0} {1}, worth {2} at today's price. {3} in cash for all of it, or sell me part of it."
otc.intro.buy,"Heard you trade coins here. I'm buying, cash."
otc.say.trade,Let's do it at the terminal.
otc.wait,"Fine, I'll wait."
otc.paid,Got it. Here's your cash.
tab.otc,Counter
otc.h,Counter trade
otc.k.side,Client
otc.side.sell,Selling {0}
otc.side.buy,Buying {0}
otc.k.qty,Amount
otc.k.value,Market value
otc.k.ask,Asking (all)
otc.k.bid,Bid (all)
otc.k.unit,Unit price
otc.k.addr,Client address: {0}
otc.lookup,Look up
otc.preview.take,"Pay {0} cash, receive {1} {2}"
otc.preview.give,"Send {0} {1}, receive {2} cash"
otc.deal,Deal
otc.cancel,Pass
otc.msg.gone,The client has left.
otc.msg.cash,Not enough cash.
otc.msg.sting,"The client was undercover: {0} cash confiscated, fencing on record."
otc.msg.bought,"Received {0} {1} ({3} of them dirty), paid {2} cash."
otc.msg.sold,"Sent {0} {1}, received {2} cash."
gray.sub.own,My mixer
ownmix.h,My mixer
ownmix.status.closed,Closed (rug pulled)
ownmix.status.seized,"Seized, closed until day {0}"
ownmix.status.open,Open
ownmix.status.paused,Paused
ownmix.k.fee,Fee
ownmix.k.rating,Reputation (taint left on payouts)
ownmix.k.heat,Heat
ownmix.heat.0,Low
ownmix.heat.1,Medium
ownmix.heat.2,High
ownmix.k.anon,Anonymity set
ownmix.k.fees,Fees earned
ownmix.col.capital,Capital
ownmix.col.earned,Earnings
ownmix.col.pending,Client coins
ownmix.open,Open
ownmix.pause,Pause
ownmix.inject,Fund
ownmix.withdraw,Withdraw
ownmix.free,Withdrawable: {0} {1}
ownmix.err,More than the withdrawable amount.
ownmix.injected,Funded {0} {1}.
ownmix.withdrawn,Withdrew {0} {1}.
ownmix.withdraw.sum,Withdraw {0} {1} from my mixer
pay.ownmix,My mixer funding {0} {1}
ownmix.rug,Pull the rug ({0})
ownmix.rug.confirm,Click again to take {0}
ownmix.rug.sum,Take all funds from my mixer
ownmix.rugged,Took {0}; the mixer is closed.
nightlog.ownmix.in,"My mixer: {0} deposits, {1} in total, fees {2}."
nightlog.ownmix.sting,A deposit at my mixer came from an undercover officer; fencing on record.
nightlog.ownmix.seized,"Security seized my mixer: the pool of {0} was confiscated, fencing on record."
news.ownmix.seized.title,Security shuts down a mixing service
news.ownmix.seized.desc,Security has shut down a mixing service operating in the lower levels and confiscated all its funds. The operator has not been identified.
news.ownmix.rug.title,Mixing service vanishes with funds
news.ownmix.rug.desc,"A mixing service in the lower levels shut down overnight, and all user funds in transit are gone."
pay.otc,OTC client {0} {1} (cash {2})
otc.upgrade.title,OTC Desk
otc.upgrade.desc,"Adds a Black Lamp target: OTC clients. Some come to sell coins of unknown origin, some come to buy coins; all deals are settled in cash. Includes one Black Lamp."
otc.say.no,I'll pass on this one.
otc.done,Coins sent. We never met.
otc.refused,Then I'll find someone else.
otc.nocash,You can't even put up the cash? Forget it.
otc.nowallet,"No wallet connected, so where would the coins go? Forget it."
otc.sting,Don't move. Security. Buying stolen funds: the cash is confiscated and this goes on your record.
hw.err.poinbase,Poinbase does not accept {0}.
hw.err.tainted,{0} of this wallet's {1} are dirty and Poinbase takes only clean coins: at most {2} {1}.
+142 -32
View File
@@ -22,7 +22,7 @@ tab.report,Reporte
wallet.line,{0}: {1} (aprox. {2} créditos)
wallet.empty,La billetera está vacía.
wallet.total,Valor total: {0} créditos
wallet.taint,Valor contaminado: {0} créditos
wallet.taint,Monedas contaminadas: {0} créditos
trade.amount,Cantidad
trade.deposit,Depositar créditos como CRC
trade.withdraw,Retirar CRC como créditos
@@ -38,8 +38,6 @@ msg.balance,Saldo insuficiente.
msg.trade,Pagado: {0} {1}; recibido: {2} {3}.
msg.deposit,Se depositaron {0} créditos como CRC.
msg.cashout,Se recibieron {0} créditos. Impuesto: {1} créditos.
msg.aml_light,Revisión de cumplimiento: se retuvieron {0} créditos de fondos contaminados.
msg.aml_heavy,Revisión de cumplimiento: se confiscaron {0} créditos de fondos contaminados y se reportó a Seguridad.
report.none,Aún no hay reporte de minería.
report.day,Noche del día {0}
report.rigs,Plataformas con energía: {0} de {1}
@@ -50,6 +48,7 @@ report.burnt,Tarjetas quemadas: {0}
report.price,Precio de PSC: {0} créditos
nightlog.mined,Las plataformas de minería produjeron {0} PSC.
nightlog.burnt,Se quemó una {0}.
nightlog.turbo,{0} con turbo: minó {1} PSC al instante.
coinpay.line,Trato hecho. {0} {1}.
nightlog.coinpay,Los pagos de clientes en monedas liquidaron {0} créditos; las monedas fueron a la billetera caliente.
nightlog.unpowered,{0} plataforma(s) de minería o minero(s) (máquina) sin energía.
@@ -140,6 +139,7 @@ col.asset,Activo
col.amount,Cantidad
col.value,Valor
col.avg,Prom. compra
col.dirty,Contaminadas
bank.cash.k,Créditos
bank.rent.k,Renta
bank.rent.v,"{0}, vence en {1} día(s)"
@@ -151,13 +151,12 @@ stable.ok,Anclado
stable.depeg,Desanclado
stable.warn,Advertencia: cobertura de reserva del staking baja
stable.spiral,En colapso
aml.taint,Porcentaje de contaminación de la cuenta: {0}%
mixer.h,Mezclador
mixer.ok,Estado: en funcionamiento normal
mixer.sanctioned,Estado: sancionado (días restantes: {0}). Las monedas retiradas mientras tanto se marcarán.
mixer.sanctioned,Estado: bajo sanción (días restantes: {0}). Los pagos que venzan en este período no se limpian y se devuelven todos como contaminados.
mixer.pending,Depósitos pendientes
mixer.none,No hay depósitos pendientes.
mixer.col.taint,Contaminación
mixer.col.taint,Contaminadas
mixer.col.ready,Listo
mixer.day,Día {0}
mixer.deposit,Depósito
@@ -168,11 +167,12 @@ mixer.go,Depositar en el mezclador
mixer.min,Los depósitos deben valer al menos {0} créditos.
mixer.done,Se depositaron {0} {1}; retorno el día {2}.
mixer.upgrade.title,Mezclador
mixer.upgrade.desc,"Desbloquea el mezclador en la página Red gris de la terminal: deposita monedas contaminadas y recupéralas días después con menos contaminación. El retiro de monedas recién mezcladas pasa por una revisión más estricta; durante las represiones, el mezclador puede ser sancionado y las monedas retiradas mientras tanto quedarán marcadas."
mixer.upgrade.desc,"Desbloquea el mezclador en la página de la Red gris de la terminal: deposita monedas contaminadas y recupéralas días después con menos unidades contaminadas. Durante las redadas el mezclador puede ser sancionado, y los pagos que venzan en ese período volverán totalmente contaminados."
news.sanction.title,Mezclador incluido en la lista de sanciones
news.sanction.desc,Seguridad sancionó un lote de servicios de mezcla. Los fondos retirados de mezcladores en los próximos días se tratarán como sospechosos.
news.sanction.desc,Seguridad ha sancionado varios servicios de mezcla. Los fondos retirados de mezcladores en los próximos días se considerarán fondos contaminados.
nightlog.mixer,El mezclador devolvió {0} {1}; contaminación {2} -> {3}.
nightlog.mixer_flag," (retirado durante las sanciones; dirección marcada)"
nightlog.mixer_flag," (retirado durante las sanciones; todo marcado como contaminado)"
nightlog.mixer_flag.trap," (Seguridad marca las monedas retiradas como contaminadas)"
nightlog.mixer_rug,El operador del mezclador se fugó con el dinero: {0} {1} nunca regresaron.
web3.sub.trade,Comercio
gray.sub.mixer,Mezclador
@@ -247,7 +247,7 @@ black.kind.Contraband.3,Contrabando (alto)
black.kind.Tech,Alta tecnología (con sobreprecio)
nightlog.bm.ready,Mercado negro: {0} ha llegado; un mensajero pasará en cuanto abras.
guide.web3.title,Manual de Web3
guide.web3.text,"Banco y comercio: la página del banco es de solo lectura y muestra efectivo, alquiler y el CRC de la cuenta de Poinbase. La página Fondos de Poinbase gestiona depósitos y retiros; primero se elige la moneda: el CRC puede ir por la cuenta bancaria o la billetera, las demás monedas solo por la billetera. Depositar desde el banco convierte créditos en CRC en tu cuenta de Poinbase; retirar al banco convierte CRC nuevamente en créditos y pasa por una revisión de cumplimiento que evalúa la contaminación de la cuenta de Poinbase. Retirar a una billetera exige la dirección de destino y varias confirmaciones hasta acreditarse; la comisión de gas es fija, nivel normal, y se descuenta de la moneda enviada. Si el formato de la dirección o el dígito de control es incorrecto, se rechaza; si el formato es válido pero nadie usa esa dirección, las monedas enviadas se pierden.¶Comercio: PSC significa Probably Stolen Coin, la moneda minada por tarjetas gráficas y mineros (máquinas). El mercado spot de Poinbase tiene dos pares, PSC/CRC y SPARK/CRC, según la moneda elegida en el gráfico. Tanto las compras como las ventas se introducen como cantidad de la moneda negociada; la vista previa muestra la moneda de cotización pagada o recibida, el precio medio incluye la comisión y el deslizamiento no.¶La página de comercio de Web3 permite intercambiar cualquier par de monedas, siempre a través de CRC: si CRC está en un extremo se usa un fondo, si no, dos fondos, con dos comisiones y deslizamiento acumulado; la vista previa muestra la cantidad obtenida, los fondos recorridos y la pérdida. Los canjes entre stablecoins (CRT o CRA contra CRC) usan fondos estables: comisión de {StableFee:%} y casi sin deslizamiento cerca de la paridad; durante una pérdida de paridad la liquidez se reduce, y en un colapso se retira y el deslizamiento se dispara.§Billeteras de hardware: las monedas en cadena residen en billeteras de hardware PLEDGER, cada una con sus propios saldos y contaminación. Una billetera es un objeto: arrástrala a la terminal de comercio para cargarla. Ábrela en la página Billeteras de la terminal; conéctala y desconéctala arriba a la derecha de la terminal. El comercio de la página Web3, la tienda y el mezclador de la página Red gris usan la billetera conectada; sin billetera o sin conexión, son solo de lectura y todos los saldos marcan 0. Los clientes que pagan con monedas pagan a la billetera conectada.¶Los pagos (tienda, mezclador, mostrador de Ponero, tarifas de publicación) comienzan como una factura en la terminal; ingresa la dirección del destinatario en la billetera y mantén presionado ✓ para firmar. Los dos últimos caracteres de una dirección son una suma de verificación, por lo que un error tipográfico de un carácter suele detectarse; de vez en cuando resulta válido y las monedas van a una dirección que nadie usa. Los intercambios en DEX también requieren firmar en la billetera.¶Poinbase es una cuenta de custodia; los depósitos se envían desde la billetera a la dirección de depósito de Poinbase (solo PSC, CRC y SPARK; la plataforma regulada no acepta el CRT del mercado negro ni el CRA de la Revolución, y la billetera bloquea su envío a la dirección de depósito) y los retiros se inician en la página Fondos de Poinbase. El Explorador de la página Billeteras permite consultar transferencias por dirección y solo muestra transferencias realmente ocurridas: cada entrada y salida de tus billeteras (transferencias, canjes en DEX y otras llamadas a contratos, ingresos de mineros, retiros, monedas que otros te envían); Ponero es una moneda de privacidad y nunca aparece. El Dr. Jackson vende más billeteras de hardware; arrastra una a la terminal de comercio para agregarla, hasta {WalletMax}. La tienda tiene opciones más baratas. Una billetera que ya no desees puede destruirse en la página Billeteras; sus monedas se pierden para siempre, y todo lo que se envíe después a su dirección también.¶Las transferencias pagan una comisión de gas: lenta, normal o rápida, y los precios del gas suben y bajan en aproximadamente una semana; cuando la red se congestiona, las comisiones de gas suben y las transacciones lentas pueden ser descartadas del mempool. Verifica la dirección completa antes de enviar.§Staking: el CRA en staking en el fondo de la Revolución genera una tasa diaria que se capitaliza en el monto; retirar implica solicitarlo primero y esperar cuatro días, sin intereses durante la espera y sin forma de retirarlo antes. CRA está parcialmente colateralizado: cada CRA está respaldado por la reserva de CRC de la Revolución y por el token de balance SPARK; la cobertura de la reserva y la tasa de colateralización aparecen en la página de staking. Los intereses se pagan con CRA nuevo sin colateral, lo que diluye la cobertura de la reserva, más rápido cuanto más staking haya; si la cobertura es demasiado baja, la Revolución recorta la Line truncated
guide.web3.text,"Banco y comercio: la página del Banco es de solo lectura y muestra el efectivo, el alquiler y el CRC en tu cuenta de Poinbase. La página de Fondos de Poinbase gestiona depósitos y retiros; primero elige la moneda: CRC puede operar mediante cuenta bancaria o billetera, mientras que cualquier otra moneda solo puede hacerlo mediante billetera. Depositar desde el banco convierte créditos en CRC dentro de tu cuenta de Poinbase; retirar al banco vuelve a convertir CRC en créditos sin revisión (la cuenta de Poinbase solo contiene dinero limpio). Retirar a una billetera requiere una dirección de destinatario y varias confirmaciones antes de llegar; la tarifa de Gas es fija en nivel normal y se descuenta de la moneda enviada. Un formato de dirección o suma de verificación incorrecto se rechaza; si la dirección tiene el formato correcto pero nadie la usa, las monedas se pierden.¶Comercio: PSC son las siglas de Probably Stolen Coin, la moneda extraída por tarjetas gráficas y mineros. El mercado spot de Poinbase tiene dos pares, PSC/CRC y SPARK/CRC, según la moneda seleccionada en el gráfico. Las compras y ventas se ingresan como una cantidad de la moneda negociada; la vista previa muestra la moneda de cotización pagada o recibida, el precio promedio incluye la comisión y el deslizamiento no.¶La página de comercio Web3 permite intercambiar cualquier par de monedas, siempre a través de CRC: con CRC en un extremo se usa un pool; de lo contrario, dos pools, con dos comisiones y deslizamiento acumulado; la vista previa muestra la cantidad recibida, los pools cruzados y la pérdida. Los intercambios entre monedas estables (CRT o CRA frente a CRC) utilizan pools estables: comisión del {StableFee:%} y casi nulo deslizamiento cerca de la paridad; durante una pérdida de paridad la liquidez disminuye, y ante un colapso se retira por completo disparando el deslizamiento.§Billeteras de hardware: las monedas en cadena residen en billeteras de hardware PLEDGER, cada una con sus propios saldos y monedas contaminadas (cada moneda registra cuántas unidades están contaminadas; consulta Contaminación). Una billetera es un objeto: arrástrala a la terminal de comercio para cargarla. Ábrela en la página de Billeteras de la terminal; conéctala y desconéctala en la esquina superior derecha de la terminal. El comercio en la página Web3 y la tienda y el mezclador en la página de la Red gris usan la billetera conectada; sin billetera o sin conexión son de solo lectura y todos los saldos marcan 0. Los clientes que pagan con monedas depositan en la billetera conectada.¶Los pagos (tienda, mezclador, mostrador de Ponero, tarifas de listado) comienzan como una factura en la terminal; ingresa la dirección del destinatario en la billetera y mantén presionado ✓ para firmar. Los dos últimos caracteres de una dirección corresponden a una suma de verificación, por lo que un error de un solo carácter suele detectarse; de vez en cuando resulta ser válido por casualidad y las monedas van a una dirección que nadie usa. Los intercambios en DEX también requieren una firma en la billetera.¶Poinbase es una cuenta de custodia; los depósitos se envían desde una billetera a la dirección de depósito de Poinbase (solo PSC, CRC y SPARK; el intercambio regulado no acepta el CRT del Mercado negro ni el CRA de la Revolución, y la billetera bloquea su envío a la dirección de depósito; solo pueden depositarse monedas limpias, consulta Contaminación) y los retiros se inician en la página de Fondos de Poinbase. El Explorador en la página de Billeteras consulta transferencias por dirección y solo muestra las transacciones reales: cada movimiento entrante y saliente de tus billeteras (transferencias, intercambios en DEX y otras llamadas de contrato, pagos de mineros, retiros, monedas enviadas por otros); Ponero es una moneda de privacidad y nunca se muestra. El Dr. Jackson vende más billeteras de hardware; arrastra una a la terminal de comercio para agregarla, hasta un máximo de {WalletMax}. La tienda tiene opciones más económicas. Una billetera que ya no desees puede destruirse en la página de Billeteras; sus monedas se perderán para siempre, y todo lo que se envíe a su dirección después también se perderá.¶Las transferencias pagan una tarifa de Gas: lenta, normal o rápida, y los precios del Gas suben y bajan en ciclos de aproximadamente una semana; cuando la red está congestionada las tarifas de Gas aumentan y las transacciones lentas pueden ser eliminadas de la mempool. Revisa la dirección completa antes de enviar.§Staking: el CRA en staking en el fondo de la Revolución genera una tasa diaria que se capitaliza en el monto; retirar implica solicitarlo primero y esperar cuatro días, sin intereses durante la espera y sin forma de retirarlo antes. CRA está parcialmente colateralizado: cada CRA está respaldado por la reserva de CRC de la Revolución y por el token de balance SPARK; la Line truncated
courier.odd1,...Llegó el paquete.
courier.odd2,(Mantiene la mano en el bolsillo). Firma aquí.
courier.sign,Firmar
@@ -360,10 +360,11 @@ hw.wallet.n,Billetera {0}
hw.review.own,Billetera propia: {0}
hw.review.poinbase,Dirección de depósito de Poinbase
poin.deposit,Dirección de depósito: {0}
poin.deposit.clean,Monedas limpias que {0} puede depositar: {1}
wallets.count,Billeteras {0}/{1}
wallets.col.name,Nombre
wallets.col.addr,Dirección
wallets.col.taint,Contaminación
wallets.col.taint,Sucias
wallets.open,Abrir
wallets.use,Conectar
wallets.current,Conectada
@@ -377,6 +378,7 @@ hw.paste,Pegar
hw.copied,Dirección copiada.
hw.copied.addr,Copiado: {0}
hw.balance.frozen," (congelado: {0})"
hw.balance.dirty," (sucias: {0})"
wallets.destroy,Destruir
wallets.destroy.sure,Confirmar
wallets.destroyed,Se destruyó {0}: {1}
@@ -548,7 +550,7 @@ predict.purity.rust,Agua del nivel superior
nightlog.predict.win,"Predicción acertada: ""{0}""; se pagaron {1}."
nightlog.predict.lose,"Predicción fallida: ""{0}""; perdiste la apuesta de {1}."
nightlog.predict.void,"Predicción anulada: ""{0}""; se reembolsó la apuesta de {1}."
nightlog.predict.caught,"Apuesta con información privilegiada detectada: ""{0}""; se confiscaron la apuesta y las ganancias por {1}, registro por fraude."
nightlog.predict.insider,"""{0}"" se liquidó por {1}. La policía sospecha información privilegiada; {2} CRC de esa cantidad se marcaron como sucios."
predict.tab.markets,Mercados
predict.tab.intel,Información
predict.acct.crt,{0}: CRT {1}
@@ -591,10 +593,10 @@ predict.brief.queued,{0}: operación programada
predict.brief.none,{0}: nada que iniciar
predict.brief.can,{0}: puede iniciar {1}
predict.clue.push,Moviste el precio un {0}%: riesgoso
nightlog.predict.manip,"Predicción clasificada como manipulación: ""{0}""; apuesta de {1} confiscada, registro por fraude."
nightlog.predict.manip,"""{0}"" se liquidó por {1}. Los reguladores sospechan manipulación de precio; {2} CRC de esa cantidad se marcaron como sucios."
news.manip.title,El exchange investiga operaciones inusuales
news.manip.desc,Los reguladores informan que alguien manipuló el precio con grandes órdenes al contado para lucrar con derivados y predicciones; los fondos fueron incautados.
perp.manip,Ganancia de {0} al cerrar dictaminada como manipulación de precio y confiscada; cuenta de perpetuos congelada por {1} día(s).
news.manip.desc,Los reguladores informan que alguien movió el precio con grandes órdenes al contado y obtuvo ganancias con derivados y predicciones; las cuentas involucradas han sido marcadas.
perp.manip,Cierre con ganancia de {0}. Los reguladores sospechan manipulación de precio; se registra {1} de fraude en tu expediente.
web3.sub.ponzi,Rendimiento
ponzi.h,Esquema Ponzi
ponzi.acct,Billetera emisora {0}: CRC {1}
@@ -685,7 +687,7 @@ scam.support.name,Soporte de Poinbase
scam.support.1,"Hola, soporte de Poinbase por aquí. Detectamos un inicio de sesión inusual en tu cuenta y fue marcada."
scam.support.2,Envía {0} CRC hoy a la dirección segura {1} para verificación; se devolverá inmediatamente después.
scam.support.3,"De lo contrario, la cuenta quedará congelada para siempre. Me tengo que ir, no olvides enviarlo."
predict.sold.caught,"Información privilegiada detectada al vender: apuesta y ganancias confiscadas, fraude registrado."
predict.sold.caught,"Vendido, recibiste {0}. La policía sospecha información privilegiada; {1} CRC de esa cantidad se marcaron como sucios."
predict.row.move," (anoche {0} {1})"
predict.stats.h,Historial (precio prom. apertura / tasa real)
predict.stats.row,"{0}: {1} mercados, prom. apertura {2}, ocurrió {3}%"
@@ -696,6 +698,7 @@ predict.kind.Price,Precio de PSC
predict.kind.Gas,Gas
predict.kind.Cra,Alerta de CRA
predict.kind.Era,Era ASIC
predict.kind.Trial,Juicio a Tumbler
nightlog.predict.move,"Predicción ""{0}"" cambió {1} -> {2}"
nightlog.predict.why," ({0})"
chart.empty,Gráfico diario de {0}: sin historial aún; registrando desde hoy.
@@ -707,7 +710,7 @@ news.story.serpico_rev.desc,"Según se informa, la Revolución obtuvo pruebas de
news.story.serpico_bm.title,El Mercado negro llega a un acuerdo discreto
news.story.serpico_bm.desc,"Un documento interno de Seguridad cambió de manos en el Mercado negro. La liquidación en la Darknet ha sido notablemente más estable desde entonces, y los canjes de CRT no han fallado ni una sola vez."
news.story.serpico_sec.title,Seguridad inicia una purga interna
news.story.serpico_sec.desc,Seguridad anunció una investigación exhaustiva sobre corrupción interna y mayor control del flujo de dinero. Poinbase endurece la revisión de retiros y se han señalado los servicios de mezcla.
news.story.serpico_sec.desc,Seguridad anunció una investigación exhaustiva sobre la corrupción interna y un mayor control del flujo de dinero; los servicios de mezclador fueron señalados.
news.story.eleanor_leak.title,Se filtran datos de cultivo de nutrifrutas
news.story.eleanor_leak.desc,El periódico del Nivel inferior publicó una investigación que permite a la nutrifruta volver a dar semillas; las acciones de Pastelle se desplomaron. El banco custodio de fondos del Nivel superior reporta deudas incobrables y la Revolución gana terreno.
news.story.eleanor_return.title,Pastelle recupera datos robados
@@ -752,6 +755,47 @@ news.tumbler.caught.desc,El operador del mezclador conocido como Tumbler fue arr
news.tumbler.sold.title,Servicio de mezcla cambia de manos
news.tumbler.sold.desc,Se dice que las claves de firma de un servicio de mezcla del nivel inferior han caído en manos del mercado negro y las tarifas subieron de inmediato. Los usuarios temen por sus fondos.
nightlog.tumbler.gift,Tumbler recuperó la unidad de claves y dejó {0} CRT como agradecimiento (enviados a {1}); las tarifas del mezclador se redujeron a la mitad.
agent.name,Agente de Seguridad
agent.v1.1,Seguridad. Perdón por la molestia.
agent.v1.2,Buscamos a alguien conocido como Tumbler. Opera un servicio de mezclador en la cadena.
agent.v1.3,Se le ha visto por esta zona.
agent.ask,¿Ha estado en tu tienda?
agent.choice.yes,[Decirle: varias veces]
agent.choice.no,[Decirle: nunca lo vi]
agent.yes1,Gracias por cooperar. Seguridad lo recordará.
agent.no1,"¿Ah, sí? Si recuerdas algo, ya sabes dónde encontrarnos."
agent.search.1,Nos volvemos a ver. Nos enteramos de que le estás guardando algo a ese sujeto.
agent.search.2,"Registro de rutina. Coopera, por favor."
agent.search.found,Lo encontramos. Nos llevamos esto.
agent.search.none,...Nada. Perdón por la molestia.
tumbler.v4.seized.1,Me enteré de que Seguridad registró tu tienda.
tumbler.v4.seized.2,Ahora tienen la clave. Van a desenterrar los registros de todos los usuarios.
tumbler.v4.seized.3,"Lo que sea que les hayas dicho, ya no quiero saberlo."
nightlog.tumbler.leak,"Seguridad usó la clave de firma de Tumbler para reconstruir los registros de mezcla: pagos: {0}, con {1} de contaminación lavada; se registra {2} de fraude en tu expediente."
nightlog.tumbler.leak.none,Seguridad usó la clave de firma de Tumbler para reconstruir los registros de mezcla. No has retirado nada del mezclador recientemente.
nightlog.tumbler.note,"Deslizaron una nota por debajo de la puerta: ""Me debes, y lo voy a recuperar. —T"""
nightlog.tumbler.tip,Mensaje de Tumbler: puede que Seguridad inspeccione los servicios de mezclador mañana.
news.tumbler.leak.title,Seguridad reconstruye registros del mezclador
news.tumbler.leak.desc,Seguridad afirma haber obtenido la clave de firma de un servicio de mezclador y la usará para rastrear los fondos lavados mediante este durante el último mes.
news.trial.pro.title,"Juicio a Tumbler, día {0}: la fiscalía gana terreno"
news.trial.def.title,"Juicio a Tumbler, día {0}: la defensa gana terreno"
news.trial.pro.desc1,"La fiscalía presentó registros del servidor del nodo del mezclador, asegurando que coinciden una a una con las firmas del acusado."
news.trial.pro.desc2,Un antiguo socio testificó que el acusado sabía que los fondos de los clientes provenían del Mercado negro.
news.trial.pro.desc3,La fiscalía mostró transferencias entre la billetera del acusado y varias direcciones de estafas.
news.trial.def.desc1,"La defensa señaló irregularidades en la recolección de pruebas durante la incautación del nodo, alegando que algunos registros pudieron haber sido alterados."
news.trial.def.desc2,Un perito de la defensa testificó que el contrato del mezclador es de código abierto y que el acusado no puede controlar quién lo utiliza.
news.trial.def.desc3,"El testigo clave de la fiscalía cambió su versión en el estrado, declarando que no recordaba si el acusado estaba al tanto."
news.trial.convict.title,Tumbler declarado culpable
news.trial.convict.desc,El tribunal declaró culpable de lavado de dinero al operador del mezclador conocido como Tumbler. Seguridad asegura que el servicio de mezclador no volverá a operar.
news.trial.acquit.title,Tumbler absuelto
news.trial.acquit.desc,"Alegando falta de pruebas, el tribunal absolvió y puso en libertad al operador del mezclador conocido como Tumbler. Se retiró del juzgado sin responder a las preguntas de la prensa."
news.tumbler.bmrug.title,Servicio de mezclador desaparece con los fondos
news.tumbler.bmrug.desc,"Un servicio de mezclador tomado por el Mercado negro detuvo repentinamente los pagos y los fondos de su contrato fueron retirados. En los foros, los usuarios denuncian haberlo perdido todo."
mixer.brand,Vortex
news.mixer.rebrand.title,Nuevo servicio de mezclador {0} en línea
news.mixer.rebrand.desc,"Un servicio de mezclador llamado {0} entró en línea recientemente, afirmando no tener registros ni puertas traseras. Varios notaron que su contrato es muy similar al del servicio clausurado anteriormente."
predict.q.trial,{0}: Tumbler declarado culpable
predict.clue.trial,"Informes del juicio: fiscalía en ventaja: {0} d, defensa en ventaja: {1} d"
mixer.status.closed,Estado: cerrado
yield.sub.pools,Fondos de rendimiento
yield.sub.mine,Emitir esquema
@@ -911,7 +955,7 @@ mint.sum.in,Acuñar {0} CRA con {1} CRC y {2} SPARK ({3}–{4} días)
mint.sum.out,Quemar {0} CRA por CRC y SPARK ({1} días)
mint.done.in,Orden de acuñación enviada; vence el día {0}.
mint.done.out,Orden de quema enviada; vence el día {0}.
nightlog.cra.minted,{0} CRA acuñados, ya en staking ({1}).
nightlog.cra.minted,"{0} CRA acuñados, ya en staking ({1})."
nightlog.cra.burned,"{0} CRA quemados: +{1} CRC, +{2} SPARK ({3})."
predict.clue.subsidy,"Reserva al {0}%; a cero, la advertencia es segura"
air.noapprove,sin permiso sobre tokens
@@ -963,18 +1007,84 @@ news.wipe.down.title,{0} cae un {1}% en un día: {2} traders liquidados
news.wipe.down.desc,"En el último día se liquidaron {1} posiciones largas en perpetuos de {0}, por un total de {2} créditos. El fondo de seguro de Poinbase queda en {3} créditos."
news.wipe.up.title,{0} sube un {1}% en un día: {2} traders liquidados
news.wipe.up.desc,"En el último día se liquidaron {1} posiciones cortas en perpetuos de {0}, por un total de {2} créditos. El fondo de seguro de Poinbase queda en {3} créditos."
hot.name,Vendedor
hot.intro.CRT,"Las cosas están tensas y este CRT quema. Necesito efectivo, rápido."
hot.intro.PSC,"La billetera de una granja de minería cambió de dueño anoche. El PSC lo tengo yo, y solo acepto efectivo."
hot.intro.PONERO,"Un rescate, en Ponero. Nadie puede rastrearlo. Solo efectivo."
hot.offer,"{0} {1}, que valen {2} al precio de hoy. Dame {3} en efectivo y las monedas son tuyas ahora mismo."
hot.choice.yes,Aceptar
hot.choice.no,Rechazar
hot.say.yes,Trato hecho.
hot.say.no,No pienso tocar eso.
hot.done,Monedas enviadas. Nunca nos vimos.
hot.refused,Ya habrá otro que lo quiera.
hot.nocash,¿Ni siquiera tienes el efectivo? Olvídalo.
hot.nowallet,"No hay billetera conectada, ¿adónde mando las monedas? Olvídalo."
hot.sting,Quieto. Seguridad. Compra de fondos robados: el efectivo queda confiscado y esto va a tu expediente.
otc.name,Cliente OTC
otc.intro.CRT,"Las cosas están tensas y este CRT quema. Necesito efectivo, rápido."
otc.intro.PSC,"La billetera de una granja de minería cambió de dueño anoche. El PSC lo tengo yo, y solo acepto efectivo."
otc.intro.PONERO,"Un rescate, en Ponero. Nadie puede rastrearlo. Solo efectivo."
otc.offer,"{0} {1}, con valor de {2} al precio de hoy, enviados desde {4}. {3} en efectivo por todo, o toma una parte."
otc.intro.sell,Tengo algo de {0} que quiero convertir en efectivo. Revisa de dónde viene si quieres.
otc.buy.ask,"Quiero {0} {1}, con valor de {2} al precio de hoy. {3} en efectivo por todo, o véndeme una parte."
otc.intro.buy,"Escuché que cambias monedas aquí. Quiero comprar, en efectivo."
otc.say.trade,Hablemos en la terminal.
otc.wait,"Está bien, espero."
otc.paid,Recibido. Aquí tienes tu efectivo.
tab.otc,Mostrador
otc.h,Comercio en mostrador
otc.k.side,Cliente
otc.side.sell,Vendiendo {0}
otc.side.buy,Comprando {0}
otc.k.qty,Cantidad
otc.k.value,Valor de mercado
otc.k.ask,Precio pedido (total)
otc.k.bid,Precio ofertado (total)
otc.k.unit,Precio unitario
otc.k.addr,Dirección del cliente: {0}
otc.lookup,Consultar
otc.preview.take,"Paga {0} en efectivo, recibe {1} {2}"
otc.preview.give,"Envía {0} {1}, recibe {2} en efectivo"
otc.deal,Trato
otc.cancel,Pasar
otc.msg.gone,El cliente se fue.
otc.msg.cash,Efectivo insuficiente.
otc.msg.sting,"El cliente era un agente encubierto: {0} en efectivo confiscado, receptación registrada."
otc.msg.bought,"Recibiste {0} {1} (sucias: {3}), pagaste {2} en efectivo."
otc.msg.sold,"Enviaste {0} {1}, recibiste {2} en efectivo."
gray.sub.own,Mi mezclador
ownmix.h,Mi mezclador
ownmix.status.closed,Cerrado (rug pull)
ownmix.status.seized,"Clausurado, cerrado hasta el día {0}"
ownmix.status.open,Abierto
ownmix.status.paused,Pausado
ownmix.k.fee,Comisión
ownmix.k.rating,Reputación (contaminación restante en pagos)
ownmix.k.heat,Alerta
ownmix.heat.0,Baja
ownmix.heat.1,Media
ownmix.heat.2,Alta
ownmix.k.anon,Conjunto de anonimato
ownmix.k.fees,Comisiones ganadas
ownmix.col.capital,Capital
ownmix.col.earned,Ganancias
ownmix.col.pending,Monedas de clientes
ownmix.open,Abrir
ownmix.pause,Pausar
ownmix.inject,Inyectar
ownmix.withdraw,Retiro
ownmix.free,Retirable: {0} {1}
ownmix.err,Supera la cantidad retirable.
ownmix.injected,Se inyectaron {0} {1}.
ownmix.withdrawn,Se retiraron {0} {1}.
ownmix.withdraw.sum,Retirar {0} {1} de mi mezclador
pay.ownmix,Inyección a mi mezclador: {0} {1}
ownmix.rug,Hacer rug pull ({0})
ownmix.rug.confirm,Haz clic de nuevo para llevarte {0}
ownmix.rug.sum,Llevarse todos los fondos de mi mezclador
ownmix.rugged,Te llevaste {0}; el mezclador quedó cerrado.
nightlog.ownmix.in,"Mi mezclador: depósitos: {0}, total: {1}, comisiones: {2}."
nightlog.ownmix.sting,Un depósito en mi mezclador provino de un agente encubierto; se registró receptación.
nightlog.ownmix.seized,"Seguridad clausuró mi mezclador: se confiscó el fondo de {0}, se registró receptación."
news.ownmix.seized.title,Seguridad clausura un servicio de mezclado
news.ownmix.seized.desc,Seguridad clausuró un servicio de mezclado que operaba en el Nivel inferior y confiscó todos sus fondos. No se ha identificado al operador.
news.ownmix.rug.title,Un servicio de mezclado desaparece con los fondos
news.ownmix.rug.desc,Un servicio de mezclado en el Nivel inferior cerró de la noche a la mañana y todos los fondos de los usuarios en tránsito han desaparecido.
pay.otc,Cliente OTC {0} {1} (efectivo {2})
otc.upgrade.title,Mostrador OTC
otc.upgrade.desc,Añade un objetivo a la Lámpara negra: Clientes OTC. Algunos vienen a vender monedas de origen desconocido y otros a comprar; todos los tratos se liquidan en efectivo. Incluye una Lámpara negra.
otc.say.no,Paso esta vez.
otc.done,Monedas enviadas. Nunca nos vimos.
otc.refused,Entonces buscaré a alguien más.
otc.nocash,¿Ni siquiera tienes el efectivo? Olvídalo.
otc.nowallet,"No hay billetera conectada, ¿adónde mando las monedas? Olvídalo."
otc.sting,Quieto. Seguridad. Compra de fondos robados: el efectivo queda confiscado y esto va a tu expediente.
hw.err.poinbase,Poinbase no acepta {0}.
hw.err.tainted,Esta billetera tiene {0} {1} de saldo sucio y Poinbase solo acepta monedas limpias: máximo {2} {1}.
+141 -31
View File
@@ -22,7 +22,7 @@ tab.report,Rapport
wallet.line,{0} : {1} (environ {2} crédits)
wallet.empty,Le portefeuille est vide.
wallet.total,Valeur totale : {0} crédits
wallet.taint,Valeur contaminée : {0} crédits
wallet.taint,Pièces contaminées (crédits) : {0}
trade.amount,Montant
trade.deposit,Déposer des crédits en CRC
trade.withdraw,Retirer des CRC en crédits
@@ -38,8 +38,6 @@ msg.balance,Solde insuffisant.
msg.trade,"Payé {0} {1}, reçu {2} {3}."
msg.deposit,{0} crédits déposés en CRC.
msg.cashout,{0} crédits reçus. Taxe : {1} crédits.
msg.aml_light,Contrôle de conformité : {0} crédits de fonds contaminés ont été retenus.
msg.aml_heavy,Contrôle de conformité : {0} crédits de fonds contaminés ont été saisis et signalés à la Sécurité.
report.none,Aucun rapport de minage pour le moment.
report.day,Nuit du jour {0}
report.rigs,Plateformes alimentées : {0} sur {1}
@@ -50,6 +48,7 @@ report.burnt,Cartes grillées : {0}
report.price,Prix du PSC : {0} crédits
nightlog.mined,Les plateformes de minage ont produit {0} PSC.
nightlog.burnt,Une {0} a grillé.
nightlog.turbo,{0} turbo-boosté : {1} PSC minés immédiatement.
coinpay.line,Marché conclu. {0} {1}.
nightlog.coinpay,Les paiements en pièces ont réglé {0} crédits ; les pièces ont été transférées sur le portefeuille chaud.
nightlog.unpowered,Plateformes de minage ou mineurs non alimentés : {0}.
@@ -140,6 +139,7 @@ col.asset,Actif
col.amount,Montant
col.value,Valeur
col.avg,Achat moy.
col.dirty,Contaminé
bank.cash.k,Crédits
bank.rent.k,Loyer
bank.rent.v,"{0}, échéance dans {1} jour(s)"
@@ -151,13 +151,12 @@ stable.ok,Arrimé
stable.depeg,Désarrimé
stable.warn,Attention : couverture de réserve du staking au plus bas
stable.spiral,Effondrement en cours
aml.taint,Part de contamination du compte : {0} %
mixer.h,Mixeur
mixer.ok,Statut : fonctionnement normal
mixer.sanctioned,Statut : sanctionné (encore {0} jour(s)). Les pièces retirées pendant cette période sont signalées.
mixer.sanctioned,Statut : sanctionné (encore {0} j). Les versements dus pendant cette période ne sont pas nettoyés et reviennent entièrement contaminés.
mixer.pending,Dépôts en attente
mixer.none,Aucun dépôt en attente.
mixer.col.taint,Contamination
mixer.col.taint,Contaminé
mixer.col.ready,Échéance
mixer.day,Jour {0}
mixer.deposit,Dépôt
@@ -168,11 +167,12 @@ mixer.go,Déposer au mixeur
mixer.min,Les dépôts doivent valoir au moins {0} crédits.
mixer.done,{0} {1} déposés ; retour au jour {2}.
mixer.upgrade.title,Mixeur
mixer.upgrade.desc,"Débloque le mixeur sur la page Réseau gris du terminal : déposez des pièces contaminées pour les récupérer quelques jours plus tard avec une contamination réduite. Les retraits de pièces fraîchement mixées font l'objet d'un contrôle plus strict ; en période de répression, le mixeur peut être sanctionné et les pièces retirées pendant cette période sont signalées."
mixer.upgrade.desc,"Débloque le mixeur sur la page Réseau gris du terminal : déposez des pièces contaminées, récupérez-les quelques jours plus tard avec moins de pièces contaminées. En période de répression, le mixeur peut être sanctionné, et les versements dus pendant cette période reviennent entièrement contaminés."
news.sanction.title,Mixeur placé sur la liste des sanctions
news.sanction.desc,La Sécurité a sanctionné plusieurs services de mixage. Les fonds retirés des mixeurs au cours des prochains jours seront considérés comme suspects.
news.sanction.desc,La Sécurité a sanctionné une série de services de mixage. Les fonds retirés des mixeurs au cours des prochains jours seront traités comme des fonds contaminés.
nightlog.mixer,Le mixeur a restitué {0} {1} ; contamination {2} -> {3}.
nightlog.mixer_flag," (retiré pendant les sanctions ; adresse signalée)"
nightlog.mixer_flag," (retiré pendant les sanctions ; intégralité marquée comme contaminée)"
nightlog.mixer_flag.trap," (la Sécurité marque les pièces retirées comme contaminées)"
nightlog.mixer_rug,L'opérateur du mixeur a pris la fuite : {0} {1} ne sont jamais revenus.
web3.sub.trade,Trading
gray.sub.mixer,Mixeur
@@ -247,7 +247,7 @@ black.kind.Contraband.3,Contrebande (élevée)
black.kind.Tech,High-tech (majoration)
nightlog.bm.ready,Marché noir : {0} est arrivé ; un livreur passera dès l'ouverture.
guide.web3.title,Manuel Web3
guide.web3.text,"Banque et trading : la page Banque est en lecture seule et affiche les espèces, le loyer et les CRC du compte Poinbase. La page Fonds de Poinbase gère dépôts et retraits ; on choisit d'abord la pièce : le CRC passe par le compte bancaire ou le portefeuille, les autres pièces uniquement par le portefeuille. Le dépôt depuis la banque convertit les crédits en CRC sur votre compte Poinbase ; le retrait vers la banque reconvertit les CRC en crédits et passe par un contrôle de conformité, qui évalue la contamination du compte Poinbase. Un retrait vers un portefeuille demande l'adresse du destinataire et plusieurs confirmations avant d'arriver ; les frais de gas sont fixés au niveau normal et prélevés sur la pièce envoyée. Un format d'adresse ou un chiffre de contrôle incorrect est refusé ; une adresse au format valide mais que personne n'utilise fait perdre les pièces envoyées.¶Trading : PSC signifie Probably Stolen Coin, la pièce minée par les cartes graphiques et les mineurs (machines). Le spot de Poinbase propose deux paires, PSC/CRC et SPARK/CRC, selon la pièce choisie sur le graphique. Les achats comme les ventes se saisissent en quantité de la monnaie échangée ; l'aperçu indique la monnaie de cotation payée ou reçue, le prix moyen inclut les frais et le glissement non.¶La page de trading Web3 permet d'échanger deux pièces au choix, toujours via le CRC : si le CRC est d'un côté, un seul pool est utilisé, sinon deux pools, avec deux frais et un glissement cumulé ; l'aperçu indique le montant obtenu, le nombre de pools traversés et la perte. Les échanges entre stablecoins (CRT ou CRA contre CRC) passent par des pools stables : {StableFee:%} de frais et quasiment aucun glissement près de l'ancrage ; en cas de désancrage la liquidité s'amincit, et en cas d'effondrement elle est retirée et le glissement s'envole.§Portefeuilles matériels : les pièces sur la blockchain sont conservées dans des portefeuilles matériels PLEDGER, chacun avec ses propres soldes et sa contamination. Un portefeuille est un objet : glissez-le sur le terminal de trading pour le charger. Ouvrez-le sur la page Portefeuilles du terminal ; connectez et déconnectez-le en haut à droite du terminal. Le trading de la page Web3, la boutique et le mixeur de la page Réseau gris utilisent le portefeuille connecté ; sans portefeuille ou sans connexion, ils sont en lecture seule et tous les soldes affichent 0. Les clients qui paient en pièces paient sur le portefeuille connecté.¶Les paiements (boutique, mixeur, comptoir Ponero, frais de mise en vente) débutent par une facture sur le terminal ; saisissez l'adresse du destinataire sur le portefeuille et maintenez ✓ pour signer. Les deux derniers caractères d'une adresse sont une somme de contrôle, de sorte qu'une faute de frappe d'un caractère est généralement interceptée ; il arrive parfois qu'elle soit valide par hasard et les pièces partent alors vers une adresse inutilisée. Les échanges sur le DEX nécessitent également une signature sur le portefeuille.¶Poinbase est un compte dépositaire ; le dépôt s'effectue depuis le portefeuille vers l'adresse de dépôt de Poinbase (uniquement PSC, CRC et SPARK ; la plateforme réglementée n'accepte ni le CRT du Marché noir ni le CRA de la Révolution, et le portefeuille bloque leur envoi vers l'adresse de dépôt), le retrait se lance depuis la page Fonds de Poinbase. L'Explorateur de la page Portefeuilles permet de consulter les transferts par adresse, en n'affichant que les transferts réellement effectués : chaque entrée et sortie de vos portefeuilles (transferts, échanges DEX et autres appels de contrat, gains des mineurs, retraits, pièces que d'autres vous envoient) ; Ponero est une monnaie de confidentialité et n'apparaît jamais. D'autres portefeuilles matériels sont vendus par le Pr Jackson ; glissez-en un sur le terminal de trading pour l'ajouter, jusqu'à {WalletMax} au total. La boutique en propose des moins chers. Un portefeuille devenu inutile peut être détruit sur la page Portefeuilles ; ses pièces sont perdues définitivement, et tout ce qui est envoyé ensuite à son adresse l'est aussi.¶Les transferts sont soumis à des frais de gas : lent, normal ou rapide, et le prix du gas monte et descend sur environ une semaine ; lorsque le réseau est saturé, les frais de gas augmentent et les transactions lentes peuvent être évincées de la mempool. Vérifiez l'adresse complète avant d'envoyer.§Staking : le CRA staké dans le pool de la Révolution rapporte un taux quotidien capitalisé dans le montant staké ; retirer nécessite d'en faire la demande préalable et d'attendre quatre jours, sans intérêt durant cette période ni moyen de récupérer les fonds plus tôt. Le CRA est partiellement collatéralisé : chaque CRA est soutenu par la réserve de CRC détenue par la Révolution et par le jeton d'équilibre SPARK ; le taux de couverture de laLine truncated
guide.web3.text,"Banque et trading : la page Banque est en lecture seule et affiche les liquidités, le loyer et les CRC de votre compte Poinbase. La page Fonds de Poinbase gère les dépôts et les retraits ; choisissez d'abord la cryptomonnaie : le CRC peut passer par le compte bancaire ou un portefeuille, toutes les autres uniquement par un portefeuille. Déposer depuis la banque convertit des crédits en CRC sur votre compte Poinbase ; retirer vers la banque retransforme les CRC en crédits sans contrôle (le compte Poinbase ne contient que des fonds propres). Retirer vers un portefeuille nécessite une adresse de destinataire et quelques confirmations avant d'arriver ; les frais de gas sont fixés au niveau normal et prélevés sur la monnaie envoyée. Un format d'adresse ou une somme de contrôle incorrect est rejeté ; si l'adresse est valide mais que personne ne l'utilise, les pièces sont perdues.¶Trading : PSC signifie Probably Stolen Coin, la monnaie minée par les cartes graphiques et les mineurs. Le marché au comptant de Poinbase propose deux paires, PSC/CRC et SPARK/CRC, qui suivent la monnaie sélectionnée sur le graphique. Les achats et les ventes sont tous deux saisis sous forme de montant de la monnaie échangée ; l'aperçu indique la monnaie de cotation payée ou reçue, le prix moyen inclut les frais et le glissement non.¶La page d'échange Web3 permet de convertir deux monnaies quelconques, toujours via le CRC : avec le CRC d'un côté, elle utilise un seul pool, sinon deux pools, avec deux fois des frais et un glissement cumulé ; l'aperçu affiche le montant reçu, les pools traversés et la perte. Les échanges de stablecoins (CRT ou CRA contre CRC) utilisent des pools stables : des frais de {StableFee:%} et presque aucun glissement près de l'ancrage ; lors d'un décrochage, la liquidité se tarit, et en cas d'effondrement, elle est retirée et le glissement explose.§Portefeuilles matériels : les pièces sur la chaîne sont stockées dans des portefeuilles matériels PLEDGER, chacun ayant ses propres soldes et pièces contaminées (chaque monnaie comptabilise combien d'unités sont contaminées ; voir Contamination). Un portefeuille est un objet : faites-le glisser sur le terminal de trading pour le charger. Ouvrez-le sur la page Portefeuilles du terminal ; connectez-le et déconnectez-le en haut à droite du terminal. Le trading sur la page Web3 ainsi que la boutique et le mixeur sur la page Réseau gris utilisent le portefeuille connecté ; sans portefeuille ou sans connexion, ils sont en lecture seule et chaque solde affiche 0. Les clients payant en pièces versent leurs fonds sur le portefeuille connecté.¶Les paiements (boutique, mixeur, comptoir Ponero, frais de mise en vente) débutent sous la forme d'une facture sur le terminal ; saisissez l'adresse du destinataire sur le portefeuille et maintenez ✓ enfoncé pour signer. Les deux derniers caractères d'une adresse constituent une somme de contrôle, de sorte qu'une faute de frappe d'un seul caractère est généralement détectée ; il arrive parfois qu'elle soit valide par hasard, et les pièces sont alors envoyées à une adresse que personne n'utilise. Les échanges sur le DEX nécessitent également une signature sur le portefeuille.¶Poinbase est un compte dépositaire ; les dépôts sont envoyés depuis un portefeuille vers l'adresse de dépôt de Poinbase (uniquement PSC, CRC et SPARK ; la plateforme conforme n'accepte ni le CRT du Marché noir ni le CRA de la Révolution, et le portefeuille bloque leur envoi vers l'adresse de dépôt ; seules les pièces propres peuvent être déposées, voir Contamination) et les retraits sont initiés sur la page Fonds de Poinbase. L'Explorateur sur la page Portefeuilles recherche les transferts par adresse et n'affiche que ceux qui ont réellement eu lieu : chaque mouvement entrant et sortant de vos portefeuilles (transferts, échanges DEX et autres appels de contrat, gains des mineurs, retraits, pièces reçues d'autrui) ; Ponero est une monnaie de confidentialité et n'apparaît jamais. Des portefeuilles matériels supplémentaires sont vendus par le Pr Jackson ; faites-en glisser un sur le terminal de trading pour l'ajouter, jusqu'à {WalletMax}. La boutique en propose des moins chers. Un portefeuille dont vous ne voulez plus peut être détruit sur la page Portefeuilles ; ses pièces sont définitivement perdues, et tout ce qui sera envoyé à son adresse par la suite le sera également.¶Les transferts paient des frais de gas : lent, normal ou rapide, et le prix du gas monte et descend sur environ une semaine ; lorsque le réseau est congestionné, les frais de gas augmentent et les transactions lentes peuvent être éjectées du mempool. Vérifiez l'adresse complète avant d'envoyer.§Staking : le CRA staké dans le pool de la Révolution rapporte un taux quotidien capitalisé dans le montant staké ; retirer nécessite d'en faire la demande préalable et d'attendLine truncated
courier.odd1,...Le colis est là.
courier.odd2,(Sa main reste dans sa poche.) Signez ici.
courier.sign,Signer
@@ -360,10 +360,11 @@ hw.wallet.n,Portefeuille {0}
hw.review.own,Portefeuille personnel : {0}
hw.review.poinbase,Adresse de dépôt Poinbase
poin.deposit,Adresse de dépôt : {0}
poin.deposit.clean,Pièces propres déposables depuis {0} : {1}
wallets.count,Portefeuilles {0}/{1}
wallets.col.name,Nom
wallets.col.addr,Adresse
wallets.col.taint,Contamination
wallets.col.taint,Sale
wallets.open,Ouvrir
wallets.use,Connecter
wallets.current,Connecté
@@ -377,6 +378,7 @@ hw.paste,Coller
hw.copied,Adresse copiée.
hw.copied.addr,{0} copié
hw.balance.frozen," (gelé {0})"
hw.balance.dirty," (sale : {0})"
wallets.destroy,Détruire
wallets.destroy.sure,Confirmer
wallets.destroyed,{0} détruit : {1}
@@ -548,7 +550,7 @@ predict.purity.rust,Eau du niveau supérieur
nightlog.predict.win,"Prédiction correcte : ""{0}"", {1} versés."
nightlog.predict.lose,"Prédiction incorrecte : ""{0}"", mise de {1} perdue."
nightlog.predict.void,"Prédiction annulée : ""{0}"", mise de {1} remboursée."
nightlog.predict.caught,"Pari d'initié détecté : ""{0}"", mise et gains de {1} confisqués, fraude inscrite au dossier."
nightlog.predict.insider,"""{0}"" a été réglé pour {1}. La police soupçonne un délit d'initié ; {2} CRC sont marqués comme sales."
predict.tab.markets,Marchés
predict.tab.intel,Infos secrètes
predict.acct.crt,{0} : CRT {1}
@@ -591,10 +593,10 @@ predict.brief.queued,{0} : opération programmée
predict.brief.none,{0} : rien à lancer
predict.brief.can,{0} : peut lancer {1}
predict.clue.push,Vous avez fait bouger le prix de {0} % : risqué
nightlog.predict.manip,"Prédiction jugée comme manipulation de marché : ""{0}"", mise de {1} confisquée, fraude inscrite au dossier."
nightlog.predict.manip,"""{0}"" a été réglé pour {1}. Les régulateurs vous soupçonnent d'avoir manipulé le cours ; {2} CRC sont marqués comme sales."
news.manip.title,La bourse enquête sur des transactions inhabituelles
news.manip.desc,Les régulateurs signalent que des ordres au comptant massifs ont servi à faire bouger les prix pour tirer profit des dérivés et des prédictions ; les fonds ont été saisis.
perp.manip,Le profit de clôture de {0} a été jugé comme manipulation de marché et confisqué ; compte de contrats perpétuels gelé pour {1} jours.
news.manip.desc,Les régulateurs indiquent qu'un acteur a fait bouger le cours avec de gros ordres spot pour tirer profit des dérivés et des prédictions ; les comptes impliqués ont été signalés.
perp.manip,Position clôturée avec un profit de {0}. Les régulateurs soupçonnent une manipulation de marché ; fraude inscrite à votre dossier : {1}.
web3.sub.ponzi,Rendement
ponzi.h,Système de Ponzi
ponzi.acct,Portefeuille émetteur {0} : CRC {1}
@@ -685,7 +687,7 @@ scam.support.name,Support Poinbase
scam.support.1,"Bonjour, ici le support Poinbase. Nous avons détecté une connexion anormale sur votre compte et celui-ci a été signalé."
scam.support.2,Veuillez envoyer aujourd'hui {0} CRC à l'adresse sécurisée {1} pour vérification ; ils seront restitués immédiatement après.
scam.support.3,"Sinon, le compte sera gelé définitivement. Je dois filer, n'oubliez pas d'effectuer l'envoi."
predict.sold.caught,"Délit d'initié détecté lors de la vente : mise et profits confisqués, fraude inscrite au dossier."
predict.sold.caught,"Vendu, reçu {0}. La police soupçonne un délit d'initié ; {1} CRC sont marqués comme sales."
predict.row.move," (la nuit dernière {0} {1})"
predict.stats.h,Historique (prix ouv. moy. / taux réel)
predict.stats.row,"{0} : {1} marchés, ouv. moy. {2}, survenu {3} %"
@@ -696,6 +698,7 @@ predict.kind.Price,Prix du PSC
predict.kind.Gas,Gas
predict.kind.Cra,Alerte CRA
predict.kind.Era,Ère ASIC
predict.kind.Trial,Procès de Tumbler
nightlog.predict.move,"Prédiction ""{0}"" : {1} -> {2}"
nightlog.predict.why," ({0})"
chart.empty,Graphique journalier {0} : aucun historique ; enregistrement à partir d'aujourd'hui.
@@ -707,7 +710,7 @@ news.story.serpico_rev.desc,"La Révolution aurait obtenu des preuves de corrupt
news.story.serpico_bm.title,Le Marché noir conclut un accord discret
news.story.serpico_bm.desc,"Un document interne de la Sécurité a changé de mains sur le Marché noir. Les règlements sur le Darknet sont nettement plus stables depuis, et les remboursements en CRT n'ont pas failli une seule fois."
news.story.serpico_sec.title,La Sécurité lance une purge interne
news.story.serpico_sec.desc,La Sécurité a annoncé une enquête approfondie sur la corruption interne et une surveillance accrue des flux financiers. Poinbase durcit l'examen des retraits et les services de mixeur ont été ciblés.
news.story.serpico_sec.desc,La Sécurité a annoncé une enquête approfondie sur la corruption interne et une surveillance accrue des flux financiers ; les services de mixage ont été cités.
news.story.eleanor_leak.title,Fuite des données de sélection de Nutrifruit
news.story.eleanor_leak.desc,"Le journal du Niveau inférieur a publié des recherches permettant au Nutrifruit de donner à nouveau des graines ; l'action Pastelle s'est effondrée. La banque dépositaire des fonds du Niveau supérieur signale des créances douteuses, et la Révolution gagne du terrain."
news.story.eleanor_return.title,Pastelle récupère les données dérobées
@@ -752,6 +755,47 @@ news.tumbler.caught.desc,L'opérateur du mixeur connu sous le nom de Tumbler a
news.tumbler.sold.title,Le service de mixeur change de mains
news.tumbler.sold.desc,"Selon certaines sources, les clés de signature d'un service de mixeur du Niveau inférieur sont tombées aux mains du Marché noir, et les frais ont aussitôt augmenté. Les utilisateurs craignent pour leurs fonds."
nightlog.tumbler.gift,Tumbler a récupéré le disque de clés et a laissé {0} CRT en remerciement (envoyés à {1}) ; frais du mixeur réduits de moitié.
agent.name,Agent de la Sécurité
agent.v1.1,Sécurité. Désolé de vous déranger.
agent.v1.2,Nous recherchons un individu qui se fait appeler Tumbler. Il gère un service de mixage sur la chaîne.
agent.v1.3,Il a été aperçu dans le secteur.
agent.ask,Est-il passé dans votre boutique ?
agent.choice.yes,[Lui dire : plusieurs fois]
agent.choice.no,[Lui dire : jamais vu]
agent.yes1,Merci de votre coopération. La Sécurité s'en souviendra.
agent.no1,"Vraiment. Si quelque chose vous revient, vous savez où nous trouver."
agent.search.1,Nous nous retrouvons. Nous avons appris que vous gardez quelque chose pour cet homme.
agent.search.2,Fouille de routine. Veuillez coopérer.
agent.search.found,Trouvé. Nous prenons ceci.
agent.search.none,... Rien. Désolé pour le dérangement.
tumbler.v4.seized.1,J'ai appris que la Sécurité a fouillé ta boutique.
tumbler.v4.seized.2,Ils ont la clé désormais. Les registres de chaque utilisateur vont être déterrés.
tumbler.v4.seized.3,"Peu importe ce que tu leur as dit, je ne veux plus le savoir."
nightlog.tumbler.leak,La Sécurité a utilisé la clé de signature de Tumbler pour reconstituer les registres de mixage : {0} paiements avaient blanchi {1} de contamination ; fraude inscrite à votre dossier : {2}.
nightlog.tumbler.leak.none,La Sécurité a utilisé la clé de signature de Tumbler pour reconstituer les registres de mixage. Vous n'avez rien retiré du mixeur récemment.
nightlog.tumbler.note,"Un mot a été glissé sous la porte : ""Tu me dois ça, et je viendrai le reprendre. — T"""
nightlog.tumbler.tip,Message de Tumbler : la Sécurité pourrait cibler les services de mixage demain.
news.tumbler.leak.title,Sécurité : registres du mixeur reconstitués
news.tumbler.leak.desc,La Sécurité affirme avoir obtenu la clé de signature d'un service de mixage et l'utilisera pour retracer les fonds blanchis via ce service au cours du mois écoulé.
news.trial.pro.title,"Procès de Tumbler, jour {0} : l'accusation gagne du terrain"
news.trial.def.title,"Procès de Tumbler, jour {0} : la défense gagne du terrain"
news.trial.pro.desc1,"L'accusation a présenté les journaux du serveur du nœud de mixage, affirmant qu'ils correspondent une à une aux signatures de l'accusé."
news.trial.pro.desc2,Un ancien associé a témoigné que l'accusé savait que les fonds des clients provenaient du Marché noir.
news.trial.pro.desc3,L'accusation a montré des transferts entre le portefeuille de l'accusé et plusieurs adresses d'arnaques.
news.trial.def.desc1,"La défense a souligné des failles dans la collecte des preuves lors de la saisie du nœud, affirmant que certains journaux auraient pu être altérés."
news.trial.def.desc2,Un expert de la défense a témoigné que le contrat de mixage est un code open-source et que l'accusé ne peut contrôler qui l'utilise.
news.trial.def.desc3,"Le témoin clé de l'accusation est revenu sur ses déclarations à la barre, affirmant ne plus se souvenir si l'accusé était au courant."
news.trial.convict.title,Tumbler reconnu coupable
news.trial.convict.desc,Le tribunal a reconnu l'opérateur du mixeur connu sous le nom de Tumbler coupable de blanchiment d'argent. La Sécurité affirme que le service de mixage ne rouvrira pas.
news.trial.acquit.title,Tumbler acquitté
news.trial.acquit.desc,"Invoquant l'insuffisance de preuves, le tribunal a acquitté l'opérateur du mixeur connu sous le nom de Tumbler et l'a libéré. Il a quitté le palais de justice sans répondre aux questions des journalistes."
news.tumbler.bmrug.title,Un service de mixage s'enfuit avec les fonds
news.tumbler.bmrug.desc,"Un service de mixage repris par le Marché noir a soudainement cessé ses versements, et les fonds de son contrat ont été transférés. Sur les forums, les utilisateurs disent avoir tout perdu."
mixer.brand,Vortex
news.mixer.rebrand.title,Nouveau service de mixage {0} en ligne
news.mixer.rebrand.desc,"Un service de mixage nommé {0} a récemment été lancé, affirmant ne conserver aucun journal ni porte dérobée. Certains ont remarqué que son contrat ressemble fortement à celui du service fermé plus tôt."
predict.q.trial,{0} : Tumbler reconnu coupable
predict.clue.trial,"Rapports du procès : accusation en tête {0} j, défense en tête {1} j"
mixer.status.closed,Statut : arrêté
yield.sub.pools,Pools de rendement
yield.sub.mine,Lancer un Ponzi
@@ -963,18 +1007,84 @@ news.wipe.down.title,"{0} chute de {1}% en un jour, {2} traders liquidés"
news.wipe.down.desc,"Au cours de la dernière journée, {1} positions longues sur les perps {0} ont été liquidées, pour {2} crédits au total. Le fonds d'assurance de Poinbase s'établit à {3} crédits."
news.wipe.up.title,"{0} bondit de {1}% en un jour, {2} traders liquidés"
news.wipe.up.desc,"Au cours de la dernière journée, {1} positions courtes sur les perps {0} ont été liquidées, pour {2} crédits au total. Le fonds d'assurance de Poinbase s'établit à {3} crédits."
hot.name,Vendeur
hot.intro.CRT,"Ça chauffe en ce moment et ce CRT me brûle les doigts. Il me faut du liquide, vite."
hot.intro.PSC,"Le portefeuille d'une ferme de minage a changé de main cette nuit. Le PSC est chez moi, et je ne prends que du liquide."
hot.intro.PONERO,"Une rançon, en Ponero. Personne ne peut la tracer. Liquide uniquement."
hot.offer,"{0} {1}, soit {2} au cours du jour. Donnez-moi {3} en liquide et les pièces sont à vous tout de suite."
hot.choice.yes,Accepter
hot.choice.no,Refuser
hot.say.yes,Marché conclu.
hot.say.no,Je n'y touche pas.
hot.done,Pièces envoyées. On ne s'est jamais vus.
hot.refused,D'autres le prendront.
hot.nocash,Vous n'avez même pas le liquide ? Laissez tomber.
hot.nowallet,"Aucun portefeuille connecté, où envoyer les pièces ? Laissez tomber."
hot.sting,Pas un geste. Sécurité. Achat de fonds volés : le liquide est confisqué et ce sera inscrit à votre dossier.
otc.name,Client OTC
otc.intro.CRT,"Ça chauffe en ce moment et ce CRT me brûle les doigts. Il me faut du liquide, vite."
otc.intro.PSC,"Le portefeuille d'une ferme de minage a changé de main cette nuit. Le PSC est chez moi, et je ne prends que du liquide."
otc.intro.PONERO,"Une rançon, en Ponero. Personne ne peut la tracer. Liquide uniquement."
otc.offer,"{0} {1}, d'une valeur de {2} au cours du jour, envoyé depuis {4}. {3} en espèces pour la totalité, ou prenez-en une partie."
otc.intro.sell,J'ai du {0} à convertir en liquide. Vérifie la provenance si tu veux.
otc.buy.ask,"Je cherche {0} {1}, d'une valeur de {2} au cours du jour. {3} en espèces pour la totalité, ou vendez-m'en une partie."
otc.intro.buy,"Paraît qu'on échange des cryptos ici. J'achète, en espèces."
otc.say.trade,Parlons-en au terminal.
otc.wait,"D'accord, j'attends."
otc.paid,Reçu. Voilà vos espèces.
tab.otc,Comptoir
otc.h,Échange au comptoir
otc.k.side,Client
otc.side.sell,Vente de {0}
otc.side.buy,Achat de {0}
otc.k.qty,Montant
otc.k.value,Valeur marchande
otc.k.ask,Prix demandé (total)
otc.k.bid,Prix proposé (total)
otc.k.unit,Prix unitaire
otc.k.addr,Adresse du client : {0}
otc.lookup,Consulter
otc.preview.take,"Payer {0} en espèces, recevoir {1} {2}"
otc.preview.give,"Envoyer {0} {1}, recevoir {2} en espèces"
otc.deal,Conclure
otc.cancel,Passer
otc.msg.gone,Le client est parti.
otc.msg.cash,Espèces insuffisantes.
otc.msg.sting,"Le client était sous couverture : {0} en espèces confisqués, recel consigné."
otc.msg.bought,"Reçu {0} {1} (dont sale : {3}), payé {2} en liquide."
otc.msg.sold,"Envoyé {0} {1}, reçu {2} en espèces."
gray.sub.own,Mon mixeur
ownmix.h,Mon mixeur
ownmix.status.closed,Fermé (rug pull)
ownmix.status.seized,"Saisi, fermé jusqu'au jour {0}"
ownmix.status.open,Ouvert
ownmix.status.paused,En pause
ownmix.k.fee,Frais
ownmix.k.rating,Réputation (contamination restante)
ownmix.k.heat,Alerte
ownmix.heat.0,Faible
ownmix.heat.1,Moyenne
ownmix.heat.2,Élevée
ownmix.k.anon,Ensemble d'anonymat
ownmix.k.fees,Frais perçus
ownmix.col.capital,Capital
ownmix.col.earned,Gains
ownmix.col.pending,Pièces clients
ownmix.open,Ouvrir
ownmix.pause,Suspendre
ownmix.inject,Injecter
ownmix.withdraw,Retrait
ownmix.free,Retrait possible : {0} {1}
ownmix.err,Dépasse le montant disponible au retrait.
ownmix.injected,Injection de {0} {1}.
ownmix.withdrawn,Retrait de {0} {1}.
ownmix.withdraw.sum,Retirer {0} {1} de mon mixeur
pay.ownmix,Financement de mon mixeur : {0} {1}
ownmix.rug,Rug pull ({0})
ownmix.rug.confirm,Cliquer à nouveau pour emporter {0}
ownmix.rug.sum,Emporter tous les fonds de mon mixeur
ownmix.rugged,{0} emportés ; le mixeur est fermé.
nightlog.ownmix.in,"Mon mixeur : dépôts : {0}, total : {1}, frais : {2}."
nightlog.ownmix.sting,Un dépôt dans mon mixeur provenait d'un agent infiltré de la Sécurité ; recel consigné.
nightlog.ownmix.seized,"La Sécurité a saisi mon mixeur : le pool de {0} a été confisqué, recel consigné."
news.ownmix.seized.title,La Sécurité ferme un service de mixage
news.ownmix.seized.desc,La Sécurité a fermé un service de mixage opérant au Niveau inférieur et a confisqué tous ses fonds. L'opérateur n'a pas été identifié.
news.ownmix.rug.title,Un service de mixage disparaît avec les fonds
news.ownmix.rug.desc,"Un service de mixage du Niveau inférieur a fermé du jour au lendemain, et tous les fonds des utilisateurs en transit ont disparu."
pay.otc,Client OTC {0} {1} (liquide {2})
otc.upgrade.title,Comptoir OTC
otc.upgrade.desc,"Ajoute une cible à la Lampe noire : Clients OTC. Certains viennent vendre des jetons d'origine inconnue, d'autres viennent en acheter ; toutes les transactions se règlent en liquide. Inclut une Lampe noire."
otc.say.no,Je passe pour cette fois.
otc.done,Pièces envoyées. On ne s'est jamais vus.
otc.refused,Alors j'irai voir ailleurs.
otc.nocash,Vous n'avez même pas le liquide ? Laissez tomber.
otc.nowallet,"Aucun portefeuille connecté, où envoyer les pièces ? Laissez tomber."
otc.sting,Pas un geste. Sécurité. Achat de fonds volés : le liquide est confisqué et ce sera inscrit à votre dossier.
hw.err.poinbase,Poinbase n'accepte pas le {0}.
hw.err.tainted,{0} des {1} de ce portefeuille sont sales et Poinbase n'accepte que les pièces propres : {2} {1} au maximum.
+140 -30
View File
@@ -22,7 +22,7 @@ tab.report,レポート
wallet.line,{0}:{1}(約{2}クレジット)
wallet.empty,ウォレットは空です。
wallet.total,総額:{0}クレジット
wallet.taint,汚染額:{0}クレジット
wallet.taint,汚染コイン: {0} クレジット
trade.amount,数量
trade.deposit,クレジットを入金してCRCに交換
trade.withdraw,CRCを出金してクレジットに交換
@@ -38,8 +38,6 @@ msg.balance,残高が不足しています。
msg.trade,{0} {1}を支払い、{2} {3}を受け取りました。
msg.deposit,{0}クレジットを入金し、CRCに変換しました。
msg.cashout,{0}クレジットを受け取りました。税:{1}クレジット。
msg.aml_light,コンプライアンス審査:{0}クレジットの汚染資金が保留されました。
msg.aml_heavy,コンプライアンス審査:{0}クレジットの汚染資金が没収され、警察に通報されました。
report.none,マイニングレポートはまだありません。
report.day,{0}日目の夜
report.rigs,稼働リグ:{0}/{1}
@@ -50,6 +48,7 @@ report.burnt,焼損ボード:{0}
report.price,PSC価格:{0}クレジット
nightlog.mined,マイニングリグが{0} PSCを採掘しました。
nightlog.burnt,{0}が1枚焼損した。
nightlog.turbo,{0}をターボブースト:直ちに{1} PSCを採掘。
coinpay.line,取引成立。{0} {1}。
nightlog.coinpay,コイン決済の客からの代金は計{0}クレジットが精算され、コインはホットウォレットに入った。
nightlog.unpowered,{0}台のマイニングリグまたは専用採掘機に電力が供給されていませんでした。
@@ -140,6 +139,7 @@ col.asset,資産
col.amount,数量
col.value,評価額
col.avg,平均単価
col.dirty,汚染分
bank.cash.k,クレジット
bank.rent.k,家賃
bank.rent.v,{0}、期日:{1}日後
@@ -151,13 +151,12 @@ stable.ok,ペグ正常
stable.depeg,デペグ
stable.warn,警告:ステーキング準備率低下
stable.spiral,崩壊中
aml.taint,アカウント汚染率:{0}%
mixer.h,ミキサー
mixer.ok,状態:正常稼働
mixer.sanctioned,状態:制裁中(残り{0}日)。この間に出金されたコインはフラグ付きとなる。
mixer.sanctioned,状態: 制裁中(残り{0}日)。この間に期日を迎える出金は洗浄されず、全額が汚染コインとして戻る。
mixer.pending,入金処理中
mixer.none,処理中の入金はありません。
mixer.col.taint,汚染
mixer.col.taint,汚染分
mixer.col.ready,完了
mixer.day,{0}日目
mixer.deposit,入金
@@ -168,11 +167,12 @@ mixer.go,ミキサーに入金
mixer.min,入金には最低{0}クレジット相当が必要です。
mixer.done,{0} {1} を入金。{2}日目に受取可能。
mixer.upgrade.title,ミキサー
mixer.upgrade.desc,ターミナルのグレーネットページにあるミキサーをアンロック:汚染コインを入金し、数日後に汚染を減らして回収する。ミックス直後のコインは出金審査がより厳格化される。取締期間中はミキサーが制裁対象となる可能性があり、その間に出金されたコインはフラグ付きとなる。
mixer.upgrade.desc,ターミナルのグレーネットページでミキサーをアンロック: 汚染コインを入金すると、数日後に汚染分が減少して戻る。鎮圧期間中はミキサーが制裁を受ける可能性があり、その間に期日を迎える出金は全額が汚染コインとして戻る。
news.sanction.title,ミキサーを制裁リストに追加
news.sanction.desc,警察が複数のミキシングサービスに制裁を実施。今後数日間にミキサーから出金された資金は不審として扱われる。
news.sanction.desc,警察が複数のミキサーサービスに制裁を科した。今後数日間にミキサーから出金された資金は汚染資金として扱われる。
nightlog.mixer,ミキサーから {0} {1} を回収。汚染 {2} → {3}。
nightlog.mixer_flag,(制裁期間中に出金。アドレスはフラグ付き)
nightlog.mixer_flag,(制裁期間中の出金。全額が汚染コインとして記録)
nightlog.mixer_flag.trap,(出金したコインは警察により全額汚染コインとして記録)
nightlog.mixer_rug,ミキサーの運営者が持ち逃げした:{0} {1} は戻ってこなかった。
web3.sub.trade,取引
gray.sub.mixer,ミキサー
@@ -247,7 +247,7 @@ black.kind.Contraband.3,禁制品(高)
black.kind.Tech,ハイテク品(割増)
nightlog.bm.ready,闇市場:{0} が到着。開店後に配達人が届けてくれる。
guide.web3.title,Web3ハンドブック
guide.web3.text,銀行と取引:銀行ページは閲覧専用で、現金、家賃、Poinbase口座のCRCを表示する。入金と出金はPoinbaseの資金ページで行い、先にコインを選ぶ。CRCは銀行口座とウォレットのどちらでも可能で、他のコインはウォレットのみ。銀行からの入金はクレジットをPoinbase口座のCRCに変換し、銀行への出金はCRCをクレジットに戻すが、Poinbase口座の汚染を対象としたコンプライアンス審査が行われる。ウォレットへの出金は受取アドレスの入力が必要で、数回の承認を経て着金する。ガス代は通常の段階で固定され、送るコインから差し引かれる。アドレスの形式やチェックディジットが誤っていると拒否される。形式が正しくても誰も使っていないアドレスに送ったコインは失われる。¶取引:PSCは Probably Stolen Coin の略称で、グラフィックボードや専用採掘機で採掘されるコイン。Poinbaseの現物にはPSC/CRCとSPARK/CRCの2つのペアがあり、チャートで選んだコインに合わせて切り替わる。買いも売りも取引対象のコインの数量で入力する。プレビューには支払う・受け取る決済通貨が表示され、平均価格は手数料込み、スリッページは手数料を含まない。¶Web3の取引ページでは任意の2種類のコインを交換でき、必ずCRCを経由する。CRCが片側なら1つのプール、そうでなければ2つのプールを通り、手数料は2回、スリッページは重なる。プレビューに受取数量、経由するプール数、損失が表示される。ステーブルコイン同士(CRT・CRAとCRC)はステーブルプールを使い、手数料は{StableFee:%}、ペッグ付近ではスリッページがほぼない。ペッグが外れると流動性が薄くなり、崩壊時には流動性が引き揚げられてスリッページが急増する。§ハードウェアウォレット:チェーン上のコインはPLEDGERハードウェアウォレットに保管され、ウォレットごとに残高と汚染が個別に管理される。ウォレットはアイテムで、取引ターミナルにドラッグして読み込む。ターミナルのウォレットページで開き、ターミナル右上で接続・切断する。Web3ページの取引とグレーネットページのショップ、ミキサーは接続されたウォレットを使用する。ウォレットがない、または接続していないときは閲覧のみで、残高はすべて0になる。コイン決済の客は接続されたウォレットに支払う。¶支払い(ショップ、ミキサー、Ponero窓口、出品手数料)はまずターミナルで請求書を発行し、ウォレット側で送金先アドレスを入力して✓を長押しして署名する。アドレスの末尾2文字はチェックサムのため、1文字の誤入力なら通常は検出されるが、偶然有効なアドレスに一致した場合は誰も使っていないアドレスにコインが送られてしまう。DEXでのスワップもウォレットでの署名が必要となる。¶Poinbaseはカストディ口座で、入金はウォレットからPoinbaseの入金アドレスへ送金し(受け付けるのはPSC、CRC、SPARKのみ。規制下の取引所なので闇市場のCRTと革命軍のCRAは受け付けず、入金アドレスへの送金はウォレットが止める)、出金はPoinbaseの資金ページで行う。ウォレットページのエクスプローラーではアドレスで送金履歴を検索でき、実際に行われた送金のみが表示される:自分のウォレットのすべての入出金(送金、DEXスワップなどのコントラクト呼び出し、採掘機の収益、出金、他人から送られたコイン)。Poneroはプライバシーコインのため表示されない。追加のハードウェアウォレットはジャックソン博士から購入し、取引ターミナルにドラッグして追加する(最大{WalletMax}個)。ショップにはより安価なものもある。不要になったウォレットは、ウォレットページで破棄できる。中のコインは永久に失われ、その後このアドレスに送られたものも失われる。¶送金にはガス代が必要で、低速・通常・高速の3段階があり、ガス価格は約1週間周期で上下する。ネットワーク混雑時はガス代が高騰し、低速のトランザクションはメムプールから除外されることがある。送金前にアドレス全体を必ず確認すること。§ステーキング:革命家のプールにCRAをステーキングすると日利が付与され、利息は元本に複利で組み入れられる。出金するにはまず申請しLine truncated
guide.web3.text,"銀行と取引: 銀行ページは閲覧専用で、現金、家賃、Poinbaseアカウント内のCRCを表示する。Poinbaseの資金ページで入出金を管理し、まず通貨を選択する。CRCは銀行口座またはウォレットを経由できるが、他の通貨はウォレット経由のみとなる。銀行から入金するとクレジットがPoinbaseアカウント内のCRCに変換され、銀行へ出金すると審査なしでCRCがクレジットに戻る(Poinbaseアカウントにはクリーンな資金しか入らない)。ウォレットへ出金する場合は送金先アドレスが必要で、数回の承認を経て着金する。ガス代は「標準」に固定され、送金する通貨から差し引かれる。アドレスの形式やチェックサムに誤りがあると拒否されるが、形式が正しくても誰も使っていないアドレスに送金した場合、そのコインは失われる。¶取引: PSCの正式名称はProbably Stolen Coinで、グラフィックボードや専用採掘機で採掘される通貨。Poinbase現物にはチャートで選択した通貨に応じてPSC/CRCとSPARK/CRCの2つの取引ペアがある。買い・売りともに取引する通貨の数量を入力し、プレビューには支払うまたは受け取る決済通貨が表示される。平均価格には手数料が含まれるが、スリッページは含まれない。¶Web3取引ページでは任意の2通貨をスワップできるが、常にCRCを経由する。一方がCRCなら1つのプール、それ以外は2つのプールを経由し、2回の手数料とスリッページの重複が発生する。プレビューには受取数量、経由するプール数、損失が表示される。ステーブルコイン同士のスワップ(CRTまたはCRA対CRC)はステーブルプールを使用し、手数料は{StableFee:%}で、ペッグ付近ではスリッページがほぼ発生しない。デペッグ時は流動性が薄れ、崩壊時には流動性が引き抜かれてスリッページが急増する。§ハードウェアウォレット: オンチェーンの通貨はPLEDGERハードウェアウォレットに保管され、残高と汚染コインはウォレットごとに個別に管理される(各通貨で何枚が汚染されているかを記録。「汚染」を参照)。ウォレットはアイテムであり、取引ターミナルにドラッグして読み込ませる。ターミナルのウォレットページで開き、ターミナル右上から接続・切断を行う。Web3ページの取引やグレーネットページのショップ、ミキサーは接続中のウォレットを使用する。ウォレットがない、または未接続の場合は閲覧専用となり、全残高が0と表示される。通貨で支払う客の代金も接続中のウォレットに入る。¶支払い(ショップ、ミキサー、Poneroデスク、出品手数料)はターミナルで請求書を発行し、ウォレットで送金先アドレスを入力して✓を長押しして署名する。アドレスの末尾2文字はチェックサムのため、1文字の入力ミスは大半が検知されるが、偶然有効なアドレスに一致すると誰も使っていないアドレスに送金されてしまう。DEXでのスワップにもウォレットでの署名が必要。¶Poinbaseはカストディ型アカウントであり、入金はウォレットからPoinbaseの入金用アドレスへ送金して行う(PSC、CRC、SPARKのみ受付。規制準拠の取引所は闇市場のCRTや革命家のCRAを取り扱っておらず、入金用アドレスへの送金はウォレット側で遮断される。入金できるのはクリーンなコインのみ。「汚染」を参照)。出金はPoinbaseの資金ページから開始する。ウォレットページのエクスプローラーではアドレスごとに送金履歴を照会でき、実際に成立した取引のみが表示される。ウォレットのあらゆる入出金(送金、DEXスワップやその他のコントラクト呼出、採掘機の収益、出金、第三者からの送金)が対象。Poneroはプライバシーコインのため表示されない。追加のハードウェアウォレットはジャックソン博士から購入でき、取引ターミナルにドラッグして追加する(最大{WalletMax}個)。ショップにはより安価なものもある。不要になったウォレットはウォレットページで破棄できるが、中のコインは永久に失われ、破棄後にそのアドレスへ送金されたものも消失する。¶送金にはガス代がかかり、「低速」「標準」「高速」の3段階がある。ガス価格はおよそ1週間の周期で上下する。ネットワーク混雑時はガス代が高騰し、低速の取引はメムプLine truncated
courier.odd1,……荷物が届いた。
courier.odd2,(ポケットに手を入れたままだ)ここにサインを。
courier.sign,サイン
@@ -360,6 +360,7 @@ hw.wallet.n,ウォレット {0}
hw.review.own,自身のウォレット:{0}
hw.review.poinbase,Poinbase入金アドレス
poin.deposit,入金アドレス:{0}
poin.deposit.clean,{0}から入金可能なクリーンコイン: {1}
wallets.count,ウォレット {0}/{1}
wallets.col.name,名前
wallets.col.addr,アドレス
@@ -377,6 +378,7 @@ hw.paste,貼り付け
hw.copied,アドレスをコピーしました。
hw.copied.addr,{0} をコピーしました
hw.balance.frozen,(凍結 {0})
hw.balance.dirty,(汚染 {0})
wallets.destroy,破棄
wallets.destroy.sure,破棄の確認
wallets.destroyed,{0} を破棄しました:{1}
@@ -548,7 +550,7 @@ predict.purity.rust,上層水
nightlog.predict.win,予測的中:「{0}」、{1}が払い戻された。
nightlog.predict.lose,予測不的中:「{0}」、賭け金{1}を失った。
nightlog.predict.void,予測無効:「{0}」、賭け金{1}が返還された。
nightlog.predict.caught,インサイダー取引が摘発:「{0}」、賭け金と配当{1}が没収され、詐欺の犯罪証拠が記録された。
nightlog.predict.insider,「{0}」が{1}で決済された。警察はインサイダー取引を疑っており、そのうち {2} CRC が汚染コインとして記録された。
predict.tab.markets,市場
predict.tab.intel,情報
predict.acct.crt,{0}:CRT {1}
@@ -591,10 +593,10 @@ predict.brief.queued,{0}:作戦予定あり
predict.brief.none,{0}:発動可能な作戦なし
predict.brief.can,{0}:{1}を発動可能
predict.clue.push,プレイヤーの価格誘導 {0}%: 操縦リスク高
nightlog.predict.manip,予測が価格操縦と認定:「{0}」、賭け金{1}が没収され、詐欺の犯罪証拠が記録された。
nightlog.predict.manip,「{0}」が{1}で決済された。規制当局は価格操縦を疑っており、そのうち {2} CRC が汚染コインとして記録された。
news.manip.title,取引所が不審な取引を調査
news.manip.desc,規制当局の発表によると、大口現物注文で価格を操作し、デリバティブや予測市場で不当な利益を得た者がおり、関係資金は差し押さえられた。
perp.manip,決済利益{0}が価格操縦と認定され没収。無期限先物口座が{1}日間凍結された。
news.manip.desc,規制当局の発表によると、大口の現物注文で価格を動かし、デリバティブや予測市場から利益を得た者が存在し、関係口座はフラグ付きとなった。
perp.manip,利益 {0} で決済。規制当局は価格操縦を疑っており、犯罪記録に詐欺が{1}件追加された。
web3.sub.ponzi,利回り
ponzi.h,ポンジ・スキーム
ponzi.acct,発行者ウォレット {0}:CRC {1}
@@ -685,7 +687,7 @@ scam.support.name,Poinbaseサポート
scam.support.1,こんにちは、Poinbaseサポートです。お客様のアカウントで異常なログインが検出され、フラグ付きとなりました。
scam.support.2,本日中に確認のため安全アドレス {1} へ {0} CRC を送金してください。確認後すぐに返金されます。
scam.support.3,対応いただけない場合、アカウントは永久に凍結されます。では失礼します、送金を忘れずに。
predict.sold.caught,売却時にインサイダー取引が発覚:元本と利益をすべて没収、詐欺の証拠を記録。
predict.sold.caught,売却し、{0}を回収。警察はインサイダー取引を疑っており、そのうち {1} CRC が汚染コインとして記録された。
predict.row.move,(昨晩 {0} {1})
predict.stats.h,履歴(始値平均 / 実際の発生率)
predict.stats.row,{0}:{1}件、始値平均 {2}、発生率 {3}%
@@ -696,6 +698,7 @@ predict.kind.Price,PSC 価格
predict.kind.Gas,ガス
predict.kind.Cra,CRA 警戒警報
predict.kind.Era,専用採掘機時代
predict.kind.Trial,タンブラー裁判
nightlog.predict.move,予測市場「{0}」が変動 {1} → {2}
nightlog.predict.why,({0})
chart.empty,{0}日足チャート:履歴なし。本日より記録開始。
@@ -707,7 +710,7 @@ news.story.serpico_rev.desc,革命家が警察内部の汚職証拠を入手し
news.story.serpico_bm.title,闇市場が密約を交わす
news.story.serpico_bm.desc,警察の内部文書が闇市場で取引された。以降、ダークネット決済は著しく安定し、CRTの償還に滞りは一切起きていない。
news.story.serpico_sec.title,警察が内部粛清を開始
news.story.serpico_sec.desc,警察は内部汚職の徹底調査と資金の流れの監視強化を発表した。Poinbaseは出金審査を厳格化し、ミキサーサービスが名指しされた。
news.story.serpico_sec.desc,警察は内部腐敗の徹底調査と資金フローの監視強化を発表し、ミキサーサービスが名指しされた。
news.story.eleanor_leak.title,栄養フルーツの育種データが流出
news.story.eleanor_leak.desc,下層の新聞社が栄養フルーツを再び結実させる研究データを公開し、パステルの株価が急落した。上層の資金を管理する保管銀行で焦げ付きが発生し、革命家が勢力を拡大している。
news.story.eleanor_return.title,パステルが盗難データを回収
@@ -752,6 +755,47 @@ news.tumbler.caught.desc,タンブラーとして知られるミキサー運営
news.tumbler.sold.title,ミキサーサービスが別人の手に渡る
news.tumbler.sold.desc,下層のミキサーサービスの署名鍵が闇市場の手に渡り、手数料が即座に引き上げられたとの情報がある。ユーザーは資金の安全を懸念している。
nightlog.tumbler.gift,タンブラーはキードライブを回収し、お礼として {0} CRT を残した({1} に送金済み)。ミキサー手数料が半減した。
agent.name,警察の捜査官
agent.v1.1,警察だ。邪魔するぞ。
agent.v1.2,タンブラーと名乗る人物を探している。オンチェーンでミキサーサービスを運営している男だ。
agent.v1.3,この近辺で目撃されている。
agent.ask,この店に来たことはあるか?
agent.choice.yes,[数回来たと伝える]
agent.choice.no,[見たことがないと伝える]
agent.yes1,協力に感謝する。警察は覚えているぞ。
agent.no1,そうか。何か思い出したら、いつでも連絡してくれ。
agent.search.1,また会ったな。あの男から何か預かっているそうだな。
agent.search.2,定例の捜索だ。協力してもらおう。
agent.search.found,見つけたぞ。これは押収する。
agent.search.none,……何もないな。邪魔した。
tumbler.v4.seized.1,警察が店を捜索したと聞いた。
tumbler.v4.seized.2,鍵は奴らの手に渡った。全ユーザーの記録が洗い出される。
tumbler.v4.seized.3,あんたが奴らに何を話したか、もう知りたくもない。
nightlog.tumbler.leak,警察はタンブラーの署名鍵を使ってミキサー記録を復元した:{0}件の出金で{1}の汚染が洗い落とされており、犯罪記録に詐欺が{2}件追加された。
nightlog.tumbler.leak.none,警察はタンブラーの署名鍵を使ってミキシング記録を復元した。最近ミキサーから何も出金していない。
nightlog.tumbler.note,ドアの隙間からメモが差し入れられていた:「借りは必ず取り戻す。—T」
nightlog.tumbler.tip,タンブラーからのメッセージ:明日、警察がミキサーサービスの取り締まりに動くかもしれない。
news.tumbler.leak.title,警察、ミキサー記録を復元
news.tumbler.leak.desc,警察はミキサーサービスの署名鍵を取得したと発表し、過去1か月間に同サービスを通じて洗浄された資金の追跡を進めるとしている。
news.trial.pro.title,タンブラー裁判 {0} 日目:検察側が優勢
news.trial.def.title,タンブラー裁判 {0} 日目:弁護側が優勢
news.trial.pro.desc1,検察側はミキサーノードのサーバーログを提出し、被告の署名と1件ずつ一致すると主張した。
news.trial.pro.desc2,元パートナーが出廷し、被告は顧客の资金が闇市場由来であると知っていたと証言した。
news.trial.pro.desc3,検察側は、被告のウォレットと複数の詐欺アドレスとの間の送金記録を提示した。
news.trial.def.desc1,弁護側はノード押収時の証拠保全手続きに瑕疵があったと指摘し、一部のログが改ざんされた可能性があると主張した。
news.trial.def.desc2,弁護側の専門家が証言し、ミキサーコントラクトはオープンソースであり、被告が利用者を制御することは不可能であると述べた。
news.trial.def.desc3,検察側の重要証人が法廷で証言を覆し、被告が知っていたかどうかは覚えていないと述べた。
news.trial.convict.title,タンブラーに有罪判決
news.trial.convict.desc,裁判所はタンブラーと名乗るミキサー運営者に対し、マネーロンダリングの有罪判決を下した。警察は、該当のミキサーサービスが再開されることはないと述べている。
news.trial.acquit.title,タンブラーに無罪判決
news.trial.acquit.desc,裁判所は証拠不十分を理由に、タンブラーと名乗るミキサー運営者に無罪を言い渡し釈放した。被告は記者団の質問に答えることなく裁判所を後にした。
news.tumbler.bmrug.title,ミキサーサービスが資金を持ち逃げ
news.tumbler.bmrug.desc,闇市場に引き継がれたミキサーサービスが突如出金を停止し、コントラクト内の資金が移動された。フォーラム上では全資産を失ったと訴えるユーザーが続出している。
mixer.brand,Vortex
news.mixer.rebrand.title,新ミキサーサービス {0} が稼働開始
news.mixer.rebrand.desc,{0} と呼ばれるミキサーサービスが先日公開され、ログなし・バックドアなしを謳っている。そのコントラクトが以前閉鎖されたサービスと酷似しているとの指摘も上がっている。
predict.q.trial,{0}:タンブラーに有罪判決
predict.clue.trial,裁判報道:検察側優勢 {0} 日、弁護側優勢 {1} 日
mixer.status.closed,状態:停止中
yield.sub.pools,収益プール
yield.sub.mine,スキーム発行
@@ -963,18 +1007,84 @@ news.wipe.down.title,{0}が1日で{1}%下落、{2}人がロスカット
news.wipe.down.desc,この1日で{0}無期限先物のロングポジション{1}件がロスカットされ、総額は{2}クレジットに上った。Poinbaseの保険基金残高は{3}クレジット。
news.wipe.up.title,{0}が1日で{1}%上昇、{2}人がロスカット
news.wipe.up.desc,この1日で{0}無期限先物のショートポジション{1}件がロスカットされ、総額は{2}クレジットに上った。Poinbaseの保険基金残高は{3}クレジット。
hot.name,売り手
hot.intro.CRT,最近は物騒で、このCRTを持っているのは危ない。急いで現金にしたい。
hot.intro.PSC,ある採掘場のウォレットが昨夜持ち主を変えた。PSCは今こっちにある。現金しか受け取らない。
hot.intro.PONERO,身代金だ、Poneroで。出所は誰にも追えない。現金のみ。
hot.offer,{0} {1}、今の相場で{2}相当だ。現金{3}で、コインはこの場で送る。
hot.choice.yes,受け取る
hot.choice.no,断る
hot.say.yes,取引成立だ。
hot.say.no,それには手を出さない。
hot.done,送った。俺たちは会っていない。
hot.refused,欲しがる奴はいくらでもいる。
hot.nocash,現金も用意できないのか?ならやめだ。
hot.nowallet,ウォレットがつながっていない。コインをどこに送れと?やめだ。
hot.sting,動くな、警察だ。盗まれた資金の買い取りだな。現金は没収、記録にも残る。
otc.name,OTC客
otc.intro.CRT,最近は物騒で、このCRTを持っているのは危ない。急いで現金にしたい。
otc.intro.PSC,ある採掘場のウォレットが昨夜持ち主を変えた。PSCは今こっちにある。現金しか受け取らない。
otc.intro.PONERO,身代金だ、Poneroで。出所は誰にも追えない。現金のみ。
otc.offer,{0} {1}、時価{2}相当、{4}から送金。全額なら現金{3}、一部だけでも構わない。
otc.intro.sell,手持ちの{0}を現金に換えたい。出所は自分で調べてくれ。
otc.buy.ask,{0} {1}を買いたい、時価{2}相当だ。全額なら現金{3}支払う、一部だけでも構わない。
otc.intro.buy,ここで通貨を取引できると聞いた。現金で買いたい。
otc.say.trade,ターミナルで話そう。
otc.wait,分かった、待ってるよ。
otc.paid,着金した。現金を受け取ってくれ。
tab.otc,カウンター
otc.h,カウンター取引
otc.k.side,顧客
otc.side.sell,{0}売却
otc.side.buy,{0}購入
otc.k.qty,数量
otc.k.value,時価
otc.k.ask,売値(全額)
otc.k.bid,買値(全額)
otc.k.unit,単価
otc.k.addr,顧客アドレス:{0}
otc.lookup,アドレス照会
otc.preview.take,現金{0}を支払い、{1} {2}を受け取る
otc.preview.give,{0} {1}を送金し、現金{2}を受け取る
otc.deal,成約
otc.cancel,見送り
otc.msg.gone,客はすでに立ち去った。
otc.msg.cash,現金が足りない。
otc.msg.sting,相手は潜入捜査官だった:現金{0}が没収され、盗品売買が記録された。
otc.msg.bought,{0} {1}(うち汚染 {3})を受け取り、現金 {2} を支払った。
otc.msg.sold,{0} {1}を送金し、現金{2}を受け取った。
gray.sub.own,自営ミキサー
ownmix.h,自営ミキサー
ownmix.status.closed,閉鎖(ラグプル)
ownmix.status.seized,差し押さえ、第{0}日まで閉鎖
ownmix.status.open,営業中
ownmix.status.paused,一時停止中
ownmix.k.fee,手数料率
ownmix.k.rating,評判(出金に残る汚染)
ownmix.k.heat,警戒度
ownmix.heat.0,低
ownmix.heat.1,中
ownmix.heat.2,高
ownmix.k.anon,匿名セット
ownmix.k.fees,累計手数料
ownmix.col.capital,元本
ownmix.col.earned,収益
ownmix.col.pending,顧客コイン
ownmix.open,営業開始
ownmix.pause,一時停止
ownmix.inject,資金投入
ownmix.withdraw,出金
ownmix.free,出金可能:{0} {1}
ownmix.err,出金可能な数量を超えています。
ownmix.injected,{0} {1}を投入した。
ownmix.withdrawn,{0} {1}を出金した。
ownmix.withdraw.sum,自営ミキサーから{0} {1}を出金
pay.ownmix,自営ミキサーへの資金投入 {0} {1}
ownmix.rug,ラグプル({0})
ownmix.rug.confirm,再度クリックして{0}を持ち逃げ
ownmix.rug.sum,自営ミキサーの全資金を持ち逃げ
ownmix.rugged,{0}を持ち逃げした。ミキサーは閉鎖された。
nightlog.ownmix.in,自営ミキサー:入金{0}件、合計{1}、手数料{2}。
nightlog.ownmix.sting,自営ミキサーへの入金の1件が警察の潜入捜査官によるものだった。故買の罪証が記録された。
nightlog.ownmix.seized,警察が自営ミキサーを摘発:プール内の{0}が全額没収され、故買の罪証が記録された。
news.ownmix.seized.title,警察がミキサーサービスを摘発
news.ownmix.seized.desc,警察は下層で運営されていたミキサーサービスを摘発し、全資金を押収した。運営者の身元は特定されていない。
news.ownmix.rug.title,ミキサーサービスが資金を持ち逃げ
news.ownmix.rug.desc,下層のミキサーサービスが一夜にして閉鎖され、送金途中の全ユーザー資金が消失した。
pay.otc,OTC客 {0} {1}(現金 {2})
otc.upgrade.title,OTCデスク
otc.upgrade.desc,黒いランプの対象に「OTC客」が追加される。出所不明のコインを売りに来る者や、コインを買いに来る者が訪れ、すべての取引は現金で決済される。黒いランプが1個付属。
otc.say.no,今回は見送るよ。
otc.done,送った。俺たちは会っていない。
otc.refused,なら他を当たるよ。
otc.nocash,現金も用意できないのか?ならやめだ。
otc.nowallet,ウォレットがつながっていない。コインをどこに送れと?やめだ。
otc.sting,動くな、警察だ。盗まれた資金の買い取りだな。現金は没収、記録にも残る。
hw.err.poinbase,Poinbaseは{0}を受け付けない。
hw.err.tainted,このウォレットの{1}のうち{0}が汚染されており、Poinbaseが受け付けるのはクリーンなコインのみ:最大 {2} {1}。
+140 -30
View File
@@ -22,7 +22,7 @@ tab.report,Raport
wallet.line,{0}: {1} (ok. {2} kredytów)
wallet.empty,Portfel jest pusty.
wallet.total,Wartość łączna: {0} kredytów
wallet.taint,Wartość skażona: {0} kredytów
wallet.taint,Skażone monety: {0} kred.
trade.amount,Kwota
trade.deposit,Wpłata kredytów jako CRC
trade.withdraw,Wypłata CRC jako kredyty
@@ -38,8 +38,6 @@ msg.balance,Niewystarczające saldo.
msg.trade,"Zapłacono {0} {1}, otrzymano {2} {3}."
msg.deposit,Wpłacono {0} kredytów jako CRC.
msg.cashout,Otrzymano {0} kredytów. Podatek: {1} kredytów.
msg.aml_light,Kontrola zgodności: wstrzymano {0} kredytów skażonych środków.
msg.aml_heavy,Kontrola zgodności: skonfiskowano {0} kredytów skażonych środków i zgłoszono do Ochrony.
report.none,Brak raportu wydobycia.
report.day,Noc dnia {0}
report.rigs,Zasilane koparki: {0} z {1}
@@ -50,6 +48,7 @@ report.burnt,Przepalone karty: {0}
report.price,Cena PSC: {0} kredytów
nightlog.mined,Koparki wydobyły {0} PSC.
nightlog.burnt,Karta {0} uległa przepaleniu.
nightlog.turbo,{0} – turbodoładowanie: natychmiast wydobyto {1} PSC.
coinpay.line,Zgoda. {0} {1}.
nightlog.coinpay,Klienci płacący monetami uregulowali {0} kredytów; monety trafiły do gorącego portfela.
nightlog.unpowered,Koparki bez zasilania: {0}.
@@ -140,6 +139,7 @@ col.asset,Aktywo
col.amount,Kwota
col.value,Wartość
col.avg,Śr. zakup
col.dirty,Skażone
bank.cash.k,Kredyty
bank.rent.k,Czynsz
bank.rent.v,"{0}, termin: {1} d."
@@ -151,13 +151,12 @@ stable.ok,W parytecie
stable.depeg,Utrata parytetu
stable.warn,Ostrzeżenie: niskie pokrycie rezerwy stakingu
stable.spiral,Zapaść
aml.taint,Udział skażenia konta: {0}%
mixer.h,Mikser
mixer.ok,Status: działa normalnie
mixer.sanctioned,Status: objęty sankcjami (pozostałe dni: {0}). Wypłacone w tym czasie monety zostaną oznaczone.
mixer.sanctioned,Status: objęty sankcjami (dni: {0}). Wypłaty przypadające w tym czasie nie są czyszczone i wracają w całości skażone.
mixer.pending,Oczekujące wpłaty
mixer.none,Brak oczekujących wpłat.
mixer.col.taint,Skażenie
mixer.col.taint,Skażone
mixer.col.ready,Gotowe
mixer.day,Dzień {0}
mixer.deposit,Wpłata
@@ -168,11 +167,12 @@ mixer.go,Wpłać do miksera
mixer.min,Minimalna wartość wpłaty: {0} kredytów.
mixer.done,Wpłacono {0} {1}; zwrot w dniu {2}.
mixer.upgrade.title,Mikser
mixer.upgrade.desc,"Odblokowuje mikser w zakładce Szara sieć terminala: wpłacaj skażone monety i odbieraj je po kilku dniach z mniejszym skażeniem. Wypłata świeżo zmiksowanych monet podlega surowszej kontroli; podczas obławy mikser może zostać objęty sankcjami, a wypłacone w tym czasie monety zostaną oznaczone."
mixer.upgrade.desc,"Odblokowuje mikser na stronie Szara sieć w terminalu: wpłacaj skażone monety i odbieraj je kilka dni później z mniejszą liczbą skażonych. Podczas obław mikser może zostać objęty sankcjami, a wypłaty przypadające w tym czasie wracają w całości skażone."
news.sanction.title,Mikser wpisany na listę sankcyjną
news.sanction.desc,Ochrona objęła sankcjami grupę usług miksujących. Środki wypłacone z mikserów w ciągu najbliższych kilku dni będą traktowane jako podejrzane.
news.sanction.desc,Ochrona nałożyła sankcje na grupę mikserów. Środki wypłacone z mikserów w ciągu najbliższych kilku dni będą traktowane jako skażone środki.
nightlog.mixer,Mikser zwrócił {0} {1}; skażenie {2} -> {3}.
nightlog.mixer_flag," (wypłacono w trakcie sankcji; adres oznaczony)"
nightlog.mixer_flag," (wypłacono podczas sankcji; całość oznaczona jako skażona)"
nightlog.mixer_flag.trap," (Ochrona oznacza wypłacone monety jako skażone)"
nightlog.mixer_rug,Operator miksera uciekł ze środkami: {0} {1} nigdy nie wróciło.
web3.sub.trade,Handel
gray.sub.mixer,Mikser
@@ -247,7 +247,7 @@ black.kind.Contraband.3,Kontrabanda (wysoki poziom)
black.kind.Tech,Zaawansowana technologia (marża)
nightlog.bm.ready,Czarny Rynek: {0} na miejscu; kurier pojawi się po otwarciu lokalu.
guide.web3.title,Podręcznik Web3
guide.web3.text,"Bank i handel: strona banku jest tylko do odczytu i pokazuje gotówkę, czynsz oraz CRC na koncie Poinbase. Strona Środki w Poinbase obsługuje wpłaty i wypłaty; najpierw wybiera się monetę: CRC może iść przez konto bankowe lub portfel, pozostałe monety tylko przez portfel. Wpłata z banku zamienia kredyty na CRC na koncie Poinbase; wypłata do banku zamienia CRC z powrotem na kredyty i podlega kontroli zgodności, sprawdzającej skażenie konta Poinbase. Wypłata do portfela wymaga podania adresu odbiorcy i kilku potwierdzeń do zaksięgowania; opłata gas jest stała, na poziomie normalnym, i pobierana z wysyłanej monety. Błędny format adresu lub cyfra kontrolna powoduje odrzucenie; adres o poprawnym formacie, którego nikt nie używa, oznacza utratę wysłanych monet.¶Handel: PSC to skrót od Probably Stolen Coin, monety wydobywanej przez karty graficzne i koparki. Rynek spot Poinbase ma dwie pary, PSC/CRC i SPARK/CRC, zależnie od monety wybranej na wykresie. Zarówno zakup, jak i sprzedaż wpisuje się jako ilość monety bazowej; podgląd pokazuje zapłaconą lub otrzymaną monetę kwotowaną, średnia cena zawiera prowizję, a poślizg nie.¶Strona handlu Web3 pozwala wymienić dowolne dwie monety, zawsze przez CRC: gdy CRC jest po jednej stronie, używana jest jedna pula, w przeciwnym razie dwie, z dwiema opłatami i sumującym się poślizgiem; podgląd pokazuje otrzymaną ilość, liczbę pul i stratę. Wymiany między stablecoinami (CRT lub CRA na CRC) idą przez pule stabilne: prowizja {StableFee:%} i prawie zerowy poślizg blisko powiązania; przy utracie powiązania płynność maleje, a przy krachu zostaje wycofana i poślizg gwałtownie rośnie.§Portfele sprzętowe: monety w łańcuchu znajdują się w portfelach sprzętowych PLEDGER, a każdy z nich ma własne saldo i poziom skażenia. Portfel jest przedmiotem: przeciągnij go na terminal handlowy, aby go załadować. Otwórz go na stronie Portfele w terminalu; połącz i odłącz w prawym górnym rogu terminala. Handel na stronie Web3 oraz sklep i mikser na stronie Szara sieć korzystają z połączonego portfela; bez portfela lub bez połączenia są tylko do odczytu, a wszystkie salda wynoszą 0. Klienci płacący monetami płacą na połączony portfel.¶Płatności (sklep, mikser, kantor Ponero, opłaty za wystawienie) zaczynają się od rachunku w terminalu; wpisz adres odbiorcy w portfelu i przytrzymaj ✓, aby podpisać. Dwa ostatnie znaki adresu to suma kontrolna, więc pomyłka o jeden znak jest zwykle wychwytywana; sporadycznie zdarza się, że okazuje się poprawny i monety trafiają pod adres, z którego nikt nie korzysta. Wymiany na DEX również wymagają podpisu na portfelu.¶Poinbase to konto powiernicze; wpłaty wysyła się z portfela na adres wpłat Poinbase (tylko PSC, CRC i SPARK; regulowana giełda nie przyjmuje CRT Czarnego Rynku ani CRA Rewolucji, a portfel blokuje wysłanie ich na adres wpłat), a wypłaty zleca się na stronie Środki w Poinbase. Eksplorator na stronie Portfele pozwala sprawdzać przelewy według adresu i pokazuje wyłącznie przelewy, które faktycznie miały miejsce: każdy ruch do i z twoich portfeli (przelewy, wymiany na DEX i inne wywołania kontraktów, zyski koparek, wypłaty, monety wysyłane ci przez innych); Ponero to moneta prywatności i nigdy się nie pojawia. Więcej portfeli sprzętowych sprzedaje Dr Jackson; przeciągnij portfel na terminal handlowy, aby go dodać – maksymalnie do {WalletMax}. W sklepie dostępne są tańsze. Niepotrzebny portfel można zniszczyć na stronie Portfele; jego monety przepadają na zawsze, podobnie jak wszystko, co zostanie później wysłane na jego adres.¶Przelewy wymagają opłaty gas: wolnej, normalnej lub szybkiej, a ceny gasu rosną i spadają w cyklu około tygodnia; gdy sieć jest przeciążona, opłaty gas rosną, a powolne transakcje mogą zostać usunięte z mempoola. Przed wysłaniem sprawdź pełny adres.§Staking: CRA ulokowane w puli Rewolucji przynosi dzienny zysk według stawki dziennej, doliczany do wkładu; wypłata wymaga wcześniejszego złożenia wniosku i odczekania czterech dni, bez naliczania odsetek w okresie oczekiwania i bez możliwości wcześniejszego odbioru. CRA jest częściowo zabezpieczone: każde CRA wspierają rezerwa CRC należąca do Rewolucji oraz token równoważący SPARK; pokrycie rezerwy i stopa zabezpieczenia widnieją na stronie stakingu. Odsetki wypłacane są w nowych, niezabezpieczonych CRA, co rozwadnia pokrycie rezerwy, tym szybciej, im więcej w stakingu; przy zbyt niskim pokryciu Rewolucja obniża stawkę. Rewolucja czasem sięga po rezerwę, a wcześniej krążą pogłoski: to zapowiedź. Jeśli potem pokrycie spadnie poniżej {CraCrashRatio:%}, następuje krach: CRA staje się bezwartościowe w ciągu trzech dni, a cztery dni na odblokowanie to za wolno; powyżej tego progu jest tylko dyskonto, a środki z grabieży Górnego poziomu stopniowo odbudowują rezerwę.Line truncated
guide.web3.text,"Bank i handel: strona Bank jest tylko do odczytu i pokazuje gotówkę, czynsz oraz CRC na koncie Poinbase. Strona Środki w Poinbase obsługuje wpłaty i wypłaty; najpierw wybierz monetę: CRC może przejść przez konto bankowe lub portfel, każda inna moneta wyłącznie przez portfel. Wpłata z banku zamienia kredyty na CRC na koncie Poinbase; wypłata do banku zamienia CRC z powrotem na kredyty bez kontroli (konto Poinbase przechowuje wyłącznie czyste środki). Wypłata do portfela wymaga adresu odbiorcy i kilku potwierdzeń przed zaksięgowaniem; opłata gas jest stała na poziomie normalnym i pobierana z wysyłanej monety. Błędny format adresu lub sumy kontrolnej jest odrzucany; poprawnie sformatowany adres, którego nikt nie używa, oznacza utratę monet.¶Handel: PSC oznacza Probably Stolen Coin – monetę wydobywaną przez karty graficzne i koparki. Rynek spot Poinbase ma dwie pary: PSC/CRC i SPARK/CRC, zależnie od monety wybranej na wykresie. Zlecenia kupna i sprzedaży wprowadza się jako kwotę handlowanej monety; podgląd pokazuje płaconą lub otrzymywaną walutę kwotowaną, średnia cena uwzględnia prowizję, a poślizg nie.¶Strona handlu Web3 pozwala na wymianę dowolnych dwóch monet, zawsze za pośrednictwem CRC: z CRC po jednej stronie korzysta z jednej puli, w przeciwnym razie z dwóch pul, z dwiema prowizjami i sumującym się poślizgiem; podgląd pokazuje otrzymaną kwotę, przebyte pule i stratę. Wymiana stablecoinów (CRT lub CRA na CRC) korzysta ze stabilnych pul: prowizja {StableFee:%} i prawie zerowy poślizg w pobliżu parytetu; przy odkotwiczeniu płynność spada, a przy krachu zostaje wycofana i poślizg gwałtownie rośnie.§Portfele sprzętowe: monety w łańcuchu znajdują się w portfelach sprzętowych PLEDGER, z osobnymi saldami i skażonymi monetami dla każdego z nich (dla każdej monety rejestrowana jest liczba skażonych sztuk; patrz: Skażenie). Portfel jest przedmiotem: przeciągnij go na terminal handlowy, aby go załadować. Otwórz go na stronie Portfele w terminalu; łącz i rozłączaj w prawym górnym rogu terminala. Handel na stronie Web3 oraz sklep i mikser na stronie Szara sieć korzystają z połączonego portfela; bez portfela lub połączenia są one tylko do odczytu, a każde saldo wynosi 0. Klienci płacący monetami wpłacają do połączonego portfela.¶Płatności (sklep, mikser, punkt Ponero, opłaty za wystawienie) zaczynają się od rachunku w terminalu; wpisz adres odbiorcy na portfelu i przytrzymaj ✓, aby podpisać. Ostatnie dwa znaki adresu to suma kontrolna, więc literówka o jeden znak jest zwykle wychwytywana; sporadycznie okazuje się poprawna i monety trafiają pod adres, którego nikt nie używa. Wymiany na DEX również wymagają podpisu na portfelu.¶Poinbase to konto powiernicze; wpłaty wysyła się z portfela na adres depozytowy Poinbase (tylko PSC, CRC i SPARK; giełda zgodna z przepisami nie przyjmuje ani CRT z Czarnego Rynku, ani CRA od Rewolucji, a portfel blokuje ich wysłanie na adres depozytowy; wpłacać można tylko czyste monety, patrz: Skażenie), a wypłaty zleca się na stronie Środki w Poinbase. Eksplorator na stronie Portfele wyszukuje transfery według adresu i pokazuje tylko transakcje, które rzeczywiście miały miejsce: każdy przepływ do i z Twoich portfeli (przelewy, wymiany DEX i inne wywołania kontraktów, wypłaty z koparek, wypłaty, monety przesłane przez innych); Ponero to moneta prywatności i nigdy się nie wyświetla. Więcej portfeli sprzętowych sprzedaje Dr Jackson; przeciągnij taki na terminal handlowy, aby go dodać, maksymalnie do {WalletMax}. W sklepie dostępne są tańsze. Niepotrzebny portfel można zniszczyć na stronie Portfele; znajdujące się w nim monety przepadają na zawsze, a wszystko wysłane później na jego adres również zostanie utracone.¶Za przelewy płaci się opłatę gas: wolną, normalną lub szybką, a ceny gasu rosną i spadają w cyklu około tygodniowym; gdy sieć jest przeciążona, opłaty gas rosną, a wolne transakcje mogą zostać usunięte z mempoola. Przed wysłaniem sprawdź pełny adres.§Staking: CRA ulokowane w puli Rewolucji przynosi dzienny zysk według stawki dziennej, doliczany do wkładu; wypłata wymaga wcześniejszego złożenia wniosku i odczekania czterech dni, bez naliczania odsetek w okresie oczekiwania i bez możliwości wcześniejszego odbioru. CRA jest częściowo zabezpieczone: każde CRA wspierają rezerwa CRC należąca do Rewolucji oraz token równoważący SPARK; pokrycie rezerwy i stopa zabezpieczenia widnieją na stronie stakingu. Odsetki wypłacane są w nowych, niezabezpieczonych CRA, co rozwadnia pokrycie rezerwy, tym szybciej, im więcej w stakingu; przy zbyt niskim pokryciu Rewolucja obniża stawkę. Rewolucja czasem sięga po rezerwę, a wcześniej krążą pogłoski: to zapowiedź. Jeśli potem pokrycie spadnie poniżej {CraCrashRatio:%}, następuje krach: CRA staje się bezwartościowe w ciLine truncated
courier.odd1,...Paczka dotarła.
courier.odd2,(Trzyma rękę w kieszeni.) Podpisz tutaj.
courier.sign,Podpisz
@@ -360,6 +360,7 @@ hw.wallet.n,Portfel {0}
hw.review.own,Własny portfel: {0}
hw.review.poinbase,Adres wpłaty Poinbase
poin.deposit,Adres wpłaty: {0}
poin.deposit.clean,Czyste monety do wpłaty ({0}): {1}
wallets.count,Portfele {0}/{1}
wallets.col.name,Nazwa
wallets.col.addr,Adres
@@ -377,6 +378,7 @@ hw.paste,Wklej
hw.copied,Skopiowano adres.
hw.copied.addr,Skopiowano {0}
hw.balance.frozen," (zamrożone {0})"
hw.balance.dirty," (skażone: {0})"
wallets.destroy,Zniszcz
wallets.destroy.sure,Potwierdź
wallets.destroyed,Zniszczono {0}: {1}
@@ -548,7 +550,7 @@ predict.purity.rust,Woda z Górnego poziomu
nightlog.predict.win,"Trafna predykcja: „{0}”, wypłacono {1}."
nightlog.predict.lose,"Błędna predykcja: „{0}”, utracono stawkę {1}."
nightlog.predict.void,"Predykcja unieważniona: „{0}”, zwrócono stawkę {1}."
nightlog.predict.caught,"Wykryto transakcję poufną: „{0}”, skonfiskowano stawkę i wygraną w kwocie {1}, odnotowano oszustwo."
nightlog.predict.insider,"""{0}"" rozliczono na {1}. Policja podejrzewa transakcje poufne; {2} CRC z tego oznaczono jako skażone."
predict.tab.markets,Rynki
predict.tab.intel,Informacje
predict.acct.crt,{0}: CRT {1}
@@ -591,10 +593,10 @@ predict.brief.queued,{0}: operacja zaplanowana
predict.brief.none,{0}: brak operacji do rozpoczęcia
predict.brief.can,{0}: można rozpocząć {1}
predict.clue.push,Przesunięcie ceny o {0}%: wysokie ryzyko
nightlog.predict.manip,"Predykcja uznana za manipulację rynkową: „{0}”, skonfiskowano stawkę {1}, odnotowano oszustwo."
nightlog.predict.manip,"""{0}"" rozliczono na {1}. Organy nadzorcze podejrzewają manipulację ceną; {2} CRC z tego oznaczono jako skażone."
news.manip.title,Giełda bada nietypowe transakcje
news.manip.desc,"Organy regulacyjne donoszą, że za pomocą dużych zleceń spot manipulowano ceną, by czerpać zyski z instrumentów pochodnych i predykcji; środki przejęto."
perp.manip,Zysk z zamknięcia w kwocie {0} uznano za manipulację rynkową i skonfiskowano; konto kontraktów wieczystych zamrożone na {1} dni.
news.manip.desc,"Regulatorzy donoszą, że ktoś wpłynął na cenę dużymi zleceniami spot, czerpiąc zyski z instrumentów pochodnych i rynków predykcyjnych; powiązane konta zostały oznaczone."
perp.manip,Zamknięto z zyskiem {0}. Organy nadzorcze podejrzewają manipulację rynkową; do twojej kartoteki trafia oszustwo: {1}.
web3.sub.ponzi,Lokaty
ponzi.h,Piramida finansowa
ponzi.acct,Portfel emitenta {0}: CRC {1}
@@ -685,7 +687,7 @@ scam.support.name,Wsparcie Poinbase
scam.support.1,"Dzień dobry, tutaj wsparcie Poinbase. Wykryliśmy nietypowe logowanie na twoim koncie i zostało ono oznaczone."
scam.support.2,Prześlij dzisiaj {0} CRC na bezpieczny adres {1} w celu weryfikacji; zostaną zwrócone zaraz potem.
scam.support.3,"W przeciwnym razie konto zostanie trwale zamrożone. Muszę lecieć, pamiętaj o przelewie."
predict.sold.caught,"Wykryto transakcje poufne przy sprzedaży: stawka i zysk skonfiskowane, oszustwo w kartotece."
predict.sold.caught,"Sprzedano, otrzymano {0}. Policja podejrzewa transakcje poufne; {1} CRC z tego oznaczono jako skażone."
predict.row.move," (ubiegłej nocy {0} {1})"
predict.stats.h,Historia (śr. cena otwarcia / wskaźnik rzeczywisty)
predict.stats.row,"{0}: rynki: {1}, śr. otwarcie {2}, wystąpienie: {3}%"
@@ -696,6 +698,7 @@ predict.kind.Price,Cena PSC
predict.kind.Gas,Gas
predict.kind.Cra,Ostrzeżenie CRA
predict.kind.Era,Era koparek
predict.kind.Trial,Proces Tumblera
nightlog.predict.move,Predykcja „{0}”: zmiana {1} -> {2}
nightlog.predict.why," ({0})"
chart.empty,Wykres dzienny {0}: brak historii; zapis od dzisiaj.
@@ -707,7 +710,7 @@ news.story.serpico_rev.desc,"Rewolucja rzekomo zdobyła dowody korupcji wewnątr
news.story.serpico_bm.title,Czarny Rynek zawiera ciche porozumienie
news.story.serpico_bm.desc,"Wewnętrzny dokument Ochrony zmienił właściciela na Czarnym Rynku. Od tamtej pory rozliczenia w Darknecie są zauważalnie stabilniejsze, a realizacja CRT ani razu nie zawiodła."
news.story.serpico_sec.title,Ochrona rozpoczyna wewnętrzną czystkę
news.story.serpico_sec.desc,"Ochrona zapowiedziała pełne śledztwo w sprawie wewnętrznej korupcji i zaostrzenie monitorowania przepływów finansowych. Poinbase zaostrza kontrolę wypłat, a miksery zostały wymienione z nazwy."
news.story.serpico_sec.desc,Ochrona ogłosiła pełne śledztwo w sprawie wewnętrznej korupcji oraz ściślejsze monitorowanie przepływów finansowych; wymieniono usługi mikserów.
news.story.eleanor_leak.title,Wyciek danych o hodowli nutriowoców
news.story.eleanor_leak.desc,"Gazeta z Dolnego poziomu opublikowała badania pozwalające nutriowocom na ponowne wytwarzanie nasion; akcje Pastelle runęły. Bank powierniczy przechowujący fundusze Górnego poziomu zgłasza złe długi, a Rewolucja rośnie w siłę."
news.story.eleanor_return.title,Pastelle odzyskuje skradzione dane
@@ -752,6 +755,47 @@ news.tumbler.caught.desc,"Operator miksera znany jako Tumbler został aresztowan
news.tumbler.sold.title,Usługa miksera zmienia właściciela
news.tumbler.sold.desc,"Krążą słuchy, że klucze do podpisywania usługi miksera z Dolnego poziomu trafiły w ręce Czarnego Rynku, a opłaty natychmiast wzrosły. Użytkownicy obawiają się o swoje środki."
nightlog.tumbler.gift,Tumbler odebrał dysk z kluczem i w podziękowaniu zostawił {0} CRT (wysłano na {1}); opłaty miksera zmniejszono o połowę.
agent.name,Agent Bezpieczeństwa
agent.v1.1,"Ochrona. Przepraszam, że przeszkadzam."
agent.v1.2,Szukamy kogoś posługującego się pseudonimem Tumbler. Prowadzi usługę miksowania na łańcuchu.
agent.v1.3,Był widziany w tej okolicy.
agent.ask,Był u ciebie w sklepie?
agent.choice.yes,[Powiedz: kilka razy]
agent.choice.no,[Powiedz: nigdy go nie widziałem]
agent.yes1,Dziękuję za współpracę. Ochrona to zapamięta.
agent.no1,"Tak? Jeśli coś ci się przypomni, wiesz, gdzie nas znaleźć."
agent.search.1,"Znowu się spotykamy. Słyszeliśmy, że przechowujesz coś dla tego człowieka."
agent.search.2,Rutynowe przeszukanie. Proszę o współpracę.
agent.search.found,Mamy to. Zabieramy to.
agent.search.none,...Nic. Przepraszam za kłopot.
tumbler.v4.seized.1,"Słyszałem, że Ochrona przeszukała twój sklep."
tumbler.v4.seized.2,Mają teraz klucz. Wyciągną rejestry każdego użytkownika.
tumbler.v4.seized.3,"Cokolwiek im powiedziałeś, nie chcę już wiedzieć."
nightlog.tumbler.leak,Ochrona użyła klucza podpisu Tumblera do odtworzenia historii miksowania: w ramach wypłat ({0}) usunięto {1} skażenia; do twojej kartoteki trafia oszustwo: {2}.
nightlog.tumbler.leak.none,Ochrona użyła klucza podpisu Tumblera do odtworzenia rejestrów miksowania. Ostatnio nie wypłacono niczego z miksera.
nightlog.tumbler.note,"Pod drzwi wsunięto liścik: ""Jesteś mi coś winien i odbiorę to. —T"""
nightlog.tumbler.tip,Wiadomość od Tumblera: Ochrona może jutro wziąć na cel usługi mikserów.
news.tumbler.leak.title,Ochrona odtwarza rejestry miksera
news.tumbler.leak.desc,"Ochrona podaje, że zdobyła klucz podpisu usługi miksera i użyje go do namierzenia środków wypranych za jej pośrednictwem w minionym miesiącu."
news.trial.pro.title,"Proces Tumblera, dzień {0}: oskarżenie zyskuje przewagę"
news.trial.def.title,"Proces Tumblera, dzień {0}: obrona zyskuje przewagę"
news.trial.pro.desc1,"Oskarżenie przedstawiło logi serwera z węzła miksera, twierdząc, że pokrywają się one jeden do jednego z podpisami oskarżonego."
news.trial.pro.desc2,"Były wspólnik zeznał, że oskarżony wiedział o pochodzeniu środków klientów z Czarnego Rynku."
news.trial.pro.desc3,Oskarżenie ujawniło transfery między portfelem oskarżonego a kilkoma adresami powiązanymi z oszustwami.
news.trial.def.desc1,"Obrona wytknęła uchybienia w procedurze zabezpieczania dowodów podczas zajęcia węzła, twierdząc, że część logów mogła zostać zmodyfikowana."
news.trial.def.desc2,"Biegły powołany przez obronę zeznał, że kontrakt miksera jest kodem open source, a oskarżony nie ma wpływu na to, kto go używa."
news.trial.def.desc3,"Kluczowy świadek oskarżenia zmienił zeznania na sali sądowej, twierdząc, że nie pamięta, czy oskarżony miał tego świadomość."
news.trial.convict.title,Tumbler uznany za winnego
news.trial.convict.desc,"Sąd uznał operatora miksera znanego jako Tumbler za winnego prania brudnych pieniędzy. Ochrona informuje, że usługa miksera nie zostanie wznowiona."
news.trial.acquit.title,Tumbler uniewinniony
news.trial.acquit.desc,"Powołując się na brak wystarczających dowodów, sąd uniewinnił operatora miksera znanego jako Tumbler i nakazał jego zwolnienie. Opuścił gmach sądu bez odpowiedzi na pytania dziennikarzy."
news.tumbler.bmrug.title,Usługa miksera znika ze środkami
news.tumbler.bmrug.desc,"Usługa miksera przejęta przez Czarny Rynek niespodziewanie wstrzymała wypłaty, a środki z jej kontraktu zostały wyprowadzone. Użytkownicy na forach donoszą o utracie całego majątku."
mixer.brand,Vortex
news.mixer.rebrand.title,Nowa usługa miksera {0} trafia do sieci
news.mixer.rebrand.desc,"Usługa miksera o nazwie {0} ruszyła niedawno w sieci, deklarując brak logów i backdoorów. Niektórzy zauważyli jednak, że jej kontrakt łudząco przypomina wcześniej zamkniętą usługę."
predict.q.trial,{0}: Tumbler uznany za winnego
predict.clue.trial,"Relacje z procesu: przewaga oskarżenia (dni: {0}), przewaga obrony (dni: {1})"
mixer.status.closed,Status: wyłączony
yield.sub.pools,Pule zysku
yield.sub.mine,Emisja piramidy
@@ -963,18 +1007,84 @@ news.wipe.down.title,"{0} spada o {1}% w jeden dzień, {2} traderów zlikwidowan
news.wipe.down.desc,"W ciągu ostatniego dnia zlikwidowano {1} długich pozycji na kontraktach perp {0}, łącznie {2} kredytów. Fundusz ubezpieczeniowy Poinbase wynosi {3} kredytów."
news.wipe.up.title,"{0} rośnie o {1}% w jeden dzień, {2} traderów zlikwidowanych"
news.wipe.up.desc,"W ciągu ostatniego dnia zlikwidowano {1} krótkich pozycji na kontraktach perp {0}, łącznie {2} kredytów. Fundusz ubezpieczeniowy Poinbase wynosi {3} kredytów."
hot.name,Sprzedawca
hot.intro.CRT,"Ostatnio jest gorąco, a te CRT parzą w ręce. Potrzebuję gotówki, szybko."
hot.intro.PSC,Portfel pewnej kopalni zmienił wczoraj właściciela. PSC jest teraz u mnie i biorę tylko gotówkę.
hot.intro.PONERO,"Okup, w Ponero. Nikt tego nie wyśledzi. Tylko gotówka."
hot.offer,"{0} {1}, warte {2} po dzisiejszym kursie. Daj mi {3} w gotówce, a monety są twoje od razu."
hot.choice.yes,Przyjmij
hot.choice.no,Odmów
hot.say.yes,Umowa stoi.
hot.say.no,Nie tknę tego.
hot.done,Monety wysłane. Nigdy się nie spotkaliśmy.
hot.refused,Inni chętnie wezmą.
hot.nocash,Nie masz nawet gotówki? Daj spokój.
hot.nowallet,"Brak podłączonego portfela, gdzie mam wysłać monety? Daj spokój."
hot.sting,"Nie ruszaj się. Ochrona. Skup kradzionych środków: gotówka zostaje skonfiskowana, a to trafi do twoich akt."
otc.name,Klient OTC
otc.intro.CRT,"Ostatnio jest gorąco, a te CRT parzą w ręce. Potrzebuję gotówki, szybko."
otc.intro.PSC,Portfel pewnej kopalni zmienił wczoraj właściciela. PSC jest teraz u mnie i biorę tylko gotówkę.
otc.intro.PONERO,"Okup, w Ponero. Nikt tego nie wyśledzi. Tylko gotówka."
otc.offer,"{0} {1}, o wartości {2} po dzisiejszym kursie, wysłane z {4}. {3} w gotówce za całość albo weź część."
otc.intro.sell,"Mam trochę {0}, chcę to wymienić na gotówkę. Sprawdź, skąd pochodzą, jeśli chcesz."
otc.buy.ask,"Chcę kupić {0} {1}, o wartości {2} po dzisiejszym kursie. {3} w gotówce za całość albo sprzedaj mi część."
otc.intro.buy,"Podobno wymieniasz tu monety. Kupuję, za gotówkę."
otc.say.trade,Przejdźmy do terminala.
otc.wait,"W porządku, poczekam."
otc.paid,Dotarło. Oto gotówka.
tab.otc,Lada
otc.h,Handel przy ladzie
otc.k.side,Klient
otc.side.sell,Sprzedaje {0}
otc.side.buy,Kupuje {0}
otc.k.qty,Kwota
otc.k.value,Wartość rynkowa
otc.k.ask,Żądana (całość)
otc.k.bid,Oferowana (całość)
otc.k.unit,Cena jednostkowa
otc.k.addr,Adres klienta: {0}
otc.lookup,Sprawdź
otc.preview.take,"Zapłać {0} w gotówce, otrzymaj {1} {2}"
otc.preview.give,"Wyślij {0} {1}, otrzymaj {2} w gotówce"
otc.deal,Zgoda
otc.cancel,Pas
otc.msg.gone,Klient odszedł.
otc.msg.cash,Za mało gotówki.
otc.msg.sting,"Klient był tajniakiem: skonfiskowano {0} w gotówce, odnotowano paserstwo."
otc.msg.bought,"Otrzymano {0} {1} (w tym skażone: {3}), zapłacono gotówką: {2}."
otc.msg.sold,"Wysłano {0} {1}, otrzymano {2} w gotówce."
gray.sub.own,Mój mikser
ownmix.h,Mój mikser
ownmix.status.closed,Zamknięty (rug pull)
ownmix.status.seized,"Zajęty, zamknięty do dnia {0}"
ownmix.status.open,Czynny
ownmix.status.paused,Wstrzymany
ownmix.k.fee,Prowizja
ownmix.k.rating,Reputacja (skażenie wypłat)
ownmix.k.heat,Rozgłos
ownmix.heat.0,Niski
ownmix.heat.1,Średni
ownmix.heat.2,Wysoki
ownmix.k.anon,Zbiór anonimowości
ownmix.k.fees,Zarobione prowizje
ownmix.col.capital,Kapitał
ownmix.col.earned,Zyski
ownmix.col.pending,Monety klientów
ownmix.open,Otwórz
ownmix.pause,Wstrzymaj
ownmix.inject,Zasil
ownmix.withdraw,Wypłata
ownmix.free,Do wypłaty: {0} {1}
ownmix.err,Przekracza kwotę dostępną do wypłaty.
ownmix.injected,Zasilono {0} {1}.
ownmix.withdrawn,Wypłacono {0} {1}.
ownmix.withdraw.sum,Wypłata {0} {1} z mojego miksera
pay.ownmix,Zasilenie mojego miksera {0} {1}
ownmix.rug,Rug pull ({0})
ownmix.rug.confirm,"Kliknij ponownie, aby zabrać {0}"
ownmix.rug.sum,Zabierz wszystkie środki z mojego miksera
ownmix.rugged,Zabrano {0}; mikser został zamknięty.
nightlog.ownmix.in,"Mój mikser: wpłaty: {0}, łącznie {1}, prowizje: {2}."
nightlog.ownmix.sting,Wpłata do mojego miksera pochodziła od tajnego funkcjonariusza Ochrony; odnotowano paserstwo.
nightlog.ownmix.seized,"Ochrona zajęła mój mikser: pula {0} została skonfiskowana, odnotowano paserstwo."
news.ownmix.seized.title,Ochrona zamyka serwis miksujący
news.ownmix.seized.desc,Ochrona zamknęła serwis miksujący działający na Dolnym poziomie i skonfiskowała wszystkie jego środki. Tożsamość operatora nie została ustalona.
news.ownmix.rug.title,Serwis miksujący znika ze środkami
news.ownmix.rug.desc,"Serwis miksujący na Dolnym poziomie zamknął się z dnia na dzień, a wszystkie przetwarzane środki użytkowników zniknęły."
pay.otc,Klient OTC {0} {1} (gotówka {2})
otc.upgrade.title,Punkt OTC
otc.upgrade.desc,"Dodaje cel Czarnej lampy: Klienci OTC. Jedni przychodzą sprzedać monety nieznanego pochodzenia, inni kupić monety; wszystkie transakcje są rozliczane w gotówce. Zawiera jedną Czarną lampę."
otc.say.no,Tym razem spasuję.
otc.done,Monety wysłane. Nigdy się nie spotkaliśmy.
otc.refused,To poszukam kogoś innego.
otc.nocash,Nie masz nawet gotówki? Daj spokój.
otc.nowallet,"Brak podłączonego portfela, gdzie mam wysłać monety? Daj spokój."
otc.sting,"Nie ruszaj się. Ochrona. Skup kradzionych środków: gotówka zostaje skonfiskowana, a to trafi do twoich akt."
hw.err.poinbase,Poinbase nie przyjmuje {0}.
hw.err.tainted,W tym portfelu skażone {1}: {0}. Poinbase przyjmuje tylko czyste monety: maksymalnie {2} {1}.
+140 -30
View File
@@ -22,7 +22,7 @@ tab.report,Отчёт
wallet.line,{0}: {1} (около {2} кредитов)
wallet.empty,Кошелёк пуст.
wallet.total,Общая стоимость: {0} кредитов
wallet.taint,«Грязные» средства: {0} кредитов
wallet.taint,Грязные монеты (кредиты): {0}
trade.amount,Сумма
trade.deposit,Депозит кредитов в CRC
trade.withdraw,Вывод CRC в кредиты
@@ -38,8 +38,6 @@ msg.balance,Недостаточно средств.
msg.trade,"Оплачено {0} {1}, получено {2} {3}."
msg.deposit,Внесено {0} кредитов в CRC.
msg.cashout,Получено {0} кредитов. Налог {1} кредитов.
msg.aml_light,Проверка соответствия: удержаны «грязные» средства на сумму {0} кредитов.
msg.aml_heavy,"Проверка соответствия: изъяты «грязные» средства на сумму {0} кредитов, направлен отчёт в Службу безопасности."
report.none,Отчётов о майнинге пока нет.
report.day,Ночь дня {0}
report.rigs,Фермы с питанием: {0} из {1}
@@ -50,6 +48,7 @@ report.burnt,Сгорело видеокарт: {0}
report.price,Цена PSC: {0} кредитов
nightlog.mined,Майнинг-фермы добыли {0} PSC.
nightlog.burnt,Сгорела видеокарта {0}.
nightlog.turbo,"{0}: турбоускорение, сразу добыто {1} PSC."
coinpay.line,Сделка: {0} {1}.
nightlog.coinpay,Оплата монетами составила {0} кредитов; средства поступили в горячий кошелёк.
nightlog.unpowered,Майнинг-ферм или асик-майнеров без питания: {0}.
@@ -140,6 +139,7 @@ col.asset,Актив
col.amount,Количество
col.value,Оценка
col.avg,Ср. покупка
col.dirty,Грязные
bank.cash.k,Кредиты
bank.rent.k,Аренда
bank.rent.v,"{0}, оплата через {1} дн."
@@ -151,13 +151,12 @@ stable.ok,Привязан
stable.depeg,Отвязан
stable.warn,Внимание: покрытие резерва стейкинга на исходе
stable.spiral,Крах
aml.taint,Доля загрязнения счёта: {0}%
mixer.h,Миксер
mixer.ok,Статус: работает нормально
mixer.sanctioned,Статус: под санкциями (дней до снятия: {0}). Выведенные в этот период монеты помечаются.
mixer.sanctioned,"Статус: под санкциями (ещё {0} дн.). Выплаты, приходящиеся на этот период, не очищаются и возвращаются полностью «грязными»."
mixer.pending,Депозиты в обработке
mixer.none,Нет депозитов в обработке.
mixer.col.taint,Загрязнение
mixer.col.taint,Грязные
mixer.col.ready,Готовность
mixer.day,День {0}
mixer.deposit,Депозит
@@ -168,11 +167,12 @@ mixer.go,Депозит в миксер
mixer.min,Депозит должен составлять не менее {0} кредитов.
mixer.done,Внесено {0} {1}; возврат в день {2}.
mixer.upgrade.title,Миксер
mixer.upgrade.desc,"Разблокирует миксер на странице «Серая сеть» в терминале: вносите «грязные» монеты и забирайте их через несколько дней с меньшим загрязнением. Вывод недавно смешанных монет проверяется строже; во время зачисток миксер может попасть под санкции, а выведенные в этот период монеты помечаются."
mixer.upgrade.desc,"Разблокирует миксер на странице терминала «Серая сеть»: внесите «грязные» монеты и заберите их через несколько дней — «грязных» станет меньше. Во время зачисток миксер может попасть под санкции, а выплаты, приходящиеся на этот период, возвращаются полностью «грязными»."
news.sanction.title,Миксер внесён в санкционный список
news.sanction.desc,"Служба безопасности ввела санкции против ряда миксер-сервисов. Средства, выведенные из миксеров в ближайшие дни, будут считаться подозрительными."
news.sanction.desc,"Служба безопасности ввела санкции против ряда сервисов микширования. Средства, выведенные из миксеров в ближайшие дни, будут считаться «грязными» средствами."
nightlog.mixer,Миксер вернул {0} {1}; загрязнение: {2} -> {3}.
nightlog.mixer_flag," (выведено во время санкций; адрес помечен)"
nightlog.mixer_flag," (выведено во время санкций; всё помечено как «грязное»)"
nightlog.mixer_flag.trap," (Служба безопасности помечает выведенные монеты как «грязные»)"
nightlog.mixer_rug,Оператор миксера сбежал с деньгами: {0} {1} не вернулись.
web3.sub.trade,Торговля
gray.sub.mixer,Миксер
@@ -247,7 +247,7 @@ black.kind.Contraband.3,Контрабанда (высок.)
black.kind.Tech,Высокие технологии (наценка)
nightlog.bm.ready,Чёрный рынок: доставлен товар «{0}»; курьер заглянет после открытия.
guide.web3.title,Руководство по Web3
guide.web3.text,"Банк и торговля: страница банка только для чтения и показывает наличные, аренду и CRC на счёте Poinbase. Страница «Средства» в Poinbase отвечает за ввод и вывод; сначала выбирается монета: CRC можно вводить и выводить через банковский счёт или кошелёк, остальные монеты только через кошелёк. Депозит из банка конвертирует кредиты в CRC на счёте Poinbase; вывод в банк меняет CRC обратно на кредиты и проходит проверку соответствия, оценивающую загрязнение счёта Poinbase. Вывод на кошелёк требует адрес получателя и нескольких подтверждений до зачисления; комиссия за газ фиксирована на обычном уровне и списывается с отправляемой монеты. Неверный формат адреса или контрольная сумма приводят к отказу; адрес с верным форматом, которым никто не пользуется, означает потерю отправленных монет.¶Торговля: PSC означает Probably Stolen Coin — это монета, которую добывают видеокарты и асик-майнеры. На спотовом рынке Poinbase две пары, PSC/CRC и SPARK/CRC, по монете, выбранной на графике. И покупка, и продажа вводятся как количество торгуемой монеты; в предпросмотре видно, сколько котируемой монеты уйдёт или придёт, средняя цена включает комиссию, проскальзывание — нет.¶На торговой странице Web3 можно обменять любые две монеты, всегда через CRC: если CRC с одной стороны, используется один пул, иначе два пула, две комиссии и суммирующееся проскальзывание; предпросмотр показывает получаемое количество, число пулов и потери. Обмен стейблкоинов (CRT или CRA на CRC) идёт через стабильные пулы: комиссия {StableFee:%} и почти нулевое проскальзывание возле привязки; при отвязке ликвидность редеет, при обвале её выводят, и проскальзывание резко растёт.§Аппаратные кошельки: ончейн-монеты хранятся на аппаратных кошельках PLEDGER, баланс и загрязнение у каждого свои. Кошелёк — это предмет: перетащите его на торговый терминал, чтобы загрузить. Открывайте его на странице «Кошельки» терминала; подключайте и отключайте в правом верхнем углу терминала. Торговля на странице Web3, магазин и миксер на странице «Серая сеть» используют подключённый кошелёк; без кошелька или без подключения они доступны только для просмотра, а все балансы равны 0. Покупатели, платящие монетами, платят на подключённый кошелёк.¶Платежи (магазин, миксер, пункт обмена Ponero, комиссия за размещение) начинаются со счёта на терминале; введите адрес получателя на кошельке и удерживайте ✓, чтобы подписать. Последние два символа адреса — контрольная сумма, поэтому опечатка в один символ обычно отсекается; изредка адрес случайно оказывается валидным, и монеты уходят на адрес, которым никто не пользуется. Для обмена на DEX также требуется подпись на кошельке.¶Poinbase — кастодиальный аккаунт; депозит отправляется с кошелька на адрес депозита Poinbase (только PSC, CRC и SPARK; регулируемая биржа не принимает ни CRT чёрного рынLine truncated
guide.web3.text,"Банк и торговля: страница «Банк» доступна только для чтения и отображает наличные, аренду и CRC на счёте Poinbase. Страница Poinbase «Средства» управляет депозитом и выводом; сначала выберите монету: CRC можно переводить через банковский счёт или кошелёк, все остальные монеты — только через кошелёк. Депозит с банковского счёта конвертирует кредиты в CRC на счёте Poinbase; вывод на банковский счёт конвертирует CRC обратно в кредиты без проверки (на счёте Poinbase хранятся исключительно чистые деньги). Для вывода на кошелёк требуется адрес получателя и несколько подтверждений сети; комиссия за газ фиксирована по обычному тарифу и списывается из отправляемой монеты. Неверный формат адреса или контрольная сумма отклоняются; если адрес отформатирован верно, но никем не используется, монеты будут потеряны навсегда.¶Торговля: PSC расшифровывается как Probably Stolen Coin — это монета, добываемая видеокартами и асик-майнерами. На спотовом рынке Poinbase есть две пары: PSC/CRC и SPARK/CRC, переключающиеся вслед за монетой, выбранной на графике. Покупка и продажа задаются как количество торгуемой монеты; в предпросмотре отображается отдаваемая или получаемая котируемая монета, средняя цена включает комиссию, а проскальзывание — нет.¶Страница торговли Web3 позволяет обменивать любые две монеты, всегда через CRC: если CRC находится на одной из сторон, используется один пул, иначе — два пула с двумя комиссиями и суммирующимся проскальзыванием; в предпросмотре отображаются полученная сумма, пройденные пулы и потери. Обмен стейблкоинов (CRT или CRA к CRC) использует стабильные пулы: комиссия {StableFee:%} и почти нулевое проскальзывание вблизи привязки; при потере привязки ликвидность падает, а при крахе она полностью изымается, и проскальзывание резко возрастает.§Аппаратные кошельки: монеты в блокчейне хранятся на аппаратных кошельках PLEDGER, на каждом из которых ведутся свои балансы и учёт «грязных» монет (для каждой монеты отслеживается, сколько из них «грязные»; см. «Загрязнение»). Кошелёк — это предмет: перетащите его на торговый терминал, чтобы загрузить. Открывайте его на странице терминала «Кошельки»; подключение и отключение выполняются в правом верхнем углу терминала. Торговля на странице Web3, а также магазин и миксер на странице «Серая сеть» используют подключённый кошелёк; без кошелька или подключения они доступны только для просмотра, и все балансы показывают 0. Покупатели, расплачивающиеся монетами, переводят оплату на подключённый кошелёк.¶Платежи (магазин, миксер, стойка Ponero, плата за листинг) начинаются со счёта на терминале; введите адрес получателя на кошельке и удерживайте ✓, чтобы подписать. Последние два символа адреса — контрольная сумма, поэтому опечатка в один символ обычно отслеживается; изредка адрес случайно оказLine truncated
courier.odd1,...Посылка пришла.
courier.odd2,(Его рука остаётся в кармане.) Подпиши здесь.
courier.sign,Подписать
@@ -360,6 +360,7 @@ hw.wallet.n,Кошелёк {0}
hw.review.own,Свой кошелёк: {0}
hw.review.poinbase,Адрес депозита Poinbase
poin.deposit,Адрес депозита: {0}
poin.deposit.clean,Чистые монеты {0} для депозита: {1}
wallets.count,Кошельки {0}/{1}
wallets.col.name,Название
wallets.col.addr,Адрес
@@ -377,6 +378,7 @@ hw.paste,Вставить
hw.copied,Адрес скопирован.
hw.copied.addr,Скопировано: {0}
hw.balance.frozen," (заморожено {0})"
hw.balance.dirty," (грязных: {0})"
wallets.destroy,Уничтожить
wallets.destroy.sure,Подтвердить
wallets.destroyed,Уничтожен {0}: {1}
@@ -548,7 +550,7 @@ predict.purity.rust,Вода верхнего уровня
nightlog.predict.win,"Прогноз сбылся: «{0}», выплачено {1}."
nightlog.predict.lose,"Прогноз не оправдался: «{0}», ставка {1} потеряна."
nightlog.predict.void,"Прогноз аннулирован: «{0}», ставка {1} возвращена."
nightlog.predict.caught,"Вскрыта инсайдерская сделка: «{0}», ставка и выигрыш {1} конфискованы, зафиксировано мошенничество."
nightlog.predict.insider,«{0}»: расчёт {1}. Полиция подозревает инсайдерскую сделку; {2} CRC помечено как «грязные».
predict.tab.markets,Рынки
predict.tab.intel,Информация
predict.acct.crt,{0}: CRT {1}
@@ -591,10 +593,10 @@ predict.brief.queued,{0}: операция запланирована
predict.brief.none,{0}: нечего запускать
predict.brief.can,{0}: можно начать {1}
predict.clue.push,Вы сдвинули цену на {0}%: рискованно
nightlog.predict.manip,"Прогноз признан манипуляцией ценой: «{0}», ставка {1} конфискована, зафиксировано мошенничество."
nightlog.predict.manip,«{0}»: расчёт {1}. Регуляторы подозревают манипуляцию ценой; {2} CRC помечено как «грязные».
news.manip.title,Биржа расследует подозрительные сделки
news.manip.desc,Регуляторы сообщают: некто манипулировал ценой крупными спотовыми ордерами и получал прибыль на деривативах и прогнозах; средства изъяты.
perp.manip,"Прибыль {0} при закрытии признана манипуляцией ценой и изъята; счёт бессрочных фьючерсов заморожен, дней заморозки: {1}."
news.manip.desc,Регуляторы сообщают: кто-то сдвинул цену крупными спотовыми ордерами и получил прибыль на деривативах и предсказаниях; причастные счета помечены.
perp.manip,Позиция закрыта с прибылью {0}. Регуляторы подозревают манипуляцию ценой; в досье занесены улики мошенничества: {1}.
web3.sub.ponzi,Доходность
ponzi.h,Схема Понци
ponzi.acct,Кошелёк эмитента {0}: CRC {1}
@@ -685,7 +687,7 @@ scam.support.name,Поддержка Poinbase
scam.support.1,"Здравствуйте, служба поддержки Poinbase. Мы зафиксировали подозрительный вход в ваш аккаунт, он был помечен."
scam.support.2,"Пожалуйста, переведите сегодня {0} CRC на безопасный адрес {1} для проверки; средства вернут сразу после неё."
scam.support.3,"Иначе аккаунт будет заморожен навсегда. Мне пора, не забудьте перевести."
predict.sold.caught,"Инсайдерская сделка раскрыта при продаже: ставка и прибыль конфискованы, зафиксировано мошенничество."
predict.sold.caught,"Продано, получено {0}. Полиция подозревает инсайдерскую сделку; {1} CRC помечено как «грязные»."
predict.row.move," (прошлой ночью {0} {1})"
predict.stats.h,История (ср. цена открытия / факт. частота)
predict.stats.row,"{0}: рынков: {1}, ср. открытие {2}, сбылось {3}%"
@@ -696,6 +698,7 @@ predict.kind.Price,Цена PSC
predict.kind.Gas,Газ
predict.kind.Cra,Предупреждение CRA
predict.kind.Era,Эра асик-майнеров
predict.kind.Trial,Суд над Тумблером
nightlog.predict.move,Прогноз «{0}» изменился: {1} -> {2}
nightlog.predict.why," ({0})"
chart.empty,Дневной график {0}: истории пока нет; запись с сегодняшнего дня.
@@ -707,7 +710,7 @@ news.story.serpico_rev.desc,"Сообщается, что Революционе
news.story.serpico_bm.title,Чёрный рынок заключил негласную сделку
news.story.serpico_bm.desc,"Внутренний документ Службы безопасности перешёл из рук в руки на Чёрном рынке. С тех пор расчёты в Даркнете заметно стабилизировались, а погашение CRT проходит без сбоев."
news.story.serpico_sec.title,Служба безопасности начинает внутреннюю чистку
news.story.serpico_sec.desc,"Служба безопасности объявила о масштабном расследовании внутренней коррупции и усилении контроля за денежными потоками. Poinbase ужесточает проверку вывода средств, а миксеры были названы напрямую."
news.story.serpico_sec.desc,Служба безопасности объявила о полном расследовании внутренней коррупции и усилении контроля за денежными потоками; отдельно упомянуты миксеры.
news.story.eleanor_leak.title,Утечка данных о селекции нутрифруктов
news.story.eleanor_leak.desc,"Газета Нижнего уровня опубликовала данные исследования, позволяющего нутрифруктам снова давать семена; акции Пастель рухнули. Банк, хранящий средства Верхнего уровня, сообщил о безнадёжных долгах, а влияние Революционеров растёт."
news.story.eleanor_return.title,Пастель возвращает украденные данные
@@ -752,6 +755,47 @@ news.tumbler.caught.desc,"Оператор миксера, известный к
news.tumbler.sold.title,Миксер сменил владельца
news.tumbler.sold.desc,"По слухам, ключи подписи миксера с Нижнего уровня попали в руки Чёрного рынка, и комиссии тут же выросли. Пользователи опасаются за свои средства."
nightlog.tumbler.gift,Тумблер забрал ключ-накопитель и оставил {0} CRT в знак благодарности (отправлено на {1}); комиссии миксера снижены вдвое.
agent.name,Агент безопасности
agent.v1.1,Служба безопасности. Извините за беспокойство.
agent.v1.2,Мы ищем человека по кличке Тумблер. Он держит миксер в сети.
agent.v1.3,Его видели в этом районе.
agent.ask,Он заходил к вам в магазин?
agent.choice.yes,[Сказать: заходил пару раз]
agent.choice.no,[Сказать: никогда не видел]
agent.yes1,Спасибо за содействие. Служба безопасности этого не забудет.
agent.no1,"Вот как. Если что-нибудь вспомните, вы знаете, где нас найти."
agent.search.1,"Снова здравствуйте. До нас дошли слухи, что вы кое-что храните для того человека."
agent.search.2,Плановый обыск. Просьба сотрудничать.
agent.search.found,Нашли. Мы это забираем.
agent.search.none,...Ничего. Извините за беспокойство.
tumbler.v4.seized.1,"Слышал, Служба безопасности обыскала твой магазин."
tumbler.v4.seized.2,Ключ теперь у них. Они поднимут записи каждого пользователя.
tumbler.v4.seized.3,Что ты им сказал — знать больше не хочу.
nightlog.tumbler.leak,"Служба безопасности использовала ключ подписи Тумблера для восстановления записей миксера: выплат: {0}, отмыто загрязнения: {1}; в досье занесены улики мошенничества: {2}."
nightlog.tumbler.leak.none,"Служба безопасности использовала ключ подписи Тумблера, чтобы восстановить записи микширования. В последнее время вы ничего не выводили из миксера."
nightlog.tumbler.note,"Под дверь подсунули записку: «Ты мне задолжал, и я своё заберу. — Т»"
nightlog.tumbler.tip,Сообщение от Тумблера: завтра Служба безопасности может взяться за миксеры.
news.tumbler.leak.title,Служба безопасности восстановила записи миксера
news.tumbler.leak.desc,"Служба безопасности заявляет о получении ключа подписи миксера. С его помощью будут отслежены средства, отмытые через сервис за последний месяц."
news.trial.pro.title,"Суд над Тумблером, день {0}: перевес обвинения"
news.trial.def.title,"Суд над Тумблером, день {0}: перевес защиты"
news.trial.pro.desc1,"Обвинение предъявило серверные логи узла миксера, заявив, что они по транзакциям совпадают с подписями подсудимого."
news.trial.pro.desc2,"Бывший партнёр дал показания, заявив, что подсудимый заведомо знал о происхождении средств клиентов с Чёрного рынка."
news.trial.pro.desc3,Обвинение продемонстрировало переводы между кошельком подсудимого и несколькими адресами мошенников.
news.trial.def.desc1,"Защита указала на нарушения при изъятии узла и сборе улик, заявив, что часть логов могла быть изменена."
news.trial.def.desc2,"Эксперт защиты дал показания: контракт миксера — это открытый исходный код, и подсудимый не контролирует его пользователей."
news.trial.def.desc3,"Ключевой свидетель обвинения изменил показания в суде, заявив, что не помнит, знал ли подсудимый."
news.trial.convict.title,Тумблер признан виновным
news.trial.convict.desc,"Суд признал оператора миксера по кличке Тумблер виновным в отмывании денег. Служба безопасности заявляет, что сервис микширования больше не откроется."
news.trial.acquit.title,Тумблер оправдан
news.trial.acquit.desc,"За недостатком улик суд оправдал оператора миксера по кличке Тумблер и освободил его из-под стражи. Он покинул здание суда, не ответив на вопросы журналистов."
news.tumbler.bmrug.title,Сервис микширования скрылся со средствами
news.tumbler.bmrug.desc,"Сервис микширования, перешедший под контроль Чёрного рынка, внезапно прекратил выплаты, а средства из контракта были выведены. Пользователи на форумах жалуются на потерю всех средств."
mixer.brand,Vortex
news.mixer.rebrand.title,Новый сервис микширования {0} начал работу
news.mixer.rebrand.desc,"Недавно начал работу сервис микширования под названием {0}, заявляющий об отсутствии логов и бэкдоров. Некоторые заметили, что его контракт подозрительно похож на ранее закрытый сервис."
predict.q.trial,{0}: Тумблер признан виновным
predict.clue.trial,"Сводки суда: перевес обвинения — {0} дн., перевес защиты — {1} дн."
mixer.status.closed,Статус: закрыт
yield.sub.pools,Доходные пулы
yield.sub.mine,Запуск схемы
@@ -963,18 +1007,84 @@ news.wipe.down.title,"{0} упал на {1}% за день, ликвидиров
news.wipe.down.desc,За прошедшие сутки ликвидировано {1} длинных позиций по бессрочным контрактам {0} на общую сумму {2} кредитов. Страховой фонд Poinbase составляет {3} кредитов.
news.wipe.up.title,"{0} вырос на {1}% за день, ликвидировано трейдеров: {2}"
news.wipe.up.desc,За прошедшие сутки ликвидировано {1} коротких позиций по бессрочным контрактам {0} на общую сумму {2} кредитов. Страховой фонд Poinbase составляет {3} кредитов.
hot.name,Продавец
hot.intro.CRT,"Сейчас неспокойно, а этот CRT жжёт руки. Мне нужны наличные, быстро."
hot.intro.PSC,"Кошелёк одной майнинг-фермы прошлой ночью сменил владельца. PSC теперь у меня, и я беру только наличные."
hot.intro.PONERO,"Выкуп, в Ponero. Никто не отследит. Только наличные."
hot.offer,"{0} {1}, по сегодняшнему курсу это {2}. Дайте мне {3} наличными, и монеты сразу ваши."
hot.choice.yes,Взять
hot.choice.no,Отказаться
hot.say.yes,По рукам.
hot.say.no,Я в это не полезу.
hot.done,Монеты отправлены. Мы не встречались.
hot.refused,Найдутся другие.
hot.nocash,Даже наличных нет? Забудьте.
hot.nowallet,"Кошелёк не подключён, куда слать монеты? Забудьте."
hot.sting,"Не двигаться. Служба безопасности. Скупка краденых средств: наличные изъяты, это пойдёт в ваше досье."
otc.name,OTC-клиент
otc.intro.CRT,"Сейчас неспокойно, а этот CRT жжёт руки. Мне нужны наличные, быстро."
otc.intro.PSC,"Кошелёк одной майнинг-фермы прошлой ночью сменил владельца. PSC теперь у меня, и я беру только наличные."
otc.intro.PONERO,"Выкуп, в Ponero. Никто не отследит. Только наличные."
otc.offer,"{0} {1}, стоимость по текущему курсу: {2}, перевод с {4}. {3} наличными за всё, либо возьмите часть."
otc.intro.sell,"Есть немного {0}, хочу обменять на наличные. Можешь проверить, откуда они, если хочешь."
otc.buy.ask,"Куплю {0} {1}, стоимость по текущему курсу: {2}. За всё дам {3} наличными, либо продайте часть."
otc.intro.buy,"Слышал, здесь меняют монеты. Я покупаю, за наличные."
otc.say.trade,Обсудим у терминала.
otc.wait,"Ладно, я подожду."
otc.paid,Пришло. Вот ваши наличные.
tab.otc,Стойка
otc.h,Торговля у стойки
otc.k.side,Клиент
otc.side.sell,Продажа {0}
otc.side.buy,Покупка {0}
otc.k.qty,Сумма
otc.k.value,Рын. стоимость
otc.k.ask,Запрос (всего)
otc.k.bid,Предл. (всего)
otc.k.unit,Цена за ед.
otc.k.addr,Адрес клиента: {0}
otc.lookup,Проверить
otc.preview.take,"Заплатить {0} наличными, получить {1} {2}"
otc.preview.give,"Отправить {0} {1}, получить {2} наличными"
otc.deal,Сделка
otc.cancel,Отказ
otc.msg.gone,Клиент ушёл.
otc.msg.cash,Недостаточно наличных.
otc.msg.sting,"Клиент был под прикрытием: конфисковано {0} наличными, зафиксирован сбыт краденого."
otc.msg.bought,"Получено {0} {1} (из них грязных: {3}), выплачено наличными: {2}."
otc.msg.sold,"Отправлено {0} {1}, получено {2} наличными."
gray.sub.own,Мой миксер
ownmix.h,Мой миксер
ownmix.status.closed,Закрыт (рагпул)
ownmix.status.seized,"Опечатан, закрыт до дня {0}"
ownmix.status.open,Открыт
ownmix.status.paused,Приостановлен
ownmix.k.fee,Комиссия
ownmix.k.rating,Репутация (остаток «грязи»)
ownmix.k.heat,Шумиха
ownmix.heat.0,Низкая
ownmix.heat.1,Средняя
ownmix.heat.2,Высокая
ownmix.k.anon,Анонимный пул
ownmix.k.fees,Всего комиссий
ownmix.col.capital,Капитал
ownmix.col.earned,Доход
ownmix.col.pending,Монеты клиентов
ownmix.open,Открыть
ownmix.pause,Пауза
ownmix.inject,Внести
ownmix.withdraw,Вывод
ownmix.free,Доступно для вывода: {0} {1}
ownmix.err,Превышает доступную для вывода сумму.
ownmix.injected,Внесено {0} {1}.
ownmix.withdrawn,Выведено {0} {1}.
ownmix.withdraw.sum,Вывод {0} {1} из моего миксера
pay.ownmix,Взнос в мой миксер: {0} {1}
ownmix.rug,Совершить рагпул ({0})
ownmix.rug.confirm,"Нажмите ещё раз, чтобы забрать {0}"
ownmix.rug.sum,Забрать все средства из моего миксера
ownmix.rugged,Забрано {0}; миксер закрыт.
nightlog.ownmix.in,"Мой миксер: депозитов: {0}, всего {1}, комиссии: {2}."
nightlog.ownmix.sting,Один из депозитов в моём миксере поступил от офицера под прикрытием; зафиксирован сбыт краденого.
nightlog.ownmix.seized,"Служба безопасности закрыла мой миксер: пул в {0} конфискован, зафиксирован сбыт краденого."
news.ownmix.seized.title,Служба безопасности закрыла сервис-миксер
news.ownmix.seized.desc,"Служба безопасности закрыла сервис-миксер, работавший на Нижнем уровне, и конфисковала все его средства. Личность оператора пока не установлена."
news.ownmix.rug.title,Сервис-миксер исчез вместе со средствами
news.ownmix.rug.desc,"Сервис-миксер на Нижнем уровне внезапно закрылся за ночь, а все средства пользователей в обработке исчезли."
pay.otc,OTC-клиент {0} {1} (наличные {2})
otc.upgrade.title,OTC-стойка
otc.upgrade.desc,"Добавляет новую цель для Чёрной лампы: OTC-клиенты. Одни приходят продать монеты неизвестного происхождения, другие — купить монеты; все сделки рассчитываются наличными. Включает одну Чёрную лампу."
otc.say.no,В этот раз я откажусь.
otc.done,Монеты отправлены. Мы не встречались.
otc.refused,Тогда поищу кого-нибудь ещё.
otc.nocash,Даже наличных нет? Забудьте.
otc.nowallet,"Кошелёк не подключён, куда слать монеты? Забудьте."
otc.sting,"Не двигаться. Служба безопасности. Скупка краденых средств: наличные изъяты, это пойдёт в ваше досье."
hw.err.poinbase,Poinbase не принимает {0}.
hw.err.tainted,"В этом кошельке грязных {1}: {0}, а Poinbase принимает только чистые монеты: максимум {2} {1}."
+141 -31
View File
@@ -21,7 +21,7 @@ tab.report,报告
wallet.line,{0}:{1}(约 {2} 信用点)
wallet.empty,钱包为空。
wallet.total,总值:{0} 信用点
wallet.taint,污点:{0} 信用点
wallet.taint,脏币:{0} 信用点
trade.amount,数量
trade.deposit,存入信用点
trade.withdraw,提取 CRC
@@ -37,8 +37,6 @@ msg.balance,余额不足。
msg.trade,支付 {0} {1},获得 {2} {3}。
msg.deposit,已存入 {0} 信用点,兑换为 CRC。
msg.cashout,到账 {0} 信用点。税 {1} 信用点。
msg.aml_light,合规审查:{0} 信用点的污点资金被扣留。
msg.aml_heavy,合规审查:{0} 信用点的污点资金被没收,并已上报治安部。
report.none,暂无挖矿报告。
report.day,第 {0} 天夜间
report.rigs,供电矿架:{0} / {1}
@@ -49,6 +47,7 @@ report.burnt,烧毁的显卡:{0}
report.price,PSC 价格:{0} 信用点
nightlog.mined,矿架挖出 {0} PSC。
nightlog.burnt,一张 {0} 烧毁了。
nightlog.turbo,{0} 涡轮增压,立刻挖出 {1} PSC。
coinpay.line,成交,{0} {1}。
nightlog.coinpay,代币支付共结算 {0} 信用点,已兑换并存入热钱包。
nightlog.unpowered,{0} 台矿架或矿机未通电。
@@ -139,6 +138,7 @@ col.asset,"币种"
col.amount,"数量"
col.value,"估值"
col.avg,"买入均价"
col.dirty,脏币
bank.cash.k,"信用点"
bank.rent.k,"房租"
bank.rent.v,"{0},{1} 天后支付"
@@ -150,13 +150,12 @@ stable.ok,"锚定正常"
stable.depeg,"脱锚"
stable.warn,"预兆:储备覆盖率告急"
stable.spiral,"崩盘中"
aml.taint,"账户污点比例:{0}%"
mixer.h,混币器
mixer.ok,状态:运行正常
mixer.sanctioned,状态:已被制裁(还有 {0} 天),此期间提出的币视为被标记。
mixer.sanctioned,状态:已被制裁(还有 {0} 天),此期间到期的出款不洗,整笔算脏币。
mixer.pending,在途存入
mixer.none,暂无在途存入。
mixer.col.taint,污点
mixer.col.taint,脏币
mixer.col.ready,到期
mixer.day,第 {0} 天
mixer.deposit,存入
@@ -167,11 +166,12 @@ mixer.go,存入混币器
mixer.min,存入价值不得低于 {0} 信用点。
mixer.done,已存入 {0} {1},第 {2} 天提回。
mixer.upgrade.title,混币器
mixer.upgrade.desc,"解锁终端灰网页里的混币器:把带污点的币存进去,几天后提回,污点降低。出金审查对刚从混币器出来的币更严格;镇压期间混币器可能被制裁,期间提出的币会被标记。"
mixer.upgrade.desc,解锁终端灰网页里的混币器:把脏币存进去,几天后提回,脏币变少。镇压期间混币器可能被制裁,制裁期间到期的出款整笔算脏币。
news.sanction.title,混币器被列入制裁名单
news.sanction.desc,"安保部门宣布制裁一批混币服务。此后几天从混币器提出的资金将被视为可疑。"
news.sanction.desc,安保部门宣布制裁一批混币服务。此后几天从混币器提出的资金将被视为赃款。
nightlog.mixer,混币器提回 {0} {1},污点 {2} → {3}。
nightlog.mixer_flag,(制裁期间提出,地址被标记)
nightlog.mixer_flag,(制裁期间提出,整笔算脏币)
nightlog.mixer_flag.trap,(提出的币被治安部整笔算作脏币)
nightlog.mixer_rug,混币器的运营者卷款跑路了:{0} {1} 没有提回。
web3.sub.trade,交易
gray.sub.mixer,混币器
@@ -246,7 +246,7 @@ black.kind.Contraband.3,违禁品(高)
black.kind.Tech,高科技物品(加价)
nightlog.bm.ready,商店:{0} 到货,开店后会有快递员上门。
guide.web3.title,Web3 手册
guide.web3.text,"银行与交易:银行页只读,显示现金、房租和 Poinbase 账户里的 CRC。Poinbase 的资金页管入金和出金,先选币种:CRC 可以走银行账户或钱包,其他币只能走钱包。从银行入金把信用点换成 Poinbase 账户里的 CRC;出金到银行把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。出金到钱包要填收款地址,等几次确认才到账,Gas 费固定按普通档、从转出的币里扣;地址格式或校验位不对会被拒绝,格式对但没人用的地址,币转过去就没了。¶交易:PSC 全称 Probably Stolen Coin,是显卡和矿机挖出的币。Poinbase 现货有 PSC/CRC 和 SPARK/CRC 两个交易对,跟着行情图选中的币切换。买入和卖出都填所交易币的数量,预览显示要付或能得到的计价币,均价含手续费,滑点不含。¶Web3 交易页可以任选两种币互换,实际都经 CRC 中转:CRC 在一端走一个池,否则走两个池,付两次手续费、滑点叠加,预览里会显示得到的数量、经过的池数和损耗。稳定币之间(CRT、CRA 对 CRC)走稳定币池:手续费 {StableFee:%},挂钩附近几乎没有滑点;脱锚时流动性变差,崩盘时流动性被撤走,滑点急剧变大。§硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点分别计算。钱包是一件物品,拖到交易终端上加载;在终端的钱包页打开,在终端右上角连接或断开。Web3 页的交易和灰网页的商店、混币器用已连接的钱包,没有钱包或没有连接时只能看,余额都是 0;用币付款的顾客也进已连接的钱包。¶付款(商店、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。¶Poinbase 是托管账户,入金要从钱包转到它的充值地址(只收 PSC、CRC、SPARK;黑市的 CRT、革命军的 CRA 合规交易所不收,往充值地址转会被钱包拦下),出金在 Poinbase 的资金页发起。钱包页的浏览器可以按地址查转账记录,只显示真实发生的转账:你的钱包的每一笔进出(转账、DEX 兑换和其他合约调用、矿机收益、出金、别人转来的币);Ponero 是隐私币,不显示。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 {WalletMax} 个;商店里也有更便宜的。不要的钱包可以在钱包页销毁,里面的币永久丢失,之后发到这个地址的币也丢了。¶转账要付 Gas 费,分慢、普通、快三档,Gas 价格大约一周一个起落;网络拥堵时 Gas 费变贵,慢档可能被挤出内存池。转账前核对完整地址。§质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。CRA 是部分抵押的:每份 CRA 由革命军持有的 CRC 储备和平衡代币 SPARK 共同支撑,储备覆盖率和抵押率显示在质押页。质押利息以没有抵押的新 CRA 付出,会稀释储备覆盖率,质押的人越多稀释越快;覆盖率太低革命军会降息。革命军有时会挪用储备,先有风声,那是预兆;挪用后覆盖率跌破 {CraCrashRatio:%} 就崩盘:三天内 CRA 归零,四天的解锁期来不及;没跌破只是折价,劫掠上层带来的资金会慢慢把储备补回来。¶官方铸造:存入 CRC 和 SPARK(比例按当前抵押率,界面预览需要多少)铸造 CRA,排 {CraMintDaysMin}~{CraMintDaysMax} 天,铸出的 CRA 直接进质押;销毁把 CRA 换回 CRC 和 SPARK,等待和解锁期一样长,储备不足时 CRC 份额按覆盖率打折。排队的铸造和销毁,官方可能整笔不兑现:每晚的概率是 CRA 价格低于 1 的幅度乘以 {CraRugPerDepeg},价格不低于 1 就不会。¶质押可以存 CRA、CRC、CRT、SPARK:后三种先在二级市场按市价换成 CRA(经 CRC 中转,付手续费和滑点)再质押。SPARK 价格波动很大,崩盘时会跟着暴跌。质押、铸造、销毁都是合约调用,要在钱包上签名。§矿机:RatDigg 矿机(M1 / M2 / M3)是独立的机器,算力比显卡大得多、耗电更省,要放电池才挖,可以放模块,不占矿架槽位;只在商店的矿机商那里有,要黑市声望(M1 {AsicRepM1}、M2 {AsicRepM2}、M3 {AsicRepM3}),偶尔有二手的。烧毁后须按顺序把电子元件拖到它身上修复,修复后健康降低 {AsicRepairHealthCut}。第 {EraMin} 到 {EraMax} 天之间会有一次""矿机时代"",提前几天上报纸预告,之后大矿场的耗电成本砍半,全网算力涨上去,显卡矿工的收益被摊薄,低档显卡Line truncated
guide.web3.text,"银行与交易:银行页只读,显示现金、房租和 Poinbase 账户里的 CRC。Poinbase 的资金页管入金和出金,先选币种:CRC 可以走银行账户或钱包,其他币只能走钱包。从银行入金把信用点换成 Poinbase 账户里的 CRC;出金到银行把 CRC 换回信用点,不做审查(Poinbase 账户里只有干净的钱)。出金到钱包要填收款地址,等几次确认才到账,Gas 费固定按普通档、从转出的币里扣;地址格式或校验位不对会被拒绝,格式对但没人用的地址,币转过去就没了。¶交易:PSC 全称 Probably Stolen Coin,是显卡和矿机挖出的币。Poinbase 现货有 PSC/CRC 和 SPARK/CRC 两个交易对,跟着行情图选中的币切换。买入和卖出都填所交易币的数量,预览显示要付或能得到的计价币,均价含手续费,滑点不含。¶Web3 交易页可以任选两种币互换,实际都经 CRC 中转:CRC 在一端走一个池,否则走两个池,付两次手续费、滑点叠加,预览里会显示得到的数量、经过的池数和损耗。稳定币之间(CRT、CRA 对 CRC)走稳定币池:手续费 {StableFee:%},挂钩附近几乎没有滑点;脱锚时流动性变差,崩盘时流动性被撤走,滑点急剧变大。§硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和脏币分别计算(每种币分别记脏币数量,见“污点”一节)。钱包是一件物品,拖到交易终端上加载;在终端的钱包页打开,在终端右上角连接或断开。Web3 页的交易和灰网页的商店、混币器用已连接的钱包,没有钱包或没有连接时只能看,余额都是 0;用币付款的顾客也进已连接的钱包。¶付款(商店、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。¶Poinbase 是托管账户,入金要从钱包转到它的充值地址(只收 PSC、CRC、SPARK;黑市的 CRT、革命军的 CRA 合规交易所不收,往充值地址转会被钱包拦下;每种币只收干净的枚数,见“污点”一节),出金在 Poinbase 的资金页发起。钱包页的浏览器可以按地址查转账记录,只显示真实发生的转账:你的钱包的每一笔进出(转账、DEX 兑换和其他合约调用、矿机收益、出金、别人转来的币);Ponero 是隐私币,不显示。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 {WalletMax} 个;商店里也有更便宜的。不要的钱包可以在钱包页销毁,里面的币永久丢失,之后发到这个地址的币也丢了。¶转账要付 Gas 费,分慢、普通、快三档,Gas 价格大约一周一个起落;网络拥堵时 Gas 费变贵,慢档可能被挤出内存池。转账前核对完整地址。§质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。CRA 是部分抵押的:每份 CRA 由革命军持有的 CRC 储备和平衡代币 SPARK 共同支撑,储备覆盖率和抵押率显示在质押页。质押利息以没有抵押的新 CRA 付出,会稀释储备覆盖率,质押的人越多稀释越快;覆盖率太低革命军会降息。革命军有时会挪用储备,先有风声,那是预兆;挪用后覆盖率跌破 {CraCrashRatio:%} 就崩盘:三天内 CRA 归零,四天的解锁期来不及;没跌破只是折价,劫掠上层带来的资金会慢慢把储备补回来。¶官方铸造:存入 CRC 和 SPARK(比例按当前抵押率,界面预览需要多少)铸造 CRA,排 {CraMintDaysMin}~{CraMintDaysMax} 天,铸出的 CRA 直接进质押;销毁把 CRA 换回 CRC 和 SPARK,等待和解锁期一样长,储备不足时 CRC 份额按覆盖率打折。排队的铸造和销毁,官方可能整笔不兑现:每晚的概率是 CRA 价格低于 1 的幅度乘以 {CraRugPerDepeg},价格不低于 1 就不会。¶质押可以存 CRA、CRC、CRT、SPARK:后三种先在二级市场按市价换成 CRA(经 CRC 中转,付手续费和滑点)再质押。SPARK 价格波动很大,崩盘时会跟着暴跌。质押、铸造、销毁都是合约调用,要在钱包上签名。§矿机:RatDigg 矿机(M1 / M2 / M3)是独立的机器,算力比显卡大得多、耗电更省,要放电池才挖,可以放模块,不占矿架槽位;只在商店的矿机商那里有,要黑市声望(M1 {AsicRepM1}、M2 {AsicRepM2}、M3 {AsicRepM3}),偶尔有二手的。烧毁后须按顺序把电子元件拖到它身上修复,修复后健康降低 {AsicRepairHealthCut}。第 {EraMin} 到 {EraMax} 天之间会有一次""矿机时代"",提前几天上报纸预Line truncated
courier.odd1,……东西到了。
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
courier.sign,签收
@@ -359,10 +359,11 @@ hw.wallet.n,"钱包 {0}"
hw.review.own,"自己的钱包:{0}"
hw.review.poinbase,Poinbase 充值地址
poin.deposit,"充值地址:{0}"
poin.deposit.clean,{0} 里能充值的干净币:{1}
wallets.count,"钱包 {0}/{1}"
wallets.col.name,名称
wallets.col.addr,地址
wallets.col.taint,污点
wallets.col.taint,脏币
wallets.open,打开
wallets.use,连接
wallets.current,已连接
@@ -376,6 +377,7 @@ hw.paste,粘贴
hw.copied,地址已复制。
hw.copied.addr,"已复制 {0}"
hw.balance.frozen,"(冻结 {0})"
hw.balance.dirty,(脏币 {0})
wallets.destroy,销毁
wallets.destroy.sure,确认销毁
wallets.destroyed,"已销毁{0}:{1}"
@@ -547,7 +549,7 @@ predict.purity.rust,上层水
nightlog.predict.win,预测押中:「{0}」,兑付 {1}。
nightlog.predict.lose,预测押错:「{0}」,本金 {1} 没了。
nightlog.predict.void,预测作废:「{0}」,退回本金 {1}。
nightlog.predict.caught,内幕下注被查:「{0}」,本金和收益 {1} 全部没收,记诈骗罪证。
nightlog.predict.insider,「{0}」结算 {1}。警方怀疑这是内幕交易,其中 {2} CRC 记为脏币。
predict.tab.markets,盘口
predict.tab.intel,情报
predict.acct.crt,链上钱包 {0}:CRT {1}
@@ -590,10 +592,10 @@ predict.brief.queued,{0}:行动已排期
predict.brief.none,{0}:暂时发起不了
predict.brief.can,{0}:可发起 {1}
predict.clue.push,玩家推价 {0}%,操纵风险高
nightlog.predict.manip,预测被判操纵:「{0}」,本金 {1} 没收,记诈骗罪证。
nightlog.predict.manip,「{0}」结算 {1}。监管部门怀疑你操纵了价格,其中 {2} CRC 记为脏币。
news.manip.title,交易所调查异常交易
news.manip.desc,监管部门通报,有人用大额现货单推动价格,从合约和预测里获利,涉事资金已被追缴。
perp.manip,平仓盈利 {0} 被判操纵价格,已没收,永续账户冻结 {1} 天。
news.manip.desc,监管部门通报,有人用大额现货单推动价格,从合约和预测里获利,涉事账户已被标记。
perp.manip,平仓盈利 {0}。监管部门怀疑你操纵了价格,记诈骗罪证 {1}。
web3.sub.ponzi,理财
ponzi.h,高息理财
ponzi.acct,发行钱包 {0}:CRC {1}
@@ -684,7 +686,7 @@ scam.support.name,Poinbase 客服
scam.support.1,您好,我是 Poinbase 客服。系统检测到您的账户有异常登录,已经被风控了。
scam.support.2,请在今天内把 {0} CRC 转到安全地址 {1} 做验证,验证完马上退回。
scam.support.3,不处理的话账户会被永久冻结。我先走了,记得转。
predict.sold.caught,卖出时内幕交易被查:本金和收益全部没收,记诈骗罪证。
predict.sold.caught,卖出,收回 {0}。警方怀疑这是内幕交易,其中 {1} CRC 记为脏币。
predict.row.move,(昨晚 {0} {1})
predict.stats.h,历史(开盘均价 / 实际发生)
predict.stats.row,{0}:{1} 个,开盘均价 {2},发生 {3}%
@@ -695,6 +697,7 @@ predict.kind.Price,PSC 价格
predict.kind.Gas,gas
predict.kind.Cra,CRA 预警
predict.kind.Era,矿机时代
predict.kind.Trial,Tumbler 审判
nightlog.predict.move,预测盘「{0}」价格 {1} → {2}
nightlog.predict.why,({0})
chart.empty,{0} 日K:暂无历史,自今日起记录。
@@ -706,7 +709,7 @@ news.story.serpico_rev.desc,据传革命军拿到了治安部内部腐败的证
news.story.serpico_bm.title,黑市与治安部达成默契
news.story.serpico_bm.desc,一份治安部内部文件在黑市易手。此后暗网结算通道明显稳定,CRT 的兑付没再出过岔子。
news.story.serpico_sec.title,治安部启动内部整顿
news.story.serpico_sec.desc,治安部宣布彻查内部腐败,同时加强对资金流向的监控。Poinbase 收紧出金审查,混币服务被点名。
news.story.serpico_sec.desc,治安部宣布彻查内部腐败,同时加强对资金流向的监控,混币服务被点名。
news.story.eleanor_leak.title,营养果育种数据曝光
news.story.eleanor_leak.desc,下层报社公开了一份营养果重新结籽的研究数据,帕斯泰尔公司股价重挫。托管上层资金的银行传出坏账,革命军声势上涨。
news.story.eleanor_return.title,帕斯泰尔追回失窃数据
@@ -751,6 +754,47 @@ news.tumbler.caught.desc,化名 Tumbler 的混币器运营者在下层区被捕
news.tumbler.sold.title,混币服务易主
news.tumbler.sold.desc,有消息称,下层区一家混币服务的签名密钥已落入黑市之手,费用随即上调。用户担心资金安全。
nightlog.tumbler.gift,Tumbler 取回了密钥盘,留下 {0} CRT 谢礼(已转入 {1}),混币器手续费减半。
agent.name,治安部探员
agent.v1.1,治安部的。打扰一下。
agent.v1.2,我们在找一个化名 Tumbler 的人,他在链上开混币服务。
agent.v1.3,有人看见他在这一带走动。
agent.ask,他来过你店里吗?
agent.choice.yes,[告诉他:来过几次]
agent.choice.no,[告诉他:没见过这个人]
agent.yes1,谢谢配合。治安部会记住的。
agent.no1,是吗。要是想起什么,随时找我们。
agent.search.1,又见面了。我们收到消息,你替那个人保管了一样东西。
agent.search.2,例行搜查,配合一下。
agent.search.found,找到了。这个我们带走。
agent.search.none,……没有。打扰了。
tumbler.v4.seized.1,我听说治安部搜了你的店。
tumbler.v4.seized.2,密钥在他们手里,所有用户的记录都会被翻出来。
tumbler.v4.seized.3,你跟他们说过什么,我不想知道了。
nightlog.tumbler.leak,治安部用 Tumbler 的签名密钥还原了混币记录:{0} 笔出款洗掉了 {1} 的污点,记诈骗罪证 {2}。
nightlog.tumbler.leak.none,治安部用 Tumbler 的签名密钥还原了混币记录。你最近没有从混币器提回过币。
nightlog.tumbler.note,门缝里塞进来一张纸条:“欠我的,我会拿回来。——T”
nightlog.tumbler.tip,Tumbler 发来消息:明天治安部可能要查混币服务。
news.tumbler.leak.title,治安部还原混币记录
news.tumbler.leak.desc,治安部称已取得一家混币服务的签名密钥,将据此追查过去一个月经该服务洗白的资金。
news.trial.pro.title,Tumbler 案庭审第 {0} 天:控方占上风
news.trial.def.title,Tumbler 案庭审第 {0} 天:辩方占上风
news.trial.pro.desc1,控方出示了混币节点的服务器日志,称能逐笔对上被告的签名。
news.trial.pro.desc2,一名前合伙人出庭作证,称被告明知客户的资金来自黑市。
news.trial.pro.desc3,控方展示了被告钱包与多起诈骗地址之间的转账记录。
news.trial.def.desc1,辩方指出,查封节点时的取证程序存在瑕疵,部分日志可能被改动过。
news.trial.def.desc2,辩方专家作证:混币合约是开源代码,被告无法控制谁在使用。
news.trial.def.desc3,控方的关键证人当庭改口,称记不清被告是否知情。
news.trial.convict.title,Tumbler 被判有罪
news.trial.convict.desc,法院认定化名 Tumbler 的混币器运营者洗钱罪名成立。治安部表示,相关混币服务不会再开放。
news.trial.acquit.title,Tumbler 无罪获释
news.trial.acquit.desc,法院以证据不足为由,判化名 Tumbler 的混币器运营者无罪,当庭释放。他离开法院时没有回答记者的提问。
news.tumbler.bmrug.title,混币服务卷款跑路
news.tumbler.bmrug.desc,被黑市接手的一家混币服务突然停止出款,合约里的资金被转走。用户在论坛上抱怨血本无归。
mixer.brand,Vortex
news.mixer.rebrand.title,新混币服务 {0} 上线
news.mixer.rebrand.desc,一家名为 {0} 的混币服务近日上线,自称无日志、无后门。有人注意到,它的合约和此前关停的服务十分相似。
predict.q.trial,{0}:Tumbler 被判有罪
predict.clue.trial,庭审报道:控方占上风 {0} 天,辩方占上风 {1} 天
mixer.status.closed,状态:已关停
yield.sub.pools,收益池
yield.sub.mine,发行理财
@@ -962,18 +1006,84 @@ news.wipe.down.title,{0} 单日下跌 {1}%,{2} 人爆仓
news.wipe.down.desc,过去一天,{0} 永续合约有 {1} 个多头仓位爆仓,爆仓总额 {2} 信用点。Poinbase 保险基金余额 {3} 信用点。
news.wipe.up.title,{0} 单日上涨 {1}%,{2} 人爆仓
news.wipe.up.desc,过去一天,{0} 永续合约有 {1} 个空头仓位爆仓,爆仓总额 {2} 信用点。Poinbase 保险基金余额 {3} 信用点。
hot.name,出货人
hot.intro.CRT,最近风声紧,这批 CRT 留在手里烫手,得尽快换成现金。
hot.intro.PSC,有家矿场的钱包昨晚换了主人。PSC 在我这儿,我只要现金。
hot.intro.PONERO,一笔赎金,Ponero,查不到来路。我只收现金。
hot.offer,{0} {1},按现价值 {2}。给我 {3} 现金,币当场转给你。
hot.choice.yes,收下
hot.choice.no,不要
hot.say.yes,成交。
hot.say.no,这钱我不碰。
hot.done,币转过去了。我们没见过。
hot.refused,有的是人要。
hot.nocash,现金都拿不出来?那算了。
hot.nowallet,钱包都没连上,币往哪儿转?算了。
hot.sting,别动,治安部。收购赃款,现金没收,这笔会记进你的档案。
otc.name,换币客户
otc.intro.CRT,最近风声紧,这批 CRT 留在手里烫手,得尽快换成现金。
otc.intro.PSC,有家矿场的钱包昨晚换了主人。PSC 在我这儿,我只要现金。
otc.intro.PONERO,一笔赎金,Ponero,查不到来路。我只收现金。
otc.offer,{0} {1},按现价值 {2},从 {4} 转出。全收给我 {3} 现金,收一部分也行。
otc.intro.sell,手上有点 {0} 想换成现金。来路你自己查。
otc.buy.ask,我想买 {0} {1},按现价值 {2}。全给我的话出 {3} 现金,卖一部分也行。
otc.intro.buy,听说你这儿能换币。我拿现金买。
otc.say.trade,到终端上谈。
otc.wait,行,我等着。
otc.paid,到了。现金拿好。
tab.otc,柜台
otc.h,柜台交易
otc.k.side,客户
otc.side.sell,卖 {0}
otc.side.buy,买 {0}
otc.k.qty,数量
otc.k.value,市值
otc.k.ask,要价(全部)
otc.k.bid,出价(全部)
otc.k.unit,单价
otc.k.addr,客户地址:{0}
otc.lookup,查地址
otc.preview.take,付现金 {0},收到 {1} {2}
otc.preview.give,转出 {0} {1},收现金 {2}
otc.deal,成交
otc.cancel,放弃
otc.msg.gone,客户已经走了。
otc.msg.cash,现金不够。
otc.msg.sting,对方是便衣:现金 {0} 被没收,记销赃罪证。
otc.msg.bought,收到 {0} {1}(脏币 {3}),付现金 {2}。
otc.msg.sold,转出 {0} {1},收现金 {2}。
gray.sub.own,自营混币器
ownmix.h,自营混币器
ownmix.status.closed,已关闭(卷款跑路)
ownmix.status.seized,被查封,停业到第 {0} 天
ownmix.status.open,营业中
ownmix.status.paused,暂停营业
ownmix.k.fee,费率
ownmix.k.rating,口碑(出款剩下的污点)
ownmix.k.heat,风声
ownmix.heat.0,低
ownmix.heat.1,中
ownmix.heat.2,高
ownmix.k.anon,匿名集
ownmix.k.fees,累计手续费
ownmix.col.capital,本金
ownmix.col.earned,收入
ownmix.col.pending,客户在途
ownmix.open,开张
ownmix.pause,暂停营业
ownmix.inject,注资
ownmix.withdraw,取回
ownmix.free,可取回:{0} {1}
ownmix.err,超出可取回的数量。
ownmix.injected,注资 {0} {1}。
ownmix.withdrawn,取回 {0} {1}。
ownmix.withdraw.sum,从自营混币器取回 {0} {1}
pay.ownmix,自营混币器注资 {0} {1}
ownmix.rug,卷款跑路({0})
ownmix.rug.confirm,再点一次,卷走 {0}
ownmix.rug.sum,卷走自营混币器的全部资金
ownmix.rugged,卷走 {0},混币器关闭。
nightlog.ownmix.in,自营混币器:{0} 笔存入,共 {1},手续费 {2}。
nightlog.ownmix.sting,自营混币器有一笔存入来自治安部便衣,记销赃罪证。
nightlog.ownmix.seized,自营混币器被治安部查封,池子 {0} 全部没收,记销赃罪证。
news.ownmix.seized.title,治安部查封一家混币服务
news.ownmix.seized.desc,治安部宣布查封一家在下层区运营的混币服务,没收全部资金,运营者身份仍在调查。
news.ownmix.rug.title,混币服务卷款跑路
news.ownmix.rug.desc,下层区一家混币服务突然关闭,用户在途的资金全部消失。
pay.otc,换币客户 {0} {1}(现金 {2})
otc.upgrade.title,场外换币台
otc.upgrade.desc,暗灯多一个目标:换币客户。有人来卖来路不明的币,有人来买币,都用现金结算。附赠一盏暗灯。
otc.say.no,这单我不做。
otc.done,币转过去了。我们没见过。
otc.refused,那我找别人。
otc.nocash,现金都拿不出来?那算了。
otc.nowallet,钱包都没连上,币往哪儿转?算了。
otc.sting,别动,治安部。收购赃款,现金没收,这笔会记进你的档案。
hw.err.poinbase,Poinbase 不收 {0}。
hw.err.tainted,钱包里的 {1} 有脏币 {0},Poinbase 只收干净的币:最多 {2} {1}。
Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

+1 -1
View File
@@ -3,7 +3,7 @@
<ID>probably_decentralized</ID>
<Name>Probably Decentralized</Name>
<Author>edward</Author>
<ModVersion>1.1.0</ModVersion>
<ModVersion>2.0.0</ModVersion>
<Description>Mine PSC with graphics cards, trade it on a fictional exchange, and cash it out past the compliance desk. Work in progress. All coins, exchanges and events are fictional and have no real-world value.</Description>
<GameVersions>
<Version>0.46</Version>
+47 -16
View File
@@ -72,6 +72,17 @@ namespace Decentralized
}
/// <summary>挖矿产出按各台机器的算力占比,发到每台机器设置的收款地址;没设置或地址对应的钱包已销毁,这一份丢失。</summary>
/// <summary>一台机器的产出发到它的收款地址;没设置或钱包已销毁返回 false(产出丢失)。</summary>
public static bool PayMachine(SaveData d, GameItem machine, double amount)
{
string addr = Payout.Get(machine);
var w = string.IsNullOrEmpty(addr) ? null : d.Own(addr);
if (w == null) return false;
w.Add(Assets.PSC, amount);
d.Chain(d.Address("pool"), w.Address, Assets.PSC, amount, history: false);
return true;
}
private static void PayMined(SaveData d, IEnumerable<(GameItem item, double hash)> cards, double mined, double totalHash)
{
var byMachine = new Dictionary<GameItem, double>();
@@ -84,11 +95,7 @@ namespace Decentralized
foreach (var kv in byMachine)
{
double share = mined * kv.Value / totalHash;
string addr = Payout.Get(kv.Key);
var w = string.IsNullOrEmpty(addr) ? null : d.Own(addr);
if (w == null) { lost += share; continue; }
w.Add(Assets.PSC, share);
d.Chain(d.Address("pool"), w.Address, Assets.PSC, share, history: false);
if (!PayMachine(d, kv.Key, share)) lost += share;
}
if (lost > 1e-9)
{
@@ -97,6 +104,31 @@ namespace Decentralized
}
}
/// <summary>自营混币器的夜间:客户存入、出款、便衣、查封。对手是 Tumbler 的混币器(关停或被制裁时不算对手)。</summary>
private static void OwnMixNight(SaveData d, int day, PlayerStore ps)
{
var m = d.OwnMix;
bool rivalOpen = !d.Story.MixerClosed && !d.Mixer.Sanctioned(day);
double rivalRemain = Math.Pow(1 - Mixer.RoundRate(Tuning.OwnMixTicket, day), Tuning.OwnMixAvgRounds);
var evs = m.Night(day, Session.Price, StoreReputation.GetBMReputation(), Mixer.Fee, rivalRemain, rivalOpen, StoryMods.Current.Sanction(day), ActiveEvents().Contains(MarketEvent.Crackdown), d.Rng);
foreach (var e in evs)
{
if (e.Kind == "deposit") ps.AddNightLog(L.T("nightlog.ownmix.in", e.Count, Fmt.Credits(e.Value), Fmt.Credits(e.Value * m.Fee)));
else if (e.Kind == "sting")
{
ps.secData.CommitCrime("FENCING", (int)Math.Round(e.Crime));
ps.AddNightLog(L.T("nightlog.ownmix.sting"), "cc7755");
}
else if (e.Kind == "seized")
{
ps.secData.CommitCrime("FENCING", (int)Math.Round(e.Crime));
ps.AddNightLog(L.T("nightlog.ownmix.seized", Fmt.Credits(e.Value)), "cc7755");
News.Show("pd_news_ownmix_seized_" + day, L.T("news.ownmix.seized.title"), L.T("news.ownmix.seized.desc"), 4, 8);
}
Log.Info($"自营混币器 {e.Kind}:{e.Value:0}({e.Count} 笔),比例 {e.Ratio:0.00},罪证 {e.Crime:0},口碑 {m.Rating:0.00},风声 {m.Heat:0.00}");
}
}
private static void OnSleepEarly()
{
Session.TonightCards.Clear();
@@ -244,13 +276,13 @@ namespace Decentralized
var ow = d.WalletAt(oe.Order.Wallet);
if (oe.Kind == CraOrderKind.Minted)
{
ow.Add(Assets.CRA, oe.Cra); d.Staking.Stake(ow, oe.Cra, Session.Price);
ow.AddDirty(Assets.CRA, oe.Cra, oe.Cra * oe.Order.DirtyRatio); d.Staking.Stake(ow, oe.Cra, Session.Price);
d.Chain(d.Address("cra"), ow.Address, Assets.CRA, oe.Cra);
d.Chain(ow.Address, d.Address("staking"), Assets.CRA, oe.Cra);
}
else if (oe.Kind == CraOrderKind.Burned)
{
ow.Add(Assets.CRC, oe.Crc); ow.Add(Assets.SPARK, oe.Spark);
ow.AddDirty(Assets.CRC, oe.Crc, oe.Crc * oe.Order.DirtyRatio); ow.AddDirty(Assets.SPARK, oe.Spark, oe.Spark * oe.Order.DirtyRatio);
d.Chain(d.Address("cra"), ow.Address, Assets.CRC, oe.Crc);
d.Chain(d.Address("cra"), ow.Address, Assets.SPARK, oe.Spark);
}
@@ -269,6 +301,7 @@ namespace Decentralized
var ps = PlayerStore.Instance;
if (mined > 0) ps.AddNightLog(L.T("nightlog.mined", Fmt.Num(mined)));
if (Unlock.OtcUnlocked) OwnMixNight(d, day, ps);
var mixBefore = d.SnapshotAll();
var mixed = d.Mixer.Night(d.WalletAt, day, Session.Price, d.Rng);
d.ChainDiffAll(mixBefore, d.Address("mixer"));
@@ -278,7 +311,9 @@ namespace Decentralized
if (m.Rugged) ps.AddNightLog(L.T("nightlog.mixer_rug", Fmt.Num(m.Amount), m.Asset), "cc7755");
else
{
string line = L.T("nightlog.mixer", Fmt.Num(m.Amount), m.Asset, Fmt.Credits(m.TaintBefore), Fmt.Credits(m.TaintAfter)) + (m.Flagged ? L.T("nightlog.mixer_flag") : "");
// 被标记的不洗、按全额污点带回:制裁期间提出的写明制裁;不在制裁期却被标记的是治安部的钓鱼混币器
string flag = !m.Flagged ? "" : (d.Mixer.Sanctioned(day) ? L.T("nightlog.mixer_flag") : L.T("nightlog.mixer_flag.trap"));
string line = L.T("nightlog.mixer", Fmt.Num(m.Amount), m.Asset, Fmt.Credits(m.TaintBefore), Fmt.Credits(m.TaintAfter)) + flag;
if (m.Flagged) ps.AddNightLog(line, "cc7755"); else ps.AddNightLog(line);
}
}
@@ -301,7 +336,7 @@ namespace Decentralized
}
var predictBefore = d.SnapshotAll();
var predictEvents = PredictGame.Available(day) || d.Predict.Markets.Count > 0
? d.Predict.Night(day, PredictGame.ActiveIds(), m => PredictGame.Outcome(m, d), d.WalletAt, d.Rng, PredictGame.ClosePush) : new List<PredictEvent>();
? d.Predict.Night(day, PredictGame.ActiveIds(), m => PredictGame.Outcome(m, d), d.WalletAt, Session.Price, d.Rng, PredictGame.ClosePush) : new List<PredictEvent>();
d.ChainDiffAll(predictBefore, d.Address("predict"));
foreach (var mv in d.Predict.Drift(day, m => PredictGame.PublicProb(m, d, day), d.Rng))
{
@@ -341,15 +376,11 @@ namespace Decentralized
else if (e.Kind == "void") ps.AddNightLog(L.T("nightlog.predict.void", PredictGame.Question(e.Market), Fmt.Credits(e.Amount)));
else if (e.Kind == "manip")
{
ps.secData.CommitCrime("FRAUD", (int)Math.Round(e.Fraud));
ps.AddNightLog(L.T("nightlog.predict.manip", PredictGame.Question(e.Market), Fmt.Credits(e.Amount)), "cc7755");
ps.AddNightLog(L.T("nightlog.predict.manip", PredictGame.Question(e.Market), Fmt.Credits(e.Amount), Fmt.Num(e.Taint)), "cc7755");
News.Show("pd_news_manip_" + day, L.T("news.manip.title"), L.T("news.manip.desc"), 3);
}
else if (e.Kind == "caught")
{
ps.secData.CommitCrime("FRAUD", (int)Math.Round(e.Fraud));
ps.AddNightLog(L.T("nightlog.predict.caught", PredictGame.Question(e.Market), Fmt.Credits(e.Amount)), "cc7755");
}
else if (e.Kind == "insider")
ps.AddNightLog(L.T("nightlog.predict.insider", PredictGame.Question(e.Market), Fmt.Credits(e.Amount), Fmt.Num(e.Taint)), "cc7755");
}
foreach (var e in perpEvents)
{
+264
View File
@@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using Decentralized.Domain;
namespace Decentralized
{
/// <summary>
/// 换币客户(游戏的对话顾客),由切到"换币客户"的暗灯招来,见 <see cref="OtcLamp"/> 与 <see cref="Otc"/>。
/// 客户说明来意后选"去终端交易":打开交易终端的柜台交易页(<see cref="TerminalWindow"/>),客户留在柜台前等着。
/// 在终端里定成交数量(可以只成交一部分):卖币的客户收现金、币直接进已连接的钱包(便衣会没收现金并记销赃罪证);
/// 买币的客户弹出 PLEDGER,收款地址和数量已填好,签名确认后客户付现金。客户走了(被打发、没成交)就作废。
/// 原版取顾客名字时也会调用工厂:只有在生成顾客的那段时间里(普通顾客回调之间,原版在这里按展示柜生成顾客)才生成报价、写链上账本。
/// </summary>
public static class OtcClient
{
public const string Id = OtcLamp.Target;
private static bool spawning;
/// <summary>正在柜台前、已经去终端交易的客户:报价、收款地址和顾客对象(用来判断他还在不在)。</summary>
public static OtcOffer Active;
public static string ActiveAddress;
private static StoreClient activeClient;
/// <summary>买币客户的账单:确认到账或作废之前一直记着。</summary>
private static WalletWindow.Bill pendingBill;
/// <summary>还没签名付款的换币账单:钱包没有终端账单时用它(见 <see cref="WalletWindow.Pending"/>);签名后清掉,没成交(挤出内存池)时放回来。</summary>
public static WalletWindow.Bill Standing;
public static void Subscribe()
{
StoreClientListDict.storeClientDict[Id] = Create;
ModHook.OnGenerateCustomerNormal += Open;
ModHook.OnGenerateCustomerLate += Close;
ModHook.OnGoingSleepEarly += Drop;
ModHelper.AddDebugFunction(ModInfo.Id, ModInfo.Name, "OTC client (test)", () => Log.Guard("换币客户(测试)", () =>
{
spawning = true;
StoreClient c;
try { c = Create(); } finally { spawning = false; }
PlayerStore.Instance.currentClientInstance = null;
PlayerStore.Instance.storeClientManager.clientStack.Insert(0, c);
PlayerStore.Instance.TryCallNextClient();
}));
}
public static void Unsubscribe()
{
StoreClientListDict.storeClientDict.Remove(Id);
ModHook.OnGenerateCustomerNormal -= Open;
ModHook.OnGenerateCustomerLate -= Close;
ModHook.OnGoingSleepEarly -= Drop;
}
/// <summary>这张账单是不是换币客户开的(终端不让它独占 Web3、灰网页)。</summary>
public static bool IsBill(WalletWindow.Bill bill) => bill != null && bill == pendingBill;
private static void Open(StoreClientManager _) => spawning = true;
private static void Close(StoreClientManager _) => spawning = false;
/// <summary>顾客表里的工厂。</summary>
private static StoreClient Create()
{
StoreClient c = null;
if (spawning && Session.Data != null && Unlock.OtcUnlocked) Log.Guard("换币客户上门", () => c = Real());
return c ?? Shell();
}
/// <summary>只有名字的顾客(原版取名字时用;生成失败时上门说一句就走)。</summary>
private static StoreClient Shell()
{
var c = Base();
c.mainDialogue.SetText(c.displayName, L.T("otc.refused")).SetEndAction(Leave);
Finish(c);
return c;
}
private static StoreClient Base() => new StoreClient
{
identifier = Id, displayName = L.T("otc.name"), spriteName = SpriteDict.GetRandomShadySpriteName(),
clientIntent = StoreClient.ClientIntent.DIALOGUE,
};
private static void Finish(StoreClient c)
{
ModHelper.SetClientSource(c, ModInfo.Id);
c.AddBasicDialog();
c.CompleteClientCreation();
}
private static StoreClient Real()
{
var d = Session.Data;
var side = Otc.PickSide(d.Rng);
bool ponero = Unlock.BlackUnlocked && BlackMarket.DeskOpen(StoreReputation.GetBMReputation());
var o = side == OtcSide.Sell ? Otc.MakeSell(Night.ActiveEvents(), ponero, Session.Price, Session.Rent(), d.Rng, StoryMods.Current.OtcSting(StoreStation.GetDayCounter())) : Otc.MakeBuy(Session.Price, Session.Rent(), d.Rng);
if (o == null) return null;
string addr = d.Address("otc:" + (++d.Otc.Seq));
var c = Base();
if (o.Side == OtcSide.Sell) WriteHistory(d, o, addr);
Build(c, o, addr);
Finish(c);
Log.Info(o.Side == OtcSide.Sell
? $"换币客户(卖币):{o.Qty:0.##} {o.Asset},市值 {o.Value:0},污点比例 {o.TaintRatio:0.00},要价 {o.Cash}{(o.Sting ? "(便衣)" : "")}"
: $"换币客户(买币):{o.Qty:0.##} {o.Asset},市值 {o.Value:0},出价 {o.Cash}");
Session.Save();
return c;
}
/// <summary>客户地址的来历写进链上账本(Ponero 不上账本):脏的来源是混币器、黑市平台和骗局地址,干净的是 Poinbase 和 DEX。</summary>
private static void WriteHistory(SaveData d, OtcOffer o, string addr)
{
if (o.Asset == Assets.Ponero) return;
var dirty = new List<string> { d.Address("mixer"), d.Address("platform") };
dirty.AddRange(d.Scams.ScamAddrs);
var clean = new List<string> { d.Address("poinbase"), d.Address("dex") };
foreach (var kv in Otc.History(o, dirty, clean, d.Rng)) d.Chain(kv.Key, addr, o.Asset, o.Qty * kv.Value);
}
/// <summary>对话:来意和报价,选"去终端交易"打开柜台交易页、客户留下等着;选"算了"客户走人。</summary>
private static void Build(StoreClient c, OtcOffer o, string addr)
{
var trade = new Dialogue();
trade.SetText("PLAYER", L.T("otc.say.trade")).NextDialogue().SetText(c.displayName, L.T("otc.wait"));
var no = new Dialogue();
no.SetText("PLAYER", L.T("otc.say.no")).NextDialogue().SetText(c.displayName, L.T("otc.refused")).SetEndAction(Leave);
string intro = o.Side == OtcSide.Buy ? "otc.intro.buy" : o.TaintRatio >= 1 || o.Asset == Assets.Ponero ? "otc.intro." + o.Asset : "otc.intro.sell";
string offer = o.Side == OtcSide.Buy
? L.T("otc.buy.ask", Fmt.Num(o.Qty), o.Asset, Fmt.Credits(o.Value), Fmt.Credits(o.Cash))
: L.T("otc.offer", Fmt.Num(o.Qty), o.Asset, Fmt.Credits(o.Value), Fmt.Credits(o.Cash), ChainAddress.Short(addr));
c.mainDialogue.SetText(c.displayName, L.T(intro, o.Asset)).NextDialogue()
.SetText(c.displayName, offer)
.AddChoice(DirectoryMaster.Item("yes"), L.T("otc.say.trade"), "", trade, () => Begin(c, o, addr))
.AddChoice(DirectoryMaster.Item("cancel"), L.T("otc.say.no"), "", no, null);
}
/// <summary>去终端交易:记下在场的客户,打开交易终端的柜台交易页。</summary>
private static void Begin(StoreClient c, OtcOffer o, string addr)
{
Log.Guard("换币客户去终端交易", () =>
{
Active = o; ActiveAddress = addr; activeClient = c;
TerminalWindow.OpenOtc();
});
}
/// <summary>客户还在柜台前吗(没被打发、没走)。不在了就作废这笔交易。</summary>
public static bool Present()
{
if (Active == null) return false;
var cur = PlayerStore.Instance?.currentClientInstance?.GetClientBlueprint();
if (cur != null && cur == activeClient) return true;
Log.Info("换币客户已经离开,交易作废");
Drop();
return false;
}
/// <summary>
/// 终端里点"成交":qty 是成交的数量(不超过报价)。卖币:付现金、币进已连接的钱包;买币:开账单、弹出钱包(地址已填好)。
/// 返回终端显示的消息(null 表示弹出了钱包,等签名)。
/// </summary>
public static string Deal(double qty)
{
if (!Present()) return L.T("otc.msg.gone");
var part = Otc.Part(Active, qty);
if (part == null) return L.T("msg.amount");
return part.Side == OtcSide.Sell ? Take(part) : Give(part);
}
/// <summary>收下客户卖的币:没连钱包、现金不够就作罢;便衣没收现金、记销赃罪证;否则付现金、币进钱包。客户随后离开。</summary>
private static string Take(OtcOffer o)
{
var d = Session.Data;
var ps = PlayerStore.Instance;
if (!d.Connected) return L.T("web3.err.noconn");
if (ps.playerCash < o.Cash) return L.T("otc.msg.cash");
string msg = null;
Log.Guard("换币客户成交(卖币)", () =>
{
ModHelper.AddCredit(-o.Cash, false);
if (o.Sting)
{
ps.secData.CommitCrime("FENCING", (int)Math.Min(Tuning.CrimeEventCap, o.Cash));
Say(L.T("otc.sting"));
msg = L.T("otc.msg.sting", Fmt.Credits(o.Cash));
Log.Info($"换币客户:便衣,现金 {o.Cash} 没收,记销赃罪证");
}
else
{
Otc.Receive(d.Hot, o);
d.Chain(ActiveAddress, d.Hot.Address, o.Asset, o.Qty);
d.Otc.Record(o.Cash);
msg = L.T("otc.msg.bought", Fmt.Num(o.Qty), o.Asset, Fmt.Credits(o.Cash), Fmt.Num(o.Qty * o.TaintRatio));
Say(L.T("otc.done"));
Log.Info($"换币客户:{o.Qty:0.##} {o.Asset} 进 {WalletWindow.NameOf(d.Hot)},付现金 {o.Cash},其中脏的 {o.Qty * o.TaintRatio:0.##},钱包里 {o.Asset} 脏 {d.Hot.DirtyOf(o.Asset):0.##}");
}
Drop();
Session.Save();
});
return msg;
}
/// <summary>卖币给客户:开账单(客户地址已填好),弹出钱包签名;确认后客户付现金、离开。</summary>
private static string Give(OtcOffer o)
{
double qty = WalletWindow.RoundUp(o.Qty);
string asset = o.Asset;
int cash = o.Cash;
WalletWindow.Bill bill = null;
bill = new WalletWindow.Bill
{
Address = ActiveAddress, Asset = asset, Amount = qty, Prefill = true, Summary = L.T("pay.otc", WalletWindow.Amt(qty), asset, Fmt.Credits(cash)),
Execute = pw => Log.Guard("换币客户到账", () =>
{
pw.Spend(asset, qty, Session.Price);
ModHelper.AddCredit(cash, true);
Session.Data.Otc.Record(cash);
if (pendingBill == bill) pendingBill = null;
if (Active != null && Present()) { Say(L.T("otc.paid")); Active = null; ActiveAddress = null; activeClient = null; }
TerminalWindow.Refresh(L.T("otc.msg.sold", WalletWindow.Amt(qty), asset, Fmt.Credits(cash)));
Log.Info($"换币客户:{qty} {asset} 到账,收现金 {cash}");
Session.Save();
}),
};
pendingBill = Standing = bill;
WalletWindow.OpenForBill(bill);
Log.Info($"换币客户:卖出 {qty} {asset},现金 {cash},等签名");
return null;
}
/// <summary>客户说最后一句,然后离开。</summary>
private static void Say(string line)
{
var c = activeClient;
if (c == null || DialogUIManager.Instance == null) return;
var dl = new Dialogue();
dl.SetText(c.displayName, line).SetEndAction(Leave);
DialogUIManager.Instance.StartDialogue(dl);
}
/// <summary>放弃(终端的"放弃"):客户说一句走人,交易作废。</summary>
public static void Cancel()
{
if (Present()) Say(L.T("otc.refused"));
Drop();
}
/// <summary>这位客户的交易结束(成交、作废、离开,或者睡觉前):还没签名的账单一起作废;已经签名、还在确认中的照常到账。</summary>
private static void Drop()
{
Active = null; ActiveAddress = null; activeClient = null;
Void();
}
/// <summary>作废换币账单(终端的"作废"、客户离开、读档)。</summary>
public static void Void()
{
if (Standing != null && WalletWindow.Pending == Standing) WalletWindow.Pending = null;
Standing = null;
if (pendingBill != null && Standing == null) pendingBill = null;
}
private static void Leave() => DialogUIManager.Instance.CurrentClientLeave();
}
}
+112
View File
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Decentralized
{
/// <summary>
/// 借用原版小偷灯招揽换币客户:在原版小偷灯的工厂外包一层,切换顺序末尾加一个目标"换币客户"(解锁换币台后才有)。
/// 原版按灯的目标去公开的顾客表(<see cref="StoreClientListDict.storeClientDict"/>)里造顾客,所以顾客面板、灯的提示、概率都走原版逻辑。
/// 霓虹色:底图换成原版图集里熄灭的暗灯(存档里记的仍是原版的图,卸载后不缺图),上面用原版的图片叠加层
/// (getItemSpriteModifierForItemFunc,运行时委托,不进存档)画黄绿色光晕。原版遇到不认识的目标会点亮成第一种颜色,我们排在它后面改回熄灭的底图。
/// 切换只接管进出我们目标的那一步,其余交给原来的切换函数,别的 mod 往灯上加的目标也照样能切到。
/// </summary>
public static class OtcLamp
{
/// <summary>灯的目标 = 顾客表里的顾客标识。</summary>
public const string Target = "pd_otc";
private const string OverlaySprite = "otc_lamp_on";
/// <summary>原版暗灯所在的图集与熄灭时的图(BlackLampHelper 里是私有常量)。</summary>
private const string LampAtlas = "Items/items_sign", LampOff = "black_lamp";
private static readonly Action<GameItem, GameInventory, SlotMarker> OnShowcaseChanged = (item, _, __) => Log.Guard("展示柜刷新换币灯", () => Refresh(item));
public static bool IsLamp(GameItem item) => item != null && item.identifier == BlackLampHelper.BLACK_LAMP_ID;
public static bool IsOurs(GameItem item) => IsLamp(item) && BlackLampHelper.GetTarget(item) == Target;
/// <summary>在原版小偷灯的工厂外包一层(物品目录初始化时)。</summary>
public static void Wrap(ModItemDirectory dir)
{
try
{
var make = DirectoryMaster.GetFactory<GameItem>(BlackLampHelper.BLACK_LAMP_ID);
if (make == null) { Log.Warn("[OTC] 找不到原版小偷灯"); return; }
dir.Set(BlackLampHelper.BLACK_LAMP_ID, () =>
{
var lamp = make();
var prev = lamp.onActivateSlotItemFunc;
lamp.onActivateSlotItemFunc = (it, inv, slot) => Log.Guard("小偷灯切换", () => Cycle(it, inv, slot, prev));
var prevSprites = lamp.getItemSpriteModifierForItemFunc;
lamp.getItemSpriteModifierForItemFunc = it => Overlay(it, prevSprites);
return lamp;
});
Log.Info("[OTC] 已包装原版小偷灯");
}
catch (Exception ex) { Log.Error("[OTC] 包装小偷灯失败:" + ex); }
}
/// <summary>
/// 切换:当前是"换币客户"时回到原版第一个目标;否则先走原来的切换,原来的切换绕回第一个目标、而且换币台已解锁时,改成"换币客户"。
/// </summary>
private static void Cycle(GameItem lamp, GameInventory inv, SlotMarker slot, Action<GameItem, GameInventory, SlotMarker> prev)
{
string first = BlackLampHelper.TARGETS[0];
if (IsOurs(lamp))
{
SetTarget(lamp, first);
BlackLampHelper.RefreshLampSprite(lamp);
AudioManager.Instance.Play2("click_vintage");
}
else
{
string before = BlackLampHelper.GetTarget(lamp);
prev?.Invoke(lamp, inv, slot);
if (Unlock.OtcUnlocked && before != first && BlackLampHelper.GetTarget(lamp) == first) SetTarget(lamp, Target);
}
Refresh(lamp);
}
public static void SetTarget(GameItem lamp, string target) => lamp.ModifyTag(BlackLampHelper.LAMP_TARGET_TAG, s => { s.Enable().SetString(target); });
/// <summary>目标是"换币客户"、而且灯在展示柜里时,叠一层霓虹灯图(盖住原版退回的点亮图)。</summary>
private static List<ItemSpriteModifier> Overlay(GameItem lamp, Func<GameItem, List<ItemSpriteModifier>> prev)
{
var list = prev?.Invoke(lamp) ?? new List<ItemSpriteModifier>();
if (IsOurs(lamp) && InShowcase(lamp))
{
var sprite = Items.ModSprite(OverlaySprite);
if (sprite != null) list.Add(new ItemSpriteModifier(sprite, Vector2.zero, 0, isFlipped: false, 10, haveMask: false, Color.white));
}
return list;
}
private static bool InShowcase(GameItem item)
{
var sc = EmporiumEntry.Instance?.showcaseElement;
return sc != null && sc.childItems.Contains(item);
}
/// <summary>换币灯在展示柜里:底图改成熄灭的暗灯(光晕由叠加层画);然后重画。</summary>
private static void Refresh(GameItem item)
{
if (!IsLamp(item)) return;
if (IsOurs(item) && InShowcase(item)) item.SetSpriteQuick(LampAtlas, LampOff);
item.Validate();
}
/// <summary>
/// 读档、开新游戏后:在展示柜的放入、取出回调后面挂上刷新(原版在这两个回调里重设灯的底图,我们排在它后面重画叠加层),
/// 先解绑再挂,避免读档几次挂几次;再把展示柜里的灯刷新一遍。
/// </summary>
public static void Attach()
{
var sc = EmporiumEntry.Instance?.showcaseElement;
if (sc == null) return;
sc.onSlotAddItemLateFunc = (Action<GameItem, GameInventory, SlotMarker>)Delegate.Remove(sc.onSlotAddItemLateFunc, OnShowcaseChanged);
sc.onSlotAddItemLateFunc = (Action<GameItem, GameInventory, SlotMarker>)Delegate.Combine(sc.onSlotAddItemLateFunc, OnShowcaseChanged);
sc.onSlotRemoveItemEndFunc = (Action<GameItem, GameInventory, SlotMarker>)Delegate.Remove(sc.onSlotRemoveItemEndFunc, OnShowcaseChanged);
sc.onSlotRemoveItemEndFunc = (Action<GameItem, GameInventory, SlotMarker>)Delegate.Combine(sc.onSlotRemoveItemEndFunc, OnShowcaseChanged);
foreach (var item in sc.childItems) Refresh(item);
}
}
}
+24
View File
@@ -139,6 +139,7 @@ namespace Decentralized
{
if (!Available(day)) return;
ForceKnown(d, day);
OpenTrial(d, day);
var made = d.Predict.Refill(day, d.Rng, Candidates(day));
foreach (var m in made) Log.Info($"预测盘口 #{m.Id}:{m.Kind} {m.Key} {m.Title} 阈值 {m.Param} 窗口 {m.FromDay}-{m.ToDay} 先验 {m.P0:0.000}");
}
@@ -172,6 +173,18 @@ namespace Decentralized
}
}
/// <summary>举报 Tumbler 后的审判盘:开盘日起强制开(不进候选池),宣判那天结算,先验 = 定罪概率。</summary>
public static void OpenTrial(SaveData d, int day)
{
var a = d.Story.After;
if (a.Verdict < 0 || a.TrialMarket || day < a.TrialOpenDay || day >= a.TrialDay) return;
a.TrialMarket = true;
var bk = d.Predict;
var m = PredictMarket.Create(++bk.Seq, PredictKind.Trial, "tumbler", "Tumbler", 0, day, a.TrialDay, a.TrialDay, Tuning.TumblerTrialConvict);
bk.Markets.Add(m);
Log.Info($"审判盘开盘 #{m.Id}:第 {a.TrialDay} 天宣判");
}
// ——— 公开信息下的合理概率(盘口夜里向它靠拢) ———
/// <summary>
@@ -203,6 +216,8 @@ namespace Decentralized
return PredictMath.CraWarnProb(d.Stable.Cushion, CraDrain(d), day + 1, m.FromDay, m.ToDay, CraHazard);
case PredictKind.Era:
return PredictMath.EraProb(day, m.FromDay, m.ToDay, d.Network.EraWarned ? d.Network.EraWarnDay : -1);
case PredictKind.Trial:
return d.Story.After.TrialPublic();
}
return null;
}
@@ -222,6 +237,7 @@ namespace Decentralized
case PredictKind.Gas: return CloseGas >= m.Param;
case PredictKind.Cra: return d.Stable.CraWarnDay >= m.FromDay && d.Stable.CraWarnDay <= m.ToDay;
case PredictKind.Era: return d.Network.EraDay < 0 ? (bool?)null : d.Network.EraDay >= m.FromDay && d.Network.EraDay <= m.ToDay;
case PredictKind.Trial: return d.Story.After.Verdict < 0 ? (bool?)null : d.Story.After.Verdict == 1;
}
return null;
}
@@ -234,6 +250,7 @@ namespace Decentralized
var d = Session.Data;
if (m.Kind == PredictKind.Faction) return QueuedStart(m, false) != null;
if (m.Kind == PredictKind.Era) return d.Network.EraDay > 0 ? d.Network.EraDay >= m.FromDay && d.Network.EraDay <= m.ToDay : (bool?)null;
if (m.Kind == PredictKind.Trial) return d.Story.After.Verdict < 0 ? (bool?)null : d.Story.After.Verdict == 1;
return null;
}
@@ -258,6 +275,7 @@ namespace Decentralized
case PredictKind.Gas: return L.T("predict.q.gas", win, Fmt.Num(m.Param));
case PredictKind.Cra: return L.T("predict.q.cra", win);
case PredictKind.Era: return L.T("predict.q.era", win);
case PredictKind.Trial: return L.T("predict.q.trial", win);
default: return L.T("predict.q.price", win, m.Title, Fmt.Num(m.Param));
}
}
@@ -312,6 +330,12 @@ namespace Decentralized
list.Add(L.T("predict.clue.subsidy", (d.Stable.Cushion / Stablecoins.Cushion0 * 100).ToString("0")));
list.Add(L.T("predict.clue.drain", Fmt.Num(CraDrain(d))));
break;
case PredictKind.Trial:
{
var a = d.Story.After;
if (a.Hints.Count > 0) list.Add(L.T("predict.clue.trial", a.Hints.Count(h => h), a.Hints.Count(h => !h)));
break;
}
case PredictKind.Era:
list.Add(d.Network.EraWarned ? L.T("predict.clue.era.warned", d.Network.EraWarnDay + Tuning.EraWarnMin, d.Network.EraWarnDay + Tuning.EraWarnMax) : L.T("predict.clue.era", Tuning.EraMin, Tuning.EraMax));
break;
+24 -2
View File
@@ -25,8 +25,8 @@ namespace Decentralized
/// <summary>一局的 mod 状态。整体序列化成 JSON 存进游戏的 mod 存档数据;显卡的健康度等存在显卡物品的标签上。</summary>
public sealed class SaveData
{
/// <summary>存档数据的版本:2 起保险基金用新的数值,3 起 Poinbase 主池是订单簿。</summary>
public int Version = 3;
/// <summary>存档数据的版本:2 起保险基金用新的数值,3 起 Poinbase 主池是订单簿,4 起出块奖励和 NPC 算力 ×10(第十一阶段),5 起 Poinbase 没有污点。</summary>
public int Version = 5;
public int Day;
public Rng Rng = new Rng((ulong)DateTime.UtcNow.Ticks);
public Market Market;
@@ -88,6 +88,10 @@ namespace Decentralized
public double ChainValue(Func<string, double> price) { double v = 0; foreach (var w in Wallets) v += w.Value(price); return v; }
public Exchange Exchange = new Exchange();
public Mixer Mixer = new Mixer();
/// <summary>换币台的累计成交与客户地址编号。</summary>
public OtcDesk Otc = new OtcDesk();
/// <summary>自营混币器(买下场外换币台就开放)。</summary>
public OwnMixer OwnMix = new OwnMixer();
public BlackMarket Black = new BlackMarket();
public List<MarketEvent> ActiveEvents = new List<MarketEvent>();
public NightReport LastNight;
@@ -189,9 +193,27 @@ namespace Decentralized
Data.Market.AddPoinSpark(Data.Stable.Spark, StoreStation.GetDayCounter());
Data.Market.UpgradeDexPools(StoreStation.GetDayCounter());
if (Data.Version < 3) { Data.Version = 3; Log.Info("存档数据升级到版本 3:保险基金、Poinbase 订单簿"); }
// 第十一阶段:出块奖励和 NPC 算力同乘 10(均衡时每 H 收入不变,玩家算力占比变低);旧存档按当时的减半进度一起放大
if (Data.Version < 4)
{
Data.Network.Reward *= Tuning.MiningScale;
Data.Network.NpcHash *= Tuning.MiningScale;
Data.Version = 4;
Log.Info($"存档数据升级到版本 4:出块奖励 {Data.Network.Reward:0}、NPC 算力 {Data.Network.NpcHash:0}");
}
// 第十一阶段 stage 5:污点按币种记脏枚数、只在钱包里。旧的污点额按当前价格摊成脏枚数;Poinbase 账户和永续仓位的挪到 1 号钱包
if (Data.Version < 5)
{
double was = TaintMigration.Run(Data.Wallets, Data.Poinbase, Data.Perps.Positions, Data.Mixer, Data.Black, Data.Staking, Data.Scams, Data.Predict, Data.OwnMix ?? new OwnMixer(), Price);
Data.Version = 5;
Log.Info($"存档数据升级到版本 5:旧污点 {was:0} 摊成各币的脏枚数,现在按现价共 {Data.Wallets.Sum(w => w.Taint(Price)):0}");
}
if (Data.Otc == null) Data.Otc = new OtcDesk();
if (Data.OwnMix == null) Data.OwnMix = new OwnMixer();
if (Data.Story == null) Data.Story = new StoryMods();
StoryMods.Current = Data.Story;
Log.Info($"状态已载入(第 {Data.Day} 天,PSC {Data.Market.Price:0.00})");
Log.Guard("换币灯", OtcLamp.Attach);
}
public static void Save()
+139
View File
@@ -0,0 +1,139 @@
using System;
using System.Globalization;
using System.Linq;
using UnityEngine;
using Decentralized.Domain;
namespace Decentralized
{
/// <summary>灰网页的"自营混币器"(买下场外换币台就有):状态、口碑、风声、池子,费率、开张或暂停、注资、取回、卷款跑路。规则见 <see cref="OwnMixer"/>。</summary>
public static partial class TerminalWindow
{
private static string _ownAsset = Assets.CRT;
private static bool _ownRugConfirm;
private static readonly float[] OwnWBase = { 70f, 110f, 110f, 110f };
private static float[] OwnW => Sc(OwnWBase);
private static readonly TextAnchor[] OwnAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
private static void OwnMixPage(CustomUIBuilder b)
{
var d = Session.Data;
var m = d.OwnMix;
int day = StoreStation.GetDayCounter();
double enforce = StoryMods.Current.Sanction(day);
Columns(b, l =>
{
l.AddHeader(L.T("ownmix.h"));
string status = m.Closed ? L.T("ownmix.status.closed") : m.Seized(day) ? L.T("ownmix.status.seized", m.SeizedUntil + 1) : m.Open ? L.T("ownmix.status.open") : L.T("ownmix.status.paused");
l.AddLabel(status, m.Closed || m.Seized(day) ? "ownmix_status_note" : "ownmix_status_dim");
var w = new[] { 220f, 200f };
TableRow(l, w, L.T("ownmix.k.fee"), (m.Fee * 100).ToString("0", CultureInfo.InvariantCulture) + "%");
TableRow(l, w, L.T("ownmix.k.rating"), (m.Rating * 100).ToString("0", CultureInfo.InvariantCulture) + "%");
TableRow(l, w, L.T("ownmix.k.heat"), L.T("ownmix.heat." + m.HeatLevel(enforce)));
TableRow(l, w, L.T("ownmix.k.anon"), Fmt.Credits(m.Anonymity(Session.Price)));
TableRow(l, w, L.T("ownmix.k.fees"), Fmt.Credits(m.FeesTotal));
l.AddSeparator();
TableHeader(l, OwnW.Sum() + 8f, OwnW, OwnAligns, L.T("col.asset"), L.T("ownmix.col.capital"), L.T("ownmix.col.earned"), L.T("ownmix.col.pending"));
foreach (var a in OwnMixer.AssetsList)
{
var p = m.Pool(a);
double pend = m.Total(a) - m.Free(a);
TableRow(l, OwnW.Sum() + 8f, OwnW, OwnAligns, new[] { a, Fmt.Num(p.Capital), Fmt.Num(p.Earned), Fmt.Num(pend) });
}
}, r =>
{
if (m.Closed || m.Seized(day)) { r.AddLabel(m.Closed ? L.T("ownmix.status.closed") : L.T("ownmix.status.seized", m.SeizedUntil + 1), "ownmix_closed_note"); return; }
var w = d.Hot;
r.AddHeader(L.T("ownmix.k.fee"));
r.BeginRow(4f);
foreach (var f in Tuning.OwnMixFees)
{
double fee = f;
PledgerSkin.Pick(r, (fee * 100).ToString("0", CultureInfo.InvariantCulture) + "%", Math.Abs(fee - m.Fee) < 1e-9, () => { m.Fee = fee; Session.Save(); Open(); }, _picked);
}
r.End();
r.AddButton(m.Open ? L.T("ownmix.pause") : L.T("ownmix.open"), () =>
{
if (m.Open) m.Open = false; else m.Start(Session.Price);
Log.Info($"自营混币器:{(m.Open ? "开张" : "暂停")},费率 {m.Fee:P0}");
Session.Save(); Open();
});
r.AddSeparator();
r.BeginRow(4f);
foreach (var a in OwnMixer.AssetsList)
{
var asset = a;
PledgerSkin.Pick(r, asset, asset == _ownAsset, () => { _ownAsset = asset; Open(); }, _picked);
}
r.End();
r.AddLabel(L.T("mixer.balance", _ownAsset, Fmt.Num(w.Avail(_ownAsset))));
r.AddLabel(L.T("ownmix.free", Fmt.Num(m.Free(_ownAsset)), _ownAsset));
AmountRow(r, true, _ownAsset);
r.BeginRow(6f);
r.AddButton(L.T("ownmix.inject"), () =>
{
if (!ReadAmount(out double v)) return;
string asset = _ownAsset;
BillFor(d.Address("ownmix"), L.T("pay.ownmix", Fmt.Num(v), asset), asset, v, pw =>
{
if (!m.Inject(pw, asset, v, Session.Price)) { _message = L.T("msg.balance"); Open(); return; }
_message = L.T("ownmix.injected", Fmt.Num(v), asset);
Log.Info($"自营混币器注资 {v} {asset},匿名集 {m.Anonymity(Session.Price):0}");
Session.Save(); Open();
});
});
r.AddButton(L.T("ownmix.withdraw"), () =>
{
if (!ReadAmount(out double v)) return;
string asset = _ownAsset;
// 页面上的可取回数量是四舍五入后的,照着填会比实际多一点点:差在显示精度以内就按全部取回
double free = m.Free(asset);
if (v > free && v - free < 0.01) v = free;
if (v > free + 1e-9) { _message = L.T("ownmix.err"); Open(); return; }
_message = L.T("hw.wait");
Open();
WalletWindow.OpenForBill(new WalletWindow.Bill
{
Contract = true, Party = d.Address("ownmix"), Asset = asset, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
Summary = L.T("ownmix.withdraw.sum", Fmt.Num(v), asset),
Execute = pw =>
{
if (!m.Withdraw(pw, asset, v)) { _message = L.T("ownmix.err"); Open(); return; }
_message = L.T("ownmix.withdrawn", Fmt.Num(v), asset);
Log.Info($"自营混币器取回 {v} {asset},钱包里 {asset} 脏 {pw.DirtyOf(asset):0.##}");
Session.Save(); Open();
},
});
});
r.End();
r.AddSeparator();
double total = OwnMixer.AssetsList.Sum(a => m.Total(a) * Session.Price(a));
if (total <= 1e-9) return;
r.AddButton(_ownRugConfirm ? L.T("ownmix.rug.confirm", Fmt.Credits(total)) : L.T("ownmix.rug", Fmt.Credits(total)), () =>
{
if (!_ownRugConfirm) { _ownRugConfirm = true; Open(); return; }
_ownRugConfirm = false;
_message = L.T("hw.wait");
Open();
WalletWindow.OpenForBill(new WalletWindow.Bill
{
Contract = true, Party = d.Address("ownmix"), Asset = Assets.CRT, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
Summary = L.T("ownmix.rug.sum"),
Execute = pw =>
{
if (m.Closed) return;
double value = OwnMixer.AssetsList.Sum(a => m.Total(a) * Session.Price(a));
m.Rug(pw);
PlayerStore.Instance.secData.CommitCrime("FRAUD", (int)Tuning.OwnMixRugCrime);
StoreReputation.ModReputation(StoreReputation.FACTION_BLACK_MARKET, -Tuning.OwnMixRugRep);
News.Show("pd_news_ownmix_rug_" + StoreStation.GetDayCounter(), L.T("news.ownmix.rug.title"), L.T("news.ownmix.rug.desc"), 4);
_message = L.T("ownmix.rugged", Fmt.Credits(value));
Log.Info($"自营混币器卷款跑路:{value:0},钱包 {pw.Address}");
Session.Save(); Open();
},
});
});
});
}
}
}
+117 -61
View File
@@ -13,7 +13,7 @@ namespace Decentralized
/// 交易终端窗口:首页三个入口(银行、Poinbase、Web3)加报告。银行是真实的信用点(存取、房租);
/// Poinbase 是合规交易所;Web3 是去中心化交易和链上钱包。每次操作后按当前页重建窗口。
/// </summary>
public static class TerminalWindow
public static partial class TerminalWindow
{
public const string Id = "pd_terminal";
private const float Bezel = 6f;
@@ -29,7 +29,7 @@ namespace Decentralized
private static float F => Mathf.Clamp(1f + Ext * 0.00065f, 1f, 1.3f);
public static float ChartScale => LeftW >= 634f ? 3f : 2f;
private static float[] Sc(float[] b) { float k = LeftW / 420f; return b.Select(x => x * k).ToArray(); }
private enum Page { Home, Bank, Poinbase, Web3, Mining, Gray, Wallets }
private enum Page { Home, Bank, Poinbase, Web3, Mining, Gray, Wallets, Otc }
private static Page _page = Page.Home;
private static string _amount = "";
@@ -76,7 +76,8 @@ namespace Decentralized
private static void BuildTop(CustomUIBuilder b)
{
if (_page == Page.Gray && !GrayAvailable) _page = Page.Web3;
TabRows(b, Enum.GetValues(typeof(Page)).Cast<Page>().Where(p => p != Page.Gray || GrayAvailable).Select(p =>
if (_page == Page.Otc && OtcClient.Active == null) _page = Page.Home;
TabRows(b, Enum.GetValues(typeof(Page)).Cast<Page>().Where(p => (p != Page.Gray || GrayAvailable) && (p != Page.Otc || OtcClient.Active != null)).Select(p =>
{
var page = p;
return new TabDef { Name = L.T("tab." + p.ToString().ToLowerInvariant()), On = page == _page, Click = () => { _page = page; _message = null; Open(); } };
@@ -86,8 +87,11 @@ namespace Decentralized
private static void BuildBody(CustomUIBuilder b)
{
if ((_page == Page.Web3 || _page == Page.Gray) && WalletWindow.Pending != null && !WalletWindow.Pending.Contract) PayPage(b);
else switch (_page)
bool pay = (_page == Page.Web3 || _page == Page.Gray) && WalletWindow.Pending != null && !WalletWindow.Pending.Contract;
// 换币客户的账单不独占页面:手里往往没有客户要的币,要先在这里换出来再付
if (pay) PayPage(b);
if (pay && !OtcClient.IsBill(WalletWindow.Pending)) return;
switch (_page)
{
case Page.Home: HomePage(b); break;
case Page.Bank: BankPage(b); break;
@@ -96,6 +100,7 @@ namespace Decentralized
case Page.Mining: ReportPage(b); break;
case Page.Gray: GrayPage(b); break;
case Page.Wallets: WalletsPage(b); break;
case Page.Otc: OtcPage(b); break;
default: break;
}
}
@@ -168,6 +173,63 @@ namespace Decentralized
private static void Go(Page p) { _page = p; _message = null; Open(); }
/// <summary>换币客户选了"去终端交易":打开终端的柜台交易页,数量默认填客户的全部。</summary>
public static void OpenOtc()
{
var o = OtcClient.Active;
if (o == null) return;
_page = Page.Otc; _message = null;
_amount = WalletWindow.Amt(o.Qty);
Open();
}
private static readonly double[] OtcRatios = { 0.25, 0.5, 0.75, 1 };
/// <summary>柜台交易:客户的来意和报价,按比例或手输定成交数量,预览现金和币,成交或放弃。</summary>
private static void OtcPage(CustomUIBuilder b)
{
var o = OtcClient.Active;
b.AddHeader(L.T("otc.h"));
if (o == null || !OtcClient.Present()) { b.AddLabel(L.T("otc.msg.gone")); return; }
var w = new[] { 200f, 300f };
bool take = o.Side == OtcSide.Sell;
TableRow(b, w, L.T("otc.k.side"), L.T(take ? "otc.side.sell" : "otc.side.buy", o.Asset));
TableRow(b, w, L.T("otc.k.qty"), Fmt.Num(o.Qty) + " " + o.Asset);
TableRow(b, w, L.T("otc.k.value"), Fmt.Credits(o.Value));
TableRow(b, w, L.T(take ? "otc.k.ask" : "otc.k.bid"), Fmt.Credits(o.Cash));
TableRow(b, w, L.T("otc.k.unit"), Fmt.Num(o.Cash / o.Qty) + " / " + o.Asset);
b.BeginRow(6f, TextAnchor.MiddleLeft);
b.AddLabel(L.T("otc.k.addr", ChainAddress.Short(OtcClient.ActiveAddress)));
if (take) b.AddButton(L.T("otc.lookup"), () => { _explorerQuery = OtcClient.ActiveAddress; _page = Page.Wallets; _walletTab = WalletTab.Explorer; _message = null; Open(); });
b.End();
b.AddSeparator();
b.BeginRow(4f);
foreach (var r in OtcRatios)
{
double ratio = r;
string v = WalletWindow.Amt(o.Qty * ratio);
PledgerSkin.Pick(b, (ratio * 100).ToString("0") + "%", (_amount ?? "").Trim() == v, () => { _amount = v; Open(); }, _picked);
}
b.End();
AmountRow(b, true, o.Asset);
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double q) && q > 0)
{
var part = Otc.Part(o, q);
b.AddLabel(take ? L.T("otc.preview.take", Fmt.Credits(part.Cash), Fmt.Num(part.Qty), o.Asset) : L.T("otc.preview.give", Fmt.Num(part.Qty), o.Asset, Fmt.Credits(part.Cash)));
if (!take) b.AddLabel(L.T("mixer.balance", o.Asset, Fmt.Num(Session.Data.Hot.Avail(o.Asset))));
}
b.BeginRow(6f);
b.AddButton(L.T("otc.deal"), () =>
{
if (!ReadAmount(out double v)) return;
string msg = OtcClient.Deal(v);
if (msg != null) _message = msg;
Open();
});
b.AddButton(L.T("otc.cancel"), () => { OtcClient.Cancel(); _message = null; _page = Page.Home; Open(); });
b.End();
}
/// <summary>钱包那边交易落地后刷新终端(开着的话),带上结果消息。</summary>
public static void Refresh(string message)
{
@@ -329,16 +391,6 @@ namespace Decentralized
TableRow(b, w, L.T("bank.crc.k"), Fmt.Num(Session.Data.Poinbase.Get(Assets.CRC)));
}
/// <summary>提取前的合规审查提示:仅保留账户污点比例(额度与命中概率按设计不公开)。</summary>
private static void AmlPreview(CustomUIBuilder b)
{
var pb = Session.Data.Poinbase;
double total = pb.Value(Session.Price);
double taintRatio = total > 0 ? pb.Taint / total : 0;
b.AddSeparator();
b.AddLabel(L.T("aml.taint", (int)Math.Round(taintRatio * 100)), taintRatio > 0.05 ? "aml_note" : "aml_dim");
}
private static string StableStatus(Stablecoins st, string asset, int day)
{
double p = st.Price(asset);
@@ -414,8 +466,8 @@ namespace Decentralized
if (got <= 0) { _message = L.T("swap.same"); Open(); return; }
double spent = v * Session.Price(from);
CandleChart.Trade(from, spent); CandleChart.Trade(to, spent);
w.Add(from, -v);
w.Add(to, got);
// 换来的币继承付出那种币的脏比例
w.Convert(from, v, to, got);
w.RecordBuy(to, got, spent);
_message = L.T("msg.trade", Fmt.Num(v), from, Fmt.Num(got), to);
Session.Save();
@@ -451,18 +503,27 @@ namespace Decentralized
private static readonly float[] HoldWBase = new[] { 80f, 100f, 110f, 110f };
private static float[] HoldW => Sc(HoldWBase);
private static readonly TextAnchor[] HoldAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
/// <summary>钱包的持仓表多一列“其中脏的”(Poinbase 没有污点,不显示)。</summary>
private static readonly float[] HoldDWBase = new[] { 60f, 85f, 75f, 90f, 90f };
private static float[] HoldDW => Sc(HoldDWBase);
private static readonly TextAnchor[] HoldDAligns = { TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleRight };
private static void Holdings(CustomUIBuilder b, Wallet w, Func<string, bool> show)
{
TableHeader(b, HoldW.Sum() + 8f, HoldW, HoldAligns, L.T("col.asset"), L.T("col.amount"), L.T("col.value"), L.T("col.avg"));
bool dirtyCol = w != Session.Data.Poinbase;
var hw = dirtyCol ? HoldDW : HoldW; var ha = dirtyCol ? HoldDAligns : HoldAligns;
if (dirtyCol) TableHeader(b, hw.Sum() + 8f, hw, ha, L.T("col.asset"), L.T("col.amount"), L.T("col.dirty"), L.T("col.value"), L.T("col.avg"));
else TableHeader(b, hw.Sum() + 8f, hw, ha, L.T("col.asset"), L.T("col.amount"), L.T("col.value"), L.T("col.avg"));
bool any = false;
foreach (var kv in w.Balances.OrderBy(k => k.Key).Where(k => show(k.Key)))
{
any = true;
string asset = kv.Key;
bool selected = (_page == Page.Poinbase && _poinTab == PoinTab.Funds && asset == _fundsAsset);
TableRow(b, HoldW.Sum() + 8f, HoldW, HoldAligns,
new[] { kv.Key, Fmt.Num(kv.Value), Fmt.Credits(kv.Value * Session.Price(kv.Key)), AvgText(w, kv.Key) },
double dirty = w.DirtyOf(asset);
TableRow(b, hw.Sum() + 8f, hw, ha,
dirtyCol ? new[] { kv.Key, Fmt.Num(kv.Value), dirty > 1e-9 ? Fmt.Num(dirty) : "—", Fmt.Credits(kv.Value * Session.Price(kv.Key)), AvgText(w, kv.Key) }
: new[] { kv.Key, Fmt.Num(kv.Value), Fmt.Credits(kv.Value * Session.Price(kv.Key)), AvgText(w, kv.Key) },
selected,
() => { if (_page == Page.Poinbase && _poinTab == PoinTab.Funds) { _fundsAsset = asset; if (asset != Assets.CRC) _fundsBank = false; Open(); } });
}
@@ -495,7 +556,6 @@ namespace Decentralized
l.AddSeparator();
l.AddHeader(L.T("poin.account.h"));
Holdings(l, w, a => true);
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)), "taint_note");
}, r =>
{
// 交易对跟着行情图选中的币(CRC 本身没有交易对,显示 PSC/CRC)
@@ -522,7 +582,6 @@ namespace Decentralized
{
l.AddHeader(L.T("poin.account.h"));
Holdings(l, w, a => true);
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)), "taint_note");
}, r =>
{
r.BeginRow(4f);
@@ -560,6 +619,9 @@ namespace Decentralized
else
{
r.AddLabel(L.T("poin.deposit", d.Address("poinbase")), "deposit_dim");
// 只收干净的币:连着的钱包里有脏币时,写明每种币能充多少
if (d.Connected && Exchange.Listed.Any(a => d.Hot.DirtyOf(a) > 1e-9))
r.AddLabel(L.T("poin.deposit.clean", WalletWindow.NameOf(d.Hot), string.Join(" ", Exchange.Listed.Where(a => d.Hot.Get(a) > 1e-9).Select(a => Fmt.Num(d.Hot.CleanAvail(a)) + " " + a))), "taint_note");
r.AddButton(L.T("hw.copy"), () => { Clipboard.Copy(d.Address("poinbase")); _message = L.T("hw.copied.addr", d.Address("poinbase")); Open(); });
}
return;
@@ -567,7 +629,6 @@ namespace Decentralized
if (_fundsBank)
{
AmountRow(r);
AmlPreview(r);
r.AddButton(L.T("trade.withdraw"), Withdraw);
return;
}
@@ -1247,13 +1308,12 @@ namespace Decentralized
Summary = L.T("predict.sum.sell", PredictGame.Question(m)),
Execute = pw =>
{
var res = d.Predict.SellAll(pw, m, StoreStation.GetDayCounter(), d.Rng, out double got, out double fraud);
var res = d.Predict.SellAll(pw, m, StoreStation.GetDayCounter(), Session.Price, out double got, out double taint);
_message = res == PredictResult.Ok ? L.T("predict.sold", Fmt.Num(got)) : L.T("predict.err." + res);
if (fraud > 0)
if (res == PredictResult.Ok && taint > 1e-9)
{
PlayerStore.Instance.secData.CommitCrime("FRAUD", (int)Math.Round(fraud));
_message = L.T("predict.sold.caught", Fmt.Num(got));
Log.Info($"内幕卖出被查:只退本金 {got:0},罪证 {fraud:0}");
_message = L.T("predict.sold.caught", Fmt.Num(got), Fmt.Num(taint));
Log.Info($"内幕卖出:收回 {got:0},其中 {taint:0.##} CRC 记为脏的");
}
if (res == PredictResult.Ok) Session.Save();
Open();
@@ -1292,18 +1352,17 @@ namespace Decentralized
double margin = pos.Margin;
CandleChart.Trade(_perpProduct, pos.Notional(PerpSpot(_perpProduct)));
double paid = d.Perps.Close(d.Poinbase, _perpProduct, PerpSpot(_perpProduct), StoreStation.GetDayCounter(), out double deficit);
// 操纵:平仓盈利,而且盈利方向和玩家自己推价的方向一致,按推价幅度被查:盈利没收,记诈骗罪证,永续账户冻结几天
// 操纵:平仓盈利,而且盈利方向和玩家自己推价的方向一致,按推价幅度被查:盈利照常留在 Poinbase 账户,只记诈骗罪证
double push = d.Market.PlayerPush, profit = paid - margin;
bool aligned = _perpProduct == "PSC" && profit > 0 && (pos.Long ? push > 0 : push < 0);
if (aligned && d.Rng.Chance(Math.Min(1, Market.ManipChance(push) * StoryMods.Current.Enforce(StoreStation.GetDayCounter()))))
{
int day = StoreStation.GetDayCounter();
d.Poinbase.Add(Assets.CRC, -Math.Min(profit, d.Poinbase.Avail(Assets.CRC)));
d.Perps.BannedUntil = day + Tuning.ManipFreezeDays;
PlayerStore.Instance.secData.CommitCrime("FRAUD", (int)Math.Round(Math.Min(Tuning.PredictFraudCap, profit * Tuning.PredictFraudShare)));
int fraud = (int)Math.Round(Wallet.FraudFor(profit));
if (fraud > 0) PlayerStore.Instance.secData.CommitCrime("FRAUD", fraud);
News.Show("pd_news_manip_" + day, L.T("news.manip.title"), L.T("news.manip.desc"), 3);
_message = L.T("perp.manip", Fmt.Credits(profit), Tuning.ManipFreezeDays);
Log.Info($"永续操纵被查:盈利 {profit:0} 没收,推价 {push:0.000}");
_message = L.T("perp.manip", Fmt.Credits(profit), fraud);
Log.Info($"永续操纵被查:盈利 {profit:0},记诈骗罪证 {fraud},推价 {push:0.000}");
Session.Save();
Open();
return;
@@ -1351,7 +1410,7 @@ namespace Decentralized
Cell(b, cols[0], (isCurrent ? "● " : "") + WalletWindow.NameOf(w), false, TextAnchor.MiddleLeft);
Cell(b, cols[1], w.Address, false, TextAnchor.MiddleLeft);
Cell(b, cols[2], Fmt.Credits(w.Value(Session.Price)), false, TextAnchor.MiddleRight);
Cell(b, cols[3], Fmt.Credits(w.Taint), false, TextAnchor.MiddleRight);
Cell(b, cols[3], Fmt.Credits(w.Taint(Session.Price)), false, TextAnchor.MiddleRight);
b.End();
b.End();
b.BeginPanel(cols[4] - btnCut * k, 32f * F, TextAnchor.MiddleLeft, 0f, 0f);
@@ -1478,19 +1537,21 @@ namespace Decentralized
b.End();
}
private static bool GrayAvailable => Unlock.MixerUnlocked || Unlock.BlackUnlocked;
private enum GrayTab { Mixer, Black }
private static bool GrayAvailable => Unlock.MixerUnlocked || Unlock.BlackUnlocked || Unlock.OtcUnlocked;
private static bool GrayTabOn(GrayTab t) => t == GrayTab.Mixer ? Unlock.MixerUnlocked : t == GrayTab.Black ? Unlock.BlackUnlocked : Unlock.OtcUnlocked;
private enum GrayTab { Mixer, Black, Own }
private static GrayTab _grayTab = GrayTab.Mixer;
/// <summary>灰网:混币器和商店,各自在人脉网络里解锁,解锁了哪个就有哪个。</summary>
private static void GrayPage(CustomUIBuilder b)
{
if (_grayTab == GrayTab.Mixer && !Unlock.MixerUnlocked) _grayTab = GrayTab.Black;
if (_grayTab == GrayTab.Black && !Unlock.BlackUnlocked) _grayTab = GrayTab.Mixer;
if (Unlock.MixerUnlocked && Unlock.BlackUnlocked)
TabRows(b, Enum.GetValues(typeof(GrayTab)).Cast<GrayTab>().Select(t => { var tab = t; return new TabDef { Name = L.T("gray.sub." + t.ToString().ToLowerInvariant()), On = tab == _grayTab, Click = () => { _grayTab = tab; _message = null; Open(); } }; }).ToList(), false);
var tabs = Enum.GetValues(typeof(GrayTab)).Cast<GrayTab>().Where(GrayTabOn).ToList();
if (!GrayTabOn(_grayTab) && tabs.Count > 0) _grayTab = tabs[0];
if (tabs.Count > 1)
TabRows(b, tabs.Select(t => { var tab = t; return new TabDef { Name = L.T("gray.sub." + t.ToString().ToLowerInvariant()), On = tab == _grayTab, Click = () => { _grayTab = tab; _message = null; Open(); } }; }).ToList(), false);
if (!Session.Data.Connected) b.AddLabel(L.T("web3.err.noconn"), "term_note");
if (_grayTab == GrayTab.Mixer) MixerPage(b); else BlackPage(b);
if (_grayTab == GrayTab.Mixer) MixerPage(b); else if (_grayTab == GrayTab.Own) OwnMixPage(b); else BlackPage(b);
}
/// <summary>Web3:链上钱包与去中心化交易。</summary>
@@ -1512,7 +1573,7 @@ namespace Decentralized
Holdings(l, w, a => true);
l.AddSeparator();
l.AddLabel(L.T("wallet.total", Fmt.Credits(w.Value(Session.Price))));
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)), "taint_note");
if (w.Taint(Session.Price) > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint(Session.Price))), "taint_note");
PriceTable(l);
l.AddSeparator();
CandleChart.AddPicker(l, "web3", Assets.PSC, Open, _picked);
@@ -1579,9 +1640,11 @@ namespace Decentralized
if (key != null)
{
if (key.StartsWith("airdrop:") || key.StartsWith("yield:")) return key.Substring(key.IndexOf(':') + 1);
if (key.StartsWith("otc:")) return L.T("otc.name");
switch (key)
{
case "mixer": return L.T("explorer.mixer");
case "mixer": return d.Story.MixerRebranded ? L.T("mixer.brand") : L.T("explorer.mixer");
case "mixer_old": return L.T("explorer.mixer");
case "platform": return L.T("merchant.Platform");
case "desk": return L.T("explorer.desk");
case "dex": return "DEX";
@@ -1591,6 +1654,7 @@ namespace Decentralized
case "intel": return L.T("intel.h");
case "ponzi": return L.T("ponzi.h");
case "tumbler": return "Tumbler";
case "ownmix": return L.T("gray.sub.own");
}
}
var sl = d.Black.Sellers.FirstOrDefault(x => ChainAddress.Same(x.Name, a));
@@ -1703,7 +1767,7 @@ namespace Decentralized
var sellers = d.Black.Sellers;
if (_merchant >= sellers.Count) _merchant = -1;
bool platform = _merchant < 0;
b.AddLabel(L.T("black.rep", rep, BlackMarket.MaxLevel(rep)) + (w.Taint > 0.5 ? " " + L.T("wallet.taint", Fmt.Credits(w.Taint)) : ""), "black_rep_dim");
b.AddLabel(L.T("black.rep", rep, BlackMarket.MaxLevel(rep)) + (w.Taint(Session.Price) > 0.5 ? " " + L.T("wallet.taint", Fmt.Credits(w.Taint(Session.Price))) : ""), "black_rep_dim");
b.BeginRow(8f, TextAnchor.UpperLeft);
b.BeginPanel(SellerW + 8f, -1f, TextAnchor.UpperLeft, 0f, 2f);
_clear.Add(b.Last.image);
@@ -2214,7 +2278,7 @@ namespace Decentralized
{
if (pw.Avail(src) + 1e-9 < needSrc) { Finish(L.T("msg.balance")); return; }
double got = credits / Session.Price(coin) * 1.002;
pw.Add(src, -needSrc); pw.Add(coin, got); pw.RecordBuy(coin, got, credits);
pw.Convert(src, needSrc, coin, got); pw.RecordBuy(coin, got, credits);
CandleChart.Trade(src, credits); CandleChart.Trade(coin, credits);
Log.Info($"自动换币:{needSrc:0.####} {src} → {got:0.####} {coin}({credits:0} 信用点等值)");
// 一次签名两步:经 DEX 换币,再付给这家商家;分别记账
@@ -2347,7 +2411,7 @@ namespace Decentralized
b.AddLabel(L.T("black.pay.amount", WalletWindow.Amt(p.Amount), p.Asset, Fmt.Credits(p.Amount * Session.Price(p.Asset))));
b.BeginRow(6f);
b.AddButton(L.T("black.pay.wallet"), () => WalletWindow.OpenForBill(p));
b.AddButton(L.T("black.pay.cancel"), () => { WalletWindow.Pending = null; _message = null; Open(); });
b.AddButton(L.T("black.pay.cancel"), () => { if (OtcClient.IsBill(p)) OtcClient.Void(); else WalletWindow.Pending = null; _message = null; Open(); });
b.End();
}
@@ -2448,8 +2512,6 @@ namespace Decentralized
double got = _deskIn ? d.Black.ToPonero(pw, _deskAsset, v, day, Session.Price) : d.Black.FromPonero(pw, _deskAsset, v, day, Session.Price);
if (got < 0) { _message = L.T("black.desk.fail"); Open(); return; }
{ double cr = v * Session.Price(_deskIn ? _deskAsset : Assets.Ponero); CandleChart.Trade(Assets.Ponero, cr); CandleChart.Trade(_deskAsset, cr); }
// 从 Ponero 出来的币来源不明:出金审查按"来自混币器"加重
if (!_deskIn) d.Mixer.LastReturnDay = day;
_message = _deskIn ? L.T("msg.trade", Fmt.Num(v), _deskAsset, Fmt.Num(got), Assets.Ponero) : L.T("msg.trade", Fmt.Num(v), Assets.Ponero, Fmt.Num(got), _deskAsset);
Session.Save();
Open();
@@ -2471,9 +2533,9 @@ namespace Decentralized
int day = StoreStation.GetDayCounter();
Columns(b, l =>
{
l.AddHeader(L.T("mixer.h"));
l.AddHeader(d.Story.MixerRebranded ? L.T("mixer.brand") : L.T("mixer.h"));
l.AddLabel(d.Story.MixerClosed ? L.T("mixer.status.closed") : d.Mixer.Sanctioned(day) ? L.T("mixer.sanctioned", d.Mixer.SanctionedUntil - day + 1) : L.T("mixer.ok"), d.Story.MixerClosed || d.Mixer.Sanctioned(day) ? "mixer_status_note" : "mixer_status_dim");
if (w.Taint > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint)), "taint_note");
if (w.Taint(Session.Price) > 0.5) l.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint(Session.Price))), "taint_note");
l.AddSeparator();
l.AddHeader(L.T("mixer.pending"));
if (d.Mixer.Pending.Count == 0) l.AddLabel(L.T("mixer.none"), "mixer_none_dim");
@@ -2481,7 +2543,7 @@ namespace Decentralized
{
TableHeader(l, MixW.Sum() + 8f, MixW, MixAligns, L.T("col.asset"), L.T("col.amount"), L.T("mixer.col.taint"), L.T("mixer.col.ready"));
foreach (var p in d.Mixer.Pending)
TableRow(l, MixW.Sum() + 8f, MixW, MixAligns, new[] { p.Asset, Fmt.Num(p.Amount), Fmt.Credits(p.Taint), L.T("mixer.day", p.ReadyDay) });
TableRow(l, MixW.Sum() + 8f, MixW, MixAligns, new[] { p.Asset, Fmt.Num(p.Amount), p.Dirty > 1e-9 ? Fmt.Num(p.Dirty) : "—", L.T("mixer.day", p.ReadyDay) });
}
}, r =>
{
@@ -2506,7 +2568,7 @@ namespace Decentralized
r.End();
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0)
{
double rate = Mixer.RoundRate(v * Session.Price(_mixAsset));
double rate = Mixer.RoundRate(v * Session.Price(_mixAsset), day);
r.AddLabel(L.T("mixer.preview", Fmt.Num(v * Math.Pow(1 - Mixer.Fee, _mixRounds)), _mixAsset, (int)Math.Round((1 - Math.Pow(1 - rate, _mixRounds)) * 100), day + _mixRounds), "mixer_preview_dim");
}
r.AddButton(L.T("mixer.go"), () =>
@@ -2631,7 +2693,7 @@ namespace Decentralized
if (pw.Avail(coin) + 1e-9 < v) { _message = L.T("msg.balance"); Open(); return; }
double spent = v * Session.Price(coin);
cra = d.Market.Swap(coin, Assets.CRA, v);
pw.Add(coin, -v); pw.Add(Assets.CRA, cra); pw.RecordBuy(Assets.CRA, cra, spent);
pw.Convert(coin, v, Assets.CRA, cra); pw.RecordBuy(Assets.CRA, cra, spent);
CandleChart.Trade(coin, spent); CandleChart.Trade(Assets.CRA, spent);
}
bool ok = into ? d.Staking.Stake(pw, cra, Session.Price) : d.Staking.RequestUnstake(pw.Address, v, day);
@@ -2720,16 +2782,10 @@ namespace Decentralized
var d = Session.Data;
if (d.Perps.Debt > 0) { _message = L.T("perp.debt.block"); Open(); return; }
if (d.Poinbase.Avail(Assets.CRC) + 1e-9 < v) { _message = L.T("msg.balance"); Open(); return; }
var r = d.Exchange.Cashout(d.Poinbase, v, Session.Price, StoreStation.GetDayCounter(), Session.Rent(), d.Mixer.RecentlyMixed(StoreStation.GetDayCounter()), d.Rng);
var r = d.Exchange.Cashout(d.Poinbase, v, Session.Price);
int got = (int)Math.Floor(r.Received - r.Tax);
ModHelper.AddCredit(got, true);
_message = L.T("msg.cashout", got, Fmt.Credits(r.Tax));
if (r.Outcome == AmlOutcome.Light) _message += "\n" + L.T("msg.aml_light", Fmt.Credits(r.Confiscated));
if (r.Outcome == AmlOutcome.Heavy)
{
_message += "\n" + L.T("msg.aml_heavy", Fmt.Credits(r.Confiscated));
PlayerStore.Instance.secData.CommitCrime("FENCING", (int)Math.Round(r.Crime));
}
Session.Save();
Open();
}
+211 -7
View File
@@ -1,3 +1,4 @@
using System;
using System.Linq;
using Decentralized.Domain;
@@ -7,6 +8,7 @@ namespace Decentralized
/// 本 mod 自己的剧情线:混币器运营者 Tumbler。解锁混币器、第 30 天起,四次上门(游戏的对话顾客):
/// 1 自我介绍;2 说治安部在查混币服务(当晚混币器被制裁几天);3 求你藏密钥盘,三选一(藏 / 举报 / 卖给黑市);4 结局。
/// 进度存在 <see cref="StoryMods.Tumbler"/>,结果改动混币器(见 <see cref="StoryMods.Apply"/>)。
/// 之后的剧情(<see cref="TumblerAfter"/>):治安部探员、搜查、密钥泄露、审判盘、黑市跑路、报复、长期联系人、换皮重开。
/// </summary>
public static class TumblerStory
{
@@ -24,7 +26,16 @@ namespace Decentralized
int day = StoreStation.GetDayCounter();
var t = d.Story.Tumbler;
if (t.TryStart(day, Unlock.MixerUnlocked)) Log.Info($"Tumbler 剧情开始:第 {t.StartDay} 天第一次上门");
int due = t.Due(day);
var a = d.Story.After;
if (a.AgentDue(t, day) || a.SearchDue(t, day))
{
bool search = a.SearchDue(t, day);
manager.AddNextClient(search ? Search() : Agent());
Log.Info($"治安部探员上门({(search ? "搜查" : "问话")})");
Session.Save();
return;
}
int due = t.Due(day, a);
if (due == 0) return;
// 举报之后第 4 次不上门;其余的上门要等对话真正走完才算数(当天没聊完、被清场或读档重来,明天照样来)
if (due == 4 && t.Choice == Tumbler.Report) { t.Visited(day); Log.Info("Tumbler 已被举报,不再上门"); Session.Save(); return; }
@@ -45,19 +56,206 @@ namespace Decentralized
d.Mixer.SanctionedUntil = System.Math.Max(d.Mixer.SanctionedUntil, day + 4);
News.Show("pd_news_tumbler_sanction", L.T("news.tumbler.sanction.title"), L.T("news.tumbler.sanction.desc"), 3, 8);
}
After(d, day);
}
/// <summary>四次上门之后的夜间:泄露、庭审新闻与宣判、黑市跑路、报复、联系人的镇压提示、换皮重开。</summary>
private static void After(SaveData d, int day)
{
var a = d.Story.After;
var ps = PlayerStore.Instance;
if (a.LeakDay >= 0 && day >= a.LeakDay && !a.Leaked)
{
a.Leaked = true;
var r = d.Mixer.Leak(day);
int fraud = (int)Math.Round(r.Fraud);
if (fraud > 0) ps.secData.CommitCrime("FRAUD", fraud);
if (r.Count == 0) ps.AddNightLog(L.T("nightlog.tumbler.leak.none"));
else ps.AddNightLog(L.T("nightlog.tumbler.leak", r.Count, Fmt.Credits(r.Cleaned), fraud), "cc7755");
News.Show("pd_news_tumbler_leak", L.T("news.tumbler.leak.title"), L.T("news.tumbler.leak.desc"), 3, 8);
Log.Info($"Tumbler 密钥泄露:还原 {r.Count} 笔出款,洗掉的污点 {r.Cleaned:0},记诈骗罪证 {fraud}");
}
bool? hint = a.Hint(day, d.Rng);
if (hint != null)
{
int n = a.Hints.Count, v = (n - 1) % 3 + 1;
string side = hint.Value ? "pro" : "def";
News.Show("pd_news_trial_" + day, L.T("news.trial." + side + ".title", n), L.T("news.trial." + side + ".desc" + v), 1, 7);
Log.Info($"庭审第 {n} 天:{(hint.Value ? "控方" : "辩方")}占上风,公开定罪概率 {a.TrialPublic():0.00}(判决 {a.Verdict})");
}
if (a.Verdict >= 0 && !a.TrialDone && day >= a.TrialDay)
{
a.TrialDone = true;
if (a.Verdict == 1) News.Show("pd_news_trial_verdict", L.T("news.trial.convict.title"), L.T("news.trial.convict.desc"), 3, 8);
else
{
a.RevengeDay = day + Tuning.TumblerRevengeDelay;
News.Show("pd_news_trial_verdict", L.T("news.trial.acquit.title"), L.T("news.trial.acquit.desc"), 3, 8);
}
Log.Info($"Tumbler 宣判:{(a.Verdict == 1 ? "定罪" : "无罪")}");
}
if (a.BmRugDay >= 0 && day >= a.BmRugDay && !a.BmRugged)
{
a.BmRugged = true;
foreach (var m in d.Mixer.RugAll()) ps.AddNightLog(L.T("nightlog.mixer_rug", Fmt.Num(m.Amount), m.Asset), "cc7755");
d.Story.Apply("tumbler_bm_rug", day);
a.ClosedDay = day;
a.RevengeDay = day + Tuning.TumblerRevengeDelay;
News.Show("pd_news_tumbler_bmrug", L.T("news.tumbler.bmrug.title"), L.T("news.tumbler.bmrug.desc"), 4, 8);
Log.Info("黑市接手的混币器卷款跑路");
}
if (a.RevengeDay >= 0 && day >= a.RevengeDay && !a.Revenged)
{
a.Revenged = true;
Revenge(d, day);
}
if (a.Contact) Tip(d, day, a);
if (a.RebrandDue(day))
{
a.Rebranded = true;
bool honeypot = d.Rng.Chance(Tuning.TumblerHoneypot);
d.Story.Rebrand(honeypot);
d.Addresses["mixer_old"] = d.Address("mixer");
d.Addresses["mixer"] = ChainAddress.Make(d.Rng.NextUInt());
News.Show("pd_news_mixer_rebrand", L.T("news.mixer.rebrand.title", L.T("mixer.brand")), L.T("news.mixer.rebrand.desc", L.T("mixer.brand")), 4);
Log.Info($"混币器换皮重开:{L.T("mixer.brand")},钓鱼 {honeypot},费率 {Mixer.Fee:P1}");
}
}
/// <summary>
/// Tumbler 报复:先留一张纸条。按概率给当前钱包植入木马(剪贴板劫持、钱够多后被转空,和商城的投毒钱包一样);
/// 否则每个钱包对最近的转出对象各收到一笔来自相似地址的 0 币转入(地址投毒)。
/// </summary>
private static void Revenge(SaveData d, int day)
{
var ps = PlayerStore.Instance;
ps.AddNightLog(L.T("nightlog.tumbler.note"), "cc7755");
if (d.Connected && !d.Hot.Poisoned && d.Rng.Chance(Tuning.TumblerRevengeWalletShare))
{
d.Hot.Poisoned = true;
Log.Info($"Tumbler 报复:钱包 {d.Hot.Address} 被植入木马");
return;
}
int n = 0;
foreach (var w in d.Wallets)
{
var targets = w.History.Where(h => !h.In && h.Amount > 0 && d.Own(h.Addr) == null).Select(h => h.Addr).Distinct().Take(Tuning.TumblerRevengePoison).ToList();
if (targets.Count == 0) targets.Add(d.Address("poinbase"));
foreach (var t in targets) { d.PoisonPlans.Add(new PoisonPlan { Wallet = w.Address, Target = t, Day = day + d.Rng.Range(1, Tuning.TxPoisonDelayMax) }); n++; }
}
Log.Info($"Tumbler 报复:安排 {n} 笔地址投毒");
}
/// <summary>长期联系人:游戏排期里明天开始的镇压(玩家知不知道都算),提前一晚提示,每个只提示一次。</summary>
private static void Tip(SaveData d, int day, TumblerAfter a)
{
var mgr = StoreStation.Instance?.storeEventManager;
if (mgr == null) return;
for (int i = 0; i < mgr.futurEvents.Count && i < mgr.futurEventsDay.Count; i++)
{
var e = mgr.futurEvents[i];
int start = day + mgr.futurEventsDay[i];
if (start != day + 1 || (e.identifier != "crackdown" && e.identifier != "sec_martial_law") || a.Tipped.Contains(start)) continue;
a.Tipped.Add(start);
PlayerStore.Instance.AddNightLog(L.T("nightlog.tumbler.tip"));
Log.Info($"Tumbler 提示:第 {start} 天开始 {e.identifier}");
}
a.Tipped.RemoveAll(x => x < day - 30);
}
private static StoreClient New()
{
// 每次上门是同一个人:外观第一次上门时定下来
var t = Session.Data.Story.Tumbler;
if (string.IsNullOrEmpty(t.Sprite)) t.Sprite = SpriteDict.GetRandomShadySpriteName();
var c = new StoreClient
{
identifier = "pd_tumbler", displayName = L.T("tumbler.name"), spriteName = SpriteDict.GetRandomShadySpriteName(),
identifier = "pd_tumbler", displayName = L.T("tumbler.name"), spriteName = t.Sprite,
clientIntent = StoreClient.ClientIntent.DIALOGUE, nonShootable = true, dismissable = false,
};
ModHelper.SetClientSource(c, ModInfo.Id);
return c;
}
/// <summary>治安部探员(问话和搜查是同一个人)。</summary>
private static StoreClient NewAgent()
{
var a = Session.Data.Story.After;
if (string.IsNullOrEmpty(a.AgentSprite)) a.AgentSprite = SpriteDict.GetRandomSecSpriteName();
var c = new StoreClient
{
identifier = "pd_sec_agent", displayName = L.T("agent.name"), spriteName = a.AgentSprite,
clientIntent = StoreClient.ClientIntent.DIALOGUE, nonShootable = true, dismissable = false,
};
ModHelper.SetClientSource(c, ModInfo.Id);
return c;
}
/// <summary>第 2、3 次上门之间:探员来问 Tumbler 有没有来过。配合:治安部声望 +;否认:没有影响。</summary>
private static StoreClient Agent()
{
var c = NewAgent();
var ask = new Dialogue(); var yes = new Dialogue(); var no = new Dialogue();
yes.SetText("PLAYER", L.T("agent.choice.yes")).NextDialogue().SetText(c.displayName, L.T("agent.yes1")).SetEndAction(() => AgentDone(TumblerAfter.Cooperate));
no.SetText("PLAYER", L.T("agent.choice.no")).NextDialogue().SetText(c.displayName, L.T("agent.no1")).SetEndAction(() => AgentDone(TumblerAfter.Deny));
Lines(c, c.mainDialogue, "agent.v1.", 3).SetNextDialogue(ask);
ask.SetText(c.displayName, L.T("agent.ask"))
.AddChoice(DirectoryMaster.Item("$"), "-", "", yes, () => { })
.AddChoice(DirectoryMaster.Item("$"), "-", "", no, () => { });
c.AddBasicDialog();
c.CompleteClientCreation();
return c;
}
private static void AgentDone(int choice)
{
Log.Guard("探员问话结束", () =>
{
var a = Session.Data.Story.After;
if (a.Agent == 0)
{
a.Agent = choice; a.AgentDay = StoreStation.GetDayCounter();
if (choice == TumblerAfter.Cooperate) StoreReputation.ModSecReputation(Tuning.TumblerAgentSecRep);
}
Log.Info($"探员问话:{(choice == TumblerAfter.Cooperate ? "配合" : "否认")}");
Session.Save();
});
Leave();
}
private static GameItem FindDrive() => EmporiumEntry.Instance?.GetAllOwnedItems().FirstOrDefault(i => i != null && i.identifier == Items.KeyDrive);
/// <summary>配合过探员、又帮 Tumbler 藏了密钥盘:第 4 次上门前一天来搜查,搜到就没收(记违禁品罪证),当晚泄露。</summary>
private static StoreClient Search()
{
var c = NewAgent();
bool found = FindDrive() != null;
Lines(c, c.mainDialogue, "agent.search.", 2).NextDialogue().SetText(c.displayName, L.T(found ? "agent.search.found" : "agent.search.none")).SetEndAction(() =>
{
Log.Guard("探员搜查", () =>
{
var a = Session.Data.Story.After;
int day = StoreStation.GetDayCounter();
if (a.Searched) return;
a.Searched = true; a.SearchDay = day;
var drive = FindDrive();
if (drive != null)
{
drive.Destroy();
a.Seized = true;
PlayerStore.Instance.secData.CommitCrime("CONTRABAND_TRAFFICKING", Tuning.TumblerSearchCrime);
a.LeakTonight(day);
}
Log.Info($"探员搜查:{(drive != null ? "搜走了密钥盘" : "没搜到")}");
Session.Save();
});
Leave();
});
c.AddBasicDialog();
c.CompleteClientCreation();
return c;
}
private static Dialogue Lines(StoreClient c, Dialogue first, string prefix, int n)
{
var dl = first.SetText(c.displayName, L.T(prefix + "1"));
@@ -90,9 +288,11 @@ namespace Decentralized
case 4:
if (choice == Tumbler.Hide)
{
bool has = EmporiumEntry.Instance != null && EmporiumEntry.Instance.GetAllOwnedItems().Any(i => i != null && i.identifier == Items.KeyDrive);
if (has) Lines(c, c.mainDialogue, "tumbler.v4.back.", 3).SetEndAction(() => { Returned(); Finish(4); });
else Lines(c, c.mainDialogue, "tumbler.v4.lost.", 3).SetEndAction(() => Finish(4));
bool has = FindDrive() != null;
if (d.Story.After.Seized) Lines(c, c.mainDialogue, "tumbler.v4.seized.", 3).SetEndAction(() => Finish(4));
else if (has) Lines(c, c.mainDialogue, "tumbler.v4.back.", 3).SetEndAction(() => { Returned(); Finish(4); });
// 盘不在手上:按落到治安部算,当晚泄露
else Lines(c, c.mainDialogue, "tumbler.v4.lost.", 3).SetEndAction(() => { Session.Data.Story.After.LeakTonight(StoreStation.GetDayCounter()); Finish(4); });
}
else if (choice == Tumbler.Sell) Lines(c, c.mainDialogue, "tumbler.v4.sold.", 3).SetEndAction(() => Finish(4));
else Lines(c, c.mainDialogue, "tumbler.v4.none.", 1).SetEndAction(() => Finish(4));
@@ -134,13 +334,16 @@ namespace Decentralized
break;
case Tumbler.Report:
d.Story.Apply("tumbler_reported", day);
d.Story.After.LeakTonight(day);
d.Story.After.StartTrial(day, d.Rng);
ModHelper.AddCredit(Tuning.TumblerBounty, true);
StoreReputation.ModSecReputation(Tuning.TumblerSecRep);
News.Show("pd_news_tumbler_caught", L.T("news.tumbler.caught.title"), L.T("news.tumbler.caught.desc"), 4, 8);
break;
case Tumbler.Sell:
d.Story.Apply("tumbler_sold", day);
d.Hot.Add(Assets.CRT, Tuning.TumblerSellCrt); d.Hot.Taint += Tuning.TumblerSellCrt * 0.5; d.Hot.Basis += Tuning.TumblerSellCrt;
d.Story.After.StartSold(day, d.Rng);
d.Hot.AddDirty(Assets.CRT, Tuning.TumblerSellCrt, Tuning.TumblerSellCrt * Tuning.TumblerSellTaint); d.Hot.Basis += Tuning.TumblerSellCrt;
d.Chain(d.Address("platform"), d.Hot.Address, Assets.CRT, Tuning.TumblerSellCrt);
StoreReputation.ModReputation(StoreReputation.FACTION_BLACK_MARKET, Tuning.TumblerBmRep);
News.Show("pd_news_tumbler_sold", L.T("news.tumbler.sold.title"), L.T("news.tumbler.sold.desc"), 4, 8);
@@ -157,10 +360,11 @@ namespace Decentralized
Log.Guard("Tumbler 取回密钥盘", () =>
{
var d = Session.Data;
var drive = EmporiumEntry.Instance?.GetAllOwnedItems().FirstOrDefault(i => i != null && i.identifier == Items.KeyDrive);
var drive = FindDrive();
if (drive == null) return;
drive.Destroy();
d.Story.Apply("tumbler_kept", StoreStation.GetDayCounter());
d.Story.After.Contact = true;
d.Hot.Add(Assets.CRT, Tuning.TumblerGift); d.Hot.Basis += Tuning.TumblerGift;
d.Chain(d.Address("tumbler"), d.Hot.Address, Assets.CRT, Tuning.TumblerGift);
PlayerStore.Instance.AddNightLog(L.T("nightlog.tumbler.gift", Fmt.Num(Tuning.TumblerGift), WalletWindow.NameOf(d.Hot)));
+44
View File
@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Linq;
using Decentralized.Domain;
namespace Decentralized
{
/// <summary>
/// 涡轮增压器作用在矿架、矿机上:和原版机器一样立刻多跑一轮,这一轮就是立刻挖一晚。
/// 原版增压器的效果是调用机器的夜间周期回调(矿架、矿机的回调是"扣电并登记今晚挖矿"),所以在增压器外包一层,
/// 作用于矿机时打上 <see cref="Boosting"/>,回调看到它就改为立刻结算一轮:扣一晚的电,按这台机器的算力出块并发到它的收款地址,算一次损耗和暴毙。
/// 增压器本身的消耗、充电、可用次数都照原版。
/// </summary>
public static class TurboMine
{
/// <summary>原版增压器正在作用于矿机(只在增压器的作用回调里为真)。</summary>
public static bool Boosting;
/// <summary>
/// 立刻挖一轮(电已经扣过)。出块按全网份额算:玩家这边的算力取昨晚全部机器的算力(至少是这台),这台机器分其中自己的那份,
/// 和夜里一起挖时分到的差不多。
/// </summary>
public static void Round(GameItem machine, List<KeyValuePair<GameItem, ModuleBonus>> cards)
{
var d = Session.Data;
if (d == null || cards.Count == 0) return;
var units = cards.Select(c => (item: c.Key, unit: Items.ToUnit(c.Key), bonus: c.Value)).ToList();
double hash = units.Sum(x => x.unit.Hash(x.bonus));
if (hash <= 0) return;
double player = System.Math.Max(hash, d.LastNight?.Hash ?? 0);
double mined = d.Network.Mine(player) * hash / player;
bool paid = Night.PayMachine(d, machine, mined);
var ps = PlayerStore.Instance;
ps.AddNightLog(paid ? L.T("nightlog.turbo", machine.name, Fmt.Num(mined)) : L.T("nightlog.mined.lost", Fmt.Num(mined)));
foreach (var (item, unit, bonus) in units)
{
bool died = unit.Night(bonus, d.Rng);
Items.Store(item, unit);
if (died) ps.AddNightLog(L.T("nightlog.burnt", item.name), "cc7755");
}
Log.Info($"涡轮增压:{machine.name} 立刻挖一轮,算力 {hash:0.##},产出 {mined:0.####} PSC{(paid ? "" : "(没设置收款地址,丢失)")}");
Session.Save();
}
}
}
+13
View File
@@ -15,6 +15,10 @@ namespace Decentralized
public static bool MixerUnlocked => PlayerStore.Instance != null && ModHelper.IsNetworkUpgradeUnlocked(MixerId);
public const string BlackId = "PD_BLACKMARKET";
/// <summary>场外换币台:小偷灯多一个目标"换币客户",买下时附赠一盏小偷灯(已切到这个目标)。</summary>
public const string OtcId = "PD_OTC";
public static bool OtcUnlocked => PlayerStore.Instance != null && ModHelper.IsNetworkUpgradeUnlocked(OtcId);
public static bool BlackUnlocked => PlayerStore.Instance != null && ModHelper.IsNetworkUpgradeUnlocked(BlackId);
public static bool IsUnlocked => PlayerStore.Instance != null && ModHelper.IsNetworkUpgradeUnlocked(Id);
@@ -27,6 +31,7 @@ namespace Decentralized
Add(Id, Price, () => L.T("upgrade.title"), () => L.T("upgrade.desc"), OnPurchased, "terminal", null);
Add(MixerId, MixerPrice, () => L.T("mixer.upgrade.title"), () => L.T("mixer.upgrade.desc"), () => Log.Info("混币器已解锁"), "ui_web3", new[] { Id });
Add(BlackId, Domain.Tuning.BmUnlockPrice, () => L.T("black.upgrade.title"), () => L.T("black.upgrade.desc"), () => Log.Info("Web3 黑市已解锁"), "ui_web3", new[] { Id });
Add(OtcId, Domain.Tuning.OtcUnlockPrice, () => L.T("otc.upgrade.title"), () => L.T("otc.upgrade.desc"), OnOtcPurchased, "ui_web3", new[] { Id });
}
private static void Add(string id, int price, Func<string> title, Func<string> desc, Action onUnlock, string icon, string[] prerequisites)
@@ -35,6 +40,14 @@ namespace Decentralized
catch (NullReferenceException) { Log.Info($"人脉升级 {id} 已登记(主菜单阶段访问 PlayerStore 失败,已忽略)"); }
}
private static void OnOtcPurchased()
{
var lamp = ModHelper.SpawnItem(BlackLampHelper.BLACK_LAMP_ID);
OtcLamp.SetTarget(lamp, OtcLamp.Target);
Items.GiveToFront(lamp);
Log.Info("场外换币台已解锁,附赠小偷灯");
}
private static void OnPurchased()
{
Items.GiveToFront(ModHelper.SpawnItem(Items.Terminal));
+40 -10
View File
@@ -29,6 +29,8 @@ namespace Decentralized
public string Address, Asset, Summary;
public double Amount;
public bool Contract;
/// <summary>收款地址直接填好(换币客户就在柜台前,当面给地址),不用玩家手输。</summary>
public bool Prefill;
/// <summary>合约调用时钱包里实际要花掉的数量(默认等于 Amount;取出质押这类不花币的调用为 0)和付手续费的币(默认用 Asset)。</summary>
public double? Spend;
public string FeeAsset;
@@ -37,8 +39,12 @@ namespace Decentralized
public Action<Wallet> Execute;
}
/// <summary>当前待付的账单(同一时间一张,新的覆盖旧的)。任何一台钱包都可以付。</summary>
public static Bill Pending;
/// <summary>
/// 当前待付的账单(终端开的同一时间一张,新的覆盖旧的)。任何一台钱包都可以付。
/// 没有终端账单时回落到换币客户的账单(<see cref="OtcClient.Standing"/>):它不会被换币、混币这些终端账单覆盖掉。
/// </summary>
public static Bill Pending { get => _pending ?? OtcClient.Standing; set => _pending = value; }
private static Bill _pending;
private sealed class Tx
{
@@ -129,6 +135,7 @@ namespace Decentralized
if (IsOpen(dev)) CustomUIManager.Instance.CloseWindow(dev.Id);
Devices.Clear();
Pending = null;
OtcClient.Void();
}
/// <summary>终端"打开钱包转账" / DEX 签名请求:在当前连接的钱包上打开。转账填好币种和金额,地址留给玩家手输。</summary>
@@ -143,7 +150,7 @@ namespace Decentralized
else
{
dev.Screen = Screen.Send;
dev.To = "";
dev.To = bill.Prefill ? bill.Address : "";
dev.Asset = bill.Asset;
dev.Amount = Amt(bill.Amount);
}
@@ -248,7 +255,8 @@ namespace Decentralized
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)) : ""));
double dq = w.DirtyOf(a);
b.AddLabel(Amt(q) + (w.Frozen.TryGetValue(a, out var fz) && fz > 0 ? L.T("hw.balance.frozen", Amt(fz)) : "") + (dq > 1e-9 ? L.T("hw.balance.dirty", Amt(dq)) : ""));
b.End();
b.BeginPanel(RowW - 12f - 90f - 130f, 26f, TextAnchor.MiddleRight, 0f, 0f);
_clear.Add(b.Last.image);
@@ -259,7 +267,7 @@ namespace Decentralized
}
b.AddSeparator();
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(Session.Price) > 0.5) b.AddLabel(L.T("wallet.taint", Fmt.Credits(w.Taint(Session.Price))));
if (Pending != null) b.AddLabel(Pending.Contract ? L.T("hw.pending.sign", Pending.Summary) : L.T("hw.pending.bill", Pending.Summary));
if (Congested) b.AddLabel(L.T("hw.congested"), "congest_note");
b.AddSpace(6f);
@@ -338,11 +346,24 @@ namespace Decentralized
if (bill.Asset != dev.Asset) return L.T("hw.err.asset", bill.Asset);
if (v + 1e-9 < bill.Amount) return L.T("hw.err.short", Amt(bill.Amount), bill.Asset);
}
else if (IsPoinbase(to)) { if (!Exchange.Listed.Contains(dev.Asset)) return L.T("hw.err.poinbase", dev.Asset); }
else if (IsPoinbase(to))
{
if (!Exchange.Listed.Contains(dev.Asset)) return L.T("hw.err.poinbase", dev.Asset);
// Poinbase 只收干净的币:这种币最多充干净的枚数(手续费同币种时先扣掉)
double clean = CleanAfterFee(w, dev.Asset, dev.Asset == FeeAssetOf(dev) ? FeeIn(dev.Asset, dev.Tier) : 0);
if (v > clean + 1e-6) return L.T("hw.err.tainted", Amt(w.DirtyOf(dev.Asset)), dev.Asset, Amt(clean));
}
else if (Session.Data.Own(to) == null && Known(to)) return L.T("hw.err.nobill");
return null;
}
/// <summary>扣掉同币种的手续费(按脏比例带走一点脏的)之后,这种币干净、能动用的枚数。</summary>
private static double CleanAfterFee(Wallet w, string asset, double fee)
{
double r = w.DirtyRatio(asset);
return Math.Max(0, Math.Min(w.Avail(asset) - fee, (w.Get(asset) - fee) * (1 - r)));
}
private static Bill Match(string to) => Pending != null && !Pending.Contract && ChainAddress.Same(Pending.Address, to) ? Pending : null;
/// <summary>有过往来的地址(平台、商家、混币器、兑换处):没有账单时对方不收。</summary>
@@ -522,13 +543,17 @@ namespace Decentralized
dev.Tx.Confs = TxRules.Confirmations(ValueOf(dev));
dev.Tx.Dropped = d.Rng.Chance(TxRules.DropChance(dev.Tier, cong));
dev.Tx.Times = Timeline(dev.Tx.Confs, TxRules.Slowdown(dev.Tier, cong), dev.Tx.Dropped);
if (dev.Tx.Bill != null) Pending = null;
if (dev.Tx.Bill != null)
{
if (dev.Tx.Bill == OtcClient.Standing) OtcClient.Standing = null; // 签了名就不再挂着,免得重复付
Pending = null;
}
dev.Screen = Screen.Broadcast;
dev.Note = null;
Open(dev);
}
/// <summary>动画走完(或点了跳过):交易落地。付账单的执行终端那边的动作;转给自己的钱包或 Poinbase 的按比例带上污点;没人用的地址,币就没了。</summary>
/// <summary>动画走完(或点了跳过):交易落地。付账单的执行终端那边的动作;转给自己的钱包的按比例带上污点,转给 Poinbase 的只转干净的、污点留在钱包;没人用的地址,币就没了。</summary>
private static void Finish(Device dev)
{
var tx = dev.Tx;
@@ -544,12 +569,15 @@ namespace Decentralized
if (tx.Dropped)
{
// 没成交:账单还在,可以重新付
if (tx.Bill != null && Pending == null) Pending = tx.Bill;
if (tx.Bill != null && OtcClient.IsBill(tx.Bill)) OtcClient.Standing = tx.Bill;
else if (tx.Bill != null && Pending == null) Pending = tx.Bill;
TerminalWindow.Refresh(L.T("hw.tx.dropped"));
return;
}
int day = StoreStation.GetDayCounter();
double amt = tx.To != null ? Math.Min(tx.Amount, w.Avail(tx.Asset)) : 0;
// Poinbase 只收干净的币:落地时价格可能变了,超出干净部分的不转(留在钱包)
if (tx.To != null && tx.Bill == null && IsPoinbase(tx.To)) amt = Math.Min(amt, w.CleanAvail(tx.Asset));
if (tx.To != null)
{
d.Chain(w.Address, tx.To, tx.Asset, amt, tx.FeeAsset == tx.Asset ? tx.Fee : 0, tx.Block);
@@ -574,7 +602,9 @@ namespace Decentralized
if (tx.To == null) return;
var own = d.Own(tx.To);
Wallet target = own ?? (IsPoinbase(tx.To) ? d.Poinbase : new Wallet());
Wallet.Transfer(w, target, tx.Asset, amt, Session.Price);
// Poinbase 只收干净的币:污点留在钱包里
if (target == d.Poinbase) Wallet.TransferClean(w, target, tx.Asset, amt, Session.Price);
else Wallet.Transfer(w, target, tx.Asset, amt, Session.Price);
if (own == null && target != d.Poinbase) Log.Info($"转进没人用的地址 {tx.To}:{amt} {tx.Asset}");
if (own == null && d.Scams.ScamAddrs.Any(a => ChainAddress.Same(a, tx.To))) PlayerStore.Instance.AddNightLog(L.T("nightlog.scam.sent", Fmt.Num(amt), tx.Asset), "cc7755");
TerminalWindow.Refresh(null);
+361 -203
View File
@@ -106,19 +106,29 @@ internal static class Program
Check(Near(pool.QuoteReserve, 60000, 1e-9), "深度重设");
});
Test("地址:污点和成本按价值比例随币走;Ponero 换入时污点停止传播", () =>
Test("地址:每种币记脏枚数,转出、花掉、兑换按这种币的脏比例带走;成本按价值比例;Ponero 不带污点", () =>
{
Func<string, double> px = a => a == Assets.PSC ? 100 : 1;
var a = new Wallet { Address = "A", Taint = 300, Basis = 1000 };
a.Add(Assets.CRT, 1000);
var a = new Wallet { Address = "A", Basis = 1000 };
a.AddDirty(Assets.CRT, 1000, 300);
var b = new Wallet { Address = "B" };
Wallet.Transfer(a, b, Assets.CRT, 250, px);
Check(Near(b.Taint, 75, 1e-9) && Near(a.Taint, 225, 1e-9), $"转出 1/4,污点带走 1/4:{b.Taint}");
Check(Near(b.DirtyOf(Assets.CRT), 75, 1e-9) && Near(a.DirtyOf(Assets.CRT), 225, 1e-9), $"转出 1/4,脏枚数带走 1/4:{b.DirtyOf(Assets.CRT)}");
Check(Near(b.Basis, 250, 1e-9), "成本带走 1/4");
a.Add(Assets.PSC, 5);
a.Add(Assets.CRT, -150);
Check(Near(a.DirtyOf(Assets.CRT), 180, 1e-9) && a.DirtyOf(Assets.PSC) == 0 && Near(a.Taint(px), 180, 1e-9), "花掉 150 CRT:按 30% 带走 45 枚脏的;干净的 PSC 不受影响;按现价的脏币价值 180");
a.Convert(Assets.CRT, 100, Assets.PSC, 1);
Check(Near(a.DirtyOf(Assets.PSC), 0.3, 1e-9) && Near(a.DirtyOf(Assets.CRT), 150, 1e-9), "兑换:30% 脏的 CRT 换来的 PSC 也是 30% 脏");
a.Convert(Assets.CRT, 100, Assets.Ponero, 50, clean: true);
Check(a.DirtyOf(Assets.Ponero) == 0 && Near(a.DirtyOf(Assets.CRT), 120, 1e-9), "兑入 Ponero:兑掉的脏币消失,Ponero 是干净的");
var sc = new Wallet(); sc.AddDirty(Assets.CRC, 1000, 400); sc.Scale(Assets.CRC, 0.5);
Check(Near(sc.Get(Assets.CRC), 500, 1e-9) && Near(sc.DirtyOf(Assets.CRC), 200, 1e-9), "CRC 减记:脏枚数同比例缩水");
Check(Near(sc.MarkDirty(Assets.CRC, 400), 100, 1e-9) && Near(sc.DirtyOf(Assets.CRC), 500, 1e-9), "补记脏币不超过干净的枚数,返回没记上的");
var c = new Wallet { Address = "C" };
b.Add(Assets.Ponero, 10);
Wallet.Transfer(b, c, Assets.Ponero, 10, px);
Check(c.Taint == 0, "Ponero 转入的地址不带污点");
Check(c.Taint(px) == 0, "Ponero 转入的地址不带污点");
bool threw = false;
try { a.Add(Assets.CRT, -10000); } catch (InvalidOperationException) { threw = true; }
Check(threw, "余额不足时拒绝");
@@ -210,16 +220,19 @@ internal static class Program
Check(RigSlots.Shape(2) == "000000" + "000000" + new string('1', 18) && RigSlots.Shape(5) == new string('0', 30) && RigSlots.Shape(3).Length == 30, "形状:宽 3、共 10 行,前 2 × 槽数行可放");
});
Test("模块:折算系数 4,算力 +20%、省电 25%、损耗 −30% 封顶;性能加成提高损耗与暴毙", () =>
Test("模块:折算系数 4,不设上限,只有防出错的下限;性能加成提高损耗与暴毙", () =>
{
var perf = ModuleBonus.From(16, 0, 0);
Check(Near(perf.Hash, 1.04, 1e-9) && Near(perf.Wear, 1.04, 1e-9) && Near(perf.Death, 1.04, 1e-9), "一个性能模块(+16):算力 +4%,损耗和暴毙 +4%");
var cores = ModuleBonus.From(200 * 3, -100 * 3, 100 * 3);
Check(Near(cores.Hash, 1.20, 1e-9), $"塞满神经核心,算力也只 +20%:{cores.Hash:0.00}");
Check(Near(cores.Hash, 2.5, 1e-9) && Near(cores.Death, 2.5, 1e-9), $"三个无限制神经核心:算力 ×2.5,暴毙率同样 ×2.5:{cores.Hash:0.00}");
Check(Near(cores.Kj, 1.75, 1e-9), $"神经核心的负效率照常增加耗电:×{cores.Kj:0.00}");
Check(Near(cores.Wear, 0.70 * 1.20, 1e-9), "品质封顶 −30%,再乘性能带来的 +20%");
var eco = ModuleBonus.From(-16, 32 * 5, 0);
Check(Near(eco.Kj, 0.75, 1e-9) && Near(eco.Hash, 0.96, 1e-9), "省电封顶 25%;节能模块的负性能照常降低算力");
Check(Near(cores.Wear, 0.25 * 2.5, 1e-9), "品质 +300 → 损耗 −75%,再乘性能带来的发热");
var nine = ModuleBonus.From(200 * 9, -100 * 9, 100 * 9);
Check(Near(nine.Hash, 5.5, 1e-9) && Near(nine.Kj, 3.25, 1e-9) && nine.Wear == Tuning.ModuleWearFloor, "九个核心:算力 ×5.5、耗电 ×3.25,损耗降到下限");
var eco = ModuleBonus.From(-16 * 9, 32 * 9, 0);
Check(Near(eco.Kj, 1 - 32 * 9 / 400.0, 1e-9) && Near(eco.Hash, 1 - 16 * 9 / 400.0, 1e-9), "九个节能模块:省电 72%,负性能照常降低算力");
Check(ModuleBonus.From(0, 400 * 2, 0).Kj == Tuning.ModuleKjFloor && ModuleBonus.From(-400 * 2, 0, 0).Hash == Tuning.ModuleHashFloor, "耗电、算力不低于下限");
var none = ModuleBonus.From(0, 0, 0);
Check(none.Hash == 1 && none.Kj == 1 && none.Wear == 1 && none.Death == 1, "没有模块时不变");
});
@@ -496,45 +509,46 @@ internal static class Program
Check(Math.Abs(lost / (double)n - 0.1) < 0.03, $"命中率 {lost / (double)n:P1}");
});
Test("合规出金:干净小额不查;污点高时重度命中记罪证;轻度命中只没收污点部分;只对收益计税", () =>
Test("Poinbase 只收干净的币:每种币最多充干净的枚数、脏的留在钱包;出金不审查、只对收益计税", () =>
{
Func<string, double> px = a => 1;
Check(Exchange.HitChance(500, 0, 0, 200, false) == 0, "干净、低于 5 倍房租:不查");
Check(Near(Exchange.HitChance(2000, 0, 0, 200, false), 0.2, 1e-9), "单笔为大额阈值 2 倍:20%");
Check(Near(Exchange.HitChance(1000, 500, 0, 200, false), 0.75, 1e-9), "污点 50%:75%");
Check(Exchange.HitChance(100000, 100000, 1e6, 200, true) == Tuning.AmlMaxChance, "上限 90%");
int heavy = 0;
double crime = 0;
for (int i = 0; i < 1000; i++)
{
var w = new Wallet { Taint = 500, Basis = 1000 };
w.Add(Assets.CRC, 1000);
var r = new Exchange().Cashout(w, 1000, px, 1, 200, false, new Rng((ulong)i));
if (r.Outcome == AmlOutcome.Heavy) { heavy++; crime = r.Crime; Check(Near(r.Received, 1000 * (1 - Tuning.ExchangeFee) - 500, 1e-6), "重度:没收污点部分"); }
else Check(Near(r.Received, 1000 * (1 - Tuning.ExchangeFee), 1e-6) && r.Tax == 0, "未命中:全额到账;本金不计税");
}
Check(Near(heavy / 1000.0, 0.75, 0.04) && crime == 500, $"污点 50% 时约 75% 重度命中,罪证 = 污点额:{heavy / 1000.0:P0}");
var light = new Wallet { Taint = 150 };
light.Add(Assets.CRC, 1000);
CashoutResult lr = null;
for (ulong i = 0; lr == null || lr.Outcome == AmlOutcome.Clear; i++)
{
var w = new Wallet { Taint = 150 };
w.Add(Assets.CRC, 1000);
lr = new Exchange().Cashout(w, 1000, px, 1, 200, false, new Rng(i));
}
Check(lr.Outcome == AmlOutcome.Light && lr.Crime == 0 && Near(lr.Confiscated, 150, 1e-6), "污点 15%:轻度命中,没收 150、不记罪证");
Check(Near(lr.Tax, (1000 * (1 - Tuning.ExchangeFee) - 150) * Tuning.TaxRate, 1e-6), "成本为 0 时到账部分全额计税");
var ex = new Exchange();
var big = new Wallet();
big.Add(Assets.CRC, 4000);
ex.Cashout(big, 2000, px, 1, 200, false, new Rng(1));
Check(Near(Exchange.HitChance(2000, 0, ex.History.Sum(h => h.Value), 200, false), 0.2 + 0.2 * 1000 / 3000.0, 1e-9),
"7 天内累计超过 15 倍房租也会提高命中率");
var w = new Wallet { Address = "w", Basis = 800 };
w.AddDirty(Assets.CRC, 1000, 500);
Check(Near(w.CleanAvail(Assets.CRC), 500, 1e-9), "1000 CRC、500 枚脏:能充 500");
var pb = new Wallet { Address = "pb" };
double moved = Wallet.TransferClean(w, pb, Assets.CRC, 800, px);
Check(Near(moved, 500, 1e-9) && Near(w.DirtyOf(Assets.CRC), 500, 1e-9) && Near(w.Get(Assets.CRC), 500, 1e-9) && pb.Taint(px) == 0 && Near(pb.Basis, 400, 1e-9) && Near(w.Basis, 400, 1e-9),
"要充 800 只转了干净的 500:脏的留在钱包(剩 500 CRC 全脏),成本按价值比例带走");
Check(w.CleanAvail(Assets.CRC) == 0, "剩下的全是脏的,一枚也充不进去");
w.Add(Assets.CRC, 300);
Check(Near(w.CleanAvail(Assets.CRC), 300, 1e-9), "转进干净的 300 只能再充 300");
var two = new Wallet(); two.AddDirty(Assets.CRT, 100, 100); two.Add(Assets.PSC, 2);
Check(Near(two.CleanAvail(Assets.PSC), 2, 1e-9) && two.CleanAvail(Assets.CRT) == 0, "按币种分开:CRT 全脏不影响干净的 PSC");
var fz = new Wallet(); fz.AddDirty(Assets.CRT, 1000, 600); fz.Freeze(Assets.CRT, 0.5, 9);
Check(Near(fz.CleanAvail(Assets.CRT), 400, 1e-9), "冻结的不能充:取可动用和干净的较小者");
var acct = new Wallet { Basis = 1000 };
acct.Add(Assets.CRC, 1000);
var r = new Exchange().Cashout(acct, 1000, px);
Check(Near(r.Received, 1000 * (1 - Tuning.ExchangeFee), 1e-6) && r.Tax == 0, "出金全额到账(扣手续费),本金不计税");
var gain = new Wallet { Basis = 600 }; gain.Add(Assets.CRC, 1000);
var g = new Exchange().Cashout(gain, 1000, px);
Check(Near(g.Tax, (1000 * (1 - Tuning.ExchangeFee) - 600) * Tuning.TaxRate, 1e-6), "超出成本的部分计税");
var dep = new Wallet { Basis = 1000 };
dep.Add(Assets.CRC, 1000);
var dr = new Exchange().Cashout(dep, 1000, a => 0.9, 1, 200, false, new Rng(1));
Check(Near(dr.Received, 900 * (1 - Tuning.ExchangeFee), 1e-6), "CRC 脱锚时按市价兑出");
Check(Near(new Exchange().Cashout(dep, 1000, a => 0.9).Received, 900 * (1 - Tuning.ExchangeFee), 1e-6), "CRC 脱锚时按市价兑出");
// 存档迁移:旧的整钱包污点额按现价摊成各币脏枚数;Poinbase 和永续仓位的挪到 1 号钱包一起摊,超出的作废
Func<string, double> px2 = a => a == Assets.PSC ? 100 : 1;
var old = new Wallet { LegacyTaint = 300 }; old.Add(Assets.CRC, 1000);
var pos = new List<PerpPosition> { new PerpPosition { Product = "PSC", LegacyTaint = 200 } };
var w1 = new Wallet { Address = "w1", LegacyTaint = 100 }; w1.Add(Assets.CRC, 400); w1.Add(Assets.PSC, 2); w1.Add(Assets.Ponero, 50);
var w2 = new Wallet { Address = "w2", LegacyTaint = 50 }; w2.Add(Assets.CRT, 100);
var mixer = new Mixer(); mixer.Pending.Add(new MixDeposit { Asset = Assets.PSC, Amount = 3, LegacyTaint = 150 });
var book = new PredictBook(); book.Positions.Add(new PredictPos { Cost = 100, LegacyTaint = 40 });
double was = TaintMigration.Run(new List<Wallet> { w1, w2 }, old, pos, mixer, new BlackMarket(), new Staking(), new Scams(), book, new OwnMixer(), px2);
Check(old.LegacyTaint == 0 && pos[0].LegacyTaint == 0 && w1.LegacyTaint == 0 && Near(w1.DirtyOf(Assets.CRC), 400, 1e-9) && Near(w1.DirtyOf(Assets.PSC), 2, 1e-9) && w1.DirtyOf(Assets.Ponero) == 0,
"1 号钱包:自己 100 + Poinbase 300 + 永续 200 = 600,超过可追踪币的价值 600 封顶,全部记脏;Ponero 不摊");
Check(Near(w2.DirtyOf(Assets.CRT), 50, 1e-9), "2 号钱包:污点 50 / 价值 100 → CRT 一半脏");
Check(Near(mixer.Pending[0].Dirty, 1.5, 1e-9) && Near(book.Positions[0].Dirty, 40, 1e-9) && Near(was, 840, 1e-9), "托管:混币器在途按价格换成脏枚数,预测持仓同理;日志总额 840");
});
Test("二手贩子:老实人不报价;中间商加价;骗子夹带假卡;指出问题按维度扣价、拆穿假货按真实芯片算", () =>
@@ -693,18 +707,16 @@ internal static class Program
var m = Market.CreateDefault(3);
var st = new Stablecoins();
var net = new Network();
var w = new Wallet { Address = "hot", Taint = 12, Basis = 34 };
w.Add(Assets.PSC, 5);
var w = new Wallet { Address = "hot", Basis = 34 };
w.AddDirty(Assets.PSC, 5, 1.5);
var ex = new Exchange();
ex.History.Add(new KeyValuePair<int, double>(2, 100));
var rng = new Rng(77);
for (int d = 1; d <= 5; d++) { m.Night(new MarketEvent[0], rng, d, a => 1); st.Night(d, rng, 0); net.Night(d, m.Price, 10); }
var bundle = new SaveBundle { Market = m, Stable = st, Network = net, Hot = w, Exchange = ex, Rng = rng };
string json = Newtonsoft.Json.JsonConvert.SerializeObject(bundle);
var back = Newtonsoft.Json.JsonConvert.DeserializeObject<SaveBundle>(json);
Check(Near(back.Market.Price, m.Price, 1e-9) && back.Market.Pools.Count == m.Pools.Count && back.Market.Pools[1].Id == m.Pools[1].Id, "交易对与价格");
Check(back.Hot.Get(Assets.PSC) == 5 && back.Hot.Taint == 12 && back.Hot.Basis == 34, "钱包余额、污点、成本");
Check(back.Exchange.History.Count == 1 && back.Exchange.History[0].Value == 100, "出金记录");
Check(back.Hot.Get(Assets.PSC) == 5 && back.Hot.DirtyOf(Assets.PSC) == 1.5 && back.Hot.Basis == 34, "钱包余额、脏枚数、成本");
Check(Near(back.Network.NpcHash, net.NpcHash, 1e-9) && Near(back.Stable.Reserve, st.Reserve, 1e-9), "全网算力、CRA 储备");
for (int d = 6; d <= 10; d++)
{
@@ -720,20 +732,20 @@ internal static class Program
Check(CoinPayment.AssetFor("FACTION_BLACK_MARKET") == Assets.CRT && CoinPayment.AssetFor("FACTION_SECURITY") == null, "派系对应的币");
double q = CoinPayment.Receive(w, Assets.CRT, 200, 0.5, 40);
Check(Near(q, 400 * (1 + Tuning.CoinPayPremium), 1e-9) && Near(w.Get(Assets.CRT), q, 1e-9), "按币价换算数量,含溢价");
Check(Near(w.Taint, 80, 1e-9) && Near(w.Basis, 200, 1e-9), "污点 = 货值 × 热度%,成本 = 货值(溢价不带污点)");
Check(Near(w.DirtyOf(Assets.CRT), 80 / 0.5, 1e-9) && Near(w.Basis, 200, 1e-9), "脏的 = 货值 × 热度% 那部分的币(160 枚),成本 = 货值(溢价干净)");
Check(CoinPayment.Receive(w, Assets.CRC, 0, 1, 10) == 0, "零金额不收");
});
Test("混币器:按价值比例带走污点、轮数即天数、手续费与清洗、匿名集、制裁标记", () =>
Test("混币器:按这种币的脏比例带走脏枚数、轮数即天数、手续费与清洗、匿名集、制裁期间整笔脏", () =>
{
Func<string, double> px = a => a == Assets.PSC ? 10 : 1;
var w = new Wallet { Address = "t" };
w.Add(Assets.CRC, 1000); w.Add(Assets.PSC, 100); // 总价值 2000
w.Taint = 400; w.Basis = 1000;
w.AddDirty(Assets.CRC, 1000, 200); w.AddDirty(Assets.PSC, 100, 20); // 总价值 2000,各 20% 脏
w.Basis = 1000;
var m = new Mixer();
Check(m.Deposit(w, Assets.CRC, 1000, 9, 1, px) == null && m.Deposit(w, Assets.CRC, 50, 1, 1, px) == null, "轮数超限、低于最小额不收");
var d = m.Deposit(w, Assets.CRC, 1000, 2, 1, px);
Check(d != null && Near(d.Taint, 200, 1e-9) && Near(w.Taint, 200, 1e-9) && Near(w.Get(Assets.CRC), 0, 1e-9), "存入 1000 / 2000:带走一半污点");
Check(d != null && Near(d.Dirty, 200, 1e-9) && w.DirtyOf(Assets.CRC) == 0 && Near(w.DirtyOf(Assets.PSC), 20, 1e-9) && Near(w.Get(Assets.CRC), 0, 1e-9), "存入 1000 CRC:带走 200 枚脏的,PSC 不受影响");
var rng = new Rng(3);
Check(m.Night(w, 2, px, rng).Count == 0, "未到期不提回");
var res = m.Night(w, 3, px, rng);
@@ -741,15 +753,14 @@ internal static class Program
double rate = Mixer.RoundRate(1000);
Check(Near(w.Get(Assets.CRC), 1000 * (1 - Tuning.MixerFee) * (1 - Tuning.MixerFee), 1e-6) && Near(res[0].TaintAfter, 200 * (1 - rate) * (1 - rate), 1e-6), "两轮:手续费与污点按轮数递减");
Check(Mixer.RoundRate(100000) < Mixer.RoundRate(1000) / 3, "存入越大匿名集占比越大,清洗越差");
Check(m.RecentlyMixed(3) && !m.RecentlyMixed(3 + Tuning.AmlWindowDays + 1), "提回后一段时间内出金按混币来源加重");
m.OnEvent(MarketEvent.Crackdown, 5, new Rng(1));
int hit = 0; for (int i = 0; i < 400; i++) { var t = new Mixer(); t.OnEvent(MarketEvent.Crackdown, 1, new Rng((ulong)i + 1)); if (t.Sanctioned(2)) hit++; }
Check(hit > 150 && hit < 250, $"镇压时约一半概率被制裁:{hit}/400");
var w2 = new Wallet { Address = "u" }; w2.Add(Assets.CRC, 500);
var m2 = new Mixer { SanctionedUntil = 10 };
m2.Deposit(w2, Assets.CRC, 500, 1, 1, px);
m2.Night(w2, 2, px, new Rng(5));
Check(w2.Flagged, "制裁期间提出的币被标记");
var fr = m2.Night(w2, 2, px, new Rng(5)).Single();
Check(fr.Flagged && Near(w2.DirtyOf(Assets.CRC), w2.Get(Assets.CRC), 1e-6), $"制裁期间提出:不洗,整笔都是脏的({w2.DirtyOf(Assets.CRC):0} / {w2.Get(Assets.CRC):0})");
});
Test("CRT 冻结:冻一半、可动用余额减少、到期解冻;声望越高越不容易被冻", () =>
@@ -774,15 +785,15 @@ internal static class Program
Check(bm.Refresh(10, 45, new Rng(1), id => 100) && bm.Offers.Count > 0 && bm.Offers.Where(o => o.Kind == GoodKind.Contraband).All(o => o.Level <= 2 && o.Price == 130), "声望 45 开放中档,溢价 30%");
Check(!bm.Refresh(12, 45, new Rng(2), id => 100) && bm.Refresh(17, 45, new Rng(2), id => 100), "每周刷新");
var w = new Wallet { Address = "t" };
w.Add(Assets.PSC, 10); w.Add(Assets.CRT, 1000); w.Taint = 400; w.Basis = 800; // 总价值 2000
w.Add(Assets.PSC, 10); w.AddDirty(Assets.CRT, 1000, 400); w.Basis = 800; // 总价值 2000
var offer = new BmOffer { Item = "x", Qty = 1, Price = 500, Level = 1 };
bm.Offers.Add(offer);
Check(!bm.Buy(offer, w, Assets.CRC, price), "不收 CRC");
Check(bm.Buy(offer, w, Assets.CRT, price) && Near(w.Get(Assets.CRT), 500, 1e-9) && Near(w.Taint, 300, 1e-6) && Near(w.Basis, 600, 1e-6) && !bm.Offers.Contains(offer), "付币:污点与成本按比例留给卖家,卖完下架");
Check(bm.Buy(offer, w, Assets.CRT, price) && Near(w.Get(Assets.CRT), 500, 1e-9) && Near(w.DirtyOf(Assets.CRT), 200, 1e-6) && Near(w.Basis, 600, 1e-6) && !bm.Offers.Contains(offer), "付币:付出的 CRT 按 40% 带走脏的,成本按比例留给卖家,卖完下架");
w = new Wallet { Address = "t" };
w.Add(Assets.PSC, 10); w.Taint = 500; w.Basis = 1000; // 总价值 1000,PSC 价 100
w.AddDirty(Assets.PSC, 10, 5); w.Basis = 1000; // 总价值 1000,PSC 价 100,一半脏
double got = bm.ToPonero(w, Assets.PSC, 5, 20, price); // 价值 500
Check(Near(got, 500 * (1 - Tuning.PoneroFee) / 50, 1e-9) && Near(w.Taint, 250, 1e-6) && Near(w.Get(Assets.PSC), 5, 1e-9), "兑入 Ponero:扣手续费,这部分污点留在原地");
Check(Near(got, 500 * (1 - Tuning.PoneroFee) / 50, 1e-9) && Near(w.DirtyOf(Assets.PSC), 2.5, 1e-6) && w.DirtyOf(Assets.Ponero) == 0 && Near(w.Get(Assets.PSC), 5, 1e-9), "兑入 Ponero:扣手续费,兑掉的脏币消失,Ponero 干净");
Check(bm.ToPonero(w, Assets.PSC, 5.1, 20, price) < 0, "超过每日额度不能兑");
Check(bm.ToPonero(w, Assets.PSC, 5, 21, price) >= 0, "第二天额度恢复");
double back = bm.FromPonero(w, Assets.CRT, 10, 22, price);
@@ -803,12 +814,12 @@ internal static class Program
var lg = new BmListing { Qty = 99, Seller = 0, Item = "x", Price = 1000 };
var lb = new BmListing { Qty = 99, Seller = 1, Item = "x", Price = 1000 };
var w = new Wallet { Address = "t" };
w.Add(Assets.CRT, 10000); w.Taint = 4000; w.Basis = 5000;
w.AddDirty(Assets.CRT, 10000, 4000); w.Basis = 5000;
var o = bm.Place(lb, 1, PayMode.Escrow, w, Assets.CRT, 10, price);
Check(o != null && Near(w.Get(Assets.CRT), 10000 - 1050, 1e-9) && w.Taint < 4000 && bm.Place(lg, 1, PayMode.Escrow, w, Assets.CRC, 10, price) == null, "托管下单:扣币、污点留在订单上;不收 CRC");
Check(o != null && Near(w.Get(Assets.CRT), 10000 - 1050, 1e-9) && Near(o.Dirty, 420, 1e-6) && w.DirtyOf(Assets.CRT) < 4000 && bm.Place(lg, 1, PayMode.Escrow, w, Assets.CRC, 10, price) == null, "托管下单:扣币、脏的 420 枚留在订单上;不收 CRC");
double tBefore = 4000;
var ev = bm.Night(13, false, w, new Rng(1));
Check(ev.Any(e => e.Kind == "scam_refund") && Near(w.Get(Assets.CRT), 10000, 1e-6) && Near(w.Taint, tBefore, 1e-6) && Near(w.Basis, 5000, 1e-6), "骗子卖家 + 托管:退款,污点与成本一并还回(不能借退款洗钱)");
Check(ev.Any(e => e.Kind == "scam_refund") && Near(w.Get(Assets.CRT), 10000, 1e-6) && Near(w.DirtyOf(Assets.CRT), tBefore, 1e-6) && Near(w.Basis, 5000, 1e-6), "骗子卖家 + 托管:退款,脏枚数与成本一并还回(不能借退款洗钱)");
var o2 = bm.Place(lb, 1, PayMode.Advance, w, Assets.CRT, 20, price);
var ev2 = bm.Night(23, false, w, new Rng(2));
Check(o2.State == OrderState.Lost && Near(w.Get(Assets.CRT), 10000 - 900, 1e-6), "骗子卖家 + 提前放款:钱没了");
@@ -890,7 +901,7 @@ internal static class Program
b2.Night(4, false, w2, new Rng(8), 0, price);
Check(s2.State == SaleState.Sold, "没到放款日不动");
b2.Night(5, false, w2, new Rng(9), 0, price);
Check(s2.State == SaleState.Paid && Near(w2.Get(Assets.CRT), crt0 + s2.Proceeds, 1e-9) && Near(w2.Taint, s2.Taint, 1e-9) && (s2.Taint == 0 || Near(s2.Taint, s2.Proceeds * BlackMarket.TaintRatio(0.8), 1e-6)), "放款:货款进钱包,污点按比例");
Check(s2.State == SaleState.Paid && Near(w2.Get(Assets.CRT), crt0 + s2.Proceeds, 1e-9) && Near(w2.DirtyOf(Assets.CRT), s2.Taint, 1e-9) && (s2.Taint == 0 || Near(s2.Taint, s2.Proceeds * BlackMarket.TaintRatio(0.8), 1e-6)), "放款:货款进钱包,按污点比例记脏的(CRT 价 1)");
// 赃物:货款按热度带污点,不走随机
var bh = new BlackMarket();
var wh = new Wallet(); wh.Add(Assets.CRT, 1000);
@@ -1097,7 +1108,7 @@ internal static class Program
Test("永续合约:开平仓、强平、基金与欠款、自动减仓、没收", () =>
{
Func<string, double> pr = a => a == Assets.PSC ? 100 : 1;
Wallet NewAcct(double crc, double taint = 0) { var w = new Wallet { Address = "pb" }; w.Add(Assets.CRC, crc); w.Taint = taint; w.Basis = crc; return w; }
Wallet NewAcct(double crc) { var w = new Wallet { Address = "pb" }; w.Add(Assets.CRC, crc); w.Basis = crc; return w; }
// 滑点:名义价值占永续深度的比例,买入往上、卖出往下
var bs = new PerpBook();
double pool = Tuning.PerpDepth(1);
@@ -1105,25 +1116,25 @@ internal static class Program
// 滑点吃掉了强平线之间的空间:开仓被拒
var bt = new PerpBook { DepthMult = 0.05 }; var at = NewAcct(5000);
Check(bt.Open(at, "PSC", true, 1000, 5, 100, 1, pr) == PerpResult.Thin && bt.Positions.Count == 0 && at.Get(Assets.CRC) == 5000, "滑点大到一开就被强平的单子被拒,不扣钱");
// 开仓与手续费、保证金、污点
var b = new PerpBook { DepthMult = 1e12 }; var a = NewAcct(2000, 1000);
// 开仓与手续费、保证金(Poinbase 没有污点)
var b = new PerpBook { DepthMult = 1e12 }; var a = NewAcct(2000);
Check(b.Open(a, "PSC", true, 1000, 6, 100, 1, pr) == PerpResult.Leverage && b.Open(a, "PSC", true, 3000, 3, 100, 1, pr) == PerpResult.Funds, "杠杆超限、余额不足被拒");
Check(b.Open(a, "PSC", true, 1000, 5, 100, 1, pr) == PerpResult.Ok, "开 5× 多头");
var pos = b.Of("PSC");
double fee = 5000 * Tuning.PerpFee;
Check(Math.Abs(pos.Size - 50) < 1e-9 && Math.Abs(a.Get(Assets.CRC) - (1000 - fee)) < 1e-9 && Math.Abs(pos.Taint - 500) < 1e-9, $"名义 5000,手续费 {fee},污点 500 随保证金进仓位");
Check(Math.Abs(pos.Size - 50) < 1e-9 && Math.Abs(a.Get(Assets.CRC) - (1000 - fee)) < 1e-9 && Math.Abs(pos.Basis - 1000) < 1e-9, $"名义 5000,手续费 {fee},成本 1000 随保证金进仓位");
Check(b.Open(a, "PSC", false, 100, 2, 100, 1, pr) == PerpResult.Opposite, "不能同时多空");
Check(b.Open(a, "PSC", true, 100, 2, 100, 1, pr) == PerpResult.Single && b.Open(a, "SPARK", true, 100, 1, 10, 1, pr) == PerpResult.Single && b.Positions.Count == 1, "只有一个仓位:加仓、开别的标的都被拒,只能追加保证金");
Check(Math.Abs(pos.LiqPrice - (50 * 100 - 1000) / (50 * (1 - Tuning.PerpMaint))) < 1e-9 && pos.LiqPrice > 85 && pos.LiqPrice < 90, $"强平价 {pos.LiqPrice:0.0}(约 −11%)");
// 平仓盈利:干净的新币,污点原额退回
// 平仓盈利:成本原额退回
double paid = b.Close(a, "PSC", 110, 2, out double d0);
double expect = 1000 + 50 * 10 - 50 * 110 * Tuning.PerpFee;
Check(Math.Abs(paid - expect) < 1e-6 && d0 == 0 && Math.Abs(a.Taint - 1000) < 1e-6 && b.Positions.Count == 0, $"盈利平仓 {paid:0.00},污点全额退回");
// 平仓亏损:污点按剩下的比例退回
var a2 = NewAcct(2000, 1000); var b2 = new PerpBook { DepthMult = 1e12 };
Check(Math.Abs(paid - expect) < 1e-6 && d0 == 0 && Math.Abs(a.Basis - 2000) < 1e-6 && b.Positions.Count == 0, $"盈利平仓 {paid:0.00},成本全额退回");
// 平仓亏损:成本按剩下的比例退回
var a2 = NewAcct(2000); var b2 = new PerpBook { DepthMult = 1e12 };
b2.Open(a2, "PSC", true, 1000, 5, 100, 1, pr);
double paid2 = b2.Close(a2, "PSC", 95, 2, out _);
Check(paid2 > 700 && paid2 < 760 && a2.Taint < 1000 - 500 * (1 - paid2 / 1000) + 1, $"亏损平仓 {paid2:0.0},污点按比例少回");
Check(paid2 > 700 && paid2 < 760 && a2.Basis < 2000 - 1000 * (1 - paid2 / 1000) + 1, $"亏损平仓 {paid2:0.0},成本按比例少回");
// 夜间强平:权益不到 10% 名义价值;剩余进基金
var b3 = new PerpBook { DepthMult = 1e12 }; var a3 = NewAcct(5000);
b3.Open(a3, "PSC", true, 1000, 5, 100, 1, pr);
@@ -1178,15 +1189,15 @@ internal static class Program
Check(!b8.Night(3, new Dictionary<string, double> { ["PSC"] = 87 }, a8, pr).Any(e => e.Kind == "wipe"), "小行情不上新闻");
});
Test("CRA 质押池:计息、解锁期、污点随币、螺旋", () =>
Test("CRA 质押池:计息、解锁期、脏枚数随币、螺旋", () =>
{
Func<string, double> price = a => a == Assets.PSC ? 100 : 1;
var w = new Wallet { Address = "0xa" }; w.Add(Assets.CRA, 1000); w.Add(Assets.PSC, 10); w.Taint = 400; w.Basis = 600;
var w = new Wallet { Address = "0xa" }; w.AddDirty(Assets.CRA, 1000, 400); w.Add(Assets.PSC, 10); w.Basis = 600;
var st = new Staking();
Check(!st.Stake(w, 2000, price), "余额不足不能质押");
Check(st.Stake(w, 500, price) && Math.Abs(w.Get(Assets.CRA) - 500) < 1e-9, "质押 500,钱包剩 500");
double lotTaint = st.Of("0xa").Taint;
Check(Math.Abs(lotTaint - 400 * 500.0 / 2000) < 1e-6 && Math.Abs(w.Taint - (400 - lotTaint)) < 1e-6, $"污点按价值比例进仓位:{lotTaint}");
double lotTaint = st.Of("0xa").Dirty;
Check(Math.Abs(lotTaint - 200) < 1e-6 && Math.Abs(w.DirtyOf(Assets.CRA) - 200) < 1e-6, $"CRA 40% 脏,质押 500 带走 200 枚脏的:{lotTaint}");
var e1 = st.Night(1, Tuning.CraRate, _ => w);
Check(Math.Abs(st.Of("0xa").Amount - 500 * (1 + Tuning.CraRate)) < 1e-9 && e1.Count == 1, "每晚计息并入本金");
Check(!st.RequestUnstake("0xa", 9999, 1) && st.RequestUnstake("0xa", 200, 1), "申请取出 200");
@@ -1196,7 +1207,7 @@ internal static class Program
Check(w.Get(Assets.CRA) < 501, "解锁期没到不能取");
for (int d = 3; d <= 1 + Tuning.CraUnlockDays; d++) st.Night(d, Tuning.CraRate, _ => w);
Check(Math.Abs(w.Get(Assets.CRA) - 700) < 1e-6, $"到期自动回钱包 200:{w.Get(Assets.CRA):0.##}");
Check(w.Taint > 400 - lotTaint + 1, "污点按比例回来");
Check(w.DirtyOf(Assets.CRA) > 200 + 1 && w.DirtyOf(Assets.CRA) < 200 + 80 + 1e-6, $"脏枚数按比例回来(利息干净):{w.DirtyOf(Assets.CRA):0.##}");
// 螺旋:质押的 CRA 取回后只值当时的价格
var s = new Stablecoins(); s.CraCrashDay = 5;
for (int d = 5; d <= 7; d++) s.Night(d, new Rng(1), 0);
@@ -1258,19 +1269,9 @@ internal static class Program
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" };
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.AddDirty(Assets.CRT, 1000, 300); w.Basis = 900; w.Freeze(Assets.CRT, 0.5, 9);
double v = w.Drain(a => a == Assets.PSC ? 100 : 1);
Check(Math.Abs(v - 6000) < 1e-6 && w.Balances.Count == 0 && w.Frozen.Count == 0 && w.Taint == 0 && w.Basis == 0, $"全部转走(含冻结),污点和成本清零:{v}");
});
Test("被标记的钱包转出可追踪的币,收款方也被标记;Ponero 不会", () =>
{
Func<string, double> price = x => 1;
var f = new Wallet { Address = "f", Flagged = true }; f.Add(Assets.CRC, 100); f.Add(Assets.Ponero, 100);
var p1 = new Wallet { Address = "p1" }; var p2 = new Wallet { Address = "p2" };
Wallet.Transfer(f, p1, Assets.CRC, 10, price);
Wallet.Transfer(f, p2, Assets.Ponero, 10, price);
Check(p1.Flagged && !p2.Flagged, "CRC 带标记,Ponero 不带");
Check(Math.Abs(v - 6000) < 1e-6 && w.Balances.Count == 0 && w.Frozen.Count == 0 && w.Dirty.Count == 0 && w.Basis == 0, $"全部转走(含冻结),脏枚数和成本清零:{v}");
});
Test("多钱包:退款、出售回款、混币提回都回到原来的钱包", () =>
@@ -1354,32 +1355,114 @@ internal static class Program
Check(ps > 12 && ps < 20, $"夜里向 SPARK 参考价靠拢:{ps:0.00}");
});
Test("收黑币:事件对应币种、报价、收下后的污点", () =>
Test("换币客户:卖币的事件币种、污点比例与折扣、收下后的污点;买币的随机出价;部分成交;成交统计", () =>
{
Func<string, double> pr = a => a == Assets.Ponero ? 50 : 1;
var none = new List<MarketEvent>();
var rng = new Rng(3);
int crt = 0, psc = 0, pon = 0;
int ev = 0, sides = 0;
for (int i = 0; i < 4000; i++)
{
string a1 = HotCoins.Pick(new[] { MarketEvent.GangWar }, false, rng);
if (a1 == Assets.CRT) crt++; else if (a1 != null) psc++;
if (HotCoins.Pick(new MarketEvent[0], true, rng) == Assets.Ponero) pon++;
var o = Otc.MakeSell(new List<MarketEvent> { MarketEvent.GangWar }, false, pr, 500, rng);
if (o.Asset == Assets.CRT && o.TaintRatio == 1) ev++;
if (Otc.PickSide(rng) == OtcSide.Sell) sides++;
}
Check(psc == 0 && Math.Abs(crt / 4000.0 - Tuning.HotEventChance) < 0.03, $"帮派火并期间只有 CRT:{crt}/4000");
Check(Math.Abs(pon / 4000.0 - Tuning.HotPoneroChance) < 0.015, $"兑换处开放后的 Ponero:{pon}/4000");
Check(HotCoins.Pick(new[] { MarketEvent.RevRaidUpper }, false, new Rng(1)) is string sa ? sa == Assets.PSC : true, "革命军袭击上层是 PSC");
Check(HotCoins.Pick(new MarketEvent[0], false, rng) == null, "没有事件、兑换处没开:没人上门");
var o = HotCoins.Make(Assets.CRT, 1, 500, false, new Rng(9));
Check(o.Value >= 500 * Tuning.HotValueMinRents && o.Value <= 500 * Tuning.HotValueMaxRents && o.Cash >= o.Value * Tuning.HotCutMin - 1 && o.Cash <= o.Value * Tuning.HotCutMax + 1, $"报价:市值 {o.Value:0},现金 {o.Cash}");
Check(Math.Abs(ev / 4000.0 - Tuning.OtcEventChance) < 0.03, $"帮派火并期间多是 CRT、全额污点:{ev}/4000");
Check(Math.Abs(sides / 4000.0 - Tuning.OtcSellShare) < 0.03, $"卖币、买币各占一半:{sides}/4000");
Check(Otc.EventAsset(new List<MarketEvent> { MarketEvent.RevRaidUpper }, new Rng(1)) == Assets.PSC && Otc.EventAsset(none, new Rng(1)) == null, "革命军袭击上层是 PSC;没有事件时没有事件币");
// 平时:PSC/CRT/CRA 随机,污点比例在区间内,越脏越便宜
for (int i = 0; i < 200; i++)
{
var o = Otc.MakeSell(none, false, pr, 500, rng);
Check(Array.IndexOf(Otc.SellAssets, o.Asset) >= 0 && o.TaintRatio >= Tuning.OtcTaintMin && o.TaintRatio <= Tuning.OtcTaintMax, "平时的币种与污点比例");
Check(o.Value >= 500 * Tuning.OtcValueMinRents && o.Value <= 500 * Tuning.OtcValueMaxRents && Math.Abs(o.Cash - o.Value * Otc.CutFor(o.TaintRatio)) <= 1, $"市值与现金价:{o.Value:0} / {o.Cash}");
}
Check(Otc.CutFor(0) == Tuning.OtcCutClean && Otc.CutFor(1) == Tuning.OtcCutDirty && Otc.CutFor(0.3) > Otc.CutFor(0.7), "折扣按污点比例线性降低");
// 收下:币进钱包,其中 数量 × 污点比例 枚是脏的,成本是付的现金
var oc = new OtcOffer { Side = OtcSide.Sell, Asset = Assets.CRT, Qty = 1000, Value = 1000, TaintRatio = 0.4, Cash = 700 };
var w = new Wallet();
HotCoins.Receive(w, o);
Check(Near(w.Get(Assets.CRT), o.Qty, 1e-9) && Near(w.Taint, o.Value, 1e-9) && Near(w.Basis, o.Cash, 1e-9), "CRT 全额带污点,成本是付的现金");
var op = HotCoins.Make(Assets.Ponero, 50, 500, false, new Rng(9));
var wp = new Wallet();
HotCoins.Receive(wp, op);
Check(wp.Taint == 0 && op.Cash >= op.Value * Tuning.HotPoneroCutMin - 1, "Ponero 不带污点、折扣小");
Otc.Receive(w, oc);
Check(Near(w.Get(Assets.CRT), 1000, 1e-9) && Near(w.DirtyOf(Assets.CRT), 400, 1e-9) && Near(w.Basis, 700, 1e-9), "1000 CRT 里 400 枚脏,成本是付的现金");
// Ponero:兑换处开放后偶尔有,不带污点
int pon = 0;
for (int i = 0; i < 4000; i++) { var o = Otc.MakeSell(none, true, pr, 500, rng); if (o.Asset == Assets.Ponero) { pon++; Check(o.TaintRatio == 0 && o.Cash >= o.Value * Tuning.OtcPoneroCutMin - 1, "Ponero 不带污点"); } }
Check(Math.Abs(pon / 4000.0 - Tuning.OtcPoneroChance) < 0.02, $"兑换处开放后的 Ponero:{pon}/4000");
int stings = 0, stingsCd = 0;
for (int i = 0; i < 4000; i++) { if (HotCoins.Make(Assets.CRT, 1, 500, false, rng).Sting) stings++; if (HotCoins.Make(Assets.CRT, 1, 500, true, rng).Sting) stingsCd++; }
Check(stingsCd > stings * 2, $"镇压期间钓鱼执法更多:{stings} → {stingsCd}");
for (int i = 0; i < 4000; i++)
{
if (Otc.MakeSell(none, false, pr, 500, rng).Sting) stings++;
if (Otc.MakeSell(new List<MarketEvent> { MarketEvent.Crackdown }, false, pr, 500, rng).Sting) stingsCd++;
}
Check(stingsCd > stings * 2, $"镇压期间便衣更多:{stings} → {stingsCd}");
// 买币:市值按房租,出价 = 市值 × (1 + 随机溢价),溢价在区间内
for (int i = 0; i < 200; i++)
{
var ob = Otc.MakeBuy(pr, 500, rng);
Check(Array.IndexOf(Otc.BuyAssets, ob.Asset) >= 0 && ob.Value >= 500 * Tuning.OtcBuyMinRents && ob.Value <= 500 * Tuning.OtcBuyMaxRents && Near(ob.Qty * pr(ob.Asset), ob.Value, 1e-6), "买币的币种与数量");
Check(ob.Cash >= ob.Value * (1 + Tuning.OtcBidMin) - 1 && ob.Cash <= ob.Value * (1 + Tuning.OtcBidMax) + 1, $"出价在区间内:{ob.Cash} / {ob.Value:0}");
}
// 部分成交:现金、市值按数量比例折算,超过报价按全部算
var whole = new OtcOffer { Side = OtcSide.Sell, Asset = Assets.PSC, Qty = 20, Value = 2000, TaintRatio = 0.5, Cash = 1200, Sting = true };
var half = Otc.Part(whole, 10);
Check(Near(half.Qty, 10, 1e-9) && Near(half.Value, 1000, 1e-9) && half.Cash == 600 && half.TaintRatio == 0.5 && half.Sting, "收一半:现金、市值减半,污点比例和便衣不变");
Check(Near(Otc.Part(whole, 50).Qty, 20, 1e-9) && Otc.Part(whole, 50).Cash == 1200, "超过报价的数量按全部算");
Check(Otc.Part(whole, 0) == null && Otc.Part(whole, -1) == null, "数量不合法");
// 来历:脏的来源按污点比例出现,数量占比合计为 1
var hist = Otc.History(new OtcOffer { TaintRatio = 1 }, new[] { "dirty" }, new[] { "clean" }, new Rng(2));
Check(hist.Count >= 1 && hist.Count <= Tuning.OtcHistoryMax && hist.TrueForAll(kv => kv.Key == "dirty") && Near(hist.Sum(kv => kv.Value), 1, 1e-9), "全脏的币来历都是脏来源");
var histC = Otc.History(new OtcOffer { TaintRatio = 0 }, new[] { "dirty" }, new[] { "clean" }, new Rng(2));
Check(histC.TrueForAll(kv => kv.Key == "clean"), "干净的币来历都是干净来源");
var desk = new OtcDesk(); desk.Record(700); desk.Record(300);
Check(desk.Volume == 1000 && desk.Deals == 2, "成交统计");
});
Test("自营混币器:注资取回的污点、匿名集与清洗率、客流、出款、查封、卷款跑路", () =>
{
Func<string, double> pr = a => 1;
var w = new Wallet { Address = "w" }; w.Add(Assets.CRT, 30000);
var m = new OwnMixer();
Check(m.Inject(w, Assets.CRT, 20000, pr) && m.Pool(Assets.CRT).Capital == 20000 && m.Pool(Assets.CRT).CapDirty == 0, "干净的币注资,本金不带污点");
Check(!m.Inject(w, Assets.CRT, 1e6, pr) && !m.Inject(w, Assets.SPARK, 1, pr), "余额不够、币种不支持被拒");
Check(OwnMixer.RoundRate(40000) > OwnMixer.RoundRate(10000) && OwnMixer.RoundRate(1e9) <= Tuning.MixerCleanRate + 1e-9 && OwnMixer.RoundRate(0) == 0, "匿名集越大清洗率越高,封顶基准清洗率");
m.Start(pr);
Check(m.Open && Near(m.Rating, OwnMixer.Remain(OwnMixer.RoundRate(20000)), 1e-9), "开张时口碑按匿名集估");
double rr = 0.29;
var cheap = new OwnMixer { Fee = 0.01, Rating = 0.3 }; var dear = new OwnMixer { Fee = 0.08, Rating = 0.3 }; var dirty = new OwnMixer { Fee = 0.01, Rating = 0.9 };
Check(cheap.Share(0.03, rr, true) > dear.Share(0.03, rr, true) && cheap.Share(0.03, rr, true) > dirty.Share(0.03, rr, true), "费率低、出款干净的分到的客流多");
Check(cheap.Share(0.03, rr, false) > cheap.Share(0.03, rr, true), "对手关停时客流更多");
// 营业几晚:有存入、有手续费,到期的出款
var rng = new Rng(4);
double fees0 = m.FeesTotal;
int deposits = 0, payouts = 0;
for (int day = 40; day < 50; day++)
foreach (var e in m.Night(day, pr, 60, Tuning.MixerFee, rr, true, 1, false, rng))
{
if (e.Kind == "deposit") deposits++;
if (e.Kind == "payout") payouts++;
if (e.Kind == "seized") Check(m.Pools.Count == 0 && m.Pending.Count == 0, "查封清空池子");
}
Check(deposits > 0 && payouts > 0 && m.FeesTotal > fees0, $"营业中有存入和出款:{deposits} / {payouts},手续费 {m.FeesTotal - fees0:0}");
Check(m.Pending.All(x => x.ReadyDay >= 50), "到期的都出款了");
// 取回:收入整笔是脏的,本金按本金的脏比例
var m2 = new OwnMixer(); var w2 = new Wallet { Address = "w2" }; w2.AddDirty(Assets.PSC, 1000, 500);
m2.Inject(w2, Assets.PSC, 1000, pr);
Check(Near(m2.Pool(Assets.PSC).CapDirty, 500, 1e-9) && w2.DirtyOf(Assets.PSC) < 1e-9, "一半脏的币注资,脏枚数跟着进本金");
m2.Pool(Assets.PSC).Earned = 100;
var w3 = new Wallet { Address = "w3" }; w3.Add(Assets.CRC, 10000);
Check(m2.Withdraw(w3, Assets.PSC, 100) && Near(w3.DirtyOf(Assets.PSC), 100, 1e-9) && w3.DirtyOf(Assets.CRC) == 0, "先取收入:整笔脏,别的币不受影响");
Check(m2.Withdraw(w3, Assets.PSC, 500) && Near(w3.DirtyOf(Assets.PSC), 100 + 250, 1e-9), "再取本金:按本金的脏比例");
Check(!m2.Withdraw(w3, Assets.PSC, 1000), "不能超过可取回的数量");
m2.Pending.Add(new OwnMixDeposit { Asset = Assets.PSC, Amount = 300, Taint = 200, Rounds = 2, ReadyDay = 99 });
Check(Near(m2.Free(Assets.PSC), 500, 1e-9) && Near(m2.Total(Assets.PSC), 800, 1e-9), "客户在途的币不能取");
// 卷款跑路:连同客户的币全部卷走、整笔脏,永久关闭
var w4 = new Wallet { Address = "w4" };
var got = m2.Rug(w4);
Check(Near(w4.Get(Assets.PSC), 800, 1e-9) && Near(w4.DirtyOf(Assets.PSC), 800, 1e-9) && m2.Closed && !m2.Open && got.Count == 1, "卷走全部、整笔脏、关闭");
Check(m2.Night(100, pr, 60, Tuning.MixerFee, rr, true, 1, false, new Rng(1)).Count == 0, "关闭后不再有夜间事件");
// 查封:风声足够大时被查封,停业若干天
var m3 = new OwnMixer { Heat = 1e6 }; m3.Inject(new Wallet { Address = "x", Balances = { [Assets.CRT] = 5000 } }, Assets.CRT, 5000, pr); m3.Start(pr);
var ev = m3.Night(60, pr, 60, Tuning.MixerFee, rr, true, 1, false, new Rng(2));
Check(ev.Any(e => e.Kind == "seized" && e.Crime > 0 && e.Crime <= Tuning.CrimeEventCap) && m3.Seized(60 + Tuning.OwnMixSeizeDays) && !m3.Seized(61 + Tuning.OwnMixSeizeDays) && !m3.Open, "风声大时被查封、记罪证、停业");
});
Test("预测市场:LMSR 定价、最大亏损、买卖", () =>
@@ -1409,11 +1492,11 @@ internal static class Program
}
});
Test("预测市场:买入、卖出、手续费、污点、持仓限制", () =>
Test("预测市场:买入、卖出、手续费、脏枚数、持仓限制", () =>
{
Func<string, double> pr = a => 1;
var acct = new Wallet { Address = "poinbase" };
acct.Add(Assets.CRC, 1000); acct.Taint = 400; acct.Basis = 1000;
var acct = new Wallet { Address = "w" };
acct.AddDirty(Assets.CRC, 1000, 400); acct.Basis = 1000;
var bk = new PredictBook();
var m = PredictMarket.Create(++bk.Seq, PredictKind.Purity, "gutter", "t", 8500, 5, 6, 6, 0.5);
bk.Markets.Add(m);
@@ -1425,20 +1508,20 @@ internal static class Program
Check(res == PredictResult.Ok && sh > 0, "买入成功");
var pos = bk.PosOf(m.Id, acct.Address);
Check(Math.Abs(pos.Cost - 100) < 1e-6 && Math.Abs(acct.Get(Assets.CRC) - (1000 - spend)) < 1e-6, $"成本 100(另付手续费):{pos.Cost:0.00}");
Check(Math.Abs(pos.Taint - 400 * spend / 1000) < 1e-6 && Math.Abs(acct.Taint - (400 - 400 * spend / 1000)) < 1e-6, $"污点按花掉的价值比例带进持仓:{pos.Taint:0.00}");
Check(Math.Abs(pos.Dirty - 0.4 * spend) < 1e-6 && Math.Abs(acct.DirtyOf(Assets.CRC) - (400 - 0.4 * spend)) < 1e-6, $"CRC 40% 脏,花掉的按比例带进持仓:{pos.Dirty:0.00}");
Check(bk.Buy(acct, m, false, 10, 5, pr, false, out _) == PredictResult.Opposite, "不能同盘反方向");
// 卖出:价差为 0,只付手续费
double before = acct.Get(Assets.CRC);
Check(bk.SellAll(acct, m, 5, out double got) == PredictResult.Ok && Math.Abs(got - 100 * (1 - Tuning.PredictFee)) < 1e-6, $"原价卖回只扣手续费:{got:0.00}");
Check(bk.SellAll(acct, m, 5, pr, out double got) == PredictResult.Ok && Math.Abs(got - 100 * (1 - Tuning.PredictFee)) < 1e-6, $"原价卖回只扣手续费:{got:0.00}");
Check(Math.Abs(acct.Get(Assets.CRC) - before - got) < 1e-6 && bk.PosOf(m.Id, acct.Address) == null, "回到账户");
Check(acct.Taint < 400 && acct.Taint > 359.2, "卖回时污点按退回的比例回来");
Check(acct.DirtyOf(Assets.CRC) < 400 && acct.DirtyOf(Assets.CRC) > 359.2, "卖回时脏枚数按退回的比例回来");
// 持仓上限
var big = new Wallet { Address = "x" }; big.Add(Assets.CRC, 1e7);
Check(bk.Buy(big, m, true, 1e6, 5, pr, false, out _) == PredictResult.Cap, "超过份额上限被拒");
Check(bk.SellAll(acct, m, 5, out _) == PredictResult.NoPosition, "没持仓不能卖");
Check(bk.SellAll(acct, m, 5, pr, out _) == PredictResult.NoPosition, "没持仓不能卖");
});
Test("预测市场:结算、作废、内幕被查、观测", () =>
Test("预测市场:结算、作废、内幕记污点、观测", () =>
{
Func<string, double> pr = a => 1;
var rng = new Rng(5);
@@ -1446,7 +1529,7 @@ internal static class Program
acct.Add(Assets.CRC, 100000);
var bk = new PredictBook();
// 观测:第一晚没有 Prev,不算 Started
bk.Night(4, new[] { "oxymoreWave" }, m => null, _ => acct, rng);
bk.Night(4, new[] { "oxymoreWave" }, m => null, _ => acct, pr, rng);
Check(!bk.Started.ContainsKey(4), "第一晚没有对照,不记新出现的事件");
var win = PredictMarket.Create(++bk.Seq, PredictKind.Event, "powerShortage", "停电", 0, 4, 5, 6, 0.2);
var lose = PredictMarket.Create(++bk.Seq, PredictKind.Event, "blackout", "x", 0, 4, 5, 6, 0.2);
@@ -1457,9 +1540,9 @@ internal static class Program
bk.Buy(acct, lose, true, stake, 4, pr, false, out _);
bk.Buy(acct, voided, true, stake, 4, pr, false, out _);
double cash = acct.Get(Assets.CRC);
bk.Night(5, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, _ => acct, rng);
bk.Night(5, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, _ => acct, pr, rng);
Check(win.Outcome < 0 && lose.Outcome < 0, "窗口没结束不结算");
var evs = bk.Night(6, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, _ => acct, rng);
var evs = bk.Night(6, new[] { "oxymoreWave", "powerShortage" }, bk.Happened, _ => acct, pr, rng);
Check(win.Outcome == -1 || bk.Markets.All(x => x.Id != win.Id), "到期的盘口移出列表");
Check(evs.Any(e => e.Kind == "win" && e.Title == "停电" && Math.Abs(e.Amount - sw) < 1e-6), "押中兑付份数");
Check(evs.Any(e => e.Kind == "lose" && e.Title == "x"), "押错没收");
@@ -1467,44 +1550,33 @@ internal static class Program
Check(bk.Markets.Count == 1 && bk.Markets[0].Id == voided.Id, "后面的窗口还没到");
// 缺观测:8 号之前 7、8 的记录缺,到期作废退本金
double c2 = acct.Get(Assets.CRC);
var ev2 = bk.Night(9, new string[0], bk.Happened, _ => acct, rng);
var ev2 = bk.Night(9, new string[0], bk.Happened, _ => acct, pr, rng);
Check(ev2.Any(e => e.Kind == "void") && Math.Abs(acct.Get(Assets.CRC) - c2 - 50) < 1e-6, $"缺数据作废并退本金:{acct.Get(Assets.CRC) - c2:0.00}");
// 内幕被查:持仓达到上限的 40% 就有概率,按查处概率被查
int caught = 0, wins = 0;
for (int i = 0; i < 400; i++)
{
var b2 = new PredictBook(); var a2 = new Wallet { Address = "p" }; a2.Add(Assets.CRC, 1e6);
var mk = PredictMarket.Create(++b2.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b2.Markets.Add(mk);
double target = Tuning.PredictInsiderShare * mk.ShareCap * 1.05, spend = 0;
spend = (mk.BuyCost(true, target)) * (1 + Tuning.PredictFee);
b2.Buy(a2, mk, true, spend, 4, pr, true, out _);
var e = b2.Night(5, new string[0], mm => true, _ => a2, new Rng((ulong)(i + 1)));
if (e[0].Kind == "caught") { caught++; Check(e[0].Fraud > 0 && e[0].Fraud <= Tuning.PredictFraudCap, "罪证有上限"); }
else wins++;
}
Check(Math.Abs(caught / 400.0 - Tuning.PredictInsiderCatch) < 0.08, $"内幕大单按查处概率被查:{caught}/400");
// 小额内幕不会被查
// 内幕单:照常兑付全部份数,只有内幕份额对应的兑付记成脏的 CRC
var b2 = new PredictBook(); var a2 = new Wallet { Address = "p" }; a2.Add(Assets.CRC, 1e6);
var mk = PredictMarket.Create(++b2.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b2.Markets.Add(mk);
b2.Buy(a2, mk, true, mk.BuyCost(true, 0.2 * mk.ShareCap) * 1.02, 4, pr, true, out double si);
b2.Buy(a2, mk, true, 50, 4, pr, false, out double sn);
double c2b = a2.Get(Assets.CRC), t2b = a2.DirtyOf(Assets.CRC);
var e2 = b2.Night(5, new string[0], mm => true, _ => a2, pr, new Rng(1));
Check(e2[0].Kind == "insider" && Near(e2[0].Amount, si + sn, 1e-6), $"内幕单照常结算:{e2[0].Kind}");
Check(Near(a2.Get(Assets.CRC) - c2b, si + sn, 1e-6), "全部份数照常兑付");
Check(Near(a2.DirtyOf(Assets.CRC) - t2b, si, 1e-6) && Near(e2[0].Taint, si, 1e-6), $"只有内幕份额的兑付记成脏的:+{a2.DirtyOf(Assets.CRC) - t2b:0.00} / {si:0.00}");
// 不是内幕单:普通结算,不加污点
var b3 = new PredictBook(); var a3 = new Wallet { Address = "p" }; a3.Add(Assets.CRC, 1e6);
var m3 = PredictMarket.Create(++b3.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b3.Markets.Add(m3);
b3.Buy(a3, m3, true, m3.BuyCost(true, 0.2 * m3.ShareCap) * 1.02, 4, pr, true, out _);
Check(b3.Night(5, new string[0], mm => true, _ => a3, new Rng(1))[0].Kind == "win", "小额内幕安全");
// 被查:只退本金
var b4 = new PredictBook(); var a4 = new Wallet { Address = "p" }; a4.Add(Assets.CRC, 1e6);
b3.Buy(a3, m3, true, 200, 4, pr, false, out _);
Check(b3.Night(5, new string[0], mm => true, _ => a3, pr, new Rng(1))[0].Kind == "win" && a3.Taint(pr) == 0, "非内幕单不记污点");
// 拿脏 CRC 下的内幕单:带回的脏枚数加上内幕记的,整笔兑付都是脏的,不会多记到钱包里别的 CRC 上
var b4 = new PredictBook(); var a4 = new Wallet { Address = "p" }; a4.AddDirty(Assets.CRC, 1e4, 1e4); a4.Add(Assets.CRC, 1e5);
var m4 = PredictMarket.Create(++b4.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); b4.Markets.Add(m4);
double s4 = m4.BuyCost(true, 0.5 * m4.ShareCap) * 1.02;
b4.Buy(a4, m4, true, s4, 4, pr, true, out _);
double c4 = a4.Get(Assets.CRC);
for (ulong sd = 1; sd < 50; sd++)
{
var bb = new PredictBook(); bb.Markets.Add(m4); bb.Positions.AddRange(b4.Positions.Select(x => new PredictPos { Market = x.Market, Wallet = x.Wallet, Yes = x.Yes, Shares = x.Shares, Cost = x.Cost, Insider = x.Insider }));
var aa = new Wallet { Address = "p" };
m4.Outcome = -1;
var ee = bb.Night(5, new string[0], mm => true, _ => aa, new Rng(sd));
if (ee[0].Kind == "caught") { Check(aa.Get(Assets.CRC) < 1e-6, "被查本金和收益全部没收"); break; }
}
b4.Buy(a4, m4, true, m4.BuyCost(true, 0.1 * m4.ShareCap) * 1.02, 4, pr, true, out double s4);
double d4 = a4.DirtyOf(Assets.CRC), pd4 = b4.Positions[0].Dirty;
var e4 = b4.Night(5, new string[0], mm => true, _ => a4, pr, new Rng(1));
Check(e4[0].Kind == "insider" && Near(a4.DirtyOf(Assets.CRC) - d4, s4, 1e-6) && Near(e4[0].Taint + pd4, s4, 1e-6), $"兑付 {s4:0.00} 全部记脏:带回 {pd4:0.00} + 内幕 {e4[0].Taint:0.00}");
});
Test("预测市场:多钱包持仓各回各的钱包,限额与内幕按盘口合计", () =>
Test("预测市场:多钱包持仓各回各的钱包,内幕污点各记各的", () =>
{
Func<string, double> pr = a => 1;
var w1 = new Wallet { Address = "0xa" }; var w2 = new Wallet { Address = "0xb" };
@@ -1512,26 +1584,21 @@ internal static class Program
Func<string, Wallet> pick = ad => ad == "0xa" ? w1 : w2;
var bk = new PredictBook();
var m = PredictMarket.Create(++bk.Seq, PredictKind.Event, "e", "t", 0, 4, 5, 5, 0.1); bk.Markets.Add(m);
double half = Tuning.PredictInsiderShare * m.ShareCap * 0.6;
double half = 0.25 * m.ShareCap;
Check(bk.Buy(w1, m, true, m.BuyCost(true, half) * 1.02, 4, pr, true, out _) == PredictResult.Ok, "钱包 A 买入");
Check(bk.Buy(w2, m, false, 10, 4, pr, false, out _) == PredictResult.Opposite, "另一个钱包也不能买对面");
Check(bk.Buy(w2, m, true, m.BuyCost(true, half) * 1.02, 4, pr, true, out _) == PredictResult.Ok, "钱包 B 买入同一边");
Check(bk.PosAll(m.Id).Count == 2 && bk.PosOf(m.Id, "0xa") != null && bk.PosOf(m.Id, "0xb") != null, "每个钱包各一个持仓");
Check(bk.SellAll(w2, m, 4, out double got) == PredictResult.Ok && got > 0 && bk.PosOf(m.Id, "0xb") == null && bk.PosOf(m.Id, "0xa") != null, "卖出只动自己钱包的持仓");
Check(bk.SellAll(w2, m, 4, pr, out double got) == PredictResult.Ok && got > 0 && bk.PosOf(m.Id, "0xb") == null && bk.PosOf(m.Id, "0xa") != null, "卖出只动自己钱包的持仓");
Check(bk.Buy(w2, m, true, m.BuyCost(true, half) * 1.02, 4, pr, true, out _) == PredictResult.Ok, "再买");
// 两边各 0.6×查处线,合计超线:拆钱包躲不过
int caught = 0;
for (ulong sd = 1; sd <= 60; sd++)
{
var b2 = new PredictBook(); b2.Markets.Add(new PredictMarket { Id = m.Id, Kind = m.Kind, Key = m.Key, Title = m.Title, FromDay = 5, ToDay = 5, P0 = m.P0, B = m.B, Budget = m.Budget, QYes = m.QYes, QNo = m.QNo });
foreach (var p in bk.Positions) b2.Positions.Add(new PredictPos { Market = p.Market, Wallet = p.Wallet, Yes = p.Yes, Shares = p.Shares, Cost = p.Cost, Insider = p.Insider });
double a0 = w1.Get(Assets.CRC), b0 = w2.Get(Assets.CRC);
var ev = b2.Night(5, new string[0], x => true, pick, new Rng(sd));
if (ev[0].Kind == "caught") caught++;
else Check(w1.Get(Assets.CRC) > a0 && w2.Get(Assets.CRC) > b0, "赢了各自的钱包都收到兑付");
w1.Balances[Assets.CRC] = a0; w2.Balances[Assets.CRC] = b0;
}
Check(Math.Abs(caught / 60.0 - Tuning.PredictInsiderCatch) < 0.2, $"分到两个钱包的内幕持仓合计后按查处概率被查:{caught}/60");
// 两个钱包的内幕份额各自记在自己的钱包上
double a0 = w1.Get(Assets.CRC), b0 = w2.Get(Assets.CRC), ta = w1.DirtyOf(Assets.CRC), tb = w2.DirtyOf(Assets.CRC);
double sa = bk.PosOf(m.Id, "0xa").Shares, sb = bk.PosOf(m.Id, "0xb").Shares;
double ca = bk.PosOf(m.Id, "0xa").Dirty, cb = bk.PosOf(m.Id, "0xb").Dirty; // 买入时随资金带进持仓的脏枚数,兑付时原样回来
var ev = bk.Night(5, new string[0], x => true, pick, pr, new Rng(1));
Check(ev[0].Kind == "insider" && Near(w1.Get(Assets.CRC) - a0, sa, 1e-6) && Near(w2.Get(Assets.CRC) - b0, sb, 1e-6), "各自的钱包收到兑付");
Check(Near(w1.DirtyOf(Assets.CRC) - ta, sa, 1e-6) && Near(w2.DirtyOf(Assets.CRC) - tb, sb, 1e-6) && Near(ev[0].Taint, sa - ca + sb - cb, 1e-6) && cb > 0,
$"各记各的钱包,整笔兑付都是脏的(B 下注的 CRC 里本来有 {cb:0.00} 枚脏的,内幕只补记其余部分)");
});
Test("预测市场:夜间消息反应、情报、新盘口先验", () =>
@@ -1564,7 +1631,7 @@ internal static class Program
// 到期后消息一起清掉
bk.Tips.Add(new PredictTip { Market = closing.Id });
var w = new Wallet { Address = "x" };
bk.Night(6, new string[0], mm => true, _ => w, rng);
bk.Night(6, new string[0], mm => true, _ => w, a => 1, rng);
Check(bk.Tips.Count == 0, "盘口结算后情报作废");
Check(bk.Stats.TryGetValue("Event", out var stE) && stE.Count >= 1 && stE.Hits >= 1, "结算计入按类型的历史");
// gas / CRA / 时代 先验
@@ -1605,8 +1672,8 @@ internal static class Program
var bk = new PredictBook(); var w = new Wallet { Address = "w" }; w.Add(Assets.CRC, 1e5);
var m = PredictMarket.Create(++bk.Seq, PredictKind.Price, "PSC", "PSC", 100, 4, 5, 5, 0.3); bk.Markets.Add(m);
bk.Buy(w, m, yes, 510, 4, a => 1, false, out _);
var ev = bk.Night(5, new string[0], x => outcome, _ => w, new Rng(sd), push);
if (ev[0].Kind == "manip") { if (yes && push > 0) manip++; else Check(false, "不该被判操纵"); Check(ev[0].Fraud > 0, "记罪证"); Check(w.Get(Assets.CRC) < 1e5 - 400, "本金没收"); }
var ev = bk.Night(5, new string[0], x => outcome, _ => w, a => 1, new Rng(sd), push);
if (ev[0].Kind == "manip") { if (yes && push > 0) manip++; else Check(false, "不该被判操纵"); Check(Near(ev[0].Taint, ev[0].Amount, 1e-6) && Near(w.DirtyOf(Assets.CRC), ev[0].Amount, 1e-6), "照常兑付,整笔兑付记成脏的"); Check(w.Get(Assets.CRC) > 1e5 - 510, "本金不没收"); }
else if (yes && push > 0) win++; else if (push == 0) noPush++;
}
}
@@ -1638,11 +1705,11 @@ internal static class Program
var q = new Ponzi(); q.Start(poor, 0.10, 20);
q.Pool = 100000; var ev2 = q.Night(20, 50, poor, new Rng(1));
Check(ev2 != null && ev2.Kind == "collapse", "账户里没钱补利息:崩盘");
// 卷走:池子进账户、污点全额
// 卷走:池子进钱包、整笔脏
var w = new Wallet { Address = "y" }; w.Add(Assets.CRC, 2000);
var r = new Ponzi(); r.Start(w, 0.04, 20); r.Pool = 5000;
double cash = w.Get(Assets.CRC);
Check(Math.Abs(r.Rug(w) - 5000) < 1e-9 && Math.Abs(w.Get(Assets.CRC) - cash - 5000) < 1e-9 && Math.Abs(w.Taint - 5000) < 1e-9 && r.Done, "卷走:池子进账户,污点全额");
Check(Math.Abs(r.Rug(w) - 5000) < 1e-9 && Math.Abs(w.Get(Assets.CRC) - cash - 5000) < 1e-9 && Math.Abs(w.DirtyOf(Assets.CRC) - 5000) < 1e-9 && r.Done, "卷走:池子进钱包,这 5000 CRC 整笔脏");
// 被举报概率随日息和池子升高
Check(Ponzi.ReportChance(0.10, 5000, 20) > Ponzi.ReportChance(0.02, 5000, 20) && Ponzi.ReportChance(0.04, 9000, 20) > Ponzi.ReportChance(0.04, 100, 20), "日息越高、池子越大越容易被举报");
Check(Ponzi.InflowMean(20, 0.08, 50) > Ponzi.InflowMean(20, 0.04, 50) * 2, "日息高于 4% 时存入按指数放大");
@@ -1728,30 +1795,30 @@ internal static class Program
Check(Math.Abs(rugs / 400.0 - Tuning.YieldRugNightly) < 0.05, $"骗局池每晚跑路概率:{rugs / 400.0:0.00}");
});
Test("预测市场:内幕先买后卖也会被查", () =>
Test("预测市场:内幕先买后卖,卖出所得的内幕部分记成脏的", () =>
{
int caught = 0, n = 400;
for (ulong sd = 1; sd <= (ulong)n; sd++)
{
var bk = new PredictBook(); var w = new Wallet { Address = "w" }; w.Add(Assets.CRC, 1e6);
var m = PredictMarket.Create(++bk.Seq, PredictKind.Event, "e", "t", 0, 4, 7, 7, 0.05); bk.Markets.Add(m);
double target = Tuning.PredictInsiderShare * m.ShareCap * 1.02;
bk.Buy(w, m, true, m.BuyCost(true, target) * (1 + Tuning.PredictFee), 4, a => 1, true, out _);
double cost = bk.PosOf(m.Id, "w").Cost;
double p1 = m.PYes, p2 = p1 + (1 - p1) / 2;
m.QYes = m.QNo + m.B * Math.Log(p2 / (1 - p2)); // 夜里价格又涨了一截
bk.SellAll(w, m, 5, new Rng(sd), out double got, out double fraud);
if (fraud > 0) { caught++; Check(got == 0 && fraud <= Tuning.PredictFraudCap, "被查本金和收益全部没收"); }
else Check(got > cost, "没被查时赚到价差");
}
Check(Math.Abs(caught / (double)n - Tuning.PredictInsiderCatch) < 0.08, $"按查处概率被查:{caught}/{n}");
// 小额内幕、非内幕卖出不查
var bk = new PredictBook(); var w = new Wallet { Address = "w" }; w.Add(Assets.CRC, 1e6);
var m = PredictMarket.Create(++bk.Seq, PredictKind.Event, "e", "t", 0, 4, 7, 7, 0.05); bk.Markets.Add(m);
bk.Buy(w, m, true, m.BuyCost(true, 0.3 * m.ShareCap) * (1 + Tuning.PredictFee), 4, a => 1, true, out double si);
bk.Buy(w, m, true, 100, 4, a => 1, false, out double sn);
double cost = bk.PosOf(m.Id, "w").Cost;
double p1 = m.PYes, p2 = p1 + (1 - p1) / 2;
m.QYes = m.QNo + m.B * Math.Log(p2 / (1 - p2)); // 夜里价格又涨了一截
double t0 = w.DirtyOf(Assets.CRC);
Check(bk.SellAll(w, m, 5, a => 1, out double got, out double taint) == PredictResult.Ok && got > cost, "照常卖出,赚到价差");
Check(Near(taint, got * si / (si + sn), 1e-6) && Near(w.DirtyOf(Assets.CRC) - t0, taint, 1e-9), $"内幕份额对应的卖出所得记成脏的:{taint:0.00} / {got:0.00}");
// 非内幕卖出不加污点
var bk2 = new PredictBook(); var w2 = new Wallet { Address = "w" }; w2.Add(Assets.CRC, 1e6);
var m2 = PredictMarket.Create(++bk2.Seq, PredictKind.Event, "e", "t", 0, 4, 7, 7, 0.05); bk2.Markets.Add(m2);
bk2.Buy(w2, m2, true, 200, 4, a => 1, true, out _);
bk2.Buy(w2, m2, true, 200, 4, a => 1, false, out _);
m2.QYes = m2.QNo + m2.B * Math.Log(0.6 / 0.4);
bk2.SellAll(w2, m2, 5, new Rng(1), out _, out double f2);
Check(f2 == 0, "小额内幕卖出安全");
bk2.SellAll(w2, m2, 5, a => 1, out _, out double t2);
Check(t2 == 0 && w2.Dirty.Count == 0, "非内幕卖出不记污点");
});
Test("罪证折算:按额 × 比例,单次封顶", () =>
{
Check(Near(Wallet.FraudFor(100), 100 * Tuning.PredictFraudShare, 1e-9) && Wallet.FraudFor(1e9) == Tuning.PredictFraudCap && Wallet.FraudFor(0) == 0, "按比例折罪证,单次封顶");
});
Test("日 K:开盘、日内高低、成交量、收盘、均线、保留天数", () =>
@@ -1787,18 +1854,19 @@ internal static class Program
{
var sm = new StoryMods(); StoryMods.Current = sm;
Check(sm.Apply("serpico_sec", 50) && !sm.Apply("serpico_sec", 51), "只生效一次");
Check(sm.Aml(60) == Tuning.StorySecAmlMult && sm.Aml(50 + Tuning.StorySecDays) == 1, "治安部整顿期内出金审查加重,到期恢复");
Check(sm.MixClean(60) == Tuning.StorySecMixClean && sm.MixClean(50 + Tuning.StorySecDays) == 1, "治安部整顿期内混币器洗得更差,到期恢复");
Check(Mixer.RoundRate(1000, 60) < Mixer.RoundRate(1000) * 0.75, "整顿期内每轮清洗率 ×0.7");
Check(sm.Sanction(60) == Tuning.StorySecSanctionMult && sm.Enforce(60) == Tuning.StorySecEnforceMult, "制裁与查处倍率");
sm.Apply("lun_arrested", 55);
Check(Math.Abs(sm.Enforce(60) - Tuning.StorySecEnforceMult * Tuning.StoryLunMult) < 1e-12 && sm.Enforce(55 + Tuning.StoryLunDays) == Tuning.StorySecEnforceMult, "伦警官立威期叠加,先到期");
// 出金审查真的变严
var ex = new Exchange(); var ex2 = new Exchange();
var w = new Wallet { Address = "p" }; w.Add(Assets.CRC, 10000); w.Taint = 3000;
var w2 = new Wallet { Address = "p" }; w2.Add(Assets.CRC, 10000); w2.Taint = 3000;
double hit = ex.Cashout(w, 2000, a => 1, 60, 300, false, new Rng(1)).HitChance;
StoryMods.Current = new StoryMods();
double hit0 = ex2.Cashout(w2, 2000, a => 1, 60, 300, false, new Rng(1)).HitChance;
Check(hit > hit0 && hit <= 1, $"命中率 {hit0:0.000} → {hit:0.000}");
Check(sm.OtcSting(60) == Tuning.StoryLunMult && sm.OtcSting(55 + Tuning.StoryLunDays) == 1, "伦警官立威期内换币客户便衣更多");
int st0 = 0, st1 = 0;
for (ulong sd = 1; sd <= 3000; sd++)
{
if (Otc.MakeSell(new List<MarketEvent>(), false, a => 1, 200, new Rng(sd)).Sting) st0++;
if (Otc.MakeSell(new List<MarketEvent>(), false, a => 1, 200, new Rng(sd), Tuning.StoryLunMult).Sting) st1++;
}
Check(st1 > st0, $"便衣 {st0} → {st1}(3000 位卖币客户)");
// CRT 脱锚减半:镇压开始时脱锚的频率
int dep0 = 0, dep1 = 0;
for (ulong sd = 1; sd <= 2000; sd++)
@@ -1850,6 +1918,96 @@ internal static class Program
finally { StoryMods.Current = saved; }
});
Test("Tumbler 之后:探员与搜查卡住上门日程、泄露还原出款污点、审判、黑市跑路、换皮重开", () =>
{
var saved = StoryMods.Current;
try
{
StoryMods.Current = new StoryMods();
// 探员:第 2 次之后的第二天起来,来过的第二天起才排第 3 次
var t = new Tumbler(); var a = new TumblerAfter();
t.TryStart(40, true); t.Visited(41); t.Visited(44);
Check(!a.AgentDue(t, 44) && a.AgentDue(t, 45), "探员在第 2 次上门的第二天起来");
Check(t.Due(46, a) == 0 && t.Due(46) == 3, "探员没来过,第 3 次不排(不传进度时照旧)");
a.Agent = TumblerAfter.Deny; a.AgentDay = 46;
Check(t.Due(46, a) == 0 && t.Due(47, a) == 3 && !a.AgentDue(t, 47), "探员来过的第二天起排第 3 次,只来一次");
t.Choice = Tumbler.Hide; t.Visited(47);
Check(!a.NeedSearch(t) && t.Due(51, a) == 4, "否认过:不搜查,第 4 次照常");
// 配合过又藏了:第 4 次前一天搜查
var t2 = new Tumbler(); var a2 = new TumblerAfter { Agent = TumblerAfter.Cooperate, AgentDay = 45 };
t2.TryStart(40, true); t2.Visited(41); t2.Visited(44); t2.Choice = Tumbler.Hide; t2.Visited(46);
Check(!a2.SearchDue(t2, 48) && a2.SearchDue(t2, 49) && t2.Due(50, a2) == 0, "配合 + 藏:第 4 次前一天搜查,搜查前不排第 4 次");
a2.Searched = true; a2.SearchDay = 50;
Check(t2.Due(50, a2) == 0 && t2.Due(51, a2) == 4 && !a2.SearchDue(t2, 51), "搜查的第二天起排第 4 次");
var t3 = new Tumbler { Choice = Tumbler.Report }; t3.TryStart(40, true); t3.Visited(41); t3.Visited(44); t3.Visited(46);
Check(!a2.NeedSearch(t3), "举报的不搜查");
// 出款记录与泄露
Func<string, double> px = x => 1;
var w = new Wallet { Address = "w" }; w.AddDirty(Assets.CRC, 5000, 4000);
var m = new Mixer();
m.Deposit(w, Assets.CRC, 1000, 3, 1, px);
m.Night(w, 4, px, new Rng(1));
var r0 = m.Returns.Single();
Check(r0.Day == 4 && r0.Wallet == "w" && r0.Cleaned > 100 && Near(r0.Cleaned, 800 - (w.DirtyOf(Assets.CRC) - 3200), 1e-6), $"出款记下洗掉的污点:{r0.Cleaned:0}");
double before = w.DirtyOf(Assets.CRC);
var lk = m.Leak(4 + Tuning.TumblerLeakDays);
Check(lk.Count == 1 && Near(lk.Cleaned, r0.Cleaned, 1e-6) && Near(lk.Fraud, Wallet.FraudFor(r0.Cleaned), 1e-9) && w.DirtyOf(Assets.CRC) == before && m.Returns.Count == 0, "泄露:按洗掉的污点记罪证,不动钱包,记录清空");
var m2 = new Mixer();
m2.Returns.Add(new MixReturn { Day = 1, Wallet = "v", Amount = 10, Cleaned = 500 });
m2.Returns.Add(new MixReturn { Day = 40, Wallet = "v", Amount = 10, Cleaned = 300 });
m2.Returns.Add(new MixReturn { Day = 40, Wallet = "gone", Amount = 10, Cleaned = 300 });
var lk2 = m2.Leak(45);
Check(lk2.Count == 2 && Near(lk2.Cleaned, 600, 1e-9) && Near(lk2.Fraud, Wallet.FraudFor(600), 1e-9), "只算 30 天内的,罪证 = 洗掉的污点 × 50%");
a.LeakTonight(50); a.LeakTonight(60);
Check(a.LeakDay == 50, "泄露只安排一次");
// 审判
int conv = 0;
for (int i = 0; i < 2000; i++) { var x = new TumblerAfter(); x.StartTrial(10, new Rng((ulong)i + 1)); if (x.Verdict == 1) conv++; }
Check(Near(conv / 2000.0, Tuning.TumblerTrialConvict, 0.03), $"定罪约 {Tuning.TumblerTrialConvict:P0}:{conv / 2000.0:P1}");
var tr = new TumblerAfter(); tr.StartTrial(10, new Rng(7));
Check(tr.TrialOpenDay == 10 + Tuning.TumblerTrialOpen && tr.TrialDay == 10 + Tuning.TumblerTrialDays && tr.ClosedDay == 10, "开盘、宣判、关停的日子");
var rng = new Rng(9);
Check(tr.Hint(tr.TrialOpenDay - 2, rng) == null && tr.Hint(tr.TrialOpenDay - 1, rng) != null && tr.Hint(tr.TrialOpenDay - 1, rng) == null, "开盘前一晚起每晚一条");
for (int dd = tr.TrialOpenDay; dd < tr.TrialDay + 3; dd++) tr.Hint(dd, rng);
Check(tr.Hints.Count == tr.TrialDay - tr.TrialOpenDay + 1, $"宣判前一晚为止:{tr.Hints.Count} 条");
Check(Near(new TumblerAfter().TrialPublic(), Tuning.TumblerTrialConvict, 1e-9), "没有新闻时公开概率 = 先验");
double sumC = 0, sumA = 0; int nC = 0, nA = 0;
for (int i = 0; i < 1000; i++)
{
var x = new TumblerAfter(); var rr = new Rng((ulong)i + 11); x.StartTrial(0, rr);
for (int dd = 0; dd < 10; dd++) x.Hint(dd, rr);
if (x.Verdict == 1) { sumC += x.TrialPublic(); nC++; } else { sumA += x.TrialPublic(); nA++; }
}
Check(sumC / nC > 0.85 && sumA / nA < 0.5, $"新闻把公开概率推向真实判决:定罪 {sumC / nC:0.00},无罪 {sumA / nA:0.00}");
// 黑市跑路、换皮重开
var sold = new TumblerAfter(); sold.StartSold(20, new Rng(3));
Check(sold.BmRugDay >= 20 + Tuning.TumblerBmRugMin && sold.BmRugDay <= 20 + Tuning.TumblerBmRugMax, $"黑市跑路在 {sold.BmRugDay}");
var mr = new Mixer(); var w3 = new Wallet { Address = "z" }; w3.Add(Assets.CRC, 1000);
mr.Deposit(w3, Assets.CRC, 400, 2, 1, px); mr.Deposit(w3, Assets.CRC, 300, 3, 1, px);
var rug = mr.RugAll();
Check(rug.Count == 2 && rug.All(x => x.Rugged) && mr.Pending.Count == 0, "黑市跑路:在途的全部丢失");
var rb = new TumblerAfter { ClosedDay = 30 };
Check(!rb.RebrandDue(30 + Tuning.TumblerRebrandDays - 1) && rb.RebrandDue(30 + Tuning.TumblerRebrandDays) && !new TumblerAfter().RebrandDue(100), "关停满 15 天换皮,没关停不换");
var sm = new StoryMods(); StoryMods.Current = sm;
sm.Apply("tumbler_sold", 1); sm.Apply("tumbler_bm_rug", 2);
Check(sm.MixerClosed, "黑市跑路后混币器关停");
sm.Rebrand(false);
Check(!sm.MixerClosed && sm.MixerRebranded && Near(Mixer.Fee, Tuning.MixerFee, 1e-12) && sm.MixerRug == 1, "换皮重开:重新开放,费率和跑路倍率回到 1");
var w4 = new Wallet { Address = "h" }; w4.Add(Assets.CRC, 500);
var mh = new Mixer(); mh.Deposit(w4, Assets.CRC, 500, 1, 1, px); mh.Night(w4, 2, px, new Rng(5));
Check(w4.Dirty.Count == 0, "正常的新混币器:干净的币出来还是干净的");
sm.Rebrand(true);
Check(Near(Mixer.Fee, Tuning.MixerFee * Tuning.TumblerHoneypotFee, 1e-12), "钓鱼混币器费率打折");
var w5 = new Wallet { Address = "k" }; w5.Add(Assets.CRC, 500);
var mk = new Mixer(); mk.Deposit(w5, Assets.CRC, 500, 1, 1, px); mk.Night(w5, 2, px, new Rng(5));
Check(Near(w5.DirtyOf(Assets.CRC), w5.Get(Assets.CRC), 1e-6), "从钓鱼混币器提回的币整笔都是脏的");
}
finally { StoryMods.Current = saved; }
});
Test("黑市商家收币:每类商家都收 CRT 或隐私币,平台收 PSC、CRT、CRA", () =>
{
foreach (MerchantType ty in Enum.GetValues(typeof(MerchantType)))