Prediction markets use on-chain wallets (signed contract calls) instead of the Poinbase account

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-04 07:33:42 +08:00
1 parent e5a6e3e22c
commit 723724aa4b
8 files changed
+138 -73

No files matched your search

+44 -18
View File
@@ -350,14 +350,13 @@ namespace Decentralized
}
private static int _predPick;
private static readonly float[] PredW = { 330f, 90f };
/// <summary>预测页:左边盘口列表和最近结果,右边选中盘口的买卖。资金走 Poinbase 的 CRC 账户,不用签名。</summary>
/// <summary>预测页:左边盘口列表和最近结果,右边选中盘口的买卖。下注和卖出是链上合约调用,用当前钱包的 CRC,要在钱包上签名;持仓记在签名的钱包名下。</summary>
private static void PredictPage(CustomUIBuilder b)
{
var d = Session.Data;
var bk = d.Predict;
var acct = d.Poinbase;
var w = d.Hot;
int day = StoreStation.GetDayCounter();
var open = bk.Markets.Where(m => m.Outcome < 0).OrderBy(m => m.FromDay).ThenBy(m => m.Id).ToList();
var sel = open.Find(m => m.Id == _predPick) ?? open.FirstOrDefault();
@@ -365,19 +364,19 @@ namespace Decentralized
Columns(b, l =>
{
l.AddHeader(L.T("predict.h"));
l.AddLabel(L.T("predict.acct", Fmt.Num(acct.Avail(Assets.CRC))));
l.AddLabel(L.T("predict.acct", WalletWindow.NameOf(w), Fmt.Num(w.Avail(Assets.CRC))));
l.AddSeparator();
if (open.Count == 0) l.AddLabel(L.T("predict.none"));
foreach (var m in open)
{
var mk = m;
var pos = bk.PosOf(m.Id);
var all = bk.PosAll(m.Id);
string q = PredictGame.Question(m);
l.BeginRow(4f);
l.AddButton(m.Id == _predPick ? "[" + q + "]" : q, () => { _predPick = mk.Id; _message = null; Open(); });
l.End();
string state = m.IsOpen(day) ? "" : " " + L.T("predict.closed");
l.AddLabel(L.T("predict.row", (m.PYes * 100).ToString("0"), ((1 - m.PYes) * 100).ToString("0")) + (pos != null ? " " + L.T("predict.held", L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares)) : "") + state);
l.AddLabel(L.T("predict.row", (m.PYes * 100).ToString("0"), ((1 - m.PYes) * 100).ToString("0")) + (all.Count > 0 ? " " + L.T("predict.held", L.T(all[0].Yes ? "predict.yes" : "predict.no"), Fmt.Num(all.Sum(x => x.Shares))) : "") + state);
}
if (bk.Recent.Count > 0)
{
@@ -391,12 +390,13 @@ namespace Decentralized
r.AddHeader(L.T("predict.pick"));
r.AddLabel(PredictGame.Question(sel));
r.AddLabel(L.T("predict.price", (sel.PYes * 100).ToString("0.0"), ((1 - sel.PYes) * 100).ToString("0.0")));
var pos = bk.PosOf(sel.Id);
if (pos != null)
foreach (var pos in bk.PosAll(sel.Id))
{
r.AddLabel(L.T("predict.pos", L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares), Fmt.Num(pos.Cost)));
r.AddLabel(L.T("predict.pos2", Fmt.Num(sel.SellValue(pos.Yes, pos.Shares) * (1 - Tuning.PredictFee))));
var ow = d.Own(pos.Wallet);
r.AddLabel(L.T("predict.pos", ow != null ? WalletWindow.NameOf(ow) : pos.Wallet, L.T(pos.Yes ? "predict.yes" : "predict.no"), Fmt.Num(pos.Shares)));
}
var mine = bk.PosOf(sel.Id, w.Address);
if (mine != null) r.AddLabel(L.T("predict.pos2", Fmt.Num(mine.Cost), Fmt.Num(sel.SellValue(mine.Yes, mine.Shares) * (1 - Tuning.PredictFee))));
if (!sel.IsOpen(day)) { r.AddLabel(L.T("predict.closed.note")); return; }
AmountRow(r);
if (double.TryParse((_amount ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double v) && v > 0 && v < 1e7)
@@ -419,21 +419,47 @@ namespace Decentralized
{
if (!ReadAmount(out double v)) return;
var d = Session.Data;
int day = StoreStation.GetDayCounter();
bool insider = PredictGame.Known(m, yes);
var res = d.Predict.Buy(d.Poinbase, m, yes, v, day, Session.Price, insider, out double shares);
_message = res == PredictResult.Ok ? L.T("predict.bought", L.T(yes ? "predict.yes" : "predict.no"), Fmt.Num(shares), Fmt.Num(v)) : L.T("predict.err." + res);
if (res == PredictResult.Ok) { Log.Info($"预测下注 #{m.Id} {(yes ? "是" : "否")} {shares:0.0} 份,花 {v:0.##},内幕 {insider}"); Session.Save(); }
var w = d.Hot;
if (w.Avail(Assets.CRC) + 1e-9 < v) { _message = L.T("predict.err.Funds"); Open(); return; }
if (d.Predict.PosAll(m.Id).Any(x => x.Yes != yes)) { _message = L.T("predict.err.Opposite"); Open(); return; }
string side = L.T(yes ? "predict.yes" : "predict.no");
_message = L.T("hw.wait");
Open();
WalletWindow.OpenForBill(new WalletWindow.Bill
{
Contract = true, Asset = Assets.CRC, Amount = v,
Summary = L.T("predict.sum.buy", PredictGame.Question(m), side, Fmt.Num(v)),
Execute = pw =>
{
int day = StoreStation.GetDayCounter();
bool insider = PredictGame.Known(m, yes);
var res = d.Predict.Buy(pw, m, yes, v, day, Session.Price, insider, out double shares);
_message = res == PredictResult.Ok ? L.T("predict.bought", side, Fmt.Num(shares), Fmt.Num(v)) : L.T("predict.err." + res);
if (res == PredictResult.Ok) { Log.Info($"预测下注 #{m.Id} {(yes ? "是" : "否")} {shares:0.0} 份,花 {v:0.##},钱包 {pw.Address},内幕 {insider}"); Session.Save(); }
Open();
},
});
}
private static void PredictSell(PredictMarket m)
{
var d = Session.Data;
var res = d.Predict.SellAll(d.Poinbase, m, StoreStation.GetDayCounter(), out double got);
_message = res == PredictResult.Ok ? L.T("predict.sold", Fmt.Num(got)) : L.T("predict.err." + res);
if (res == PredictResult.Ok) Session.Save();
var w = d.Hot;
if (d.Predict.PosOf(m.Id, w.Address) == null) { _message = L.T("predict.err.NoPosition"); Open(); return; }
_message = L.T("hw.wait");
Open();
WalletWindow.OpenForBill(new WalletWindow.Bill
{
Contract = true, Asset = Assets.CRC, Amount = 0, Spend = 0, FeeAsset = BestFeeAsset(w),
Summary = L.T("predict.sum.sell", PredictGame.Question(m)),
Execute = pw =>
{
var res = d.Predict.SellAll(pw, m, StoreStation.GetDayCounter(), out double got);
_message = res == PredictResult.Ok ? L.T("predict.sold", Fmt.Num(got)) : L.T("predict.err." + res);
if (res == PredictResult.Ok) Session.Save();
Open();
},
});
}
private static string PerpErr(PerpResult res) => L.T("perp.err." + res);