@n1xyz/nord-ts 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +57 -0
- package/dist/actions.js +186 -0
- package/dist/bundle.js +79181 -0
- package/dist/{nord/client → client}/Nord.d.ts +90 -107
- package/dist/{nord/client → client}/Nord.js +157 -218
- package/dist/{nord/client → client}/NordAdmin.d.ts +68 -102
- package/dist/{nord/client → client}/NordAdmin.js +109 -140
- package/dist/{nord/client → client}/NordUser.d.ts +86 -98
- package/dist/{nord/client → client}/NordUser.js +204 -271
- package/dist/const.js +5 -8
- package/dist/{nord/utils/NordError.js → error.js} +7 -5
- package/dist/gen/nord_pb.js +88 -92
- package/dist/gen/openapi.d.ts +39 -0
- package/dist/gen/openapi.js +1 -2
- package/dist/index.d.ts +6 -1
- package/dist/index.js +10 -21
- package/dist/types.d.ts +5 -50
- package/dist/types.js +21 -83
- package/dist/utils.d.ts +8 -11
- package/dist/utils.js +55 -90
- package/dist/websocket/NordWebSocketClient.js +12 -17
- package/dist/{nord/models → websocket}/Subscriber.d.ts +1 -1
- package/dist/{nord/models → websocket}/Subscriber.js +6 -7
- package/dist/websocket/events.js +1 -2
- package/dist/websocket/index.d.ts +19 -2
- package/dist/websocket/index.js +80 -5
- package/package.json +2 -3
- package/dist/api/client.d.ts +0 -14
- package/dist/api/client.js +0 -45
- package/dist/bridge/client.d.ts +0 -151
- package/dist/bridge/client.js +0 -434
- package/dist/bridge/const.d.ts +0 -23
- package/dist/bridge/const.js +0 -47
- package/dist/bridge/index.d.ts +0 -4
- package/dist/bridge/index.js +0 -23
- package/dist/bridge/types.d.ts +0 -120
- package/dist/bridge/types.js +0 -18
- package/dist/bridge/utils.d.ts +0 -64
- package/dist/bridge/utils.js +0 -131
- package/dist/gen/common.d.ts +0 -68
- package/dist/gen/common.js +0 -215
- package/dist/gen/nord.d.ts +0 -882
- package/dist/gen/nord.js +0 -6520
- package/dist/idl/bridge.d.ts +0 -569
- package/dist/idl/bridge.js +0 -8
- package/dist/idl/bridge.json +0 -1506
- package/dist/idl/index.d.ts +0 -607
- package/dist/idl/index.js +0 -8
- package/dist/nord/api/actions.d.ts +0 -126
- package/dist/nord/api/actions.js +0 -397
- package/dist/nord/api/core.d.ts +0 -16
- package/dist/nord/api/core.js +0 -81
- package/dist/nord/api/market.d.ts +0 -36
- package/dist/nord/api/market.js +0 -96
- package/dist/nord/api/metrics.d.ts +0 -67
- package/dist/nord/api/metrics.js +0 -229
- package/dist/nord/api/queries.d.ts +0 -46
- package/dist/nord/api/queries.js +0 -109
- package/dist/nord/api/triggers.d.ts +0 -7
- package/dist/nord/api/triggers.js +0 -38
- package/dist/nord/client/NordClient.d.ts +0 -33
- package/dist/nord/client/NordClient.js +0 -45
- package/dist/nord/index.d.ts +0 -11
- package/dist/nord/index.js +0 -36
- package/src/const.ts +0 -34
- package/src/gen/.gitkeep +0 -0
- package/src/gen/nord_pb.ts +0 -5053
- package/src/gen/openapi.ts +0 -2864
- package/src/index.ts +0 -5
- package/src/nord/api/actions.ts +0 -648
- package/src/nord/api/core.ts +0 -96
- package/src/nord/api/metrics.ts +0 -269
- package/src/nord/client/Nord.ts +0 -937
- package/src/nord/client/NordAdmin.ts +0 -514
- package/src/nord/client/NordClient.ts +0 -79
- package/src/nord/client/NordUser.ts +0 -1211
- package/src/nord/index.ts +0 -25
- package/src/nord/models/Subscriber.ts +0 -56
- package/src/nord/utils/NordError.ts +0 -76
- package/src/types.ts +0 -377
- package/src/utils.ts +0 -269
- package/src/websocket/NordWebSocketClient.ts +0 -316
- package/src/websocket/events.ts +0 -31
- package/src/websocket/index.ts +0 -2
- /package/dist/{nord/utils/NordError.d.ts → error.d.ts} +0 -0
|
@@ -1,98 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.NordUser = void 0;
|
|
37
|
-
const spl_token_1 = require("@solana/spl-token");
|
|
38
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
39
|
-
const ed = __importStar(require("@noble/ed25519"));
|
|
40
|
-
const sha512_1 = require("@noble/hashes/sha512");
|
|
41
|
-
ed.etc.sha512Sync = sha512_1.sha512;
|
|
42
|
-
const proton_1 = require("@n1xyz/proton");
|
|
43
|
-
const types_1 = require("../../types");
|
|
44
|
-
const proto = __importStar(require("../../gen/nord_pb"));
|
|
45
|
-
const utils_1 = require("../../utils");
|
|
46
|
-
const protobuf_1 = require("@bufbuild/protobuf");
|
|
47
|
-
const actions_1 = require("../api/actions");
|
|
48
|
-
const NordError_1 = require("../utils/NordError");
|
|
49
|
-
const NordClient_1 = require("./NordClient");
|
|
1
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddress, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, } from "@solana/spl-token";
|
|
2
|
+
import { PublicKey, Transaction } from "@solana/web3.js";
|
|
3
|
+
import * as ed from "@noble/ed25519";
|
|
4
|
+
import { floatToScaledBigIntLossy } from "@n1xyz/proton";
|
|
5
|
+
import { Side, TriggerKind, fillModeToProtoFillMode, } from "../types";
|
|
6
|
+
import * as proto from "../gen/nord_pb";
|
|
7
|
+
import { checkedFetch, assert, findMarket, findToken, optExpect, keypairFromPrivateKey, toScaledU64, } from "../utils";
|
|
8
|
+
import { create } from "@bufbuild/protobuf";
|
|
9
|
+
import { createSession, revokeSession, atomic, expectReceiptKind, createAction, sendAction, } from "../actions";
|
|
10
|
+
import { NordError } from "../error";
|
|
50
11
|
/**
|
|
51
12
|
* User class for interacting with the Nord protocol
|
|
52
13
|
*/
|
|
53
|
-
class NordUser
|
|
14
|
+
export class NordUser {
|
|
15
|
+
nord;
|
|
16
|
+
sessionSignFn;
|
|
17
|
+
transactionSignFn;
|
|
18
|
+
sessionId;
|
|
19
|
+
sessionPubKey;
|
|
20
|
+
publicKey;
|
|
21
|
+
lastTs = 0;
|
|
22
|
+
nonce = 0;
|
|
23
|
+
/** User balances by token symbol */
|
|
24
|
+
balances = {};
|
|
25
|
+
/** User positions by account ID */
|
|
26
|
+
positions = {};
|
|
27
|
+
/** User margins by account ID */
|
|
28
|
+
margins = {};
|
|
29
|
+
/** User's account IDs */
|
|
30
|
+
accountIds;
|
|
31
|
+
/** SPL token information */
|
|
32
|
+
splTokenInfos = [];
|
|
54
33
|
/**
|
|
55
34
|
* Create a new NordUser instance
|
|
56
35
|
*
|
|
57
|
-
* @param
|
|
36
|
+
* @param nord - Nord client instance
|
|
37
|
+
* @param sessionSignFn - Function to sign messages with the user's session key
|
|
38
|
+
* @param transactionSignFn - Function to sign transactions with the user's wallet (optional)
|
|
39
|
+
* @param sessionId - Existing session identifier
|
|
40
|
+
* @param sessionPubKey - Session public key
|
|
41
|
+
* @param publicKey - Wallet public key
|
|
58
42
|
* @throws {NordError} If required parameters are missing
|
|
59
43
|
*/
|
|
60
|
-
constructor({
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (!sessionPubKey) {
|
|
68
|
-
throw new NordError_1.NordError("Session public key is required");
|
|
69
|
-
}
|
|
70
|
-
let parsedAddress;
|
|
71
|
-
try {
|
|
72
|
-
parsedAddress = new web3_js_1.PublicKey(address);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
throw new NordError_1.NordError("Invalid Solana address", { cause: error });
|
|
76
|
-
}
|
|
77
|
-
super({
|
|
78
|
-
nord,
|
|
79
|
-
address: parsedAddress,
|
|
80
|
-
walletSignFn,
|
|
81
|
-
sessionSignFn,
|
|
82
|
-
transactionSignFn,
|
|
83
|
-
connection,
|
|
84
|
-
sessionId,
|
|
85
|
-
sessionPubKey,
|
|
86
|
-
publicKey,
|
|
87
|
-
});
|
|
88
|
-
/** User balances by token symbol */
|
|
89
|
-
this.balances = {};
|
|
90
|
-
/** User positions by account ID */
|
|
91
|
-
this.positions = {};
|
|
92
|
-
/** User margins by account ID */
|
|
93
|
-
this.margins = {};
|
|
94
|
-
/** SPL token information */
|
|
95
|
-
this.splTokenInfos = [];
|
|
44
|
+
constructor({ nord, sessionSignFn, transactionSignFn, sessionId, sessionPubKey, publicKey, }) {
|
|
45
|
+
this.nord = nord;
|
|
46
|
+
this.sessionSignFn = sessionSignFn;
|
|
47
|
+
this.transactionSignFn = transactionSignFn;
|
|
48
|
+
this.sessionId = sessionId;
|
|
49
|
+
this.sessionPubKey = new PublicKey(sessionPubKey);
|
|
50
|
+
this.publicKey = publicKey;
|
|
96
51
|
// Convert tokens from info endpoint to SPLTokenInfo
|
|
97
52
|
if (this.nord.tokens && this.nord.tokens.length > 0) {
|
|
98
53
|
this.splTokenInfos = this.nord.tokens.map((token) => ({
|
|
@@ -103,85 +58,36 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
103
58
|
}));
|
|
104
59
|
}
|
|
105
60
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Create a clone of this NordUser instance
|
|
108
|
-
*
|
|
109
|
-
* @returns A new NordUser instance with the same properties
|
|
110
|
-
*/
|
|
111
|
-
clone() {
|
|
112
|
-
const cloned = new NordUser({
|
|
113
|
-
nord: this.nord,
|
|
114
|
-
address: this.address.toBase58(),
|
|
115
|
-
walletSignFn: this.walletSignFn,
|
|
116
|
-
sessionSignFn: this.sessionSignFn,
|
|
117
|
-
transactionSignFn: this.transactionSignFn,
|
|
118
|
-
connection: this.connection,
|
|
119
|
-
sessionPubKey: new Uint8Array(this.sessionPubKey),
|
|
120
|
-
publicKey: this.publicKey,
|
|
121
|
-
sessionId: this.sessionId,
|
|
122
|
-
});
|
|
123
|
-
// Copy other properties
|
|
124
|
-
cloned.balances = { ...this.balances };
|
|
125
|
-
cloned.positions = { ...this.positions };
|
|
126
|
-
cloned.margins = { ...this.margins };
|
|
127
|
-
cloned.accountIds = this.accountIds ? [...this.accountIds] : undefined;
|
|
128
|
-
cloned.splTokenInfos = [...this.splTokenInfos];
|
|
129
|
-
this.cloneClientState(cloned);
|
|
130
|
-
return cloned;
|
|
131
|
-
}
|
|
132
61
|
/**
|
|
133
62
|
* Create a NordUser from a private key
|
|
134
63
|
*
|
|
135
64
|
* @param nord - Nord instance
|
|
136
65
|
* @param privateKey - Private key as string or Uint8Array
|
|
137
|
-
* @param connection - Solana connection (optional)
|
|
138
66
|
* @returns NordUser instance
|
|
139
67
|
* @throws {NordError} If the private key is invalid
|
|
140
68
|
*/
|
|
141
|
-
static fromPrivateKey(nord, privateKey
|
|
69
|
+
static fromPrivateKey(nord, privateKey) {
|
|
142
70
|
try {
|
|
143
|
-
const keypair =
|
|
71
|
+
const keypair = keypairFromPrivateKey(privateKey);
|
|
144
72
|
const publicKey = keypair.publicKey;
|
|
145
|
-
// Create a signing function that uses the keypair but doesn't expose it
|
|
146
|
-
const walletSignFn = async (message) => {
|
|
147
|
-
function toHex(buffer) {
|
|
148
|
-
return Array.from(buffer)
|
|
149
|
-
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
150
|
-
.join("");
|
|
151
|
-
}
|
|
152
|
-
const messageBuffer = new TextEncoder().encode(toHex(message));
|
|
153
|
-
// Use ed25519 to sign the message
|
|
154
|
-
const signature = ed.sign(messageBuffer, keypair.secretKey.slice(0, 32));
|
|
155
|
-
return signature;
|
|
156
|
-
};
|
|
157
73
|
const sessionSignFn = async (message) => {
|
|
158
74
|
// Use ed25519 to sign the message
|
|
159
75
|
return ed.sign(message, keypair.secretKey.slice(0, 32));
|
|
160
76
|
};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (transaction.sign) {
|
|
165
|
-
// Solana transaction
|
|
166
|
-
transaction.sign(keypair);
|
|
167
|
-
return transaction;
|
|
168
|
-
}
|
|
169
|
-
// For other transaction types, would need specific implementation
|
|
170
|
-
throw new NordError_1.NordError("Unsupported transaction type for signing");
|
|
77
|
+
const transactionSignFn = async (tx) => {
|
|
78
|
+
tx.sign(keypair);
|
|
79
|
+
return tx;
|
|
171
80
|
};
|
|
172
81
|
return new NordUser({
|
|
173
82
|
nord,
|
|
174
|
-
address: publicKey.toBase58(),
|
|
175
|
-
walletSignFn,
|
|
176
83
|
sessionSignFn,
|
|
177
84
|
transactionSignFn,
|
|
178
|
-
connection,
|
|
179
85
|
publicKey,
|
|
180
|
-
sessionPubKey: publicKey.toBytes(),
|
|
86
|
+
sessionPubKey: publicKey.toBytes(),
|
|
181
87
|
});
|
|
182
88
|
}
|
|
183
89
|
catch (error) {
|
|
184
|
-
throw new
|
|
90
|
+
throw new NordError("Failed to create NordUser from private key", {
|
|
185
91
|
cause: error,
|
|
186
92
|
});
|
|
187
93
|
}
|
|
@@ -194,26 +100,23 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
194
100
|
* @throws {NordError} If required parameters are missing or operation fails
|
|
195
101
|
*/
|
|
196
102
|
async getAssociatedTokenAccount(mint) {
|
|
197
|
-
if (!this.getSolanaPublicKey()) {
|
|
198
|
-
throw new NordError_1.NordError("Solana public key is required to get associated token account");
|
|
199
|
-
}
|
|
200
103
|
try {
|
|
201
104
|
// Get the token program ID from the mint account
|
|
202
|
-
const mintAccount = await this.
|
|
105
|
+
const mintAccount = await this.nord.solanaConnection.getAccountInfo(mint);
|
|
203
106
|
if (!mintAccount) {
|
|
204
|
-
throw new
|
|
107
|
+
throw new NordError("Mint account not found");
|
|
205
108
|
}
|
|
206
109
|
const tokenProgramId = mintAccount.owner;
|
|
207
110
|
// Validate that the mint is owned by a supported SPL token program
|
|
208
|
-
if (!tokenProgramId.equals(
|
|
209
|
-
!tokenProgramId.equals(
|
|
210
|
-
throw new
|
|
111
|
+
if (!tokenProgramId.equals(TOKEN_PROGRAM_ID) &&
|
|
112
|
+
!tokenProgramId.equals(TOKEN_2022_PROGRAM_ID)) {
|
|
113
|
+
throw new NordError("Mint Account is not owned by a supported SPL token program");
|
|
211
114
|
}
|
|
212
|
-
const associatedTokenAddress = await
|
|
115
|
+
const associatedTokenAddress = await getAssociatedTokenAddress(mint, this.publicKey, false, tokenProgramId, ASSOCIATED_TOKEN_PROGRAM_ID);
|
|
213
116
|
return associatedTokenAddress;
|
|
214
117
|
}
|
|
215
118
|
catch (error) {
|
|
216
|
-
throw new
|
|
119
|
+
throw new NordError("Failed to get associated token account", {
|
|
217
120
|
cause: error,
|
|
218
121
|
});
|
|
219
122
|
}
|
|
@@ -246,30 +149,30 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
246
149
|
// Find the token info
|
|
247
150
|
const tokenInfo = this.splTokenInfos.find((t) => t.tokenId === tokenId);
|
|
248
151
|
if (!tokenInfo) {
|
|
249
|
-
throw new
|
|
152
|
+
throw new NordError(`Token with ID ${tokenId} not found`);
|
|
250
153
|
}
|
|
251
|
-
const mint = new
|
|
154
|
+
const mint = new PublicKey(tokenInfo.mint);
|
|
252
155
|
const fromAccount = await this.getAssociatedTokenAccount(mint);
|
|
253
|
-
const payer = this.
|
|
156
|
+
const payer = this.publicKey;
|
|
254
157
|
const { ix, extraSigner } = await this.nord.protonClient.buildDepositIx({
|
|
255
158
|
payer,
|
|
256
159
|
recipient: recipient ?? payer,
|
|
257
|
-
quantAmount:
|
|
160
|
+
quantAmount: floatToScaledBigIntLossy(amount, tokenInfo.precision),
|
|
258
161
|
mint,
|
|
259
162
|
sourceTokenAccount: fromAccount,
|
|
260
163
|
});
|
|
261
|
-
const { blockhash } = await this.
|
|
262
|
-
const tx = new
|
|
164
|
+
const { blockhash } = await this.nord.solanaConnection.getLatestBlockhash();
|
|
165
|
+
const tx = new Transaction();
|
|
263
166
|
tx.add(ix);
|
|
264
167
|
tx.recentBlockhash = blockhash;
|
|
265
168
|
tx.feePayer = payer;
|
|
266
169
|
const signedTx = await this.transactionSignFn(tx);
|
|
267
170
|
signedTx.partialSign(extraSigner);
|
|
268
|
-
const signature = await this.
|
|
171
|
+
const signature = await this.nord.solanaConnection.sendRawTransaction(signedTx.serialize(), sendOptions);
|
|
269
172
|
return signature;
|
|
270
173
|
}
|
|
271
174
|
catch (error) {
|
|
272
|
-
throw new
|
|
175
|
+
throw new NordError(`Failed to deposit ${amount} of token ID ${tokenId}`, { cause: error });
|
|
273
176
|
}
|
|
274
177
|
}
|
|
275
178
|
/**
|
|
@@ -278,7 +181,7 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
278
181
|
* @returns Nonce as number
|
|
279
182
|
*/
|
|
280
183
|
getNonce() {
|
|
281
|
-
return this.
|
|
184
|
+
return ++this.nonce;
|
|
282
185
|
}
|
|
283
186
|
async submitSessionAction(kind) {
|
|
284
187
|
return this.submitSignedAction(kind, async (message) => {
|
|
@@ -297,18 +200,18 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
297
200
|
async updateAccountId() {
|
|
298
201
|
try {
|
|
299
202
|
if (!this.publicKey) {
|
|
300
|
-
throw new
|
|
203
|
+
throw new NordError("Public key is required to update account ID");
|
|
301
204
|
}
|
|
302
205
|
const resp = await this.nord.getUser({
|
|
303
206
|
pubkey: this.publicKey.toBase58(),
|
|
304
207
|
});
|
|
305
208
|
if (!resp) {
|
|
306
|
-
throw new
|
|
209
|
+
throw new NordError(`User ${this.publicKey.toBase58()} not found`);
|
|
307
210
|
}
|
|
308
211
|
this.accountIds = resp.accountIds;
|
|
309
212
|
}
|
|
310
213
|
catch (error) {
|
|
311
|
-
throw new
|
|
214
|
+
throw new NordError("Failed to update account ID", { cause: error });
|
|
312
215
|
}
|
|
313
216
|
}
|
|
314
217
|
/**
|
|
@@ -319,7 +222,7 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
319
222
|
async fetchInfo() {
|
|
320
223
|
if (this.accountIds !== undefined) {
|
|
321
224
|
const accountsData = await Promise.all(this.accountIds.map(async (accountId) => {
|
|
322
|
-
const response = await
|
|
225
|
+
const response = await checkedFetch(`${this.nord.webServerUrl}/account/${accountId}`);
|
|
323
226
|
const accountData = (await response.json());
|
|
324
227
|
// Ensure we have the correct accountId
|
|
325
228
|
return {
|
|
@@ -350,8 +253,8 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
350
253
|
* @throws {NordError} If the operation fails
|
|
351
254
|
*/
|
|
352
255
|
async refreshSession() {
|
|
353
|
-
const result = await
|
|
354
|
-
userPubkey:
|
|
256
|
+
const result = await createSession(this.nord.httpClient, this.transactionSignFn, await this.nord.getTimestamp(), this.getNonce(), {
|
|
257
|
+
userPubkey: this.publicKey,
|
|
355
258
|
sessionPubkey: this.sessionPubKey,
|
|
356
259
|
});
|
|
357
260
|
this.sessionId = result.sessionId;
|
|
@@ -364,12 +267,13 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
364
267
|
*/
|
|
365
268
|
async revokeSession(sessionId) {
|
|
366
269
|
try {
|
|
367
|
-
await
|
|
270
|
+
await revokeSession(this.nord.httpClient, this.transactionSignFn, await this.nord.getTimestamp(), this.getNonce(), {
|
|
271
|
+
userPubkey: this.publicKey,
|
|
368
272
|
sessionId,
|
|
369
273
|
});
|
|
370
274
|
}
|
|
371
275
|
catch (error) {
|
|
372
|
-
throw new
|
|
276
|
+
throw new NordError(`Failed to revoke session ${sessionId}`, {
|
|
373
277
|
cause: error,
|
|
374
278
|
});
|
|
375
279
|
}
|
|
@@ -381,7 +285,7 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
381
285
|
*/
|
|
382
286
|
checkSessionValidity() {
|
|
383
287
|
if (this.sessionId === undefined || this.sessionId === BigInt(0)) {
|
|
384
|
-
throw new
|
|
288
|
+
throw new NordError("Invalid or empty session ID. Please create or refresh your session.");
|
|
385
289
|
}
|
|
386
290
|
}
|
|
387
291
|
/**
|
|
@@ -394,70 +298,76 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
394
298
|
async withdraw({ amount, tokenId, }) {
|
|
395
299
|
try {
|
|
396
300
|
this.checkSessionValidity();
|
|
397
|
-
const token =
|
|
398
|
-
const scaledAmount =
|
|
301
|
+
const token = findToken(this.nord.tokens, tokenId);
|
|
302
|
+
const scaledAmount = toScaledU64(amount, token.decimals);
|
|
399
303
|
if (scaledAmount <= 0n) {
|
|
400
|
-
throw new
|
|
304
|
+
throw new NordError("Withdraw amount must be positive");
|
|
401
305
|
}
|
|
402
306
|
const receipt = await this.submitSessionAction({
|
|
403
307
|
case: "withdraw",
|
|
404
|
-
value:
|
|
405
|
-
sessionId: BigInt(
|
|
308
|
+
value: create(proto.Action_WithdrawSchema, {
|
|
309
|
+
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
406
310
|
tokenId,
|
|
407
311
|
amount: scaledAmount,
|
|
408
312
|
}),
|
|
409
313
|
});
|
|
410
|
-
|
|
314
|
+
expectReceiptKind(receipt, "withdrawResult", "withdraw");
|
|
411
315
|
return { actionId: receipt.actionId };
|
|
412
316
|
}
|
|
413
317
|
catch (error) {
|
|
414
|
-
throw new
|
|
318
|
+
throw new NordError(`Failed to withdraw ${amount} of token ID ${tokenId}`, { cause: error });
|
|
415
319
|
}
|
|
416
320
|
}
|
|
417
321
|
/**
|
|
418
322
|
* Place an order on the exchange
|
|
419
323
|
*
|
|
420
|
-
* @param
|
|
324
|
+
* @param marketId - Target market identifier
|
|
325
|
+
* @param side - Order side
|
|
326
|
+
* @param fillMode - Fill mode (limit, market, etc.)
|
|
327
|
+
* @param isReduceOnly - Reduce-only flag
|
|
328
|
+
* @param size - Base size to place
|
|
329
|
+
* @param price - Limit price
|
|
330
|
+
* @param quoteSize - Quote-sized order representation
|
|
331
|
+
* @param accountId - Account executing the order
|
|
332
|
+
* @param clientOrderId - Optional client-specified identifier
|
|
421
333
|
* @returns Object containing actionId, orderId (if posted), fills, and clientOrderId
|
|
422
334
|
* @throws {NordError} If the operation fails
|
|
423
335
|
*/
|
|
424
|
-
async placeOrder(
|
|
336
|
+
async placeOrder({ marketId, side, fillMode, isReduceOnly, size, price, quoteSize, accountId, clientOrderId, }) {
|
|
425
337
|
try {
|
|
426
338
|
this.checkSessionValidity();
|
|
427
|
-
const market =
|
|
339
|
+
const market = findMarket(this.nord.markets, marketId);
|
|
428
340
|
if (!market) {
|
|
429
|
-
throw new
|
|
341
|
+
throw new NordError(`Market with ID ${marketId} not found`);
|
|
430
342
|
}
|
|
431
|
-
const sessionId =
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
const scaledQuote =
|
|
435
|
-
?
|
|
343
|
+
const sessionId = optExpect(this.sessionId, "No session");
|
|
344
|
+
const scaledPrice = toScaledU64(price ?? 0, market.priceDecimals);
|
|
345
|
+
const scaledSize = toScaledU64(size ?? 0, market.sizeDecimals);
|
|
346
|
+
const scaledQuote = quoteSize
|
|
347
|
+
? quoteSize.toWire(market.priceDecimals, market.sizeDecimals)
|
|
436
348
|
: undefined;
|
|
437
|
-
|
|
349
|
+
assert(scaledPrice > 0n || scaledSize > 0n || scaledQuote !== undefined, "OrderLimit must include at least one of: size, price, or quoteSize");
|
|
438
350
|
const receipt = await this.submitSessionAction({
|
|
439
351
|
case: "placeOrder",
|
|
440
|
-
value:
|
|
352
|
+
value: create(proto.Action_PlaceOrderSchema, {
|
|
441
353
|
sessionId: BigInt(sessionId),
|
|
442
|
-
senderAccountId:
|
|
443
|
-
marketId
|
|
444
|
-
side:
|
|
445
|
-
fillMode:
|
|
446
|
-
isReduceOnly
|
|
447
|
-
price,
|
|
448
|
-
size,
|
|
354
|
+
senderAccountId: accountId,
|
|
355
|
+
marketId,
|
|
356
|
+
side: side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
357
|
+
fillMode: fillModeToProtoFillMode(fillMode),
|
|
358
|
+
isReduceOnly,
|
|
359
|
+
price: scaledPrice,
|
|
360
|
+
size: scaledSize,
|
|
449
361
|
quoteSize: scaledQuote === undefined
|
|
450
362
|
? undefined
|
|
451
|
-
:
|
|
363
|
+
: create(proto.QuoteSizeSchema, {
|
|
452
364
|
size: scaledQuote.size,
|
|
453
365
|
price: scaledQuote.price,
|
|
454
366
|
}),
|
|
455
|
-
clientOrderId:
|
|
456
|
-
? undefined
|
|
457
|
-
: BigInt(params.clientOrderId),
|
|
367
|
+
clientOrderId: clientOrderId === undefined ? undefined : BigInt(clientOrderId),
|
|
458
368
|
}),
|
|
459
369
|
});
|
|
460
|
-
|
|
370
|
+
expectReceiptKind(receipt, "placeOrderResult", "place order");
|
|
461
371
|
const result = receipt.kind.value;
|
|
462
372
|
return {
|
|
463
373
|
actionId: receipt.actionId,
|
|
@@ -466,7 +376,7 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
466
376
|
};
|
|
467
377
|
}
|
|
468
378
|
catch (error) {
|
|
469
|
-
throw new
|
|
379
|
+
throw new NordError("Failed to place order", { cause: error });
|
|
470
380
|
}
|
|
471
381
|
}
|
|
472
382
|
/**
|
|
@@ -483,13 +393,13 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
483
393
|
this.checkSessionValidity();
|
|
484
394
|
const receipt = await this.submitSessionAction({
|
|
485
395
|
case: "cancelOrderById",
|
|
486
|
-
value:
|
|
396
|
+
value: create(proto.Action_CancelOrderByIdSchema, {
|
|
487
397
|
orderId: BigInt(orderId),
|
|
488
|
-
sessionId: BigInt(
|
|
398
|
+
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
489
399
|
senderAccountId: accountId,
|
|
490
400
|
}),
|
|
491
401
|
});
|
|
492
|
-
|
|
402
|
+
expectReceiptKind(receipt, "cancelOrderResult", "cancel order");
|
|
493
403
|
return {
|
|
494
404
|
actionId: receipt.actionId,
|
|
495
405
|
orderId: receipt.kind.value.orderId,
|
|
@@ -497,7 +407,7 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
497
407
|
};
|
|
498
408
|
}
|
|
499
409
|
catch (error) {
|
|
500
|
-
throw new
|
|
410
|
+
throw new NordError(`Failed to cancel order ${orderId}`, {
|
|
501
411
|
cause: error,
|
|
502
412
|
});
|
|
503
413
|
}
|
|
@@ -505,116 +415,137 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
505
415
|
/**
|
|
506
416
|
* Add a trigger for the current session
|
|
507
417
|
*
|
|
508
|
-
* @param
|
|
418
|
+
* @param marketId - Market to watch
|
|
419
|
+
* @param side - Order side for the trigger
|
|
420
|
+
* @param kind - Stop-loss or take-profit trigger type
|
|
421
|
+
* @param triggerPrice - Price that activates the trigger
|
|
422
|
+
* @param limitPrice - Limit price placed once the trigger fires
|
|
423
|
+
* @param accountId - Account executing the trigger
|
|
509
424
|
* @returns Object containing the actionId of the submitted trigger
|
|
510
425
|
* @throws {NordError} If the operation fails
|
|
511
426
|
*/
|
|
512
|
-
async addTrigger(
|
|
427
|
+
async addTrigger({ marketId, side, kind, triggerPrice, limitPrice, accountId, }) {
|
|
513
428
|
try {
|
|
514
429
|
this.checkSessionValidity();
|
|
515
|
-
const market =
|
|
430
|
+
const market = findMarket(this.nord.markets, marketId);
|
|
516
431
|
if (!market) {
|
|
517
|
-
throw new
|
|
432
|
+
throw new NordError(`Market with ID ${marketId} not found`);
|
|
518
433
|
}
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
const
|
|
434
|
+
const scaledTriggerPrice = toScaledU64(triggerPrice, market.priceDecimals);
|
|
435
|
+
assert(scaledTriggerPrice > 0n, "Trigger price must be positive");
|
|
436
|
+
const scaledLimitPrice = limitPrice === undefined
|
|
522
437
|
? undefined
|
|
523
|
-
:
|
|
524
|
-
if (
|
|
525
|
-
|
|
438
|
+
: toScaledU64(limitPrice, market.priceDecimals);
|
|
439
|
+
if (scaledLimitPrice !== undefined) {
|
|
440
|
+
assert(scaledLimitPrice > 0n, "Limit price must be positive");
|
|
526
441
|
}
|
|
527
|
-
const key =
|
|
528
|
-
kind:
|
|
442
|
+
const key = create(proto.TriggerKeySchema, {
|
|
443
|
+
kind: kind === TriggerKind.StopLoss
|
|
529
444
|
? proto.TriggerKind.STOP_LOSS
|
|
530
445
|
: proto.TriggerKind.TAKE_PROFIT,
|
|
531
|
-
side:
|
|
446
|
+
side: side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
532
447
|
});
|
|
533
|
-
const prices =
|
|
534
|
-
triggerPrice,
|
|
535
|
-
limitPrice,
|
|
448
|
+
const prices = create(proto.Action_TriggerPricesSchema, {
|
|
449
|
+
triggerPrice: scaledTriggerPrice,
|
|
450
|
+
limitPrice: scaledLimitPrice,
|
|
536
451
|
});
|
|
537
452
|
const receipt = await this.submitSessionAction({
|
|
538
453
|
case: "addTrigger",
|
|
539
|
-
value:
|
|
540
|
-
sessionId: BigInt(
|
|
541
|
-
marketId
|
|
454
|
+
value: create(proto.Action_AddTriggerSchema, {
|
|
455
|
+
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
456
|
+
marketId,
|
|
542
457
|
key,
|
|
543
458
|
prices,
|
|
544
|
-
accountId
|
|
459
|
+
accountId,
|
|
545
460
|
}),
|
|
546
461
|
});
|
|
547
|
-
|
|
462
|
+
expectReceiptKind(receipt, "triggerAdded", "add trigger");
|
|
548
463
|
return { actionId: receipt.actionId };
|
|
549
464
|
}
|
|
550
465
|
catch (error) {
|
|
551
|
-
throw new
|
|
466
|
+
throw new NordError("Failed to add trigger", { cause: error });
|
|
552
467
|
}
|
|
553
468
|
}
|
|
554
469
|
/**
|
|
555
470
|
* Remove a trigger for the current session
|
|
556
471
|
*
|
|
557
|
-
* @param
|
|
472
|
+
* @param marketId - Market the trigger belongs to
|
|
473
|
+
* @param side - Order side for the trigger
|
|
474
|
+
* @param kind - Stop-loss or take-profit trigger type
|
|
475
|
+
* @param accountId - Account executing the trigger
|
|
558
476
|
* @returns Object containing the actionId of the removal action
|
|
559
477
|
* @throws {NordError} If the operation fails
|
|
560
478
|
*/
|
|
561
|
-
async removeTrigger(
|
|
479
|
+
async removeTrigger({ marketId, side, kind, accountId, }) {
|
|
562
480
|
try {
|
|
563
481
|
this.checkSessionValidity();
|
|
564
|
-
const market =
|
|
482
|
+
const market = findMarket(this.nord.markets, marketId);
|
|
565
483
|
if (!market) {
|
|
566
|
-
throw new
|
|
484
|
+
throw new NordError(`Market with ID ${marketId} not found`);
|
|
567
485
|
}
|
|
568
|
-
const key =
|
|
569
|
-
kind:
|
|
486
|
+
const key = create(proto.TriggerKeySchema, {
|
|
487
|
+
kind: kind === TriggerKind.StopLoss
|
|
570
488
|
? proto.TriggerKind.STOP_LOSS
|
|
571
489
|
: proto.TriggerKind.TAKE_PROFIT,
|
|
572
|
-
side:
|
|
490
|
+
side: side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
573
491
|
});
|
|
574
492
|
const receipt = await this.submitSessionAction({
|
|
575
493
|
case: "removeTrigger",
|
|
576
|
-
value:
|
|
577
|
-
sessionId: BigInt(
|
|
578
|
-
marketId
|
|
494
|
+
value: create(proto.Action_RemoveTriggerSchema, {
|
|
495
|
+
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
496
|
+
marketId,
|
|
579
497
|
key,
|
|
580
|
-
accountId
|
|
498
|
+
accountId,
|
|
581
499
|
}),
|
|
582
500
|
});
|
|
583
|
-
|
|
501
|
+
expectReceiptKind(receipt, "triggerRemoved", "remove trigger");
|
|
584
502
|
return { actionId: receipt.actionId };
|
|
585
503
|
}
|
|
586
504
|
catch (error) {
|
|
587
|
-
throw new
|
|
505
|
+
throw new NordError("Failed to remove trigger", { cause: error });
|
|
588
506
|
}
|
|
589
507
|
}
|
|
590
508
|
/**
|
|
591
509
|
* Transfer tokens to another account
|
|
592
510
|
*
|
|
593
|
-
* @param
|
|
511
|
+
* @param tokenId - Token identifier to move
|
|
512
|
+
* @param amount - Amount to transfer
|
|
513
|
+
* @param fromAccountId - Source account id
|
|
514
|
+
* @param toAccountId - Destination account id
|
|
594
515
|
* @throws {NordError} If the operation fails
|
|
595
516
|
*/
|
|
596
|
-
async transferToAccount(
|
|
517
|
+
async transferToAccount({ tokenId, amount, fromAccountId, toAccountId, }) {
|
|
597
518
|
try {
|
|
598
519
|
this.checkSessionValidity();
|
|
599
|
-
const token =
|
|
600
|
-
const
|
|
601
|
-
if (
|
|
602
|
-
throw new
|
|
520
|
+
const token = findToken(this.nord.tokens, tokenId);
|
|
521
|
+
const scaledAmount = toScaledU64(amount, token.decimals);
|
|
522
|
+
if (scaledAmount <= 0n) {
|
|
523
|
+
throw new NordError("Transfer amount must be positive");
|
|
603
524
|
}
|
|
604
525
|
const receipt = await this.submitSessionAction({
|
|
605
526
|
case: "transfer",
|
|
606
|
-
value:
|
|
607
|
-
sessionId: BigInt(
|
|
608
|
-
fromAccountId:
|
|
609
|
-
toAccountId:
|
|
610
|
-
tokenId
|
|
611
|
-
amount,
|
|
527
|
+
value: create(proto.Action_TransferSchema, {
|
|
528
|
+
sessionId: BigInt(optExpect(this.sessionId, "No session")),
|
|
529
|
+
fromAccountId: optExpect(fromAccountId, "No source account"),
|
|
530
|
+
toAccountId: optExpect(toAccountId, "No target account"),
|
|
531
|
+
tokenId,
|
|
532
|
+
amount: scaledAmount,
|
|
612
533
|
}),
|
|
613
534
|
});
|
|
614
|
-
|
|
535
|
+
expectReceiptKind(receipt, "transferred", "transfer tokens");
|
|
536
|
+
if (receipt.kind.value.accountCreated) {
|
|
537
|
+
assert(receipt.kind.value.toUserAccount !== undefined, `toAccount must be defined on new account on ${receipt.kind.value}`);
|
|
538
|
+
return {
|
|
539
|
+
actionId: receipt.actionId,
|
|
540
|
+
newAccountId: receipt.kind.value.toUserAccount,
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
return { actionId: receipt.actionId };
|
|
545
|
+
}
|
|
615
546
|
}
|
|
616
547
|
catch (error) {
|
|
617
|
-
throw new
|
|
548
|
+
throw new NordError("Failed to transfer tokens", { cause: error });
|
|
618
549
|
}
|
|
619
550
|
}
|
|
620
551
|
/**
|
|
@@ -634,13 +565,13 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
634
565
|
this.checkSessionValidity();
|
|
635
566
|
const accountId = providedAccountId != null ? providedAccountId : this.accountIds?.[0];
|
|
636
567
|
if (accountId == null) {
|
|
637
|
-
throw new
|
|
568
|
+
throw new NordError("Account ID is undefined. Make sure to call updateAccountId() before atomic operations.");
|
|
638
569
|
}
|
|
639
570
|
const apiActions = userActions.map((act) => {
|
|
640
571
|
if (act.kind === "place") {
|
|
641
|
-
const market =
|
|
572
|
+
const market = findMarket(this.nord.markets, act.marketId);
|
|
642
573
|
if (!market) {
|
|
643
|
-
throw new
|
|
574
|
+
throw new NordError(`Market ${act.marketId} not found`);
|
|
644
575
|
}
|
|
645
576
|
return {
|
|
646
577
|
kind: "place",
|
|
@@ -661,15 +592,15 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
661
592
|
orderId: act.orderId,
|
|
662
593
|
};
|
|
663
594
|
});
|
|
664
|
-
const result = await
|
|
665
|
-
sessionId:
|
|
595
|
+
const result = await atomic(this.nord.httpClient, this.sessionSignFn, await this.nord.getTimestamp(), this.getNonce(), {
|
|
596
|
+
sessionId: optExpect(this.sessionId, "No session"),
|
|
666
597
|
accountId: accountId,
|
|
667
598
|
actions: apiActions,
|
|
668
599
|
});
|
|
669
600
|
return result;
|
|
670
601
|
}
|
|
671
602
|
catch (error) {
|
|
672
|
-
throw new
|
|
603
|
+
throw new NordError("Atomic operation failed", { cause: error });
|
|
673
604
|
}
|
|
674
605
|
}
|
|
675
606
|
/**
|
|
@@ -718,17 +649,14 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
718
649
|
*/
|
|
719
650
|
async getSolanaBalances(options = {}) {
|
|
720
651
|
const { includeZeroBalances = true, includeTokenAccounts = false, maxConcurrent = 5, maxRetries = 3, } = options;
|
|
721
|
-
if (!this.connection || !this.getSolanaPublicKey()) {
|
|
722
|
-
throw new NordError_1.NordError("Connection and Solana public key are required to get Solana balances");
|
|
723
|
-
}
|
|
724
652
|
const balances = {};
|
|
725
653
|
const tokenAccounts = {};
|
|
726
654
|
try {
|
|
727
655
|
// Get SOL balance (native token)
|
|
728
|
-
const solBalance = await this.retryWithBackoff(() => this.
|
|
656
|
+
const solBalance = await this.retryWithBackoff(() => this.nord.solanaConnection.getBalance(this.publicKey), maxRetries);
|
|
729
657
|
balances["SOL"] = solBalance / 1e9; // Convert lamports to SOL
|
|
730
658
|
if (includeTokenAccounts) {
|
|
731
|
-
tokenAccounts["SOL"] = this.
|
|
659
|
+
tokenAccounts["SOL"] = this.publicKey.toString();
|
|
732
660
|
}
|
|
733
661
|
// Get SPL token balances using mintAddr from Nord tokens
|
|
734
662
|
if (this.nord.tokens && this.nord.tokens.length > 0) {
|
|
@@ -739,13 +667,13 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
739
667
|
// Process batch in parallel
|
|
740
668
|
const batchPromises = batch.map(async (token) => {
|
|
741
669
|
try {
|
|
742
|
-
const mint = new
|
|
743
|
-
const associatedTokenAddress = await this.retryWithBackoff(() =>
|
|
670
|
+
const mint = new PublicKey(token.mintAddr);
|
|
671
|
+
const associatedTokenAddress = await this.retryWithBackoff(() => getAssociatedTokenAddress(mint, this.publicKey), maxRetries);
|
|
744
672
|
if (includeTokenAccounts) {
|
|
745
673
|
tokenAccounts[token.symbol] = associatedTokenAddress.toString();
|
|
746
674
|
}
|
|
747
675
|
try {
|
|
748
|
-
const tokenBalance = await this.retryWithBackoff(() => this.
|
|
676
|
+
const tokenBalance = await this.retryWithBackoff(() => this.nord.solanaConnection.getTokenAccountBalance(associatedTokenAddress), maxRetries);
|
|
749
677
|
const balance = Number(tokenBalance.value.uiAmount);
|
|
750
678
|
if (balance > 0 || includeZeroBalances) {
|
|
751
679
|
balances[token.symbol] = balance;
|
|
@@ -772,10 +700,15 @@ class NordUser extends NordClient_1.NordClient {
|
|
|
772
700
|
return includeTokenAccounts ? { balances, tokenAccounts } : { balances };
|
|
773
701
|
}
|
|
774
702
|
catch (error) {
|
|
775
|
-
throw new
|
|
703
|
+
throw new NordError("Failed to get Solana token balances", {
|
|
776
704
|
cause: error,
|
|
777
705
|
});
|
|
778
706
|
}
|
|
779
707
|
}
|
|
708
|
+
async submitSignedAction(kind, makeSignedMessage) {
|
|
709
|
+
const nonce = this.getNonce();
|
|
710
|
+
const currentTimestamp = await this.nord.getTimestamp();
|
|
711
|
+
const action = createAction(currentTimestamp, nonce, kind);
|
|
712
|
+
return sendAction(this.nord.httpClient, makeSignedMessage, action);
|
|
713
|
+
}
|
|
780
714
|
}
|
|
781
|
-
exports.NordUser = NordUser;
|