You are integrating the **Ryze offline quote SDK** (TypeScript binding) into this repository so it can compute Ryze AMM **swap 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 Node.js project — plain ESM `.mjs` with the SDK's shipped type declarations is fine; no TypeScript build step is required — 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 TypeScript 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-typescript--.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 (one caveat: `npm install` itself records a generated `file:` entry in package.json / package-lock.json — that is expected npm behavior; the rule applies to files YOU write). ## Distribution layout & wiring ```text $RYZE_SDK_DIST/ package.json # name "@ryze-protocol/sdk", "type": "module", main dist/ryze.js dist/ryze.js # the binding (ESM); dist/ryze.d.ts has the full typed API lib/ryze_native.node # N-API addon that hosts the engine lib/libryzesdk.* # the closed-source native quote engine (.dylib/.so/.dll) examples/quote.mjs # smoke example you can run as-is ``` **Wiring.** Install the unpacked folder as a dependency: ```bash npm install "$RYZE_SDK_DIST" # installs as @ryze-protocol/sdk # standalone mode first: npm init -y && npm pkg set type=module ``` - Requires Node >= 18. The package is **ESM-only** — use `import`, not `require`. - The native addon resolves from the package's own `lib/` folder automatically. Only if you relocate files, point `RYZE_SDK_NODE_ADDON` at `ryze_native.node` (or call `configure({ addonPath })` before the first quote). - Every quote function is **synchronous** and CPU-only. On invalid input it throws `RyzeSDKError` — `err.message` is the engine's error text, `err.details.operation` names the native call. ## The API you are integrating ```ts import { quoteSwap, type SwapRequest, type SwapResponse } from "@ryze-protocol/sdk"; function quoteSwap(request: SwapRequest): SwapResponse; // sync; throws RyzeSDKError interface SwapRequest { pool: WeightedPoolJSON; tokenIn: string; // token address; must be one of pool.assets tokenOut: string; amountIn?: string; // REQUIRED when isExactIn=true — base units of tokenIn amountOut?: string; // REQUIRED when isExactIn=false — base units of tokenOut isExactIn: boolean; numHops?: number; // total hops of the enclosing route; use 1 for a direct swap isIntentSwap?: boolean; // price with the pool's discounted intent fee rates feeMultiplierWad?: string; // per-account fee discount (wad); omit for full fees tokenPrices?: TokenPriceJSON[]; // REQUIRED for SmartShield pools wbrConfig?: unknown; // REQUIRED for SmartShield pools; opaque pass-through } interface SwapResponse { amountIn: string; // engine-echoed input (exact-out mode: the answer) amountOut: string; // exact-in mode: the answer poolAddress: string; // lowercase path: string[]; // [tokenIn, tokenOut], lowercase feeDetails: FeeDetailsJSON; } interface FeeDetailsJSON { // every component: { token: string; amount: string } swapFee: FeeAssetJSON; // charged in tokenIn takerFee: FeeAssetJSON; wbfFee: FeeAssetJSON; // weight-breaking fee slippageFee: FeeAssetJSON; wbrFee: FeeAssetJSON; // weight-breaking REWARD, denominated in tokenOut } ``` Pool schema (`WeightedPoolJSON`), token prices, and every other type ship in `dist/ryze.d.ts` — the fixture below is a valid `pool`/`tokenPrices`/`wbrConfig` triple, so you can also read the schema off it. ## 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. - SmartShield pools (`smartShieldEnabled: true` — the fixture pool is one) price against the oracle. For them BOTH `tokenPrices` AND `wbrConfig` are REQUIRED and travel together: omitting `tokenPrices` fails with `WeightedPoolMathError("Token price not found")`; supplying prices but omitting `wbrConfig` fails with `WeightBreakingRewardError("Config not loaded")`. Pass `wbrConfig` through VERBATIM as one opaque JSON object, exactly as the Ryze API returns it — never interpret, rename, filter, or re-derive its fields. - `tokenPrices` are USD per WHOLE token in wad (1e18 = $1.00) — e.g. the fixture prices USDC at $0.9997 and WETH at $1662.50. - 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. - **Exact-in vs exact-out** is one flag: `isExactIn` decides which of `amountIn`/`amountOut` you provide; the engine returns the other. - **numHops** is the total hop count of the enclosing route (fees derate for multi-hop routes). A direct pool swap is `1`. For an N-hop route your router quotes each hop separately with `numHops: N`, feeding each hop's output into the next. - The weight-breaking reward (`wbrFee`) is denominated in the OUTPUT token and is paid on top of `amountOut` on-chain — an aggregator comparing venues should rank by `amountOut + wbrFee.amount` when the reward token equals tokenOut. ## Deliverable 1 — the integration module Expose one narrow, typed function shaped for a router/aggregator integration — for example `quoteRyzeSwap(pool, tokenIn, tokenOut, amountIn, opts)` returning the output amount plus the fee breakdown, and its exact-out twin — where `opts` carries the pass-through inputs (`numHops`, `isIntentSwap`, `feeMultiplierWad`, `tokenPrices`, `wbrConfig`; the last two are required for SmartShield pools). 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 fee re-derivation, 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 `scripts/verify-ryze-swap.mjs` (repo mode) or `verify-ryze-swap.mjs` (standalone mode); run it with plain `node` — no TypeScript toolchain or bundler is needed for the script itself. 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. For the negative case: wrap each negative call in try/catch and check `err.message`. 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 | exact-in: `tokenIn`=USDC, `tokenOut`=WETH, `amountIn`="1000000000", `isExactIn`=true, `numHops`=1, with fixture `tokenPrices`+`wbrConfig` | `amountOut` = "599970135642316143" | | 2 | same response | `feeDetails`: swapFee.amount="1000000", takerFee.amount="500000", wbfFee.amount="199956", slippageFee.amount="550367" (all in USDC); wbrFee.amount="0" with wbrFee.token = WETH (lowercase) | | 3 | same response | `path` = [USDC, WETH] both lowercase | | 4 | exact-out: same pair, `amountOut`="599970135642316143", `isExactIn`=false, `numHops`=1, prices+wbrConfig | `amountIn` = "999999042" (round-trips to ~the 1000 USDC of #1) | | 5 | NEGATIVE — repeat #1 but omit `tokenPrices` entirely | the call FAILS and the error text contains `Token price not found` | ## 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` (no literal SDK paths in any file you wrote; npm's generated `file:` entry is expected) - [ ] 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 - [ ] Exact-in and exact-out both work through the public function(s) you exposed - [ ] The negative test asserts the failure, i.e. a successful quote there is a FAIL - [ ] You actually RAN the verification script and pasted its output in your summary