@whworjs7946/clmsr-v0
v1.13.0
Published
TypeScript SDK for CLMSR market calculations and utilities
Maintainers
Readme
CLMSR TypeScript SDK
📈 CLMSR (Conditional Liquidity Market Maker) TypeScript SDK for prediction market systems
🎯 Overview
TypeScript SDK for CLMSR (Constant Logarithmic Market Scoring Rule) prediction market calculations.
v1.11.0 adds overlay-fee awareness across all pricing helpers while keeping the underlying mathematics pure and deterministic.
🔄 What's new in v1.11.0
- Overlay fee descriptors (e.g.
{"policy":"percentage","params":{"bps":"50"}}) are now propagated through the SDK.- All buy/sell helpers return
feeAmount,feeRate, and richfeeInfo. - Pure helpers in
fees.tsallow frontends to preview on-chain fee policies without importing ABIs.
- All buy/sell helpers return
- Flexible fee metadata: descriptors can embed labels such as
"name": "PercentFeePolicy50bps", which the SDK surfaces to your UI. - Ethers v6 dependency: the SDK now bundles lightweight resolver helpers built on top of
[email protected].
⚠️ TypeScript breaking change: the additional fields on
OpenCostResult,IncreaseCostResult,DecreaseProceedsResult, andCloseProceedsResultare mandatory.
Update any local mocks or adapters that instantiate these interfaces before upgrading.
🔁 Prior release: v1.8.0 settlement enhancement
- Previous:
calculateClaim(position, settlementLowerTick, settlementUpperTick) - Enhanced:
calculateClaim(position, settlementTick)
This change allows frontends to display the exact settlement price instead of just knowing a winning range, improving user experience and transparency.
🚀 Key Features
- Pure functional calculations: TypeScript implementation of contract view functions
- Fixed quantity limits: Corrected scaling for market-specific limits (α × 0.13 × 1000)
- Large trade support: Proper handling of quantities up to 26,000 USDC (α=200) with accurate calculations
- Enhanced error handling: Clear validation messages with correct limit display
- Improved scaling: Consistent MathUtils-based decimal handling for all conversions
- Optimized type system: Minimal required fields for better performance
- Inverse function calculation: Mathematical inverse function to calculate quantity from target cost
- High-precision arithmetic: Accurate fixed-point operations based on Big.js
- LMSR compliant: Implements all LMSR mathematical properties
- Comprehensive testing: 25+ test cases including large-scale trading scenarios
📦 Installation
npm install @whworjs7946/clmsr-v0🏁 Quick Start
1. Basic Usage
import {
CLMSRSDK,
toWAD,
toMicroUSDC,
mapMarket,
mapDistribution,
} from "@whworjs7946/clmsr-v0";
// SDK instance creation
const sdk = new CLMSRSDK();
// Market configuration (converted from raw data)
const rawMarket = {
liquidityParameter: "1000000000000000000000", // 1000 * 1e18 (WAD)
minTick: 100000, // $1000.00
maxTick: 140000, // $1400.00
tickSpacing: 100, // $1.00 increments
};
const market = mapMarket(rawMarket);
// Distribution data (raw data from GraphQL - unified scaling)
const rawDistribution = {
totalSum: "400000000000000000000", // WAD format (18 decimals)
minFactor: "1000000000000000000", // WAD format (18 decimals)
maxFactor: "2000000000000000000", // WAD format (18 decimals)
avgFactor: "1500000000000000000", // WAD format (18 decimals)
totalVolume: "50000000", // raw USDC (6 decimals) - 50 USDC
binFactors: ["1000000000000000000", "1500000000000000000" /* ... */], // WAD
binVolumes: ["1000000", "2000000" /* ... */], // raw USDC (6 decimals)
tickRanges: ["100000-100100", "100100-100200" /* ... */], // each = [tick, tick+spacing)
};
const distribution = mapDistribution(rawDistribution);
// Calculate cost for betting 50 USDC on [$1150-$1250] range
const result = sdk.calculateOpenCost(
115000, // lowerTick ($1150.00)
125000, // upperTick ($1250.00)
toUSDC("50"), // 50 USDC
distribution,
market
);
console.log(`Cost: ${result.cost.toString()} USDC`);
console.log(`Average price: ${result.averagePrice.toString()}`);2. Large Trade Support with Dynamic Limits
// Market-specific maximum quantity (α × 0.13 × 1000)
// For α = 200: max = 26,000 USDC
// For α = 1000: max = 130,000 USDC
// Large quantities within limits are handled safely
const largeResult = sdk.calculateOpenCost(
115000,
125000,
toUSDC("25000"), // 25,000 USDC (within α=200 limit)
distribution,
market
); // ✅ Processes normally with automatic chunking
// Exceeding market limits throws clear error
try {
sdk.calculateOpenCost(115000, 125000, toUSDC("30000"), distribution, market);
} catch (error) {
console.log(error.message);
// "Quantity too large. Max per trade = 26000 USDC (market limit: α × 0.13 × 1000)"
}3. Inverse Function Calculation
// Calculate how much can be bet when willing to spend up to 300 USDC (total including fees)
const targetCost = toUSDC("300");
const inverseBuy = sdk.calculateQuantityFromCost(
115000,
125000,
targetCost,
distribution,
market
);
console.log(`Buy quantity: ${inverseBuy.quantity.toString()}`);
console.log(
`Actual base cost (excl. fee): ${inverseBuy.actualCost.toString()}`
);
// Calculate how much must be sold to receive 180 USDC after paying fees
const position = {
lowerTick: 115000,
upperTick: 125000,
quantity: toUSDC("800"), // current position size
};
const targetProceeds = toUSDC("180");
const inverseSell = sdk.calculateQuantityFromProceeds(
position,
targetProceeds,
distribution,
market
);
console.log(`Sell quantity: ${inverseSell.quantity.toString()}`);
console.log(
`Actual base proceeds (before fee): ${inverseSell.actualProceeds.toString()}`
);4. Overlay fee helpers (pure functions)
import { FeePolicyKind } from "@whworjs7946/clmsr-v0";
import { CLMSRSDK } from "@whworjs7946/clmsr-v0";
// Descriptor published by on-chain fee policy contracts or the subgraph
const percentageDescriptor = JSON.stringify({
policy: "percentage",
params: {
bps: "150", // 1.5%
name: "OnePointFive",
},
});
// Attach the descriptor to the market object (e.g. value fetched from the subgraph)
market.feePolicyDescriptor = percentageDescriptor;
const sdk = new CLMSRSDK();
const open = sdk.calculateOpenCost(
115000,
125000,
toMicroUSDC("50"),
distribution,
market
);
if (open.feeInfo.policy === FeePolicyKind.Percentage) {
console.log(
open.feeAmount.toString(), // fee amount (micro USDC)
open.feeRate.toString(), // decimal fee rate
open.feeInfo.bps?.toString() // raw basis points (150)
);
const totalCostWithFee = open.cost.plus(open.feeAmount);
console.log(totalCostWithFee.toString());
}ℹ️ As long as you forward the descriptor string from the chain or subgraph, the SDK can resolve the fee calculation, display labels, and expose the raw basis points without any additional configuration. Keep the descriptor in sync with the latest on-chain value to guarantee accurate previews.
📖 API Reference
Data Types
Raw Types (Received from GraphQL/Subgraph)
interface MarketDistributionRaw {
// Required fields for calculations
totalSum: string; // WAD format (18 decimals) - "400000000000000000000"
binFactors: string[]; // WAD format array - ["1000000000000000000", ...]
// Optional fields (informational only)
minFactor?: string; // WAD format (18 decimals) - "1000000000000000000"
maxFactor?: string; // WAD format (18 decimals) - "2000000000000000000"
avgFactor?: string; // WAD format (18 decimals) - "1500000000000000000"
totalVolume?: string; // raw USDC (6 decimals) - "50000000"
binVolumes?: string[]; // raw USDC array - ["1000000", "2000000", ...]
tickRanges?: string[]; // tick range array - ["100000-100100", ...]
}
interface MarketRaw {
liquidityParameter: string; // WAD format - "1000000000000000000000"
minTick: number;
maxTick: number;
tickSpacing: number;
feePolicyDescriptor?: string;
}SDK Calculation Types (Big Objects)
interface MarketDistribution {
// Required fields for calculations
totalSum: WADAmount; // WAD calculation value (18 decimals) - core calculation
binFactors: WADAmount[]; // WAD format bin factor array (18 decimals) - core calculation
// Optional fields (informational only)
minFactor?: WADAmount; // Minimum factor value (WAD, 18 decimals)
maxFactor?: WADAmount; // Maximum factor value (WAD, 18 decimals)
avgFactor?: WADAmount; // Average factor value (WAD, 18 decimals)
totalVolume?: USDCAmount; // Total volume (raw 6 decimals) - informational
binVolumes?: USDCAmount[]; // Bin volume array (raw 6 decimals) - informational
tickRanges?: string[]; // Tick range string array
}
interface Market {
liquidityParameter: WADAmount; // Big object (WAD)
minTick: number;
maxTick: number;
tickSpacing: number;
feePolicyDescriptor?: string;
}Adapter Functions
mapDistribution()
function mapDistribution(raw: MarketDistributionRaw): MarketDistribution;
// Usage example
const dist = mapDistribution(await fetchFromGraphQL(marketId));mapMarket()
function mapMarket(raw: MarketRaw): Market;
// Usage example
const market = mapMarket(await fetchMarketFromGraphQL(marketId));Core Calculation Functions
calculateOpenCost()
Calculate cost to open new position
calculateOpenCost(
lowerTick: number,
upperTick: number,
quantity: USDCAmount,
distribution: MarketDistribution,
market: Market
): OpenCostResultcalculateDecreaseProceeds() / calculateSellProceeds()
Calculate proceeds when decreasing position (both functions use unified internal logic)
calculateDecreaseProceeds(
position: Position,
sellQuantity: USDCAmount,
distribution: MarketDistribution,
market: Market
): DecreaseProceedsResult
calculateSellProceeds(
position: Position,
sellQuantity: USDCAmount,
distribution: MarketDistribution,
market: Market
): DecreaseProceedsResultcalculateQuantityFromCost()
Calculate quantity from target cost (inverse function)
calculateQuantityFromCost(
lowerTick: number,
upperTick: number,
targetCost: USDCAmount,
distribution: MarketDistribution,
market: Market,
includeFees?: boolean
): QuantityFromCostResulttargetCostis the total spending limit including fees.includeFeesset tofalsecalculates based on pure cost excluding fees (defaulttrue).- The returned
actualCostis the pure betting cost (excluding fees), and callingcalculateOpenCostwithquantityshould result inactualCost + feeAmountbeing close totargetCostwithin the same range.
calculateQuantityFromProceeds()
Calculate sell quantity from target proceeds (inverse function)
calculateQuantityFromProceeds(
position: Position,
targetProceeds: USDCAmount,
distribution: MarketDistribution,
market: Market,
includeFees?: boolean
): QuantityFromProceedsResulttargetProceedsis the actual amount you want to receive after fees.includeFeesset tofalsecalculates based on base proceeds excluding fees (defaulttrue).- The returned
actualProceedsis the pre-fee base proceeds, and callingcalculateDecreaseProceedswith the same quantity should result inactualProceeds - feeAmountbeing close totargetProceeds.
calculateClaim()
Calculate claim amount after settlement
calculateClaim(
position: Position,
settlementTick: number
): ClaimResultUtility Functions
Scale Conversion
toWAD(amount: string | number): WADAmount // Convert to 18 decimal WAD
toUSDC(amount: string | number): USDCAmount // Convert to 6 decimal USDC🏗️ Architecture
Unified Scaling Design
┌─────────────────┐ ┌──────────────┐ ┌─────────────┐
│ Subgraph API │───▶│ Adapter │───▶│ SDK Calc │
│ (BigInt→string) │ │ (parse only) │ │ (Big ops) │
└─────────────────┘ └──────────────┘ └─────────────┘
Raw WAD/USDC mapXXX() raw scale- Subgraph Layer: Provides raw-scale BigInt values converted to strings
- Adapter Layer: Simple string → Big object conversion (no scaling)
- SDK Layer: Performs calculations using raw contract scales
Scaling Standards
- Factors: WAD format (18 decimals) - used for LMSR calculations
- USDC Amounts: Raw 6 decimals - quantity, cost, proceeds
- No normalization: All values maintain contract-native scales
Chunking Support
// Internal safeExp use makes large values safe
// Auto chunk splitting when quantity/α > 0.13
const result = sdk.calculateOpenCost(
lowerTick,
upperTick,
toUSDC("10000"), // very large quantity
distribution,
market
); // ✅ Processes normally🧪 Testing
npm test25+ test cases including:
- ✅ Price impact (non-linearity)
- ✅ Range effects
- ✅ Mathematical consistency (pure functions)
- ✅ Inverse function accuracy
- ✅ Claim logic
- ✅ Error handling
- ✅ Large-scale trading (α=200 environment)
- ✅ Dynamic quantity limits
- ✅ Scaling & Chunking
📋 Type Definitions
type WADAmount = Big; // 18 decimal (factor values)
type USDCAmount = Big; // 6 decimal (quantity/cost values)
type Quantity = Big; // 6 decimal
type Tick = number; // tick value
interface Position {
lowerTick: Tick;
upperTick: Tick;
quantity: Quantity;
}📝 Changelog
v1.6.2 (Latest)
- 🔧 Error handling consistency: Unified error types across SDK (ValidationError, CalculationError)
- 📊 Enhanced MathUtils integration: Consistent use of MathUtils functions for all scaling operations
- 🎯 Refined decimal precision: Improved Big.js usage for consistent decimal handling
- ⚡ Code consistency improvements: Eliminated magic numbers, standardized conversion patterns
v1.6.1
- 🔢 Fixed decimal scaling: Corrected quantity limits and scaling issues
- 📈 Enhanced quantity limits: Proper market-specific limit calculations (α × 0.13 × 1000)
- 🛠️ Improved error messages: Clear validation messages with accurate limit display
🔗 Related Links
- Subgraph API: CLMSR Subgraph Documentation
- Contract Integration: Contract Integration Guide
- Quick Start: Quick Start Guide
- Complete Documentation: Main Documentation
