@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.
Files changed (39) hide show
  1. package/dist/actions.d.ts +57 -0
  2. package/dist/actions.js +229 -0
  3. package/dist/client/Nord.d.ts +379 -0
  4. package/dist/client/Nord.js +718 -0
  5. package/dist/client/NordAdmin.d.ts +225 -0
  6. package/dist/client/NordAdmin.js +394 -0
  7. package/dist/client/NordUser.d.ts +350 -0
  8. package/dist/client/NordUser.js +743 -0
  9. package/dist/error.d.ts +35 -0
  10. package/dist/error.js +49 -0
  11. package/dist/gen/openapi.d.ts +40 -0
  12. package/dist/index.d.ts +6 -1
  13. package/dist/index.js +29 -1
  14. package/dist/nord/client/NordAdmin.js +2 -0
  15. package/dist/types.d.ts +4 -50
  16. package/dist/types.js +1 -24
  17. package/dist/utils.d.ts +8 -11
  18. package/dist/utils.js +54 -41
  19. package/dist/websocket/Subscriber.d.ts +37 -0
  20. package/dist/websocket/Subscriber.js +25 -0
  21. package/dist/websocket/index.d.ts +19 -2
  22. package/dist/websocket/index.js +82 -2
  23. package/package.json +1 -1
  24. package/src/actions.ts +333 -0
  25. package/src/{nord/client → client}/Nord.ts +207 -210
  26. package/src/{nord/client → client}/NordAdmin.ts +123 -153
  27. package/src/{nord/client → client}/NordUser.ts +216 -305
  28. package/src/gen/openapi.ts +40 -0
  29. package/src/index.ts +7 -1
  30. package/src/types.ts +4 -54
  31. package/src/utils.ts +44 -47
  32. package/src/{nord/models → websocket}/Subscriber.ts +2 -2
  33. package/src/websocket/index.ts +105 -2
  34. package/src/nord/api/actions.ts +0 -648
  35. package/src/nord/api/core.ts +0 -96
  36. package/src/nord/api/metrics.ts +0 -269
  37. package/src/nord/client/NordClient.ts +0 -79
  38. package/src/nord/index.ts +0 -25
  39. /package/src/{nord/utils/NordError.ts → error.ts} +0 -0
@@ -1,11 +1,11 @@
1
1
  import { create } from "@bufbuild/protobuf";
2
- import { PublicKey } from "@solana/web3.js";
3
- import * as proto from "../../gen/nord_pb";
4
- import { decodeHex } from "../../utils";
5
- import { createAction, sendAction, expectReceiptKind } from "../api/actions";
6
- import { NordError } from "../utils/NordError";
2
+ import { PublicKey, Transaction } from "@solana/web3.js";
3
+ import * as proto from "../gen/nord_pb";
4
+ import { decodeHex, signUserPayload } from "../utils";
5
+ import { createAction, sendAction, expectReceiptKind } from "../actions";
6
+ import { NordError } from "../error";
7
7
  import { Nord } from "./Nord";
8
- import { FeeTierConfig } from "../../gen/nord_pb";
8
+ import { FeeTierConfig } from "../gen/nord_pb";
9
9
 
10
10
  // NOTE: keep in sync with `acl.rs`.
11
11
  // NOTE: don't forget `number` as int is internally a u32.
@@ -16,84 +16,13 @@ export enum AclRole {
16
16
  ADMIN = 1 << 31,
17
17
  }
18
18
 
19
- /**
20
- * Parameters required to register a new token via the admin API.
21
- */
22
- export interface CreateTokenParams {
23
- tokenDecimals: number;
24
- weightBps: number;
25
- viewSymbol: string;
26
- oracleSymbol: string;
27
- mintAddr: PublicKey;
28
- }
29
-
30
- /**
31
- * Parameters used when creating a new market.
32
- */
33
- export interface CreateMarketParams {
34
- sizeDecimals: number;
35
- priceDecimals: number;
36
- imfBps: number;
37
- cmfBps: number;
38
- mmfBps: number;
39
- marketType: proto.MarketType;
40
- viewSymbol: string;
41
- oracleSymbol: string;
42
- baseTokenId: number;
43
- }
44
-
45
- /**
46
- * Configuration for updating the Wormhole guardian set on the oracle.
47
- */
48
- export interface PythSetWormholeGuardiansParams {
49
- guardianSetIndex: number;
50
- addresses: string[];
51
- }
52
-
53
- /**
54
- * Parameters required to link an oracle symbol to a Pyth price feed.
55
- */
56
- export interface PythSetSymbolFeedParams {
57
- oracleSymbol: string;
58
- priceFeedId: string;
59
- }
60
-
61
- /**
62
- * Identifies a market that should be frozen.
63
- */
64
- export interface FreezeMarketParams {
65
- marketId: number;
66
- }
67
-
68
- /**
69
- * Identifies a market that should be unfrozen.
70
- */
71
- export interface UnfreezeMarketParams {
72
- marketId: number;
73
- }
74
-
75
- /**
76
- * Parameters for adding a new fee tier.
77
- */
78
- export interface AddFeeTierParams {
79
- config: FeeTierConfig;
80
- }
81
-
82
- /**
83
- * Parameters for updating an existing fee tier.
84
- */
85
- export interface UpdateFeeTierParams {
86
- tierId: number;
87
- config: FeeTierConfig;
88
- }
89
-
90
19
  /**
91
20
  * Administrative client capable of submitting privileged configuration actions.
92
21
  */
93
22
  export class NordAdmin {
94
23
  private readonly nord: Nord;
95
24
  private readonly admin: PublicKey;
96
- private readonly signFn: (x: Uint8Array) => Promise<Uint8Array>;
25
+ private readonly signFn: (_: Transaction) => Promise<Transaction>;
97
26
 
98
27
  private constructor({
99
28
  nord,
@@ -102,7 +31,7 @@ export class NordAdmin {
102
31
  }: {
103
32
  nord: Nord;
104
33
  admin: PublicKey;
105
- signFn: (x: Uint8Array) => Promise<Uint8Array>;
34
+ signFn: (x: Transaction) => Promise<Transaction>;
106
35
  }) {
107
36
  this.nord = nord;
108
37
  this.admin = admin;
@@ -114,23 +43,6 @@ export class NordAdmin {
114
43
  * @param nord - Nord instance
115
44
  * @param admin - The user that will be signing actions.
116
45
  * @param signFn - Function to sign messages with the admin's wallet.
117
- *
118
- * `signFn` must sign the _hex-encoded_ message, not the raw message itself, for
119
- * the purpose of being compatible with Solana wallets.
120
- *
121
- * In practice, you will do something along the lines of:
122
- *
123
- * ```typescript
124
- * (x) => wallet.signMessage(new TextEncoder().encode(x.toHex()));
125
- * ```
126
- *
127
- * For a software signing key, this might look more like:
128
- *
129
- * ```typescript
130
- * (x) => nacl.sign.detached(new TextEncoder().encode(x.toHex()), sk);
131
- * ``
132
- *
133
- * where `nacl` is the tweetnacl library.
134
46
  */
135
47
  public static new({
136
48
  nord,
@@ -139,7 +51,7 @@ export class NordAdmin {
139
51
  }: Readonly<{
140
52
  nord: Nord;
141
53
  admin: PublicKey;
142
- signFn: (m: Uint8Array) => Promise<Uint8Array>;
54
+ signFn: (m: Transaction) => Promise<Transaction>;
143
55
  }>): NordAdmin {
144
56
  return new NordAdmin({
145
57
  nord,
@@ -160,12 +72,13 @@ export class NordAdmin {
160
72
  const timestamp = await this.nord.getTimestamp();
161
73
  const action = createAction(timestamp, 0, kind);
162
74
  return sendAction(
163
- this.nord.webServerUrl,
75
+ this.nord.httpClient,
164
76
  async (xs: Uint8Array) => {
165
- const signature = await this.signFn(xs);
166
- if (signature.length !== 64) {
167
- throw new NordError("invalid signature length; must be 64 bytes");
168
- }
77
+ const signature = await signUserPayload({
78
+ payload: xs,
79
+ user: this.admin,
80
+ signTransaction: this.signFn,
81
+ });
169
82
  return Uint8Array.from([...xs, ...signature]);
170
83
  },
171
84
  action,
@@ -208,6 +121,8 @@ export class NordAdmin {
208
121
  value: create(proto.Action_UpdateAclSchema, {
209
122
  aclPubkey: this.admin.toBytes(),
210
123
  targetPubkey: target.toBytes(),
124
+ rolesValue: values,
125
+ rolesMask: mask,
211
126
  }),
212
127
  });
213
128
  expectReceiptKind(receipt, "aclUpdated", "update acl");
@@ -218,7 +133,11 @@ export class NordAdmin {
218
133
  /**
219
134
  * Register a new token that can be listed on Nord.
220
135
  *
221
- * @param params - Token configuration values
136
+ * @param tokenDecimals - Decimal shift used when parsing deposits/withdrawals
137
+ * @param weightBps - Risk weight in basis points applied in account value calculations
138
+ * @param viewSymbol - Symbol surfaced to Nord clients
139
+ * @param oracleSymbol - Symbol resolved by the oracle adapter
140
+ * @param mintAddr - Solana mint backing this token
222
141
  * @returns Action identifier and resulting token metadata
223
142
  * @throws {NordError} If the action submission fails
224
143
  */
@@ -228,9 +147,13 @@ export class NordAdmin {
228
147
  viewSymbol,
229
148
  oracleSymbol,
230
149
  mintAddr,
231
- }: CreateTokenParams): Promise<
232
- { actionId: bigint } & proto.Receipt_InsertTokenResult
233
- > {
150
+ }: Readonly<{
151
+ tokenDecimals: number;
152
+ weightBps: number;
153
+ viewSymbol: string;
154
+ oracleSymbol: string;
155
+ mintAddr: PublicKey;
156
+ }>): Promise<{ actionId: bigint } & proto.Receipt_InsertTokenResult> {
234
157
  const receipt = await this.submitAction({
235
158
  case: "createToken",
236
159
  value: create(proto.Action_CreateTokenSchema, {
@@ -249,26 +172,52 @@ export class NordAdmin {
249
172
  /**
250
173
  * Open a new market with the provided trading parameters.
251
174
  *
252
- * @param params - Market configuration to apply
175
+ * @param sizeDecimals - Decimal shift for contract sizes
176
+ * @param priceDecimals - Decimal shift for price ticks
177
+ * @param imfBps - Base initial margin fraction (IMF) in basis points, see docs/MARKETS.md
178
+ * @param cmfBps - Cancel margin fraction (CMF) in basis points, see docs/MARKETS.md
179
+ * @param mmfBps - Maintenance margin fraction (MMF) in basis points, see docs/MARKETS.md
180
+ * @param marketType - Spot or perpetual market type
181
+ * @param viewSymbol - Symbol exposed to Nord clients
182
+ * @param oracleSymbol - Symbol resolved by the oracle adapter
183
+ * @param baseTokenId - Registered base token backing this market
253
184
  * @returns Action identifier and resulting market metadata
254
185
  * @throws {NordError} If the action submission fails
255
186
  */
256
- async createMarket(
257
- params: CreateMarketParams,
258
- ): Promise<{ actionId: bigint } & proto.Receipt_InsertMarketResult> {
187
+ async createMarket({
188
+ sizeDecimals,
189
+ priceDecimals,
190
+ imfBps,
191
+ cmfBps,
192
+ mmfBps,
193
+ marketType,
194
+ viewSymbol,
195
+ oracleSymbol,
196
+ baseTokenId,
197
+ }: Readonly<{
198
+ sizeDecimals: number;
199
+ priceDecimals: number;
200
+ imfBps: number;
201
+ cmfBps: number;
202
+ mmfBps: number;
203
+ marketType: proto.MarketType;
204
+ viewSymbol: string;
205
+ oracleSymbol: string;
206
+ baseTokenId: number;
207
+ }>): Promise<{ actionId: bigint } & proto.Receipt_InsertMarketResult> {
259
208
  const receipt = await this.submitAction({
260
209
  case: "createMarket",
261
210
  value: create(proto.Action_CreateMarketSchema, {
262
211
  aclPubkey: this.admin.toBytes(),
263
- sizeDecimals: params.sizeDecimals,
264
- priceDecimals: params.priceDecimals,
265
- imfBps: params.imfBps,
266
- cmfBps: params.cmfBps,
267
- mmfBps: params.mmfBps,
268
- marketType: params.marketType,
269
- viewSymbol: params.viewSymbol,
270
- oracleSymbol: params.oracleSymbol,
271
- baseTokenId: params.baseTokenId,
212
+ sizeDecimals,
213
+ priceDecimals,
214
+ imfBps,
215
+ cmfBps,
216
+ mmfBps,
217
+ marketType,
218
+ viewSymbol,
219
+ oracleSymbol,
220
+ baseTokenId,
272
221
  }),
273
222
  });
274
223
  expectReceiptKind(receipt, "insertMarketResult", "create market");
@@ -282,14 +231,19 @@ export class NordAdmin {
282
231
  * leading `0x` prefix). The engine validates the supplied guardian set index
283
232
  * before applying the update.
284
233
  *
285
- * @param params - Guardian set index and guardian addresses
234
+ * @param guardianSetIndex - Wormhole guardian set index that must already exist
235
+ * @param addresses - 20-byte hex-encoded guardian addresses
286
236
  * @returns Action identifier and guardian update receipt
287
237
  * @throws {NordError} If the action submission fails
288
238
  */
289
- async pythSetWormholeGuardians(
290
- params: PythSetWormholeGuardiansParams,
291
- ): Promise<{ actionId: bigint } & proto.Receipt_UpdateGuardianSetResult> {
292
- const addresses = params.addresses.map((address) => {
239
+ async pythSetWormholeGuardians({
240
+ guardianSetIndex,
241
+ addresses,
242
+ }: Readonly<{
243
+ guardianSetIndex: number;
244
+ addresses: readonly string[];
245
+ }>): Promise<{ actionId: bigint } & proto.Receipt_UpdateGuardianSetResult> {
246
+ const parsedAddresses = addresses.map((address) => {
293
247
  try {
294
248
  const decoded = decodeHex(address);
295
249
  if (decoded.length !== 20) {
@@ -308,8 +262,8 @@ export class NordAdmin {
308
262
  case: "pythSetWormholeGuardians",
309
263
  value: create(proto.Action_PythSetWormholeGuardiansSchema, {
310
264
  aclPubkey: this.admin.toBytes(),
311
- guardianSetIndex: params.guardianSetIndex,
312
- addresses,
265
+ guardianSetIndex,
266
+ addresses: parsedAddresses,
313
267
  }),
314
268
  });
315
269
  expectReceiptKind(
@@ -327,16 +281,21 @@ export class NordAdmin {
327
281
  * leading `0x` prefix). Use this call to create or update the mapping used
328
282
  * by the oracle integration.
329
283
  *
330
- * @param params - Oracle symbol and price feed identifier
284
+ * @param oracleSymbol - Symbol resolved by the oracle adapter
285
+ * @param priceFeedId - 32-byte hex-encoded Pyth price feed identifier
331
286
  * @returns Action identifier and symbol feed receipt
332
287
  * @throws {NordError} If the action submission fails
333
288
  */
334
- async pythSetSymbolFeed(
335
- params: PythSetSymbolFeedParams,
336
- ): Promise<{ actionId: bigint } & proto.Receipt_OracleSymbolFeedResult> {
289
+ async pythSetSymbolFeed({
290
+ oracleSymbol,
291
+ priceFeedId: priceFeedIdHex,
292
+ }: Readonly<{
293
+ oracleSymbol: string;
294
+ priceFeedId: string;
295
+ }>): Promise<{ actionId: bigint } & proto.Receipt_OracleSymbolFeedResult> {
337
296
  let priceFeedId: Uint8Array;
338
297
  try {
339
- priceFeedId = decodeHex(params.priceFeedId);
298
+ priceFeedId = decodeHex(priceFeedIdHex);
340
299
  if (priceFeedId.length !== 32) {
341
300
  throw new Error("price feed id must be 32 bytes");
342
301
  }
@@ -350,7 +309,7 @@ export class NordAdmin {
350
309
  case: "pythSetSymbolFeed",
351
310
  value: create(proto.Action_PythSetSymbolFeedSchema, {
352
311
  aclPubkey: this.admin.toBytes(),
353
- oracleSymbol: params.oracleSymbol,
312
+ oracleSymbol,
354
313
  priceFeedId,
355
314
  }),
356
315
  });
@@ -395,17 +354,19 @@ export class NordAdmin {
395
354
  /**
396
355
  * Freeze an individual market, preventing new trades and orders.
397
356
  *
398
- * @param params - Target market identifier
357
+ * @param marketId - Target market identifier
399
358
  * @returns Action identifier and freeze receipt
400
359
  * @throws {NordError} If the action submission fails
401
360
  */
402
- async freezeMarket(
403
- params: FreezeMarketParams,
404
- ): Promise<{ actionId: bigint } & proto.Receipt_MarketFreezeUpdated> {
361
+ async freezeMarket({
362
+ marketId,
363
+ }: Readonly<{
364
+ marketId: number;
365
+ }>): Promise<{ actionId: bigint } & proto.Receipt_MarketFreezeUpdated> {
405
366
  const receipt = await this.submitAction({
406
367
  case: "freezeMarket",
407
368
  value: create(proto.Action_FreezeMarketSchema, {
408
- marketId: params.marketId,
369
+ marketId,
409
370
  aclPubkey: this.admin.toBytes(),
410
371
  }),
411
372
  });
@@ -416,17 +377,19 @@ export class NordAdmin {
416
377
  /**
417
378
  * Unfreeze a market that was previously halted.
418
379
  *
419
- * @param params - Target market identifier
380
+ * @param marketId - Target market identifier
420
381
  * @returns Action identifier and freeze receipt
421
382
  * @throws {NordError} If the action submission fails
422
383
  */
423
- async unfreezeMarket(
424
- params: UnfreezeMarketParams,
425
- ): Promise<{ actionId: bigint } & proto.Receipt_MarketFreezeUpdated> {
384
+ async unfreezeMarket({
385
+ marketId,
386
+ }: Readonly<{
387
+ marketId: number;
388
+ }>): Promise<{ actionId: bigint } & proto.Receipt_MarketFreezeUpdated> {
426
389
  const receipt = await this.submitAction({
427
390
  case: "unfreezeMarket",
428
391
  value: create(proto.Action_UnfreezeMarketSchema, {
429
- marketId: params.marketId,
392
+ marketId,
430
393
  aclPubkey: this.admin.toBytes(),
431
394
  }),
432
395
  });
@@ -441,18 +404,20 @@ export class NordAdmin {
441
404
  * the default Nord fees; use `updateFeeTier` if you need to change it.
442
405
  * - The first appended tier receives id 1, and subsequent tiers increment the id.
443
406
  *
444
- * @param params - Fee tier configuration to insert
407
+ * @param config - Fee tier configuration to insert
445
408
  * @returns Action identifier and fee tier addition receipt
446
409
  * @throws {NordError} If the action submission fails or the new tier exceeds the maximum range (0-15).
447
410
  */
448
- async addFeeTier(
449
- params: AddFeeTierParams,
450
- ): Promise<{ actionId: bigint } & proto.Receipt_FeeTierAdded> {
411
+ async addFeeTier({
412
+ config,
413
+ }: Readonly<{
414
+ config: FeeTierConfig;
415
+ }>): Promise<{ actionId: bigint } & proto.Receipt_FeeTierAdded> {
451
416
  const receipt = await this.submitAction({
452
417
  case: "addFeeTier",
453
418
  value: create(proto.Action_AddFeeTierSchema, {
454
419
  aclPubkey: this.admin.toBytes(),
455
- config: create(proto.FeeTierConfigSchema, params.config),
420
+ config: create(proto.FeeTierConfigSchema, config),
456
421
  }),
457
422
  });
458
423
  expectReceiptKind(receipt, "feeTierAdded", "add fee tier");
@@ -465,19 +430,24 @@ export class NordAdmin {
465
430
  * Tier identifiers must already exist; attempting to update a missing tier
466
431
  * causes the action to fail.
467
432
  *
468
- * @param params - Fee tier identifier and updated configuration
433
+ * @param tierId - Existing fee tier identifier to update
434
+ * @param config - Replacement configuration for the tier
469
435
  * @returns Action identifier and fee tier update receipt
470
436
  * @throws {NordError} If the action submission fails or the tier ID exceeds the configured range.
471
437
  */
472
- async updateFeeTier(
473
- params: UpdateFeeTierParams,
474
- ): Promise<{ actionId: bigint } & proto.Receipt_FeeTierUpdated> {
438
+ async updateFeeTier({
439
+ tierId,
440
+ config,
441
+ }: Readonly<{
442
+ tierId: number;
443
+ config: FeeTierConfig;
444
+ }>): Promise<{ actionId: bigint } & proto.Receipt_FeeTierUpdated> {
475
445
  const receipt = await this.submitAction({
476
446
  case: "updateFeeTier",
477
447
  value: create(proto.Action_UpdateFeeTierSchema, {
478
448
  aclPubkey: this.admin.toBytes(),
479
- id: params.tierId,
480
- config: create(proto.FeeTierConfigSchema, params.config),
449
+ id: tierId,
450
+ config: create(proto.FeeTierConfigSchema, config),
481
451
  }),
482
452
  });
483
453
  expectReceiptKind(receipt, "feeTierUpdated", "update fee tier");