@pear-protocol/hyperliquid 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.
package/README.md ADDED
@@ -0,0 +1,615 @@
1
+ # @pear-protocol/hyperliquid
2
+
3
+ TypeScript SDK for building on Pear Protocol — pair trading on Hyperliquid.
4
+
5
+ ```bash
6
+ npm install @pear-protocol/hyperliquid
7
+ ```
8
+
9
+ ```ts
10
+ import { PearSdk } from '@pear-protocol/hyperliquid';
11
+ const sdk = new PearSdk();
12
+ ```
13
+
14
+ ---
15
+
16
+ ## 1. Market Data
17
+
18
+ ### Instruments
19
+
20
+ Real-time prices, funding rates, and leverage for every listed asset.
21
+
22
+ ```ts
23
+ const unsub = sdk.market.instrument((instruments) => {
24
+ for (const token of instruments) {
25
+ console.log(token.assetName, token.markPrice, token.funding);
26
+ }
27
+ });
28
+ ```
29
+
30
+ | Field | Type | Description |
31
+ |-------|------|-------------|
32
+ | `assetName` | `string` | Symbol (`"BTC"`, `"ETH"`) |
33
+ | `markPrice` | `number` | Mark price |
34
+ | `oraclePrice` | `number` | Oracle price |
35
+ | `prevDayPrice` | `number` | Previous day price |
36
+ | `priceChange24hPercent` | `number` | 24h change % |
37
+ | `funding` | `number` | Funding rate |
38
+ | `maxLeverage` | `number` | Max leverage |
39
+
40
+ ### Basket Snapshot
41
+
42
+ Real-time computed metrics for any long/short basket. Weights are decimals (0-1), and the sum of all long + short weights must equal 1.
43
+
44
+ ```ts
45
+ const unsub = sdk.market.basket(
46
+ [{ symbol: 'BTC', weight: 0.25 }, { symbol: 'ETH', weight: 0.25 }],
47
+ [{ symbol: 'SOL', weight: 0.5 }],
48
+ (snapshot) => {
49
+ snapshot.weightedRatio; // geometric mean ratio
50
+ snapshot.priceRatio; // long/short price (1L:1S only, else null)
51
+ snapshot.price; // single-asset price (1L or 1S only, else null)
52
+ snapshot.weightedRatio24hAgo; // weightedRatio 24h ago
53
+ snapshot.priceRatio24hAgo; // priceRatio 24h ago (1L:1S only, else null)
54
+ snapshot.price24hAgo; // price 24h ago (1L or 1S only, else null)
55
+ snapshot.netFundingRate; // weighted net funding in decimal form (e.g. 0.01 for 1%)
56
+ snapshot.maxLeverage; // min across all assets
57
+ },
58
+ );
59
+ ```
60
+
61
+ ### Ideas (Picks)
62
+
63
+ Pre-curated trading baskets streamed via WebSocket. Includes actives, watchlist (when authenticated), and AI-generated pairs.
64
+
65
+ ```ts
66
+ const unsub = sdk.market.picks((baskets) => {
67
+ for (const b of baskets) {
68
+ b.category; // 'active' | 'watchlist' | 'ai-picks'
69
+ b.name; // optional display name
70
+ b.longTokens; // TokenSelection[] (symbol + weight)
71
+ b.shortTokens;
72
+ b.weightedRatio; // computed from live prices
73
+ b.netFundingRate;
74
+ b.maxLeverage;
75
+ }
76
+ });
77
+ ```
78
+
79
+ Filter by category on the client:
80
+
81
+ ```ts
82
+ const aiPicks = baskets.filter((b) => b.category === 'ai-picks');
83
+ ```
84
+
85
+ ### Chart
86
+
87
+ Historical and real-time OHLCV candle data for any basket or individual asset.
88
+
89
+ ```ts
90
+ const { chart } = sdk.market;
91
+
92
+ // 1. Configure tokens
93
+ chart.setTokens(
94
+ [{ symbol: 'BTC', weight: 0.25 }, { symbol: 'ETH', weight: 0.25 }],
95
+ [{ symbol: 'SOL', weight: 0.5 }],
96
+ );
97
+
98
+ // 2. Set candle interval (default: '1h')
99
+ chart.setCandleInterval('15m');
100
+
101
+ // 3. Fetch historical bars (requires chart type)
102
+ const bars = await chart.getBars('weighted-ratio', startMs, endMs);
103
+ const assetBars = await chart.getAssetBars('BTC', startMs, endMs);
104
+
105
+ // 4. Real-time basket updates
106
+ const subId = chart.subscribeRealtimeBars('weighted-ratio', (bar) => {
107
+ console.log(bar.time, bar.close);
108
+ });
109
+
110
+ // 5. Real-time single-asset updates
111
+ const assetSubId = chart.subscribeRealtimeAssetBars('BTC', (bar) => {
112
+ console.log(bar.time, bar.close);
113
+ });
114
+
115
+ // 6. Unsubscribe
116
+ chart.unsubscribeRealtimeBars(subId);
117
+ chart.unsubscribeRealtimeBars(assetSubId);
118
+
119
+ // 7. Get earliest data boundary (useful for "no data" pagination)
120
+ const boundary = chart.getEffectiveDataBoundary(); // ms timestamp | null
121
+
122
+ // 8. Clear cached data (call after changing tokens or interval)
123
+ chart.clearCache();
124
+
125
+ // 9. Cleanup
126
+ chart.destroy();
127
+ ```
128
+
129
+ #### Chart Types
130
+
131
+ | Type | Description |
132
+ |------|-------------|
133
+ | `'weighted-ratio'` | Geometric mean of weighted long basket / short basket prices |
134
+ | `'price-ratio'` | Long / short price ratio (1L:1S pairs only, else empty) |
135
+ | `'performance'` | Individual asset performance from a baseline — use with overlays |
136
+ | `'price'` | Single-asset price |
137
+
138
+ #### Candle Intervals
139
+
140
+ ```ts
141
+ type CandleInterval =
142
+ | '1m' | '3m' | '5m' | '15m' | '30m'
143
+ | '1h' | '2h' | '4h' | '8h' | '12h'
144
+ | '1d' | '3d' | '1w' | '1M';
145
+ ```
146
+
147
+ #### Bar Shape
148
+
149
+ ```ts
150
+ interface Bar {
151
+ time: number; // ms timestamp
152
+ open: number;
153
+ high: number;
154
+ low: number;
155
+ close: number;
156
+ }
157
+ ```
158
+
159
+ ### TradingView Datafeed Integration
160
+
161
+ The SDK chart plugs into TradingView's `IBasicDataFeed`. Two key methods to wire up:
162
+
163
+ #### `getBars` — Historical Data
164
+
165
+ Convert TradingView's resolution to a `CandleInterval`, set it on the chart, then fetch bars. TradingView passes timestamps in seconds — multiply by 1000 for the SDK.
166
+
167
+ ```ts
168
+ // Map TradingView resolution strings to SDK intervals:
169
+ // '1' → '1m', '5' → '5m', '60' → '1h', '240' → '4h', '1D' → '1d', etc.
170
+
171
+ const sdkInterval = tvResolutionToSdkInterval(resolution);
172
+ chart.setCandleInterval(sdkInterval);
173
+
174
+ const startMs = periodParams.from * 1000;
175
+ const endMs = periodParams.to * 1000;
176
+
177
+ // For basket symbols → chart.getBars(chartType, startMs, endMs)
178
+ // For individual assets (e.g. 'LONG:BTC') → chart.getAssetBars('BTC', startMs, endMs)
179
+ const bars = await chart.getBars('weighted-ratio', startMs, endMs);
180
+
181
+ // When no data, use getEffectiveDataBoundary() to tell TradingView where data starts
182
+ if (bars.length === 0) {
183
+ const boundary = chart.getEffectiveDataBoundary();
184
+ onResult([], { noData: true, nextTime: boundary ? boundary / 1000 : undefined });
185
+ }
186
+ ```
187
+
188
+ #### `subscribeBars` — Real-time Updates
189
+
190
+ Subscribe to live bar updates. Map each TradingView `listenerGuid` to an SDK subscription ID for cleanup.
191
+
192
+ ```ts
193
+ // For basket symbols:
194
+ const subId = chart.subscribeRealtimeBars('weighted-ratio', (bar) => {
195
+ onTick({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close });
196
+ });
197
+
198
+ // For individual assets (used in 'performance' overlays):
199
+ const subId = chart.subscribeRealtimeAssetBars('BTC', (bar) => {
200
+ onTick({ time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close });
201
+ });
202
+
203
+ // Unsubscribe:
204
+ chart.unsubscribeRealtimeBars(subId);
205
+ ```
206
+
207
+ ---
208
+
209
+ ## 2. Authentication
210
+
211
+ EIP-712 signature-based login. Tokens auto-refresh on 401.
212
+
213
+ ```ts
214
+ // 1. Get message to sign
215
+ const { data: msg } = await sdk.api.public.auth.getEIP712Message(address, clientId, chainId);
216
+
217
+ // 2. Sign with wallet (e.g. wagmi signTypedData)
218
+ const signature = await signTypedData({ ... });
219
+
220
+ // 3. Login
221
+ await sdk.api.public.auth.login({
222
+ address,
223
+ clientId: CLIENT_ID,
224
+ details: { signature, timestamp: eip712.timestamp, chainId },
225
+ method: 'eip712',
226
+ });
227
+
228
+ // 4. Set address for WebSocket subscriptions
229
+ sdk.setAddress(address);
230
+ ```
231
+
232
+ Token persistence:
233
+
234
+ ```ts
235
+ // Restore from storage
236
+ sdk.api.setTokens(savedAccessToken, savedRefreshToken);
237
+
238
+ // Read current tokens
239
+ const accessToken = sdk.api.getAccessToken();
240
+ const refreshToken = sdk.api.getRefreshToken();
241
+
242
+ // Listen for refreshes
243
+ sdk.api.onTokenRefreshed = (access, refresh) => {
244
+ localStorage.setItem('tokens', JSON.stringify({ access, refresh }));
245
+ };
246
+
247
+ sdk.api.onLogout = () => {
248
+ localStorage.removeItem('tokens');
249
+ };
250
+ ```
251
+
252
+ ### Approval Checks
253
+
254
+ Before trading, two Hyperliquid approvals are required:
255
+
256
+ ```ts
257
+ const builderOk = await sdk.user.isBuilderApproved();
258
+ const agentOk = await sdk.user.isAgentApproved();
259
+ ```
260
+
261
+ **Builder fee approval** — if `isBuilderApproved()` returns `false`, the user must send an `approveBuilderFee` action to the Hyperliquid exchange endpoint ([docs](https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#approve-a-builder-fee)):
262
+
263
+ ```ts
264
+ // Sign and send via Hyperliquid exchange API
265
+ {
266
+ "type": "approveBuilderFee",
267
+ "builder": "0xA47D4d99191db54A4829cdf3de2417E527c3b042",
268
+ "maxFeeRate": "0.06%"
269
+ }
270
+ ```
271
+
272
+ **Agent wallet approval** — if `isAgentApproved()` returns `false`, create and approve via:
273
+
274
+ ```ts
275
+ await sdk.api.private.agentWallet.create();
276
+ ```
277
+
278
+ ---
279
+
280
+ ## 3. Trading
281
+
282
+ ### Open a Position
283
+
284
+ ```ts
285
+ await sdk.api.private.positions.create({
286
+ usdValue: 1000,
287
+ leverage: 5,
288
+ longAssets: [{ asset: 'BTC', weight: 0.25 }, { asset: 'ETH', weight: 0.25 }],
289
+ shortAssets: [{ asset: 'SOL', weight: 0.5 }],
290
+ executionType: 'MARKET',
291
+ slippage: 0.02,
292
+ takeProfit: { type: 'PERCENTAGE', value: 10 },
293
+ stopLoss: { type: 'PERCENTAGE', value: -5 },
294
+ });
295
+ ```
296
+
297
+ ### Close
298
+
299
+ ```ts
300
+ await sdk.api.private.positions.close(positionId, { executionType: 'MARKET' });
301
+ await sdk.api.private.positions.closeAll({ executionType: 'MARKET' });
302
+ ```
303
+
304
+ ### Adjust Size & Leverage
305
+
306
+ ```ts
307
+ await sdk.api.private.positions.adjust(positionId, {
308
+ adjustmentType: 'INCREASE',
309
+ usdValue: 500,
310
+ executionType: 'MARKET',
311
+ });
312
+
313
+ await sdk.api.private.positions.adjustLeverage(positionId, {
314
+ leverage: 10,
315
+ });
316
+ ```
317
+
318
+ ### Update TP/SL
319
+
320
+ ```ts
321
+ await sdk.api.private.positions.updateRiskParameters(positionId, {
322
+ takeProfit: { type: 'PERCENTAGE', value: 15 },
323
+ stopLoss: { type: 'PERCENTAGE', value: -8, trailingStop: true },
324
+ });
325
+ ```
326
+
327
+ ### Rebalance Weights
328
+
329
+ Preview then execute weight changes on an existing position:
330
+
331
+ ```ts
332
+ const { data: plan } = await sdk.api.private.positions.planRebalance(positionId, {
333
+ targetWeights: { BTC: 0.3, ETH: 0.2, SOL: 0.5 },
334
+ });
335
+ // plan.assets — what will be traded
336
+ // plan.skippedAssets — what can't be changed and why
337
+
338
+ await sdk.api.private.positions.rebalance(positionId, {
339
+ targetWeights: { BTC: 0.3, ETH: 0.2, SOL: 0.5 },
340
+ });
341
+ ```
342
+
343
+ ### Adjust (Advanced)
344
+
345
+ Set target absolute sizes per asset on an existing position:
346
+
347
+ ```ts
348
+ await sdk.api.private.positions.adjustAdvance(positionId, [{
349
+ longAssets: [{ asset: 'BTC', size: 0.5 }, { asset: 'ETH', size: 2.0 }],
350
+ shortAssets: [{ asset: 'SOL', size: 10.0 }],
351
+ }]);
352
+ ```
353
+
354
+ ### TWAP Orders
355
+
356
+ ```ts
357
+ await sdk.api.private.positions.create({
358
+ usdValue: 5000,
359
+ leverage: 3,
360
+ longAssets: [{ asset: 'BTC', weight: 0.5 }],
361
+ shortAssets: [{ asset: 'ETH', weight: 0.5 }],
362
+ executionType: 'TWAP',
363
+ twapDuration: 3600,
364
+ twapInterval: 300,
365
+ });
366
+ ```
367
+
368
+ ### Spot Orders
369
+
370
+ ```ts
371
+ await sdk.api.private.orders.spot({
372
+ coin: 'HYPE',
373
+ isBuy: true,
374
+ sz: 100,
375
+ limitPx: null,
376
+ });
377
+ ```
378
+
379
+ ### Cancel Orders
380
+
381
+ ```ts
382
+ await sdk.api.private.orders.cancel(orderId);
383
+ await sdk.api.private.orders.cancelTwap(orderId);
384
+ ```
385
+
386
+ ---
387
+
388
+ ## 4. Tracking
389
+
390
+ All subscriptions return an unsubscribe function. Require `sdk.setAddress()` first.
391
+
392
+ ### Positions
393
+
394
+ Enriched with real-time mark prices from Hyperliquid.
395
+
396
+ ```ts
397
+ const unsub = sdk.user.positions((positions) => {
398
+ for (const pos of positions) {
399
+ pos.positionId;
400
+ pos.entryRatio;
401
+ pos.markRatio;
402
+ pos.unrealizedPnl;
403
+ pos.unrealizedPnlPercentage;
404
+ pos.marginUsed;
405
+ pos.positionValue;
406
+ pos.stopLoss;
407
+ pos.takeProfit;
408
+
409
+ for (const asset of [...pos.longAssets, ...pos.shortAssets]) {
410
+ asset.coin;
411
+ asset.entryPrice;
412
+ asset.actualSize;
413
+ asset.leverage;
414
+ asset.unrealizedPnl;
415
+ asset.currentWeight;
416
+ asset.fundingPaid;
417
+ }
418
+ }
419
+ });
420
+ ```
421
+
422
+ ### Orders
423
+
424
+ ```ts
425
+ sdk.user.orders((orders) => {
426
+ for (const ord of orders) {
427
+ ord.orderId;
428
+ ord.orderType; // 'MARKET' | 'TRIGGER' | 'TWAP' | 'TP' | 'SL'
429
+ ord.status; // 'OPEN' | 'PROCESSING' | 'EXECUTED'
430
+ ord.longAssets;
431
+ ord.shortAssets;
432
+ }
433
+ });
434
+ ```
435
+
436
+ ### Account & History
437
+
438
+ ```ts
439
+ sdk.user.accountSummary((summary) => { ... });
440
+ sdk.user.tradeHistories((histories) => { ... });
441
+ sdk.user.twapDetails((twaps) => { ... });
442
+ sdk.user.notifications((notifications) => {
443
+ for (const n of notifications) {
444
+ n.category; // 'TP_ORDER_FILLED' | 'POSITION_LIQUIDATED' | ...
445
+ n.parameters; // typed per category
446
+ n.is_read;
447
+ }
448
+ });
449
+ ```
450
+
451
+ ### Fills Sync
452
+
453
+ Automatic. Syncs on first connection and on every `userFills` WebSocket event from Hyperliquid. No manual call needed.
454
+
455
+ ---
456
+
457
+ ## 5. REST API Reference
458
+
459
+ ### Public (no auth)
460
+
461
+ ```ts
462
+ await sdk.api.public.markets.list({ searchText: 'BTC', sort: 'volume' });
463
+ await sdk.api.public.markets.active();
464
+ await sdk.api.public.fills.list({ address: '0x...', assetName: 'BTC' });
465
+ await sdk.api.public.stats.addressStats('0xabc,0xdef');
466
+ ```
467
+
468
+ ### Private (requires auth)
469
+
470
+ ```ts
471
+ // Account
472
+ await sdk.api.private.accounts.summary();
473
+ await sdk.api.private.portfolio.get();
474
+
475
+ // Agent wallet
476
+ await sdk.api.private.agentWallet.get();
477
+ await sdk.api.private.agentWallet.create();
478
+
479
+ // API keys
480
+ await sdk.api.private.apiKeys.list();
481
+ await sdk.api.private.apiKeys.create({ name: 'Bot Key' });
482
+
483
+ // Positions — see Trading section above
484
+ await sdk.api.private.positions.list();
485
+ await sdk.api.private.positions.create({ ... });
486
+ await sdk.api.private.positions.close(id, { ... });
487
+ await sdk.api.private.positions.closeAll({ ... });
488
+ await sdk.api.private.positions.adjust(id, { ... });
489
+ await sdk.api.private.positions.adjustAdvance(id, [{ longAssets: [{ asset, size }], shortAssets: [{ asset, size }] }]);
490
+ await sdk.api.private.positions.adjustLeverage(id, { ... });
491
+ await sdk.api.private.positions.updateRiskParameters(id, { ... });
492
+ await sdk.api.private.positions.planRebalance(id, { targetWeights });
493
+ await sdk.api.private.positions.rebalance(id, { targetWeights });
494
+
495
+ // Orders
496
+ await sdk.api.private.orders.list({ page: 1, limit: 50, status: 'EXECUTED' });
497
+ await sdk.api.private.orders.open();
498
+ await sdk.api.private.orders.twap();
499
+ await sdk.api.private.orders.cancel(orderId);
500
+ await sdk.api.private.orders.cancelTwap(orderId);
501
+ await sdk.api.private.orders.spot({ ... });
502
+ await sdk.api.private.orders.triggers({ category: 'TP' });
503
+ await sdk.api.private.orders.kalshiTriggers({ category: 'active', search: '', cursor: '', pageSize: 20 });
504
+
505
+ // History
506
+ await sdk.api.private.tradeHistory.list({ limit: 100 });
507
+ await sdk.api.private.notifications.list({ limit: 50 });
508
+ await sdk.api.private.notifications.markRead({ ids: ['...'] });
509
+
510
+ // Watchlist
511
+ await sdk.api.private.watchlist.get();
512
+ await sdk.api.private.watchlist.toggle({ longAssets: [...], shortAssets: [...] });
513
+
514
+ // Sync
515
+ await sdk.api.private.sync.fills({ user, fills, assetPositions });
516
+ ```
517
+
518
+ ---
519
+
520
+ ## 6. Error Handling
521
+
522
+ All API methods throw `PearApiError` on failure. Each error carries a machine-readable `code`, HTTP `statusCode`, and human-readable `message`.
523
+
524
+ ```ts
525
+ import { PearApiError, PearErrorCode } from '@pear-protocol/hyperliquid';
526
+
527
+ try {
528
+ await sdk.api.private.positions.close(positionId, { executionType: 'MARKET' });
529
+ } catch (err) {
530
+ if (err instanceof PearApiError) {
531
+ err.statusCode; // 404
532
+ err.code; // 'POSITION_NOT_FOUND'
533
+ err.message; // 'Position abc123 not found or already closed'
534
+ err.originalError; // raw AxiosError (for headers, response body, etc.)
535
+ }
536
+ }
537
+ ```
538
+
539
+ ### Handling by Status Code
540
+
541
+ ```ts
542
+ try {
543
+ await sdk.api.private.positions.create({ ... });
544
+ } catch (err) {
545
+ if (!(err instanceof PearApiError)) throw err;
546
+
547
+ switch (err.statusCode) {
548
+ case 400: showValidationError(err.message); break;
549
+ case 404: showNotFound(err.message); break;
550
+ case 408: retryAfterDelay(); break; // ACTIVE_TRADE_TIMEOUT
551
+ case 409: showConflict(err.message); break;
552
+ case 429: backoff(); break;
553
+ default: showGenericError(); break;
554
+ }
555
+ }
556
+ ```
557
+
558
+ ### Handling by Error Code
559
+
560
+ ```ts
561
+ if (err instanceof PearApiError && err.code === PearErrorCode.ACTIVE_TRADE_TIMEOUT) {
562
+ // Another trade is in progress, retry shortly
563
+ await sleep(2000);
564
+ return retry();
565
+ }
566
+ ```
567
+
568
+ ### Network Errors
569
+
570
+ When the server is unreachable, `PearApiError` is thrown with `statusCode: 0` and `code: 'NETWORK_ERROR'`.
571
+
572
+ ```ts
573
+ if (err instanceof PearApiError && err.statusCode === 0) {
574
+ showOfflineMessage();
575
+ }
576
+ ```
577
+
578
+ ### Error Codes Reference
579
+
580
+ | Code | Status | When |
581
+ |------|--------|------|
582
+ | `POSITION_NOT_FOUND` | 404 | Position doesn't exist or already closed |
583
+ | `POSITION_NOT_OPEN` | 400 | Position exists but isn't open |
584
+ | `POSITION_UNAUTHORIZED` | 403 | Position belongs to another user |
585
+ | `ORDER_NOT_FOUND` | 404 | Order doesn't exist |
586
+ | `INVALID_ORDER_STATUS` | 400 | Order can't be cancelled in current status |
587
+ | `INVALID_ORDER_TYPE` | 400 | Wrong order type for the endpoint |
588
+ | `ACTIVE_TRADE_TIMEOUT` | 408 | Another trade is still processing |
589
+ | `UNSUPPORTED_EXECUTION_TYPE` | 400 | Execution type not supported for this action |
590
+ | `DUPLICATE_TRIGGER` | 409 | Position already has an active trigger |
591
+ | `INVALID_ADDRESS` | 400 | Malformed wallet address |
592
+ | `MISSING_REQUIRED_FIELD` | 400 | Required parameter missing |
593
+ | `INVALID_FIELD_VALUE` | 400 | Parameter value is invalid |
594
+ | `UNSUPPORTED_TRIGGER_TYPE` | 400 | Trigger type not supported |
595
+ | `INVALID_POSITION_STRUCTURE` | 400 | Position assets don't match trigger requirements |
596
+ | `INVALID_LADDER_CONFIG` | 400 | Missing or invalid ladder configuration |
597
+ | `TWAP_DURATION_REQUIRED` | 400 | TWAP order missing duration |
598
+ | `TWAP_INSUFFICIENT_VALUE` | 400 | Asset value too low for TWAP chunking |
599
+ | `INVALID_RISK_PARAMETERS` | 400 | Can't update TP/SL for this position |
600
+ | `HL_CANCEL_FAILED` | 500 | Failed to cancel order on Hyperliquid |
601
+ | `LEVERAGE_CONFIG_FAILED` | 400 | Leverage configuration failed |
602
+ | `VAULT_WALLET_NOT_FOUND` | 404 | Vault wallet doesn't exist |
603
+ | `VAULT_UNAUTHORIZED` | 403 | Vault doesn't belong to this user |
604
+ | `VAULT_UNSUPPORTED_TOKEN` | 400 | Token not supported for this vault operation |
605
+ | `INTERNAL_ERROR` | 500 | Unexpected server error |
606
+ | `NETWORK_ERROR` | 0 | Server unreachable |
607
+
608
+ ---
609
+
610
+ ## 7. Cleanup
611
+
612
+ ```ts
613
+ sdk.destroy(); // closes all WebSocket connections and subscriptions
614
+ ```
615
+