On-chain
- Attribution.
predict_facade::attribute_*is wired in two places: the resolver’s settlement PTB (buildAtomicSettlementPTB→attribute_redeem, used by both the HTTP endpoint and the keeper) andscripts/supply-plp.ts(attribute_plp_supply). Frontend and agent-service mints are unattributed, and no indexer stream consumes facade events. Wiring those is a republish-era follow-up. - Predict pin. Predict is pinned to
predict-testnet-4-16. Never referencemain. The branch will shift before mainnet. - DUSDC is the only quote asset Predict accepts (DeepBook-allowlisted):
0xe95040085976bfd54a1a07225cd46c8a2b4e8e2b6732f140a0fc49850ba73e1a::dusdc::DUSDC. - Binary market = one Move publish. Slug must match
/^[a-z0-9_]+$/. Registry on disk (.binary-markets/registry.json) is authoritative. agent::pause/resumeemit no events (verified atagent.move:215); serverpausedcolumn is stuck at the registration default. Authoritative source for an agent’s active state issui.getObject(TradeAgent).is_active. FrontendOwnerControlsand the agent-service tick guard both read it live. Do not trust the API field.- Vault settlement sweep must pull idle vault balance. Receipt redemption pays from
final_payout_microonly, so idle DUSDC left invault.balanceafter the keeper’s final tick would be stranded forever. Encoded inscripts/keeper-tick.ts. - Predict strike grid is
$1.$63,601quotes,$63,600.50aborts. Baselines snap to the nearest dollar (≤50¢ from the true open). Probe-based strike pickers walk $50 rungs. - No DEEP needed for users. All V3 swap PTBs use
pay_with_deep=false(fees in the input token at the penalty multiplier). Quote estimates come fromget_quantity_out_input_feeso the math matches. - 5% fee headroom on every V3 quoting leg (keeper + seeder).
place_limit_orderwithpay_with_deep=falselocks qty + input-token fee at placement, so quoting 100% of holdings abortsEBalanceManagerBalanceTooLow. One shared convention. - Auto-settle keeper redeems one position per PTB (not batched). Memoises tried tuples: success = never re-fire, failure = 30 min backoff. Always self-pays (Enoki allow-list rejects Predict targets).
Off-chain
.envlives at repo root. Gitignored..env.exampleis the schema.loadEnv()throws with a clean message on missing required vars.- Sponsored gas, three strategies. Frontend
useTxSigner()cascades:- Self-sponsor via
POST /v1/sponsor. - Enoki sponsorAndExecute (zk only, on
SponsorUnavailableError). - Direct execution (user pays gas).
- Self-sponsor via
- Realized P&L computed at read time.
/v1/users/:address/redemptionscomputespayout − qty × avg_fill_costper(oracle, strike, side). Binary rows returnnullbasis (V3 fills aren’t indexed). Profile chart end == Realized P&L tile. useAppAccount()is the only account hook UI should use. dApp Kit’suseCurrentAccount()returnsnullfor zkLogin users;useAppAccountunifies wallet + zk.- One resolver process. A
tsx watchreload can leave the oldsetIntervalalive while a new one starts, causing duplicate-digest log lines and validator sequence-number conflicts. Always⌃Cand restart, never let two run. - Indexer needs a restart per new binary market.
binary::<slug>event streams are created at startup from.binary-markets/registry.json. New scaffold → indexer restart before its stream exists./v1/binary-marketsis unaffected (reads disk + chain).
Frontend
- Hide settled/expired markets from
/markets. Filtered inallMarketsmemo. Reachable from/positions//profile. - Dedupe parallel oracles by (asset, expiry).
lastUpdateMs1h-bucket primary,fillCounttiebreak. Twin stays reachable by direct URL. - Trading is blocked on expired markets.
BuyPanelshort-circuits + shows a banner.RecentFillsdisables its rows. - Losing-side holdings of resolved markets are hidden from
/positions. The coins are worthless and unredeemable; showing them with a misleading “redeem” CTA was clutter.