npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@opinion-labs/opinion-clob-sdk

v0.5.3

Published

TypeScript SDK for Opinion Labs Prediction Market CLOB operations

Readme

@opinion-labs/opinion-clob-sdk

TypeScript SDK for Opinion Labs Prediction Market. Trade on blockchain-based prediction markets with EIP-712 signed orders, Gnosis Safe multi-sig support, and real-time WebSocket updates.

Installation

npm install @opinion-labs/opinion-clob-sdk

Quick Start

import { Client, CHAIN_ID_BNB_MAINNET, DEFAULT_API_HOST, OrderSide, OrderType } from '@opinion-labs/opinion-clob-sdk';

const client = new Client({
  host: DEFAULT_API_HOST,
  apiKey: 'your_api_key',
  chainId: CHAIN_ID_BNB_MAINNET,
  rpcUrl: 'https://bsc-dataseed.binance.org',
  privateKey: '0x...',
  multiSigAddress: '0x...',
});

// Get markets
const markets = await client.getMarkets({ limit: 10 });
console.log(markets.result.list);

// Place a limit buy order
await client.placeOrder({
  marketId: 1234,
  tokenId: '0x...',
  side: OrderSide.BUY,
  orderType: OrderType.LIMIT_ORDER,
  price: '0.5',
  makerAmountInQuoteToken: '10',
});

Prerequisites

| Requirement | Description | |-------------|-------------| | API Key | Obtained from the Opinion Labs team | | Private Key | EOA wallet private key for signing orders | | Multi-sig Address | Your Gnosis Safe wallet address (visible in "My Portfolio") | | RPC URL | BNB Chain RPC endpoint |

Note: The signer (private key) and multi-sig address may be different wallets. The signer is an owner of the Safe that authorizes trades.

Configuration

const client = new Client({
  // Required
  host: 'https://openapi.opinion.trade/openapi',
  apiKey: 'your_api_key',
  chainId: 56,                    // BNB Chain (only supported chain)
  rpcUrl: 'https://bsc-dataseed.binance.org',
  privateKey: '0x...',
  multiSigAddress: '0x...',

  // Optional
  proxyUrl: 'http://127.0.0.1:7890',  // HTTP proxy
  quoteTokensCacheTtl: 3600,           // Cache TTL in seconds (default: 3600)
  marketCacheTtl: 300,                 // Cache TTL in seconds (default: 300)
});

API Reference

Market Data

// List markets with filters
const markets = await client.getMarkets({
  topicType: TopicType.BINARY,            // BINARY, CATEGORICAL, or ALL
  status: TopicStatusFilter.ACTIVATED,     // ACTIVATED, RESOLVED, or ALL
  sortBy: TopicSortType.BY_VOLUME_DESC,   // Sort order
  page: 1,
  limit: 20,
});

// Get market details
const market = await client.getMarket(marketId);

// Get categorical market details
const catMarket = await client.getCategoricalMarket(marketId);

// Get orderbook
const orderbook = await client.getOrderbook(tokenId);

// Get latest price
const price = await client.getLatestPrice(tokenId);

// Get price history
const history = await client.getPriceHistory(tokenId, {
  interval: '1h',   // '1m' | '1h' | '1d' | '1w' | 'max'
  startAt: 1700000000,
  endAt: 1700100000,
});

// Get quote tokens
const tokens = await client.getQuoteTokens();

// Get on-chain fee rates
const fees = await client.getFeeRates(tokenId);

Trading

All order operations are gas-free (signed off-chain, submitted via API).

// Enable trading (one-time approval, requires gas)
await client.enableTrading();

// Place a limit buy order
await client.placeOrder({
  marketId: 1234,
  tokenId: '0x...',
  side: OrderSide.BUY,
  orderType: OrderType.LIMIT_ORDER,
  price: '0.5',
  makerAmountInQuoteToken: '10',  // 10 USDC
});

// Place a limit sell order
await client.placeOrder({
  marketId: 1234,
  tokenId: '0x...',
  side: OrderSide.SELL,
  orderType: OrderType.LIMIT_ORDER,
  price: '0.6',
  makerAmountInBaseToken: '10',   // 10 outcome tokens
});

// Place a market buy order
await client.placeOrder({
  marketId: 1234,
  tokenId: '0x...',
  side: OrderSide.BUY,
  orderType: OrderType.MARKET_ORDER,
  price: '0',
  makerAmountInQuoteToken: '10',
});

// Cancel an order
await client.cancelOrder(orderId);

// Batch operations
await client.placeOrdersBatch([order1, order2, order3]);
await client.cancelOrdersBatch([id1, id2, id3]);

// Cancel all open orders (with optional filters)
await client.cancelAllOrders({ marketId: 1234, side: OrderSide.BUY });

User Data

const positions = await client.getMyPositions({ marketId: 1234 });
const trades = await client.getMyTrades({ page: 1, limit: 20 });
const orders = await client.getMyOrders({ status: '1' });  // 1 = open
const order = await client.getOrderById(orderId);
const balances = await client.getMyBalances();
const auth = await client.getUserAuth();

Token Operations

These operations interact directly with the blockchain and require gas (BNB).

// Split: convert collateral (USDC) into Yes + No tokens
await client.split(marketId, 10000000n);  // amount in wei

// Merge: convert Yes + No tokens back to collateral
await client.merge(marketId, 10000000n);

// Redeem: claim winnings from a resolved market
await client.redeem(marketId);

WebSocket

Subscribe to real-time market updates.

const ws = client.createWebSocketClient({
  heartbeatInterval: 30,
  onOpen: () => console.log('Connected'),
  onClose: () => console.log('Disconnected'),
  onError: (err) => console.error(err),
});

await ws.connect();

// Subscribe to orderbook depth updates
ws.subscribeMarketDepthDiff(marketId, (msg) => {
  console.log(`Depth: ${msg.side} ${msg.price} @ ${msg.size}`);
});

// Subscribe to price updates
ws.subscribeMarketLastPrice(marketId, (msg) => {
  console.log(`Price: ${msg.price}`);
});

// Subscribe to trade updates
ws.subscribeMarketLastTrade(marketId, (msg) => {
  console.log(`Trade: ${msg.price} x ${msg.size}`);
});

// Subscribe to your order status updates
ws.subscribeTradeOrderUpdate((msg) => {
  console.log(`Order ${msg.orderId}: ${msg.status}`);
});

// Subscribe to your new trade records
ws.subscribeTradeRecordNew((msg) => {
  console.log(`New trade: ${msg.tradeId}`);
});

// Disconnect when done
await ws.disconnect();

Builder Mode

For platforms that place orders on behalf of users without holding their private keys.

import { BuilderClient, UserClient } from '@opinion-labs/opinion-clob-sdk';

const builder = new BuilderClient({
  host: 'https://openapi.opinion.trade/openapi',
  builderApiKey: 'your_builder_api_key',
  chainId: 56,
  rpcUrl: 'https://bsc-dataseed.binance.org',
});

// Create a sub-account for a user
const userInfo = await builder.createUser('0xUserWallet...');
// IMPORTANT: userInfo.apikey is returned only once — store it securely

// Build an order for the user to sign
const orderResult = await builder.buildOrderForSigning({
  marketId: 1234,
  tokenId: '0x...',
  userWalletAddress: '0xUserSafeWallet...',
  side: OrderSide.BUY,
  orderType: OrderType.LIMIT_ORDER,
  amount: 10,
  price: '0.5',
});

// User signs the order (client-side)
const user = new UserClient('0xUserPrivateKey...');
const signature = await user.signTypedData(orderResult.typedData);

// Submit the signed order
await builder.placeOrderForUser({
  ...orderResult,
  signature,
  userWalletAddress: '0xUserSafeWallet...',
});

Error Handling

import {
  SdkError,
  InvalidParamError,
  OpenApiError,
  BalanceNotEnough,
  InsufficientGasBalance,
} from '@opinion-labs/opinion-clob-sdk';

try {
  await client.placeOrder(orderData);
} catch (err) {
  if (err instanceof InvalidParamError) {
    // Invalid order parameters
  } else if (err instanceof BalanceNotEnough) {
    // Insufficient token balance
  } else if (err instanceof InsufficientGasBalance) {
    // Not enough BNB for gas
  } else if (err instanceof OpenApiError) {
    // API call failed
  }
}

Response Format

All API methods return:

{
  errno: number;    // 0 = success
  errmsg: string;   // Error message (empty on success)
  result: T;        // Response payload
}

Workflow

1. enableTrading()     One-time approval (requires gas)
                           |
2. split() (optional)  Convert USDC -> Yes/No tokens (requires gas)
                           |
3. placeOrder()        Place limit/market orders (gas-free)
                           |
4. cancelOrder()       Cancel open orders (gas-free)
                           |
5. merge() / redeem()  Convert back or claim winnings (requires gas)

Requirements

  • Node.js >= 18
  • TypeScript >= 5.3

License

MIT