@n1xyz/nord-ts 0.0.12 → 0.0.15
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/.prettierignore +1 -0
- package/README.md +9 -6
- package/dist/bridge/client.d.ts +8 -7
- package/dist/bridge/client.js +38 -37
- package/dist/bridge/index.d.ts +1 -2
- package/dist/bridge/index.js +2 -2
- package/dist/bridge/types.d.ts +5 -3
- package/dist/bridge/types.js +3 -1
- package/dist/gen/nord.d.ts +66 -5
- package/dist/gen/nord.js +449 -22
- package/dist/idl/bridge.json +1493 -0
- package/dist/idl/index.d.ts +585 -0
- package/dist/idl/index.js +8 -0
- package/dist/nord/api/core.d.ts +8 -8
- package/dist/nord/api/core.js +58 -15
- package/dist/nord/api/metrics.js +7 -4
- package/dist/nord/client/Nord.d.ts +57 -72
- package/dist/nord/client/Nord.js +134 -125
- package/dist/nord/client/NordUser.d.ts +0 -17
- package/dist/nord/client/NordUser.js +4 -35
- package/dist/types.d.ts +38 -15
- package/dist/types.js +2 -2
- package/dist/websocket/NordWebSocketClient.d.ts +0 -3
- package/dist/websocket/NordWebSocketClient.js +10 -13
- package/dist/websocket/events.d.ts +2 -2
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/Nord.html +99 -100
- package/docs/classes/NordError.html +6 -6
- package/docs/classes/NordUser.html +48 -53
- package/docs/classes/NordWebSocketClient.html +6 -11
- package/docs/classes/SolanaBridgeClient.html +23 -23
- package/docs/classes/Subscriber.html +3 -3
- package/docs/enums/FillMode.html +2 -2
- package/docs/enums/KeyType.html +2 -2
- package/docs/enums/MetricPeriod.html +2 -2
- package/docs/enums/PdaSeedType.html +2 -2
- package/docs/enums/PeakTpsPeriodUnit.html +2 -2
- package/docs/enums/Side.html +2 -2
- package/docs/enums/WebSocketMessageType.html +3 -3
- package/docs/functions/actionQueryRollman.html +1 -1
- package/docs/functions/actionsQueryRollman.html +1 -1
- package/docs/functions/aggregateMetrics-1.html +1 -1
- package/docs/functions/assert.html +1 -1
- package/docs/functions/bigIntToProtoU128.html +1 -1
- package/docs/functions/bridgeToBN.html +1 -1
- package/docs/functions/bufferToHex.html +1 -1
- package/docs/functions/cancelOrder.html +1 -1
- package/docs/functions/checkPubKeyLength.html +1 -1
- package/docs/functions/checkedFetch.html +1 -1
- package/docs/functions/createSession.html +1 -1
- package/docs/functions/decodeLengthDelimited.html +1 -1
- package/docs/functions/encodeLengthDelimited.html +1 -1
- package/docs/functions/fillModeToProtoFillMode.html +1 -1
- package/docs/functions/findMarket.html +1 -1
- package/docs/functions/findPda.html +1 -1
- package/docs/functions/findToken.html +1 -1
- package/docs/functions/fromBN.html +1 -1
- package/docs/functions/getAccount.html +1 -1
- package/docs/functions/getActionNonce.html +1 -1
- package/docs/functions/getCurrentTps.html +1 -1
- package/docs/functions/getInfo.html +1 -1
- package/docs/functions/{queryLastNBlocks.html → getLastActionId.html} +3 -3
- package/docs/functions/getMedianLatency.html +1 -1
- package/docs/functions/getOrderbook.html +1 -1
- package/docs/functions/getPeakTps.html +1 -1
- package/docs/functions/getTimestamp.html +1 -1
- package/docs/functions/getTotalTransactions.html +1 -1
- package/docs/functions/getTrades.html +1 -1
- package/docs/functions/getUserAccountIds.html +1 -1
- package/docs/functions/hexToBuffer.html +1 -1
- package/docs/functions/initWebSocketClient.html +9 -9
- package/docs/functions/keypairFromPrivateKey.html +1 -1
- package/docs/functions/makeSigningFunction.html +1 -1
- package/docs/functions/makeWalletSignFn.html +1 -1
- package/docs/functions/marketsStats.html +1 -1
- package/docs/functions/optExpect.html +1 -1
- package/docs/functions/optMap.html +1 -1
- package/docs/functions/optUnwrap.html +1 -1
- package/docs/functions/panic.html +1 -1
- package/docs/functions/placeOrder.html +1 -1
- package/docs/functions/queryAction.html +1 -1
- package/docs/functions/queryPrometheus.html +1 -1
- package/docs/functions/queryRecentActions.html +4 -3
- package/docs/functions/revokeSession.html +1 -1
- package/docs/functions/shortenPublicKey.html +1 -1
- package/docs/functions/signAction.html +1 -1
- package/docs/functions/toBN.html +1 -1
- package/docs/functions/toScaledU128.html +1 -1
- package/docs/functions/toScaledU64.html +1 -1
- package/docs/functions/transfer.html +1 -1
- package/docs/functions/withdraw.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +2 -2
- package/docs/interfaces/Account.html +2 -2
- package/docs/interfaces/ActionInfo.html +2 -2
- package/docs/interfaces/ActionNonceResponse.html +2 -2
- package/docs/interfaces/ActionQuery.html +2 -2
- package/docs/interfaces/ActionResponse.html +2 -2
- package/docs/interfaces/ActionsExtendedInfo.html +2 -2
- package/docs/interfaces/ActionsQuery.html +2 -2
- package/docs/interfaces/ActionsResponse.html +2 -2
- package/docs/interfaces/AggregateMetrics.html +2 -2
- package/docs/interfaces/BlockFacts.html +5 -5
- package/docs/interfaces/BlockQuery.html +2 -2
- package/docs/interfaces/BlockResponse.html +2 -2
- package/docs/interfaces/BlockSummary.html +2 -2
- package/docs/interfaces/BlockSummaryResponse.html +2 -2
- package/docs/interfaces/DeltaEvent.html +2 -2
- package/docs/interfaces/DepositSplParams.html +5 -5
- package/docs/interfaces/Info.html +2 -2
- package/docs/interfaces/Market.html +2 -2
- package/docs/interfaces/MarketStats.html +2 -2
- package/docs/interfaces/MarketsStatsResponse.html +2 -2
- package/docs/interfaces/NordConfig.html +19 -9
- package/docs/interfaces/NordWebSocketClientEvents.html +2 -2
- package/docs/interfaces/NordWebSocketEvents.html +3 -3
- package/docs/interfaces/Order.html +2 -2
- package/docs/interfaces/OrderInfo.html +2 -2
- package/docs/interfaces/OrderbookEntry.html +2 -2
- package/docs/interfaces/OrderbookQuery.html +2 -2
- package/docs/interfaces/OrderbookResponse.html +2 -2
- package/docs/interfaces/OrderbookSubscription.html +5 -5
- package/docs/interfaces/PerpMarketStats.html +2 -2
- package/docs/interfaces/RollmanActionExtendedInfo.html +2 -2
- package/docs/interfaces/RollmanActionInfo.html +2 -2
- package/docs/interfaces/RollmanActionResponse.html +2 -2
- package/docs/interfaces/RollmanActionsResponse.html +2 -2
- package/docs/interfaces/RollmanBlockResponse.html +2 -2
- package/docs/interfaces/SPLTokenInfo.html +5 -5
- package/docs/interfaces/SolanaBridgeConfig.html +5 -5
- package/docs/interfaces/StateFacts.html +5 -5
- package/docs/interfaces/SubscriberConfig.html +2 -2
- package/docs/interfaces/TimestampResponse.html +2 -2
- package/docs/interfaces/Token.html +2 -2
- package/docs/interfaces/TokenInfo.html +3 -2
- package/docs/interfaces/Trade.html +2 -2
- package/docs/interfaces/TradeSubscription.html +5 -5
- package/docs/interfaces/Trades.html +2 -2
- package/docs/interfaces/TradesQuery.html +2 -2
- package/docs/interfaces/TradesResponse.html +2 -2
- package/docs/interfaces/TransferParams.html +4 -4
- package/docs/interfaces/UserAccountIdsQuery.html +2 -2
- package/docs/interfaces/UserAccountIdsResponse.html +2 -2
- package/docs/interfaces/WebSocketAccountUpdate.html +6 -0
- package/docs/interfaces/WebSocketDeltaUpdate.html +3 -3
- package/docs/interfaces/WebSocketSubscription.html +3 -3
- package/docs/interfaces/WebSocketTradeUpdate.html +3 -3
- package/docs/interfaces/WithdrawalClaim.html +7 -7
- package/docs/interfaces/WithdrawalParams.html +4 -4
- package/docs/modules.html +1 -1
- package/docs/types/BigIntValue.html +1 -1
- package/docs/types/SubscriptionPattern.html +4 -0
- package/docs/types/SubscriptionType.html +2 -0
- package/docs/types/WebSocketMessage.html +1 -1
- package/docs/variables/DEBUG_KEYS.html +1 -1
- package/docs/variables/DEFAULT_FUNDING_AMOUNTS.html +1 -1
- package/docs/variables/DEV_TOKEN_INFOS.html +1 -1
- package/docs/variables/DEV_URL.html +1 -1
- package/docs/variables/MAX_BUFFER_LEN.html +1 -1
- package/docs/variables/SESSION_TTL.html +1 -1
- package/docs/variables/WEBSERVER_DEV_URL.html +1 -1
- package/docs/variables/ZERO_DECIMAL.html +1 -1
- package/docs/variables/_private.html +1 -1
- package/idl-generate.sh +4 -0
- package/package.json +4 -3
- package/src/bridge/client.ts +54 -43
- package/src/bridge/index.ts +1 -2
- package/src/bridge/types.ts +5 -3
- package/src/gen/nord.ts +518 -19
- package/src/idl/bridge.json +1493 -0
- package/src/idl/index.ts +2 -0
- package/src/nord/api/core.ts +70 -20
- package/src/nord/api/metrics.ts +28 -20
- package/src/nord/api/queries.ts +7 -11
- package/src/nord/client/Nord.ts +174 -168
- package/src/nord/client/NordUser.ts +32 -98
- package/src/types.ts +40 -15
- package/src/websocket/NordWebSocketClient.ts +11 -14
- package/src/websocket/events.ts +2 -2
- package/docs/functions/blockQueryRollman.html +0 -6
- package/docs/functions/blockSummaryQueryRollman.html +0 -6
- package/docs/functions/queryBlock.html +0 -6
- package/docs/functions/queryRecentBlocks.html +0 -6
- package/docs/interfaces/WebSocketUserUpdate.html +0 -6
- package/src/idl/bridge.ts +0 -930
- package/test.ts +0 -107
package/src/nord/client/Nord.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
2
3
|
import {
|
|
3
4
|
Account,
|
|
4
5
|
ActionQuery,
|
|
@@ -14,11 +15,12 @@ import {
|
|
|
14
15
|
PeakTpsPeriodUnit,
|
|
15
16
|
RollmanActionResponse,
|
|
16
17
|
RollmanActionsResponse,
|
|
18
|
+
SubscriptionPattern,
|
|
17
19
|
Token,
|
|
18
20
|
TradesQuery,
|
|
19
21
|
TradesResponse,
|
|
20
22
|
UserAccountIdsQuery,
|
|
21
|
-
UserAccountIdsResponse
|
|
23
|
+
UserAccountIdsResponse,
|
|
22
24
|
} from "../../types";
|
|
23
25
|
import { NordWebSocketClient } from "../../websocket/index";
|
|
24
26
|
import * as core from "../api/core";
|
|
@@ -35,6 +37,18 @@ export interface UserSubscription extends EventEmitter {
|
|
|
35
37
|
close: () => void;
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
/**
|
|
41
|
+
* WebSocket subscription options interface
|
|
42
|
+
*/
|
|
43
|
+
export interface WebSocketSubscriptionOptions {
|
|
44
|
+
/** Market symbols to subscribe to for trade updates */
|
|
45
|
+
trades?: string[];
|
|
46
|
+
/** Market symbols to subscribe to for orderbook delta updates */
|
|
47
|
+
deltas?: string[];
|
|
48
|
+
/** Account IDs to subscribe to for account updates */
|
|
49
|
+
accounts?: number[];
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
/**
|
|
39
53
|
* Main Nord client class for interacting with the Nord API
|
|
40
54
|
*/
|
|
@@ -42,8 +56,11 @@ export class Nord {
|
|
|
42
56
|
/** Base URL for the Nord web server */
|
|
43
57
|
public readonly webServerUrl: string;
|
|
44
58
|
|
|
45
|
-
/**
|
|
46
|
-
public readonly
|
|
59
|
+
/** Bridge verification key */
|
|
60
|
+
public readonly bridgeVk: string;
|
|
61
|
+
|
|
62
|
+
/** Optional Solana program ID (will be derived from bridgeVk) */
|
|
63
|
+
private _solanaProgramId!: string;
|
|
47
64
|
|
|
48
65
|
/** Solana RPC URL */
|
|
49
66
|
public readonly solanaUrl: string;
|
|
@@ -57,113 +74,92 @@ export class Nord {
|
|
|
57
74
|
/** Map of symbol to market_id */
|
|
58
75
|
private symbolToMarketId: Map<string, number> = new Map();
|
|
59
76
|
|
|
60
|
-
/**
|
|
61
|
-
* WebSocket client for trades
|
|
62
|
-
* @private
|
|
63
|
-
*/
|
|
64
|
-
private tradesWs: NordWebSocketClient | null = null;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* WebSocket client for orderbook deltas
|
|
68
|
-
* @private
|
|
69
|
-
*/
|
|
70
|
-
private deltasWs: NordWebSocketClient | null = null;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* WebSocket client for user updates
|
|
74
|
-
* @private
|
|
75
|
-
*/
|
|
76
|
-
private userWs: NordWebSocketClient | null = null;
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Initial subscriptions for the trades WebSocket
|
|
80
|
-
* @private
|
|
81
|
-
*/
|
|
82
|
-
private tradesSubscriptions?: string[];
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Initial subscriptions for the deltas WebSocket
|
|
86
|
-
* @private
|
|
87
|
-
*/
|
|
88
|
-
private deltasSubscriptions?: string[];
|
|
89
|
-
|
|
90
77
|
/**
|
|
91
78
|
* Create a new Nord client
|
|
92
79
|
*
|
|
93
80
|
* @param config - Configuration options for the Nord client
|
|
94
81
|
* @param config.webServerUrl - Base URL for the Nord web server
|
|
95
|
-
* @param config.
|
|
82
|
+
* @param config.bridgeVk - Bridge verification key
|
|
96
83
|
* @param config.solanaUrl - Solana cluster URL
|
|
97
|
-
* @param config.initWebSockets - Whether to initialize WebSockets on creation, defaults to true
|
|
98
|
-
* @param config.tradesSubscriptions - Optional array of trades subscriptions to initialize with (e.g., ["trades@BTCUSDC"])
|
|
99
|
-
* @param config.deltasSubscriptions - Optional array of deltas subscriptions to initialize with (e.g., ["deltas@BTCUSDC"])
|
|
100
84
|
* @throws {Error} If required configuration is missing
|
|
101
85
|
*/
|
|
102
|
-
constructor({
|
|
103
|
-
webServerUrl
|
|
104
|
-
solanaProgramId,
|
|
105
|
-
solanaUrl,
|
|
106
|
-
initWebSockets = true,
|
|
107
|
-
tradesSubscriptions,
|
|
108
|
-
deltasSubscriptions,
|
|
109
|
-
}: NordConfig) {
|
|
110
|
-
if (!webServerUrl) {
|
|
86
|
+
constructor(config: NordConfig) {
|
|
87
|
+
if (!config.webServerUrl) {
|
|
111
88
|
throw new NordError("webServerUrl is required");
|
|
112
89
|
}
|
|
113
90
|
|
|
114
|
-
if (!
|
|
115
|
-
throw new NordError("
|
|
91
|
+
if (!config.bridgeVk) {
|
|
92
|
+
throw new NordError("bridgeVk is required");
|
|
116
93
|
}
|
|
117
94
|
|
|
118
|
-
if (!solanaUrl) {
|
|
95
|
+
if (!config.solanaUrl) {
|
|
119
96
|
throw new NordError("solanaUrl is required");
|
|
120
97
|
}
|
|
121
98
|
|
|
122
|
-
this.webServerUrl = webServerUrl;
|
|
123
|
-
this.
|
|
124
|
-
this.solanaUrl = solanaUrl;
|
|
125
|
-
|
|
126
|
-
// Store subscription parameters
|
|
127
|
-
this.tradesSubscriptions = tradesSubscriptions;
|
|
128
|
-
this.deltasSubscriptions = deltasSubscriptions;
|
|
129
|
-
|
|
130
|
-
// Initialize WebSocket clients only if initWebSockets is true
|
|
131
|
-
if (initWebSockets) {
|
|
132
|
-
this.initializeWebSockets();
|
|
133
|
-
}
|
|
99
|
+
this.webServerUrl = config.webServerUrl;
|
|
100
|
+
this.bridgeVk = config.bridgeVk;
|
|
101
|
+
this.solanaUrl = config.solanaUrl;
|
|
134
102
|
}
|
|
135
103
|
|
|
136
104
|
/**
|
|
137
|
-
*
|
|
105
|
+
* Create a WebSocket client with specific subscriptions
|
|
138
106
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
107
|
+
* @param options - Subscription options that specify which data streams to subscribe to
|
|
108
|
+
* @returns A new WebSocket client with the requested subscriptions
|
|
109
|
+
* @throws {NordError} If invalid subscription options are provided
|
|
141
110
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
111
|
+
* @example
|
|
112
|
+
* // Create a client for trades and deltas from one market and an account
|
|
113
|
+
* const wsClient = nord.createWebSocketClient({
|
|
114
|
+
* trades: ["BTCUSDC"],
|
|
115
|
+
* deltas: ["BTCUSDC"],
|
|
116
|
+
* accounts: [123]
|
|
117
|
+
* });
|
|
144
118
|
*
|
|
145
|
-
* @
|
|
146
|
-
*
|
|
119
|
+
* @example
|
|
120
|
+
* // Create a client for trades from multiple markets
|
|
121
|
+
* const tradesClient = nord.createWebSocketClient({
|
|
122
|
+
* trades: ["BTCUSDC", "ETHUSDC"]
|
|
123
|
+
* });
|
|
147
124
|
*/
|
|
148
|
-
public
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
125
|
+
public createWebSocketClient(
|
|
126
|
+
options: WebSocketSubscriptionOptions,
|
|
127
|
+
): NordWebSocketClient {
|
|
128
|
+
const subscriptions: SubscriptionPattern[] = [];
|
|
129
|
+
|
|
130
|
+
// Add trade subscriptions
|
|
131
|
+
if (options.trades && options.trades.length > 0) {
|
|
132
|
+
options.trades.forEach((symbol) => {
|
|
133
|
+
subscriptions.push(`trades@${symbol}` as SubscriptionPattern);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Add delta subscriptions
|
|
138
|
+
if (options.deltas && options.deltas.length > 0) {
|
|
139
|
+
options.deltas.forEach((symbol) => {
|
|
140
|
+
subscriptions.push(`deltas@${symbol}` as SubscriptionPattern);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Add account subscriptions
|
|
145
|
+
if (options.accounts && options.accounts.length > 0) {
|
|
146
|
+
options.accounts.forEach((accountId) => {
|
|
147
|
+
if (isNaN(accountId) || accountId <= 0) {
|
|
148
|
+
throw new NordError(
|
|
149
|
+
`Invalid account ID: ${accountId}. Must be a positive number.`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
subscriptions.push(`account@${accountId}` as SubscriptionPattern);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Validate that at least one subscription was provided
|
|
157
|
+
if (subscriptions.length === 0) {
|
|
158
|
+
throw new NordError("At least one subscription must be provided");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Create and return a new WebSocket client
|
|
162
|
+
return core.initWebSocketClient(this.webServerUrl, subscriptions);
|
|
167
163
|
}
|
|
168
164
|
|
|
169
165
|
/**
|
|
@@ -212,20 +208,49 @@ export class Nord {
|
|
|
212
208
|
*
|
|
213
209
|
* @param nordConfig - Configuration options for the Nord client
|
|
214
210
|
* @param nordConfig.webServerUrl - Base URL for the Nord web server
|
|
215
|
-
* @param nordConfig.
|
|
211
|
+
* @param nordConfig.bridgeVk - Bridge verification key
|
|
216
212
|
* @param nordConfig.solanaUrl - Solana cluster URL
|
|
217
|
-
* @param nordConfig.initWebSockets - Whether to initialize WebSockets on creation, defaults to true
|
|
218
|
-
* @param nordConfig.tradesSubscriptions - Optional array of trades subscriptions (e.g., ["trades@BTCUSDC"])
|
|
219
|
-
* @param nordConfig.deltasSubscriptions - Optional array of deltas subscriptions (e.g., ["deltas@BTCUSDC"])
|
|
220
213
|
* @returns Initialized Nord client
|
|
221
214
|
* @throws {NordError} If initialization fails
|
|
222
215
|
*/
|
|
223
216
|
public static async initNord(nordConfig: NordConfig): Promise<Nord> {
|
|
224
217
|
const nord = new Nord(nordConfig);
|
|
225
|
-
await nord.
|
|
218
|
+
await nord.init();
|
|
226
219
|
return nord;
|
|
227
220
|
}
|
|
228
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Initialize the Nord client by deriving program ID and fetching info
|
|
224
|
+
* @private
|
|
225
|
+
*/
|
|
226
|
+
private async init(): Promise<void> {
|
|
227
|
+
const connection = new Connection(this.solanaUrl);
|
|
228
|
+
const bridgeVkPubkey = new PublicKey(this.bridgeVk);
|
|
229
|
+
|
|
230
|
+
const bridgeAccount = await connection.getAccountInfo(bridgeVkPubkey);
|
|
231
|
+
if (!bridgeAccount) {
|
|
232
|
+
throw new NordError(`Bridge account ${this.bridgeVk} not found`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this._solanaProgramId = bridgeAccount.owner.toString();
|
|
236
|
+
await this.fetchNordInfo();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get the Solana program ID derived from bridge VK
|
|
241
|
+
*
|
|
242
|
+
* @returns Program ID string
|
|
243
|
+
* @throws {NordError} If program ID hasn't been initialized
|
|
244
|
+
*/
|
|
245
|
+
public getSolanaProgramId(): string {
|
|
246
|
+
if (!this._solanaProgramId) {
|
|
247
|
+
throw new NordError(
|
|
248
|
+
"Solana program ID not initialized. Have you called Nord.initNord()?",
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return this._solanaProgramId;
|
|
252
|
+
}
|
|
253
|
+
|
|
229
254
|
/**
|
|
230
255
|
* Get market statistics
|
|
231
256
|
*
|
|
@@ -268,7 +293,7 @@ export class Nord {
|
|
|
268
293
|
async getLastActionId(): Promise<number> {
|
|
269
294
|
return queries.getLastActionId(this.webServerUrl);
|
|
270
295
|
}
|
|
271
|
-
|
|
296
|
+
|
|
272
297
|
/**
|
|
273
298
|
* Fetch aggregate metrics from the Nord API
|
|
274
299
|
*
|
|
@@ -364,68 +389,22 @@ export class Nord {
|
|
|
364
389
|
return metrics.queryPrometheus(this.webServerUrl, params);
|
|
365
390
|
}
|
|
366
391
|
|
|
367
|
-
/**
|
|
368
|
-
* Get the trades WebSocket client (default)
|
|
369
|
-
* If not already initialized, it will be created
|
|
370
|
-
*
|
|
371
|
-
* @returns WebSocket client for trades
|
|
372
|
-
*/
|
|
373
|
-
public getWebSocketClient(): NordWebSocketClient {
|
|
374
|
-
if (!this.tradesWs) {
|
|
375
|
-
this.initializeWebSockets();
|
|
376
|
-
}
|
|
377
|
-
return this.tradesWs!;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* Get the trades WebSocket client
|
|
382
|
-
* If not already initialized, it will be created
|
|
383
|
-
*
|
|
384
|
-
* @returns WebSocket client for trades
|
|
385
|
-
*/
|
|
386
|
-
public getTradesWebSocketClient(): NordWebSocketClient {
|
|
387
|
-
if (!this.tradesWs) {
|
|
388
|
-
this.initializeWebSockets();
|
|
389
|
-
}
|
|
390
|
-
return this.tradesWs!;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Get the deltas WebSocket client
|
|
395
|
-
* If not already initialized, it will be created
|
|
396
|
-
*
|
|
397
|
-
* @returns WebSocket client for orderbook deltas
|
|
398
|
-
*/
|
|
399
|
-
public getDeltasWebSocketClient(): NordWebSocketClient {
|
|
400
|
-
if (!this.deltasWs) {
|
|
401
|
-
this.initializeWebSockets();
|
|
402
|
-
}
|
|
403
|
-
return this.deltasWs!;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Get the user WebSocket client
|
|
408
|
-
* If not already initialized, it will be created
|
|
409
|
-
*
|
|
410
|
-
* @returns WebSocket client for user updates
|
|
411
|
-
*/
|
|
412
|
-
public getUserWebSocketClient(): NordWebSocketClient {
|
|
413
|
-
if (!this.userWs) {
|
|
414
|
-
// Initialize user WebSocket client on demand
|
|
415
|
-
this.userWs = core.initWebSocketClient(this.webServerUrl, "user");
|
|
416
|
-
return this.userWs;
|
|
417
|
-
}
|
|
418
|
-
return this.userWs;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
392
|
/**
|
|
422
393
|
* Subscribe to orderbook updates for a market
|
|
423
394
|
*
|
|
424
395
|
* @param symbol - Market symbol
|
|
425
396
|
* @returns Orderbook subscription
|
|
397
|
+
* @throws {NordError} If symbol is invalid
|
|
426
398
|
*/
|
|
427
399
|
public subscribeOrderbook(symbol: string): OrderbookSubscription {
|
|
400
|
+
if (!symbol || typeof symbol !== "string") {
|
|
401
|
+
throw new NordError("Invalid market symbol");
|
|
402
|
+
}
|
|
403
|
+
|
|
428
404
|
const subscription = new EventEmitter() as OrderbookSubscription;
|
|
405
|
+
const wsClient = this.createWebSocketClient({
|
|
406
|
+
deltas: [symbol],
|
|
407
|
+
});
|
|
429
408
|
|
|
430
409
|
const handleDelta = (update: {
|
|
431
410
|
symbol: string;
|
|
@@ -439,17 +418,11 @@ export class Nord {
|
|
|
439
418
|
subscription.emit("message", update);
|
|
440
419
|
};
|
|
441
420
|
|
|
442
|
-
|
|
443
|
-
if (!this.deltasWs) {
|
|
444
|
-
this.initializeWebSockets();
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
this.deltasWs!.on("delta", handleDelta);
|
|
448
|
-
this.deltasWs!.subscribe([`deltas@${symbol}`]);
|
|
421
|
+
wsClient.on("delta", handleDelta);
|
|
449
422
|
|
|
450
423
|
subscription.close = () => {
|
|
451
|
-
|
|
452
|
-
|
|
424
|
+
wsClient.unsubscribe([`deltas@${symbol}`]);
|
|
425
|
+
wsClient.removeListener("delta", handleDelta);
|
|
453
426
|
subscription.removeAllListeners();
|
|
454
427
|
};
|
|
455
428
|
|
|
@@ -461,9 +434,17 @@ export class Nord {
|
|
|
461
434
|
*
|
|
462
435
|
* @param symbol - Market symbol
|
|
463
436
|
* @returns Trade subscription
|
|
437
|
+
* @throws {NordError} If symbol is invalid
|
|
464
438
|
*/
|
|
465
439
|
public subscribeTrades(symbol: string): TradeSubscription {
|
|
440
|
+
if (!symbol || typeof symbol !== "string") {
|
|
441
|
+
throw new NordError("Invalid market symbol");
|
|
442
|
+
}
|
|
443
|
+
|
|
466
444
|
const subscription = new EventEmitter() as TradeSubscription;
|
|
445
|
+
const wsClient = this.createWebSocketClient({
|
|
446
|
+
trades: [symbol],
|
|
447
|
+
});
|
|
467
448
|
|
|
468
449
|
const handleTrade = (update: {
|
|
469
450
|
symbol: string;
|
|
@@ -481,17 +462,47 @@ export class Nord {
|
|
|
481
462
|
subscription.emit("message", update);
|
|
482
463
|
};
|
|
483
464
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
465
|
+
wsClient.on("trades", handleTrade);
|
|
466
|
+
|
|
467
|
+
subscription.close = () => {
|
|
468
|
+
wsClient.unsubscribe([`trades@${symbol}`]);
|
|
469
|
+
wsClient.removeListener("trades", handleTrade);
|
|
470
|
+
subscription.removeAllListeners();
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
return subscription;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Subscribe to account updates
|
|
478
|
+
*
|
|
479
|
+
* @param accountId - Account ID to subscribe to
|
|
480
|
+
* @returns User subscription
|
|
481
|
+
* @throws {NordError} If accountId is invalid
|
|
482
|
+
*/
|
|
483
|
+
public subscribeAccount(accountId: number): UserSubscription {
|
|
484
|
+
if (isNaN(accountId) || accountId <= 0) {
|
|
485
|
+
throw new NordError("Invalid account ID");
|
|
487
486
|
}
|
|
488
487
|
|
|
489
|
-
|
|
490
|
-
this.
|
|
488
|
+
const subscription = new EventEmitter() as UserSubscription;
|
|
489
|
+
const wsClient = this.createWebSocketClient({
|
|
490
|
+
accounts: [accountId],
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const handleAccountUpdate = (update: any) => {
|
|
494
|
+
if (update.account_id !== accountId) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
subscription.emit("message", update);
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
wsClient.on("account", handleAccountUpdate);
|
|
491
502
|
|
|
492
503
|
subscription.close = () => {
|
|
493
|
-
|
|
494
|
-
|
|
504
|
+
wsClient.unsubscribe([`account@${accountId}`]);
|
|
505
|
+
wsClient.removeListener("account", handleAccountUpdate);
|
|
495
506
|
subscription.removeAllListeners();
|
|
496
507
|
};
|
|
497
508
|
|
|
@@ -547,11 +558,6 @@ export class Nord {
|
|
|
547
558
|
query = { market_id: marketId };
|
|
548
559
|
}
|
|
549
560
|
|
|
550
|
-
// Ensure market_id is provided
|
|
551
|
-
if (query.market_id === undefined) {
|
|
552
|
-
throw new NordError("market_id is required for orderbook query");
|
|
553
|
-
}
|
|
554
|
-
|
|
555
561
|
return market.getOrderbook(this.webServerUrl, query);
|
|
556
562
|
}
|
|
557
563
|
|
|
@@ -111,17 +111,6 @@ export interface TransferParams {
|
|
|
111
111
|
toAccountId: number;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
/**
|
|
115
|
-
* Parameters for creating a new account
|
|
116
|
-
*/
|
|
117
|
-
export interface CreateAccountParams {
|
|
118
|
-
/** Token ID for initial funding */
|
|
119
|
-
tokenId: number;
|
|
120
|
-
|
|
121
|
-
/** Initial funding amount */
|
|
122
|
-
amount: Decimal.Value;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
114
|
/**
|
|
126
115
|
* User class for interacting with the Nord protocol
|
|
127
116
|
*/
|
|
@@ -311,7 +300,7 @@ export class NordUser {
|
|
|
311
300
|
if (
|
|
312
301
|
!this.getSolanaPublicKey() ||
|
|
313
302
|
!this.connection ||
|
|
314
|
-
!this.nord.
|
|
303
|
+
!this.nord.getSolanaProgramId()
|
|
315
304
|
) {
|
|
316
305
|
throw new NordError(
|
|
317
306
|
"Solana public key, connection, and program ID are required to initialize bridge client",
|
|
@@ -342,9 +331,10 @@ export class NordUser {
|
|
|
342
331
|
this.bridgeClient = new SolanaBridgeClient(
|
|
343
332
|
{
|
|
344
333
|
rpcUrl: this.connection.rpcEndpoint,
|
|
345
|
-
programId: this.nord.
|
|
334
|
+
programId: this.nord.getSolanaProgramId(),
|
|
346
335
|
commitment: "confirmed",
|
|
347
336
|
tokenInfos: this.splTokenInfos,
|
|
337
|
+
bridgeVk: this.nord.bridgeVk,
|
|
348
338
|
},
|
|
349
339
|
wallet,
|
|
350
340
|
);
|
|
@@ -473,16 +463,6 @@ export class NordUser {
|
|
|
473
463
|
const mint = new PublicKey(tokenInfo.mint);
|
|
474
464
|
// Get the user's token account
|
|
475
465
|
const fromAccount = await this.getAssociatedTokenAccount(mint);
|
|
476
|
-
|
|
477
|
-
// Get the bridge's token account
|
|
478
|
-
const [authority] = await this.bridgeClient.findAuthorityPda();
|
|
479
|
-
const toAccount = await getAssociatedTokenAddress(
|
|
480
|
-
mint,
|
|
481
|
-
authority,
|
|
482
|
-
true,
|
|
483
|
-
TOKEN_2022_PROGRAM_ID,
|
|
484
|
-
);
|
|
485
|
-
|
|
486
466
|
// Convert amount to BN with proper decimals
|
|
487
467
|
const amountBN = utilsToBN(amount, tokenInfo.precision);
|
|
488
468
|
|
|
@@ -491,7 +471,6 @@ export class NordUser {
|
|
|
491
471
|
amount: amountBN,
|
|
492
472
|
mint,
|
|
493
473
|
fromAccount,
|
|
494
|
-
toAccount,
|
|
495
474
|
});
|
|
496
475
|
} catch (error) {
|
|
497
476
|
throw new NordError(
|
|
@@ -599,23 +578,23 @@ export class NordUser {
|
|
|
599
578
|
* @throws {NordError} If the operation fails
|
|
600
579
|
*/
|
|
601
580
|
async fetchInfo(): Promise<void> {
|
|
602
|
-
|
|
581
|
+
type OpenOrder = {
|
|
603
582
|
orderId: number;
|
|
604
|
-
size: number;
|
|
605
|
-
price: number;
|
|
606
583
|
marketId: number;
|
|
607
584
|
side: "ask" | "bid";
|
|
608
|
-
|
|
585
|
+
size: number;
|
|
586
|
+
price: number;
|
|
587
|
+
originalOrderSize: number;
|
|
609
588
|
clientOrderId?: number | null;
|
|
610
|
-
}
|
|
589
|
+
};
|
|
611
590
|
|
|
612
|
-
|
|
591
|
+
type Balance = {
|
|
613
592
|
tokenId: number;
|
|
614
593
|
token: string;
|
|
615
594
|
amount: number;
|
|
616
|
-
}
|
|
595
|
+
};
|
|
617
596
|
|
|
618
|
-
|
|
597
|
+
type Position = {
|
|
619
598
|
marketId: number;
|
|
620
599
|
openOrders: number;
|
|
621
600
|
perp?: {
|
|
@@ -627,9 +606,9 @@ export class NordUser {
|
|
|
627
606
|
isLong: boolean;
|
|
628
607
|
};
|
|
629
608
|
actionId: number;
|
|
630
|
-
}
|
|
609
|
+
};
|
|
631
610
|
|
|
632
|
-
|
|
611
|
+
type Margins = {
|
|
633
612
|
omf: number;
|
|
634
613
|
mf: number;
|
|
635
614
|
imf: number;
|
|
@@ -638,32 +617,31 @@ export class NordUser {
|
|
|
638
617
|
pon: number;
|
|
639
618
|
pn: number;
|
|
640
619
|
bankruptcy: boolean;
|
|
641
|
-
}
|
|
620
|
+
};
|
|
642
621
|
|
|
643
|
-
|
|
622
|
+
type Account = {
|
|
644
623
|
updateId: number;
|
|
645
|
-
orders:
|
|
624
|
+
orders: OpenOrder[];
|
|
646
625
|
positions: Position[];
|
|
647
626
|
balances: Balance[];
|
|
648
627
|
margins: Margins;
|
|
649
|
-
|
|
650
|
-
accountId: number;
|
|
651
|
-
}
|
|
628
|
+
};
|
|
652
629
|
|
|
653
630
|
if (this.accountIds !== undefined) {
|
|
654
|
-
const accountsData
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
631
|
+
const accountsData: (Account & { accountId: number })[] =
|
|
632
|
+
await Promise.all(
|
|
633
|
+
this.accountIds.map(async (accountId) => {
|
|
634
|
+
const response = await checkedFetch(
|
|
635
|
+
`${this.nord.webServerUrl}/account/${accountId}`,
|
|
636
|
+
);
|
|
637
|
+
const accountData = (await response.json()) as Account;
|
|
638
|
+
// Ensure we have the correct accountId
|
|
639
|
+
return {
|
|
640
|
+
...accountData,
|
|
641
|
+
accountId,
|
|
642
|
+
};
|
|
643
|
+
}),
|
|
644
|
+
);
|
|
667
645
|
|
|
668
646
|
for (const accountData of accountsData) {
|
|
669
647
|
// Process balances
|
|
@@ -678,13 +656,7 @@ export class NordUser {
|
|
|
678
656
|
|
|
679
657
|
// Process orders
|
|
680
658
|
this.orders[accountData.accountId] = accountData.orders.map(
|
|
681
|
-
(order: {
|
|
682
|
-
orderId: number;
|
|
683
|
-
side: string;
|
|
684
|
-
size: number;
|
|
685
|
-
price: number;
|
|
686
|
-
marketId: number;
|
|
687
|
-
}) => {
|
|
659
|
+
(order: OpenOrder) => {
|
|
688
660
|
return {
|
|
689
661
|
orderId: order.orderId,
|
|
690
662
|
isLong: order.side === "bid",
|
|
@@ -892,44 +864,6 @@ export class NordUser {
|
|
|
892
864
|
}
|
|
893
865
|
}
|
|
894
866
|
|
|
895
|
-
/**
|
|
896
|
-
* Create a new account
|
|
897
|
-
*
|
|
898
|
-
* @param params - Account creation parameters
|
|
899
|
-
* @returns New NordUser instance
|
|
900
|
-
* @throws {NordError} If the operation fails
|
|
901
|
-
*/
|
|
902
|
-
async createAccount(params: CreateAccountParams): Promise<NordUser> {
|
|
903
|
-
try {
|
|
904
|
-
this.checkSessionValidity();
|
|
905
|
-
// Create a new keypair for the account
|
|
906
|
-
const keypair = Keypair.generate();
|
|
907
|
-
|
|
908
|
-
// Create a new NordUser
|
|
909
|
-
const newUser = NordUser.fromPrivateKey(
|
|
910
|
-
this.nord,
|
|
911
|
-
keypair.secretKey,
|
|
912
|
-
this.connection,
|
|
913
|
-
);
|
|
914
|
-
|
|
915
|
-
// Transfer initial funds
|
|
916
|
-
await this.transferToAccount({
|
|
917
|
-
to: newUser,
|
|
918
|
-
tokenId: params.tokenId,
|
|
919
|
-
amount: params.amount,
|
|
920
|
-
fromAccountId: optExpect(this.accountIds?.[0], "No account ID"),
|
|
921
|
-
toAccountId: optExpect(
|
|
922
|
-
newUser.accountIds?.[0],
|
|
923
|
-
"No account ID for new user",
|
|
924
|
-
),
|
|
925
|
-
});
|
|
926
|
-
|
|
927
|
-
return newUser;
|
|
928
|
-
} catch (error) {
|
|
929
|
-
throw new NordError("Failed to create account", { cause: error });
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
|
|
933
867
|
/**
|
|
934
868
|
* Helper function to retry a promise with exponential backoff
|
|
935
869
|
*
|