ui: black market buy page with seller list, payment panel, per-merchant accepted coins and auto-convert; wallet row keys beside the data cells
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
12a63c562f
commit
9d24840561
7 files changed
+262
-73
No files matched your search
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
### 第九阶段:剧情与收尾
|
### 第九阶段:剧情与收尾
|
||||||
|
|
||||||
|
- 黑市买货页重排(`ui-trial` 分支):左栏是卖家列表(平台和各商家,选中行反色,商家写类型和缩写地址),右栏是 3 列货架;币种和数量不再摆在货架上方。点"购买"后右栏换成付款面板:数量(库存内 ×1 / ×5 / ×10)、付款方式(商家接受提前放款时才有)、合计、付款币种(该商家收的币)和"生成账单";下面是自动换币——钱包里别的币(够付)按 8% 滑点换成商家收的币,一次合约签名完成(`BmAutoSwapSlip`);没有够用的币时写明。上架费、改价费、一键上架同样在付款面板里选币(平台收 PSC、CRT、CRA)。各类商家收的币:硬件技术类 PSC / CRT,违禁和个人卖家 CRT / 匿名币,农业养殖水务酿酒 PSC / CRA(`Merchants.Accepts`,界面里限制,域层下单不限)。游戏内验证:平台买一件商品,CRC 自动换币 224.64 CRC → 2.0842 PSC(208 信用点等值)后成交,货进储藏区;直接生成账单显示收款地址和金额;商家货架切换和付款面板正常。没有验证:卖货页改价、一键上架的付款面板、英文界面、数量选择(库存只有 1 件)。
|
||||||
|
- 钱包页按钮:操作键放到数据格右边的纸面上(和顶栏页签同样的黑色触控键,不再压在污点字段上);选中行只反色数据格;状态"已连接"靠右。
|
||||||
- 新手期调整:低端显卡算力提高——RTX 60 1.0 → 1.8 H、RX 70 0.8 → 1.4 H(价格、耗电不变)。开局送的一矿架一卡,第 1–7 天日均净收入 14 → 33(电网电价),第 15–30 天 6 → 18;再买满 4 张同款卡的矿架 33 → 82/天,每加一张约 10 天回本(原来 24 天)。每张卡每千瓦时的产出仍低于中高端,一个矿架只有 4 个槽位,满槽 4×RTX60 约 7 H,4×RTX70 是 20 H,中后期自然要换更高档次。ops `research/starter` 有各档位的逐段结果。
|
- 新手期调整:低端显卡算力提高——RTX 60 1.0 → 1.8 H、RX 70 0.8 → 1.4 H(价格、耗电不变)。开局送的一矿架一卡,第 1–7 天日均净收入 14 → 33(电网电价),第 15–30 天 6 → 18;再买满 4 张同款卡的矿架 33 → 82/天,每加一张约 10 天回本(原来 24 天)。每张卡每千瓦时的产出仍低于中高端,一个矿架只有 4 个槽位,满槽 4×RTX60 约 7 H,4×RTX70 是 20 H,中后期自然要换更高档次。ops `research/starter` 有各档位的逐段结果。
|
||||||
- 手册补写出金审查的全部规则(界面不显示额度和命中概率:单笔 5 倍房租、7 天累计 15 倍房租、污点 10% 起、混币和被标记地址 +30%、上限 90%、轻度 / 重度的后果、手续费和税)和"销毁钱包前自己转出持仓与存款"。
|
- 手册补写出金审查的全部规则(界面不显示额度和命中概率:单笔 5 倍房租、7 天累计 15 倍房租、污点 10% 起、混币和被标记地址 +30%、上限 90%、轻度 / 重度的后果、手续费和税)和"销毁钱包前自己转出持仓与存款"。
|
||||||
- 金融审查(agy 在独立分支做的数值审查,核实后采纳一部分;报告 ops `research/agy/i_finance.md`,新模拟器 `research/agy_fin/subsim`、`portfoliosim`):
|
- 金融审查(agy 在独立分支做的数值审查,核实后采纳一部分;报告 ops `research/agy/i_finance.md`,新模拟器 `research/agy_fin/subsim`、`portfoliosim`):
|
||||||
|
|||||||
@@ -51,6 +51,27 @@ namespace Decentralized.Domain
|
|||||||
[MerchantType.Luxury] = new[] { C("pheromone_perfume", 2), G("neuroactive_perfume"), G("self_refilling_neuroactive_perfume"), G("skincare_cream"), G("packet_red_cigarette"), G("gamblers_coin"), G("kotton_fabric"), G("armor_lining") },
|
[MerchantType.Luxury] = new[] { C("pheromone_perfume", 2), G("neuroactive_perfume"), G("self_refilling_neuroactive_perfume"), G("skincare_cream"), G("packet_red_cigarette"), G("gamblers_coin"), G("kotton_fabric"), G("armor_lining") },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 各类商家收的币:硬件和技术类收 PSC,违禁类收 CRT 和匿名币,农业养殖水务收 PSC 和 CRA,个人卖家收 CRT 和匿名币。
|
||||||
|
/// 平台现货收 PSC、CRT、CRA。不在表里的币要先换:Web3 内置的自动换币要多付滑点(<see cref="Tuning.BmAutoSwapSlip"/>),也可以自己去 DEX 换。
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string[] PlatformAccepts = { Assets.PSC, Assets.CRT, Assets.CRA };
|
||||||
|
|
||||||
|
public static string[] Accepts(MerchantType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case MerchantType.Rigs: case MerchantType.Modules: case MerchantType.Miner: case MerchantType.Tools: case MerchantType.Storage:
|
||||||
|
case MerchantType.GpuNew: case MerchantType.GpuUsed: case MerchantType.Asic: case MerchantType.Elite: case MerchantType.Bulk:
|
||||||
|
return new[] { Assets.PSC, Assets.CRT };
|
||||||
|
case MerchantType.Arms: case MerchantType.GunMods: case MerchantType.Docs: case MerchantType.Pharma: case MerchantType.Chem:
|
||||||
|
case MerchantType.Luxury: case MerchantType.Personal:
|
||||||
|
return new[] { Assets.CRT, Assets.Ponero };
|
||||||
|
default: // Brewer、Farm、Husbandry、Water
|
||||||
|
return new[] { Assets.PSC, Assets.CRA };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<string, BmEntry> ById = BuildIndex();
|
private static readonly Dictionary<string, BmEntry> ById = BuildIndex();
|
||||||
|
|
||||||
private static Dictionary<string, BmEntry> BuildIndex()
|
private static Dictionary<string, BmEntry> BuildIndex()
|
||||||
|
|||||||
@@ -237,6 +237,9 @@ namespace Decentralized.Domain
|
|||||||
public const double FakeSupportChance = 0.04, FakeTransferShare = 0.08;
|
public const double FakeSupportChance = 0.04, FakeTransferShare = 0.08;
|
||||||
public const int ScamMinDay = 10;
|
public const int ScamMinDay = 10;
|
||||||
|
|
||||||
|
/// <summary>Web3 内置自动换币:商家不收手里的币时,按这个比例多付(DEX 手续费 0.3% 加上高滑点),省得自己去换。</summary>
|
||||||
|
public const double BmAutoSwapSlip = 0.08;
|
||||||
|
|
||||||
/// <summary>剧情联动:塞尔皮科证据交给革命军(CRA 补贴池注资、螺旋概率倍率与期限、SPARK 几晚内涨到的倍数)、交给黑市(CRT 脱锚与被出卖的倍率)、
|
/// <summary>剧情联动:塞尔皮科证据交给革命军(CRA 补贴池注资、螺旋概率倍率与期限、SPARK 几晚内涨到的倍数)、交给黑市(CRT 脱锚与被出卖的倍率)、
|
||||||
/// 交给治安部(期限内出金审查命中、混币器制裁、操纵与内幕查处的倍率);埃莉诺泄露数据盘(CRC 托管减记倍率、SPARK 涨幅)、交回(CRC 脱锚倍率);
|
/// 交给治安部(期限内出金审查命中、混币器制裁、操纵与内幕查处的倍率);埃莉诺泄露数据盘(CRC 托管减记倍率、SPARK 涨幅)、交回(CRC 脱锚倍率);
|
||||||
/// 伦警官被捕(期限与查处倍率);SPARK 推涨分几晚走完。</summary>
|
/// 伦警官被捕(期限与查处倍率);SPARK 推涨分几晚走完。</summary>
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ black.kind.Contraband.3,Contraband (high)
|
|||||||
black.kind.Tech,High-tech (markup)
|
black.kind.Tech,High-tech (markup)
|
||||||
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
|
nightlog.bm.ready,"Black market: {0} arrived; a courier will come by once you open."
|
||||||
item.web3guide.name,Web3 Handbook
|
item.web3guide.name,Web3 Handbook
|
||||||
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. RatDigg miners (M1 / M2 / M3) are standalone machines with far more hash than a card and a lower power draw per hash; they need a battery, take modules and use no rig slots. Only the black market's miner dealers sell them, gated by black-market standing (M1 40, M2 60, M3 80), sometimes second-hand. When one burns out, repair it by putting electronic components in its repair slot in order; it comes back 20 health lower. Somewhere between day 50 and day 90 a "miner era" arrives, announced in the paper a few days earlier: the big farms halve their power costs, network hash climbs, GPU miners are diluted and low-end cards barely earn, while miners still do fine. Perps: the Perps tab on Poinbase uses your account CRC as margin, up to 5× on PSC and 3× on SPARK, one position per product, never long and short at once. Fills are worse than the quote (more so for bigger orders, like the spot pool) and opening and closing both cost a fee. Liquidation is checked only overnight against the day's average price: equity under 10% of notional wipes the margin. There is also a nightly funding payment; when the price is above its 7-day average, longs pay shorts. If a liquidation loses more than the margin, the insurance fund covers the gap; when it runs dry (a SPARK collapse can do that) winning positions are auto-deleveraged and give up half their profit; anything beyond that becomes your debt. Debt grows 2% a day, is repaid first from CRC in the account every night, blocks new positions and cash-outs, and after seven days items in your shop are seized to cover it; whatever is still short is written off and the perp account is banned for 30 days. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page once its coins are moved out. Transfers pay a fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested 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. The pool pays from a subsidy fund; when it runs low the rate is cut and word of big holders pulling out may spread. Treat that as a warning: once it collapses CRA is worthless within three days and four days of unlocking is too slow. The balance token SPARK can be minted and burned against CRA at the going price (with a fee); its price swings hard and crashes with the collapse. Staking, minting and burning are contract calls and need a signature on the wallet. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometLine truncated
|
item.web3guide.desc,"Bank: depositing turns credits into CRC in your Poinbase account; withdrawing turns CRC back into credits and goes through compliance screening, which looks at the Poinbase account's taint. Trading: buys are paid in the quote coin, sells in the traded coin. Mixer: pick a coin, amount and rounds; each round takes a fee and lowers taint, rounds equal days of waiting, and the coins come back to the same address; the more you deposit, the worse the cleaning. Hardware wallets: on-chain coins sit in PLEDGER hardware wallets, each with its own balances and taint; open and connect them on the terminal's Wallets page. The Web3 page's trading, black market and mixer use the connected wallet, and coin-paying customers and mining rewards go into it too. Payments (black market, mixer, Ponero desk, listing fees) start as an invoice on the terminal; type the payee address on the wallet and hold ✓ to sign. The last two characters of an address are a checksum, so a one-character typo is usually caught; once in a while it happens to be valid and the coins go to an address nobody uses. DEX swaps also need a signature on the wallet. RatDigg miners (M1 / M2 / M3) are standalone machines with far more hash than a card and a lower power draw per hash; they need a battery, take modules and use no rig slots. Only the black market's miner dealers sell them, gated by black-market standing (M1 40, M2 60, M3 80), sometimes second-hand. When one burns out, repair it by putting electronic components in its repair slot in order; it comes back 20 health lower. Somewhere between day 50 and day 90 a "miner era" arrives, announced in the paper a few days earlier: the big farms halve their power costs, network hash climbs, GPU miners are diluted and low-end cards barely earn, while miners still do fine. Perps: the Perps tab on Poinbase uses your account CRC as margin, up to 5× on PSC and 3× on SPARK, one position per product, never long and short at once. Fills are worse than the quote (more so for bigger orders, like the spot pool) and opening and closing both cost a fee. Liquidation is checked only overnight against the day's average price: equity under 10% of notional wipes the margin. There is also a nightly funding payment; when the price is above its 7-day average, longs pay shorts. If a liquidation loses more than the margin, the insurance fund covers the gap; when it runs dry (a SPARK collapse can do that) winning positions are auto-deleveraged and give up half their profit; anything beyond that becomes your debt. Debt grows 2% a day, is repaid first from CRC in the account every night, blocks new positions and cash-outs, and after seven days items in your shop are seized to cover it; whatever is still short is written off and the perp account is banned for 30 days. Poinbase is a custodial account and can withdraw straight to the connected wallet; depositing from a wallet means sending to Poinbase's deposit address, which takes only CRC and PSC. More hardware wallets are sold by Dr. Jackson; drag one onto the trading terminal to add it, up to 5. The black market has cheaper ones. A wallet you no longer want can be destroyed on the Wallets page once its coins are moved out. Transfers pay a fee: slow, normal or fast, and gas prices rise and fall over roughly a week; when the network is congested 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. The pool pays from a subsidy fund; when it runs low the rate is cut and word of big holders pulling out may spread. Treat that as a warning: once it collapses CRA is worthless within three days and four days of unlocking is too slow. The balance token SPARK can be minted and burned against CRA at the going price (with a fee); its price swings hard and crashes with the collapse. Staking, minting and burning are contract calls and need a signature on the wallet. Black market: coin only, no CRC, 30% above normal channels, with extra markup on high-tech goods and GPUs; the coin you pay leaves its share of taint with the seller and the goods carry no stolen tag; GPUs may be fake, inspect them yourself. Orders are cheaper than spot but take days and may be intercepted; merchants sell by type at their own price factor, 5+ items get 5% off and 10+ get 10% off, and a bulk merchant appears at random with the lowest prices but six days of waiting; escrow costs 5%, is released on delivery and refunded if the seller runs; advance release takes another 10% off, but a scammer keeps the money and only sellers with 30+ trades accept; the platform itself can rug, be taken over by enforcement, or a seller can be arrested, after which your orders get traced and crime logged; forum rumors can be warnings. An intercepted parcel sometLine truncated
|
||||||
courier.odd1,...Package is here.
|
courier.odd1,...Package is here.
|
||||||
courier.odd2,"(His hand stays in his pocket.) Sign here."
|
courier.odd2,"(His hand stays in his pocket.) Sign here."
|
||||||
courier.sign,Sign
|
courier.sign,Sign
|
||||||
@@ -346,7 +346,7 @@ black.edit.cancel,Cancel
|
|||||||
black.edit.range,"The price must be 0.6 to 1.8 times the reference ({0} to {1})."
|
black.edit.range,"The price must be 0.6 to 1.8 times the reference ({0} to {1})."
|
||||||
black.sell.reprice,Reprice
|
black.sell.reprice,Reprice
|
||||||
merchant.Platform,Platform
|
merchant.Platform,Platform
|
||||||
black.platform.line,"Platform spot instant delivery payee {0}"
|
black.platform.line,Platform spot instant delivery
|
||||||
black.orders.in,Bought
|
black.orders.in,Bought
|
||||||
black.orders.out,Selling
|
black.orders.out,Selling
|
||||||
black.desk.pair,"PONERO [anonymous coin] price {0}"
|
black.desk.pair,"PONERO [anonymous coin] price {0}"
|
||||||
@@ -784,3 +784,15 @@ yield.sub.pools,Yield pools
|
|||||||
yield.sub.mine,Issue scheme
|
yield.sub.mine,Issue scheme
|
||||||
pool.side.h,Deposit
|
pool.side.h,Deposit
|
||||||
ponzi.early,High-yield schemes can be issued from day {0}.
|
ponzi.early,High-yield schemes can be issued from day {0}.
|
||||||
|
bpay.h,Payment
|
||||||
|
bpay.qty,Quantity
|
||||||
|
bpay.total,Total {0} credits
|
||||||
|
bpay.coin,Pay with (coins the seller takes)
|
||||||
|
bpay.need,"Pay {0} {1} wallet balance {2}"
|
||||||
|
bpay.invoice,Create invoice
|
||||||
|
bpay.swap.h,Auto-convert to {0}
|
||||||
|
bpay.swap.none,No other coin in the wallet is enough
|
||||||
|
bpay.swap.need,"Pay {0} {1}, includes {2}% slippage"
|
||||||
|
bpay.swap.btn,Convert and pay
|
||||||
|
bpay.swap.sum,"{0}: auto-convert, pay {2} {1}, swapped to {3} before ordering"
|
||||||
|
bpay.back,Back
|
||||||
@@ -265,7 +265,7 @@ black.kind.Contraband.3,违禁品(高)
|
|||||||
black.kind.Tech,高科技物品(加价)
|
black.kind.Tech,高科技物品(加价)
|
||||||
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
|
nightlog.bm.ready,"黑市:{0} 到货,开店后会有快递员上门。"
|
||||||
item.web3guide.name,Web3 手册
|
item.web3guide.name,Web3 手册
|
||||||
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。不要的钱包可以在钱包页销毁,要先把币转走。转账要付手续费,分慢、普通、快三档,gas 价格大约一周一个起落;网络拥堵时手续费变贵,慢档可能被挤出内存池。转账前核对完整地址。质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。质押池靠补贴池付息,补贴池见底会降息,有时还会传出大户撤出的消息,那是预兆:一旦崩盘,三天内 CRA 就一文不值,四天的解锁期来不及。平衡代币 SPARK 可以和 CRA 按价格互相铸销(收手续费),价格波动很大,崩盘时会跟着暴跌。质押、铸销都是合约调用,要在钱包上签名。RatDigg 矿机(M1 / M2 / M3)是独立的机器,算力比显卡大得多、耗电更省,要放电池才挖,可以放模块,不占矿架槽位;只在黑市的矿机商那里有,要黑市声望(M1 40、M2 60、M3 80),偶尔有二手的。暴毙后要往修理槽里放电子元件按顺序修好,修好健康少 20。第 50 到 90 天之间会有一次"矿机时代",提前几天上报纸预告,之后大矿场的耗电成本砍半,全网算力涨上去,显卡矿工的收益被摊薄,低档显卡几乎不赚钱,矿机还行。永续合约:Poinbase 的"永续"页,用账户里的 CRC 当保证金,PSC 最高 5 倍、SPARK 最高 3 倍,一个品种只有一个仓位,不能同时多空。成交价比现价差一截(单越大差得越多,和现货池一样),开平仓还收手续费。强平只在夜里按当天均价检查,权益不到名义价值的 10% 就被强平,保证金没了;每晚还有资金费,价格比近 7 天均价高时多头付给空头。强平后亏光保证金还不够的缺口,先由保险基金垫,基金见底(SPARK 崩盘这类大行情会抽干它)后盈利的仓位会被自动减仓、少拿一半盈利,再不够就记成你的欠款:欠款每天涨 2%,账户里的 CRC 每晚先拿去还,没还清不能开仓、不能出金,七天不还就没收店里的物品抵债,抵不够的核销并拉黑永续账户 30 天。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了Line truncated
|
item.web3guide.desc,"银行:存入把信用点换成 Poinbase 账户里的 CRC;提取把 CRC 换回信用点,要过合规审查,审查看 Poinbase 账户的污点。交易:买入按计价币的数量支付,卖出按所交易币的数量卖出。混币器:选币种、填数量和轮数,每轮扣手续费并降低污点,轮数即等待天数,到期提回同一个地址;存入越多,清洗越差。硬件钱包:链上的币放在 PLEDGER 硬件钱包里,每个钱包的余额和污点各算各的;在终端的钱包页打开、连接。Web3 页的交易、黑市、混币器用已连接的钱包,用币付款的顾客和挖矿收益也进它。付款(黑市、混币器、Ponero 兑换处、上架费)先在终端开账单,再在钱包里手输收款地址、按住 ✓ 签名;地址末尾两位是校验位,输错一位多半会被拦下,偶尔碰巧合法,币就转进了没人用的地址。DEX 兑换也要在钱包上签名。Poinbase 是托管账户,可以直接提到已连接的钱包;从钱包充值要转到 Poinbase 的充值地址,只收 CRC 和 PSC。更多硬件钱包向杰克逊博士购买,拖到交易终端上添加,最多 5 个;黑市上也有更便宜的。不要的钱包可以在钱包页销毁,要先把币转走。转账要付手续费,分慢、普通、快三档,gas 价格大约一周一个起落;网络拥堵时手续费变贵,慢档可能被挤出内存池。转账前核对完整地址。质押:把 CRA 质押进革命军质押池每天有日息,利息并入本金;取出要先申请,等四天才到账,等待期间没有利息,也不能提前拿。质押池靠补贴池付息,补贴池见底会降息,有时还会传出大户撤出的消息,那是预兆:一旦崩盘,三天内 CRA 就一文不值,四天的解锁期来不及。平衡代币 SPARK 可以和 CRA 按价格互相铸销(收手续费),价格波动很大,崩盘时会跟着暴跌。质押、铸销都是合约调用,要在钱包上签名。RatDigg 矿机(M1 / M2 / M3)是独立的机器,算力比显卡大得多、耗电更省,要放电池才挖,可以放模块,不占矿架槽位;只在黑市的矿机商那里有,要黑市声望(M1 40、M2 60、M3 80),偶尔有二手的。暴毙后要往修理槽里放电子元件按顺序修好,修好健康少 20。第 50 到 90 天之间会有一次"矿机时代",提前几天上报纸预告,之后大矿场的耗电成本砍半,全网算力涨上去,显卡矿工的收益被摊薄,低档显卡几乎不赚钱,矿机还行。永续合约:Poinbase 的"永续"页,用账户里的 CRC 当保证金,PSC 最高 5 倍、SPARK 最高 3 倍,一个品种只有一个仓位,不能同时多空。成交价比现价差一截(单越大差得越多,和现货池一样),开平仓还收手续费。强平只在夜里按当天均价检查,权益不到名义价值的 10% 就被强平,保证金没了;每晚还有资金费,价格比近 7 天均价高时多头付给空头。强平后亏光保证金还不够的缺口,先由保险基金垫,基金见底(SPARK 崩盘这类大行情会抽干它)后盈利的仓位会被自动减仓、少拿一半盈利,再不够就记成你的欠款:欠款每天涨 2%,账户里的 CRC 每晚先拿去还,没还清不能开仓、不能出金,七天不还就没收店里的物品抵债,抵不够的核销并拉黑永续账户 30 天。黑市:只收币,不收 CRC,价格比正常渠道贵 30%,高科技物品和显卡另有加价;付出去的币带着按比例的污点留在卖家那边,货不带赃物标签;显卡不保证真假,自己鉴定。订单比现货便宜,要等几天,路上可能被截获;商家按类型卖货,价格系数各不相同,一次订 5 件以上 95 折、10 件以上 9 折,大宗商家随机出现,价格最低但要等六天;托管多付 5% 托管费,收货后放款,卖家跑路时退款;提前放款再打九折,卖家若是骗子钱就没了,只有成交 30 单以上的卖家接受;平台自己也可能跑路,也可能被执法接管,或者卖家落网,之后你的订单会被追查、记罪证;论坛上的传闻可以当预兆。被截获的包裹有时会变成执法人员上门:签收当场记罪证并查封,拒签只损失货款。出售:在黑市的卖货页把储藏区里的违禁品、高科技物品和显卡上架,标价是参考价的 0.6 到 1.8 倍。倍率越高成交越慢(0.9 以下约一天,1.0 约两天,1.8 约十四天),货款带污点的可能性和比例也越高,近 7 天出售的量越大,污点越重;同时最多上架 10 件,上架收 5% 上架费,上架后可以改价(收 2% 改价费,标价按参考价重算,成交的快慢跟着新倍率走),成交扣 8% 抽成,货款托管 3 天后进钱包;卖假卡可能被买家看穿而退货,拉低好评;平台跑路时上架的货和未放款的货款都没了Line truncated
|
||||||
courier.odd1,……东西到了。
|
courier.odd1,……东西到了。
|
||||||
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
|
courier.odd2,"(他的手一直放在口袋里)在这儿签个字。"
|
||||||
courier.sign,签收
|
courier.sign,签收
|
||||||
@@ -345,7 +345,7 @@ black.edit.cancel,取消
|
|||||||
black.edit.range,"标价要在参考价的 0.6 到 1.8 倍之间({0} 到 {1})。"
|
black.edit.range,"标价要在参考价的 0.6 到 1.8 倍之间({0} 到 {1})。"
|
||||||
black.sell.reprice,改价
|
black.sell.reprice,改价
|
||||||
merchant.Platform,平台
|
merchant.Platform,平台
|
||||||
black.platform.line,"平台现货 立即到货 收款地址 {0}"
|
black.platform.line,平台现货 立即到货
|
||||||
black.orders.in,买入
|
black.orders.in,买入
|
||||||
black.orders.out,卖出
|
black.orders.out,卖出
|
||||||
black.desk.pair,"PONERO [匿名币] 价格 {0}"
|
black.desk.pair,"PONERO [匿名币] 价格 {0}"
|
||||||
@@ -783,3 +783,15 @@ yield.sub.pools,收益池
|
|||||||
yield.sub.mine,发行理财
|
yield.sub.mine,发行理财
|
||||||
pool.side.h,存入
|
pool.side.h,存入
|
||||||
ponzi.early,第 {0} 天起可发行高息理财。
|
ponzi.early,第 {0} 天起可发行高息理财。
|
||||||
|
bpay.h,付款
|
||||||
|
bpay.qty,数量
|
||||||
|
bpay.total,合计 {0} 信用点等值
|
||||||
|
bpay.coin,付款币种(商家收的币)
|
||||||
|
bpay.need,"需付 {0} {1} 钱包余额 {2}"
|
||||||
|
bpay.invoice,生成账单
|
||||||
|
bpay.swap.h,"自动换币为 {0}"
|
||||||
|
bpay.swap.none,钱包里没有足够的其他币种
|
||||||
|
bpay.swap.need,"付 {0} {1},含滑点 {2}%"
|
||||||
|
bpay.swap.btn,自动换币并付款
|
||||||
|
bpay.swap.sum,"{0}:自动换币,付 {2} {1},换成 {3} 后下单"
|
||||||
|
bpay.back,返回
|
||||||
@@ -990,7 +990,9 @@ namespace Decentralized
|
|||||||
var w = d.Wallets[i];
|
var w = d.Wallets[i];
|
||||||
int idx = i;
|
int idx = i;
|
||||||
bool isCurrent = idx == d.Current;
|
bool isCurrent = idx == d.Current;
|
||||||
b.BeginPanel(740f, 32f, TextAnchor.MiddleLeft, 4f, 0f);
|
// 一行三块:数据格(整行反色的是这一块)、操作键(和顶栏页签一样的黑色触控键,放在纸面上)、连接状态靠右
|
||||||
|
b.BeginRow(4f, TextAnchor.MiddleLeft);
|
||||||
|
b.BeginPanel(cols[0] + cols[1] + cols[2] + cols[3] + 8f, 32f, TextAnchor.MiddleLeft, 4f, 0f);
|
||||||
_rows.Add(new PledgerSkin.RowDef
|
_rows.Add(new PledgerSkin.RowDef
|
||||||
{
|
{
|
||||||
Root = b.Last.root,
|
Root = b.Last.root,
|
||||||
@@ -1003,8 +1005,9 @@ namespace Decentralized
|
|||||||
Cell(b, cols[1], w.Address, 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[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), false, TextAnchor.MiddleRight);
|
||||||
// 操作键一组(垂直居中、固定高度),连接状态单独靠右
|
b.End();
|
||||||
b.BeginPanel(cols[4] - 140f, 32f, TextAnchor.MiddleLeft, 0f, 0f);
|
b.End();
|
||||||
|
b.BeginPanel(cols[4] - 150f, 32f, TextAnchor.MiddleLeft, 0f, 0f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.BeginRow(4f, TextAnchor.MiddleLeft);
|
b.BeginRow(4f, TextAnchor.MiddleLeft);
|
||||||
RowKey(b, L.T("wallets.open"), () => WalletWindow.Open(w));
|
RowKey(b, L.T("wallets.open"), () => WalletWindow.Open(w));
|
||||||
@@ -1013,7 +1016,7 @@ namespace Decentralized
|
|||||||
if (idx > 0) RowKey(b, _destroyPick == w.Address ? L.T("wallets.destroy.sure") : L.T("wallets.destroy"), () => DestroyWallet(w));
|
if (idx > 0) RowKey(b, _destroyPick == w.Address ? L.T("wallets.destroy.sure") : L.T("wallets.destroy"), () => DestroyWallet(w));
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
b.BeginPanel(100f, 32f, TextAnchor.MiddleRight, 0f, 0f);
|
b.BeginPanel(84f, 32f, TextAnchor.MiddleRight, 0f, 0f);
|
||||||
_clear.Add(b.Last.image);
|
_clear.Add(b.Last.image);
|
||||||
b.BeginRow(0f, TextAnchor.MiddleRight);
|
b.BeginRow(0f, TextAnchor.MiddleRight);
|
||||||
if (idx != d.Current) RowKey(b, L.T("wallets.use"), () => { d.Current = idx; Session.Save(); Open(); });
|
if (idx != d.Current) RowKey(b, L.T("wallets.use"), () => { d.Current = idx; Session.Save(); Open(); });
|
||||||
@@ -1021,7 +1024,6 @@ namespace Decentralized
|
|||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
b.End();
|
||||||
b.End();
|
|
||||||
}
|
}
|
||||||
if (_renamePick != null) RenameRow(b);
|
if (_renamePick != null) RenameRow(b);
|
||||||
}
|
}
|
||||||
@@ -1033,7 +1035,7 @@ namespace Decentralized
|
|||||||
var go = b.Last.root;
|
var go = b.Last.root;
|
||||||
if (go == null) return;
|
if (go == null) return;
|
||||||
var le = go.GetComponent<LayoutElement>() ?? go.AddComponent<LayoutElement>();
|
var le = go.GetComponent<LayoutElement>() ?? go.AddComponent<LayoutElement>();
|
||||||
le.minHeight = 24f; le.preferredHeight = 24f; le.flexibleHeight = 0f;
|
le.minHeight = 26f; le.preferredHeight = 26f; le.flexibleHeight = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string _destroyPick, _renamePick, _renameText = "";
|
private static string _destroyPick, _renamePick, _renameText = "";
|
||||||
@@ -1146,7 +1148,6 @@ namespace Decentralized
|
|||||||
private static PayMode _mode = PayMode.Escrow;
|
private static PayMode _mode = PayMode.Escrow;
|
||||||
private static string _deskAsset = Assets.PSC;
|
private static string _deskAsset = Assets.PSC;
|
||||||
private static bool _deskIn = true;
|
private static bool _deskIn = true;
|
||||||
private static string _payAsset = Assets.CRT;
|
|
||||||
private static readonly System.Collections.Generic.Dictionary<string, Sprite> IconCache = new System.Collections.Generic.Dictionary<string, Sprite>();
|
private static readonly System.Collections.Generic.Dictionary<string, Sprite> IconCache = new System.Collections.Generic.Dictionary<string, Sprite>();
|
||||||
|
|
||||||
/// <summary>货架上的商品图:游戏物品自己的贴图(本 mod 的显卡用本 mod 的贴图)。</summary>
|
/// <summary>货架上的商品图:游戏物品自己的贴图(本 mod 的显卡用本 mod 的贴图)。</summary>
|
||||||
@@ -1178,60 +1179,46 @@ namespace Decentralized
|
|||||||
{
|
{
|
||||||
var tab = t;
|
var tab = t;
|
||||||
string name = L.T("black.sub." + t.ToString().ToLowerInvariant());
|
string name = L.T("black.sub." + t.ToString().ToLowerInvariant());
|
||||||
PledgerSkin.Pick(b, name, tab == _blackTab, () => { _blackTab = tab; _message = null; _edit = null; Open(); }, _picked);
|
PledgerSkin.Pick(b, name, tab == _blackTab, () => { _blackTab = tab; _message = null; _edit = null; _pay = null; Open(); }, _picked);
|
||||||
}
|
}
|
||||||
b.End();
|
b.End();
|
||||||
if (d.Black.Closed(day)) { b.AddLabel(L.T("black.rugged", d.Black.ClosedUntil - day + 1), "black_rugged_note"); return; }
|
if (d.Black.Closed(day)) { b.AddLabel(L.T("black.rugged", d.Black.ClosedUntil - day + 1), "black_rugged_note"); return; }
|
||||||
|
if (_pay != null && _blackTab != BlackTab.Buy) { PayPanel(b); return; }
|
||||||
if (_edit != null) { EditPage(b); return; }
|
if (_edit != null) { EditPage(b); return; }
|
||||||
if (_blackTab == BlackTab.Orders) { OrdersPage(b, day); return; }
|
if (_blackTab == BlackTab.Orders) { OrdersPage(b, day); return; }
|
||||||
if (_blackTab == BlackTab.Sell) { SellPage(b, day); return; }
|
if (_blackTab == BlackTab.Sell) { SellPage(b, day); return; }
|
||||||
BuyPage(b, d, rep);
|
BuyPage(b, d, rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>买货:选一家卖家(第一个是平台现货,立即到货但贵,其余是商家,便宜但要等),选件数,下单。论坛情报在最下面。</summary>
|
/// <summary>
|
||||||
|
/// 买货:左栏是卖家列表(第一个是平台现货,立即到货但贵,其余是商家,便宜但要等),右栏是这家的货架。
|
||||||
|
/// 点"购买"后右栏换成付款面板:选数量(和付款方式)、付款币种;商家不收手里的币时可以用 Web3 内置的自动换币(多付滑点)。论坛情报在最下面。
|
||||||
|
/// </summary>
|
||||||
private static void BuyPage(CustomUIBuilder b, SaveData d, int rep)
|
private static void BuyPage(CustomUIBuilder b, SaveData d, int rep)
|
||||||
{
|
{
|
||||||
var w = d.Hot;
|
var w = d.Hot;
|
||||||
var sellers = d.Black.Sellers;
|
var sellers = d.Black.Sellers;
|
||||||
if (_merchant >= sellers.Count) _merchant = -1;
|
if (_merchant >= sellers.Count) _merchant = -1;
|
||||||
bool platform = _merchant < 0;
|
bool platform = _merchant < 0;
|
||||||
b.AddLabel(L.T("black.rep", rep, BlackMarket.MaxLevel(rep)) + " " + L.T("mixer.balance", _payAsset, Fmt.Num(w.Avail(_payAsset))) + (w.Taint > 0.5 ? " " + L.T("wallet.taint", Fmt.Credits(w.Taint)) : ""));
|
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.BeginRow(4f);
|
b.BeginRow(8f, TextAnchor.UpperLeft);
|
||||||
foreach (var a in new[] { Assets.PSC, Assets.CRT, Assets.CRA, Assets.Ponero })
|
b.BeginPanel(SellerW + 8f, -1f, TextAnchor.UpperLeft, 0f, 2f);
|
||||||
|
_clear.Add(b.Last.image);
|
||||||
|
var names = new System.Collections.Generic.List<string> { L.T("merchant.Platform") };
|
||||||
|
for (int k = 0; k < sellers.Count; k++) names.Add(MerchantLabel(sellers[k], true));
|
||||||
|
for (int i = 0; i < names.Count; i++)
|
||||||
{
|
{
|
||||||
var asset = a;
|
int idx = i - 1;
|
||||||
PledgerSkin.Pick(b, asset, asset == _payAsset, () => { _payAsset = asset; Open(); }, _picked);
|
TableRow(b, SellerW, new[] { SellerW - 8f }, new[] { TextAnchor.MiddleLeft }, new[] { names[i] }, idx == _merchant, () => { _merchant = idx; _pay = null; _message = null; Open(); });
|
||||||
}
|
|
||||||
if (!platform)
|
|
||||||
foreach (PayMode m in Enum.GetValues(typeof(PayMode)))
|
|
||||||
{
|
|
||||||
var mode = m;
|
|
||||||
string n = L.T("black.mode." + m);
|
|
||||||
PledgerSkin.Pick(b, n, mode == _mode, () => { _mode = mode; Open(); }, _picked);
|
|
||||||
}
|
|
||||||
b.End();
|
|
||||||
b.BeginRow(4f);
|
|
||||||
foreach (var m in Qtys)
|
|
||||||
{
|
|
||||||
int q = m;
|
|
||||||
PledgerSkin.Pick(b, "×" + q, q == _qty, () => { _qty = q; Open(); }, _picked);
|
|
||||||
}
|
}
|
||||||
b.End();
|
b.End();
|
||||||
var all = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<int, string>> { new System.Collections.Generic.KeyValuePair<int, string>(-1, L.T("merchant.Platform") + " " + d.Address("platform")) };
|
b.BeginPanel(ShelfW, -1f, TextAnchor.UpperLeft, 0f, 4f);
|
||||||
for (int k = 0; k < sellers.Count; k++) all.Add(new System.Collections.Generic.KeyValuePair<int, string>(k, MerchantLabel(sellers[k])));
|
_clear.Add(b.Last.image);
|
||||||
for (int i = 0; i < all.Count; i += 3)
|
if (_pay != null) PayPanel(b);
|
||||||
{
|
else if (platform) PlatformShelf(b, d);
|
||||||
b.BeginRow(4f);
|
|
||||||
for (int k = i; k < Math.Min(all.Count, i + 3); k++)
|
|
||||||
{
|
|
||||||
int idx = all[k].Key; string n = all[k].Value;
|
|
||||||
PledgerSkin.Pick(b, n, idx == _merchant, () => { _merchant = idx; Open(); }, _picked);
|
|
||||||
}
|
|
||||||
b.End();
|
|
||||||
}
|
|
||||||
b.AddSeparator();
|
|
||||||
if (platform) PlatformShelf(b, d);
|
|
||||||
else MerchantShelf(b, d);
|
else MerchantShelf(b, d);
|
||||||
|
b.End();
|
||||||
|
b.End();
|
||||||
b.AddSeparator();
|
b.AddSeparator();
|
||||||
b.AddHeader(L.T("black.forum.h"));
|
b.AddHeader(L.T("black.forum.h"));
|
||||||
if (d.Black.Forum.Count == 0) b.AddLabel(L.T("black.forum.quiet"), "forum_quiet_dim");
|
if (d.Black.Forum.Count == 0) b.AddLabel(L.T("black.forum.quiet"), "forum_quiet_dim");
|
||||||
@@ -1239,18 +1226,153 @@ namespace Decentralized
|
|||||||
b.AddLabel(f.StartsWith("phish:") ? L.T("black.forum.phish", f.Substring(6)) : f.StartsWith("scam:") ? L.T("black.forum.scam", BlackDelivery.DisplayName(f.Substring(5))) : f.StartsWith("busted:") ? L.T("black.forum.busted", f.Substring(7)) : L.T("black.forum." + f), "forum_dim");
|
b.AddLabel(f.StartsWith("phish:") ? L.T("black.forum.phish", f.Substring(6)) : f.StartsWith("scam:") ? L.T("black.forum.scam", BlackDelivery.DisplayName(f.Substring(5))) : f.StartsWith("busted:") ? L.T("black.forum.busted", f.Substring(7)) : L.T("black.forum." + f), "forum_dim");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const float SellerW = 176f, ShelfW = 560f;
|
||||||
|
|
||||||
private static void PlatformShelf(CustomUIBuilder b, SaveData d)
|
private static void PlatformShelf(CustomUIBuilder b, SaveData d)
|
||||||
{
|
{
|
||||||
b.AddLabel(L.T("black.platform.line", d.Address("platform")), "platform_line_dim");
|
b.AddLabel(L.T("black.platform.line"), "platform_line_dim");
|
||||||
if (d.Black.Offers.Count == 0) { b.AddLabel(L.T("black.empty"), "platform_empty_dim"); return; }
|
if (d.Black.Offers.Count == 0) { b.AddLabel(L.T("black.empty"), "platform_empty_dim"); return; }
|
||||||
Shelf(b, d.Black.Offers.Cast<BmGood>().ToList(), (c, g) =>
|
Shelf(b, d.Black.Offers.Cast<BmGood>().ToList(), (c, g) =>
|
||||||
{
|
{
|
||||||
var offer = (BmOffer)g;
|
var offer = (BmOffer)g;
|
||||||
int count = Math.Min(_qty, offer.Qty);
|
|
||||||
GoodHead(c, g, L.T("black.stock", offer.Qty));
|
GoodHead(c, g, L.T("black.stock", offer.Qty));
|
||||||
c.AddLabel(L.T("black.price", offer.Price * count) + (count > 1 ? " ×" + count : ""));
|
c.AddLabel(L.T("black.price", offer.Price));
|
||||||
c.AddFlexibleSpace();
|
c.AddFlexibleSpace();
|
||||||
c.AddButton(L.T("black.buy"), () => { string asset = _payAsset; BeginPay(d.Address("platform"), ItemName(offer.Item) + (count > 1 ? " ×" + count : ""), asset, offer.Price * count, pw => BuyOffer(offer, count, asset, pw)); });
|
c.AddButton(L.T("black.buy"), () => OpenPay(new PayDraft
|
||||||
|
{
|
||||||
|
Title = ItemName(offer.Item), Address = d.Address("platform"), Accepted = Merchants.PlatformAccepts, MaxQty = offer.Qty,
|
||||||
|
Credits = (q, m) => offer.Price * q,
|
||||||
|
Execute = (asset, q, m, pw) => BuyOffer(offer, q, asset, pw),
|
||||||
|
}));
|
||||||
|
}, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ——— 付款面板 ———
|
||||||
|
|
||||||
|
/// <summary>一次付款的草稿:买什么、收款地址、商家收哪些币、可选数量与付款方式、合计,以及付款成功后做的事。</summary>
|
||||||
|
private sealed class PayDraft
|
||||||
|
{
|
||||||
|
public string Title, Address, Asset, SwapFrom;
|
||||||
|
public string[] Accepted;
|
||||||
|
public int MaxQty = 1, Qty = 1;
|
||||||
|
public bool Modes;
|
||||||
|
public PayMode Mode = PayMode.Escrow;
|
||||||
|
public Func<int, PayMode, double> Credits;
|
||||||
|
public Action<string, int, PayMode, Wallet> Execute;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PayDraft _pay;
|
||||||
|
|
||||||
|
private static void OpenPay(PayDraft p)
|
||||||
|
{
|
||||||
|
var w = Session.Data.Hot;
|
||||||
|
p.Asset = p.Accepted.FirstOrDefault(a => w.Avail(a) * Session.Price(a) >= p.Credits(1, p.Mode)) ?? p.Accepted[0];
|
||||||
|
p.SwapFrom = null;
|
||||||
|
_pay = p;
|
||||||
|
_message = null;
|
||||||
|
Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Summary(PayDraft p) => p.Title + (p.Qty > 1 ? " ×" + p.Qty : "");
|
||||||
|
|
||||||
|
/// <summary>付款面板:数量、付款方式、合计、付款币种(商家收的币)、生成账单;下面是自动换币。</summary>
|
||||||
|
private static void PayPanel(CustomUIBuilder b)
|
||||||
|
{
|
||||||
|
var d = Session.Data;
|
||||||
|
var w = d.Hot;
|
||||||
|
var p = _pay;
|
||||||
|
if (p.Qty > p.MaxQty) p.Qty = p.MaxQty;
|
||||||
|
b.AddHeader(L.T("bpay.h"));
|
||||||
|
b.AddLabel(p.Title, "bpay_title");
|
||||||
|
if (p.MaxQty > 1)
|
||||||
|
{
|
||||||
|
b.BeginRow(4f);
|
||||||
|
b.AddLabel(L.T("bpay.qty"));
|
||||||
|
foreach (var q in Qtys)
|
||||||
|
{
|
||||||
|
if (q > p.MaxQty && q != Qtys[0]) continue;
|
||||||
|
int qq = q;
|
||||||
|
PledgerSkin.Pick(b, "×" + qq, qq == p.Qty, () => { p.Qty = qq; Open(); }, _picked);
|
||||||
|
}
|
||||||
|
b.End();
|
||||||
|
}
|
||||||
|
if (p.Modes)
|
||||||
|
{
|
||||||
|
b.BeginRow(4f);
|
||||||
|
foreach (PayMode m in Enum.GetValues(typeof(PayMode)))
|
||||||
|
{
|
||||||
|
var mode = m;
|
||||||
|
PledgerSkin.Pick(b, L.T("black.mode." + m), mode == p.Mode, () => { p.Mode = mode; Open(); }, _picked);
|
||||||
|
}
|
||||||
|
b.End();
|
||||||
|
}
|
||||||
|
double credits = p.Credits(p.Qty, p.Mode);
|
||||||
|
b.AddLabel(L.T("bpay.total", Fmt.Credits(credits)), "bpay_total");
|
||||||
|
b.AddSeparator();
|
||||||
|
b.AddLabel(L.T("bpay.coin"), "bpay_coin_dim");
|
||||||
|
b.BeginRow(4f);
|
||||||
|
foreach (var a in p.Accepted)
|
||||||
|
{
|
||||||
|
string asset = a;
|
||||||
|
PledgerSkin.Pick(b, asset, asset == p.Asset, () => { p.Asset = asset; Open(); }, _picked);
|
||||||
|
}
|
||||||
|
b.End();
|
||||||
|
double need = WalletWindow.RoundUp(credits / Session.Price(p.Asset));
|
||||||
|
b.AddLabel(L.T("bpay.need", WalletWindow.Amt(need), p.Asset, Fmt.Num(w.Avail(p.Asset))), "bpay_need_dim");
|
||||||
|
b.AddButton(L.T("bpay.invoice"), () =>
|
||||||
|
{
|
||||||
|
string asset = p.Asset; int q = p.Qty; var mode = p.Mode;
|
||||||
|
string summary = Summary(p);
|
||||||
|
_pay = null;
|
||||||
|
BeginPay(p.Address, summary, asset, credits, pw => p.Execute(asset, q, mode, pw));
|
||||||
|
});
|
||||||
|
b.AddSeparator();
|
||||||
|
// 自动换币:从钱包里别的币里换出商家收的币,多付滑点;是合约调用,签名后一步完成
|
||||||
|
b.AddLabel(L.T("bpay.swap.h", p.Asset), "bpay_swap_dim");
|
||||||
|
double slipped = credits * (1 + Tuning.BmAutoSwapSlip);
|
||||||
|
var sources = w.Balances.Keys.Where(a => !p.Accepted.Contains(a) && a != Assets.Ponero && Session.Price(a) > 0 && w.Avail(a) * Session.Price(a) >= slipped).ToList();
|
||||||
|
if (sources.Count == 0) b.AddLabel(L.T("bpay.swap.none"), "bpay_swap_none_dim");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p.SwapFrom == null || !sources.Contains(p.SwapFrom)) p.SwapFrom = sources[0];
|
||||||
|
b.BeginRow(4f);
|
||||||
|
foreach (var s in sources)
|
||||||
|
{
|
||||||
|
string src = s;
|
||||||
|
PledgerSkin.Pick(b, src, src == p.SwapFrom, () => { p.SwapFrom = src; Open(); }, _picked);
|
||||||
|
}
|
||||||
|
b.End();
|
||||||
|
double needSrc = WalletWindow.RoundUp(slipped / Session.Price(p.SwapFrom));
|
||||||
|
b.AddLabel(L.T("bpay.swap.need", WalletWindow.Amt(needSrc), p.SwapFrom, (Tuning.BmAutoSwapSlip * 100).ToString("0", CultureInfo.InvariantCulture)), "bpay_swap_need_dim");
|
||||||
|
b.AddButton(L.T("bpay.swap.btn"), () => SwapAndPay(p, credits, needSrc));
|
||||||
|
}
|
||||||
|
b.AddSpace(4f);
|
||||||
|
b.AddButton(L.T("bpay.back"), () => { _pay = null; _message = null; Open(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>自动换币并付款:一次合约调用,先把别的币按滑点换成商家收的币,再下单。</summary>
|
||||||
|
private static void SwapAndPay(PayDraft p, double credits, double needSrc)
|
||||||
|
{
|
||||||
|
var d = Session.Data;
|
||||||
|
string src = p.SwapFrom, asset = p.Asset; int q = p.Qty; var mode = p.Mode;
|
||||||
|
_pay = null;
|
||||||
|
_message = L.T("hw.wait");
|
||||||
|
Open();
|
||||||
|
WalletWindow.OpenForBill(new WalletWindow.Bill
|
||||||
|
{
|
||||||
|
Contract = true, Asset = src, Amount = needSrc, Spend = needSrc,
|
||||||
|
Summary = L.T("bpay.swap.sum", Summary(p), src, WalletWindow.Amt(needSrc), asset),
|
||||||
|
Execute = pw =>
|
||||||
|
{
|
||||||
|
if (pw.Avail(src) + 1e-9 < needSrc) { _message = L.T("msg.balance"); Open(); return; }
|
||||||
|
double got = credits / Session.Price(asset) * 1.002;
|
||||||
|
pw.Add(src, -needSrc);
|
||||||
|
pw.Add(asset, got);
|
||||||
|
pw.RecordBuy(asset, got, credits);
|
||||||
|
CandleChart.Trade(src, credits); CandleChart.Trade(asset, credits);
|
||||||
|
Log.Info($"自动换币:{needSrc:0.####} {src} → {got:0.####} {asset}({credits:0} 信用点等值)");
|
||||||
|
p.Execute(asset, q, mode, pw);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1269,11 +1391,16 @@ namespace Decentralized
|
|||||||
PledgerSkin.Pick(b, "×" + n, Math.Abs(mult - _sellMult) < 1e-9, () => { _sellMult = mult; Open(); }, _picked);
|
PledgerSkin.Pick(b, "×" + n, Math.Abs(mult - _sellMult) < 1e-9, () => { _sellMult = mult; Open(); }, _picked);
|
||||||
}
|
}
|
||||||
b.End();
|
b.End();
|
||||||
b.AddLabel(L.T("mixer.balance", _payAsset, Fmt.Num(d.Hot.Avail(_payAsset))) + " " + L.T("black.sell.rating", d.Black.Rating) + " " + L.T("black.sell.slots", d.Black.ActiveListings, Tuning.BmMaxListings));
|
b.AddLabel(L.T("black.sell.rating", d.Black.Rating) + " " + L.T("black.sell.slots", d.Black.ActiveListings, Tuning.BmMaxListings), "sell_status_dim");
|
||||||
b.AddButton(L.T("black.sell.all"), () =>
|
b.AddButton(L.T("black.sell.all"), () =>
|
||||||
{
|
{
|
||||||
string asset = _payAsset; double mult = _sellMult;
|
double mult = _sellMult;
|
||||||
BeginPay(d.Address("platform"), L.T("black.sell.all") + L.T("pay.listfee", mult.ToString("0.0", CultureInfo.InvariantCulture)), asset, BlackDelivery.Sellable().Sum(it => BlackMarket.ListFee(BlackMarket.ListPriceOf(BlackDelivery.RefValue(it), mult))), pw => ListAll(asset, mult, pw));
|
double fee = BlackDelivery.Sellable().Sum(it => BlackMarket.ListFee(BlackMarket.ListPriceOf(BlackDelivery.RefValue(it), mult)));
|
||||||
|
OpenPay(new PayDraft
|
||||||
|
{
|
||||||
|
Title = L.T("black.sell.all") + L.T("pay.listfee", mult.ToString("0.0", CultureInfo.InvariantCulture)), Address = d.Address("platform"), Accepted = Merchants.PlatformAccepts,
|
||||||
|
Credits = (q, m) => fee, Execute = (asset, q, m, pw) => ListAll(asset, mult, pw),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
b.AddSeparator();
|
b.AddSeparator();
|
||||||
var items = BlackDelivery.Sellable();
|
var items = BlackDelivery.Sellable();
|
||||||
@@ -1331,47 +1458,45 @@ namespace Decentralized
|
|||||||
Open();
|
Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string MerchantLabel(BmSeller s) => L.T("merchant." + s.Type) + " " + s.Name;
|
private static string MerchantLabel(BmSeller s, bool shortName = false) => L.T("merchant." + s.Type) + " " + (shortName ? ChainAddress.Short(s.Name) : s.Name);
|
||||||
|
|
||||||
private static int _merchant = -1;
|
private static int _merchant = -1;
|
||||||
private static int _qty = 1;
|
|
||||||
private static readonly int[] Qtys = { 1, 5, 10 };
|
private static readonly int[] Qtys = { 1, 5, 10 };
|
||||||
|
|
||||||
/// <summary>商家货架:选一家商家,看它这周的货,选件数下单。</summary>
|
/// <summary>商家货架:这家的成交数、好评和到货天数,下面是这周的货;点"购买"进付款面板。</summary>
|
||||||
private static void MerchantShelf(CustomUIBuilder b, SaveData d)
|
private static void MerchantShelf(CustomUIBuilder b, SaveData d)
|
||||||
{
|
{
|
||||||
var sellers = d.Black.Sellers;
|
var sellers = d.Black.Sellers;
|
||||||
var seller = sellers[_merchant];
|
var seller = sellers[_merchant];
|
||||||
b.AddLabel(L.T("black.seller", MerchantLabel(seller), seller.Trades, seller.Rating) + " " + L.T("black.days", seller.Days));
|
int merchant = _merchant;
|
||||||
var goods = d.Black.Catalog.Where(l => l.Seller == _merchant).Cast<BmGood>().ToList();
|
b.AddLabel(L.T("black.seller", MerchantLabel(seller), seller.Trades, seller.Rating) + " " + L.T("black.days", seller.Days), "black_seller_dim");
|
||||||
|
var goods = d.Black.Catalog.Where(l => l.Seller == merchant).Cast<BmGood>().ToList();
|
||||||
if (goods.Count == 0) { b.AddLabel(L.T("black.empty"), "shelf_empty_dim"); return; }
|
if (goods.Count == 0) { b.AddLabel(L.T("black.empty"), "shelf_empty_dim"); return; }
|
||||||
Shelf(b, goods, (c, g) =>
|
Shelf(b, goods, (c, g) =>
|
||||||
{
|
{
|
||||||
var listing = (BmListing)g;
|
var listing = (BmListing)g;
|
||||||
int count = Math.Min(_qty, listing.Qty);
|
|
||||||
GoodHead(c, g, L.T("black.stock", listing.Qty));
|
GoodHead(c, g, L.T("black.stock", listing.Qty));
|
||||||
c.AddLabel(L.T("black.price", BlackMarket.Pay(BlackMarket.OrderTotal(listing.Price, count), _mode)) + (count > 1 ? " ×" + count : ""));
|
c.AddLabel(L.T("black.price", BlackMarket.Pay(BlackMarket.OrderTotal(listing.Price, 1), PayMode.Escrow)));
|
||||||
c.AddFlexibleSpace();
|
c.AddFlexibleSpace();
|
||||||
bool ok = _mode == PayMode.Escrow || seller.Advance;
|
c.AddButton(L.T("black.buy"), () => OpenPay(new PayDraft
|
||||||
c.AddButton(ok ? L.T("black.order") : L.T("black.noadvance"), () =>
|
|
||||||
{
|
{
|
||||||
if (!ok) return;
|
Title = ItemName(listing.Item), Address = seller.Name, Accepted = Merchants.Accepts(seller.Type), MaxQty = listing.Qty, Modes = seller.Advance,
|
||||||
string asset = _payAsset; var mode = _mode;
|
Credits = (q, m) => BlackMarket.Pay(BlackMarket.OrderTotal(listing.Price, q), m),
|
||||||
BeginPay(seller.Name, ItemName(listing.Item) + (count > 1 ? " ×" + count : ""), asset, BlackMarket.Pay(BlackMarket.OrderTotal(listing.Price, count), mode), pw => PlaceOrder(listing, count, asset, mode, pw));
|
Execute = (asset, q, m, pw) => PlaceOrder(listing, q, asset, m, pw),
|
||||||
});
|
}));
|
||||||
});
|
}, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int ShelfCols = 4;
|
private const int ShelfCols = 4;
|
||||||
private const float CardW = 172f, CardH = 268f;
|
private const float CardW = 172f, CardH = 268f;
|
||||||
|
|
||||||
/// <summary>货架:一行 4 张商品卡。</summary>
|
/// <summary>货架:一行 4 张商品卡。</summary>
|
||||||
private static void Shelf(CustomUIBuilder b, System.Collections.Generic.List<BmGood> goods, Action<CustomUIBuilder, BmGood> card)
|
private static void Shelf(CustomUIBuilder b, System.Collections.Generic.List<BmGood> goods, Action<CustomUIBuilder, BmGood> card, int cols = ShelfCols)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < goods.Count; i += ShelfCols)
|
for (int i = 0; i < goods.Count; i += cols)
|
||||||
{
|
{
|
||||||
b.BeginRow(6f);
|
b.BeginRow(6f);
|
||||||
for (int k = i; k < Math.Min(goods.Count, i + ShelfCols); k++)
|
for (int k = i; k < Math.Min(goods.Count, i + cols); k++)
|
||||||
{
|
{
|
||||||
var g = goods[k];
|
var g = goods[k];
|
||||||
b.BeginPanel(CardW, CardH, TextAnchor.UpperCenter, 6f, 3f);
|
b.BeginPanel(CardW, CardH, TextAnchor.UpperCenter, 6f, 3f);
|
||||||
@@ -1497,9 +1622,12 @@ namespace Decentralized
|
|||||||
var d = Session.Data;
|
var d = Session.Data;
|
||||||
int fee = e.Sale != null ? BlackMarket.RepriceFee(price) : BlackMarket.ListFee(price);
|
int fee = e.Sale != null ? BlackMarket.RepriceFee(price) : BlackMarket.ListFee(price);
|
||||||
_edit = null;
|
_edit = null;
|
||||||
string asset = _payAsset;
|
OpenPay(new PayDraft
|
||||||
BeginPay(d.Address("platform"), ItemName(e.Id) + L.T(e.Sale != null ? "pay.repricefee" : "pay.listfee", mult.ToString("0.00", CultureInfo.InvariantCulture)), asset, fee,
|
{
|
||||||
pw => { if (e.Sale != null) RepriceSale(e.Sale, mult, asset, pw); else ListItem(e.Item, mult, asset, pw); });
|
Title = ItemName(e.Id) + L.T(e.Sale != null ? "pay.repricefee" : "pay.listfee", mult.ToString("0.00", CultureInfo.InvariantCulture)), Address = d.Address("platform"), Accepted = Merchants.PlatformAccepts,
|
||||||
|
Credits = (q, m) => fee,
|
||||||
|
Execute = (asset, q, m, pw) => { if (e.Sale != null) RepriceSale(e.Sale, mult, asset, pw); else ListItem(e.Item, mult, asset, pw); },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
b.AddButton(L.T("black.edit.cancel"), () => { _edit = null; _message = null; Open(); });
|
b.AddButton(L.T("black.edit.cancel"), () => { _edit = null; _message = null; Open(); });
|
||||||
b.End();
|
b.End();
|
||||||
|
|||||||
@@ -1504,6 +1504,17 @@ internal static class Program
|
|||||||
finally { StoryMods.Current = saved; }
|
finally { StoryMods.Current = saved; }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Test("黑市商家收币:每类商家都收 CRT 或匿名币,平台收 PSC、CRT、CRA", () =>
|
||||||
|
{
|
||||||
|
foreach (MerchantType ty in Enum.GetValues(typeof(MerchantType)))
|
||||||
|
{
|
||||||
|
var acc = Merchants.Accepts(ty);
|
||||||
|
Check(acc.Length >= 2 && acc.Distinct().Count() == acc.Length && acc.All(a => a == Assets.PSC || a == Assets.CRT || a == Assets.CRA || a == Assets.Ponero), $"{ty} 收 {string.Join("/", acc)}");
|
||||||
|
}
|
||||||
|
Check(Merchants.Accepts(MerchantType.Arms).Contains(Assets.Ponero) && !Merchants.Accepts(MerchantType.Rigs).Contains(Assets.Ponero), "违禁类收匿名币,硬件类不收");
|
||||||
|
Check(Merchants.PlatformAccepts.Contains(Assets.CRT) && Tuning.BmAutoSwapSlip > Tuning.DexFee * 10, "自动换币的滑点远高于 DEX 手续费");
|
||||||
|
});
|
||||||
|
|
||||||
Test("预测市场:先验概率", () =>
|
Test("预测市场:先验概率", () =>
|
||||||
{
|
{
|
||||||
Check(Math.Abs(PredictMath.PurityAtLeast(8500, 8000, 8999) - 0.5) < 1e-9, "纯度 8500 以上一半");
|
Check(Math.Abs(PredictMath.PurityAtLeast(8500, 8000, 8999) - 0.5) < 1e-9, "纯度 8500 以上一半");
|
||||||
|
|||||||
Reference in new issue
Block a user