@magistery/sdk
v0.1.4
Published
TypeScript SDK for Magistery, a permissionless prediction market order book on Polygon using USDC and Conditional Tokens.
Maintainers
Readme
@magistery/sdk
TypeScript SDK for Magistery — permissionless prediction market protocol on Polygon. On-chain order book, pluggable resolution, USDC settlement.
Install
npm install @magistery/sdkRequires Node.js 18+.
Quick Start
import { createPublicClient, createWalletClient, http } from "viem";
import { polygon } from "viem/chains";
import { MagisteryClient, DEPLOYED_ADDRESSES } from "@magistery/sdk";
// Read-only client (no wallet needed)
const publicClient = createPublicClient({
chain: polygon,
transport: http("https://polygon-rpc.com"),
});
const sdk = new MagisteryClient({
publicClient,
addresses: DEPLOYED_ADDRESSES,
});
// Read a market
const market = await sdk.getMarket(conditionId);
console.log(market.questionId, market.deadline, market.resolved);With a wallet (for writes)
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0x...");
const walletClient = createWalletClient({
account,
chain: polygon,
transport: http("https://polygon-rpc.com"),
});
const sdk = new MagisteryClient({
publicClient,
walletClient,
addresses: DEPLOYED_ADDRESSES,
});
// Create a market
const conditionId = await sdk.createMarket(
"Will ETH hit $10k by end of 2026?",
2, // binary: YES/NO
1798761600n, // deadline (unix timestamp)
DEPLOYED_ADDRESSES.umaResolver!,
);API Reference
Constructor
new MagisteryClient({
publicClient: PublicClient, // viem public client (Polygon)
walletClient?: WalletClient, // optional, required for writes
addresses: ProtocolAddresses, // use DEPLOYED_ADDRESSES for mainnet
})Reads
| Method | Returns | Description |
|--------|---------|-------------|
| getMarket(conditionId) | Market | Market struct (questionId, deadline, outcomes, resolved, creator, resolver) |
| nextOrderId() | bigint | Next order ID that will be assigned |
| isTrading(conditionId) | boolean | True if market exists and not resolved |
| isResolvable(conditionId) | boolean | True if past deadline and not yet resolved |
| getOrder(orderId) | Order | Order struct (maker, side, price, amount, filled, cancelled) |
| getPosition(owner, conditionId, outcomeIndex) | Position | Token balance for a specific outcome |
| getPositionId(conditionId, outcomeIndex) | bigint | CTF position ID for an outcome |
| bondAmount(resolverType?) | bigint | Current UMA/Kalshi bond requirement (default: "uma") |
| getActiveAssertion(conditionId, resolverType?) | Hex | Active UMA/Kalshi assertion ID (0x0 if none) |
| getKalshiTicker(conditionId) | Hex | Linked Kalshi ticker for a market |
| getPolymarketCondition(conditionId) | Hex | Linked Polymarket condition ID |
Fee Math (pure, no RPC)
| Method | Returns | Description |
|--------|---------|-------------|
| calculateFee(amount) | bigint | Protocol fee (0.1%, rounded up) |
| estimateFillBuyVsBuy(price0, remaining0, price1, remaining1) | FillEstimate | Estimate fill for two opposing buy orders |
| estimateFillBuyVsSell(buyPrice, buyRemaining, sellPrice, sellRemaining) | FillEstimate | Estimate fill for buy vs sell on same outcome |
Writes (require wallet)
Markets
| Method | Returns | Description |
|--------|---------|-------------|
| createMarket(question, outcomeSlotCount, deadline, resolver) | Hex (conditionId) | Create a new market |
| forceVoid(conditionId) | Hex (tx hash) | Void a stale market (90d timeout, 180d emergency) |
| withdrawFees(token) | Hex (tx hash) | Withdraw accumulated fees to treasury recipient |
Orders
| Method | Returns | Description |
|--------|---------|-------------|
| placeBuyOrder(conditionId, outcomeIndex, price, amount) | bigint (orderId) | Place a limit buy order |
| placeBuyOrders(conditionIds[], outcomeIndexes[], prices[], amounts[]) | bigint[] (orderIds) | Batch place buy orders |
| placeSellOrder(conditionId, outcomeIndex, price, amount) | bigint (orderId) | Place a limit sell order |
| fillBuyVsBuy(buyId0, buyId1, fillAmount) | Hex (tx hash) | Match two opposing buy orders |
| fillBuyVsSell(buyId, sellId, fillAmount) | Hex (tx hash) | Match buy and sell on same outcome |
| cancelOrder(orderId) | Hex (tx hash) | Cancel your own order |
| cancelOrders(orderIds[]) | Hex (tx hash) | Batch cancel orders |
| cancelExpiredOrder(orderId) | Hex (tx hash) | Cancel any order on a resolved market |
Approvals
| Method | Returns | Description |
|--------|---------|-------------|
| approveUsdc(spender, amount?) | Hex (tx hash) | Approve USDC spending (default: max) |
| approveCtf(operator) | Hex (tx hash) | Approve CTF token transfers |
Resolution (UMA)
| Method | Returns | Description |
|--------|---------|-------------|
| assertOutcome(conditionId, assertedOutcome) | Hex (tx hash) | Assert market outcome (requires bond) |
| assertOutcomeFor(conditionId, assertedOutcome, asserter) | Hex (tx hash) | Assert on behalf of another (bond from caller) |
| settleAssertion(assertionId, resolverType?) | Hex (tx hash) | Settle after liveness period |
Resolution (Kalshi)
| Method | Returns | Description |
|--------|---------|-------------|
| kalshiLinkMarket(conditionId, ticker) | Hex (tx hash) | Link market to Kalshi ticker |
| kalshiAssertOutcome(conditionId, assertedOutcome) | Hex (tx hash) | Assert via Kalshi resolver |
| kalshiAssertOutcomeFor(conditionId, assertedOutcome, asserter) | Hex (tx hash) | Assert on behalf via Kalshi |
Resolution (Polymarket)
| Method | Returns | Description |
|--------|---------|-------------|
| polymarketLinkMarket(conditionId, polymarketConditionId) | Hex (tx hash) | Link to Polymarket condition |
| polymarketResolve(conditionId) | Hex (tx hash) | Resolve from Polymarket outcome |
Resolution (Operator)
For operator-deployed resolver contracts (community resolution, bot-managed markets).
| Method | Returns | Description |
|--------|---------|-------------|
| operatorResolve(conditionId, winnerIndex, evidenceHash?) | Hex (tx hash) | Resolve directly (operator only) |
| operatorResolveBySig(conditionId, winnerIndex, evidenceHash, nonce, expiry, signature) | Hex (tx hash) | Resolve via EIP-712 signature |
| operatorResolverOperator() | Address | Get the operator address |
| operatorResolverDomainSeparator() | Hex | EIP-712 domain separator |
| operatorResolverNonceUsed(nonce) | boolean | Check if signature nonce is consumed |
Redemption
| Method | Returns | Description |
|--------|---------|-------------|
| redeemWinnings(conditionId, outcomeIndices[]) | Hex (tx hash) | Redeem winning outcome tokens for USDC |
Multi-Outcome (NegRisk)
| Method | Returns | Description |
|--------|---------|-------------|
| createMultiOutcomeMarket(question, labels[], deadline, resolver) | bigint (marketId) | Create multi-outcome market (3+ outcomes) |
| getOutcomeCount(marketId) | bigint | Number of outcomes |
| getOutcomeConditionId(marketId, outcomeIndex) | Hex | ConditionId for a specific leg |
| getAllConditionIds(marketId) | Hex[] | All leg conditionIds |
| getOutcomeLabel(marketId, outcomeIndex) | string | Label for a specific outcome |
| isMultiOutcomeResolved(marketId) | boolean | True if all legs resolved |
Safety Helpers (for frontends)
| Method | Returns | Description |
|--------|---------|-------------|
| isTrustedResolver(conditionId, trustedAddresses[]) | boolean | Check resolver against whitelist |
| isKalshiLinked(conditionId) | boolean | Check if Kalshi market is linked to a ticker |
| isPolymarketLinked(conditionId) | boolean | Check if Polymarket market is linked |
| isPastDeadline(conditionId) | boolean | True if past deadline and not resolved (danger zone) |
Types
interface Market {
questionId: Hex;
conditionId: Hex;
deadline: bigint;
outcomeSlotCount: number;
resolved: boolean;
creator: Address;
resolver: Address;
}
interface Order {
orderId: bigint;
maker: Address;
side: Side; // Side.BUY (0) or Side.SELL (1)
cancelled: boolean;
conditionId: Hex;
outcomeIndex: bigint;
price: bigint; // 6 decimals (1e6 = $1.00)
amount: bigint;
filled: bigint;
}
interface Position {
conditionId: Hex;
outcomeIndex: number;
positionId: bigint;
balance: bigint;
}
interface FillEstimate {
fillAmount: bigint;
cost: bigint;
fee: bigint;
surplus: bigint;
sellerProceeds: bigint;
}
interface ProtocolAddresses {
factory: Address;
orderBook: Address;
treasury: Address;
negRiskAdapter: Address;
umaResolver?: Address;
kalshiResolver?: Address;
polymarketResolver?: Address;
operatorResolver?: Address;
}Constants
import {
DEPLOYED_ADDRESSES, // All 7 mainnet contract addresses
CTF_ADDRESS, // Gnosis CTF (0x4D97...045)
USDC_ADDRESS, // Native USDC (0x3c49...359)
UMA_OOV3_ADDRESS, // UMA Oracle (0x5953...3D)
POLYGON_CHAIN_ID, // 137
PRICE_SCALE, // 1_000_000n ($1.00)
MIN_ORDER_COST, // 10_000n ($0.01)
FEE_BPS, // 10n (0.1%)
BOND_AMOUNT, // 750_000_000n (750 USDC)
LIVENESS, // 7200n (2 hours)
VOID_TIMEOUT, // 7_776_000n (90 days)
EMERGENCY_VOID_TIMEOUT,// 15_552_000n (180 days)
} from "@magistery/sdk";Price Scale
All prices use 6 decimals (USDC decimals). 1_000_000n = $1.00.
| Price | Meaning |
|-------|---------|
| 650_000n | $0.65 (65% probability) |
| 1_000_000n | $1.00 (100% — max) |
| 10_000n | $0.01 (1% — minimum order) |
Approval Flow
Before placing orders, approve the OrderBook to spend your USDC and transfer your CTF tokens:
await sdk.approveUsdc(DEPLOYED_ADDRESSES.orderBook);
await sdk.approveCtf(DEPLOYED_ADDRESSES.orderBook);Before asserting outcomes (UMA/Kalshi), approve the resolver to spend the bond:
const bond = await sdk.bondAmount(); // 750 USDC
await sdk.approveUsdc(DEPLOYED_ADDRESSES.umaResolver!, bond);Example: Full Trading Flow
// 1. Create market
const cid = await sdk.createMarket("Will X happen?", 2, deadline, resolver);
// 2. Place buy order on YES at $0.60
const buyId = await sdk.placeBuyOrder(cid, 0, 600_000n, 10_000_000n);
// 3. Someone else places buy on NO at $0.45
const noId = await sdk.placeBuyOrder(cid, 1, 450_000n, 10_000_000n);
// 4. Match the orders (anyone can call this)
await sdk.fillBuyVsBuy(buyId, noId, 10_000_000n);
// 5. After resolution, redeem winnings
await sdk.redeemWinnings(cid, [0]); // redeem YES tokensCLI
The SDK ships a CLI for direct protocol interaction. No code required.
# Set environment
export POLYGON_RPC_URL=https://...
export MAGISTERY_PRIVATE_KEY=0x... # only for write commands
# Read commands
npx @magistery/sdk market get <conditionId>
npx @magistery/sdk market list
npx @magistery/sdk market search "ETH"
npx @magistery/sdk order get <orderId>
npx @magistery/sdk position list
# Trading
npx @magistery/sdk order buy <conditionId> 0 0.55 100
npx @magistery/sdk order sell <conditionId> 0 0.6 50
npx @magistery/sdk order cancel <orderId>
# Resolution
npx @magistery/sdk market assert <conditionId> 0
npx @magistery/sdk market settle <assertionId>
npx @magistery/sdk market redeem <conditionId>All commands output JSON. Prices are 0–1 exclusive. Amounts are in shares with max 6 decimal precision.
Testing
# Unit tests (no RPC needed)
pnpm test
# Fork tests (requires Polygon RPC)
POLYGON_RPC=https://... pnpm test:fork135 tests total (84 unit + 51 fork).
License
MIT
