Skip to content

[CLOB-084, CLOB-085, CLOB-087, CLOB-017] Quote rounding, maker and taker fees, and state ownership - #1100

Merged
IbrahimIjai merged 3 commits into
SO4-Markets:mainfrom
medunrebecca-dot:feat/clob-settlement-fees-and-state-ownership
Oct 21, 2026
Merged

IbrahimIjai merged 3 commits into
SO4-Markets:mainfrom
medunrebecca-dot:feat/clob-settlement-fees-and-state-ownership

Conversation

@medunrebecca-dot

Copy link
Copy Markdown
Contributor

Closes #960
Closes #961
Closes #963
Closes #893

Summary

One branch for four Stellar Wave issues, in libs/clob_settlement_core and docs/:

  • [CLOB-084] Align SDK preview arithmetic and prevent fragmentation rounding gains #960, quote rounding. QuoteCalculator is replaced by quote_atoms and fill_quote_legs, which follow protocol spec sections 4.2 and 7.2. Bid locks and buyer debits round up, seller credits round down, and the difference is the dust residue. The Nearest mode and calculate_dust are removed, because the spec defines neither. Tests reproduce spec Examples B and C, and check that splitting a bid does not change its total debit or credit or let seller credit grow.
  • [CLOB-085] Implement maker fee accounting #961, maker fees. MarketFeeConfig stores maker_fee_rate beside the taker rate. Both rates must lie in [0, MAX_FEE_RATE], so maker rebates stay out of scope for v1. The maker fee comes from the maker's quote leg, and a zero fee records no liability. A bid's reservation covers the larger of the maker and taker fees, because a resting bid can fill as a maker later.
  • [CLOB-087] Deduct and accrue taker fees consistently across partial and multi-maker fills #963, taker fees across fragments. The taker fee for a fill is the difference of two rounded-up fees on the taker's running total. Fills split across several makers therefore pay the same total as one fill. Settlement takes taker_quote_before for the running total.
  • [CLOB-017] Define state ownership and deployment implications for the chosen topology #893, state ownership. docs/clob-state-ownership.md chooses a per-market topology: one market contract owns its book, balances, dust, and fee accumulators, and one registry creates market instances. Trading makes no cross-contract call.

Also updated: docs/fee-accounting.md (formulas, settlement legs, test vectors) and docs/protocol.md section 2. That section listed all trading fees as a v1 non-goal, which conflicts with #961 and #963.

Decisions to confirm

  1. Topology ([CLOB-017] Define state ownership and deployment implications for the chosen topology #893). [CLOB-016] Evaluate CLOB contract topology options and call overhead #892 is still open, so no topology has been chosen upstream. The per-market topology in docs/clob-state-ownership.md is a recommendation. It also resolves the [CLOB-029] Support deposits independent of a single market when assets are reused #905 TODOs in contracts/reader/src/clob_reads.rs by choosing per-market custody, not shared custody.
  2. Maximum fee rate. MAX_FEE_RATE equals FEE_RATE_SCALE (100%), the bound the code already enforced. A lower protocol maximum is a product decision, so I did not invent one.
  3. Maker rebates. Rejected in v1, as the issue allowed.
  4. Spec wording. Spec section 7.2 and invariant I10 say residue is "less than one quote atom". The integer residue is 0 or 1, as Example B shows, so the code checks 0 <= residue <= 1. The spec wording should be fixed separately.

Verification

  • No build, test, or lint command was run. The task asked for no build commands, so the changes were checked by reading and by hand-computing the expected values in the tests.
  • The repository has no Cargo.toml, AGENTS.md, or SDK package, so the verification gate named in the issues cannot run here. The SDK part of [CLOB-084] Align SDK preview arithmetic and prevent fragmentation rounding gains #960 ("identical amounts") is covered by the test vectors in docs/fee-accounting.md and the unit tests, and needs an SDK package to be checked end to end.
  • Pre-existing issues, not changed here: the top of lib.rs imports i128 from soroban_sdk and uses Vec without importing it, which will not compile in a no_std crate. The CI workflow's cargo fmt --check also fails on lines that predate this PR. I did not touch these, so this PR does not make them worse, but they need fixing before CI can pass.

Replace QuoteCalculator with quote_atoms and fill_quote_legs, which follow
protocol spec sections 4.2 and 7.2: bid locks and buyer debits round up,
seller credits round down, and the difference is the dust residue.

Drop the Nearest mode and calculate_dust. The spec defines neither, and
dust was computed in scaled units rather than quote atoms. Nothing in the
repository called either.

Tests reproduce spec Examples B and C and check that splitting a bid does
not change its total debit or credit.
Maker fees (SO4-Markets#961): MarketFeeConfig stores a maker_fee_rate next to the
taker rate. Both are validated against MAX_FEE_RATE and must be
non-negative, so maker rebates stay out of scope for v1. A maker fee is
taken from the maker's quote leg, and a zero fee leaves no liability
entry. Bid reservation now covers the larger of the maker and taker
fees, because a resting bid can fill as a maker later.

Taker fees across fragments (SO4-Markets#963): the taker fee for a fill is the
difference of rounded-up fees on the taker's running total, so fills
split across several makers pay the same as one fill. Settlement takes
taker_quote_before for that running total.

Also updates protocol spec section 2, which listed all trading fees as
a v1 non-goal, and rewrites docs/fee-accounting.md with the formulas and
test vectors.
Choose a per-market contract topology for v1: one market contract owns
its orders, book, balances, dust account, and fee accumulators, and one
registry creates market instances and owns the market list. Trading makes
no cross-contract call, and custody is never shared across markets.

The document names the owner and writer of every state item, the call
paths for deposit, withdrawal, trading, fee withdrawal, market creation,
and reads, and the deployment implications for upgrades, immutable
parameters, blast radius, and resource limits.

SO4-Markets#892 is still open, so this topology is a recommendation pending its
outcome.
@drips-wave

drips-wave Bot commented Oct 20, 2026

Copy link
Copy Markdown

@medunrebecca-dot 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! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants