@agg-build/sdk 1.0.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.
package/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # @agg-build/sdk
2
+
3
+ Vanilla TypeScript client for the [AGG](https://docs.agg.market/) prediction market aggregator —
4
+ one client, every surface (auth, markets, orderbooks, charts, trading, managed execution,
5
+ WebSockets).
6
+
7
+ ## What it is
8
+
9
+ `@agg-build/sdk` is the lowest-level package partners install. It speaks to the AGG API over HTTPS and
10
+ WebSockets, manages auth/session state, and exposes typed wrappers for every product endpoint
11
+ (markets, orderbooks, charts, trade execution, deposits/withdrawals, managed wallets, admin and
12
+ more). It has no framework dependency and works in browsers, Node.js, and React Native.
13
+
14
+ For React apps, layer `@agg-build/hooks`, `@agg-build/ui`, and `@agg-build/auth` on top — they all use this client
15
+ under the hood.
16
+
17
+ ## When to use this package
18
+
19
+ - You are building a vanilla TS/JS app, React Native app, or server-side integration.
20
+ - You want a typed, batteries-included client for the AGG REST API and WebSocket gateway.
21
+ - You are authoring your own React layer and need the underlying auth/session/trade primitives.
22
+ - You need a server-only helper: SSR config fetching, webhook verification, external-ID signing,
23
+ or the admin API client (see `@agg-build/sdk/server`).
24
+
25
+ If you are shipping a React app and want ready-made hooks or UI, install [`@agg-build/hooks`](https://www.npmjs.com/package/@agg-build/hooks),
26
+ [`@agg-build/ui`](https://www.npmjs.com/package/@agg-build/ui), and [`@agg-build/auth`](https://www.npmjs.com/package/@agg-build/auth)
27
+ instead — those packages are built on top of `@agg-build/sdk`.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm install @agg-build/sdk
33
+ ```
34
+
35
+ ## Quick start
36
+
37
+ ```typescript
38
+ import { createAggClient } from "@agg-build/sdk";
39
+
40
+ const client = createAggClient({
41
+ baseUrl: "https://api.agg.market",
42
+ appId: "your-app-id",
43
+ });
44
+
45
+ // Health check
46
+ const hello = await client.getHello();
47
+
48
+ // Current auth state (persisted across reloads via localStorage)
49
+ console.log(client.isAuthenticated, client.user);
50
+ ```
51
+
52
+ ### Authenticate a user (SIWE)
53
+
54
+ ```typescript
55
+ // 1. Start the auth flow
56
+ const { nonce } = await client.authStart({ provider: "siwe" });
57
+
58
+ // 2. Build the EIP-4361 message
59
+ const message = client.buildSiweMessage({
60
+ address: "0x…",
61
+ chainId: 1,
62
+ nonce,
63
+ domain: window.location.host,
64
+ uri: window.location.origin,
65
+ });
66
+
67
+ // 3. Sign with the user's wallet (wagmi, viem, ethers, …)
68
+ const signature = await wallet.signMessage(message);
69
+
70
+ // 4. Verify → stores accessToken + user in the client
71
+ const { accessToken, user } = await client.verify({ message, signature });
72
+ ```
73
+
74
+ SIWS (Solana) uses the same flow with `buildSiwsMessage` and a base58-encoded Ed25519 signature.
75
+
76
+ Redirect flows (Google, Apple, Twitter, email magic-link) return a URL to navigate to:
77
+
78
+ ```typescript
79
+ const response = await client.authStart({
80
+ provider: "google",
81
+ redirectUrl: "https://yourapp.com/auth/callback",
82
+ });
83
+
84
+ if (response.type === "redirect") {
85
+ window.location.assign(response.url);
86
+ }
87
+
88
+ // …back on the callback page:
89
+ const code = new URLSearchParams(window.location.search).get("code");
90
+ if (code) {
91
+ await client.exchangeAuthCode(code);
92
+ window.history.replaceState(null, "", window.location.pathname);
93
+ }
94
+ ```
95
+
96
+ ### HttpOnly cookie-refresh mode
97
+
98
+ For browser apps that prefer an `HttpOnly` refresh cookie over storing the refresh token in JS,
99
+ construct the client with `authDelivery: "cookie-refresh"`:
100
+
101
+ ```typescript
102
+ const client = createAggClient({
103
+ baseUrl: "https://api.agg.market",
104
+ appId: "your-app-id",
105
+ authDelivery: "cookie-refresh",
106
+ });
107
+ ```
108
+
109
+ In this mode, `verify()` and `exchangeAuthCode()` may omit `refreshToken` from the returned
110
+ object. The refresh cookie stays scoped to the AGG API host under `/auth` and is never exposed to
111
+ partner frontend domains.
112
+
113
+ ### Streaming data (WebSocket)
114
+
115
+ ```typescript
116
+ const ws = client.createWebSocket({
117
+ onSnapshot: (marketId, book) => console.log("snapshot", marketId, book),
118
+ onDelta: (marketId, book) => console.log("delta", marketId, book),
119
+ onTrade: (trade) => console.log("trade", trade.venue, trade.price, trade.size),
120
+ });
121
+
122
+ const unsubscribe = ws.subscribe("market-id");
123
+
124
+ // Optional: authenticate for user-scoped events (orders, balances)
125
+ ws.authenticate(accessToken);
126
+
127
+ // Cleanup
128
+ unsubscribe();
129
+ ws.destroy();
130
+ ```
131
+
132
+ `createWebSocket` requires `wsUrl` to be passed to `createAggClient`. The underlying
133
+ `AggWebSocket` can also be instantiated directly if you want finer control.
134
+
135
+ ### Server-side helpers
136
+
137
+ Import from the `/server` entrypoint for Node-only utilities:
138
+
139
+ ```typescript
140
+ import { AggAdminClient, signExternalId, verifyWebhook } from "@agg-build/sdk/server";
141
+
142
+ // Sign a partner external-ID assertion before sending to the browser
143
+ const assertion = signExternalId(process.env.AGG_APP_SECRET!, partnerUser.id);
144
+
145
+ // Verify an inbound webhook
146
+ verifyWebhook(rawBody, req.headers, process.env.AGG_WEBHOOK_SECRET!);
147
+
148
+ // Admin API client (uses x-app-api-key)
149
+ const admin = new AggAdminClient({
150
+ baseUrl: "https://api.agg.market",
151
+ apiKey: process.env.AGG_APP_API_KEY!,
152
+ });
153
+ const users = await admin.listUsers("your-app-id", { limit: 50 });
154
+ ```
155
+
156
+ ## Client options
157
+
158
+ | Option | Type | Required | Description |
159
+ | ---------------- | ---------------------------- | --------------------- | ----------------------------------------------------- |
160
+ | `baseUrl` | `string` | Yes | API base URL (e.g. `https://api.agg.market`) |
161
+ | `appId` | `string` | One of appId/adminKey | Partner app ID |
162
+ | `adminKey` | `string` | One of appId/adminKey | Admin API key for admin-tier routes |
163
+ | `wsUrl` | `string` | For WebSocket | WebSocket gateway URL (required by `createWebSocket`) |
164
+ | `authDelivery` | `"body" \| "cookie-refresh"` | No | Token delivery mode (default: `"body"`) |
165
+ | `persistSession` | `boolean` | No | Persist auth hint to localStorage (default: `true`) |
166
+ | `kalshiDemo` | `boolean` | No | Use Kalshi demo environment |
167
+
168
+ Call `client.destroy()` when you no longer need the client to release internal resources
169
+ (BroadcastChannel used for cross-tab auth sync).
170
+
171
+ ## Main exports
172
+
173
+ ### `AggClient` methods
174
+
175
+ #### Auth & session
176
+
177
+ | Method | Description |
178
+ | --------------------------------------------------- | ----------------------------------------------------- |
179
+ | `authStart(body)` | Start any auth flow (SIWE nonce, OAuth redirect, etc) |
180
+ | `buildSiweMessage(params)` | Build an EIP-4361 message string |
181
+ | `buildSiwsMessage(params)` | Build a Solana sign-in message |
182
+ | `verify({ message, signature })` | Verify SIWE/SIWS signature, store tokens |
183
+ | `exchangeAuthCode(code)` | Exchange redirect auth code for tokens |
184
+ | `refreshAccessToken()` | Refresh the JWT using the stored refresh token |
185
+ | `setSession({ accessToken, refreshToken?, user? })` | Seed tokens from a redirect callback |
186
+ | `setAccessToken(token)` / `clearAccessToken()` | Manage the JWT directly |
187
+ | `signOut()` | Revoke tokens server-side and clear local session |
188
+ | `onAuthStateChange(listener)` | Subscribe to auth state changes |
189
+ | `isAuthenticated` / `user` / `authStatus` | Getters for current session state |
190
+
191
+ #### User profile & accounts
192
+
193
+ | Method | Description |
194
+ | ------------------------------------------------- | ------------------------------------------- |
195
+ | `getCurrentUser()` | Fetch current user profile from `/users/me` |
196
+ | `updateUser(data)` | Update username, avatar, etc. |
197
+ | `createAvatarUploadUrl(contentType)` | Get a presigned avatar upload URL |
198
+ | `linkExternalId({ externalId, timestamp, hmac })` | Link a backend-signed partner user ID |
199
+ | `disconnectAccount(provider)` | Remove a linked auth provider |
200
+
201
+ #### Market data
202
+
203
+ | Method | Description |
204
+ | --------------------------- | --------------------------------------------------- |
205
+ | `getVenueEvents(options?)` | List prediction market events (cursor pagination) |
206
+ | `getVenueEventById(id)` | Get a single event by ID |
207
+ | `getVenueMarkets(options?)` | List venue markets with filters (cursor pagination) |
208
+ | `getCategories(options?)` | List market categories |
209
+
210
+ #### Orderbooks & routing
211
+
212
+ | Method | Description |
213
+ | ----------------------- | -------------------------------------------------------- |
214
+ | `getOrderbooks(params)` | Get live per-venue orderbooks for given `venueMarketIds` |
215
+ | `getSmartRoute(params)` | MILP solver for optimal order routing across venues |
216
+
217
+ #### Chart data
218
+
219
+ | Method | Description |
220
+ | ----------------------------- | ---------------------------------------------- |
221
+ | `getChartBars(params)` | TradingView-style OHLCV candles for an outcome |
222
+ | `getPricesHistory(params)` | Price history for venue market outcomes |
223
+ | `getOrderbookHistory(params)` | Orderbook depth history for an outcome |
224
+
225
+ #### Trading
226
+
227
+ | Method | Description |
228
+ | --------------------- | ----------------------------------------------- |
229
+ | `validateTrade(body)` | Pre-flight trade validation |
230
+ | `executeTrade(body)` | Execute orders on Kalshi/Polymarket via backend |
231
+ | `computeSplits(body)` | Compute best order splits across venues via API |
232
+
233
+ #### Venue keys
234
+
235
+ | Method | Description |
236
+ | ------------------------ | ------------------------------------------------ |
237
+ | `listVenueKeys(venues?)` | List stored venue API keys (no secrets returned) |
238
+ | `upsertVenueKey(body)` | Create or update a venue API key |
239
+ | `deleteVenueKey(venue)` | Delete a venue API key |
240
+
241
+ #### Managed execution & balances
242
+
243
+ | Method | Description |
244
+ | ----------------------------- | ---------------------------------------------------- |
245
+ | `validateManaged(params)` | Check balances and bridge requirements |
246
+ | `quoteManaged(params)` | Request a 2-min TTL quote with execution steps |
247
+ | `executeManaged(params)` | Execute a previously quoted trade |
248
+ | `withdrawManaged(params)` | Withdraw from managed wallets to an external address |
249
+ | `syncManagedBalances()` | Trigger on-chain balance sync |
250
+ | `cancelManagedOrder(orderId)` | Cancel a pending managed execution order |
251
+ | `getVenueBalances(venues)` | Get balances across venues |
252
+ | `getManagedBalances()` | Get managed wallet balances per-chain + per-venue |
253
+ | `getDepositAddresses()` | Get managed wallet deposit addresses (EVM + Solana) |
254
+
255
+ #### Orders & positions
256
+
257
+ | Method | Description |
258
+ | -------------------------------- | -------------------------------------------- |
259
+ | `getOrders(params?)` | List trade-executor orders |
260
+ | `getExecutionOrders(params?)` | List execution orders (cursor pagination) |
261
+ | `getExecutionPositions(params?)` | List execution positions (cursor pagination) |
262
+ | `getPositions(params?)` | Get open positions grouped by matched market |
263
+ | `getUserHoldings(params)` | Get user positions per venue |
264
+
265
+ #### Utilities
266
+
267
+ | Method | Description |
268
+ | ----------------------------- | ---------------------------------------------- |
269
+ | `getHello()` | Health check |
270
+ | `request(path, init?)` | Generic HTTP with auto-retry on 401 |
271
+ | `createWebSocket(callbacks?)` | Create a WebSocket instance (requires `wsUrl`) |
272
+ | `destroy()` | Release resources (BroadcastChannel, etc.) |
273
+
274
+ ### Standalone utilities
275
+
276
+ | Export | Description |
277
+ | --------------------------- | --------------------------------------------------------------- |
278
+ | `AggWebSocket` | Low-level WebSocket client (used by `client.createWebSocket()`) |
279
+ | `CandleBuilder` | Chart-library-agnostic candle builder (trades → OHLC) |
280
+ | `computeBestSplitsByAmount` | Pure function: optimal split across venue orderbooks |
281
+ | `applyOrderbookDelta` | Apply WS delta to an in-memory orderbook |
282
+ | `computeChecksum` | CRC32 orderbook checksum (used to detect desync) |
283
+ | `snapshotToOrderbook` | Convert a WS snapshot to the in-memory shape |
284
+ | `aggregateMidpoint` | Aggregate outcome midpoints across venues |
285
+ | `mergeCandles` | Merge live + historical candles, live-wins on collision |
286
+ | `mergeClosedCandles` | Variant that merges only fully closed candles |
287
+ | `TurnstileChallengeError` | Thrown when an auth endpoint requires a Turnstile challenge |
288
+
289
+ ### `@agg-build/sdk/server`
290
+
291
+ | Export | Description |
292
+ | --------------------------------------------- | ------------------------------------------------------------- |
293
+ | `AggAdminClient` | Admin REST client using `x-app-api-key` (server-only) |
294
+ | `signExternalId(secret, userId)` | HMAC-sign a partner user ID before calling `linkExternalId()` |
295
+ | `verifyWebhook(payload, headers, secret)` | Verify an inbound webhook signature |
296
+ | `parseWebhookEvent(payload, headers, secret)` | Verify + parse a webhook into a typed event |
297
+ | `WebhookVerificationError` | Thrown on signature/timestamp failures |
298
+
299
+ ## WebSocket callbacks
300
+
301
+ | Callback | When it fires |
302
+ | ------------------------------------ | ---------------------------------------------- |
303
+ | `onSnapshot(marketId, book)` | Full orderbook snapshot received |
304
+ | `onDelta(marketId, book)` | Incremental orderbook update applied |
305
+ | `onTrade(trade)` | Trade execution |
306
+ | `onHeartbeat(hb)` | Server heartbeat (every 30s) |
307
+ | `onSubscribed` / `onUnsubscribed` | Subscription lifecycle |
308
+ | `onConnected` / `onAuthenticated` | Connection / auth lifecycle |
309
+ | `onOrderSubmitted` | Order placed via WebSocket |
310
+ | `onBalanceUpdate` | Balance change event |
311
+ | `onMarketResolved` | Market resolution |
312
+ | `onDiagnostics(event)` | Diagnostic events (seq gaps, stale connection) |
313
+ | `onConnectionStateChange(connected)` | Connection state changed |
314
+ | `onError(msg)` | Error message from server |
315
+
316
+ Key behaviors:
317
+
318
+ - Single persistent connection with refcounted per-market subscriptions.
319
+ - In-memory orderbook state per market (snapshot + delta with CRC32 verification).
320
+ - Sequence gap or checksum mismatch triggers automatic resnapshot.
321
+ - Auto-reconnect with exponential backoff (base 1s, max 30s).
322
+ - Liveness: no message for 45s triggers reconnect; server heartbeats every 30s.
323
+ - Bids always sorted descending, asks always sorted ascending.
324
+
325
+ ## Headers attached automatically
326
+
327
+ - `x-app-id` — from the `appId` option
328
+ - `Authorization: Bearer <jwt>` — after sign-in
329
+ - `x-admin-key` — if `adminKey` is provided
330
+ - `x-auth-delivery: cookie-refresh` + `credentials: "include"` on auth-only requests when
331
+ `authDelivery: "cookie-refresh"` is enabled
332
+
333
+ ## Entrypoints
334
+
335
+ | Entry | Purpose |
336
+ | ----------------------- | -------------------------------------------------------------- |
337
+ | `@agg-build/sdk` | `AggClient`, `createAggClient`, `AggWebSocket`, utilities |
338
+ | `@agg-build/sdk/server` | Node-only helpers: admin client, external-ID / webhook signing |
339
+
340
+ ## Peer dependencies
341
+
342
+ None. `@agg-build/sdk` is dependency-light and bundles its own clients for `viem`, `ethers@5`,
343
+ `@polymarket/clob-client`, and `@polymarket/builder-signing-sdk`.
344
+
345
+ ## Links
346
+
347
+ - Documentation — <https://docs.agg.market/>
348
+ - Demo app — <https://demo.agg.market/>
349
+ - React hooks — [`@agg-build/hooks`](https://www.npmjs.com/package/@agg-build/hooks)
350
+ - Trading UI — [`@agg-build/ui`](https://www.npmjs.com/package/@agg-build/ui)
351
+ - Connect / sign-in UX — [`@agg-build/auth`](https://www.npmjs.com/package/@agg-build/auth)
352
+
353
+ ## License
354
+
355
+ MIT
@@ -0,0 +1,45 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __async = (__this, __arguments, generator) => {
21
+ return new Promise((resolve, reject) => {
22
+ var fulfilled = (value) => {
23
+ try {
24
+ step(generator.next(value));
25
+ } catch (e) {
26
+ reject(e);
27
+ }
28
+ };
29
+ var rejected = (value) => {
30
+ try {
31
+ step(generator.throw(value));
32
+ } catch (e) {
33
+ reject(e);
34
+ }
35
+ };
36
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
37
+ step((generator = generator.apply(__this, __arguments)).next());
38
+ });
39
+ };
40
+
41
+ export {
42
+ __spreadValues,
43
+ __spreadProps,
44
+ __async
45
+ };