@n1xyz/nord-ts 0.0.21 → 0.0.22
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/api/client.d.ts +14 -0
- package/dist/api/client.js +45 -0
- package/dist/bridge/client.d.ts +151 -0
- package/dist/bridge/client.js +434 -0
- package/dist/bridge/const.d.ts +23 -0
- package/dist/bridge/const.js +47 -0
- package/dist/bridge/index.d.ts +4 -0
- package/dist/bridge/index.js +23 -0
- package/dist/bridge/types.d.ts +120 -0
- package/dist/bridge/types.js +18 -0
- package/dist/bridge/utils.d.ts +64 -0
- package/dist/bridge/utils.js +131 -0
- package/dist/gen/common.d.ts +68 -0
- package/dist/gen/common.js +215 -0
- package/dist/gen/nord_pb.d.ts +3651 -0
- package/dist/gen/nord_pb.js +892 -0
- package/dist/gen/openapi.d.ts +241 -2
- package/dist/idl/bridge.d.ts +569 -0
- package/dist/idl/bridge.js +8 -0
- package/dist/idl/bridge.json +1506 -0
- package/dist/idl/index.d.ts +607 -0
- package/dist/idl/index.js +8 -0
- package/dist/nord/api/actions.d.ts +30 -72
- package/dist/nord/api/actions.js +179 -200
- package/dist/nord/api/market.d.ts +36 -0
- package/dist/nord/api/market.js +96 -0
- package/dist/nord/api/queries.d.ts +46 -0
- package/dist/nord/api/queries.js +109 -0
- package/dist/nord/client/Nord.js +3 -3
- package/dist/nord/client/NordUser.d.ts +26 -13
- package/dist/nord/client/NordUser.js +13 -10
- package/dist/types.d.ts +12 -1
- package/dist/types.js +29 -2
- package/dist/utils.d.ts +6 -20
- package/dist/utils.js +17 -35
- package/dist/websocket/NordWebSocketClient.js +2 -6
- package/package.json +26 -23
- package/src/gen/nord_pb.ts +4172 -0
- package/src/gen/openapi.ts +241 -2
- package/src/nord/api/actions.ts +249 -370
- package/src/nord/client/Nord.ts +3 -3
- package/src/nord/client/NordUser.ts +40 -19
- package/src/types.ts +32 -1
- package/src/utils.ts +24 -43
- package/src/websocket/NordWebSocketClient.ts +2 -8
package/src/nord/client/Nord.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ProtonClient } from "@n1xyz/proton";
|
|
|
2
2
|
import { Connection, PublicKey } from "@solana/web3.js";
|
|
3
3
|
import { EventEmitter } from "events";
|
|
4
4
|
import createClient, { Client, FetchOptions } from "openapi-fetch";
|
|
5
|
-
import * as proto from "../../gen/
|
|
5
|
+
import * as proto from "../../gen/nord_pb";
|
|
6
6
|
import type { paths } from "../../gen/openapi.ts";
|
|
7
7
|
import {
|
|
8
8
|
Account,
|
|
@@ -305,9 +305,9 @@ export class Nord {
|
|
|
305
305
|
actionId: x.actionId,
|
|
306
306
|
action: utils.decodeLengthDelimited(
|
|
307
307
|
Buffer.from(x.payload, "base64"),
|
|
308
|
-
proto.
|
|
308
|
+
proto.ActionSchema,
|
|
309
309
|
),
|
|
310
|
-
physicalExecTime: new Date(x.physicalTime
|
|
310
|
+
physicalExecTime: new Date(x.physicalTime),
|
|
311
311
|
}));
|
|
312
312
|
}
|
|
313
313
|
|
|
@@ -4,14 +4,19 @@ import {
|
|
|
4
4
|
TOKEN_PROGRAM_ID,
|
|
5
5
|
TOKEN_2022_PROGRAM_ID,
|
|
6
6
|
} from "@solana/spl-token";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Connection,
|
|
9
|
+
PublicKey,
|
|
10
|
+
Transaction,
|
|
11
|
+
SendOptions,
|
|
12
|
+
} from "@solana/web3.js";
|
|
8
13
|
import Decimal from "decimal.js";
|
|
9
14
|
import * as ed from "@noble/ed25519";
|
|
10
15
|
import { sha512 } from "@noble/hashes/sha512";
|
|
11
16
|
ed.etc.sha512Sync = sha512;
|
|
12
17
|
import { floatToScaledBigIntLossy } from "@n1xyz/proton";
|
|
13
|
-
import { FillMode,
|
|
14
|
-
import * as proto from "../../gen/
|
|
18
|
+
import { FillMode, Side, SPLTokenInfo, QuoteSize } from "../../types";
|
|
19
|
+
import * as proto from "../../gen/nord_pb";
|
|
15
20
|
import {
|
|
16
21
|
BigIntValue,
|
|
17
22
|
checkedFetch,
|
|
@@ -87,8 +92,8 @@ export interface PlaceOrderParams {
|
|
|
87
92
|
/** Order price */
|
|
88
93
|
price?: Decimal.Value;
|
|
89
94
|
|
|
90
|
-
/** Quote size (
|
|
91
|
-
quoteSize?:
|
|
95
|
+
/** Quote size object (requires non-zero price and size) */
|
|
96
|
+
quoteSize?: QuoteSize;
|
|
92
97
|
|
|
93
98
|
/** Account ID to place the order from */
|
|
94
99
|
accountId?: number;
|
|
@@ -142,8 +147,8 @@ export interface UserAtomicSubaction {
|
|
|
142
147
|
/** Order price */
|
|
143
148
|
price?: Decimal.Value;
|
|
144
149
|
|
|
145
|
-
/** Quote size (for market
|
|
146
|
-
quoteSize?:
|
|
150
|
+
/** Quote size object (for market-style placement) */
|
|
151
|
+
quoteSize?: QuoteSize;
|
|
147
152
|
|
|
148
153
|
/** The client order ID of the order. */
|
|
149
154
|
clientOrderId?: BigIntValue;
|
|
@@ -471,6 +476,7 @@ export class NordUser {
|
|
|
471
476
|
* @param amount - Amount to deposit
|
|
472
477
|
* @param tokenId - Token ID
|
|
473
478
|
* @param recipient - Recipient address; defaults to the user's address
|
|
479
|
+
* @param sendOptions - Send options for .sendTransaction
|
|
474
480
|
* @returns Transaction signature
|
|
475
481
|
* @throws {NordError} If required parameters are missing or operation fails
|
|
476
482
|
*/
|
|
@@ -478,10 +484,12 @@ export class NordUser {
|
|
|
478
484
|
amount,
|
|
479
485
|
tokenId,
|
|
480
486
|
recipient,
|
|
487
|
+
sendOptions,
|
|
481
488
|
}: Readonly<{
|
|
482
489
|
amount: number;
|
|
483
490
|
tokenId: number;
|
|
484
491
|
recipient?: PublicKey;
|
|
492
|
+
sendOptions?: SendOptions;
|
|
485
493
|
}>): Promise<string> {
|
|
486
494
|
try {
|
|
487
495
|
// Find the token info
|
|
@@ -502,8 +510,7 @@ export class NordUser {
|
|
|
502
510
|
sourceTokenAccount: fromAccount,
|
|
503
511
|
});
|
|
504
512
|
|
|
505
|
-
const { blockhash } =
|
|
506
|
-
await this.connection.getLatestBlockhash("confirmed");
|
|
513
|
+
const { blockhash } = await this.connection.getLatestBlockhash();
|
|
507
514
|
const tx = new Transaction();
|
|
508
515
|
|
|
509
516
|
tx.add(ix);
|
|
@@ -515,6 +522,7 @@ export class NordUser {
|
|
|
515
522
|
|
|
516
523
|
const signature = await this.connection.sendRawTransaction(
|
|
517
524
|
signedTx.serialize(),
|
|
525
|
+
sendOptions,
|
|
518
526
|
);
|
|
519
527
|
|
|
520
528
|
return signature;
|
|
@@ -657,7 +665,7 @@ export class NordUser {
|
|
|
657
665
|
* @throws {NordError} If the operation fails
|
|
658
666
|
*/
|
|
659
667
|
async refreshSession(): Promise<void> {
|
|
660
|
-
|
|
668
|
+
const result = await createSession(
|
|
661
669
|
this.nord.webServerUrl,
|
|
662
670
|
this.walletSignFn,
|
|
663
671
|
await this.nord.getTimestamp(),
|
|
@@ -667,6 +675,7 @@ export class NordUser {
|
|
|
667
675
|
sessionPubkey: this.sessionPubKey,
|
|
668
676
|
},
|
|
669
677
|
);
|
|
678
|
+
this.sessionId = result.sessionId;
|
|
670
679
|
}
|
|
671
680
|
/**
|
|
672
681
|
* Revoke a session
|
|
@@ -746,10 +755,14 @@ export class NordUser {
|
|
|
746
755
|
* Place an order on the exchange
|
|
747
756
|
*
|
|
748
757
|
* @param params - Order parameters
|
|
749
|
-
* @returns
|
|
758
|
+
* @returns Object containing actionId, orderId (if posted), fills, and clientOrderId
|
|
750
759
|
* @throws {NordError} If the operation fails
|
|
751
760
|
*/
|
|
752
|
-
async placeOrder(params: PlaceOrderParams): Promise<
|
|
761
|
+
async placeOrder(params: PlaceOrderParams): Promise<{
|
|
762
|
+
actionId: bigint;
|
|
763
|
+
orderId?: bigint;
|
|
764
|
+
fills: proto.Receipt_Trade[];
|
|
765
|
+
}> {
|
|
753
766
|
try {
|
|
754
767
|
this.checkSessionValidity();
|
|
755
768
|
const market = findMarket(this.nord.markets, params.marketId);
|
|
@@ -757,7 +770,7 @@ export class NordUser {
|
|
|
757
770
|
throw new NordError(`Market with ID ${params.marketId} not found`);
|
|
758
771
|
}
|
|
759
772
|
|
|
760
|
-
|
|
773
|
+
const result = await placeOrder(
|
|
761
774
|
this.nord.webServerUrl,
|
|
762
775
|
this.sessionSignFn,
|
|
763
776
|
await this.nord.getTimestamp(),
|
|
@@ -776,6 +789,7 @@ export class NordUser {
|
|
|
776
789
|
quoteSize: params.quoteSize,
|
|
777
790
|
},
|
|
778
791
|
);
|
|
792
|
+
return result;
|
|
779
793
|
} catch (error) {
|
|
780
794
|
throw new NordError("Failed to place order", { cause: error });
|
|
781
795
|
}
|
|
@@ -786,18 +800,22 @@ export class NordUser {
|
|
|
786
800
|
*
|
|
787
801
|
* @param orderId - Order ID to cancel
|
|
788
802
|
* @param providedAccountId - Account ID that placed the order
|
|
789
|
-
* @returns
|
|
803
|
+
* @returns Object containing actionId, cancelled orderId, and accountId
|
|
790
804
|
* @throws {NordError} If the operation fails
|
|
791
805
|
*/
|
|
792
806
|
async cancelOrder(
|
|
793
807
|
orderId: BigIntValue,
|
|
794
808
|
providedAccountId?: number,
|
|
795
|
-
): Promise<
|
|
809
|
+
): Promise<{
|
|
810
|
+
actionId: bigint;
|
|
811
|
+
orderId: bigint;
|
|
812
|
+
accountId: number;
|
|
813
|
+
}> {
|
|
796
814
|
const accountId =
|
|
797
815
|
providedAccountId != null ? providedAccountId : this.accountIds?.[0];
|
|
798
816
|
try {
|
|
799
817
|
this.checkSessionValidity();
|
|
800
|
-
|
|
818
|
+
const result = await cancelOrder(
|
|
801
819
|
this.nord.webServerUrl,
|
|
802
820
|
this.sessionSignFn,
|
|
803
821
|
await this.nord.getTimestamp(),
|
|
@@ -808,6 +826,7 @@ export class NordUser {
|
|
|
808
826
|
orderId,
|
|
809
827
|
},
|
|
810
828
|
);
|
|
829
|
+
return result;
|
|
811
830
|
} catch (error) {
|
|
812
831
|
throw new NordError(`Failed to cancel order ${orderId}`, {
|
|
813
832
|
cause: error,
|
|
@@ -860,7 +879,10 @@ export class NordUser {
|
|
|
860
879
|
async atomic(
|
|
861
880
|
userActions: UserAtomicSubaction[],
|
|
862
881
|
providedAccountId?: number,
|
|
863
|
-
): Promise<
|
|
882
|
+
): Promise<{
|
|
883
|
+
actionId: bigint;
|
|
884
|
+
results: proto.Receipt_AtomicSubactionResultKind[];
|
|
885
|
+
}> {
|
|
864
886
|
try {
|
|
865
887
|
this.checkSessionValidity();
|
|
866
888
|
|
|
@@ -889,8 +911,7 @@ export class NordUser {
|
|
|
889
911
|
priceDecimals: market.priceDecimals,
|
|
890
912
|
size: act.size,
|
|
891
913
|
price: act.price,
|
|
892
|
-
|
|
893
|
-
quoteSizePrice: undefined,
|
|
914
|
+
quoteSize: act.quoteSize,
|
|
894
915
|
clientOrderId: act.clientOrderId,
|
|
895
916
|
} as ApiAtomicSubaction;
|
|
896
917
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import * as proto from "./gen/
|
|
1
|
+
import * as proto from "./gen/nord_pb";
|
|
2
2
|
import type { components } from "./gen/openapi.ts";
|
|
3
|
+
import Decimal from "decimal.js";
|
|
4
|
+
import { toScaledU64 } from "./utils";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* The peak TPS rate is queried over the specified period.
|
|
@@ -300,3 +302,32 @@ export interface SPLTokenInfo {
|
|
|
300
302
|
tokenId: number;
|
|
301
303
|
name: string;
|
|
302
304
|
}
|
|
305
|
+
|
|
306
|
+
// Positive decimal price and size.
|
|
307
|
+
export class QuoteSize {
|
|
308
|
+
price: Decimal;
|
|
309
|
+
size: Decimal;
|
|
310
|
+
constructor(quotePrice: Decimal.Value, quoteSize: Decimal.Value) {
|
|
311
|
+
const p = new Decimal(quotePrice);
|
|
312
|
+
const s = new Decimal(quoteSize);
|
|
313
|
+
if (p.isZero() || s.isZero()) {
|
|
314
|
+
throw new Error("quotePrice and quoteSize must be non-zero");
|
|
315
|
+
}
|
|
316
|
+
this.price = p;
|
|
317
|
+
this.size = s;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
value(): Decimal {
|
|
321
|
+
return this.price.mul(this.size);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
toScaledU64(
|
|
325
|
+
marketPriceDecimals: number,
|
|
326
|
+
marketSizeDecimals: number,
|
|
327
|
+
): { price: bigint; size: bigint } {
|
|
328
|
+
return {
|
|
329
|
+
price: toScaledU64(this.price, marketPriceDecimals),
|
|
330
|
+
size: toScaledU64(this.size, marketSizeDecimals),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -4,8 +4,9 @@ import { bls12_381 as bls } from "@noble/curves/bls12-381";
|
|
|
4
4
|
import { secp256k1 as secp } from "@noble/curves/secp256k1";
|
|
5
5
|
import { sha256 } from "@noble/hashes/sha256";
|
|
6
6
|
import { KeyType, type Market, type Token } from "./types";
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
7
|
+
import { sizeDelimitedPeek } from "@bufbuild/protobuf/wire";
|
|
8
|
+
import { fromBinary, type Message } from "@bufbuild/protobuf";
|
|
9
|
+
import type { GenMessage } from "@bufbuild/protobuf/codegenv2";
|
|
9
10
|
import { ethers } from "ethers";
|
|
10
11
|
import fetch from "node-fetch";
|
|
11
12
|
import { RequestInfo, RequestInit, Response } from "node-fetch";
|
|
@@ -101,6 +102,7 @@ export function makeWalletSignFn(
|
|
|
101
102
|
signingKey.sign(ethers.hashMessage(message)).serialized;
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
// Returned numbers do fit into specified bits range, or error is thrown.
|
|
104
106
|
function makeToScaledBigUint(params: {
|
|
105
107
|
precision: number;
|
|
106
108
|
exponent: number;
|
|
@@ -176,63 +178,42 @@ export const toScaledU128 = makeToScaledBigUint({
|
|
|
176
178
|
exponent: 56,
|
|
177
179
|
});
|
|
178
180
|
|
|
179
|
-
/**
|
|
180
|
-
* Encodes any protobuf message into a length-delimited format,
|
|
181
|
-
* i.e. prefixed with its length encoded as varint
|
|
182
|
-
* @param message message object
|
|
183
|
-
* @param coder associated coder object which implements `MessageFns` interface
|
|
184
|
-
* @returns Encoded message as Uint8Array, prefixed with its length
|
|
185
|
-
*/
|
|
186
|
-
export function encodeLengthDelimited<T, M extends proto.MessageFns<T>>(
|
|
187
|
-
message: T,
|
|
188
|
-
coder: M,
|
|
189
|
-
): Uint8Array {
|
|
190
|
-
const encoded = coder.encode(message).finish();
|
|
191
|
-
if (encoded.byteLength > MAX_PAYLOAD_SIZE) {
|
|
192
|
-
throw new Error(
|
|
193
|
-
`Encoded message size (${encoded.byteLength} bytes) is greater than max payload size (${MAX_PAYLOAD_SIZE} bytes).`,
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
const encodedLength = new BinaryWriter().uint32(encoded.byteLength).finish();
|
|
197
|
-
return new Uint8Array([...encodedLength, ...encoded]);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
181
|
/**
|
|
201
182
|
* Decodes any protobuf message from a length-delimited format,
|
|
202
183
|
* i.e. prefixed with its length encoded as varint
|
|
203
184
|
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* ```
|
|
208
|
-
* const foo: proto.Bar = decodeLengthDelimited(bytes, proto.Bar);
|
|
209
|
-
* ```
|
|
210
|
-
*
|
|
211
|
-
* @param bytes Byte array with encoded message
|
|
212
|
-
* @param coder associated coder object which implements `MessageFns` interface
|
|
213
|
-
* @returns Decoded Action as Uint8Array.
|
|
185
|
+
* @param bytes Byte array with encoded message
|
|
186
|
+
* @param schema Message schema for decoding
|
|
187
|
+
* @returns Decoded message
|
|
214
188
|
*/
|
|
215
|
-
export function decodeLengthDelimited<T
|
|
189
|
+
export function decodeLengthDelimited<T extends Message>(
|
|
216
190
|
bytes: Uint8Array,
|
|
217
|
-
|
|
191
|
+
schema: GenMessage<T>,
|
|
218
192
|
): T {
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
193
|
+
// use sizeDelimitedPeek to extract the message length and offset
|
|
194
|
+
const peekResult = sizeDelimitedPeek(bytes);
|
|
195
|
+
|
|
196
|
+
if (peekResult.size === null || peekResult.offset === null) {
|
|
197
|
+
throw new Error("Failed to parse size-delimited message");
|
|
198
|
+
}
|
|
222
199
|
|
|
223
|
-
if (
|
|
200
|
+
if (peekResult.size > MAX_PAYLOAD_SIZE) {
|
|
224
201
|
throw new Error(
|
|
225
|
-
`Encoded message size (${
|
|
202
|
+
`Encoded message size (${peekResult.size} bytes) is greater than max payload size (${MAX_PAYLOAD_SIZE} bytes).`,
|
|
226
203
|
);
|
|
227
204
|
}
|
|
228
205
|
|
|
229
|
-
if (
|
|
206
|
+
if (peekResult.offset + peekResult.size > bytes.length) {
|
|
230
207
|
throw new Error(
|
|
231
|
-
`Encoded message size (${
|
|
208
|
+
`Encoded message size (${peekResult.size} bytes) is greater than remaining buffer size (${bytes.length - peekResult.offset} bytes).`,
|
|
232
209
|
);
|
|
233
210
|
}
|
|
234
211
|
|
|
235
|
-
|
|
212
|
+
// decode the message using the offset and size from peek
|
|
213
|
+
return fromBinary(
|
|
214
|
+
schema,
|
|
215
|
+
bytes.slice(peekResult.offset, peekResult.offset + peekResult.size),
|
|
216
|
+
);
|
|
236
217
|
}
|
|
237
218
|
|
|
238
219
|
export function checkPubKeyLength(keyType: KeyType, len: number): void {
|
|
@@ -27,9 +27,6 @@ type WebSocketInstance = WebSocket | BrowserWebSocket;
|
|
|
27
27
|
|
|
28
28
|
const VALID_STREAM_TYPES = ["trades", "delta", "account"];
|
|
29
29
|
|
|
30
|
-
// Constants for WebSocket readyState
|
|
31
|
-
const WS_OPEN = 1;
|
|
32
|
-
|
|
33
30
|
/**
|
|
34
31
|
* WebSocket client for Nord exchange
|
|
35
32
|
*
|
|
@@ -189,10 +186,7 @@ export class NordWebSocketClient
|
|
|
189
186
|
}
|
|
190
187
|
};
|
|
191
188
|
|
|
192
|
-
(this.ws as BrowserWebSocket).onclose = (
|
|
193
|
-
const reason =
|
|
194
|
-
event && event.reason ? ` Reason: ${event.reason}` : "";
|
|
195
|
-
const code = event && event.code ? ` Code: ${event.code}` : "";
|
|
189
|
+
(this.ws as BrowserWebSocket).onclose = (_event: any) => {
|
|
196
190
|
this.emit("disconnected");
|
|
197
191
|
this.reconnect();
|
|
198
192
|
};
|
|
@@ -227,7 +221,7 @@ export class NordWebSocketClient
|
|
|
227
221
|
}
|
|
228
222
|
});
|
|
229
223
|
|
|
230
|
-
nodeWs.on("close", (
|
|
224
|
+
nodeWs.on("close", (_code: number, _reason: string) => {
|
|
231
225
|
this.emit("disconnected");
|
|
232
226
|
if (this.pingInterval) {
|
|
233
227
|
clearInterval(this.pingInterval);
|