@n1xyz/nord-ts 0.0.21 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +20 -16
  2. package/dist/api/client.d.ts +14 -0
  3. package/dist/api/client.js +45 -0
  4. package/dist/bridge/client.d.ts +151 -0
  5. package/dist/bridge/client.js +434 -0
  6. package/dist/bridge/const.d.ts +23 -0
  7. package/dist/bridge/const.js +47 -0
  8. package/dist/bridge/index.d.ts +4 -0
  9. package/dist/bridge/index.js +23 -0
  10. package/dist/bridge/types.d.ts +120 -0
  11. package/dist/bridge/types.js +18 -0
  12. package/dist/bridge/utils.d.ts +64 -0
  13. package/dist/bridge/utils.js +131 -0
  14. package/dist/gen/common.d.ts +68 -0
  15. package/dist/gen/common.js +215 -0
  16. package/dist/gen/nord_pb.d.ts +3719 -0
  17. package/dist/gen/nord_pb.js +945 -0
  18. package/dist/gen/openapi.d.ts +268 -4
  19. package/dist/idl/bridge.d.ts +569 -0
  20. package/dist/idl/bridge.js +8 -0
  21. package/dist/idl/bridge.json +1506 -0
  22. package/dist/idl/index.d.ts +607 -0
  23. package/dist/idl/index.js +8 -0
  24. package/dist/nord/api/actions.d.ts +31 -72
  25. package/dist/nord/api/actions.js +199 -201
  26. package/dist/nord/api/market.d.ts +36 -0
  27. package/dist/nord/api/market.js +96 -0
  28. package/dist/nord/api/queries.d.ts +46 -0
  29. package/dist/nord/api/queries.js +109 -0
  30. package/dist/nord/client/Nord.js +3 -3
  31. package/dist/nord/client/NordUser.d.ts +26 -13
  32. package/dist/nord/client/NordUser.js +13 -10
  33. package/dist/types.d.ts +12 -1
  34. package/dist/types.js +29 -2
  35. package/dist/utils.d.ts +6 -20
  36. package/dist/utils.js +17 -35
  37. package/dist/websocket/NordWebSocketClient.js +2 -6
  38. package/package.json +26 -23
  39. package/src/gen/nord_pb.ts +4257 -0
  40. package/src/gen/openapi.ts +268 -4
  41. package/src/nord/api/actions.ts +278 -369
  42. package/src/nord/client/Nord.ts +3 -3
  43. package/src/nord/client/NordUser.ts +40 -19
  44. package/src/types.ts +32 -1
  45. package/src/utils.ts +24 -43
  46. package/src/websocket/NordWebSocketClient.ts +2 -8
@@ -1,16 +1,26 @@
1
1
  import Decimal from "decimal.js";
2
- import * as proto from "../../gen/nord";
3
- import { FillMode, fillModeToProtoFillMode, KeyType, Side } from "../../types";
2
+ import * as proto from "../../gen/nord_pb";
3
+ import { paths, components } from "../../gen/openapi";
4
+ import createClient from "openapi-fetch";
5
+
6
+ import { create } from "@bufbuild/protobuf";
7
+ import {
8
+ FillMode,
9
+ fillModeToProtoFillMode,
10
+ KeyType,
11
+ Side,
12
+ QuoteSize,
13
+ } from "../../types";
4
14
  import {
5
15
  assert,
6
16
  BigIntValue,
7
17
  checkedFetch,
8
18
  checkPubKeyLength,
9
19
  decodeLengthDelimited,
10
- encodeLengthDelimited,
11
20
  SESSION_TTL,
12
21
  toScaledU64,
13
22
  } from "../../utils";
23
+ import { sizeDelimitedEncode } from "@bufbuild/protobuf/wire";
14
24
 
15
25
  async function sessionSign(
16
26
  signFn: (message: Uint8Array) => Promise<Uint8Array>,
@@ -28,44 +38,77 @@ async function walletSign(
28
38
  return new Uint8Array([...message, ...signature]);
29
39
  }
30
40
 
31
- function makeSendHttp(
32
- serverUrl: string,
33
- ): (encoded: Uint8Array) => Promise<Uint8Array> {
34
- return async (body) => {
35
- // TODO: this should be changed to use openapi
36
- const response = await checkedFetch(`${serverUrl}/action`, {
37
- method: "POST",
38
- headers: {
39
- "Content-Type": "application/json",
40
- },
41
- body,
42
- });
43
- return new Uint8Array(await response.arrayBuffer());
44
- };
41
+ // Helper to create an action with common fields
42
+ function createAction(
43
+ currentTimestamp: bigint,
44
+ nonce: number,
45
+ kind: proto.Action["kind"],
46
+ ): proto.Action {
47
+ return create(proto.ActionSchema, {
48
+ currentTimestamp,
49
+ nonce,
50
+ kind,
51
+ });
45
52
  }
46
53
 
47
54
  async function sendAction(
48
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
55
+ serverUrl: string,
49
56
  makeSignedMessage: (message: Uint8Array) => Promise<Uint8Array>,
50
57
  action: proto.Action,
51
58
  actionErrorDesc: string,
52
59
  ): Promise<proto.Receipt> {
53
- const encoded = encodeLengthDelimited(action, proto.Action);
54
- const body = await makeSignedMessage(encoded);
55
- const rawResp = await sendFn(body);
56
- const resp: proto.Receipt = decodeLengthDelimited(rawResp, proto.Receipt);
60
+ const body = await prepareAction(action, makeSignedMessage);
61
+ // NOTE: restructure and reuse client as it is in Nord.ts
62
+ const client = createClient<paths>({ baseUrl: serverUrl });
63
+ const response = await client.POST("/action", {
64
+ params: {
65
+ header: {
66
+ "content-type": "application/octet-stream",
67
+ },
68
+ },
69
+ body: body,
70
+ });
71
+
72
+ if (response.error) {
73
+ throw new Error(`Failed to ${actionErrorDesc}, HTTP status ${response}`);
74
+ }
75
+
76
+ const rawResp = new Uint8Array(await response.response.arrayBuffer());
57
77
 
58
- if (resp.kind?.$case === "err") {
78
+ const resp: proto.Receipt = decodeLengthDelimited(
79
+ rawResp,
80
+ proto.ReceiptSchema,
81
+ );
82
+
83
+ if (resp.kind?.case === "err") {
59
84
  throw new Error(
60
- `Could not ${actionErrorDesc}, reason: ${proto.errorToJSON(resp.kind.value)}`,
85
+ `Could not ${actionErrorDesc}, reason: ${proto.Error[resp.kind.value]}`,
61
86
  );
62
87
  }
63
88
 
64
89
  return resp;
65
90
  }
66
91
 
67
- async function createSessionImpl(
68
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
92
+ // Given action and signature function, prepare the signed message to send to server as `body`.
93
+ // `makeSignedMessage` must include the original message and signature.
94
+ export async function prepareAction(
95
+ action: proto.Action,
96
+ makeSignedMessage: (message: Uint8Array) => Promise<Uint8Array>,
97
+ ) {
98
+ const encoded = sizeDelimitedEncode(proto.ActionSchema, action);
99
+ // NOTE(agent): keep in sync with MAX_HTTP_REQUEST_BODY_SIZE in rust code
100
+ const MAX_HTTP_REQUEST_BODY_SIZE = 1024;
101
+ if (encoded.byteLength > MAX_HTTP_REQUEST_BODY_SIZE) {
102
+ throw new Error(
103
+ `Encoded message size (${encoded.byteLength} bytes) is greater than max payload size (${MAX_HTTP_REQUEST_BODY_SIZE} bytes).`,
104
+ );
105
+ }
106
+ const body = await makeSignedMessage(encoded);
107
+ return body;
108
+ }
109
+
110
+ export async function createSession(
111
+ serverUrl: string,
69
112
  walletSignFn: (message: string | Uint8Array) => Promise<Uint8Array>,
70
113
  currentTimestamp: bigint,
71
114
  nonce: number,
@@ -75,7 +118,7 @@ async function createSessionImpl(
75
118
  // If not specified, set to current moment plus default session TTL
76
119
  expiryTimestamp?: bigint;
77
120
  },
78
- ): Promise<bigint> {
121
+ ): Promise<{ actionId: bigint; sessionId: bigint }> {
79
122
  checkPubKeyLength(KeyType.Ed25519, params.userPubkey.length);
80
123
  checkPubKeyLength(KeyType.Ed25519, params.sessionPubkey.length);
81
124
 
@@ -91,102 +134,60 @@ async function createSessionImpl(
91
134
  expiry = currentTimestamp + SESSION_TTL;
92
135
  }
93
136
 
94
- const action: proto.Action = {
95
- currentTimestamp,
96
- nonce,
97
- kind: {
98
- $case: "createSession",
99
- value: {
100
- userPubkey: params.userPubkey,
101
- blstPubkey: params.sessionPubkey,
102
- expiryTimestamp: expiry,
103
- },
104
- },
105
- };
137
+ const action = createAction(currentTimestamp, nonce, {
138
+ case: "createSession",
139
+ value: create(proto.Action_CreateSessionSchema, {
140
+ userPubkey: params.userPubkey,
141
+ blstPubkey: params.sessionPubkey,
142
+ expiryTimestamp: expiry,
143
+ }),
144
+ });
106
145
 
107
146
  const resp = await sendAction(
108
- sendFn,
147
+ serverUrl,
109
148
  (m) => walletSign(walletSignFn, m),
110
149
  action,
111
150
  "create a new session",
112
151
  );
113
152
 
114
- if (resp.kind?.$case === "createSessionResult") {
115
- return resp.kind.value.sessionId;
153
+ if (resp.kind?.case === "createSessionResult") {
154
+ return {
155
+ actionId: resp.actionId,
156
+ sessionId: resp.kind.value.sessionId,
157
+ };
116
158
  } else {
117
- throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
159
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
118
160
  }
119
161
  }
120
162
 
121
- export async function createSession(
163
+ export async function revokeSession(
122
164
  serverUrl: string,
123
165
  walletSignFn: (message: string | Uint8Array) => Promise<Uint8Array>,
124
166
  currentTimestamp: bigint,
125
167
  nonce: number,
126
- params: {
127
- userPubkey: Uint8Array;
128
- sessionPubkey: Uint8Array;
129
- // If not specified, set to current moment plus default session TTL
130
- expiryTimestamp?: bigint;
131
- },
132
- ): Promise<bigint> {
133
- return createSessionImpl(
134
- makeSendHttp(serverUrl),
135
- walletSignFn,
136
- currentTimestamp,
137
- nonce,
138
- params,
139
- );
140
- }
141
-
142
- async function revokeSessionImpl(
143
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
144
- walletSignFn: (message: string | Uint8Array) => Promise<Uint8Array>,
145
- currentTimestamp: bigint,
146
- nonce: number,
147
168
  params: {
148
169
  sessionId: BigIntValue;
149
170
  },
150
- ): Promise<void> {
151
- const action: proto.Action = {
152
- currentTimestamp,
153
- nonce,
154
- kind: {
155
- $case: "revokeSession",
156
- value: {
157
- sessionId: BigInt(params.sessionId),
158
- },
159
- },
160
- };
171
+ ): Promise<{ actionId: bigint }> {
172
+ const action = createAction(currentTimestamp, nonce, {
173
+ case: "revokeSession",
174
+ value: create(proto.Action_RevokeSessionSchema, {
175
+ sessionId: BigInt(params.sessionId),
176
+ }),
177
+ });
161
178
 
162
- await sendAction(
163
- sendFn,
179
+ const resp = await sendAction(
180
+ serverUrl,
164
181
  (m) => walletSign(walletSignFn, m),
165
182
  action,
166
- "create a new session",
183
+ "revoke session",
167
184
  );
168
- }
169
185
 
170
- export async function revokeSession(
171
- serverUrl: string,
172
- walletSignFn: (message: string | Uint8Array) => Promise<Uint8Array>,
173
- currentTimestamp: bigint,
174
- nonce: number,
175
- params: {
176
- sessionId: BigIntValue;
177
- },
178
- ): Promise<void> {
179
- return revokeSessionImpl(
180
- makeSendHttp(serverUrl),
181
- walletSignFn,
182
- currentTimestamp,
183
- nonce,
184
- params,
185
- );
186
+ return { actionId: resp.actionId };
186
187
  }
187
188
 
188
- async function withdrawImpl(
189
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
189
+ export async function withdraw(
190
+ serverUrl: string,
190
191
  signFn: (message: Uint8Array) => Promise<Uint8Array>,
191
192
  currentTimestamp: bigint,
192
193
  nonce: number,
@@ -203,63 +204,37 @@ async function withdrawImpl(
203
204
  throw new Error("Withdraw amount must be positive");
204
205
  }
205
206
 
206
- const action: proto.Action = {
207
- currentTimestamp,
208
- nonce,
209
- kind: {
210
- $case: "withdraw",
211
- value: {
212
- sessionId: BigInt(params.sessionId),
213
- tokenId: params.tokenId,
214
- amount,
215
- },
216
- },
217
- };
207
+ const action = createAction(currentTimestamp, nonce, {
208
+ case: "withdraw",
209
+ value: create(proto.Action_WithdrawSchema, {
210
+ sessionId: BigInt(params.sessionId),
211
+ tokenId: params.tokenId,
212
+ amount,
213
+ }),
214
+ });
218
215
 
219
216
  const resp = await sendAction(
220
- sendFn,
217
+ serverUrl,
221
218
  (m) => sessionSign(signFn, m),
222
219
  action,
223
220
  "withdraw",
224
221
  );
225
222
 
226
- if (resp.kind?.$case === "withdrawResult") {
223
+ if (resp.kind?.case === "withdrawResult") {
227
224
  return { actionId: resp.actionId, ...resp.kind.value };
228
225
  } else {
229
- throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
226
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
230
227
  }
231
228
  }
232
229
 
233
- export async function withdraw(
230
+ export async function placeOrder(
234
231
  serverUrl: string,
235
232
  signFn: (message: Uint8Array) => Promise<Uint8Array>,
236
233
  currentTimestamp: bigint,
237
234
  nonce: number,
238
- params: {
239
- sizeDecimals: number;
240
- sessionId: BigIntValue;
241
- tokenId: number;
242
- amount: number;
243
- },
244
- ): Promise<{ actionId: bigint } & proto.Receipt_WithdrawResult> {
245
- return withdrawImpl(
246
- makeSendHttp(serverUrl),
247
- signFn,
248
- currentTimestamp,
249
- nonce,
250
- params,
251
- );
252
- }
253
-
254
- async function placeOrderImpl(
255
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
256
- signFn: (message: Uint8Array) => Promise<Uint8Array>,
257
- currentTimestamp: bigint,
258
- nonce: number,
259
235
  params: {
260
236
  sessionId: BigIntValue;
261
237
  senderId?: number;
262
- liquidateeId?: number;
263
238
  sizeDecimals: number;
264
239
  priceDecimals: number;
265
240
  marketId: number;
@@ -269,154 +244,120 @@ async function placeOrderImpl(
269
244
  // NOTE: if `size` equals 1.0, it will sell whole unit, for example 1.0 BTC
270
245
  size?: Decimal.Value;
271
246
  price?: Decimal.Value;
272
- quoteSizeSize?: Decimal.Value;
273
- quoteSizePrice?: Decimal.Value;
247
+ quoteSize?: QuoteSize;
248
+ liquidateeId?: number;
274
249
  clientOrderId?: BigIntValue;
275
250
  },
276
- ): Promise<bigint | undefined> {
251
+ ): Promise<{
252
+ actionId: bigint;
253
+ orderId?: bigint;
254
+ fills: proto.Receipt_Trade[];
255
+ }> {
277
256
  const price = toScaledU64(params.price ?? 0, params.priceDecimals);
278
257
  const size = toScaledU64(params.size ?? 0, params.sizeDecimals);
279
- const quoteSize = toScaledU64(params.quoteSizeSize ?? 0, params.sizeDecimals);
280
- const quotePrice = toScaledU64(
281
- params.quoteSizePrice ?? 0,
282
- params.priceDecimals,
258
+
259
+ const scaledQuote = params.quoteSize
260
+ ? params.quoteSize.toScaledU64(params.priceDecimals, params.sizeDecimals)
261
+ : undefined;
262
+
263
+ assert(
264
+ price > 0n || size > 0n || scaledQuote !== undefined,
265
+ "OrderLimit must include at least one of: size, price, or quoteSize",
283
266
  );
284
267
 
285
- // Compose action object
286
- const action: proto.Action = {
287
- currentTimestamp,
288
- nonce,
289
- kind: {
290
- $case: "placeOrder",
291
- value: {
292
- sessionId: BigInt(params.sessionId),
293
- senderAccountId: params.senderId,
294
- marketId: params.marketId,
295
- side: params.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
296
- fillMode: fillModeToProtoFillMode(params.fillMode),
297
- isReduceOnly: params.isReduceOnly,
298
- price,
299
- size,
300
- quoteSize: { size: quoteSize, price: quotePrice },
301
- clientOrderId:
302
- params.clientOrderId === undefined
303
- ? undefined
304
- : BigInt(params.clientOrderId),
305
- delegatorAccountId: params.liquidateeId,
306
- },
307
- },
308
- };
268
+ const action = createAction(currentTimestamp, nonce, {
269
+ case: "placeOrder",
270
+ value: create(proto.Action_PlaceOrderSchema, {
271
+ sessionId: BigInt(params.sessionId),
272
+ senderAccountId: params.senderId,
273
+ marketId: params.marketId,
274
+ side: params.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
275
+ fillMode: fillModeToProtoFillMode(params.fillMode),
276
+ isReduceOnly: params.isReduceOnly,
277
+ price,
278
+ size,
279
+ quoteSize:
280
+ scaledQuote === undefined
281
+ ? undefined
282
+ : create(proto.QuoteSizeSchema, {
283
+ size: scaledQuote.size,
284
+ price: scaledQuote.price,
285
+ }),
286
+ clientOrderId:
287
+ params.clientOrderId === undefined
288
+ ? undefined
289
+ : BigInt(params.clientOrderId),
290
+ delegatorAccountId: params.liquidateeId,
291
+ }),
292
+ });
309
293
 
310
294
  const resp = await sendAction(
311
- sendFn,
295
+ serverUrl,
312
296
  (m) => sessionSign(signFn, m),
313
297
  action,
314
- "place the order",
298
+ "place order",
315
299
  );
316
300
 
317
- if (resp.kind?.$case === "placeOrderResult") {
318
- return resp.kind.value.posted?.orderId;
301
+ if (resp.kind?.case === "placeOrderResult") {
302
+ return {
303
+ actionId: resp.actionId,
304
+ orderId: resp.kind.value.posted?.orderId,
305
+ fills: resp.kind.value.fills,
306
+ };
319
307
  } else {
320
- throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
308
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
321
309
  }
322
310
  }
323
311
 
324
- export async function placeOrder(
312
+ export async function cancelOrder(
325
313
  serverUrl: string,
326
314
  signFn: (message: Uint8Array) => Promise<Uint8Array>,
327
315
  currentTimestamp: bigint,
328
316
  nonce: number,
329
- params: {
330
- sessionId: BigIntValue;
331
- senderId?: number;
332
- sizeDecimals: number;
333
- priceDecimals: number;
334
- marketId: number;
335
- side: Side;
336
- fillMode: FillMode;
337
- isReduceOnly: boolean;
338
- size?: Decimal.Value;
339
- price?: Decimal.Value;
340
- quoteSize?: Decimal.Value;
341
- liquidateeId?: number;
342
- clientOrderId?: BigIntValue;
343
- },
344
- ): Promise<bigint | undefined> {
345
- return placeOrderImpl(
346
- makeSendHttp(serverUrl),
347
- signFn,
348
- currentTimestamp,
349
- nonce,
350
- params,
351
- );
352
- }
353
-
354
- async function cancelOrderImpl(
355
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
356
- signFn: (message: Uint8Array) => Promise<Uint8Array>,
357
- currentTimestamp: bigint,
358
- nonce: number,
359
317
  params: {
360
318
  sessionId: BigIntValue;
361
319
  senderId?: number;
362
320
  orderId: BigIntValue;
363
321
  liquidateeId?: number;
364
322
  },
365
- ): Promise<bigint> {
366
- const action: proto.Action = {
367
- currentTimestamp,
368
- nonce: nonce,
369
- kind: {
370
- $case: "cancelOrderById",
371
- value: {
372
- orderId: BigInt(params.orderId),
373
- sessionId: BigInt(params.sessionId),
374
- senderAccountId: params.senderId,
375
- delegatorAccountId: params.liquidateeId,
376
- },
377
- },
378
- };
323
+ ): Promise<{
324
+ actionId: bigint;
325
+ orderId: bigint;
326
+ accountId: number;
327
+ }> {
328
+ const action = createAction(currentTimestamp, nonce, {
329
+ case: "cancelOrderById",
330
+ value: create(proto.Action_CancelOrderByIdSchema, {
331
+ orderId: BigInt(params.orderId),
332
+ sessionId: BigInt(params.sessionId),
333
+ senderAccountId: params.senderId,
334
+ delegatorAccountId: params.liquidateeId,
335
+ }),
336
+ });
379
337
 
380
338
  const resp = await sendAction(
381
- sendFn,
339
+ serverUrl,
382
340
  (m) => sessionSign(signFn, m),
383
341
  action,
384
- "cancel the order",
342
+ "cancel order",
385
343
  );
386
344
 
387
- if (resp.kind?.$case === "cancelOrderResult") {
388
- return resp.kind.value.orderId;
345
+ if (resp.kind?.case === "cancelOrderResult") {
346
+ return {
347
+ actionId: resp.actionId,
348
+ orderId: resp.kind.value.orderId,
349
+ accountId: resp.kind.value.accountId,
350
+ };
389
351
  } else {
390
- throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
352
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
391
353
  }
392
354
  }
393
355
 
394
- export async function cancelOrder(
356
+ export async function transfer(
395
357
  serverUrl: string,
396
358
  signFn: (message: Uint8Array) => Promise<Uint8Array>,
397
359
  currentTimestamp: bigint,
398
360
  nonce: number,
399
- params: {
400
- sessionId: BigIntValue;
401
- senderId?: number;
402
- orderId: BigIntValue;
403
- liquidateeId?: number;
404
- },
405
- ): Promise<bigint> {
406
- return cancelOrderImpl(
407
- makeSendHttp(serverUrl),
408
- signFn,
409
- currentTimestamp,
410
- nonce,
411
- params,
412
- );
413
- }
414
-
415
- async function transferImpl(
416
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
417
- signFn: (message: Uint8Array) => Promise<Uint8Array>,
418
- currentTimestamp: bigint,
419
- nonce: number,
420
361
  params: {
421
362
  sessionId: BigIntValue;
422
363
  fromAccountId: number;
@@ -425,63 +366,46 @@ async function transferImpl(
425
366
  tokenDecimals: number;
426
367
  amount: Decimal.Value;
427
368
  },
428
- ): Promise<number | undefined> {
429
- const action: proto.Action = {
430
- currentTimestamp,
431
- nonce: nonce,
432
- kind: {
433
- $case: "transfer",
434
- value: {
435
- sessionId: BigInt(params.sessionId),
436
- fromAccountId: params.fromAccountId,
437
- toAccountId: params.toAccountId,
438
- tokenId: params.tokenId,
439
- amount: toScaledU64(params.amount ?? 0, params.tokenDecimals),
440
- },
441
- },
442
- };
369
+ ): Promise<{
370
+ actionId: bigint;
371
+ fromAccountId: number;
372
+ toAccountId?: number;
373
+ tokenId: number;
374
+ amount: bigint;
375
+ accountCreated: boolean;
376
+ }> {
377
+ const action = createAction(currentTimestamp, nonce, {
378
+ case: "transfer",
379
+ value: create(proto.Action_TransferSchema, {
380
+ sessionId: BigInt(params.sessionId),
381
+ fromAccountId: params.fromAccountId,
382
+ toAccountId: params.toAccountId,
383
+ tokenId: params.tokenId,
384
+ amount: toScaledU64(params.amount ?? 0, params.tokenDecimals),
385
+ }),
386
+ });
443
387
 
444
388
  const resp = await sendAction(
445
- sendFn,
389
+ serverUrl,
446
390
  (m) => sessionSign(signFn, m),
447
391
  action,
448
- "transfer asset to other account",
392
+ "transfer",
449
393
  );
450
394
 
451
- if (resp.kind?.$case === "transferred") {
452
- if (resp.kind.value.accountCreated) {
453
- return resp.kind.value.toUserAccount;
454
- } else {
455
- return undefined;
456
- }
395
+ if (resp.kind?.case === "transferred") {
396
+ return {
397
+ actionId: resp.actionId,
398
+ fromAccountId: resp.kind.value.fromAccountId,
399
+ toAccountId: resp.kind.value.toUserAccount,
400
+ tokenId: resp.kind.value.tokenId,
401
+ amount: resp.kind.value.amount,
402
+ accountCreated: resp.kind.value.accountCreated,
403
+ };
457
404
  } else {
458
- throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
405
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
459
406
  }
460
407
  }
461
408
 
462
- export async function transfer(
463
- serverUrl: string,
464
- signFn: (message: Uint8Array) => Promise<Uint8Array>,
465
- currentTimestamp: bigint,
466
- nonce: number,
467
- params: {
468
- sessionId: BigIntValue;
469
- fromAccountId: number;
470
- toAccountId?: number;
471
- tokenId: number;
472
- tokenDecimals: number;
473
- amount: Decimal.Value;
474
- },
475
- ): Promise<number | undefined> {
476
- return transferImpl(
477
- makeSendHttp(serverUrl),
478
- signFn,
479
- currentTimestamp,
480
- nonce,
481
- params,
482
- );
483
- }
484
-
485
409
  export type AtomicSubaction =
486
410
  | {
487
411
  kind: "place";
@@ -496,8 +420,7 @@ export type AtomicSubaction =
496
420
  // at least one of the three has to be specified; 0 treated as "not set"
497
421
  size?: Decimal.Value;
498
422
  price?: Decimal.Value;
499
- quoteSizeSize?: Decimal.Value;
500
- quoteSizePrice?: Decimal.Value;
423
+ quoteSize?: QuoteSize;
501
424
  clientOrderId?: BigIntValue;
502
425
  }
503
426
  | {
@@ -505,8 +428,8 @@ export type AtomicSubaction =
505
428
  orderId: BigIntValue;
506
429
  };
507
430
 
508
- async function atomicImpl(
509
- sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
431
+ export async function atomic(
432
+ serverUrl: string,
510
433
  signFn: (message: Uint8Array) => Promise<Uint8Array>,
511
434
  currentTimestamp: bigint,
512
435
  nonce: number,
@@ -515,7 +438,10 @@ async function atomicImpl(
515
438
  accountId?: number;
516
439
  actions: AtomicSubaction[];
517
440
  },
518
- ): Promise<proto.Receipt_AtomicResult> {
441
+ ): Promise<{
442
+ actionId: bigint;
443
+ results: proto.Receipt_AtomicSubactionResultKind[];
444
+ }> {
519
445
  assert(
520
446
  params.actions.length > 0 && params.actions.length <= 4,
521
447
  "Atomic action must contain between 1 and 4 sub-actions",
@@ -525,89 +451,72 @@ async function atomicImpl(
525
451
  if (a.kind === "place") {
526
452
  const price = toScaledU64(a.price ?? 0, a.priceDecimals);
527
453
  const size = toScaledU64(a.size ?? 0, a.sizeDecimals);
528
- const quoteSizeSize = toScaledU64(a.quoteSizeSize ?? 0, a.sizeDecimals);
529
- const quoteSizePrice = toScaledU64(
530
- a.quoteSizePrice ?? 0,
531
- a.priceDecimals,
454
+ const scaledQuote = a.quoteSize
455
+ ? a.quoteSize.toScaledU64(a.priceDecimals, a.sizeDecimals)
456
+ : undefined;
457
+
458
+ // Require at least one limit to be set (non-zero size, non-zero price, or quoteSize)
459
+ assert(
460
+ price > 0n || size > 0n || scaledQuote !== undefined,
461
+ "OrderLimit must include at least one of: size, price, or quoteSize",
532
462
  );
533
- const tradeOrPlace: proto.TradeOrPlace = {
534
- marketId: a.marketId,
535
- orderType: {
536
- side: a.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
537
- fillMode: fillModeToProtoFillMode(a.fillMode),
538
- isReduceOnly: a.isReduceOnly,
539
- },
540
- limit: {
541
- price,
542
- size,
543
- quoteSize: { size: quoteSizeSize, price: quoteSizePrice },
463
+
464
+ const tradeOrPlace: proto.TradeOrPlace = create(
465
+ proto.TradeOrPlaceSchema,
466
+ {
467
+ marketId: a.marketId,
468
+ orderType: create(proto.OrderTypeSchema, {
469
+ side: a.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
470
+ fillMode: fillModeToProtoFillMode(a.fillMode),
471
+ isReduceOnly: a.isReduceOnly,
472
+ }),
473
+ limit: create(proto.OrderLimitSchema, {
474
+ price,
475
+ size,
476
+ quoteSize:
477
+ scaledQuote === undefined
478
+ ? undefined
479
+ : create(proto.QuoteSizeSchema, {
480
+ size: scaledQuote.size,
481
+ price: scaledQuote.price,
482
+ }),
483
+ }),
484
+ clientOrderId:
485
+ a.clientOrderId === undefined ? undefined : BigInt(a.clientOrderId),
544
486
  },
545
- clientOrderId:
546
- a.clientOrderId === undefined ? undefined : BigInt(a.clientOrderId),
547
- };
548
- return {
549
- inner: { $case: "tradeOrPlace", value: tradeOrPlace },
550
- } as proto.AtomicSubactionKind;
487
+ );
488
+ return create(proto.AtomicSubactionKindSchema, {
489
+ inner: { case: "tradeOrPlace", value: tradeOrPlace },
490
+ });
551
491
  }
552
- return {
553
- inner: { $case: "cancelOrder", value: { orderId: BigInt(a.orderId) } },
554
- } as proto.AtomicSubactionKind;
492
+ return create(proto.AtomicSubactionKindSchema, {
493
+ inner: {
494
+ case: "cancelOrder",
495
+ value: create(proto.CancelOrderSchema, { orderId: BigInt(a.orderId) }),
496
+ },
497
+ });
555
498
  });
556
499
 
557
- const action: proto.Action = {
558
- currentTimestamp,
559
- nonce,
560
- kind: {
561
- $case: "atomic",
562
- value: {
563
- sessionId: BigInt(params.sessionId),
564
- accountId: params.accountId, // optional
565
- actions: subactions,
566
- },
567
- },
568
- };
500
+ const action = createAction(currentTimestamp, nonce, {
501
+ case: "atomic",
502
+ value: create(proto.AtomicSchema, {
503
+ sessionId: BigInt(params.sessionId),
504
+ accountId: params.accountId, // optional
505
+ actions: subactions,
506
+ }),
507
+ });
569
508
 
570
509
  const resp = await sendAction(
571
- sendFn,
510
+ serverUrl,
572
511
  (m) => sessionSign(signFn, m),
573
512
  action,
574
513
  "execute atomic action",
575
514
  );
576
- if (resp.kind?.$case === "atomic") {
577
- return resp.kind.value;
515
+ if (resp.kind?.case === "atomic") {
516
+ return {
517
+ actionId: resp.actionId,
518
+ results: resp.kind.value.results,
519
+ };
578
520
  }
579
- throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
580
- }
581
-
582
- export async function atomic(
583
- serverUrl: string,
584
- signFn: (message: Uint8Array) => Promise<Uint8Array>,
585
- currentTimestamp: bigint,
586
- nonce: number,
587
- params: {
588
- sessionId: BigIntValue;
589
- accountId?: number;
590
- actions: AtomicSubaction[];
591
- },
592
- ): Promise<proto.Receipt_AtomicResult> {
593
- return atomicImpl(
594
- makeSendHttp(serverUrl),
595
- signFn,
596
- currentTimestamp,
597
- nonce,
598
- params,
599
- );
521
+ throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
600
522
  }
601
-
602
- /**
603
- * For testing purposes
604
- */
605
- export const _private = {
606
- createSessionImpl,
607
- revokeSessionImpl,
608
- withdrawImpl,
609
- placeOrderImpl,
610
- cancelOrderImpl,
611
- transferImpl,
612
- atomicImpl,
613
- };