@n1xyz/nord-ts 0.0.18-8121ed05.0 → 0.0.18
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/.local/qa.ts +77 -0
- package/.local/test-atomic.ts +112 -0
- package/check.sh +4 -0
- package/default.nix +47 -0
- package/dist/api/client.d.ts +14 -0
- package/dist/api/client.js +45 -0
- package/dist/gen/nord.d.ts +52 -23
- package/dist/gen/nord.js +322 -170
- package/dist/gen/openapi.d.ts +2244 -0
- package/dist/gen/openapi.js +6 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -9
- package/dist/nord/api/actions.d.ts +30 -1
- package/dist/nord/api/actions.js +60 -3
- package/dist/nord/api/core.d.ts +1 -34
- package/dist/nord/api/core.js +0 -71
- package/dist/nord/client/Nord.d.ts +31 -33
- package/dist/nord/client/Nord.js +100 -60
- package/dist/nord/client/NordUser.d.ts +64 -11
- package/dist/nord/client/NordUser.js +90 -33
- package/dist/nord/index.d.ts +0 -2
- package/dist/nord/index.js +0 -2
- package/dist/nord/models/Subscriber.d.ts +2 -2
- package/dist/types.d.ts +43 -190
- package/dist/utils.d.ts +1 -19
- package/dist/utils.js +5 -39
- package/dist/websocket/NordWebSocketClient.js +18 -13
- package/dist/websocket/index.d.ts +1 -1
- package/package.json +20 -27
- package/src/index.ts +0 -16
- package/src/nord/api/actions.ts +131 -9
- package/src/nord/api/core.ts +0 -71
- package/src/nord/client/Nord.ts +142 -76
- package/src/nord/client/NordUser.ts +171 -50
- package/src/nord/index.ts +0 -2
- package/src/nord/models/Subscriber.ts +2 -2
- package/src/types.ts +55 -216
- package/src/utils.ts +6 -42
- package/src/websocket/NordWebSocketClient.ts +23 -15
- package/src/websocket/index.ts +1 -1
- package/tests/utils.spec.ts +1 -34
- package/dist/idl/bridge.json +0 -1506
- package/jest.config.ts +0 -9
- package/nodemon.json +0 -4
- package/protoc-generate.sh +0 -23
- package/src/idl/bridge.json +0 -1506
- package/src/nord/api/market.ts +0 -122
- package/src/nord/api/queries.ts +0 -135
package/dist/nord/client/Nord.js
CHANGED
|
@@ -32,17 +32,21 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.Nord = void 0;
|
|
37
40
|
const events_1 = require("events");
|
|
41
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
42
|
+
const openapi_fetch_1 = __importDefault(require("openapi-fetch"));
|
|
38
43
|
const types_1 = require("../../types");
|
|
39
44
|
const proton_1 = require("@n1xyz/proton");
|
|
45
|
+
const proto = __importStar(require("../../gen/nord"));
|
|
40
46
|
const core = __importStar(require("../api/core"));
|
|
41
|
-
const market = __importStar(require("../api/market"));
|
|
42
47
|
const metrics = __importStar(require("../api/metrics"));
|
|
43
|
-
const
|
|
48
|
+
const utils = __importStar(require("../../utils"));
|
|
44
49
|
const NordError_1 = require("../utils/NordError");
|
|
45
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
46
50
|
/**
|
|
47
51
|
* Main Nord client class for interacting with the Nord API
|
|
48
52
|
*/
|
|
@@ -67,6 +71,7 @@ class Nord {
|
|
|
67
71
|
this.bridgeVk = bridgeVk;
|
|
68
72
|
this.solanaUrl = solanaUrl;
|
|
69
73
|
this.protonClient = protonClient;
|
|
74
|
+
this.httpClient = (0, openapi_fetch_1.default)({ baseUrl: webServerUrl });
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
77
|
* Create a WebSocket client with specific subscriptions
|
|
@@ -119,6 +124,18 @@ class Nord {
|
|
|
119
124
|
// Create and return a new WebSocket client
|
|
120
125
|
return core.initWebSocketClient(this.webServerUrl, subscriptions);
|
|
121
126
|
}
|
|
127
|
+
async GET(path, options) {
|
|
128
|
+
const r = await this.httpClient.GET(path, options);
|
|
129
|
+
if (r.error) {
|
|
130
|
+
throw new NordError_1.NordError(`failed to GET ${path}`, { cause: r.error });
|
|
131
|
+
}
|
|
132
|
+
if (r.data === undefined) {
|
|
133
|
+
// this should never happen, but the type checker seems unhappy.
|
|
134
|
+
// if we catch this we'll need to debug accordingly.
|
|
135
|
+
throw new NordError_1.NordError("internal assertion violation", { cause: r });
|
|
136
|
+
}
|
|
137
|
+
return r.data;
|
|
138
|
+
}
|
|
122
139
|
/**
|
|
123
140
|
* Get the current timestamp from the Nord server
|
|
124
141
|
*
|
|
@@ -126,7 +143,7 @@ class Nord {
|
|
|
126
143
|
* @throws {NordError} If the request fails
|
|
127
144
|
*/
|
|
128
145
|
async getTimestamp() {
|
|
129
|
-
return
|
|
146
|
+
return BigInt(await this.GET("/timestamp", {}));
|
|
130
147
|
}
|
|
131
148
|
/**
|
|
132
149
|
* Get the last event nonce from the Nord server
|
|
@@ -135,7 +152,7 @@ class Nord {
|
|
|
135
152
|
* @throws {NordError} If the request fails
|
|
136
153
|
*/
|
|
137
154
|
async getActionNonce() {
|
|
138
|
-
return
|
|
155
|
+
return await this.GET("/event/last-acked-nonce", {});
|
|
139
156
|
}
|
|
140
157
|
/**
|
|
141
158
|
* Fetch information about Nord markets and tokens
|
|
@@ -144,7 +161,7 @@ class Nord {
|
|
|
144
161
|
*/
|
|
145
162
|
async fetchNordInfo() {
|
|
146
163
|
try {
|
|
147
|
-
const info = await
|
|
164
|
+
const info = await this.GET("/info", {});
|
|
148
165
|
this.markets = info.markets;
|
|
149
166
|
this.tokens = info.tokens;
|
|
150
167
|
// Populate the symbolToMarketId map
|
|
@@ -194,15 +211,6 @@ class Nord {
|
|
|
194
211
|
async init() {
|
|
195
212
|
await this.fetchNordInfo();
|
|
196
213
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Get market statistics
|
|
199
|
-
*
|
|
200
|
-
* @returns Market statistics response
|
|
201
|
-
* @throws {NordError} If the request fails
|
|
202
|
-
*/
|
|
203
|
-
async marketsStats() {
|
|
204
|
-
return market.marketsStats(this.webServerUrl);
|
|
205
|
-
}
|
|
206
214
|
/**
|
|
207
215
|
* Query a specific action
|
|
208
216
|
*
|
|
@@ -210,8 +218,11 @@ class Nord {
|
|
|
210
218
|
* @returns Action response
|
|
211
219
|
* @throws {NordError} If the request fails
|
|
212
220
|
*/
|
|
213
|
-
async queryAction(
|
|
214
|
-
return
|
|
221
|
+
async queryAction({ action_id, }) {
|
|
222
|
+
return ((await this.queryRecentActions({
|
|
223
|
+
from: action_id,
|
|
224
|
+
to: action_id,
|
|
225
|
+
}))[0] ?? null);
|
|
215
226
|
}
|
|
216
227
|
/**
|
|
217
228
|
* Query recent actions
|
|
@@ -221,8 +232,17 @@ class Nord {
|
|
|
221
232
|
* @returns Actions response
|
|
222
233
|
* @throws {NordError} If the request fails
|
|
223
234
|
*/
|
|
224
|
-
async queryRecentActions(
|
|
225
|
-
|
|
235
|
+
async queryRecentActions(query) {
|
|
236
|
+
const xs = await this.GET("/action", {
|
|
237
|
+
params: {
|
|
238
|
+
query,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
return xs.map((x) => ({
|
|
242
|
+
actionId: x.actionId,
|
|
243
|
+
action: utils.decodeLengthDelimited(Buffer.from(x.payload, "base64"), proto.Action),
|
|
244
|
+
physicalExecTime: new Date(x.physicalTime * 1000),
|
|
245
|
+
}));
|
|
226
246
|
}
|
|
227
247
|
/**
|
|
228
248
|
* Get the last action ID
|
|
@@ -231,7 +251,7 @@ class Nord {
|
|
|
231
251
|
* @throws {NordError} If the request fails
|
|
232
252
|
*/
|
|
233
253
|
async getLastActionId() {
|
|
234
|
-
return
|
|
254
|
+
return await this.GET("/action/last-executed-id", {});
|
|
235
255
|
}
|
|
236
256
|
/**
|
|
237
257
|
* Fetch aggregate metrics from the Nord API
|
|
@@ -283,26 +303,6 @@ class Nord {
|
|
|
283
303
|
async getTotalTransactions() {
|
|
284
304
|
return metrics.getTotalTransactions(this.webServerUrl);
|
|
285
305
|
}
|
|
286
|
-
/**
|
|
287
|
-
* Query an action from Rollman
|
|
288
|
-
*
|
|
289
|
-
* @param query - Action query parameters
|
|
290
|
-
* @returns Rollman action response
|
|
291
|
-
* @throws {NordError} If the request fails
|
|
292
|
-
*/
|
|
293
|
-
async actionQueryRollman(query) {
|
|
294
|
-
return queries.actionQueryRollman(this.webServerUrl, query);
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Query actions from Rollman
|
|
298
|
-
*
|
|
299
|
-
* @param last_n - Number of recent actions to query
|
|
300
|
-
* @returns Rollman actions response
|
|
301
|
-
* @throws {NordError} If the request fails
|
|
302
|
-
*/
|
|
303
|
-
async actionsQueryRollman(last_n) {
|
|
304
|
-
return queries.actionsQueryRollman(this.webServerUrl, last_n);
|
|
305
|
-
}
|
|
306
306
|
/**
|
|
307
307
|
* Query Prometheus metrics
|
|
308
308
|
*
|
|
@@ -408,7 +408,26 @@ class Nord {
|
|
|
408
408
|
* @throws {NordError} If the request fails
|
|
409
409
|
*/
|
|
410
410
|
async getTrades(query) {
|
|
411
|
-
|
|
411
|
+
if (query.sinceRcf3339 && !utils.isRfc3339(query.sinceRcf3339)) {
|
|
412
|
+
throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.sinceRcf3339}`);
|
|
413
|
+
}
|
|
414
|
+
if (query.untilRfc3339 && !utils.isRfc3339(query.untilRfc3339)) {
|
|
415
|
+
throw new NordError_1.NordError(`Invalid RFC3339 timestamp: ${query.untilRfc3339}`);
|
|
416
|
+
}
|
|
417
|
+
return await this.GET("/trades", {
|
|
418
|
+
params: {
|
|
419
|
+
query: {
|
|
420
|
+
takerId: query.takerId,
|
|
421
|
+
makerId: query.makerId,
|
|
422
|
+
marketId: query.marketId,
|
|
423
|
+
pageSize: query.pageSize,
|
|
424
|
+
takerSide: query.takerSide,
|
|
425
|
+
since: query.sinceRcf3339,
|
|
426
|
+
until: query.untilRfc3339,
|
|
427
|
+
startInclusive: query.pageId,
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
});
|
|
412
431
|
}
|
|
413
432
|
/**
|
|
414
433
|
* Get user account IDs
|
|
@@ -417,8 +436,16 @@ class Nord {
|
|
|
417
436
|
* @returns User account IDs response
|
|
418
437
|
* @throws {NordError} If the request fails
|
|
419
438
|
*/
|
|
420
|
-
async
|
|
421
|
-
|
|
439
|
+
async getUser(query) {
|
|
440
|
+
const r = await this.httpClient.GET("/user/{pubkey}", {
|
|
441
|
+
params: {
|
|
442
|
+
path: { pubkey: query.pubkey.toString() },
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
if (r.response.status === 404) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
return r.data;
|
|
422
449
|
}
|
|
423
450
|
/**
|
|
424
451
|
* Get orderbook for a market
|
|
@@ -431,18 +458,29 @@ class Nord {
|
|
|
431
458
|
*/
|
|
432
459
|
async getOrderbook(query) {
|
|
433
460
|
// If only symbol is provided, convert it to market_id
|
|
461
|
+
let marketId;
|
|
434
462
|
if (query.symbol && query.market_id === undefined) {
|
|
435
463
|
// If the map is empty, try to fetch market information first
|
|
436
464
|
if (this.symbolToMarketId.size === 0) {
|
|
437
465
|
await this.fetchNordInfo();
|
|
438
466
|
}
|
|
439
|
-
const
|
|
440
|
-
if (
|
|
467
|
+
const id = this.symbolToMarketId.get(query.symbol);
|
|
468
|
+
if (id === undefined) {
|
|
441
469
|
throw new NordError_1.NordError(`Unknown market symbol: ${query.symbol}`);
|
|
442
470
|
}
|
|
443
|
-
|
|
471
|
+
marketId = id;
|
|
472
|
+
}
|
|
473
|
+
else if (query.market_id !== undefined) {
|
|
474
|
+
marketId = query.market_id;
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
throw new NordError_1.NordError("Either symbol or market_id must be provided for orderbook query");
|
|
444
478
|
}
|
|
445
|
-
return
|
|
479
|
+
return await this.GET("/market/{market_id}/orderbook", {
|
|
480
|
+
params: {
|
|
481
|
+
path: { market_id: marketId },
|
|
482
|
+
},
|
|
483
|
+
});
|
|
446
484
|
}
|
|
447
485
|
/**
|
|
448
486
|
* Get information about the Nord server
|
|
@@ -451,7 +489,7 @@ class Nord {
|
|
|
451
489
|
* @throws {NordError} If the request fails
|
|
452
490
|
*/
|
|
453
491
|
async getInfo() {
|
|
454
|
-
return
|
|
492
|
+
return await this.GET("/info", {});
|
|
455
493
|
}
|
|
456
494
|
/**
|
|
457
495
|
* Get account information
|
|
@@ -461,31 +499,33 @@ class Nord {
|
|
|
461
499
|
* @throws {NordError} If the request fails
|
|
462
500
|
*/
|
|
463
501
|
async getAccount(accountId) {
|
|
464
|
-
return
|
|
502
|
+
return await this.GET("/account/{account_id}", {
|
|
503
|
+
params: {
|
|
504
|
+
path: { account_id: accountId },
|
|
505
|
+
},
|
|
506
|
+
});
|
|
465
507
|
}
|
|
466
508
|
/**
|
|
467
509
|
* Get market statistics (alias for marketsStats for backward compatibility)
|
|
468
510
|
*
|
|
469
|
-
* @deprecated Use marketsStats instead
|
|
470
511
|
* @returns Market statistics response
|
|
471
512
|
*/
|
|
472
|
-
async getMarketStats() {
|
|
473
|
-
return this.
|
|
513
|
+
async getMarketStats({ marketId, }) {
|
|
514
|
+
return await this.GET("/market/{market_id}/stats", {
|
|
515
|
+
params: {
|
|
516
|
+
path: { market_id: marketId },
|
|
517
|
+
},
|
|
518
|
+
});
|
|
474
519
|
}
|
|
475
520
|
/**
|
|
476
521
|
* Check if an account exists for the given address
|
|
477
522
|
*
|
|
478
523
|
* @param address - The public key address to check
|
|
479
524
|
* @returns True if the account exists, false otherwise
|
|
525
|
+
* @deprecated use getUser instead
|
|
480
526
|
*/
|
|
481
|
-
async accountExists(
|
|
482
|
-
|
|
483
|
-
await market.getUserAccountIds(this.webServerUrl, { pubkey: address });
|
|
484
|
-
return true;
|
|
485
|
-
}
|
|
486
|
-
catch {
|
|
487
|
-
return false;
|
|
488
|
-
}
|
|
527
|
+
async accountExists(pubkey) {
|
|
528
|
+
return !!(await this.getUser({ pubkey }));
|
|
489
529
|
}
|
|
490
530
|
}
|
|
491
531
|
exports.Nord = Nord;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Connection, PublicKey } from "@solana/web3.js";
|
|
1
|
+
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
|
2
2
|
import Decimal from "decimal.js";
|
|
3
|
-
import { SPLTokenInfo } from "
|
|
4
|
-
import
|
|
3
|
+
import { FillMode, Side, SPLTokenInfo } from "../../types";
|
|
4
|
+
import * as proto from "../../gen/nord";
|
|
5
5
|
import { BigIntValue } from "../../utils";
|
|
6
6
|
import { Nord } from "./Nord";
|
|
7
7
|
/**
|
|
@@ -17,7 +17,7 @@ export interface NordUserParams {
|
|
|
17
17
|
/** Function to sign messages with the user's session key */
|
|
18
18
|
sessionSignFn: (message: Uint8Array) => Promise<Uint8Array>;
|
|
19
19
|
/** Function to sign transactions with the user's wallet (optional) */
|
|
20
|
-
transactionSignFn: (
|
|
20
|
+
transactionSignFn: <T extends Transaction>(tx: T) => Promise<T>;
|
|
21
21
|
/** Solana connection (optional) */
|
|
22
22
|
connection?: Connection;
|
|
23
23
|
/** Session ID (optional) */
|
|
@@ -63,6 +63,31 @@ export interface TransferParams {
|
|
|
63
63
|
/** Destination account ID */
|
|
64
64
|
toAccountId: number;
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Parameters for individual atomic subactions (user-friendly version)
|
|
68
|
+
*/
|
|
69
|
+
export interface UserAtomicSubaction {
|
|
70
|
+
/** The type of action to perform. */
|
|
71
|
+
kind: "place" | "cancel";
|
|
72
|
+
/** The market ID to place the order in. */
|
|
73
|
+
marketId?: number;
|
|
74
|
+
/** The order ID to cancel. */
|
|
75
|
+
orderId?: BigIntValue;
|
|
76
|
+
/** Order side (bid or ask) */
|
|
77
|
+
side?: Side;
|
|
78
|
+
/** Fill mode (limit, market, etc.) */
|
|
79
|
+
fillMode?: FillMode;
|
|
80
|
+
/** Whether the order is reduce-only. */
|
|
81
|
+
isReduceOnly?: boolean;
|
|
82
|
+
/** The size of the order. */
|
|
83
|
+
size?: Decimal.Value;
|
|
84
|
+
/** Order price */
|
|
85
|
+
price?: Decimal.Value;
|
|
86
|
+
/** Quote size (for market orders) */
|
|
87
|
+
quoteSize?: Decimal.Value;
|
|
88
|
+
/** The client order ID of the order. */
|
|
89
|
+
clientOrderId?: BigIntValue;
|
|
90
|
+
}
|
|
66
91
|
/**
|
|
67
92
|
* User class for interacting with the Nord protocol
|
|
68
93
|
*/
|
|
@@ -76,7 +101,7 @@ export declare class NordUser {
|
|
|
76
101
|
/** Function to sign messages with the user's session key */
|
|
77
102
|
readonly sessionSignFn: (message: Uint8Array) => Promise<Uint8Array>;
|
|
78
103
|
/** Function to sign transactions with the user's wallet */
|
|
79
|
-
readonly transactionSignFn: (
|
|
104
|
+
readonly transactionSignFn: <T extends Transaction>(tx: T) => Promise<T>;
|
|
80
105
|
/** User balances by token symbol */
|
|
81
106
|
balances: {
|
|
82
107
|
[key: string]: {
|
|
@@ -85,10 +110,6 @@ export declare class NordUser {
|
|
|
85
110
|
symbol: string;
|
|
86
111
|
}[];
|
|
87
112
|
};
|
|
88
|
-
/** User orders by market symbol */
|
|
89
|
-
orders: {
|
|
90
|
-
[key: string]: Order[];
|
|
91
|
-
};
|
|
92
113
|
/** User positions by account ID */
|
|
93
114
|
positions: {
|
|
94
115
|
[key: string]: {
|
|
@@ -170,10 +191,26 @@ export declare class NordUser {
|
|
|
170
191
|
*
|
|
171
192
|
* @param amount - Amount to deposit
|
|
172
193
|
* @param tokenId - Token ID
|
|
194
|
+
* @param recipient - Recipient address; defaults to the user's address
|
|
173
195
|
* @returns Transaction signature
|
|
196
|
+
* @deprecated Use deposit instead
|
|
174
197
|
* @throws {NordError} If required parameters are missing or operation fails
|
|
175
198
|
*/
|
|
176
|
-
depositSpl(amount: number, tokenId: number): Promise<string>;
|
|
199
|
+
depositSpl(amount: number, tokenId: number, recipient?: PublicKey): Promise<string>;
|
|
200
|
+
/**
|
|
201
|
+
* Deposit SPL tokens to the bridge
|
|
202
|
+
*
|
|
203
|
+
* @param amount - Amount to deposit
|
|
204
|
+
* @param tokenId - Token ID
|
|
205
|
+
* @param recipient - Recipient address; defaults to the user's address
|
|
206
|
+
* @returns Transaction signature
|
|
207
|
+
* @throws {NordError} If required parameters are missing or operation fails
|
|
208
|
+
*/
|
|
209
|
+
deposit({ amount, tokenId, recipient, }: Readonly<{
|
|
210
|
+
amount: number;
|
|
211
|
+
tokenId: number;
|
|
212
|
+
recipient?: PublicKey;
|
|
213
|
+
}>): Promise<string>;
|
|
177
214
|
/**
|
|
178
215
|
* Get a new nonce for actions
|
|
179
216
|
*
|
|
@@ -218,7 +255,10 @@ export declare class NordUser {
|
|
|
218
255
|
* @param amount - Amount to withdraw
|
|
219
256
|
* @throws {NordError} If the operation fails
|
|
220
257
|
*/
|
|
221
|
-
withdraw(
|
|
258
|
+
withdraw({ amount, tokenId, }: Readonly<{
|
|
259
|
+
tokenId: number;
|
|
260
|
+
amount: number;
|
|
261
|
+
}>): Promise<{
|
|
222
262
|
actionId: bigint;
|
|
223
263
|
}>;
|
|
224
264
|
/**
|
|
@@ -245,6 +285,19 @@ export declare class NordUser {
|
|
|
245
285
|
* @throws {NordError} If the operation fails
|
|
246
286
|
*/
|
|
247
287
|
transferToAccount(params: TransferParams): Promise<void>;
|
|
288
|
+
/**
|
|
289
|
+
* Execute up to four place/cancel operations atomically.
|
|
290
|
+
* Per Market:
|
|
291
|
+
* 1. cancels can only be in the start (one cannot predict future order ids)
|
|
292
|
+
* 2. intermediate trades can trade only
|
|
293
|
+
* 3. placements go last
|
|
294
|
+
*
|
|
295
|
+
* Across Markets, order action can be any
|
|
296
|
+
*
|
|
297
|
+
* @param userActions array of user-friendly subactions
|
|
298
|
+
* @param providedAccountId optional account performing the action (defaults to first account)
|
|
299
|
+
*/
|
|
300
|
+
atomic(userActions: UserAtomicSubaction[], providedAccountId?: number): Promise<proto.Receipt_AtomicResult>;
|
|
248
301
|
/**
|
|
249
302
|
* Helper function to retry a promise with exponential backoff
|
|
250
303
|
*
|
|
@@ -56,8 +56,6 @@ class NordUser {
|
|
|
56
56
|
constructor({ address, nord, publicKey, sessionPubKey, sessionSignFn, transactionSignFn, walletSignFn, connection, sessionId, }) {
|
|
57
57
|
/** User balances by token symbol */
|
|
58
58
|
this.balances = {};
|
|
59
|
-
/** User orders by market symbol */
|
|
60
|
-
this.orders = {};
|
|
61
59
|
/** User positions by account ID */
|
|
62
60
|
this.positions = {};
|
|
63
61
|
/** User margins by account ID */
|
|
@@ -126,7 +124,6 @@ class NordUser {
|
|
|
126
124
|
});
|
|
127
125
|
// Copy other properties
|
|
128
126
|
cloned.balances = { ...this.balances };
|
|
129
|
-
cloned.orders = { ...this.orders };
|
|
130
127
|
cloned.positions = { ...this.positions };
|
|
131
128
|
cloned.margins = { ...this.margins };
|
|
132
129
|
cloned.accountIds = this.accountIds ? [...this.accountIds] : undefined;
|
|
@@ -231,10 +228,24 @@ class NordUser {
|
|
|
231
228
|
*
|
|
232
229
|
* @param amount - Amount to deposit
|
|
233
230
|
* @param tokenId - Token ID
|
|
231
|
+
* @param recipient - Recipient address; defaults to the user's address
|
|
234
232
|
* @returns Transaction signature
|
|
233
|
+
* @deprecated Use deposit instead
|
|
235
234
|
* @throws {NordError} If required parameters are missing or operation fails
|
|
236
235
|
*/
|
|
237
|
-
async depositSpl(amount, tokenId) {
|
|
236
|
+
async depositSpl(amount, tokenId, recipient) {
|
|
237
|
+
return this.deposit({ amount, tokenId, recipient });
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Deposit SPL tokens to the bridge
|
|
241
|
+
*
|
|
242
|
+
* @param amount - Amount to deposit
|
|
243
|
+
* @param tokenId - Token ID
|
|
244
|
+
* @param recipient - Recipient address; defaults to the user's address
|
|
245
|
+
* @returns Transaction signature
|
|
246
|
+
* @throws {NordError} If required parameters are missing or operation fails
|
|
247
|
+
*/
|
|
248
|
+
async deposit({ amount, tokenId, recipient, }) {
|
|
238
249
|
try {
|
|
239
250
|
// Find the token info
|
|
240
251
|
const tokenInfo = this.splTokenInfos.find((t) => t.tokenId === tokenId);
|
|
@@ -242,24 +253,22 @@ class NordUser {
|
|
|
242
253
|
throw new NordError_1.NordError(`Token with ID ${tokenId} not found`);
|
|
243
254
|
}
|
|
244
255
|
const mint = new web3_js_1.PublicKey(tokenInfo.mint);
|
|
245
|
-
// Get the user's token account
|
|
246
256
|
const fromAccount = await this.getAssociatedTokenAccount(mint);
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
257
|
+
const payer = this.getSolanaPublicKey();
|
|
258
|
+
const { ix, extraSigner } = await this.nord.protonClient.buildDepositIx({
|
|
259
|
+
payer,
|
|
260
|
+
recipient: recipient ?? payer,
|
|
261
|
+
quantAmount: (0, proton_1.floatToScaledBigIntLossy)(amount, tokenInfo.precision),
|
|
252
262
|
mint,
|
|
253
|
-
fromAccount,
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const signedTx = await this.transactionSignFn(
|
|
261
|
-
|
|
262
|
-
// this is incredibly annoying to debug and i could've saved 30 mins.
|
|
263
|
+
sourceTokenAccount: fromAccount,
|
|
264
|
+
});
|
|
265
|
+
const { blockhash } = await this.connection.getLatestBlockhash("confirmed");
|
|
266
|
+
const tx = new web3_js_1.Transaction();
|
|
267
|
+
tx.add(ix);
|
|
268
|
+
tx.recentBlockhash = blockhash;
|
|
269
|
+
tx.feePayer = payer;
|
|
270
|
+
const signedTx = await this.transactionSignFn(tx);
|
|
271
|
+
signedTx.partialSign(extraSigner);
|
|
263
272
|
const signature = await this.connection.sendRawTransaction(signedTx.serialize());
|
|
264
273
|
return signature;
|
|
265
274
|
}
|
|
@@ -285,9 +294,12 @@ class NordUser {
|
|
|
285
294
|
if (!this.publicKey) {
|
|
286
295
|
throw new NordError_1.NordError("Public key is required to update account ID");
|
|
287
296
|
}
|
|
288
|
-
const resp = await this.nord.
|
|
297
|
+
const resp = await this.nord.getUser({
|
|
289
298
|
pubkey: this.publicKey.toBase58(),
|
|
290
299
|
});
|
|
300
|
+
if (!resp) {
|
|
301
|
+
throw new NordError_1.NordError(`User ${this.publicKey.toBase58()} not found`);
|
|
302
|
+
}
|
|
291
303
|
this.accountIds = resp.accountIds;
|
|
292
304
|
}
|
|
293
305
|
catch (error) {
|
|
@@ -320,16 +332,6 @@ class NordUser {
|
|
|
320
332
|
symbol: balance.token,
|
|
321
333
|
});
|
|
322
334
|
}
|
|
323
|
-
// Process orders
|
|
324
|
-
this.orders[accountData.accountId] = accountData.orders.map((order) => {
|
|
325
|
-
return {
|
|
326
|
-
orderId: order.orderId,
|
|
327
|
-
isLong: order.side === "bid",
|
|
328
|
-
size: order.size,
|
|
329
|
-
price: order.price,
|
|
330
|
-
marketId: order.marketId,
|
|
331
|
-
};
|
|
332
|
-
});
|
|
333
335
|
// Process positions
|
|
334
336
|
this.positions[accountData.accountId] = accountData.positions;
|
|
335
337
|
// Process margins
|
|
@@ -343,7 +345,6 @@ class NordUser {
|
|
|
343
345
|
* @throws {NordError} If the operation fails
|
|
344
346
|
*/
|
|
345
347
|
async refreshSession() {
|
|
346
|
-
console.log(this.publicKey);
|
|
347
348
|
this.sessionId = await (0, actions_1.createSession)(this.nord.webServerUrl, this.walletSignFn, await this.nord.getTimestamp(), this.getNonce(), {
|
|
348
349
|
userPubkey: (0, utils_1.optExpect)(this.publicKey.toBytes(), "No user's public key"),
|
|
349
350
|
sessionPubkey: this.sessionPubKey,
|
|
@@ -384,7 +385,7 @@ class NordUser {
|
|
|
384
385
|
* @param amount - Amount to withdraw
|
|
385
386
|
* @throws {NordError} If the operation fails
|
|
386
387
|
*/
|
|
387
|
-
async withdraw(tokenId,
|
|
388
|
+
async withdraw({ amount, tokenId, }) {
|
|
388
389
|
try {
|
|
389
390
|
this.checkSessionValidity();
|
|
390
391
|
const { actionId } = await (0, actions_1.withdraw)(this.nord.webServerUrl, this.sessionSignFn, await this.nord.getTimestamp(), this.getNonce(), {
|
|
@@ -478,6 +479,62 @@ class NordUser {
|
|
|
478
479
|
throw new NordError_1.NordError("Failed to transfer tokens", { cause: error });
|
|
479
480
|
}
|
|
480
481
|
}
|
|
482
|
+
/**
|
|
483
|
+
* Execute up to four place/cancel operations atomically.
|
|
484
|
+
* Per Market:
|
|
485
|
+
* 1. cancels can only be in the start (one cannot predict future order ids)
|
|
486
|
+
* 2. intermediate trades can trade only
|
|
487
|
+
* 3. placements go last
|
|
488
|
+
*
|
|
489
|
+
* Across Markets, order action can be any
|
|
490
|
+
*
|
|
491
|
+
* @param userActions array of user-friendly subactions
|
|
492
|
+
* @param providedAccountId optional account performing the action (defaults to first account)
|
|
493
|
+
*/
|
|
494
|
+
async atomic(userActions, providedAccountId) {
|
|
495
|
+
try {
|
|
496
|
+
this.checkSessionValidity();
|
|
497
|
+
const accountId = providedAccountId != null ? providedAccountId : this.accountIds?.[0];
|
|
498
|
+
if (accountId == null) {
|
|
499
|
+
throw new NordError_1.NordError("Account ID is undefined. Make sure to call updateAccountId() before atomic operations.");
|
|
500
|
+
}
|
|
501
|
+
const apiActions = userActions.map((act) => {
|
|
502
|
+
if (act.kind === "place") {
|
|
503
|
+
const market = (0, utils_1.findMarket)(this.nord.markets, act.marketId);
|
|
504
|
+
if (!market) {
|
|
505
|
+
throw new NordError_1.NordError(`Market ${act.marketId} not found`);
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
kind: "place",
|
|
509
|
+
marketId: act.marketId,
|
|
510
|
+
side: act.side,
|
|
511
|
+
fillMode: act.fillMode,
|
|
512
|
+
isReduceOnly: act.isReduceOnly,
|
|
513
|
+
sizeDecimals: market.sizeDecimals,
|
|
514
|
+
priceDecimals: market.priceDecimals,
|
|
515
|
+
size: act.size,
|
|
516
|
+
price: act.price,
|
|
517
|
+
quoteSizeSize: act.quoteSize, // treated as quote size; we pass only size component
|
|
518
|
+
quoteSizePrice: undefined,
|
|
519
|
+
clientOrderId: act.clientOrderId,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
kind: "cancel",
|
|
524
|
+
orderId: act.orderId,
|
|
525
|
+
};
|
|
526
|
+
});
|
|
527
|
+
const result = await (0, actions_1.atomic)(this.nord.webServerUrl, this.sessionSignFn, await this.nord.getTimestamp(), this.getNonce(), {
|
|
528
|
+
sessionId: (0, utils_1.optExpect)(this.sessionId, "No session"),
|
|
529
|
+
accountId: accountId,
|
|
530
|
+
actions: apiActions,
|
|
531
|
+
});
|
|
532
|
+
return result;
|
|
533
|
+
}
|
|
534
|
+
catch (error) {
|
|
535
|
+
throw new NordError_1.NordError("Atomic operation failed", { cause: error });
|
|
536
|
+
}
|
|
537
|
+
}
|
|
481
538
|
/**
|
|
482
539
|
* Helper function to retry a promise with exponential backoff
|
|
483
540
|
*
|
package/dist/nord/index.d.ts
CHANGED
|
@@ -3,7 +3,5 @@ export { NordUser } from "./client/NordUser";
|
|
|
3
3
|
export { NordError } from "./utils/NordError";
|
|
4
4
|
export * from "./api/core";
|
|
5
5
|
export * from "./api/metrics";
|
|
6
|
-
export * from "./api/queries";
|
|
7
|
-
export * from "./api/market";
|
|
8
6
|
export * from "./api/actions";
|
|
9
7
|
export * from "./models/Subscriber";
|
package/dist/nord/index.js
CHANGED
|
@@ -26,8 +26,6 @@ Object.defineProperty(exports, "NordError", { enumerable: true, get: function ()
|
|
|
26
26
|
// Export API modules
|
|
27
27
|
__exportStar(require("./api/core"), exports);
|
|
28
28
|
__exportStar(require("./api/metrics"), exports);
|
|
29
|
-
__exportStar(require("./api/queries"), exports);
|
|
30
|
-
__exportStar(require("./api/market"), exports);
|
|
31
29
|
__exportStar(require("./api/actions"), exports);
|
|
32
30
|
// Export models
|
|
33
31
|
__exportStar(require("./models/Subscriber"), exports);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
|
-
import { Account, DeltaEvent, OrderbookResponse, SubscriberConfig,
|
|
2
|
+
import { Account, DeltaEvent, OrderbookResponse, SubscriberConfig, StreamTrade, Trades } from "../../types";
|
|
3
3
|
/**
|
|
4
4
|
* Subscriber class for handling WebSocket subscriptions
|
|
5
5
|
*/
|
|
@@ -30,7 +30,7 @@ export interface OrderbookSubscription extends EventEmitter {
|
|
|
30
30
|
* Interface for trade subscription
|
|
31
31
|
*/
|
|
32
32
|
export interface TradeSubscription extends EventEmitter {
|
|
33
|
-
on(event: "message", listener: (data:
|
|
33
|
+
on(event: "message", listener: (data: StreamTrade[]) => void): this;
|
|
34
34
|
on(event: "error", listener: (error: Error) => void): this;
|
|
35
35
|
close(): void;
|
|
36
36
|
removeAllListeners(event?: string): this;
|