@crypticdot/defituna-api 1.1.28

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/dist/index.mjs ADDED
@@ -0,0 +1,493 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/client/client.ts
8
+ import camelcaseKeys from "camelcase-keys";
9
+
10
+ // src/client/schemas.ts
11
+ var schemas_exports = {};
12
+ __export(schemas_exports, {
13
+ LendingPosition: () => LendingPosition,
14
+ LimitOrder: () => LimitOrder,
15
+ LimitOrderState: () => LimitOrderState,
16
+ LimitOrderStateSchema: () => LimitOrderStateSchema,
17
+ Market: () => Market,
18
+ Mint: () => Mint,
19
+ NotificationAction: () => NotificationAction,
20
+ NotificationActionSchema: () => NotificationActionSchema,
21
+ NotificationEntity: () => NotificationEntity,
22
+ NotificationEntitySchema: () => NotificationEntitySchema,
23
+ OrderBook: () => OrderBook,
24
+ OrderBookEntry: () => OrderBookEntry,
25
+ OrderBookNotification: () => OrderBookNotification,
26
+ OrderBookNotificationMeta: () => OrderBookNotificationMeta,
27
+ Pool: () => Pool,
28
+ PoolPriceCandle: () => PoolPriceCandle,
29
+ PoolPriceUpdate: () => PoolPriceUpdate,
30
+ PoolPriceUpdateNotification: () => PoolPriceUpdateNotification,
31
+ PoolProvider: () => PoolProvider,
32
+ PoolProviderSchema: () => PoolProviderSchema,
33
+ PoolSwap: () => PoolSwap,
34
+ PoolSwapNotification: () => PoolSwapNotification,
35
+ PoolTicks: () => PoolTicks,
36
+ Tick: () => Tick,
37
+ TokenOraclePrice: () => TokenOraclePrice,
38
+ TunaPosition: () => TunaPosition,
39
+ TunaPositionState: () => TunaPositionState,
40
+ TunaPositionStateSchema: () => TunaPositionStateSchema,
41
+ Vault: () => Vault
42
+ });
43
+ import { z } from "zod";
44
+ var amountWithoutUsd = z.object({
45
+ amount: z.coerce.bigint()
46
+ });
47
+ var amountWithUsd = z.object({
48
+ amount: z.coerce.bigint(),
49
+ usd: z.number()
50
+ });
51
+ var tokensPnl = z.object({
52
+ amount: z.coerce.bigint(),
53
+ bps: z.number()
54
+ });
55
+ var usdPnl = z.object({
56
+ amount: z.number(),
57
+ bps: z.number()
58
+ });
59
+ var NotificationEntity = {
60
+ POOL_SWAP: "pool_swap",
61
+ POOL_PRICE: "pool_price",
62
+ ORDER_BOOK: "order_book"
63
+ };
64
+ var NotificationAction = {
65
+ CREATE: "create",
66
+ UPDATE: "update"
67
+ };
68
+ var PoolProvider = {
69
+ ORCA: "orca",
70
+ FUSION: "fusion"
71
+ };
72
+ var TunaPositionState = {
73
+ OPEN: "open",
74
+ LIQUIDATED: "liquidated",
75
+ CLOSED_BY_LIMIT_ORDER: "closed_by_limit_order",
76
+ CLOSED: "closed"
77
+ };
78
+ var LimitOrderState = {
79
+ OPEN: "open",
80
+ PARTIALLY_FILLED: "partially_filled",
81
+ FILLED: "filled",
82
+ COMPLETE: "complete",
83
+ CANCELLED: "cancelled"
84
+ };
85
+ var NotificationEntitySchema = z.enum([NotificationEntity.POOL_SWAP, ...Object.values(NotificationEntity)]);
86
+ var NotificationActionSchema = z.enum([NotificationAction.CREATE, ...Object.values(NotificationAction)]);
87
+ var PoolProviderSchema = z.enum([PoolProvider.ORCA, ...Object.values(PoolProvider)]);
88
+ var TunaPositionStateSchema = z.enum([TunaPositionState.OPEN, ...Object.values(TunaPositionState)]);
89
+ var LimitOrderStateSchema = z.enum([LimitOrderState.OPEN, ...Object.values(LimitOrderState)]);
90
+ var Mint = z.object({
91
+ symbol: z.string(),
92
+ mint: z.string(),
93
+ logo: z.string(),
94
+ decimals: z.number()
95
+ });
96
+ var Market = z.object({
97
+ address: z.string(),
98
+ addressLookupTable: z.string(),
99
+ poolAddress: z.string(),
100
+ poolFeeRate: z.number(),
101
+ provider: PoolProviderSchema,
102
+ maxLeverage: z.number(),
103
+ protocolFee: z.number(),
104
+ protocolFeeOnCollateral: z.number(),
105
+ liquidationFee: z.number(),
106
+ liquidationThreshold: z.number(),
107
+ limitOrderExecutionFee: z.number(),
108
+ borrowedFundsA: amountWithUsd,
109
+ borrowedFundsB: amountWithUsd,
110
+ availableBorrowA: amountWithUsd,
111
+ availableBorrowB: amountWithUsd,
112
+ borrowLimitA: amountWithUsd,
113
+ borrowLimitB: amountWithUsd,
114
+ disabled: z.boolean()
115
+ });
116
+ var TokenOraclePrice = z.object({
117
+ mint: z.string(),
118
+ price: z.coerce.bigint(),
119
+ decimals: z.number(),
120
+ time: z.coerce.date()
121
+ });
122
+ var Vault = z.object({
123
+ address: z.string(),
124
+ mint: z.string(),
125
+ depositedFunds: amountWithUsd,
126
+ borrowedFunds: amountWithUsd,
127
+ supplyLimit: amountWithUsd,
128
+ borrowedShares: z.coerce.bigint(),
129
+ depositedShares: z.coerce.bigint(),
130
+ supplyApy: z.number(),
131
+ borrowApy: z.number(),
132
+ utilization: z.number(),
133
+ pythOracleFeedId: z.string(),
134
+ pythOraclePriceUpdate: z.string()
135
+ });
136
+ var Pool = z.object({
137
+ address: z.string(),
138
+ provider: PoolProviderSchema,
139
+ tokenAMint: z.string(),
140
+ tokenBMint: z.string(),
141
+ tokenAVault: z.string(),
142
+ tokenBVault: z.string(),
143
+ tvlUsdc: z.coerce.number(),
144
+ tickSpacing: z.number(),
145
+ feeRate: z.number(),
146
+ protocolFeeRate: z.number(),
147
+ liquidity: z.coerce.bigint(),
148
+ sqrtPrice: z.coerce.bigint(),
149
+ tickCurrentIndex: z.number(),
150
+ stats: z.object({
151
+ "24h": z.object({
152
+ volume: z.coerce.number(),
153
+ fees: z.coerce.number(),
154
+ rewards: z.coerce.number(),
155
+ yieldOverTvl: z.coerce.number()
156
+ }),
157
+ "7d": z.object({
158
+ volume: z.coerce.number(),
159
+ fees: z.coerce.number(),
160
+ rewards: z.coerce.number(),
161
+ yieldOverTvl: z.coerce.number()
162
+ }),
163
+ "30d": z.object({
164
+ volume: z.coerce.number(),
165
+ fees: z.coerce.number(),
166
+ rewards: z.coerce.number(),
167
+ yieldOverTvl: z.coerce.number()
168
+ })
169
+ })
170
+ });
171
+ var Tick = z.object({
172
+ index: z.number(),
173
+ liquidity: z.coerce.bigint()
174
+ });
175
+ var PoolTicks = z.object({
176
+ tickSpacing: z.number(),
177
+ ticks: Tick.array()
178
+ });
179
+ var LendingPosition = z.object({
180
+ address: z.string(),
181
+ authority: z.string(),
182
+ mint: z.string(),
183
+ vault: z.string(),
184
+ shares: z.coerce.bigint(),
185
+ funds: amountWithUsd,
186
+ earned: amountWithUsd
187
+ });
188
+ var TunaPosition = z.object({
189
+ address: z.string(),
190
+ authority: z.string(),
191
+ version: z.number(),
192
+ state: TunaPositionStateSchema,
193
+ positionMint: z.string(),
194
+ liquidity: z.coerce.bigint(),
195
+ tickLowerIndex: z.number(),
196
+ tickUpperIndex: z.number(),
197
+ tickEntryIndex: z.number(),
198
+ tickStopLossIndex: z.number(),
199
+ tickTakeProfitIndex: z.number(),
200
+ swapToTokenOnLimitOrder: z.number(),
201
+ flags: z.number(),
202
+ pool: z.string(),
203
+ poolSqrtPrice: z.coerce.bigint(),
204
+ depositedCollateralA: amountWithoutUsd,
205
+ depositedCollateralB: amountWithoutUsd,
206
+ depositedCollateralUsd: z.object({
207
+ amount: z.number()
208
+ }),
209
+ loanFundsA: amountWithUsd,
210
+ loanFundsB: amountWithUsd,
211
+ currentLoanA: amountWithUsd,
212
+ currentLoanB: amountWithUsd,
213
+ leftoversA: amountWithUsd,
214
+ leftoversB: amountWithUsd,
215
+ yieldA: amountWithUsd,
216
+ yieldB: amountWithUsd,
217
+ compoundedYieldA: amountWithUsd,
218
+ compoundedYieldB: amountWithUsd,
219
+ totalA: amountWithUsd,
220
+ totalB: amountWithUsd,
221
+ pnlA: tokensPnl,
222
+ pnlB: tokensPnl,
223
+ pnlUsd: usdPnl,
224
+ openedAt: z.coerce.date(),
225
+ updatedAtSlot: z.coerce.bigint()
226
+ });
227
+ var PoolSwap = z.object({
228
+ id: z.string(),
229
+ amountIn: z.coerce.bigint(),
230
+ amountOut: z.coerce.bigint(),
231
+ aToB: z.boolean(),
232
+ pool: z.string(),
233
+ time: z.coerce.date()
234
+ });
235
+ var OrderBookEntry = z.object({
236
+ concentratedAmount: z.coerce.bigint(),
237
+ concentratedAmountQuote: z.coerce.bigint(),
238
+ concentratedTotal: z.coerce.bigint(),
239
+ concentratedTotalQuote: z.coerce.bigint(),
240
+ limitAmount: z.coerce.bigint(),
241
+ limitAmountQuote: z.coerce.bigint(),
242
+ limitTotal: z.coerce.bigint(),
243
+ limitTotalQuote: z.coerce.bigint(),
244
+ price: z.number(),
245
+ askSide: z.boolean()
246
+ });
247
+ var PoolPriceUpdate = z.object({
248
+ pool: z.string(),
249
+ price: z.number(),
250
+ sqrtPrice: z.coerce.bigint(),
251
+ time: z.coerce.date()
252
+ });
253
+ var OrderBook = z.object({
254
+ entries: OrderBookEntry.array(),
255
+ poolPrice: z.number()
256
+ });
257
+ var LimitOrder = z.object({
258
+ address: z.string(),
259
+ mint: z.string(),
260
+ pool: z.string(),
261
+ state: LimitOrderStateSchema,
262
+ aToB: z.boolean(),
263
+ tickIndex: z.number(),
264
+ fillRatio: z.number(),
265
+ openTxSignature: z.string(),
266
+ closeTxSignature: z.string().nullable(),
267
+ amount: amountWithUsd,
268
+ openedAt: z.coerce.date(),
269
+ closedAt: z.coerce.date().nullable()
270
+ });
271
+ var PoolPriceCandle = z.object({
272
+ time: z.number(),
273
+ open: z.number(),
274
+ close: z.number(),
275
+ high: z.number(),
276
+ low: z.number()
277
+ });
278
+ var createNotificationSchema = (dataSchema, metaSchema) => z.object({
279
+ entity: NotificationEntitySchema,
280
+ action: NotificationActionSchema,
281
+ data: dataSchema,
282
+ id: z.string(),
283
+ authority: z.nullable(z.string()),
284
+ ...metaSchema ? { meta: metaSchema } : { meta: z.undefined().nullable() }
285
+ });
286
+ var OrderBookNotificationMeta = z.object({
287
+ pool: z.string(),
288
+ priceStep: z.number(),
289
+ inverted: z.boolean()
290
+ });
291
+ var PoolSwapNotification = createNotificationSchema(PoolSwap);
292
+ var PoolPriceUpdateNotification = createNotificationSchema(PoolPriceUpdate);
293
+ var OrderBookNotification = createNotificationSchema(OrderBook, OrderBookNotificationMeta);
294
+
295
+ // src/client/client.ts
296
+ var DEFAULT_TIMEOUT = 5e3;
297
+ var DEFAULT_HTTP_RETRIES = 3;
298
+ var ProviderFilter = /* @__PURE__ */ ((ProviderFilter2) => {
299
+ ProviderFilter2["ORCA"] = "orca";
300
+ ProviderFilter2["FUSION"] = "fusion";
301
+ ProviderFilter2["ALL"] = "all";
302
+ return ProviderFilter2;
303
+ })(ProviderFilter || {});
304
+ var TunaApiClient = class {
305
+ get baseURL() {
306
+ return this._baseURL;
307
+ }
308
+ get timeout() {
309
+ return this._timeout;
310
+ }
311
+ get httpRetries() {
312
+ return this._httpRetries;
313
+ }
314
+ get headers() {
315
+ return this._headers;
316
+ }
317
+ constructor(baseURL, config) {
318
+ this._baseURL = baseURL;
319
+ this._timeout = config?.timeout ?? DEFAULT_TIMEOUT;
320
+ this._httpRetries = config?.httpRetries ?? DEFAULT_HTTP_RETRIES;
321
+ this._headers = config?.headers ?? {};
322
+ }
323
+ setConfig(config) {
324
+ if (config.baseURL) {
325
+ this._baseURL = config.baseURL;
326
+ }
327
+ this._timeout = config?.timeout ?? DEFAULT_TIMEOUT;
328
+ this._httpRetries = config?.httpRetries ?? DEFAULT_HTTP_RETRIES;
329
+ this._headers = config?.headers ?? {};
330
+ }
331
+ async httpRequest(url, schema, options, retries = this.httpRetries, backoff = 100 + Math.floor(Math.random() * 100)) {
332
+ try {
333
+ const controller = new AbortController();
334
+ const abort = setTimeout(() => {
335
+ controller.abort();
336
+ }, this.timeout);
337
+ const signal = options?.signal || controller.signal;
338
+ const response = await fetch(url, {
339
+ ...options,
340
+ signal,
341
+ headers: { ...this.headers, ...options?.headers }
342
+ });
343
+ clearTimeout(abort);
344
+ if (!response.ok) {
345
+ const errorBody = await response.json();
346
+ throw errorBody;
347
+ }
348
+ const data = await response.json();
349
+ const transformed = camelcaseKeys(data, { deep: true, exclude: ["24h", "7d", "30d"] });
350
+ return schema.parse(transformed.data);
351
+ } catch (error) {
352
+ if (retries > 0 && !(error instanceof Error && error.name === "AbortError")) {
353
+ await new Promise((resolve) => setTimeout(resolve, backoff));
354
+ return this.httpRequest(url, schema, options, retries - 1, backoff * 2);
355
+ }
356
+ throw error;
357
+ }
358
+ }
359
+ /* Endpoints */
360
+ async getMints() {
361
+ const url = this.buildURL("mints");
362
+ return await this.httpRequest(url.toString(), Mint.array());
363
+ }
364
+ async getMint(mintAddress) {
365
+ const url = this.buildURL(`mints/${mintAddress}`);
366
+ return await this.httpRequest(url.toString(), Mint);
367
+ }
368
+ async getMarkets() {
369
+ const url = this.buildURL("markets");
370
+ return await this.httpRequest(url.toString(), Market.array());
371
+ }
372
+ async getMarket(marketAddress) {
373
+ const url = this.buildURL(`markets/${marketAddress}`);
374
+ return await this.httpRequest(url.toString(), Market);
375
+ }
376
+ async getOraclePrices() {
377
+ const url = this.buildURL("oracle-prices");
378
+ return await this.httpRequest(url.toString(), TokenOraclePrice.array());
379
+ }
380
+ async getOraclePrice(mintAddress) {
381
+ const url = this.buildURL(`oracle-prices/${mintAddress}`);
382
+ return await this.httpRequest(url.toString(), TokenOraclePrice);
383
+ }
384
+ async getVaults() {
385
+ const url = this.buildURL("vaults");
386
+ return await this.httpRequest(url.toString(), Vault.array());
387
+ }
388
+ async getVault(vaultAddress) {
389
+ const url = this.buildURL(`vaults/${vaultAddress}`);
390
+ return await this.httpRequest(url.toString(), Vault);
391
+ }
392
+ async getPools(providerFilter) {
393
+ const url = this.buildURL("pools");
394
+ if (providerFilter && providerFilter !== "all" /* ALL */) {
395
+ this.appendUrlSearchParams(url, { provider: providerFilter });
396
+ }
397
+ return await this.httpRequest(url.toString(), Pool.array());
398
+ }
399
+ async getPool(address) {
400
+ const url = this.buildURL(`pools/${address}`);
401
+ return await this.httpRequest(url.toString(), Pool);
402
+ }
403
+ async getPoolTicks(poolAddress) {
404
+ const url = this.buildURL(`pools/${poolAddress}/ticks`);
405
+ return await this.httpRequest(url.toString(), PoolTicks);
406
+ }
407
+ async getPoolSwaps(poolAddress) {
408
+ const url = this.buildURL(`pools/${poolAddress}/swaps`);
409
+ return await this.httpRequest(url.toString(), PoolSwap.array());
410
+ }
411
+ async getPoolOrderBook(poolAddress, priceStep, inverted) {
412
+ const url = this.buildURL(`pools/${poolAddress}/order-book`);
413
+ this.appendUrlSearchParams(url, { price_step: priceStep.toString() });
414
+ if (inverted) {
415
+ this.appendUrlSearchParams(url, { inverted: inverted.toString() });
416
+ }
417
+ return await this.httpRequest(url.toString(), OrderBook);
418
+ }
419
+ async getPoolPriceCandles(poolAddress, options) {
420
+ const { from, to, interval, candles } = options;
421
+ const url = this.buildURL(`pools/${poolAddress}/candles`);
422
+ this.appendUrlSearchParams(url, {
423
+ from: from.toISOString(),
424
+ to: to.toISOString(),
425
+ candles: candles.toString(),
426
+ interval
427
+ });
428
+ return await this.httpRequest(url.toString(), PoolPriceCandle.array());
429
+ }
430
+ async getUserLendingPositions(userAddress) {
431
+ const url = this.buildURL(`users/${userAddress}/lending-positions`);
432
+ return await this.httpRequest(url.toString(), LendingPosition.array());
433
+ }
434
+ async getUserLendingPositionByAddress(userAddress, lendingPositionAddress) {
435
+ const url = this.buildURL(`users/${userAddress}/lending-positions/${lendingPositionAddress}`);
436
+ return await this.httpRequest(url.toString(), LendingPosition);
437
+ }
438
+ async getUserTunaPositions(userAddress) {
439
+ const url = this.buildURL(`users/${userAddress}/tuna-positions`);
440
+ return await this.httpRequest(url.toString(), TunaPosition.array());
441
+ }
442
+ async getUserTunaPositionByAddress(userAddress, tunaPositionAddress) {
443
+ const url = this.buildURL(`users/${userAddress}/tuna-positions/${tunaPositionAddress}`);
444
+ return await this.httpRequest(url.toString(), TunaPosition);
445
+ }
446
+ async getUserLimitOrders(userAddress, poolFilter) {
447
+ const url = this.buildURL(`users/${userAddress}/limit-orders`);
448
+ if (poolFilter) {
449
+ this.appendUrlSearchParams(url, { pool: poolFilter });
450
+ }
451
+ return await this.httpRequest(url.toString(), LimitOrder.array());
452
+ }
453
+ async getUserLimitOrderByAddress(userAddress, limitOrderAddress) {
454
+ const url = this.buildURL(`users/${userAddress}/limit-orders/${limitOrderAddress}`);
455
+ return await this.httpRequest(url.toString(), LimitOrder);
456
+ }
457
+ async getPoolUpdatesStream(poolAddress, priceStep, inverted) {
458
+ const url = this.buildURL(`stream`);
459
+ this.appendUrlSearchParams(url, { pool: poolAddress });
460
+ if (priceStep) {
461
+ this.appendUrlSearchParams(url, { price_step: priceStep.toString() });
462
+ }
463
+ if (inverted) {
464
+ this.appendUrlSearchParams(url, { inverted: inverted.toString() });
465
+ }
466
+ return new EventSource(url.toString());
467
+ }
468
+ /* Utility functions */
469
+ buildURL(endpoint) {
470
+ return new URL(
471
+ `./v1/${endpoint}`,
472
+ // We ensure the `baseURL` ends with a `/` so that URL doesn't resolve the
473
+ // path relative to the parent.
474
+ `${this.baseURL}${this.baseURL.endsWith("/") ? "" : "/"}`
475
+ );
476
+ }
477
+ appendUrlSearchParams(url, params) {
478
+ Object.entries(params).forEach(([key, value]) => {
479
+ if (value !== void 0) {
480
+ url.searchParams.append(key, String(value));
481
+ }
482
+ });
483
+ }
484
+ };
485
+ export {
486
+ NotificationAction,
487
+ NotificationEntity,
488
+ PoolProvider,
489
+ ProviderFilter,
490
+ TunaApiClient,
491
+ TunaPositionState,
492
+ schemas_exports as schemas
493
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@crypticdot/defituna-api",
3
+ "version": "1.1.28",
4
+ "private": false,
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "license": "MIT",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "devDependencies": {
17
+ "@crypticdot/defituna-client": "^2.0.6",
18
+ "@crypticdot/eslint-config": "^1.0.0",
19
+ "@crypticdot/prettier-config": "^1.0.0",
20
+ "@crypticdot/typescript-config": "^1.0.0",
21
+ "@solana/kit": "^2.1.0",
22
+ "@types/node": "^22.13.14",
23
+ "decimal.js": "^10.5.0",
24
+ "dotenv": "^16.4.7",
25
+ "eslint": "^8.57.0",
26
+ "eventsource": "^3.0.6",
27
+ "express": "^4.21.2",
28
+ "tsup": "^8.3.5",
29
+ "typescript": "5.8.3",
30
+ "vite-tsconfig-paths": "^5.1.4",
31
+ "vitest": "^3.0.9"
32
+ },
33
+ "dependencies": {
34
+ "camelcase-keys": "^9.1.3",
35
+ "zod": "^3.24.1"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup src/index.ts --format cjs,esm --dts",
39
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
40
+ "eslint": "eslint \"**/*.ts\"",
41
+ "eslint:fix": "eslint \"**/*.ts\" --fix",
42
+ "type-check": "tsc -p .",
43
+ "lint": "pnpm eslint && pnpm type-check",
44
+ "test": "vitest run --disable-console-intercept",
45
+ "test:start-server": "node scripts/startTestsServer.mjs",
46
+ "test:watch": "vitest watch"
47
+ }
48
+ }