Skip to content

Commit 41fe021

Browse files
committed
feat: interact with TN's orderbook
1 parent 859e632 commit 41fe021

File tree

15 files changed

+3357
-4
lines changed

15 files changed

+3357
-4
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ out
2323
!.env.example
2424
*.env
2525

26-
CLAUDE.md
26+
CLAUDE.md
27+
28+
# Orderbook example temporary files
29+
examples/orderbook/.query_id

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,46 @@ await primitiveAction.insertRecord({
396396
-**High-throughput data insertion** (independent records)
397397
-**Fire-and-forget operations** (with proper error handling)
398398

399+
### Prediction Markets (Order Book)
400+
401+
The SDK supports binary prediction markets through the Order Book API. Markets are settled automatically based on real-world data from trusted data providers.
402+
403+
```typescript
404+
// Load the order book action
405+
const orderbook = client.loadOrderbookAction();
406+
407+
// Get market info
408+
const market = await orderbook.getMarketInfo(queryId);
409+
console.log(`Settles at: ${new Date(market.settleTime * 1000)}`);
410+
411+
// Place a buy order for YES shares at 55 cents
412+
const result = await orderbook.placeBuyOrder({
413+
queryId: market.id,
414+
outcome: true, // YES
415+
price: 55, // 55 cents = 55% implied probability
416+
amount: 100, // 100 shares
417+
});
418+
await client.waitForTx(result.data!.tx_hash);
419+
420+
// Get best prices
421+
const prices = await orderbook.getBestPrices(market.id, true);
422+
console.log(`YES: Bid=${prices.bestBid}c, Ask=${prices.bestAsk}c`);
423+
```
424+
425+
#### Market Making with Split Limit Orders
426+
427+
```typescript
428+
// Create YES/NO pairs and provide liquidity
429+
await orderbook.placeSplitLimitOrder({
430+
queryId: market.id,
431+
truePrice: 55, // YES at 55c, NO at 45c
432+
amount: 100, // 100 pairs
433+
});
434+
// Result: 100 YES holdings + 100 NO sell orders at 45c
435+
```
436+
437+
**📖 For complete documentation including market creation, order types, settlement, and examples, see the [Order Book Operations](./docs/api-reference.md#order-book-operations) section in the API Reference and [examples/orderbook](./examples/orderbook).**
438+
399439
### Using the SDK with Your Local Node
400440

401441
If you are running your own TRUF.NETWORK node, you can configure the SDK to interact with your local instance by changing the `endpoint` in the client configuration, as shown in the [Basic Client Initialization](#basic-client-initialization) section. This is useful for development, testing, or when operating within a private network.
@@ -488,6 +528,11 @@ For other bundlers or serverless platforms, consult their documentation on modul
488528
| List attestations | `attestationAction.listAttestations({requester, limit, offset, orderBy})` |
489529
| Get transaction event | `transactionAction.getTransactionEvent({txId})` |
490530
| List transaction fees | `transactionAction.listTransactionFees({wallet, mode, limit, offset})` |
531+
| Create prediction market | `orderbook.createMarket({bridge, queryComponents, settleTime, ...})` |
532+
| Place buy order | `orderbook.placeBuyOrder({queryId, outcome, price, amount})` |
533+
| Place split limit order | `orderbook.placeSplitLimitOrder({queryId, truePrice, amount})` |
534+
| Get order book | `orderbook.getOrderBook(queryId, outcome)` |
535+
| Get best prices | `orderbook.getBestPrices(queryId, outcome)` |
491536
| Destroy stream | `client.destroyStream(streamLocator)` |
492537

493538
**Safe Operation Pattern:**

docs/api-reference.md

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,275 @@ async function waitForClaimableWithdrawal(bridgeId: string, address: string, max
11311131
}
11321132
```
11331133

1134+
## Order Book Operations
1135+
1136+
The Order Book API enables binary prediction markets on TRUF.NETWORK. Markets are automatically settled based on real-world data from trusted data providers.
1137+
1138+
### Loading the Order Book Action
1139+
1140+
```typescript
1141+
const orderbook = client.loadOrderbookAction();
1142+
```
1143+
1144+
### Market Operations
1145+
1146+
#### `orderbook.createMarket(input: CreateMarketInput): Promise<TxReceipt>`
1147+
1148+
Creates a new binary prediction market.
1149+
1150+
##### Parameters
1151+
- `input: Object`
1152+
- `bridge: BridgeIdentifier` - Bridge for collateral (`"hoodi_tt2"`, `"sepolia_bridge"`, `"ethereum_bridge"`)
1153+
- `queryComponents: Uint8Array` - ABI-encoded query tuple (use `encodeQueryComponents()`)
1154+
- `settleTime: number` - Unix timestamp for market settlement
1155+
- `maxSpread: number` - Maximum bid-ask spread (1-50 cents)
1156+
- `minOrderSize: number` - Minimum order size
1157+
1158+
##### Example
1159+
```typescript
1160+
import { OrderbookAction } from "@trufnetwork/sdk-js";
1161+
1162+
const args = OrderbookAction.encodeActionArgs(
1163+
dataProviderAddress,
1164+
streamId,
1165+
timestamp,
1166+
"50000.00", // threshold
1167+
frozenAt
1168+
);
1169+
1170+
const queryComponents = OrderbookAction.encodeQueryComponents(
1171+
dataProviderAddress,
1172+
streamId,
1173+
"price_above_threshold",
1174+
args
1175+
);
1176+
1177+
const result = await orderbook.createMarket({
1178+
bridge: "hoodi_tt2",
1179+
queryComponents,
1180+
settleTime: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
1181+
maxSpread: 10,
1182+
minOrderSize: 1,
1183+
});
1184+
1185+
await client.waitForTx(result.data!.tx_hash);
1186+
```
1187+
1188+
#### `orderbook.createPriceAboveThresholdMarket(input): Promise<TxReceipt>`
1189+
1190+
Convenience method for creating "price above threshold" markets.
1191+
1192+
```typescript
1193+
const result = await orderbook.createPriceAboveThresholdMarket({
1194+
dataProvider: "0x4710a8d8f0d845da110086812a32de6d90d7ff5c",
1195+
streamId: "stbtc0000000000000000000000000000",
1196+
timestamp: Math.floor(Date.now() / 1000) + 3600,
1197+
threshold: "50000.00",
1198+
frozenAt: 0,
1199+
bridge: "hoodi_tt2",
1200+
settleTime: Math.floor(Date.now() / 1000) + 3600,
1201+
maxSpread: 10,
1202+
minOrderSize: 1,
1203+
});
1204+
```
1205+
1206+
#### `orderbook.getMarketInfo(queryId: number): Promise<MarketInfo>`
1207+
1208+
Gets detailed information about a market.
1209+
1210+
```typescript
1211+
const market = await orderbook.getMarketInfo(queryId);
1212+
console.log(`Settle Time: ${new Date(market.settleTime * 1000)}`);
1213+
console.log(`Settled: ${market.settled}`);
1214+
if (market.settled) {
1215+
console.log(`Winner: ${market.winningOutcome ? "YES" : "NO"}`);
1216+
}
1217+
```
1218+
1219+
#### `orderbook.listMarkets(input?: ListMarketsInput): Promise<MarketSummary[]>`
1220+
1221+
Lists markets with optional filtering.
1222+
1223+
```typescript
1224+
// Get all unsettled markets
1225+
const markets = await orderbook.listMarkets({
1226+
settledFilter: true, // true=unsettled, false=settled, null=all
1227+
limit: 100,
1228+
offset: 0,
1229+
});
1230+
```
1231+
1232+
#### `orderbook.validateMarketCollateral(queryId: number): Promise<MarketValidation>`
1233+
1234+
Validates market collateral integrity (YES/NO token parity and vault balance).
1235+
1236+
```typescript
1237+
const validation = await orderbook.validateMarketCollateral(queryId);
1238+
console.log(`Valid: ${validation.validCollateral}`);
1239+
console.log(`Total YES: ${validation.totalTrue}`);
1240+
console.log(`Total NO: ${validation.totalFalse}`);
1241+
```
1242+
1243+
### Order Operations
1244+
1245+
#### `orderbook.placeBuyOrder(input: PlaceOrderInput): Promise<TxReceipt>`
1246+
1247+
Places a buy order for shares. Locks collateral: `amount x price x 10^16 wei`.
1248+
1249+
```typescript
1250+
await orderbook.placeBuyOrder({
1251+
queryId: market.id,
1252+
outcome: true, // true=YES, false=NO
1253+
price: 55, // 55 cents
1254+
amount: 100, // 100 shares
1255+
});
1256+
```
1257+
1258+
#### `orderbook.placeSellOrder(input: PlaceOrderInput): Promise<TxReceipt>`
1259+
1260+
Places a sell order for owned shares.
1261+
1262+
```typescript
1263+
await orderbook.placeSellOrder({
1264+
queryId: market.id,
1265+
outcome: true,
1266+
price: 60,
1267+
amount: 50,
1268+
});
1269+
```
1270+
1271+
#### `orderbook.placeSplitLimitOrder(input: PlaceSplitLimitOrderInput): Promise<TxReceipt>`
1272+
1273+
Places a split limit order for market making. Atomically:
1274+
1. Locks collateral ($1.00 per pair)
1275+
2. Mints a YES/NO share pair
1276+
3. Keeps YES shares as holdings
1277+
4. Places NO shares as a sell order at `(100 - truePrice)` cents
1278+
1279+
```typescript
1280+
// Create 100 pairs: YES holdings + NO sell orders at 45c
1281+
await orderbook.placeSplitLimitOrder({
1282+
queryId: market.id,
1283+
truePrice: 55, // YES at 55c, NO at 45c
1284+
amount: 100,
1285+
});
1286+
```
1287+
1288+
#### `orderbook.cancelOrder(input: CancelOrderInput): Promise<TxReceipt>`
1289+
1290+
Cancels an open order (cannot cancel holdings where price=0).
1291+
1292+
```typescript
1293+
await orderbook.cancelOrder({
1294+
queryId: market.id,
1295+
outcome: true,
1296+
price: 55, // Price of order to cancel
1297+
});
1298+
```
1299+
1300+
### Query Operations
1301+
1302+
#### `orderbook.getOrderBook(queryId: number, outcome: boolean): Promise<OrderBookEntry[]>`
1303+
1304+
Gets the order book for a market outcome.
1305+
1306+
```typescript
1307+
const yesOrders = await orderbook.getOrderBook(queryId, true);
1308+
for (const order of yesOrders) {
1309+
const type = order.price < 0 ? "BUY" : order.price > 0 ? "SELL" : "HOLDING";
1310+
console.log(`${type}: ${order.amount} shares at ${Math.abs(order.price)}c`);
1311+
}
1312+
```
1313+
1314+
#### `orderbook.getBestPrices(queryId: number, outcome: boolean): Promise<BestPrices>`
1315+
1316+
Gets the best bid and ask prices for an outcome.
1317+
1318+
```typescript
1319+
const prices = await orderbook.getBestPrices(queryId, true);
1320+
console.log(`YES: Bid=${prices.bestBid}c, Ask=${prices.bestAsk}c, Spread=${prices.spread}c`);
1321+
```
1322+
1323+
#### `orderbook.getMarketDepth(queryId: number, outcome: boolean): Promise<DepthLevel[]>`
1324+
1325+
Gets aggregated volume at each price level.
1326+
1327+
```typescript
1328+
const depth = await orderbook.getMarketDepth(queryId, true);
1329+
for (const level of depth) {
1330+
console.log(`${level.price}c: ${level.totalAmount} shares`);
1331+
}
1332+
```
1333+
1334+
#### `orderbook.getUserPositions(): Promise<UserPosition[]>`
1335+
1336+
Gets the caller's positions across all markets.
1337+
1338+
```typescript
1339+
const positions = await orderbook.getUserPositions();
1340+
for (const pos of positions) {
1341+
const type = pos.price === 0 ? "HOLDING" : pos.price < 0 ? "BUY" : "SELL";
1342+
console.log(`Market ${pos.queryId}: ${pos.outcome ? "YES" : "NO"} ${type} ${pos.amount}`);
1343+
}
1344+
```
1345+
1346+
#### `orderbook.getUserCollateral(): Promise<UserCollateral>`
1347+
1348+
Gets the caller's total locked collateral.
1349+
1350+
```typescript
1351+
const collateral = await orderbook.getUserCollateral();
1352+
console.log(`Total Locked: ${collateral.totalLocked} wei`);
1353+
console.log(`Buy Orders: ${collateral.buyOrdersLocked} wei`);
1354+
console.log(`Shares Value: ${collateral.sharesValue} wei`);
1355+
```
1356+
1357+
### Settlement Operations
1358+
1359+
#### `orderbook.settleMarket(queryId: number): Promise<TxReceipt>`
1360+
1361+
Settles a market after settlement time has passed.
1362+
1363+
```typescript
1364+
const result = await orderbook.settleMarket(queryId);
1365+
await client.waitForTx(result.data!.tx_hash);
1366+
```
1367+
1368+
### Price Representation
1369+
1370+
Prices are represented as integers in cents (1-99):
1371+
- A YES price of 60 means 60 cents, implying 60% probability
1372+
- The complementary NO price is always `100 - YES_price`
1373+
1374+
### Order Types
1375+
1376+
| Price Value | Type | Description |
1377+
|------------|------|-------------|
1378+
| -99 to -1 | Buy Order | Bid to buy at \|price\| cents |
1379+
| 0 | Holding | Shares owned (not listed) |
1380+
| 1 to 99 | Sell Order | Ask to sell at price cents |
1381+
1382+
### Static Helper Methods
1383+
1384+
```typescript
1385+
// Encode action arguments for query components
1386+
const args = OrderbookAction.encodeActionArgs(
1387+
dataProvider, // Ethereum address
1388+
streamId, // 32-char stream ID
1389+
timestamp, // Unix timestamp
1390+
threshold, // Price threshold (e.g., "50000.00")
1391+
frozenAt // Block height for data snapshot
1392+
);
1393+
1394+
// Encode full query components
1395+
const queryComponents = OrderbookAction.encodeQueryComponents(
1396+
dataProvider,
1397+
streamId,
1398+
actionId, // e.g., "price_above_threshold"
1399+
args
1400+
);
1401+
```
1402+
11341403
## Performance Recommendations
11351404
- Use batch record insertions
11361405
- Implement client-side caching

0 commit comments

Comments
 (0)