@n1xyz/nord-ts 0.1.6 → 0.1.7
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/actions.d.ts +57 -0
- package/dist/actions.js +229 -0
- package/dist/client/Nord.d.ts +379 -0
- package/dist/client/Nord.js +718 -0
- package/dist/client/NordAdmin.d.ts +225 -0
- package/dist/client/NordAdmin.js +394 -0
- package/dist/client/NordUser.d.ts +350 -0
- package/dist/client/NordUser.js +743 -0
- package/dist/error.d.ts +35 -0
- package/dist/error.js +49 -0
- package/dist/gen/openapi.d.ts +40 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +29 -1
- package/dist/nord/client/NordAdmin.js +2 -0
- package/dist/types.d.ts +4 -50
- package/dist/types.js +1 -24
- package/dist/utils.d.ts +8 -11
- package/dist/utils.js +54 -41
- package/dist/websocket/Subscriber.d.ts +37 -0
- package/dist/websocket/Subscriber.js +25 -0
- package/dist/websocket/index.d.ts +19 -2
- package/dist/websocket/index.js +82 -2
- package/package.json +1 -1
- package/src/actions.ts +333 -0
- package/src/{nord/client → client}/Nord.ts +207 -210
- package/src/{nord/client → client}/NordAdmin.ts +123 -153
- package/src/{nord/client → client}/NordUser.ts +216 -305
- package/src/gen/openapi.ts +40 -0
- package/src/index.ts +7 -1
- package/src/types.ts +4 -54
- package/src/utils.ts +44 -47
- package/src/{nord/models → websocket}/Subscriber.ts +2 -2
- package/src/websocket/index.ts +105 -2
- package/src/nord/api/actions.ts +0 -648
- package/src/nord/api/core.ts +0 -96
- package/src/nord/api/metrics.ts +0 -269
- package/src/nord/client/NordClient.ts +0 -79
- package/src/nord/index.ts +0 -25
- /package/src/{nord/utils/NordError.ts → error.ts} +0 -0
|
@@ -4,16 +4,9 @@ import {
|
|
|
4
4
|
TOKEN_PROGRAM_ID,
|
|
5
5
|
TOKEN_2022_PROGRAM_ID,
|
|
6
6
|
} from "@solana/spl-token";
|
|
7
|
-
import {
|
|
8
|
-
Connection,
|
|
9
|
-
PublicKey,
|
|
10
|
-
Transaction,
|
|
11
|
-
SendOptions,
|
|
12
|
-
} from "@solana/web3.js";
|
|
7
|
+
import { PublicKey, Transaction, SendOptions } from "@solana/web3.js";
|
|
13
8
|
import Decimal from "decimal.js";
|
|
14
9
|
import * as ed from "@noble/ed25519";
|
|
15
|
-
import { sha512 } from "@noble/hashes/sha512";
|
|
16
|
-
ed.etc.sha512Sync = sha512;
|
|
17
10
|
import { floatToScaledBigIntLossy } from "@n1xyz/proton";
|
|
18
11
|
import {
|
|
19
12
|
FillMode,
|
|
@@ -22,8 +15,8 @@ import {
|
|
|
22
15
|
QuoteSize,
|
|
23
16
|
TriggerKind,
|
|
24
17
|
fillModeToProtoFillMode,
|
|
25
|
-
} from "
|
|
26
|
-
import * as proto from "
|
|
18
|
+
} from "../types";
|
|
19
|
+
import * as proto from "../gen/nord_pb";
|
|
27
20
|
import {
|
|
28
21
|
BigIntValue,
|
|
29
22
|
checkedFetch,
|
|
@@ -33,117 +26,19 @@ import {
|
|
|
33
26
|
optExpect,
|
|
34
27
|
keypairFromPrivateKey,
|
|
35
28
|
toScaledU64,
|
|
36
|
-
} from "
|
|
29
|
+
} from "../utils";
|
|
37
30
|
import { create } from "@bufbuild/protobuf";
|
|
38
31
|
import {
|
|
39
32
|
createSession,
|
|
40
33
|
revokeSession,
|
|
41
34
|
atomic,
|
|
42
35
|
expectReceiptKind,
|
|
36
|
+
createAction,
|
|
37
|
+
sendAction,
|
|
43
38
|
AtomicSubaction,
|
|
44
|
-
} from "../
|
|
45
|
-
import { NordError } from "../
|
|
39
|
+
} from "../actions";
|
|
40
|
+
import { NordError } from "../error";
|
|
46
41
|
import { Nord } from "./Nord";
|
|
47
|
-
import { NordClient } from "./NordClient";
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Parameters for creating a NordUser instance
|
|
51
|
-
*/
|
|
52
|
-
export interface NordUserParams {
|
|
53
|
-
/** Nord client instance */
|
|
54
|
-
nord: Nord;
|
|
55
|
-
|
|
56
|
-
/** User's blockchain address */
|
|
57
|
-
address: string;
|
|
58
|
-
|
|
59
|
-
/** Function to sign messages with the user's wallet */
|
|
60
|
-
walletSignFn: (message: Uint8Array | string) => Promise<Uint8Array>;
|
|
61
|
-
|
|
62
|
-
/** Function to sign messages with the user's session key */
|
|
63
|
-
sessionSignFn: (message: Uint8Array) => Promise<Uint8Array>;
|
|
64
|
-
|
|
65
|
-
/** Function to sign transactions with the user's wallet (optional) */
|
|
66
|
-
transactionSignFn: <T extends Transaction>(tx: T) => Promise<T>;
|
|
67
|
-
|
|
68
|
-
/** Solana connection (optional) */
|
|
69
|
-
connection?: Connection;
|
|
70
|
-
|
|
71
|
-
/** Session ID (optional) */
|
|
72
|
-
sessionId?: bigint;
|
|
73
|
-
|
|
74
|
-
/** Session public key (required) */
|
|
75
|
-
sessionPubKey: Uint8Array;
|
|
76
|
-
|
|
77
|
-
/** Session public key (required) */
|
|
78
|
-
publicKey: PublicKey;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Parameters for placing an order
|
|
83
|
-
*/
|
|
84
|
-
export interface PlaceOrderParams {
|
|
85
|
-
/** Market ID */
|
|
86
|
-
marketId: number;
|
|
87
|
-
|
|
88
|
-
/** Order side (bid or ask) */
|
|
89
|
-
side: Side;
|
|
90
|
-
|
|
91
|
-
/** Fill mode (limit, market, etc.) */
|
|
92
|
-
fillMode: FillMode;
|
|
93
|
-
|
|
94
|
-
/** Whether the order is reduce-only */
|
|
95
|
-
isReduceOnly: boolean;
|
|
96
|
-
|
|
97
|
-
/** Order size */
|
|
98
|
-
size?: Decimal.Value;
|
|
99
|
-
|
|
100
|
-
/** Order price */
|
|
101
|
-
price?: Decimal.Value;
|
|
102
|
-
|
|
103
|
-
/** Quote size object (requires non-zero price and size) */
|
|
104
|
-
quoteSize?: QuoteSize;
|
|
105
|
-
|
|
106
|
-
/** Account ID to place the order from */
|
|
107
|
-
accountId?: number;
|
|
108
|
-
|
|
109
|
-
clientOrderId?: BigIntValue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export interface AddTriggerParams {
|
|
113
|
-
marketId: number;
|
|
114
|
-
side: Side;
|
|
115
|
-
kind: TriggerKind;
|
|
116
|
-
triggerPrice: Decimal.Value;
|
|
117
|
-
limitPrice?: Decimal.Value;
|
|
118
|
-
accountId?: number;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface RemoveTriggerParams {
|
|
122
|
-
marketId: number;
|
|
123
|
-
side: Side;
|
|
124
|
-
kind: TriggerKind;
|
|
125
|
-
accountId?: number;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Parameters for transferring tokens between accounts
|
|
130
|
-
*/
|
|
131
|
-
export interface TransferParams {
|
|
132
|
-
/** Recipient user */
|
|
133
|
-
to: NordUser;
|
|
134
|
-
|
|
135
|
-
/** Token ID to transfer */
|
|
136
|
-
tokenId: number;
|
|
137
|
-
|
|
138
|
-
/** Amount to transfer */
|
|
139
|
-
amount: Decimal.Value;
|
|
140
|
-
|
|
141
|
-
/** Source account ID */
|
|
142
|
-
fromAccountId: number;
|
|
143
|
-
|
|
144
|
-
/** Destination account ID */
|
|
145
|
-
toAccountId: number;
|
|
146
|
-
}
|
|
147
42
|
|
|
148
43
|
/**
|
|
149
44
|
* Parameters for individual atomic subactions (user-friendly version)
|
|
@@ -183,7 +78,17 @@ export interface UserAtomicSubaction {
|
|
|
183
78
|
/**
|
|
184
79
|
* User class for interacting with the Nord protocol
|
|
185
80
|
*/
|
|
186
|
-
export class NordUser
|
|
81
|
+
export class NordUser {
|
|
82
|
+
public readonly nord: Nord;
|
|
83
|
+
public readonly sessionSignFn: (message: Uint8Array) => Promise<Uint8Array>;
|
|
84
|
+
public readonly transactionSignFn: (tx: Transaction) => Promise<Transaction>;
|
|
85
|
+
public sessionId?: bigint;
|
|
86
|
+
public sessionPubKey: PublicKey;
|
|
87
|
+
public publicKey: PublicKey;
|
|
88
|
+
public lastTs = 0;
|
|
89
|
+
|
|
90
|
+
private nonce = 0;
|
|
91
|
+
|
|
187
92
|
/** User balances by token symbol */
|
|
188
93
|
public balances: {
|
|
189
94
|
[key: string]: { accountId: number; balance: number; symbol: string }[];
|
|
@@ -229,48 +134,35 @@ export class NordUser extends NordClient {
|
|
|
229
134
|
/**
|
|
230
135
|
* Create a new NordUser instance
|
|
231
136
|
*
|
|
232
|
-
* @param
|
|
137
|
+
* @param nord - Nord client instance
|
|
138
|
+
* @param sessionSignFn - Function to sign messages with the user's session key
|
|
139
|
+
* @param transactionSignFn - Function to sign transactions with the user's wallet (optional)
|
|
140
|
+
* @param sessionId - Existing session identifier
|
|
141
|
+
* @param sessionPubKey - Session public key
|
|
142
|
+
* @param publicKey - Wallet public key
|
|
233
143
|
* @throws {NordError} If required parameters are missing
|
|
234
144
|
*/
|
|
235
145
|
constructor({
|
|
236
|
-
address,
|
|
237
146
|
nord,
|
|
238
|
-
publicKey,
|
|
239
|
-
sessionPubKey,
|
|
240
147
|
sessionSignFn,
|
|
241
148
|
transactionSignFn,
|
|
242
|
-
walletSignFn,
|
|
243
|
-
connection,
|
|
244
149
|
sessionId,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
super({
|
|
264
|
-
nord,
|
|
265
|
-
address: parsedAddress,
|
|
266
|
-
walletSignFn,
|
|
267
|
-
sessionSignFn,
|
|
268
|
-
transactionSignFn,
|
|
269
|
-
connection,
|
|
270
|
-
sessionId,
|
|
271
|
-
sessionPubKey,
|
|
272
|
-
publicKey,
|
|
273
|
-
});
|
|
150
|
+
sessionPubKey,
|
|
151
|
+
publicKey,
|
|
152
|
+
}: Readonly<{
|
|
153
|
+
nord: Nord;
|
|
154
|
+
sessionSignFn: (message: Uint8Array) => Promise<Uint8Array>;
|
|
155
|
+
transactionSignFn: (tx: Transaction) => Promise<Transaction>;
|
|
156
|
+
sessionId?: bigint;
|
|
157
|
+
sessionPubKey: Uint8Array;
|
|
158
|
+
publicKey: PublicKey;
|
|
159
|
+
}>) {
|
|
160
|
+
this.nord = nord;
|
|
161
|
+
this.sessionSignFn = sessionSignFn;
|
|
162
|
+
this.transactionSignFn = transactionSignFn;
|
|
163
|
+
this.sessionId = sessionId;
|
|
164
|
+
this.sessionPubKey = new PublicKey(sessionPubKey);
|
|
165
|
+
this.publicKey = publicKey;
|
|
274
166
|
|
|
275
167
|
// Convert tokens from info endpoint to SPLTokenInfo
|
|
276
168
|
if (this.nord.tokens && this.nord.tokens.length > 0) {
|
|
@@ -283,74 +175,19 @@ export class NordUser extends NordClient {
|
|
|
283
175
|
}
|
|
284
176
|
}
|
|
285
177
|
|
|
286
|
-
/**
|
|
287
|
-
* Create a clone of this NordUser instance
|
|
288
|
-
*
|
|
289
|
-
* @returns A new NordUser instance with the same properties
|
|
290
|
-
*/
|
|
291
|
-
clone(): NordUser {
|
|
292
|
-
const cloned = new NordUser({
|
|
293
|
-
nord: this.nord,
|
|
294
|
-
address: this.address.toBase58(),
|
|
295
|
-
walletSignFn: this.walletSignFn,
|
|
296
|
-
sessionSignFn: this.sessionSignFn,
|
|
297
|
-
transactionSignFn: this.transactionSignFn,
|
|
298
|
-
connection: this.connection,
|
|
299
|
-
sessionPubKey: new Uint8Array(this.sessionPubKey),
|
|
300
|
-
publicKey: this.publicKey,
|
|
301
|
-
sessionId: this.sessionId,
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
// Copy other properties
|
|
305
|
-
cloned.balances = { ...this.balances };
|
|
306
|
-
cloned.positions = { ...this.positions };
|
|
307
|
-
cloned.margins = { ...this.margins };
|
|
308
|
-
cloned.accountIds = this.accountIds ? [...this.accountIds] : undefined;
|
|
309
|
-
cloned.splTokenInfos = [...this.splTokenInfos];
|
|
310
|
-
this.cloneClientState(cloned);
|
|
311
|
-
|
|
312
|
-
return cloned;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
178
|
/**
|
|
316
179
|
* Create a NordUser from a private key
|
|
317
180
|
*
|
|
318
181
|
* @param nord - Nord instance
|
|
319
182
|
* @param privateKey - Private key as string or Uint8Array
|
|
320
|
-
* @param connection - Solana connection (optional)
|
|
321
183
|
* @returns NordUser instance
|
|
322
184
|
* @throws {NordError} If the private key is invalid
|
|
323
185
|
*/
|
|
324
|
-
static fromPrivateKey(
|
|
325
|
-
nord: Nord,
|
|
326
|
-
privateKey: string | Uint8Array,
|
|
327
|
-
connection?: Connection,
|
|
328
|
-
): NordUser {
|
|
186
|
+
static fromPrivateKey(nord: Nord, privateKey: string | Uint8Array): NordUser {
|
|
329
187
|
try {
|
|
330
188
|
const keypair = keypairFromPrivateKey(privateKey);
|
|
331
189
|
const publicKey = keypair.publicKey;
|
|
332
190
|
|
|
333
|
-
// Create a signing function that uses the keypair but doesn't expose it
|
|
334
|
-
const walletSignFn = async (
|
|
335
|
-
message: Uint8Array | string,
|
|
336
|
-
): Promise<Uint8Array> => {
|
|
337
|
-
function toHex(buffer: Uint8Array) {
|
|
338
|
-
return Array.from(buffer)
|
|
339
|
-
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
340
|
-
.join("");
|
|
341
|
-
}
|
|
342
|
-
const messageBuffer = new TextEncoder().encode(
|
|
343
|
-
toHex(message as Uint8Array),
|
|
344
|
-
);
|
|
345
|
-
|
|
346
|
-
// Use ed25519 to sign the message
|
|
347
|
-
const signature = ed.sign(
|
|
348
|
-
messageBuffer,
|
|
349
|
-
keypair.secretKey.slice(0, 32),
|
|
350
|
-
);
|
|
351
|
-
return signature;
|
|
352
|
-
};
|
|
353
|
-
|
|
354
191
|
const sessionSignFn = async (
|
|
355
192
|
message: Uint8Array,
|
|
356
193
|
): Promise<Uint8Array> => {
|
|
@@ -358,28 +195,19 @@ export class NordUser extends NordClient {
|
|
|
358
195
|
return ed.sign(message, keypair.secretKey.slice(0, 32));
|
|
359
196
|
};
|
|
360
197
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
transaction.sign(keypair);
|
|
367
|
-
return transaction;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// For other transaction types, would need specific implementation
|
|
371
|
-
throw new NordError("Unsupported transaction type for signing");
|
|
198
|
+
const transactionSignFn = async (
|
|
199
|
+
tx: Transaction,
|
|
200
|
+
): Promise<Transaction> => {
|
|
201
|
+
tx.sign(keypair);
|
|
202
|
+
return tx;
|
|
372
203
|
};
|
|
373
204
|
|
|
374
205
|
return new NordUser({
|
|
375
206
|
nord,
|
|
376
|
-
address: publicKey.toBase58(),
|
|
377
|
-
walletSignFn,
|
|
378
207
|
sessionSignFn,
|
|
379
208
|
transactionSignFn,
|
|
380
|
-
connection,
|
|
381
209
|
publicKey,
|
|
382
|
-
sessionPubKey: publicKey.toBytes(),
|
|
210
|
+
sessionPubKey: publicKey.toBytes(),
|
|
383
211
|
});
|
|
384
212
|
} catch (error) {
|
|
385
213
|
throw new NordError("Failed to create NordUser from private key", {
|
|
@@ -396,15 +224,9 @@ export class NordUser extends NordClient {
|
|
|
396
224
|
* @throws {NordError} If required parameters are missing or operation fails
|
|
397
225
|
*/
|
|
398
226
|
async getAssociatedTokenAccount(mint: PublicKey): Promise<PublicKey> {
|
|
399
|
-
if (!this.getSolanaPublicKey()) {
|
|
400
|
-
throw new NordError(
|
|
401
|
-
"Solana public key is required to get associated token account",
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
227
|
try {
|
|
406
228
|
// Get the token program ID from the mint account
|
|
407
|
-
const mintAccount = await this.
|
|
229
|
+
const mintAccount = await this.nord.solanaConnection.getAccountInfo(mint);
|
|
408
230
|
if (!mintAccount) {
|
|
409
231
|
throw new NordError("Mint account not found");
|
|
410
232
|
}
|
|
@@ -422,7 +244,7 @@ export class NordUser extends NordClient {
|
|
|
422
244
|
|
|
423
245
|
const associatedTokenAddress = await getAssociatedTokenAddress(
|
|
424
246
|
mint,
|
|
425
|
-
this.
|
|
247
|
+
this.publicKey,
|
|
426
248
|
false,
|
|
427
249
|
tokenProgramId,
|
|
428
250
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
@@ -483,7 +305,7 @@ export class NordUser extends NordClient {
|
|
|
483
305
|
|
|
484
306
|
const mint = new PublicKey(tokenInfo.mint);
|
|
485
307
|
const fromAccount = await this.getAssociatedTokenAccount(mint);
|
|
486
|
-
const payer = this.
|
|
308
|
+
const payer = this.publicKey;
|
|
487
309
|
|
|
488
310
|
const { ix, extraSigner } = await this.nord.protonClient.buildDepositIx({
|
|
489
311
|
payer,
|
|
@@ -493,7 +315,8 @@ export class NordUser extends NordClient {
|
|
|
493
315
|
sourceTokenAccount: fromAccount,
|
|
494
316
|
});
|
|
495
317
|
|
|
496
|
-
const { blockhash } =
|
|
318
|
+
const { blockhash } =
|
|
319
|
+
await this.nord.solanaConnection.getLatestBlockhash();
|
|
497
320
|
const tx = new Transaction();
|
|
498
321
|
|
|
499
322
|
tx.add(ix);
|
|
@@ -503,7 +326,7 @@ export class NordUser extends NordClient {
|
|
|
503
326
|
const signedTx = await this.transactionSignFn(tx);
|
|
504
327
|
signedTx.partialSign(extraSigner);
|
|
505
328
|
|
|
506
|
-
const signature = await this.
|
|
329
|
+
const signature = await this.nord.solanaConnection.sendRawTransaction(
|
|
507
330
|
signedTx.serialize(),
|
|
508
331
|
sendOptions,
|
|
509
332
|
);
|
|
@@ -523,7 +346,7 @@ export class NordUser extends NordClient {
|
|
|
523
346
|
* @returns Nonce as number
|
|
524
347
|
*/
|
|
525
348
|
getNonce(): number {
|
|
526
|
-
return this.
|
|
349
|
+
return ++this.nonce;
|
|
527
350
|
}
|
|
528
351
|
|
|
529
352
|
private async submitSessionAction(
|
|
@@ -661,12 +484,12 @@ export class NordUser extends NordClient {
|
|
|
661
484
|
*/
|
|
662
485
|
async refreshSession(): Promise<void> {
|
|
663
486
|
const result = await createSession(
|
|
664
|
-
this.nord.
|
|
665
|
-
this.
|
|
487
|
+
this.nord.httpClient,
|
|
488
|
+
this.transactionSignFn,
|
|
666
489
|
await this.nord.getTimestamp(),
|
|
667
490
|
this.getNonce(),
|
|
668
491
|
{
|
|
669
|
-
userPubkey:
|
|
492
|
+
userPubkey: this.publicKey,
|
|
670
493
|
sessionPubkey: this.sessionPubKey,
|
|
671
494
|
},
|
|
672
495
|
);
|
|
@@ -681,11 +504,12 @@ export class NordUser extends NordClient {
|
|
|
681
504
|
async revokeSession(sessionId: BigIntValue): Promise<void> {
|
|
682
505
|
try {
|
|
683
506
|
await revokeSession(
|
|
684
|
-
this.nord.
|
|
685
|
-
this.
|
|
507
|
+
this.nord.httpClient,
|
|
508
|
+
this.transactionSignFn,
|
|
686
509
|
await this.nord.getTimestamp(),
|
|
687
510
|
this.getNonce(),
|
|
688
511
|
{
|
|
512
|
+
userPubkey: this.publicKey,
|
|
689
513
|
sessionId,
|
|
690
514
|
},
|
|
691
515
|
);
|
|
@@ -751,29 +575,57 @@ export class NordUser extends NordClient {
|
|
|
751
575
|
/**
|
|
752
576
|
* Place an order on the exchange
|
|
753
577
|
*
|
|
754
|
-
* @param
|
|
578
|
+
* @param marketId - Target market identifier
|
|
579
|
+
* @param side - Order side
|
|
580
|
+
* @param fillMode - Fill mode (limit, market, etc.)
|
|
581
|
+
* @param isReduceOnly - Reduce-only flag
|
|
582
|
+
* @param size - Base size to place
|
|
583
|
+
* @param price - Limit price
|
|
584
|
+
* @param quoteSize - Quote-sized order representation
|
|
585
|
+
* @param accountId - Account executing the order
|
|
586
|
+
* @param clientOrderId - Optional client-specified identifier
|
|
755
587
|
* @returns Object containing actionId, orderId (if posted), fills, and clientOrderId
|
|
756
588
|
* @throws {NordError} If the operation fails
|
|
757
589
|
*/
|
|
758
|
-
async placeOrder(
|
|
590
|
+
async placeOrder({
|
|
591
|
+
marketId,
|
|
592
|
+
side,
|
|
593
|
+
fillMode,
|
|
594
|
+
isReduceOnly,
|
|
595
|
+
size,
|
|
596
|
+
price,
|
|
597
|
+
quoteSize,
|
|
598
|
+
accountId,
|
|
599
|
+
clientOrderId,
|
|
600
|
+
}: Readonly<{
|
|
601
|
+
marketId: number;
|
|
602
|
+
side: Side;
|
|
603
|
+
fillMode: FillMode;
|
|
604
|
+
isReduceOnly: boolean;
|
|
605
|
+
size?: Decimal.Value;
|
|
606
|
+
price?: Decimal.Value;
|
|
607
|
+
quoteSize?: QuoteSize;
|
|
608
|
+
accountId?: number;
|
|
609
|
+
clientOrderId?: BigIntValue;
|
|
610
|
+
}>): Promise<{
|
|
759
611
|
actionId: bigint;
|
|
760
612
|
orderId?: bigint;
|
|
761
613
|
fills: proto.Receipt_Trade[];
|
|
762
614
|
}> {
|
|
763
615
|
try {
|
|
764
616
|
this.checkSessionValidity();
|
|
765
|
-
const market = findMarket(this.nord.markets,
|
|
617
|
+
const market = findMarket(this.nord.markets, marketId);
|
|
766
618
|
if (!market) {
|
|
767
|
-
throw new NordError(`Market with ID ${
|
|
619
|
+
throw new NordError(`Market with ID ${marketId} not found`);
|
|
768
620
|
}
|
|
769
621
|
const sessionId = optExpect(this.sessionId, "No session");
|
|
770
|
-
const
|
|
771
|
-
const
|
|
772
|
-
const scaledQuote =
|
|
773
|
-
?
|
|
622
|
+
const scaledPrice = toScaledU64(price ?? 0, market.priceDecimals);
|
|
623
|
+
const scaledSize = toScaledU64(size ?? 0, market.sizeDecimals);
|
|
624
|
+
const scaledQuote = quoteSize
|
|
625
|
+
? quoteSize.toWire(market.priceDecimals, market.sizeDecimals)
|
|
774
626
|
: undefined;
|
|
775
627
|
assert(
|
|
776
|
-
|
|
628
|
+
scaledPrice > 0n || scaledSize > 0n || scaledQuote !== undefined,
|
|
777
629
|
"OrderLimit must include at least one of: size, price, or quoteSize",
|
|
778
630
|
);
|
|
779
631
|
|
|
@@ -781,13 +633,13 @@ export class NordUser extends NordClient {
|
|
|
781
633
|
case: "placeOrder",
|
|
782
634
|
value: create(proto.Action_PlaceOrderSchema, {
|
|
783
635
|
sessionId: BigInt(sessionId),
|
|
784
|
-
senderAccountId:
|
|
785
|
-
marketId
|
|
786
|
-
side:
|
|
787
|
-
fillMode: fillModeToProtoFillMode(
|
|
788
|
-
isReduceOnly
|
|
789
|
-
price,
|
|
790
|
-
size,
|
|
636
|
+
senderAccountId: accountId,
|
|
637
|
+
marketId,
|
|
638
|
+
side: side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
639
|
+
fillMode: fillModeToProtoFillMode(fillMode),
|
|
640
|
+
isReduceOnly,
|
|
641
|
+
price: scaledPrice,
|
|
642
|
+
size: scaledSize,
|
|
791
643
|
quoteSize:
|
|
792
644
|
scaledQuote === undefined
|
|
793
645
|
? undefined
|
|
@@ -796,9 +648,7 @@ export class NordUser extends NordClient {
|
|
|
796
648
|
price: scaledQuote.price,
|
|
797
649
|
}),
|
|
798
650
|
clientOrderId:
|
|
799
|
-
|
|
800
|
-
? undefined
|
|
801
|
-
: BigInt(params.clientOrderId),
|
|
651
|
+
clientOrderId === undefined ? undefined : BigInt(clientOrderId),
|
|
802
652
|
}),
|
|
803
653
|
});
|
|
804
654
|
expectReceiptKind(receipt, "placeOrderResult", "place order");
|
|
@@ -857,48 +707,67 @@ export class NordUser extends NordClient {
|
|
|
857
707
|
/**
|
|
858
708
|
* Add a trigger for the current session
|
|
859
709
|
*
|
|
860
|
-
* @param
|
|
710
|
+
* @param marketId - Market to watch
|
|
711
|
+
* @param side - Order side for the trigger
|
|
712
|
+
* @param kind - Stop-loss or take-profit trigger type
|
|
713
|
+
* @param triggerPrice - Price that activates the trigger
|
|
714
|
+
* @param limitPrice - Limit price placed once the trigger fires
|
|
715
|
+
* @param accountId - Account executing the trigger
|
|
861
716
|
* @returns Object containing the actionId of the submitted trigger
|
|
862
717
|
* @throws {NordError} If the operation fails
|
|
863
718
|
*/
|
|
864
|
-
async addTrigger(
|
|
719
|
+
async addTrigger({
|
|
720
|
+
marketId,
|
|
721
|
+
side,
|
|
722
|
+
kind,
|
|
723
|
+
triggerPrice,
|
|
724
|
+
limitPrice,
|
|
725
|
+
accountId,
|
|
726
|
+
}: Readonly<{
|
|
727
|
+
marketId: number;
|
|
728
|
+
side: Side;
|
|
729
|
+
kind: TriggerKind;
|
|
730
|
+
triggerPrice: Decimal.Value;
|
|
731
|
+
limitPrice?: Decimal.Value;
|
|
732
|
+
accountId?: number;
|
|
733
|
+
}>): Promise<{ actionId: bigint }> {
|
|
865
734
|
try {
|
|
866
735
|
this.checkSessionValidity();
|
|
867
|
-
const market = findMarket(this.nord.markets,
|
|
736
|
+
const market = findMarket(this.nord.markets, marketId);
|
|
868
737
|
if (!market) {
|
|
869
|
-
throw new NordError(`Market with ID ${
|
|
738
|
+
throw new NordError(`Market with ID ${marketId} not found`);
|
|
870
739
|
}
|
|
871
|
-
const
|
|
872
|
-
|
|
740
|
+
const scaledTriggerPrice = toScaledU64(
|
|
741
|
+
triggerPrice,
|
|
873
742
|
market.priceDecimals,
|
|
874
743
|
);
|
|
875
|
-
assert(
|
|
876
|
-
const
|
|
877
|
-
|
|
744
|
+
assert(scaledTriggerPrice > 0n, "Trigger price must be positive");
|
|
745
|
+
const scaledLimitPrice =
|
|
746
|
+
limitPrice === undefined
|
|
878
747
|
? undefined
|
|
879
|
-
: toScaledU64(
|
|
880
|
-
if (
|
|
881
|
-
assert(
|
|
748
|
+
: toScaledU64(limitPrice, market.priceDecimals);
|
|
749
|
+
if (scaledLimitPrice !== undefined) {
|
|
750
|
+
assert(scaledLimitPrice > 0n, "Limit price must be positive");
|
|
882
751
|
}
|
|
883
752
|
const key = create(proto.TriggerKeySchema, {
|
|
884
753
|
kind:
|
|
885
|
-
|
|
754
|
+
kind === TriggerKind.StopLoss
|
|
886
755
|
? proto.TriggerKind.STOP_LOSS
|
|
887
756
|
: proto.TriggerKind.TAKE_PROFIT,
|
|
888
|
-
side:
|
|
757
|
+
side: side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
889
758
|
});
|
|
890
759
|
const prices = create(proto.Action_TriggerPricesSchema, {
|
|
891
|
-
triggerPrice,
|
|
892
|
-
limitPrice,
|
|
760
|
+
triggerPrice: scaledTriggerPrice,
|
|
761
|
+
limitPrice: scaledLimitPrice,
|
|
893
762
|
});
|
|
894
763
|
const receipt = await this.submitSessionAction({
|
|
895
764
|
case: "addTrigger",
|
|
896
765
|
value: create(proto.Action_AddTriggerSchema, {
|
|
897
766
|
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
898
|
-
marketId
|
|
767
|
+
marketId,
|
|
899
768
|
key,
|
|
900
769
|
prices,
|
|
901
|
-
accountId
|
|
770
|
+
accountId,
|
|
902
771
|
}),
|
|
903
772
|
});
|
|
904
773
|
expectReceiptKind(receipt, "triggerAdded", "add trigger");
|
|
@@ -911,33 +780,44 @@ export class NordUser extends NordClient {
|
|
|
911
780
|
/**
|
|
912
781
|
* Remove a trigger for the current session
|
|
913
782
|
*
|
|
914
|
-
* @param
|
|
783
|
+
* @param marketId - Market the trigger belongs to
|
|
784
|
+
* @param side - Order side for the trigger
|
|
785
|
+
* @param kind - Stop-loss or take-profit trigger type
|
|
786
|
+
* @param accountId - Account executing the trigger
|
|
915
787
|
* @returns Object containing the actionId of the removal action
|
|
916
788
|
* @throws {NordError} If the operation fails
|
|
917
789
|
*/
|
|
918
|
-
async removeTrigger(
|
|
919
|
-
|
|
920
|
-
|
|
790
|
+
async removeTrigger({
|
|
791
|
+
marketId,
|
|
792
|
+
side,
|
|
793
|
+
kind,
|
|
794
|
+
accountId,
|
|
795
|
+
}: Readonly<{
|
|
796
|
+
marketId: number;
|
|
797
|
+
side: Side;
|
|
798
|
+
kind: TriggerKind;
|
|
799
|
+
accountId?: number;
|
|
800
|
+
}>): Promise<{ actionId: bigint }> {
|
|
921
801
|
try {
|
|
922
802
|
this.checkSessionValidity();
|
|
923
|
-
const market = findMarket(this.nord.markets,
|
|
803
|
+
const market = findMarket(this.nord.markets, marketId);
|
|
924
804
|
if (!market) {
|
|
925
|
-
throw new NordError(`Market with ID ${
|
|
805
|
+
throw new NordError(`Market with ID ${marketId} not found`);
|
|
926
806
|
}
|
|
927
807
|
const key = create(proto.TriggerKeySchema, {
|
|
928
808
|
kind:
|
|
929
|
-
|
|
809
|
+
kind === TriggerKind.StopLoss
|
|
930
810
|
? proto.TriggerKind.STOP_LOSS
|
|
931
811
|
: proto.TriggerKind.TAKE_PROFIT,
|
|
932
|
-
side:
|
|
812
|
+
side: side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
933
813
|
});
|
|
934
814
|
const receipt = await this.submitSessionAction({
|
|
935
815
|
case: "removeTrigger",
|
|
936
816
|
value: create(proto.Action_RemoveTriggerSchema, {
|
|
937
817
|
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
938
|
-
marketId
|
|
818
|
+
marketId,
|
|
939
819
|
key,
|
|
940
|
-
accountId
|
|
820
|
+
accountId,
|
|
941
821
|
}),
|
|
942
822
|
});
|
|
943
823
|
expectReceiptKind(receipt, "triggerRemoved", "remove trigger");
|
|
@@ -950,16 +830,32 @@ export class NordUser extends NordClient {
|
|
|
950
830
|
/**
|
|
951
831
|
* Transfer tokens to another account
|
|
952
832
|
*
|
|
953
|
-
* @param
|
|
833
|
+
* @param tokenId - Token identifier to move
|
|
834
|
+
* @param amount - Amount to transfer
|
|
835
|
+
* @param fromAccountId - Source account id
|
|
836
|
+
* @param toAccountId - Destination account id
|
|
954
837
|
* @throws {NordError} If the operation fails
|
|
955
838
|
*/
|
|
956
|
-
async transferToAccount(
|
|
839
|
+
async transferToAccount({
|
|
840
|
+
tokenId,
|
|
841
|
+
amount,
|
|
842
|
+
fromAccountId,
|
|
843
|
+
toAccountId,
|
|
844
|
+
}: Readonly<{
|
|
845
|
+
tokenId: number;
|
|
846
|
+
amount: Decimal.Value;
|
|
847
|
+
fromAccountId?: number;
|
|
848
|
+
toAccountId?: number;
|
|
849
|
+
}>): Promise<{
|
|
850
|
+
actionId: bigint;
|
|
851
|
+
newAccountId?: number;
|
|
852
|
+
}> {
|
|
957
853
|
try {
|
|
958
854
|
this.checkSessionValidity();
|
|
959
|
-
const token = findToken(this.nord.tokens,
|
|
855
|
+
const token = findToken(this.nord.tokens, tokenId);
|
|
960
856
|
|
|
961
|
-
const
|
|
962
|
-
if (
|
|
857
|
+
const scaledAmount = toScaledU64(amount, token.decimals);
|
|
858
|
+
if (scaledAmount <= 0n) {
|
|
963
859
|
throw new NordError("Transfer amount must be positive");
|
|
964
860
|
}
|
|
965
861
|
|
|
@@ -967,13 +863,25 @@ export class NordUser extends NordClient {
|
|
|
967
863
|
case: "transfer",
|
|
968
864
|
value: create(proto.Action_TransferSchema, {
|
|
969
865
|
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
970
|
-
fromAccountId: optExpect(
|
|
971
|
-
toAccountId: optExpect(
|
|
972
|
-
tokenId
|
|
973
|
-
amount,
|
|
866
|
+
fromAccountId: optExpect(fromAccountId, "No source account"),
|
|
867
|
+
toAccountId: optExpect(toAccountId, "No target account"),
|
|
868
|
+
tokenId,
|
|
869
|
+
amount: scaledAmount,
|
|
974
870
|
}),
|
|
975
871
|
});
|
|
976
872
|
expectReceiptKind(receipt, "transferred", "transfer tokens");
|
|
873
|
+
if (receipt.kind.value.accountCreated) {
|
|
874
|
+
assert(
|
|
875
|
+
receipt.kind.value.toUserAccount !== undefined,
|
|
876
|
+
`toAccount must be defined on new account on ${receipt.kind.value}`,
|
|
877
|
+
);
|
|
878
|
+
return {
|
|
879
|
+
actionId: receipt.actionId,
|
|
880
|
+
newAccountId: receipt.kind.value.toUserAccount,
|
|
881
|
+
};
|
|
882
|
+
} else {
|
|
883
|
+
return { actionId: receipt.actionId };
|
|
884
|
+
}
|
|
977
885
|
} catch (error) {
|
|
978
886
|
throw new NordError("Failed to transfer tokens", { cause: error });
|
|
979
887
|
}
|
|
@@ -1037,7 +945,7 @@ export class NordUser extends NordClient {
|
|
|
1037
945
|
});
|
|
1038
946
|
|
|
1039
947
|
const result = await atomic(
|
|
1040
|
-
this.nord.
|
|
948
|
+
this.nord.httpClient,
|
|
1041
949
|
this.sessionSignFn,
|
|
1042
950
|
await this.nord.getTimestamp(),
|
|
1043
951
|
this.getNonce(),
|
|
@@ -1124,24 +1032,18 @@ export class NordUser extends NordClient {
|
|
|
1124
1032
|
maxRetries = 3,
|
|
1125
1033
|
} = options;
|
|
1126
1034
|
|
|
1127
|
-
if (!this.connection || !this.getSolanaPublicKey()) {
|
|
1128
|
-
throw new NordError(
|
|
1129
|
-
"Connection and Solana public key are required to get Solana balances",
|
|
1130
|
-
);
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
1035
|
const balances: { [symbol: string]: number } = {};
|
|
1134
1036
|
const tokenAccounts: { [symbol: string]: string } = {};
|
|
1135
1037
|
|
|
1136
1038
|
try {
|
|
1137
1039
|
// Get SOL balance (native token)
|
|
1138
1040
|
const solBalance = await this.retryWithBackoff(
|
|
1139
|
-
() => this.
|
|
1041
|
+
() => this.nord.solanaConnection.getBalance(this.publicKey),
|
|
1140
1042
|
maxRetries,
|
|
1141
1043
|
);
|
|
1142
1044
|
balances["SOL"] = solBalance / 1e9; // Convert lamports to SOL
|
|
1143
1045
|
if (includeTokenAccounts) {
|
|
1144
|
-
tokenAccounts["SOL"] = this.
|
|
1046
|
+
tokenAccounts["SOL"] = this.publicKey.toString();
|
|
1145
1047
|
}
|
|
1146
1048
|
|
|
1147
1049
|
// Get SPL token balances using mintAddr from Nord tokens
|
|
@@ -1157,8 +1059,7 @@ export class NordUser extends NordClient {
|
|
|
1157
1059
|
try {
|
|
1158
1060
|
const mint = new PublicKey(token.mintAddr);
|
|
1159
1061
|
const associatedTokenAddress = await this.retryWithBackoff(
|
|
1160
|
-
() =>
|
|
1161
|
-
getAssociatedTokenAddress(mint, this.getSolanaPublicKey()),
|
|
1062
|
+
() => getAssociatedTokenAddress(mint, this.publicKey),
|
|
1162
1063
|
maxRetries,
|
|
1163
1064
|
);
|
|
1164
1065
|
|
|
@@ -1169,7 +1070,7 @@ export class NordUser extends NordClient {
|
|
|
1169
1070
|
try {
|
|
1170
1071
|
const tokenBalance = await this.retryWithBackoff(
|
|
1171
1072
|
() =>
|
|
1172
|
-
this.
|
|
1073
|
+
this.nord.solanaConnection.getTokenAccountBalance(
|
|
1173
1074
|
associatedTokenAddress,
|
|
1174
1075
|
),
|
|
1175
1076
|
maxRetries,
|
|
@@ -1208,4 +1109,14 @@ export class NordUser extends NordClient {
|
|
|
1208
1109
|
});
|
|
1209
1110
|
}
|
|
1210
1111
|
}
|
|
1112
|
+
|
|
1113
|
+
protected async submitSignedAction(
|
|
1114
|
+
kind: proto.Action["kind"],
|
|
1115
|
+
makeSignedMessage: (message: Uint8Array) => Promise<Uint8Array>,
|
|
1116
|
+
): Promise<proto.Receipt> {
|
|
1117
|
+
const nonce = this.getNonce();
|
|
1118
|
+
const currentTimestamp = await this.nord.getTimestamp();
|
|
1119
|
+
const action = createAction(currentTimestamp, nonce, kind);
|
|
1120
|
+
return sendAction(this.nord.httpClient, makeSignedMessage, action);
|
|
1121
|
+
}
|
|
1211
1122
|
}
|