onit-markets
v0.2.2
Published
An sdk for interacting with Onit, a prediction market protocol
Readme
onit-markets
An SDK for interacting with Onit, a prediction market protocol built on Base.
Description
onit-markets provides a type-safe client for interacting with Onit's prediction market smart contracts. It allows developers to seamlessly create & integrate with prediction markets from any JavaScript or TypeScript application.
Features
- 🔒 Type-safe API client - Built with full TypeScript support
- ⚛️ React integration - Comprehensive React Query hooks for all market operations
- 🌐 Framework agnostic - Works with any JavaScript framework (React, Vue, Node.js, etc.)
- 📚 OpenAPI documentation - Complete API reference available
- 🎣 Auto-generated hooks - Automatic React Query hook generation for type safety
Installation
# Using npm
npm install onit-markets
# Using yarn
yarn add onit-markets
# Using pnpm
pnpm add onit-markets
# Using bun
bun add onit-marketsNote: This package has peer dependencies on zod (version >4) and hono (^4.7.5). For React usage, you'll also need @tanstack/react-query (^5) and react (^18).
Important: Zod v4 is required for the advanced codec functionality that powers the schema validation system. Make sure to install Zod v4:
npm install zod@^4.0.0Basic Usage
import { getClient } from 'onit-markets';
// Initialize the client with your API endpoint
const client = getClient('https://markets.onit-labs.workers.dev');
// Example: Get all markets
async function getAllMarkets() {
const response = await client.markets.$get();
if (response.ok) {
const data = await response.json();
if (data.success) {
console.log('Markets:', data.data);
return data.data;
} else {
console.error('Error:', data.error);
}
}
}
// Example: Create a new market (simplified)
async function createMarket(marketData) {
const response = await client.markets.$post({
json: marketData
});
if (response.ok) {
const result = await response.json();
return result;
}
}
// Example: Get market
async function getMarket(marketAddress) {
const response = await client.markets[':marketAddress'].$get({
param: { marketAddress }
});
if (response.ok) {
const market = await response.json();
return market;
}
}React Usage
For React applications, use the provided hooks and provider:
import { OnitMarketsProvider, useMarkets, useMarket } from 'onit-markets/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<OnitMarketsProvider endpoint="https://markets.onit-labs.workers.dev">
<MarketsList />
</OnitMarketsProvider>
</QueryClientProvider>
);
}
function MarketsList() {
// Get all markets with filtering
const { data: markets, isLoading, error } = useMarkets({
limit: 10,
includeResolved: false,
orderBy: 'createdAt',
orderDirection: 'desc'
});
// Get specific market
const { data: market } = useMarket('0x1234...abcd');
if (isLoading) return <div>Loading markets...</div>;
if (error) return <div>Error loading markets</div>;
return (
<div>
{markets?.data?.map(market => (
<div key={market.address}>
<h3>{market.title}</h3>
<p>Pot: {market.backing.toString()}</p>
</div>
))}
</div>
);
}Available React Hooks
useMarkets(params?)- List and filter marketsuseMarket(marketAddress)- Get individual market datauseMarketParticipants(marketAddress)- Get market participantsuseMarketPredictions(marketAddress)- Get market predictionsuseMarketTimeseries(marketAddress)- Get timeseries datauseMarketResolution(marketAddress)- Get resolution datauseMarketBetCalldata(marketAddress)- Get betting calldatauseUserPredictions(userAddress)- Get user's predictionsusePayouts(marketAddress)- Get market payoutsuseClaims(marketAddress)- Get market claims
Example Application
For a complete example of how to use this package in a real-world application, check out our Onit Prediction Market API Example repository. This repository contains a Next.js application that demonstrates:
- Creating new prediction markets
- Fetching market data and participants
- Placing bets on existing markets
- Custom React hooks for interacting with the Onit API
The example app includes:
- Market creation forms
- Betting interfaces
- Wallet integration
- Full implementation of the onit-markets package
API Documentation
Complete API documentation is available at: https://markets.onit-labs.workers.dev/api/~/docs
The documentation provides detailed information about available endpoints, request parameters, and response formats.
This sdk is built ontop of Hono's hono/client, for more usage information check this guide.
Type Safety & Schema Validation
This package provides complete type definitions and Zod v4 schema validation for all API responses, ensuring type safety and runtime validation:
// The client automatically provides types for all API endpoints
type MarketData = Awaited<ReturnType<typeof client.markets.$get>['json']>['data'];
// You can also import types directly
import { ApiResponse } from 'onit-markets';Schema Validation with Zod v4 Codecs
The package uses advanced Zod v4 schemas with bidirectional encoding/decoding for type-safe data transformation:
import { marketSchema } from '@forecast/api/durable-objects/api/db/validators/market/select';
// Using with the normal API client
async function getValidatedMarket(marketAddress: string) {
const response = await client.markets[':marketAddress'].$get({
param: { marketAddress }
});
if (response.ok) {
const data = await response.json();
if ('data' in data && data.data) {
// Encode the market data using Zod schema for type safety
const validatedMarket = marketSchema.encode(data.data);
return validatedMarket;
}
}
throw new Error('Failed to fetch market');
}
// Using with React hooks (validation happens automatically)
function MarketComponent({ marketAddress }: { marketAddress: string }) {
const { data: market, isLoading } = useMarket(marketAddress);
if (isLoading) return <div>Loading...</div>;
// market is already validated and typed based on the discriminated union
return (
<div>
<h3>{market?.questionTitle}</h3>
{market?.marketType === 'discrete' && (
<div>Discrete market with {market.metadata.buckets.length} options</div>
)}
{market?.marketType === 'percentage' && (
<div>Binary market: {market.metadata.questionTitle}</div>
)}
{market?.marketType === 'normal' && (
<div>Normal distribution: μ={market.metadata.mean}, σ={market.metadata.standardDeviation}</div>
)}
</div>
);
}Market Type Schemas
The package includes discriminated union schemas for all market types:
import {
discreteMarketSchema,
percentageMarketSchema,
normalMarketSchema,
scoreMarketSchema,
daysUntilMarketSchema,
genericMarketSchema,
marketSchema
} from '@forecast/api/durable-objects/api/db/validators/market/select';
// The main market schema is a discriminated union based on marketType
type Market = z.input<typeof marketSchema>;
type ExternalMarket = z.output<typeof marketSchema>;
// TypeScript will narrow the type based on marketType
function handleMarket(market: Market) {
switch (market.marketType) {
case 'discrete':
// market.metadata is now typed as DiscreteMarketMetadata
console.log(`Discrete market with ${market.metadata.buckets.length} options`);
break;
case 'percentage':
// market.metadata is now typed as PercentageMarketMetadata
console.log(`Binary market: ${market.metadata.questionTitle}`);
break;
case 'normal':
// market.metadata is now typed as NormalMarketMetadata
console.log(`Normal distribution: μ=${market.metadata.mean}`);
break;
// ... other market types
}
}Query Options with Schema Validation
The package exports query options functions for all hooks, which can be used directly or customized:
import { createMarketQueryOptions, createMarketsQueryOptions } from 'onit-markets/react';
import { useOnitMarketsContext } from 'onit-markets/react';
import { useQuery } from '@tanstack/react-query';
// Using exported query options functions
function CustomMarketComponent() {
const { client } = useOnitMarketsContext();
const marketAddress = '0x1234...abcd';
// Method 1: Using exported createMarketQueryOptions
const marketQuery = useQuery(
createMarketQueryOptions(client, marketAddress, {
staleTime: 1000 * 60 * 5, // 5 minutes
refetchOnWindowFocus: false,
})
);
// Method 2: Using exported createMarketsQueryOptions
const marketsQuery = useQuery(
createMarketsQueryOptions(
client,
{ limit: 20, includeResolved: false },
{ enabled: true }
)
);
return <div>...</div>;
}
// Method 3: Using ExtendedClient query options directly
function DirectQueryOptionsExample() {
const { client } = useOnitMarketsContext();
const marketAddress = '0x1234...abcd' as const;
const marketQuery = useQuery(
client.markets[':marketAddress'].$get.queryOptions(
{ param: { marketAddress } },
{
staleTime: 1000 * 60 * 10, // 10 minutes
select: (data) => {
// Custom data transformation with schema validation
if ('data' in data && data.data) {
return marketSchema.encode(data.data);
}
return null;
},
}
)
);
const marketsQuery = useQuery(
client.markets.$get.queryOptions(
{
query: {
limit: '50',
includeResolved: 'false',
orderBy: 'createdAt',
orderDirection: 'desc',
},
},
{
select: (response) => {
// Transform and validate the entire response
if ('data' in response && response.data) {
return response.data.map(market => marketSchema.encode(market));
}
return [];
},
}
)
);
return <div>...</div>;
}Custom Hook with Schema Validation
Here's how to create custom hooks that leverage the schema validation:
import { useQuery } from '@tanstack/react-query';
import { marketSchema } from '@forecast/api/durable-objects/api/db/validators/market/select';
import { createMarketQueryOptions } from 'onit-markets/react';
function useValidatedMarket(marketAddress: `0x${string}`) {
const { client } = useOnitMarketsContext();
return useQuery({
...createMarketQueryOptions(client, marketAddress),
select: (data) => {
if ('data' in data && data.data) {
// Schema validation with encoding happens here
return marketSchema.encode(data.data);
}
return null;
},
});
}
// Alternative: Custom validation with direct client usage
function useCustomValidatedMarket(marketAddress: string) {
const { client } = useOnitMarketsContext();
return useQuery({
queryKey: ['custom-market', marketAddress],
queryFn: async () => {
const response = await client.markets[':marketAddress'].$get({
param: { marketAddress }
});
const data = await response.json();
if ('error' in data) throw new Error(data.error);
if (!data.data) return null;
// Manual schema validation with encoding
return marketSchema.encode(data.data);
},
enabled: !!marketAddress,
});
}Advanced Query Patterns
Combine query options with custom validation for powerful data fetching:
import { useQueries } from '@tanstack/react-query';
import { createMarketQueryOptions } from 'onit-markets/react';
function useMultipleMarkets(marketAddresses: `0x${string}`[]) {
const { client } = useOnitMarketsContext();
return useQueries({
queries: marketAddresses.map((address) => ({
...createMarketQueryOptions(client, address),
select: (data) => {
if ('data' in data && data.data) {
const validatedMarket = marketSchema.encode(data.data);
// Additional processing based on market type
if (validatedMarket.marketType === 'discrete') {
return {
...validatedMarket,
bucketCount: validatedMarket.metadata.buckets.length,
};
}
return validatedMarket;
}
return null;
},
staleTime: 1000 * 60 * 5,
})),
});
}Advanced Features
Complex Data Types
The client automatically handles complex data types like BigInt:
// BigInt values are properly serialized/deserialized
const market = await client.markets[':marketAddress'].$get({ param: { marketAddress: '0x...' } });
const marketData = await market.json();
// BigInt values work seamlessly
const marketPot = marketData.data.backing; // BigInt valueLicense
This project is licensed under the MIT License - see the LICENSE file for details.
About Onit Labs
Onit is a prediction market protocol built on Ethereum. For more information, visit the GitHub repository.
