@alpha-arcade/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,535 @@
1
+ import algosdk, { Algodv2, Indexer, TransactionSigner } from 'algosdk';
2
+
3
+ /** Configuration for initializing the AlphaClient */
4
+ type AlphaClientConfig = {
5
+ /** Algorand algod client instance */
6
+ algodClient: Algodv2;
7
+ /** Algorand indexer client instance */
8
+ indexerClient: Indexer;
9
+ /** Transaction signer (wallet or account signer) */
10
+ signer: TransactionSigner;
11
+ /** The active Algorand address that will sign transactions */
12
+ activeAddress: string;
13
+ /** Matcher contract app ID (mainnet: 741347297) */
14
+ matcherAppId: number;
15
+ /** USDC ASA ID on Algorand (mainnet: 31566704) */
16
+ usdcAssetId: number;
17
+ /** Platform fee collection address */
18
+ feeAddress: string;
19
+ /** Base URL for the Alpha REST API (default: https://partners.alphaarcade.com/api) */
20
+ apiBaseUrl?: string;
21
+ /** API key for the Alpha partners API (passed as x-api-key header) */
22
+ apiKey: string;
23
+ };
24
+ /** A prediction market returned from the Alpha API */
25
+ type Market = {
26
+ id: string;
27
+ title: string;
28
+ slug?: string;
29
+ image?: string;
30
+ marketAppId: number;
31
+ yesAssetId: number;
32
+ noAssetId: number;
33
+ yesProb: number;
34
+ noProb: number;
35
+ volume: number;
36
+ endTs: number;
37
+ resolution?: number;
38
+ isResolved?: boolean;
39
+ isLive?: boolean;
40
+ categories?: string[];
41
+ featured?: boolean;
42
+ options?: MarketOption[];
43
+ feeBase?: number;
44
+ [key: string]: unknown;
45
+ };
46
+ /** An option within a multi-choice market */
47
+ type MarketOption = {
48
+ id: string;
49
+ title: string;
50
+ marketAppId: number;
51
+ yesAssetId: number;
52
+ noAssetId: number;
53
+ yesProb: number;
54
+ noProb: number;
55
+ [key: string]: unknown;
56
+ };
57
+ /** Global state of a market app read from on-chain */
58
+ type MarketGlobalState = {
59
+ collateral_asset_id: number;
60
+ yes_asset_id: number;
61
+ no_asset_id: number;
62
+ yes_supply: number;
63
+ no_supply: number;
64
+ is_resolved: number;
65
+ is_activated: number;
66
+ outcome: number;
67
+ resolution_time: number;
68
+ fee_base_percent: number;
69
+ fee_timer_threshold: number;
70
+ title: string;
71
+ rules: string;
72
+ oracle_address: string;
73
+ fee_address: string;
74
+ market_friend_addr: string;
75
+ escrow_cancel_address: string;
76
+ };
77
+ /** Position: 1 = Yes, 0 = No */
78
+ type Position = 0 | 1;
79
+ /** Order side: 'BUY' or 'SELL' */
80
+ type OrderSide = 'BUY' | 'SELL';
81
+ /** Parameters for creating a limit order */
82
+ type CreateLimitOrderParams = {
83
+ /** Market app ID */
84
+ marketAppId: number;
85
+ /** 1 for Yes, 0 for No */
86
+ position: Position;
87
+ /** Price in microunits (e.g. 500000 = $0.50) */
88
+ price: number;
89
+ /** Quantity in microunits (e.g. 1000000 = 1 share) */
90
+ quantity: number;
91
+ /** Whether this is a buy order */
92
+ isBuying: boolean;
93
+ /** Fee base in microunits (e.g. 70000 = 7%). If omitted, reads from market global state. */
94
+ feeBase?: number;
95
+ };
96
+ /** Parameters for creating a market order */
97
+ type CreateMarketOrderParams = {
98
+ /** Market app ID */
99
+ marketAppId: number;
100
+ /** 1 for Yes, 0 for No */
101
+ position: Position;
102
+ /** Price in microunits (e.g. 500000 = $0.50) */
103
+ price: number;
104
+ /** Quantity in microunits (e.g. 1000000 = 1 share) */
105
+ quantity: number;
106
+ /** Whether this is a buy order */
107
+ isBuying: boolean;
108
+ /** Slippage tolerance in microunits (e.g. 50000 = $0.05) */
109
+ slippage: number;
110
+ /** Fee base in microunits. If omitted, reads from market global state. */
111
+ feeBase?: number;
112
+ /** Pre-computed matching orders. If omitted, auto-fetches orderbook and computes matches. */
113
+ matchingOrders?: CounterpartyMatch[];
114
+ };
115
+ /** Parameters for cancelling an order */
116
+ type CancelOrderParams = {
117
+ /** Market app ID */
118
+ marketAppId: number;
119
+ /** The escrow app ID of the order to cancel */
120
+ escrowAppId: number;
121
+ /** The owner address of the order */
122
+ orderOwner: string;
123
+ };
124
+ /** Parameters for proposing a match between two existing orders */
125
+ type ProposeMatchParams = {
126
+ /** Market app ID */
127
+ marketAppId: number;
128
+ /** The maker escrow app ID (existing order) */
129
+ makerEscrowAppId: number;
130
+ /** The maker's Algorand address */
131
+ makerAddress: string;
132
+ /** Quantity to match in microunits */
133
+ quantityMatched: number;
134
+ };
135
+ /** A counterparty order to match against */
136
+ type CounterpartyMatch = {
137
+ /** Escrow app ID of the counterparty order */
138
+ escrowAppId: number;
139
+ /** Quantity available to match in microunits */
140
+ quantity: number;
141
+ /** Owner address of the counterparty order */
142
+ owner: string;
143
+ };
144
+ /** Result of creating an order */
145
+ type CreateOrderResult = {
146
+ /** The escrow app ID of the newly created order */
147
+ escrowAppId: number;
148
+ /** Transaction IDs from the atomic group */
149
+ txIds: string[];
150
+ /** Confirmed round number */
151
+ confirmedRound: number;
152
+ };
153
+ /** Result of creating a market order (includes match info) */
154
+ type CreateMarketOrderResult = CreateOrderResult & {
155
+ /** Total quantity that was matched */
156
+ matchedQuantity: number;
157
+ };
158
+ /** Result of cancelling an order */
159
+ type CancelOrderResult = {
160
+ /** Whether the cancellation succeeded */
161
+ success: boolean;
162
+ /** Transaction IDs */
163
+ txIds: string[];
164
+ };
165
+ /** Result of proposing a match */
166
+ type ProposeMatchResult = {
167
+ /** Whether the match succeeded */
168
+ success: boolean;
169
+ /** Transaction IDs */
170
+ txIds: string[];
171
+ };
172
+ /** A single entry in the orderbook (one price level, one order) */
173
+ type OrderbookEntry = {
174
+ /** Price in microunits (e.g. 500000 = $0.50) */
175
+ price: number;
176
+ /** Remaining quantity in microunits */
177
+ quantity: number;
178
+ /** Escrow app ID for this order */
179
+ escrowAppId: number;
180
+ /** Owner address */
181
+ owner: string;
182
+ };
183
+ /** One side (bids or asks) of the orderbook */
184
+ type OrderbookSide = {
185
+ bids: OrderbookEntry[];
186
+ asks: OrderbookEntry[];
187
+ };
188
+ /** Full orderbook for a market */
189
+ type Orderbook = {
190
+ yes: OrderbookSide;
191
+ no: OrderbookSide;
192
+ };
193
+ /** Aggregated orderbook entry (multiple orders at same price) */
194
+ type AggregatedOrderbookEntry = {
195
+ /** Price in microunits */
196
+ price: number;
197
+ /** Total quantity at this price level in microunits */
198
+ quantity: number;
199
+ /** Number of orders at this price level */
200
+ orderCount: number;
201
+ };
202
+ /** Aggregated orderbook side */
203
+ type AggregatedOrderbookSide = {
204
+ bids: AggregatedOrderbookEntry[];
205
+ asks: AggregatedOrderbookEntry[];
206
+ };
207
+ /** Aggregated orderbook for display */
208
+ type AggregatedOrderbook = {
209
+ yes: AggregatedOrderbookSide;
210
+ no: AggregatedOrderbookSide;
211
+ };
212
+ /** Parameters for split shares */
213
+ type SplitSharesParams = {
214
+ /** Market app ID */
215
+ marketAppId: number;
216
+ /** Amount to split in microunits (e.g. 1000000 = $1.00 USDC) */
217
+ amount: number;
218
+ };
219
+ /** Parameters for merge shares */
220
+ type MergeSharesParams = {
221
+ /** Market app ID */
222
+ marketAppId: number;
223
+ /** Amount to merge in microunits */
224
+ amount: number;
225
+ };
226
+ /** Parameters for claiming resolved tokens */
227
+ type ClaimParams = {
228
+ /** Market app ID */
229
+ marketAppId: number;
230
+ /** The outcome token ASA ID to redeem */
231
+ assetId: number;
232
+ /** Amount to claim in microunits. If omitted, claims entire balance. */
233
+ amount?: number;
234
+ };
235
+ /** Result of a split or merge operation */
236
+ type SplitMergeResult = {
237
+ success: boolean;
238
+ txIds: string[];
239
+ confirmedRound: number;
240
+ };
241
+ /** Result of a claim operation */
242
+ type ClaimResult = {
243
+ success: boolean;
244
+ txIds: string[];
245
+ confirmedRound: number;
246
+ };
247
+ /** A wallet's token position in a market */
248
+ type WalletPosition = {
249
+ /** Market app ID */
250
+ marketAppId: number;
251
+ /** YES token ASA ID */
252
+ yesAssetId: number;
253
+ /** NO token ASA ID */
254
+ noAssetId: number;
255
+ /** YES token balance in microunits */
256
+ yesBalance: number;
257
+ /** NO token balance in microunits */
258
+ noBalance: number;
259
+ };
260
+ /** An open order belonging to the wallet */
261
+ type OpenOrder = {
262
+ /** Escrow app ID */
263
+ escrowAppId: number;
264
+ /** Market app ID */
265
+ marketAppId: number;
266
+ /** Position: 0=No, 1=Yes */
267
+ position: Position;
268
+ /** Side: 0=Sell, 1=Buy */
269
+ side: number;
270
+ /** Price in microunits */
271
+ price: number;
272
+ /** Total quantity in microunits */
273
+ quantity: number;
274
+ /** Filled quantity in microunits */
275
+ quantityFilled: number;
276
+ /** Slippage in microunits (0 = limit order) */
277
+ slippage: number;
278
+ /** Owner address */
279
+ owner: string;
280
+ };
281
+ /** Raw global state of an escrow app */
282
+ type EscrowGlobalState = {
283
+ position?: number;
284
+ side?: number;
285
+ price?: number;
286
+ quantity?: number;
287
+ quantity_filled?: number;
288
+ slippage?: number;
289
+ owner?: string;
290
+ market_app_id?: number;
291
+ asset_listed?: number;
292
+ fee_timer_start?: number;
293
+ };
294
+
295
+ /**
296
+ * The main client for interacting with Alpha Market prediction markets on Algorand.
297
+ *
298
+ * Provides methods for:
299
+ * - **Trading**: Create limit/market orders, cancel orders, propose matches
300
+ * - **Positions**: Split/merge shares, claim resolved tokens, view positions
301
+ * - **Orderbook**: Read on-chain orderbook for any market
302
+ * - **Markets**: Fetch live markets from the Alpha API
303
+ *
304
+ * @example
305
+ * ```typescript
306
+ * import { AlphaClient } from '@alpha-market/sdk';
307
+ * import algosdk from 'algosdk';
308
+ *
309
+ * const algodClient = new algosdk.Algodv2('', 'https://mainnet-api.algonode.cloud', 443);
310
+ * const indexerClient = new algosdk.Indexer('', 'https://mainnet-idx.algonode.cloud', 443);
311
+ * const account = algosdk.mnemonicToSecretKey('your mnemonic ...');
312
+ * const signer = algosdk.makeBasicAccountTransactionSigner(account);
313
+ *
314
+ * const client = new AlphaClient({
315
+ * algodClient,
316
+ * indexerClient,
317
+ * signer,
318
+ * activeAddress: account.addr,
319
+ * matcherAppId: 741347297,
320
+ * usdcAssetId: 31566704,
321
+ * feeAddress: 'FEE_ADDRESS_HERE',
322
+ * });
323
+ *
324
+ * // Fetch markets
325
+ * const markets = await client.getMarkets();
326
+ *
327
+ * // Place a limit order
328
+ * const result = await client.createLimitOrder({
329
+ * marketAppId: markets[0].marketAppId,
330
+ * position: 1, // Yes
331
+ * price: 500_000, // $0.50
332
+ * quantity: 1_000_000, // 1 share
333
+ * isBuying: true,
334
+ * });
335
+ * ```
336
+ */
337
+ declare class AlphaClient {
338
+ private config;
339
+ constructor(config: AlphaClientConfig);
340
+ /**
341
+ * Creates a limit order on a market.
342
+ *
343
+ * A limit order sits on the orderbook at your specified price until matched
344
+ * or cancelled. Slippage is 0 — the order executes only at your exact price.
345
+ *
346
+ * @param params - Order parameters (marketAppId, position, price, quantity, isBuying)
347
+ * @returns The created escrow app ID and transaction info
348
+ */
349
+ createLimitOrder(params: CreateLimitOrderParams): Promise<CreateOrderResult>;
350
+ /**
351
+ * Creates a market order with automatic matching.
352
+ *
353
+ * Fetches the live orderbook, finds the best counterparty orders within your
354
+ * slippage tolerance, then creates the order and proposes matches in a single
355
+ * atomic transaction group.
356
+ *
357
+ * @param params - Order parameters (marketAppId, position, price, quantity, isBuying, slippage)
358
+ * @returns The created escrow app ID, matched quantity, and transaction info
359
+ */
360
+ createMarketOrder(params: CreateMarketOrderParams): Promise<CreateMarketOrderResult>;
361
+ /**
362
+ * Cancels an open order by deleting its escrow app.
363
+ *
364
+ * Returns the escrowed funds (USDC or outcome tokens) to the order owner,
365
+ * and reclaims the ALGO minimum balance used by the escrow app.
366
+ *
367
+ * @param params - Cancel parameters (marketAppId, escrowAppId, orderOwner)
368
+ * @returns Whether the cancellation succeeded
369
+ */
370
+ cancelOrder(params: CancelOrderParams): Promise<CancelOrderResult>;
371
+ /**
372
+ * Proposes a match between an existing maker order and a new taker order.
373
+ *
374
+ * Use this for advanced matching scenarios where you want to explicitly
375
+ * specify which orders to match against.
376
+ *
377
+ * @param params - Match parameters (marketAppId, makerEscrowAppId, makerAddress, quantityMatched)
378
+ * @returns Whether the match succeeded
379
+ */
380
+ proposeMatch(params: ProposeMatchParams): Promise<ProposeMatchResult>;
381
+ /**
382
+ * Splits USDC into equal amounts of YES and NO outcome tokens.
383
+ *
384
+ * For example, splitting 1 USDC gives you 1 YES token + 1 NO token.
385
+ * Together they're always worth $1.00 — you can trade them independently.
386
+ *
387
+ * @param params - Split parameters (marketAppId, amount in microunits)
388
+ * @returns Transaction result
389
+ */
390
+ splitShares(params: SplitSharesParams): Promise<SplitMergeResult>;
391
+ /**
392
+ * Merges equal amounts of YES and NO tokens back into USDC.
393
+ *
394
+ * The inverse of split. 1 YES + 1 NO = 1 USDC, always.
395
+ *
396
+ * @param params - Merge parameters (marketAppId, amount in microunits)
397
+ * @returns Transaction result
398
+ */
399
+ mergeShares(params: MergeSharesParams): Promise<SplitMergeResult>;
400
+ /**
401
+ * Claims USDC from a resolved market by redeeming outcome tokens.
402
+ *
403
+ * - Winning tokens: redeemed 1:1 for USDC
404
+ * - Voided market: redeemed at half value
405
+ * - Losing tokens: burned (no USDC returned)
406
+ *
407
+ * @param params - Claim parameters (marketAppId, assetId, optional amount)
408
+ * @returns Transaction result
409
+ */
410
+ claim(params: ClaimParams): Promise<ClaimResult>;
411
+ /**
412
+ * Gets all token positions for a wallet across all markets.
413
+ *
414
+ * Reads on-chain account info and maps ASA holdings to markets.
415
+ * Returns raw token balances (YES/NO amounts per market).
416
+ *
417
+ * @param walletAddress - Optional wallet (defaults to activeAddress)
418
+ * @returns Array of positions with market app IDs and token balances
419
+ */
420
+ getPositions(walletAddress?: string): Promise<WalletPosition[]>;
421
+ /**
422
+ * Fetches the full on-chain orderbook for a market.
423
+ *
424
+ * Reads all escrow apps created by the market contract, decodes their
425
+ * global state, and organizes into yes/no bids and asks.
426
+ * Only includes limit orders (slippage = 0) with unfilled quantity.
427
+ *
428
+ * @param marketAppId - The market app ID
429
+ * @returns Orderbook with yes and no sides, each having bids and asks
430
+ */
431
+ getOrderbook(marketAppId: number): Promise<Orderbook>;
432
+ /**
433
+ * Gets open orders for a specific wallet on a market.
434
+ *
435
+ * @param marketAppId - The market app ID
436
+ * @param walletAddress - Optional wallet (defaults to activeAddress)
437
+ * @returns Array of open orders
438
+ */
439
+ getOpenOrders(marketAppId: number, walletAddress?: string): Promise<OpenOrder[]>;
440
+ /**
441
+ * Fetches all live, tradeable markets from the Alpha API.
442
+ *
443
+ * Automatically paginates to retrieve all markets.
444
+ *
445
+ * @returns Array of live markets
446
+ */
447
+ getMarkets(): Promise<Market[]>;
448
+ /**
449
+ * Fetches a single market by its ID.
450
+ *
451
+ * @param marketId - The market ID
452
+ * @returns The market data, or null if not found
453
+ */
454
+ getMarket(marketId: string): Promise<Market | null>;
455
+ }
456
+
457
+ /**
458
+ * Calculates the required fee amount in microunits.
459
+ *
460
+ * Formula: `fee = feeBase * quantity * price * (1 - price)`
461
+ *
462
+ * All values are in microunits where 1,000,000 = $1.00.
463
+ *
464
+ * @param quantity - Order quantity in microunits (e.g. 1_000_000 for 1 share)
465
+ * @param price - Order price in microunits (e.g. 500_000 for $0.50)
466
+ * @param feeBase - Fee base in microunits (e.g. 70_000 for 7%)
467
+ * @returns Fee amount in microunits (ceiling)
468
+ *
469
+ * @example
470
+ * ```typescript
471
+ * // 1 share at $0.50 with 7% fee base
472
+ * calculateFee(1_000_000, 500_000, 70_000); // => 17500
473
+ * ```
474
+ */
475
+ declare const calculateFee: (quantity: number | bigint, price: number | bigint, feeBase: number | bigint) => number;
476
+ /**
477
+ * Calculates the fee when given a total amount that includes the fee.
478
+ *
479
+ * @param totalAmount - Total amount in microunits including fee
480
+ * @param price - Price in microunits
481
+ * @param feeBase - Fee base in microunits
482
+ * @returns Fee amount in microunits (ceiling)
483
+ */
484
+ declare const calculateFeeFromTotal: (totalAmount: number | bigint, price: number | bigint, feeBase: number | bigint) => number;
485
+
486
+ /**
487
+ * Computes matching counterparty orders from an orderbook for a given order.
488
+ *
489
+ * Supports both direct matching (Buy YES vs Sell YES) and complementary matching
490
+ * (Buy YES at 60c vs Buy NO at 40c — prices sum to $1.00).
491
+ *
492
+ * @param orderbook - The full orderbook for the market
493
+ * @param isBuying - Whether the taker is buying
494
+ * @param isYes - Whether the taker's position is Yes (true) or No (false)
495
+ * @param quantity - Desired quantity in microunits
496
+ * @param price - Desired price in microunits
497
+ * @param slippageTolerance - Maximum acceptable slippage in microunits
498
+ * @returns Array of counterparty matches, sorted by best price, up to the requested quantity
499
+ */
500
+ declare const calculateMatchingOrders: (orderbook: Orderbook, isBuying: boolean, isYes: boolean, quantity: number, price: number, slippageTolerance: number) => CounterpartyMatch[];
501
+
502
+ /**
503
+ * Decodes raw Algorand global state array into a key-value object.
504
+ *
505
+ * @param rawState - The raw global-state array from algod/indexer
506
+ * @returns Decoded key-value object
507
+ */
508
+ declare const decodeGlobalState: (rawState: any[]) => Record<string, any>;
509
+ /**
510
+ * Reads the global state of a market app from the chain.
511
+ *
512
+ * @param algodClient - Algod client
513
+ * @param marketAppId - The market app ID
514
+ * @returns Decoded market global state
515
+ */
516
+ declare const getMarketGlobalState: (algodClient: algosdk.Algodv2, marketAppId: number) => Promise<MarketGlobalState>;
517
+ /**
518
+ * Reads the global state of an escrow app via the indexer.
519
+ *
520
+ * @param indexerClient - Indexer client
521
+ * @param escrowAppId - The escrow app ID
522
+ * @returns Decoded escrow global state
523
+ */
524
+ declare const getEscrowGlobalState: (indexerClient: algosdk.Indexer, escrowAppId: number) => Promise<EscrowGlobalState>;
525
+ /**
526
+ * Checks if an address has opted into an ASA.
527
+ *
528
+ * @param algodClient - Algod client
529
+ * @param address - The Algorand address to check
530
+ * @param assetId - The ASA ID to check
531
+ * @returns True if opted in
532
+ */
533
+ declare const checkAssetOptIn: (algodClient: algosdk.Algodv2, address: string, assetId: number) => Promise<boolean>;
534
+
535
+ export { type AggregatedOrderbook, type AggregatedOrderbookEntry, type AggregatedOrderbookSide, AlphaClient, type AlphaClientConfig, type CancelOrderParams, type CancelOrderResult, type ClaimParams, type ClaimResult, type CounterpartyMatch, type CreateLimitOrderParams, type CreateMarketOrderParams, type CreateMarketOrderResult, type CreateOrderResult, type EscrowGlobalState, type Market, type MarketGlobalState, type MarketOption, type MergeSharesParams, type OpenOrder, type OrderSide, type Orderbook, type OrderbookEntry, type OrderbookSide, type Position, type ProposeMatchParams, type ProposeMatchResult, type SplitMergeResult, type SplitSharesParams, type WalletPosition, calculateFee, calculateFeeFromTotal, calculateMatchingOrders, checkAssetOptIn, decodeGlobalState, getEscrowGlobalState, getMarketGlobalState };