Repository navigation
feat: categorical (#6), scalar (#7) and dynamic-fee (#9) markets - #161
MFrancis-dev wants to merge 6 commits into
Conversation
packages/bindings/resolver/src/index.ts was missing the OracleDesk#159 interface additions (ResolutionSpecV2, Verdict, Escalation, ExpiryMode, DisputeSettled, expire_dispute/settle_dispute/escalation_attest/ current_round/register_spec_v2, Error variants 19-25). Regenerated with scripts/gen-bindings.sh at stellar-cli 27.1.0; no other package drifted.
…fee markets (OracleDesk#6 OracleDesk#7 OracleDesk#9) - market-core: n-outcome categorical FPMM (2..=8) — create_categorical_market, buy_cat/sell_cat, resolve_cat, redeem_cat, claim_pool_remainder_cat, get_cat_market/get_cat_position/get_prices_cat/quote_buy_cat, new math_categorical module, CatTrade/CatMarketResolved events, MarketKind dispatch - market-core: scalar range markets — create_scalar_market, resolve_scalar (MarketStatus::ResolvedScalar), get_scalar_meta; binary pool reused (LONG=YES) - market-core: dynamic fee schedules — FeeSchedule + effective_fee_bps, create_market_with_fees, set_default_fee_schedule, current_fee_bps, quote_buy_at, TradeV2 carrying the realised fee_bps - resolver: register_cat_signer_spec/attest_cat/finalize_cat and register_scalar_spec/resolve_scalar_price (Reflector, clamped, floored) - treasury: agent_buy_cat under the existing caps - mirrors: model/fpmm_model.py + agents/core/fpmm.ts pinned to shared model/fee_fixtures.json; trader reads get_market + current_fee_bps in live mode - bindings regenerated (market-core/resolver/treasury); vendored market_core.wasm rebuilt via make build - docs: ADR 0005 (market-kind storage split), ADR 0001 appended-variants note, architecture/frontend-integration/STATUS updates
|
@MFrancis-dev Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
…-scalar-dynamic-fees # Conflicts: # oracledesk-stellar/docs/STATUS.md
Conflicts resolved by hand in ten paths. The semantic collisions, not the textual ones: - resolver `Error`: develop filled codes 26-41, so `InvalidOutcomeIndex` moves from 26 to 42 (a duplicate tag would silently alias two errors). - resolver `ResolutionSpec`: categorical/scalar keep tags 4/5 (they are pinned by the golden commitment-hash vectors in this branch's `golden_legacy_scalar_spec_hashes`); committee/hybrid append at 6/7. Variants stay append-only either way. - resolver `DataKey`: develop's V2/migration keys and the new categorical keys both append, develop's first, so neither side's stored tags move. - resolver `cancel_proposal`: develop's constrained guardian (typed reason, `MAX_CANCELS_PER_MARKET`, `GuardianAction` event, dispute-window bound) wins, extended to clear the separate categorical proposal slot. `cat_proposal_opens_with_quorum_and_guards_expiry` is re-sequenced for the window bound: the quorum now lands after the grace deadline, so the proposal is still inside its window when the guardian cancels it. - treasury: develop's price-checked `do_agent_buy` replaces this branch's copy, and `agent_buy_cat` is rewritten on the agent-registry helpers (`require_agent`/`check_trade_size`/`authorize_token_pull`/ `reserve_capacity`/`reserve_net`); both categorical tests port to the `agent`-first signature. `settle_value` gains the `ResolvedScalar` arm mirroring market-core's `math::scalar_payout`, which the merged exhaustive match requires. - spec-hash: both parser sets kept (cat-signers/scalar and committee/hybrid), 11 tests including all golden vectors. - ADR renumbered 0005 -> 0006: develop consumed 0003 and 0005. Regenerated artifacts, which is what the two `git diff --exit-code` gates demand (both were red on untouched develop at 6f186e6): `stellar contract build` -> vendored market_core/reasoning_registry wasm, `scripts/gen-bindings.sh` -> packages/bindings. Verified idempotent: a second generation leaves `packages/bindings` diff-clean.
The addendum's figures were measured pre-merge (208 workspace tests, 52 agents). On the merged branch: 305 (market-core 97, resolver 107, treasury 95, reasoning-registry 6) and 54 agents tests.
|
Conflicts resolved and every nested Head Gates, in ci.yml order
Resolutions worth flaggingFour of these were semantic collisions, not textual ones:
Also in treasury: Why committed wasm/bindings are part of this PRBoth Environment disclosureMeasured on macOS arm64 — |
…al-scalar-dynamic-fees Resolves the textual conflicts against develop, which has advanced since this branch last merged (PR OracleDesk#182). Source conflicts resolved: - .env.example, agents/bin/trader.ts, agents/core/fpmm.test.ts, agents/stellar/adapter.ts, docs/architecture.md, model/fpmm_model.py, contracts/market-core/src/lib.rs, contracts/resolver/src/lib.rs. trader.ts/adapter: our inline trader + getMarketView/currentFeeBps kept alongside develop's register/reveal-spec entrypoints (our getMarket renamed to getMarketView to avoid a name clash with develop's getMarket). Safe, independent fixes applied on top of the merge: - rust-toolchain.toml: 1.90.0 -> 1.93.0 (soroban-sdk 27.0.6 needs >=1.91; CI's stellar-cli 27.1.0 needs >=1.93; 1.90 failed before any repo command). - contracts/oracledesk-types/src/lib.rs: add the missing `contracttype` import (develop ships the attribute without importing it; the crate cannot compile). Also carries this branch's CatSignerConfig/ScalarPriceConfig/ResolvedScalar and ResolutionSpec tags 6/7. - scripts/verify-wasm-hash.rs: convert the `//!` header to `//` (rustc 1.93 rejects inner doc-comments inside the include!d non-crate-root fragment). Generated artifacts are NOT regenerated — they mirror develop's committed bytes verbatim (packages/bindings/{market-core,resolver,treasury}/src/index.ts, contracts/resolver/{market_core,reasoning_registry}.wasm, contracts/treasury/market_core.wasm, and their .sha256 pins). Reason: the workspace wasm build cannot run, because contracts/reasoning-registry/src/lib.rs on develop is corrupt at tip d75e2fa (unrecoverable without authoring contract logic we will not fabricate). This branch is therefore NOT CI-green. See the PR comment for the enumerated, re-measured develop-caused breakages and the follow-up owed on this branch's market-core test module.
Status: merged with develop, conflicts resolved — but this PR cannot go green while
|
What this does
Implements #6, #7 and #9 together: categorical (n-outcome) markets, scalar (range) markets resolved from a Reflector feed, and per-market dynamic fee schedules — plus the resolver and treasury plumbing each needs, the Python/TypeScript mirrors the issues call for, regenerated bindings and refreshed vendored wasm.
Closes #6
Closes #7
Closes #9
The branch also carries one small independent fix:
ac7b283regeneratespackages/bindings/resolver, which was stale since #159 (the committed client was missingResolutionSpecV2,Verdict,Escalation,expiry_mode,expire_dispute/settle_dispute/escalation_attest/current_round/register_spec_v2).#6 — categorical N-outcome FPMM
create_categorical_market(…, probs_bps)— 2..=8 outcomes (MAX_MARKET_OUTCOMES = 8), each probability withinMIN_PROB_BPS..=MAX_PROB_BPSand summing to exactlyBPS; the creator/LP receives the leftover shares of the likelier outcomes.math_categoricalmodule: the binaryk = r_yes·r_noinvariant generalized tok = Π(r_i), with the same iterative floor/ceil scheme as the binary path — a trade never profits from rounding andknever decreases (property-tested in all three mirrors).buy_cat/sell_cat(share-count bounds),quote_buy_cat,get_prices_cat(implied probabilities; sum withinnof 10000 bps),redeem_cat,claim_pool_remainder_cat,get_cat_market/get_cat_position,market_kind.CatMarketCreated/CatTrade/CatMarketResolved;void_marketand the pause switch cover categorical ids; theFeeTermssnapshot is shared.fee_bps(no dynamic schedule) and have no price-based breakers (those are binary-price-only) — both deliberate, documented indocs/architecture.md.cat_eight_outcomes_resource_budget, printed on every run):buy_catatn = 8= 444,604 instructions / 64,092 B memory / 1,640 write bytes / 5 write entries — asserted to stay under 60% of the network limits (400M instructions / 40 MiB / 132,096 write bytes).#7 — scalar (range) markets
create_scalar_market(…, lower, upper)— a binary market (LONG = YES, SHORT = NO) plus aScalarMetaside entry; trading, fees (schedules included), breakers and the solvency invariant are inherited unchanged.resolve_scalar(market_id, payout_long_bps)writesMarketStatus::ResolvedScalar(u32)(appended variant; 0 = full SHORT, 10000 = full LONG); redemption paysfloor(yes·p/10000) + floor(no·(10000−p)/10000)— both legs floor, so payouts stay withinsets_minted.register_scalar_spec+resolve_scalar_price— reads the committed Reflector feed at/after close (same max-staleness window asresolve_price), rejects a non-positive reading asInvalidPrice, clamps into[lower, upper], floors the payout toward the pool.get_scalar_metaview (display only).#9 — dynamic fees from time-to-close and pool skew
FeeSchedule { base_bps, max_bps, ramp_secs, skew_mult_bps }→math::effective_fee_bps(schedule, now, close_time, r_yes, r_no)=min(max_bps, base + ramp + skew):ramprises linearly frombase_bpstomax_bpsover the lastramp_secsbefore close (flat atmax_bpsafterwards),skewaddsskew_mult_bpsper 1000 bps of pre-trade YES-price distance from 50/50. All floors round toward the trader.set_default_fee_schedule(validated; new markets only) or an explicitcreate_market_with_fees, validated against the default'smax_bpscap before a market id is allocated. Legacy markets have no schedule → flatfee_bps; no migration needed.buy/sell,_v2, quotes and the fee split); views:current_fee_bps,fee_schedule, andquote_buy_at(market_id, outcome, collateral_in, at_timestamp)so clients can price expected inclusion latency under a ramping fee.TradeV2carries the realisedfee_bpsalongside the existingTradeevent (the indexer-facing XDR ofTradeis unchanged).Resolver / treasury / agents
register_cat_signer_spec/attest_cat/finalize_cat— categorical counterpart of Attested mode, same dispute window; the guardian'scancel_proposalclears a categorical proposal too.agent_buy_catforwards to market-core under the existing per-trade/per-market/daily caps (with a test).get_market+current_fee_bpsso the strategy quotes with the effective (scheduled) fee and clamps size to the impact breaker; a trader test asserts the effective fee — not the flat one — reaches the decision trace.Mirrors (Python + TS, pinned to one fixture file)
model/fpmm_model.pyandagents/core/fpmm.ts(tests inagents/core/fpmm.test.ts) gain categorical buy/sell/prices/seed/settle, scalar payout andeffective_fee_bps.model/fee_fixtures.jsonholds golden vectors read by Python, Rust and TypeScript — all three implementations are pinned to the same numbers, so they cannot drift silently.Compatibility notes
Market's XDR is unchanged: the new state is side storage under the samemarket_id.MarketStatus::ResolvedScalarandResolutionSpec::{CategoricalSigners (tag 4), Scalar (tag 5)}are appended after the existing variants — old values keep decoding, but bindings generated before this change cannot decode values that use the new variants (release note indocs/STATUS.md; committed bindings are regenerated here). Rationale and rejected alternatives:docs/adr/0005-market-kind-storage-split.md.get_marketreturnsMarketNotFoundfor categorical ids — readers dispatch onmarket_kind(documented indocs/frontend-integration.md).InvalidOutcomeIndex); 23–27 were left free for PR Storage TTL policy, outstanding-share finalization, TWAP accumulator, and batched redemption (#16, #14, #11, #15) #156.Checklist
cargo fmt --checkandcargo clippy --workspace --all-targets -- -D warningspasscargo test --workspacepasses — 208 tests (market-core 97, resolver 82, treasury 23, reasoning-registry 6), plusscripts/spec-hash11make bindings) and included; regeneration verified idempotentmarket-core's interface changed →resolver/treasuryvendoredmarket_core.wasmrefreshed (rebuild byte-identical)docs/STATUS.mdupdated (dated addendum)How this was tested
Final local run (the CI workflow in this repo is nested under
oracledesk-stellar/.github/and does not run on GitHub; these commands mirror it exactly and then some):cargo fmt --check→ cleancargo clippy --workspace --all-targets -- -D warnings→ cleancargo test --workspace→ market-core 97, reasoning-registry 6, resolver 82, treasury 23 — 0 failedcargo test --manifest-path scripts/spec-hash/Cargo.toml→ 11 passed, 0 failedpython3 model/fpmm_model.py→fpmm_model: all property checks passedx402:npm ci && npm run build && npm test→ 5 pass / 0 failagents:npm install && npm run build && npm test→ 52 pass / 0 failscripts/gen-bindings.shre-run → byte-identical output; wasm rebuild → byte-identical (md5 stable); all fourpackages/bindings/*packagesnpm install && npm run build→ cleanNot done (called out honestly): no testnet redeploy — the new entrypoints are covered by contract-level and off-chain tests, not yet by a real-ledger transaction.