You are integrating the **Ryze offline quote SDK** (Rust binding) into this repository so it can compute Ryze AMM **proportional join quotes** locally — no HTTP call, no RPC, no network at all. Study the repo first and match its conventions (module layout, config handling, error types, logging, test framework). If you are instead running in an empty directory, create a minimal standalone binary crate (`cargo init --bin` — `cargo new` refuses a pre-existing directory) whose only purpose is the verification script defined at the end — the acceptance gate is identical either way. ## What the SDK is The Ryze quote SDK is a precompiled native library (`libryzesdk`) plus a thin Rust binding. It reproduces the exact quote math of the on-chain Ryze AMM (an oracle-anchored weighted-pool DEX on Base): same integers, same rounding, same errors. You hand it a snapshot of a pool's state; it returns the quote synchronously, in microseconds, with zero I/O. This replaces quoting through Ryze's hosted Router API on the hot path — same numbers, none of the latency. It is distributed as a platform tarball: `ryze-sdk-rust--.tar.gz` (darwin-arm64, linux-amd64, …). Obtain the tarball matching your deploy target from the Ryze team. Throughout this prompt `$RYZE_SDK_DIST` means the absolute path of the UNPACKED distribution folder — read it from the environment; never hardcode it (single exception: the literal `path` in Cargo.toml, as documented above). ## Distribution layout & wiring ```text $RYZE_SDK_DIST/ Cargo.toml # package "ryze-sdk", lib name "ryze_sdk", edition 2021 src/lib.rs # the binding: typed structs + libloading, fully documented lib/libryzesdk.* # the closed-source native quote engine (.dylib/.so/.dll) examples/quote.rs # smoke example (cargo run --example quote) ``` **Wiring.** Add a path dependency. Cargo path deps must be literal paths, so substitute the actual value of `$RYZE_SDK_DIST` when you edit Cargo.toml (this is the one place an absolute path is acceptable): ```toml [dependencies] ryze-sdk = { path = "/absolute/path/to/ryze-sdk-rust--" } serde_json = "1" # you need Value for the fixture / wbrConfig pass-through ``` - Construct the engine with `RyzeSdk::new()` — it finds `libryzesdk` inside the crate's own `lib/` folder. `RYZE_SDK_LIB=/abs/path/to/libryzesdk.` overrides, and `RyzeSdk::from_library_path(..)` is the programmatic equivalent. - `RyzeSdk` is cheap to clone and safe to share; calls are **synchronous**. - Errors are `ryze_sdk::RyzeSdkError` (thiserror). Engine rejections surface as `RyzeSdkError::Native { operation, message }` — `message` is the engine's text. - Prefer building the pool from the fixture JSON with `serde_json::from_value` instead of hand-writing struct literals (`WeightedPool` has a flattened `extra` map you'd otherwise have to fill with `BTreeMap::new()`). ## The API you are integrating ```rust use ryze_sdk::{RyzeSdk, ProportionalJoinRequest, ProportionalJoinResponse, WeightedPool}; let sdk = RyzeSdk::new()?; let resp: ProportionalJoinResponse = sdk.quote_proportional_join(&req)?; // sync pub struct ProportionalJoinRequest { pub pool: WeightedPool, pub amounts_in: Vec, // one entry per pool asset, IN POOL ASSET ORDER } pub struct ProportionalJoinResponse { pub pool_shares_out: String, // LP tokens minted (18 decimals) pub amounts_used: Vec, // amounts actually consumed, pool asset order pub pool_address: String, // lowercase } ``` Proportional joins are fee-free and oracle-free: no token prices, no wbrConfig, no fee details in the response. The engine deposits at the pool's current balance ratio; the LIMITING asset decides, and surplus on the other side is left unused (`amounts_used <= amounts_in` per entry). Deserialize the fixture's `pool` with `serde_json::from_value` rather than hand-writing struct literals. ## Non-negotiable semantics - Every amount, balance, weight, fee, and price is a **base-10 integer string** in base units. Never let a float touch any of these values — not in parsing, not in display math inside the quote path. - "Wad" fields are 18-decimal fixed point: 1e18 = 1.0 (= 100% for rates/weights). - `pool` is a point-in-time snapshot. In production hydrate it from the Ryze API (`GET {RYZE_API_URL}/pools` — mainnet `https://mainnet.api.ryze.pro/api`, testnet `https://sepolia.api.ryze.pro/api`) or your own indexer, and re-quote when state changes. The SDK is deterministic: stale input means stale quote. The verification script below uses the embedded fixture INSTEAD of any network fetch. - `pool.assets` order is canonical — preserve it in every array you build. - This operation uses only `pool` from the fixture. `tokenPrices`/`wbrConfig` are included in the fixture so that all four Ryze SDK prompts share one identical fixture — this operation simply ignores them. - Responses echo all addresses in **lowercase**. Compare addresses case-insensitively everywhere. - Engine errors are terse Solidity-style strings (`InvalidSlippage()`, `WeightedPoolMathError("...")`). Treat them as opaque: assert on them in tests, never branch on them in production logic. - `amountsIn` MUST follow the pool's asset order (the order of `pool.assets`). - The engine joins at the pool's CURRENT balance ratio. The limiting asset determines the shares; any surplus of the other asset is simply not consumed — always surface `amountsUsed` to the caller so the UI/executor can refund or withhold the surplus. - Proportional joins charge no fees and need no oracle prices. ## Deliverable 1 — the integration module Expose one narrow, typed function — for example `quoteRyzeProportionalJoin(pool, amountsIn)` — returning the LP shares minted and the amounts actually consumed. Internally it must do nothing but validate inputs, assemble the SDK request, call the SDK, and map the response into your codebase's own types. Do NOT post-process the numbers: no rounding, no float conversion — the SDK's integers are the on-chain truth. Example names above are illustrative — adapt them to this language's and this repo's conventions (snake_case in Rust, exported PascalCase in Go, camelCase in TypeScript). ## Deliverable 2 — standalone verification script (the acceptance gate) Create a cargo example `examples/verify_ryze_join_proportional.rs` in the consuming crate (repo mode) or `src/main.rs` of a fresh `cargo new` binary (standalone mode); run with `cargo run`. The script must be fully self-contained: it embeds the fixture below verbatim, calls the SDK through YOUR integration module (not around it), checks every assertion in the table, prints one `PASS ` / `FAIL expected= got=` line per assertion (a table row that bundles several fields may emit one line per field), and exits 0 only if every assertion passed. Golden values below were produced by this exact SDK build from this exact fixture — they are deterministic. An off-by-one is a real failure; investigate your request construction before suspecting the goldens. ### Fixture (embed verbatim — tokens are USDC, 6 decimals, and WETH, 18 decimals) ```json { "pool": { "address": "0x0000000000000000000000000000000000001001", "totalSupplyLP": "1000000000000000000000", "parameters": { "swapFeeWad": "1000000000000000", "takerFeeWad": "500000000000000", "smartShieldEnabled": true, "smartShieldParams": { "thresholdWeightDiffWad": "10000000000000000", "minOracleWeightWad": "150000000000000000" }, "intentSwapFeeWad": "400000000000000", "intentTakerFeeWad": "200000000000000" }, "assets": [ { "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "balance": "1000000000000", "weight": "500000000000000000", "decimals": 6, "liability": "0", "custody": "0", "externalLiquidityRatio": "2000000000000000000", "liquidityBands": [ { "thresholdWad": "1000000000000000000000", "ratioWad": "2000000000000000000", "minSlippageWad": "100000000000000" }, { "thresholdWad": "100000000000000000000000", "ratioWad": "1500000000000000000", "minSlippageWad": "200000000000000" }, { "thresholdWad": "1000000000000000000000000", "ratioWad": "1200000000000000000", "minSlippageWad": "400000000000000" } ] }, { "tokenAddress": "0x4200000000000000000000000000000000000006", "balance": "500000000000000000000", "weight": "500000000000000000", "decimals": 18, "liability": "0", "custody": "0", "externalLiquidityRatio": "2000000000000000000", "liquidityBands": [ { "thresholdWad": "1000000000000000000000", "ratioWad": "2000000000000000000", "minSlippageWad": "100000000000000" }, { "thresholdWad": "100000000000000000000000", "ratioWad": "1500000000000000000", "minSlippageWad": "200000000000000" }, { "thresholdWad": "1000000000000000000000000", "ratioWad": "1200000000000000000", "minSlippageWad": "400000000000000" } ] } ] }, "tokenPrices": [ { "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "priceWad": "999700000000000000" }, { "token": "0x4200000000000000000000000000000000000006", "priceWad": "1662500000000000000000" } ], "wbrConfig": { "wad": "1000000000000000000", "maxWbfWad": "990000000000000000", "maxExpArgWad": "20000000000000000000", "maxFeeBps": "10000", "maxSkewPenaltyWad": "40000000000000000", "maxWbfRateBps": "400", "maxWeightWad": "1000000000000000000", "weightThresholdWad": "550000000000000000", "weightThresholdLambdaWad": "600000000000000000", "weightThresholdLogWad": "800000000000000000", "weightThresholdSigmaWad": "550000000000000000", "weightMultiplierLogWad": "10000000000000000000", "weightHalfWad": "500000000000000000", "bQuoteAdjustmentWad": "0", "bOverflowCap": "1000000000000000000000000000000", "bSAnchorS": [ "250000000000000000000", "1000000000000000000000", "10000000000000000000000", "100000000000000000000000", "500000000000000000000000", "1000000000000000000000000", "2500000000000000000000000", "5000000000000000000000000" ], "bSAnchorBpsWad": [ "0", "2000000000000000000", "3000000000000000000", "6000000000000000000", "11000000000000000000", "15000000000000000000", "25000000000000000000", "45000000000000000000" ], "bSNumAnchors": "8", "fPowMultiplier": "5", "fPowExponentWad": "2500000000000000000", "fLogExponentMultiplier": "10", "fLogOverflowDivisor": "10", "smoothstepCoeff1": "3000000000000000000", "smoothstepCoeff2": "2000000000000000000", "wrrDivisor": "2", "wrrMaxBps": "200", "wrrWeightMaxWad": "850000000000000000", "basisPointsDivisor": "10000", "bpsToWadMultiplier": "100000000000000" } } ``` ### Assertions | # | Call | Expected (assert EXACTLY) | |---|---|---| | 1 | balanced: `amountsIn` = ["1000000000", "500000000000000000"] (1,000 USDC + 0.5 WETH — the pool ratio) | `poolSharesOut` = "1000000000000000000" (exactly 1 LP = 0.1% of the 1000-LP supply) | | 2 | same response | `amountsUsed` = ["1000000000", "500000000000000000"] (everything consumed) | | 3 | imbalanced: `amountsIn` = ["2000000000", "500000000000000000"] (2,000 USDC but still 0.5 WETH) | `poolSharesOut` = "1000000000000000000" AND `amountsUsed` = ["1000000000", "500000000000000000"] — WETH limits, the extra 1,000 USDC is NOT consumed | | 4 | both responses above | `poolAddress` = the fixture pool address (lowercase) | ## Hard rules - No network calls anywhere in the quote path or the verification script. - No floats in amount handling; no silent re-rounding of SDK outputs. - Do not modify, move, or rename anything inside `$RYZE_SDK_DIST`. - Do not reimplement or "simplify" the quote math from observed outputs — the SDK is the only source of Ryze numbers in this codebase. - Keep the wbrConfig object opaque wherever it appears. ## Acceptance checklist (verify each before declaring done) - [ ] SDK wired via `$RYZE_SDK_DIST` (only Cargo.toml may contain the literal SDK path) - [ ] Integration module exposes the typed function(s) described above, in this repo's own conventions - [ ] Verification script exists, runs offline, prints one PASS/FAIL line per assertion, and exits 0 with every assertion passing - [ ] All amounts handled as integer strings end-to-end - [ ] The imbalanced case (#3) is asserted — it proves limiting-ratio semantics survive your mapping layer - [ ] You actually RAN the verification script and pasted its output in your summary