fix(finance/order-book): decimal-agnostic pricing with base_lot_size + quote_lot_size#66
Merged
Merged
Conversation
The base mint was using 6 decimals matching USDC, but the real NVDAx token on-chain has 8 decimals. Split MINT_DECIMALS into BASE_DECIMALS=8 and QUOTE_DECIMALS=6 and add a comment explaining the price resolution consequence: with these two mints, one tick = 10^(8-6) = $100/share. https://claude.ai/code/session_01G6iaAjzg8aoFwe8ZWWG9VR
Without base_lot_size, raw-unit prices for NVDAx (8 dec) / USDC (6 dec) produce a $100 minimum tick — unusable at real NVIDIA prices. Fix: quantities are now in lots (1 lot = base_lot_size raw base tokens); setting base_lot_size = 10^(d_base - d_quote) = 100 makes `price` equal the human-readable USDC/share rate with a $1 minimum tick. All base-token flows updated with u128 overflow-safe arithmetic per Solana best practices: ask lock in place_order, bid fill receipt, ask fill maker credit, and ask cancel refund in cancel_order. Tests split MINT_DECIMALS into BASE_DECIMALS=8 / QUOTE_DECIMALS=6, add BASE_LOT_SIZE=100, update all assertions, and add a rejection test for zero base_lot_size. https://claude.ai/code/session_01G6iaAjzg8aoFwe8ZWWG9VR
Adopts the Serum/Openbook two-lot model. Previously the program was only
correct when d_base >= d_quote (e.g. NVDAx/USDC), because quote amounts
were computed as price × quantity raw quote tokens with no scaling on the
quote side.
With quote_lot_size, all raw-quote amounts become:
raw_quote = price × quantity × quote_lot_size
Choosing base_lot_size = 10^max(d_base - d_quote, 0) and
quote_lot_size = 10^max(d_quote - d_base, 0)
makes price the human-readable quote/base rate and tick_size=1 one atomic
price increment regardless of which mint has more decimals.
NVDAx (8 dec) / USDC (6 dec): base_lot_size=100, quote_lot_size=1 (values unchanged)
WBTC (8 dec) / HD-USDC (18 dec): base_lot_size=1, quote_lot_size=10^10
Changes: InvalidQuoteLotSize error; quote_lot_size field on Market; new
param in initialize_market; bid lock, gross_quote, and locked_for_this_fill
in place_order all gain x quote_lot_size; bid cancel refund in cancel_order
gains x quote_lot_size; settle_funds untouched (already operates on raw
amounts in unsettled_*). Tests add QUOTE_LOT_SIZE=1, update all call sites
and assertions, add rejection test for zero quote_lot_size.
https://claude.ai/code/session_01G6iaAjzg8aoFwe8ZWWG9VR
"Balanced-by-construction" lumped critbit in with red-black/AVL trees, which it isn't — critbit never rotates or recolours. Reframe the §8 heading and prose around the accurate property: a radix trie whose depth is bounded by the key's bit width (<=128), so it cannot degenerate under adversarial insert order. The security conclusion is unchanged. https://claude.ai/code/session_01G6iaAjzg8aoFwe8ZWWG9VR
Resolves the PR #66 conflict caused by main renaming defi/ -> finance/. Git rename detection carried the order-book changes (base_lot_size, quote_lot_size, README critbit wording) into finance/order-book/ cleanly; verified cargo check passes at the new path. https://claude.ai/code/session_01G6iaAjzg8aoFwe8ZWWG9VR
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.
Follow-up to #41. Makes the order-book example price correctly for any pair of token decimals, surfaced when modelling its own NVDAx/USDC market with real on-chain decimals.
The problem
NVDAx has 8 decimals on-chain; USDC has 6. The program stored
priceas raw quote units per raw base unit, so the minimum representable tick was10^(d_base − d_quote) = 10^2 = $100/share— unusable at realistic prices (~$130). Tests also used a singleMINT_DECIMALS = 6for both mints, hiding the mismatch.The fix — two-lot model (Serum/Openbook)
Quantities are denominated in base lots and prices in quote lots, so neither side is tied to a mint's raw decimals:
Choosing
base_lot_size = 10^max(d_base − d_quote, 0)andquote_lot_size = 10^max(d_quote − d_base, 0)makespricethe human-readable quote/base rate andtick_size = 1one atomic increment — for any decimal combination, includingd_quote > d_base.base_lot_size = 100,quote_lot_size = 1base_lot_size = 1,quote_lot_size = 10^10Changes
state/market.rs—base_lot_size+quote_lot_sizefields with decimal-math docs.instructions/initialize_market.rs,lib.rs— both lot-size params,> 0validation, storage.errors.rs—InvalidBaseLotSize,InvalidQuoteLotSize.instructions/place_order.rs— base flows ×base_lot_size; quote flows (bid lock,gross_quote, price-improvement rebate) ×quote_lot_size. All viau128intermediates to stay overflow-safe.instructions/cancel_order.rs— ask refund ×base_lot_size, bid refund ×quote_lot_size.tests/test_order_book.rs—BASE_DECIMALS = 8/QUOTE_DECIMALS = 6,BASE_LOT_SIZE = 100,QUOTE_LOT_SIZE = 1, all assertions updated, plus rejection tests for zero base/quote lot size.README.md— reword the critbit section: it's a depth-bounded radix trie (depth ≤ key bit width), not a self-balancing BST. Same security conclusion (no adversarial-insert degeneration), stated accurately.settle_fundsis untouched — it transfersunsettled_*, already stored in raw tokens.Verification
cargo checkis clean at the newfinance/order-book/path. The full LiteSVM suite requires the Solana BPF toolchain (anchor build), which runs in CI.https://claude.ai/code/session_01G6iaAjzg8aoFwe8ZWWG9VR