Repository navigation
Conversation
…iture (OracleDesk#23) Adds an admin-set creation bond (`set_creation_bond` / `get_creation_bond`, instance key `DataKey::CreationBond`, absent = 0 so fresh and legacy instances keep today's behaviour). Every new market snapshots the bond into a `DataKey::Bond(u64)` sidecar — `Market`'s XDR shape is untouched, same pattern as `FeeTerms(u64)` — and the creator's seed and bond leave in ONE transfer so the treasury's `(from, to, amount)` pre-authorisation triple keeps matching. Settlement is a new permissionless `claim_bond(market_id)` entrypoint, callable only after finality: on a plain void or a resolution the bond refunds to the creator; on a guardian-fault void (`void_market_with_reason(id, VoidReason::GuardianFault)`) it is forfeited to the fee-recipient snapshot taken at void time, so a later treasury migration cannot move the penalty. Existing `void_market` is unchanged and stays NoFault. Legacy markets without a sidecar entry reject the claim with `NoBond`. The append-only `DataKey`/`Error` tails also reserve OracleDesk#24's entries (`MaxIntegratorFeeBps`, `MarketMaxIntegratorFee(u64)`, `IntegratorFees(Address)`, `IntegratorFeeTooHigh = 27`) so the enums are appended once across this PR. 14 bond tests authored (refund, forfeiture, forfeit-recipient snapshot, double-claim, pre-finality, legacy markets, auth negatives, multi-market solvency invariant extended with held bonds). Not executed here: this checkout's gate is `cargo +stable fmt -p market-core -- --check` (exit 0, clean baseline at tip).
…racleDesk#24) Adds `buy_via` / `sell_via` (and `quote_buy_via` / `quote_sell_via`) — additive entrypoints that route an extra `integrator_fee_bps` cut to an agent/integrator. Buys charge the integrator on top of the protocol fee (both round up, so rounding keeps favouring the pool); sells widen the gross spread by the integrator bps and carve the integrator portion out with floor division, so the protocol never collects less than plain `sell` would have (`fee + integrator_fee == gross - collateral_out` is exact). `calc_buy`/`calc_sell` are now `calc_buy_with`/`calc_sell_with` with bps = 0 and produce byte-identical quotes — the shared-vectors test and existing fill accounting are untouched. Caps (OracleDesk#24): an admin opts the platform in with `set_max_integrator_fee_bps`, validated against the current default fee so `fee + cap <= MAX_FEE_BPS`; `create_market` snapshots the cap into a `MarketMaxIntegratorFee(u64)` sidecar (absent = 0, integrator routing disabled), so later admin changes can neither widen nor narrow what an existing market honours. `Market`'s XDR shape again stays frozen. `claim_integrator_fees(integrator)` pays the cross-market balance (CEI: balance removed before transfer, zero-balance claim is a no-op returning 0) and is auth'd by the integrator only; accruals are credited without integrator consent so routing needs no registration. 17 tests authored here (11 entrypoint/accrual/cap/claim/auth/solvency tests in lib.rs, 6 math tests including zero-equivalence and split-never-shortchanges-property LCG sweeps; the solvency invariant helper from the bond commit already carries the integrator term). Gate on this checkout: `cargo +stable fmt -p market-core -- --check` exit 0; tests are not executed (repo does not compile at develop tip).
…acleDesk#23) Adds `guardian_void_market_v2(market_id, reason, at_fault)` — the same validation as `guardian_void_market` (shared `guardian_void_inner`) plus an explicit at-fault flag for the creator's refundable bond. With `at_fault = true` the void is forwarded to market-core's `void_market_with_reason(id, GuardianFault)` via **name dispatch** (`env.try_invoke_contract` + `Symbol`), because `contractimport!` binds this crate to the vendored `market_core.wasm`, whose generated client does not know the new function until `make build` regenerates it. `VoidReason` is an all-unit-variant `#[contracttype]` enum and encodes as a plain `ScVal::Symbol`, mirrored locally so the call is wire-compatible across that boundary. `guardian_void_market` itself keeps its exact signature, call path (generated `void_market` client = NoFault) and event set: existing callers, tests and the Invalid-verdict, expire_dispute and finalize_signers void paths are untouched, per the frozen-entrypoint rule. A new `GuardianVoided { market_id, at_fault }` event is emitted only on the v2 entrypoint, alongside the existing `GuardianAction`. Appends `Error::MarketCoreCallFailed = 44` (append-only tail; OracleDesk#241 also claims 44 on its branch — last merger re-appends). 3 resolver tests authored (NoFault parity + event, at-fault forwarding + event, shared validation and admin auth). The at-fault test passes only after `market_core.wasm` regeneration; with the committed pre-OracleDesk#23 blob the dispatch reverts the whole call with `MarketCoreCallFailed` and no state is touched. Tests not executed here (resolver does not compile at develop tip: `core_call` is used but defined nowhere — pre-existing, out of scope; my additions are self-contained and avoid it). Gate: `cargo +stable fmt -p resolver -- --check` flags exactly the same 12 pre-existing hunks as the untouched-tip baseline (bodies verified identical, only line numbers shifted); zero new formatting diffs in added ranges.
… that owns their helpers The three `guardian_void_v2_*` tests were appended inside `mod golden`, whose `use super::*` pulls from the crate root. `dispute_fixture`, `open`, `status`, `signer_fixture`, `DEADLINE`, `McImpl`, `with_market_core`, `MockAuth` and `MockAuthInvoke` are all declared inside `mod test`, so the block referenced helpers that are not in scope there and `cargo test -p resolver` would not build. Relocated into `mod test`, directly after the `both_modes!` invocation. Also pinned the vendored-blob reality rather than asserting past it: the at-fault route name-dispatches `void_market_with_reason`, which the vendored `market_core.wasm` does not export, so - `guardian_void_v2_at_fault_forwards_guardian_fault` now runs under an explicit `with_market_core(McImpl::Native, ..)` (the existing idiom in `native_and_wasm_market_core_agree`) instead of defaulting to the wasm registration where the dispatch cannot resolve; and - a new `guardian_void_v2_at_fault_fails_closed_on_stale_wasm` asserts the default path fails closed with `MarketCoreCallFailed`, leaves the market `Open`/`Disputed` and publishes no `GuardianVoided` event. Co-Authored-By: anitahhhh <ajalamodupe@outlook.com>
OracleDesk#23/OracleDesk#24 Closes the coverage gaps against the issue's money invariants: - `buy_via_fee_at_exact_market_cap_is_accepted` / `combined_take_at_exact_max_fee_bps_is_accepted` pin both `>` boundaries (`bps > cap`, `bps + fee_bps > MAX_FEE_BPS`), which the rejecting-side tests alone leave unfalsifiable. - `sell_via_rejects_fee_above_market_cap` — the cap was only exercised on buy. - `buy_via_with_zero_integrator_fee_matches_buy` / `sell_via_with_zero_integrator_fee_matches_sell` prove OracleDesk#24's compatibility criterion at contract level on two identically seeded markets: equal shares, reserves, sets_minted, fees_accrued, and no integrator balance opened. - `claim_integrator_fees_second_call_is_a_noop` and `integrator_fees_accumulate_across_markets` pin zero-before-transfer and the per-integrator-across-markets accrual shape. - `void_reason_encodes_as_plain_symbol_name` asserts the single encoding claim the whole OracleDesk#23 resolver route rests on, instead of only asserting it in a comment. Co-Authored-By: anitahhhh <ajalamodupe@outlook.com>
…OracleDesk#23) market-core now pulls `seed_amount + creation_bond` in one transfer, so treasury-created markets would have failed their nested-transfer pre-authorisation: `authorize_transfer` matches a single exact `(from, to, amount)` triple, and the treasury was authorizing `seed` only. - `core_creation_bond` reads `get_creation_bond` by name via `env.try_invoke_contract`, because `contractimport!` generates `MarketCoreClient` from the vendored `market_core.wasm`, which predates the view; a client-based call would not compile until `make build` regenerates the blob. Same mechanism `core_trade` already uses for trades. - **Absent means zero.** A pre-OracleDesk#23 core has no such function, and every dispatch failure degrades to `0` rather than to `MarketCoreCallFailed`, so treasury keeps funding legacy cores. Fail-closed in the dangerous direction: under-reporting a required bond leaves the nested transfer unauthorised and the whole creation reverts. - `total_outlay = seed + bond` is what `ensure_liquid`, `ensure_unreserved`, `authorize_token_pull` and `reserve_capacity` (market + daily caps) now see. - Net position exposure and P&L cost basis stay on the seed: the bond is refundable collateral, not a position, so it is not double-counted against `DataKey::Net(id)`/`Pnl(id)`. A refund or forfeit back into this treasury lands in `idle()` and so reaches `nav()` exactly once, with no receivable tracked anywhere. - `check_trade_size(total)` runs only when `bond > 0`. `agent_create_market` has never checked `max_trade` on the seed itself (it checks `creation_policy.max_seed`); applying the check unconditionally would silently tighten that pre-existing, unrelated gap and would preempt `SeedCapExceeded` in `agent_create_market_rejects_seed_above_trade_cap`. Tests: `agent_create_market_counts_bond_toward_caps`, `agent_create_market_rejects_seed_plus_bond_above_trade_cap` (both under an explicit `McImpl::Native` registration, dispatched by name for the same reason) and `agent_create_market_funds_no_bond_against_pre_23_core`, which pins the default wasm registration degrading to a seed-only pull. Co-Authored-By: anitahhhh <ajalamodupe@outlook.com>
…ntegrator fees (OracleDesk#23, OracleDesk#24) ADR 0010 records the two decisions every OracleDesk#23/OracleDesk#24 consumer depends on: new per-market state goes in appended `DataKey` sidecars with absent-means-default (never a new `Market` field, which would break the three vendored-blob decoders), and new cross-contract entrypoints are reached by name (`env.try_invoke_contract` + the `ScVal::Symbol("Variant")` encoding of an all-unit `#[contracttype]` enum), so nothing here waits on `make build`. Also documents the two properties that are easy to get wrong later: why a forfeited bond is not a treasury receivable (kept out of `Net(id)`/`Pnl(id)` so a refund is not read as position profit), and why `core_creation_bond` treats a dispatch failure as `0` rather than as an error. `docs/architecture.md`: the solvency invariant is stated in its global form (`+ Σ held bonds + Σ unclaimed integrator balances`), matching `test::assert_global_solvency`, plus lifecycle notes for the bond and the integrator-fee paths. Lists as required follow-up what this host cannot do: `make build` (stellar-cli 22.0.0 vs 27.1.0, rustc pinned to 1.90.0 by OracleDesk#58), `make bindings`, and the Python/TS reference mirrors. Co-Authored-By: anitahhhh <ajalamodupe@outlook.com>
…nd (OracleDesk#23, OracleDesk#24) The two issues' acceptance criteria include the front-end contract: a UI that displays only `seed_amount` at creation produces transactions that fail on balance, and an integrator that derives `min_shares_out` from `quote_buy` over-states its own trade by the fee it is charging. Documents the three contract-enforced fee rules, which quote to use for the bound, that `Trade` is unchanged in favour of `IntegratorFeeAccrued`, the bond settlement table, and the real gap that `buy_via`/`sell_via` carry neither deadline nor limit price. Co-Authored-By: Claude <noreply@anthropic.com>
|
@anitahhhh 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! 🚀 |
…esk#24) Port of the OracleDesk#24 integrator-fee math to model/fpmm_model.py, with the known-value literals transcribed from math.rs's buy_with_integrator_known_values / sell_with_integrator_known_values and the property sweeps driven by the same LCG stream math.rs uses, so the two implementations sweep identical inputs instead of independent random ones. Adds the collateral-identity sweep the contract asserts on chain (collateral_in == net + fee + ifee, gross == collateral_out + fee + ifee), because a break there is a break in the solvency invariant. This is the only gate on this host that actually executes: python3 model/fpmm_model.py passes, each new check runs individually, and a mutated expectation fails. BuyQuote/SellQuote deliberately keep their shape and the "with" variants get their own dataclasses, since _check_shared_vectors compares __dict__ against the committed vectors.jsonl and a new field would invalidate every calc_buy line; math.rs can carry the field because its shared_vectors_match enumerates the compared keys explicitly. Co-Authored-By: Claude <noreply@anthropic.com>
…ils precisely (OracleDesk#24) ADR 0010 listed the Python mirror as follow-up work it could not do. That was wrong about the constraint: model/fpmm_model.py runs here, so its checks are executed rather than merely authored. Only the TypeScript mirror stays out, because Node on this host cannot strip types. Also drops the claim that resolver's Error 44 collides with OracleDesk#239/OracleDesk#240/OracleDesk#241: it is OracleDesk#241 (QuorumNotReached) and OracleDesk#242 (EventTimeMismatch) that reach 44, verified from each PR's own patch. Co-Authored-By: Claude <noreply@anthropic.com>
…riant rule (OracleDesk#23, OracleDesk#24) `develop`'s resolver enum ends at 43, so `MarketCoreCallFailed = 44` reaches for the same number as OracleDesk#241's `QuorumNotReached` and OracleDesk#242's `EventTimeMismatch`. The renumber rule is worth writing down where the next contributor will hit it: asserting the named variant survives the renumber, asserting the numeric literal does not. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two protocol-economics features across the three money-moving contracts, both
delivered as storage sidecars with appended
DataKeytails and newentrypoints, so no existing XDR shape changes:
bondon top ofseed_amountin a single atomic transfer. It is refunded on normalresolution and on a
NoFaultvoid, and forfeited to the fee recipient whenthe guardian voids the market for cause.
claim_bond(market_id)ispermissionless once final so a keeper can drive it.
buy_via/sell_via/quote_buy_via/quote_sell_viacharge an integrator cut bounded by aper-market cap snapshotted at creation, accrue it to
DataKey::IntegratorFees(Address), and pay it out throughclaim_integrator_fees.buy/sellkeep their exact prior behaviour asthe zero-integrator path.
Branch is 11 commits on
develop@f1900f4, head1820f98. This PR was resumed from apushed-but-unopened branch; two defects in the inherited code were found and
fixed here (see Inherited code).
The
model/fpmm_model.pymirror is the one part of this PR that ran on thishost and passed — see The one gate that executed. Nothing else did.
Closes #23
Closes #24
What changed, per contract
contracts/market-core/src/lib.rsvoid_market_with_reason,claim_bond, integrator caps + accrual + claim,*_viaentrypoints, 4 new errors/events, 33 testscontracts/market-core/src/math.rscalc_buy_with/calc_sell_with;calc_buy/calc_sellre-expressed as the zero-integrator calls, 6 testscontracts/treasury/src/lib.rsagent_create_marketfunds and caps the bond, 3 testscontracts/resolver/src/lib.rsguardian_void_market_v2,VoidReasonmirror, name-dispatch void,GuardianVoided, 4 testsdocs/adr/0010-bond-and-fee-sidecars.mddocs/architecture.mddocs/frontend-integration.mdmodel/fpmm_model.pycalc_buy_with/calc_sell_with+ 6 checksNo
*.wasm, no*.wasm.sha256, nopackages/bindingsfile is touched(
git diff --name-only develop...HEAD | grep -c wasm→0).market-core
DataKey::CreationBond(instance, default 0) and the per-market snapshotDataKey::Bond(u64) -> MarketBond { amount, state, void_reason, forfeit_to }.BondState { None, Held, Refunded, Forfeited }.create_marketpullsseed_amount + creation_bondin onetoken.transfer(checked_add →Overflow), because treasury's nested-transferpre-authorisation matches exactly one
(from, to, amount)triple.void_market(market_id)keeps its pre-Add a refundable creation bond forfeited when a market is guardian-voided #23 semantics by delegating tovoid_inner(.., NoFault);void_market_with_reason(market_id, reason)is thenew resolver-only entrypoint.
claim_bondapplies CEI: state flips toRefunded/Forfeitedbefore thetransfer, so a replay pays nothing; a second call is
AlreadyClaimed(17).Forfeiture happens only on
MarketStatus::Void && void_reason == GuardianFault.forfeit_tois snapshotted at void time (falling back toDataKey::Treasury)so a later
propose_treasury/accept_treasurycannot redirect the forfeit between the void and theclaim — the issue's edge-case recommendation.
Bondentry:claim_bond→NoBond(28), and thesolvency helper reads absent as 0. The upgrade is migration-free.
set_max_integrator_fee_bps/get_max_integrator_fee_bps(global, default0) and the per-market snapshot
DataKey::MarketMaxIntegratorFee(u64), withdefault_fee_bps + bps ≤ MAX_FEE_BPSenforced at the setter and per trade(
validate_integrator_fee), because the market snapshot is authoritative.accrue_integrator_feeadds toIntegratorFees(integrator)and emits the newIntegratorFeeAccruedevent rather than changingTrade's field list(indexers decode
Trade).claim_integrator_feesrequires the integrator's auth,removes the keybefore the transfer, and returns
Ok(0)on an empty balance.math.rs — rounding
Buy: both fees are
mul_div_ceil, sonet = collateral_in − fee − ifeeneverexceeds the pool-credit and the trader (who opted into the integrator) eats the
rounding. Sell:
grossis ceiled on the combined bps, thenifee = mul_div_floor(total_fee, int_fee_bps, fee_bps + int_fee_bps)andfee = total_fee − ifee, so the protocol's leg is never reduced below aprotocol-only computation. The zero-denominator case (
fee_bps + int_fee_bps == 0)returns
ifee = 0instead of dividing.treasury
agent_create_marketreads the bond through a newcore_creation_bondnamedispatch, then uses
total_outlay = seed_amount + bondforcheck_trade_size,ensure_liquid,ensure_unreserved,authorize_token_pullandreserve_capacity— a single combined authorisation, exactly the amount thenested transfer pulls. The bond-aware
check_trade_sizeis gated onbond > 0so the existing
agent_create_market_rejects_seed_above_trade_cap(which expectsSeedCapExceededforseed 151 > max_trade 150) keeps its contract.reserve_net/record_costdeliberately stay onseed_amount: the bond is notposition cost, and treating a refund as position profit would corrupt NAV.
resolver
guardian_void_market(market_id, reason, at_fault)is added asguardian_void_market_v2, leaving v1's signature intact, and emitsGuardianVoided { market_id, at_fault }. It reaches market-core throughenv.try_invoke_contract::<(), soroban_sdk::Error>(core, &Symbol::new(env, "void_market_with_reason"), args).Why name dispatch instead of generated clients
Two hard walls, both recorded in
docs/adr/0010-bond-and-fee-sidecars.md:market_core.wasmblobs are pinned by.sha256and verified atbuild time by
resolver/build.rsandtreasury/build.rs; a stale blob is acompile error, and rebuilding them requires
stellar-cli 27.1.0while thishost has 22.0.0, plus a rustc newer than the 1.90.0 pinned by issue Replace vendored market_core.wasm with a deterministic build and hash-pinned import #58.
an existing entrypoint would break the three decoders that still hold the old
spec.
Name dispatch sidesteps both: it needs no client and no blob refresh. The
encoding it relies on is not assumed —
#[contracttype]enums whose variants areall unit with no explicit discriminants encode as
ScVal::Symbol("Variant")(
soroban-sdk-macros-27.0.6/derive_enum.rs:370), andvoid_reason_encodes_as_plain_symbol_nameasserts it directly:The fail-closed direction is deliberate:
core_void_with_reasonreturnsMarketCoreCallFailed(44) rather than silently degrading to a no-fault void, andcore_creation_bondtreats a dispatch failure as0(the pre-#23 core genuinelyhas no bond). Both behaviours are tested.
Acceptance criteria — #23
set_creation_bond/get_creation_bondexist; setter is admin-gated witha wrong-caller test —
wrong_caller_cannot_set_creation_bond.create_marketpullsseed + bondatomically and recordsBond(id)without changing the
MarketXDR —create_market_pulls_seed_plus_bond;Marketis byte-for-byte unmodified (verified by reading the struct).void_market_with_reasonexists andvoid_marketkeepsNoFault—bond_refunded_on_plain_void_keeps_no_fault_semantics,wrong_caller_cannot_void_with_reason.claim_bondhandles every terminal path and cannot run twice —bond_refunded_on_resolution,bond_refunded_on_explicit_no_fault_void,bond_forfeited_on_guardian_fault_void,claim_bond_twice_fails,legacy_market_has_no_bond,claim_pool_remainder_does_not_touch_bond.GuardianVoided—guardian_void_v2_no_fault_keeps_plain_void_semantics,guardian_void_v2_at_fault_forwards_guardian_fault(nativemarket-core),guardian_void_v2_shares_validation_and_auth,guardian_void_v2_at_fault_fails_closed_on_stale_wasm.Deviation: the issue proposed changing
guardian_void_market'ssignature. That is a breaking change to a live entrypoint that three
vendored decoders still hold, so the flag ships as a new
v2entrypointinstead, which is this repo's established pattern (
buy→buy_v2).with tests —
agent_create_market_counts_bond_toward_caps,agent_create_market_rejects_seed_plus_bond_above_trade_cap,agent_create_market_funds_no_bond_against_pre_23_core.Not done: the optional treasury
claim_bondwrapper. The market-coreentrypoint is permissionless (the issue explicitly allows relying on it),
so a wrapper would add an auth path with no capability.
global_solvency_includes_bonds_multi_marketovertest::assert_global_solvency, which sumssets_minted + fees_accrued + DataKey::LpFees(id) + held bonds + unclaimed integrator balancesacross0..market_count().market_core.wasm, bindings and docs updated,make test/make lintpass — not met, and not achievable on this host. Docs areupdated (
architecture.md,frontend-integration.md, ADR 0010). The wasmblobs and generated bindings are deliberately absent; see Refusals and
Zero execution. Consequence: against the currently vendored blob the
resolver's at-fault path fails closed instead of voiding. The behaviour is
correct and tested in both directions, but it is inert until a maintainer
runs
make buildand re-pins the hashes.Acceptance criteria — #24
buy_via/sell_via/quote_buy_via/quote_sell_viaexist andbuy/sellbehaviour is unchanged —buyandsellare thindelegations to
do_buy/do_sellwithNone, andbuy_via_with_zero_integrator_fee_matches_buy/sell_via_with_zero_integrator_fee_matches_sellpin the equivalence."Bindings signatures unchanged" holds trivially because no bindings were
regenerated.
never exceed
MAX_FEE_BPS—buy_via_fee_at_exact_market_cap_is_accepted,combined_take_at_exact_max_fee_bps_is_accepted,sell_via_rejects_fee_above_market_cap,max_integrator_fee_snapshot_not_affected_by_admin_change.Deviation: the issue proposed appending
max_integrator_fee_bpstoMarket. A newMarketfield changes the XDR thatget_marketreturnsand that the resolver's stale vendored decoders read, so the cap lives in
the
MarketMaxIntegratorFee(u64)sidecar with absent-means-zero, followingthe existing
FeeTermssidecar precedent. The semantics are identical;get_market_max_integrator_fee_bpsis the accessor.claim_integrator_feesis integrator-authorised, zeroes before transfer,emits an event —
claim_integrator_fees_pays_and_zeroes,wrong_caller_cannot_claim_integrator_fees,claim_integrator_fees_second_call_is_a_noop,integrator_fees_accumulate_across_markets.round trips never profit —
calc_sell_with_split_never_shortchanges_protocol,round_trip_with_integrator_never_profits, andcalc_buy_with_zero_integrator_matches_calc_buyover 2000 LCG iterations.Python half done and executed on this host (
model/fpmm_model.pygainscalc_buy_with/calc_sell_with, the Rust literals pinned in_check_integrator_known_values, and five sweeps driven by the same LCGstream
math.rsuses, so the two languages cover identical inputs ratherthan independent random ones).
python3 model/fpmm_model.pypasses — theonly real execution in this PR, see The one gate that executed.
TypeScript not done (
agents/core/fpmm.tscannot be type-stripped bythis host's Node). Also note what "identical" means here: the expected
values are transcribed from the Rust source, not produced by a differential
run, because Rust could not execute.
scripts/fpmm-diff/(oracle,diff.pydispatch table,vectors.jsonl) is not extended for the newfunctions either — see Refusals.
solvency_includes_integrator_balances.docs/frontend-integration.mddocuments how to pass an integrator —new "Integrator (frontend) fees" and "Creation bond" sections, including
the warning that
min_shares_outmust be derived fromquote_buy_via, notquote_buy.make testandmake lintpass — not run. See Zero execution.Zero execution — read this before reviewing
No Rust in this PR was compiled, type-checked, clippy'd, or run. Not one Rust
test executed. The sole exception is the Python mirror (see The one gate that
executed). This is not an oversight; on this host it is impossible:
cargo build/cargo test/cargo checkcannot link: the vendoredcross-contract
.wasmblobs are hash-pinned bybuild.rsand the toolchainneeded to rebuild them (
stellar-cli 27.1.0, rustc ≥ 1.91 per soroban-sdk27.0.6's MSRV) is not installed —
rust-toolchain.tomlpins 1.90.0 per issueReplace vendored market_core.wasm with a deterministic build and hash-pinned import #58.
make build,make test,make lint,make bindingsall depend on thattoolchain.
agents/'s test runner cannotexecute even the tests that already exist. (
python3does work, hence themirror.)
So the 46 new
#[test]functions in this PR are authored and staticallyreviewed, never executed (the 6 new Python checks were run). They are written against helpers I read in full and
whose signatures I checked at the definition site, but their correctness is
argued, not demonstrated. Treat every test name below as an unverified claim
until a machine with the pinned toolchain runs:
Three specific things I could not verify and would check first:
#[should_panic(expected = "Error(Contract, #27)")]expectations —the number is my reading of the enum, not compiler output. Every other new
error assertion compares the named variant, so it cannot drift.
void_reason_encodes_as_plain_symbol_name— it asserts thederive_enum.rs:370encoding I built the resolver dispatch on. If it fails,the resolver's at-fault path is wrong too, not just the test.
treasury'swith_market_core(McImpl::Native, ..)usage in the two newbond-cap tests — the fixtures are shared with the
#111/#112lanes and Icould not confirm the dual-registration helper's exact signature by compiling.
The one gate that executed
Python
model/fpmm_model.pyruns on this host, so the #24 mirror is verified,not merely authored.
run_all()now includes the six new checks alongside the pre-existing ones, andeach was also invoked individually so a silent skip is impossible:
Two things that run proves and that review alone could not settle:
calc_sell_with(10_000, 20_000, 1000, 100, 50)givesgross = 1016,total_fee = 16,ifee = floor(16*50/150) = 5,fee = 11—matching the
assert!(q_with.fee >= q_solo.fee)claim incalc_sell_with_split_never_shortchanges_protocolon 2000 shared LCG inputs._check_integrator_accounting_is_exactruns the identities the contractasserts on chain (
collateral_in == net + fee + ifee,gross == collateral_out + fee + ifee) across the same domain, so thesolvency formula in
docs/architecture.mdis checked rather than asserted.This does not transfer to the Rust side. Python mirroring Rust's literals
only shows the two sources agree as written;
cargo test -p market-corestillhas to run the Rust copies of these five sweeps.
Gate differentials (before / after)
Two gates are available here.
python3 model/fpmm_model.pygenuinely executes,and its result is in the first row below.
cargo +stable fmt -p <crate>provesrustfmt parses the file and nothing more — it is not a type check. Both are
reported as a differential against the same command at the merge base
f1900f4, never as "clean":f1900f4python3 model/fpmm_model.py(executes)cargo +stable fmt -p market-core -- --checkcargo +stable fmt -p resolver -- --checkcargo +stable fmt -p treasury -- --checkcargo build/test/checkThe
resolverandtreasurynon-zero exits are pre-existing ondevelopand are not this PR's work; they belong to the code those branches' earlier
merges left unformatted. They are not merely equal in count: the flagged content
is byte-identical. Reproduce with (ANSI escapes stripped first — cargo colourises,
and a naive
grepcomparison silently returns zero lines and looks green):My own additions did regress fmt twice, both found and fixed by hand rather
than by
cargo fmtwrite-mode (which would have reformatted the 13 + 2pre-existing hunks and inflated this PR's diff): five line-width hunks in
treasury, two inresolver.Manual audit, since no compiler could do it: no duplicate
Errordiscriminantsin
market-core, no duplicateDataKeyvariant names, notodo!/unimplemented!, balanced braces in all four edited.rsfiles, andtreasury::map_market_core_error's catch-all_ =>arm means market-core's twonew variants cannot break treasury's exhaustive match.
Inherited code — two defects found and fixed
This branch was resumed from
210758c9f(pushed by an earlier lane that diedbefore opening a PR). Static review of inherited code caught two things that
would have failed at compile time — the exact class of bug a zero-execution
environment hides:
resolver: three new#23tests had been appended insidemod golden, notmod test.mod golden'suse super::*resolves against the crate root, soevery fixture helper they call is out of scope: a compile error. Moved
into
mod test.resolver: the at-fault success test asserted that a void happened throughthe default
mc_impl(), which isMcImpl::Wasm— the vendored blob that doesnot contain
void_market_with_reason. It would have failed on a realtoolchain. Wrapped in
with_market_core(McImpl::Native, ..)(precedent:native_and_wasm_market_core_agree) and paired withguardian_void_v2_at_fault_fails_closed_on_stale_wasm, which asserts thewasm-mode behaviour is
Err(MarketCoreCallFailed), status stillOpen, statestill
Disputed, and noGuardianVoidedevent published.Tail collisions — expected, and the merge order matters
Two
Errortails are extended, and both are already claimed by open siblingPRs. Verified by reading each PR's patch, not guessed.
market-coreends atPendingAdminExpired = 26ondevelop; this PR appendsIntegratorFeeTooHigh = 27andNoBond = 28:ModeRestricted = 27DirectoryEntryArchived = 27AlreadyHalted = 27,HaltTooOld = 28MigrationBatchTooLarge = 30resolvergainsMarketCoreCallFailed = 44; three PRs claim that number too:QuorumNotReached = 44,FeedsDisagree = 45EventTimeMismatch = 44…CoreUnavailable = 48Duplicate discriminants are a hard compile error in Soroban (
E0081), sowhichever merges last must re-append its variants one past whatever is then at
the tail. Blast radius of a renumber, measured rather than assumed:
#27appearsas a literal in exactly two
#[should_panic(expected = "Error(Contract, #27)")]expectations (
lib.rs:4009,lib.rs:4361) and in one prose mention indocs/frontend-integration.md.#28and#44need no chase at all — the teststhat depend on them compare against the named variant
(
Err(Ok(Error::NoBond)),Err(Ok(Error::MarketCoreCallFailed))), so changingthe discriminant moves the number without touching an assertion. That is the
argument for keeping new tests off numeric literals, and these two variants are
the only ones here that use one. An in-code note at the collision point says so. The
same applies to the
DataKeyappend point. Nothing in this PR can resolve theordering from here, and I have not "helpfully" renumbered to a gap that may be
filled by an earlier merge.
Refusals — what is deliberately not in this PR
.wasmand no.wasm.sha256. Regenerating them requires a toolchainthis host does not have; committing a blob I cannot build from source is
strictly worse than committing none, because the
.sha256pin would thenattest to a binary nobody can reproduce.
make buildis the first step ofmerge, not of this PR.
packages/bindings.make bindingscannot run, and hand-editinggenerated files would produce a diff the generator contradicts.
agents/core/fpmm.tsmirror. The acceptance criterion is identicalknown values, which requires running Rust against the mirror and diffing. Rust
cannot be run here, so a hand-transcribed mirror would be an unverifiable claim
about arithmetic in the hottest money path in the repo — and this host's Node
cannot even execute the TS tests that already exist.
model/fpmm_model.pyisthe exception, not the rule: its port does execute (see The one gate that
executed), which is why it was worth writing and the TS one is not.
The remaining refusal, in order: (1)
agents/core/fpmm.tsgainscalcBuyWith/calcSellWithwithcollateralOutForSharestaking an optionalintegratorFeeBps; (2)scripts/fpmm-diff/oracle(acargobinary) exposes thetwo new functions, and
diff.py's dispatch table gainscalc_buy_with/calc_sell_withrows; (3)vectors.jsonlis regenerated by the differentialrun itself, so all three languages assert the same boundary inputs without
anyone transcribing a literal.
the migration schema are other lanes' tails.
resolver/treasuryfmt debt.It would bury the reviewable diff in noise.
Notes for whoever merges
developmoved from5ede00atof1900f4during this work andthis branch was rebased onto it (
--force-with-lease, diff verified unchanged).Error/DataKeytail per Tail collisions.make build, then re-pin the resolver and treasury blob hashes, thenmake bindingsand check the committed bindings are not stale (CI enforces).cargo build --manifest-path scripts/fpmm-diff/oracle/Cargo.toml --releasethen
python3 scripts/fpmm-diff/diff.py --seed 1 --count 50000is the realtest of the Add integrator (frontend) fees on buy and sell with per-market caps #24 math: it runs Rust against the Python mirror already in this
PR. Add the TS mirror after that passes, not before.
GuardianVoidedis a new event andguardian_void_market(v1)is unchanged, so backend sync scripts do not need a coordinated redeploy —
but the bindings for
buy_via/sell_via/claim_bonddo need regeneratingbefore any UI can call them.