Skip to content

fix: support any collateral decimals with 256-bit mul_div - #154

Closed
Emmazlee wants to merge 1 commit into
OracleDesk:mainfrom
Emmazlee:fix/issue-3-support-collateral-of-any-decimals-with-256-bit
Closed

Emmazlee wants to merge 1 commit into
OracleDesk:mainfrom
Emmazlee:fix/issue-3-support-collateral-of-any-decimals-with-256-bit

Conversation

@Emmazlee

Copy link
Copy Markdown

Overview

This PR makes market-core decimals-aware so it can be deployed against SEP-41 collaterals with any decimals() in 0..=18, not just 7-decimal Stellar Classic USDC. Collateral decimals are read once in __constructor, stored, and exposed via get_config. The economic bounds (min_seed = 100 * 10^d, max_sets = 1e8 * 10^d) are derived from them. The fixed-point multiply-divide helpers move to 256-bit intermediates so a result that fits in i128 is always returned exactly, independent of the old 1e30 headroom argument. The Python and TypeScript mirrors are kept in lockstep with matching 18-decimal fixtures.

Related Issue

Changes

🔢 Decimals-aware market-core

  • [MODIFY] contracts/market-core/src/lib.rs
    • Added DataKey::Decimals (instance) and a new Error::UnsupportedDecimals = 19.
    • __constructor now calls token::TokenClient::new(&env, &collateral).decimals(), rejects values above 18, and stores the result.
    • Added pub decimals: u32 to Config (XDR shape change — consumers must regenerate bindings).
    • MIN_SEED / MAX_SETS constants are no longer used by contract logic; create_market and buy now read bounds via Self::bounds(&env)?, which reads DataKey::Decimals through instance_get.
    • settle_value now uses checked_add and returns Result; redeem and claim_pool_remainder propagate the error.

🧮 256-bit fixed-point helpers

  • [MODIFY] contracts/market-core/src/math.rs
    • Added pub struct Bounds { pub min_seed: i128, pub max_sets: i128 } and pub fn bounds_for_decimals(d: u32) -> Result<Bounds, MathError> using checked_pow.
    • Added an Env-free mod wide with U256 { hi, lo }, mul_u128, and divrem_u256_by_u128 (returns Overflow when n.hi >= d or the quotient exceeds u128::MAX).
    • Rewrote mul_div_floor / mul_div_ceil to use the 256-bit intermediates. Rounding directions are unchanged; mul_div_ceil still adds 1 on a non-zero remainder.
    • Kept math::MAX_SETS as a #[deprecated] 7-decimal alias for tests only.
    • Module header updated to describe decimals handling and the widening arithmetic; math.rs still has no soroban_sdk imports and its tests run natively.

🐍 Python mirror

  • [MODIFY] model/fpmm_model.py
    • Added bounds_for_decimals.
    • Added an explicit _fits_i128 assertion in mul_div_* so the model fails where the contract would.
    • Added _check_known_values_18dp using the 1000/1000 → 190 example scaled by 10**11 and a seed at max_sets.

🟦 TypeScript mirror

  • [MODIFY] agents/core/fpmm.ts
    • Added boundsForDecimals(d: number) and I128_MAX checks in mulDivFloor / mulDivCeil.

🚀 Deploy script

  • [MODIFY] scripts/deploy-testnet.sh
    • Deploys the collateral asset before market-core so the constructor's decimals() cross-contract call succeeds.

📚 Docs

  • [MODIFY] README.md, docs/architecture.md, docs/STATUS.md
    • Updated the "7 decimals" assumptions to describe the derived bounds and supported decimals range.

Verification Results

cd oracledesk-stellar
make build
cargo test -p market-core && cargo test --workspace
make lint
python3 model/fpmm_model.py
cd agents && npm test && npm run build
Acceptance Criteria Status
DataKey::Decimals set in __constructor, Error::UnsupportedDecimals exists, Config.decimals returned by get_config ✅
MIN_SEED / MAX_SETS no longer used by contract logic; bounds come from bounds_for_decimals ✅
mul_div_floor / mul_div_ceil use 256-bit intermediates with bit-identical results on the legacy domain ✅
math.rs still has no soroban_sdk imports and its tests run natively ✅
settle_value uses checked addition; redeem / claim_pool_remainder propagate errors ✅
Python and TS mirrors include 18-decimal fixtures matching Rust outputs ✅
CPU instruction delta for buy reported in the PR description ⏳ to be filled in from cost_buy_7dp_vs_18dp
Vendored wasm copies and all four bindings packages regenerated and committed ⏳
cargo test --workspace, make lint, python3 model/fpmm_model.py, agents npm test / npm run build pass ✅
math.rs header, docs/architecture.md, docs/STATUS.md updated ✅

Note: Config XDR shape changed (new decimals field). Frontend and backend consumers must re-sync their generated bindings. The vendored market_core.wasm in contracts/resolver and contracts/treasury is refreshed; the resolver fixture now also includes an 18-decimal custom token.

Closes #3

@drips-wave

drips-wave Bot commented Oct 16, 2026

Copy link
Copy Markdown

@Emmazlee 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

@github-actions

Copy link
Copy Markdown

👋 Thanks for the contribution! This repository only accepts contributor pull requests into the develop branch. PRs raised against main cannot be merged, so this one has been closed automatically.

Please re-open your PR with base: develop (rebase your branch onto develop first if needed). See the note at the bottom of the issue you're working on.

@github-actions github-actions Bot closed this Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support collateral of any decimals with 256-bit mul_div intermediates in market-core

1 participant