@n1xyz/nord-ts 0.1.4 → 0.1.6
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/gen/nord_pb.d.ts +516 -7
- package/dist/gen/nord_pb.js +133 -64
- package/dist/gen/openapi.d.ts +165 -1
- package/dist/nord/api/actions.d.ts +9 -0
- package/dist/nord/api/actions.js +16 -0
- package/dist/nord/client/Nord.d.ts +92 -2
- package/dist/nord/client/Nord.js +150 -2
- package/dist/nord/client/NordAdmin.d.ts +202 -13
- package/dist/nord/client/NordAdmin.js +273 -67
- package/dist/nord/client/NordClient.d.ts +0 -9
- package/dist/nord/client/NordClient.js +0 -48
- package/dist/nord/client/NordUser.js +6 -6
- package/dist/nord/index.d.ts +2 -2
- package/dist/nord/index.js +2 -1
- package/dist/types.d.ts +7 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +5 -0
- package/package.json +2 -2
- package/src/gen/nord_pb.ts +640 -63
- package/src/gen/openapi.ts +165 -1
- package/src/nord/api/actions.ts +26 -1
- package/src/nord/client/Nord.ts +185 -1
- package/src/nord/client/NordAdmin.ts +366 -84
- package/src/nord/client/NordClient.ts +0 -26
- package/src/nord/client/NordUser.ts +7 -6
- package/src/nord/index.ts +1 -2
- package/src/types.ts +9 -0
- package/src/utils.ts +5 -0
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
import * as proto from "../../gen/nord_pb";
|
|
2
1
|
import { create } from "@bufbuild/protobuf";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
2
|
+
import { PublicKey } from "@solana/web3.js";
|
|
3
|
+
import * as proto from "../../gen/nord_pb";
|
|
4
|
+
import { decodeHex } from "../../utils";
|
|
5
|
+
import { createAction, sendAction, expectReceiptKind } from "../api/actions";
|
|
5
6
|
import { NordError } from "../utils/NordError";
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
|
|
7
|
+
import { Nord } from "./Nord";
|
|
8
|
+
import { FeeTierConfig } from "../../gen/nord_pb";
|
|
9
|
+
|
|
10
|
+
// NOTE: keep in sync with `acl.rs`.
|
|
11
|
+
// NOTE: don't forget `number` as int is internally a u32.
|
|
12
|
+
export enum AclRole {
|
|
13
|
+
FEE_MANAGER = 1 << 0,
|
|
14
|
+
MARKET_MANAGER = 1 << 1,
|
|
15
|
+
// TODO: unsure about this?
|
|
16
|
+
ADMIN = 1 << 31,
|
|
17
|
+
}
|
|
9
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Parameters required to register a new token via the admin API.
|
|
21
|
+
*/
|
|
10
22
|
export interface CreateTokenParams {
|
|
11
23
|
tokenDecimals: number;
|
|
12
24
|
weightBps: number;
|
|
13
25
|
viewSymbol: string;
|
|
14
26
|
oracleSymbol: string;
|
|
15
|
-
|
|
27
|
+
mintAddr: PublicKey;
|
|
16
28
|
}
|
|
17
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Parameters used when creating a new market.
|
|
32
|
+
*/
|
|
18
33
|
export interface CreateMarketParams {
|
|
19
34
|
sizeDecimals: number;
|
|
20
35
|
priceDecimals: number;
|
|
@@ -27,114 +42,224 @@ export interface CreateMarketParams {
|
|
|
27
42
|
baseTokenId: number;
|
|
28
43
|
}
|
|
29
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Configuration for updating the Wormhole guardian set on the oracle.
|
|
47
|
+
*/
|
|
30
48
|
export interface PythSetWormholeGuardiansParams {
|
|
31
49
|
guardianSetIndex: number;
|
|
32
|
-
addresses:
|
|
50
|
+
addresses: string[];
|
|
33
51
|
}
|
|
34
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Parameters required to link an oracle symbol to a Pyth price feed.
|
|
55
|
+
*/
|
|
35
56
|
export interface PythSetSymbolFeedParams {
|
|
36
57
|
oracleSymbol: string;
|
|
37
|
-
priceFeedId:
|
|
58
|
+
priceFeedId: string;
|
|
38
59
|
}
|
|
39
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Identifies a market that should be frozen.
|
|
63
|
+
*/
|
|
40
64
|
export interface FreezeMarketParams {
|
|
41
65
|
marketId: number;
|
|
42
66
|
}
|
|
43
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Identifies a market that should be unfrozen.
|
|
70
|
+
*/
|
|
44
71
|
export interface UnfreezeMarketParams {
|
|
45
72
|
marketId: number;
|
|
46
73
|
}
|
|
47
74
|
|
|
48
|
-
|
|
49
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Parameters for adding a new fee tier.
|
|
77
|
+
*/
|
|
78
|
+
export interface AddFeeTierParams {
|
|
79
|
+
config: FeeTierConfig;
|
|
50
80
|
}
|
|
51
81
|
|
|
52
|
-
|
|
53
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Parameters for updating an existing fee tier.
|
|
84
|
+
*/
|
|
85
|
+
export interface UpdateFeeTierParams {
|
|
86
|
+
tierId: number;
|
|
87
|
+
config: FeeTierConfig;
|
|
88
|
+
}
|
|
54
89
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Administrative client capable of submitting privileged configuration actions.
|
|
92
|
+
*/
|
|
93
|
+
export class NordAdmin {
|
|
94
|
+
private readonly nord: Nord;
|
|
95
|
+
private readonly admin: PublicKey;
|
|
96
|
+
private readonly signFn: (x: Uint8Array) => Promise<Uint8Array>;
|
|
60
97
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
this.cloneClientState(copy);
|
|
75
|
-
return copy;
|
|
98
|
+
private constructor({
|
|
99
|
+
nord,
|
|
100
|
+
admin,
|
|
101
|
+
signFn,
|
|
102
|
+
}: {
|
|
103
|
+
nord: Nord;
|
|
104
|
+
admin: PublicKey;
|
|
105
|
+
signFn: (x: Uint8Array) => Promise<Uint8Array>;
|
|
106
|
+
}) {
|
|
107
|
+
this.nord = nord;
|
|
108
|
+
this.admin = admin;
|
|
109
|
+
this.signFn = signFn;
|
|
76
110
|
}
|
|
77
111
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
112
|
+
/** Create a new admin client.
|
|
113
|
+
*
|
|
114
|
+
* @param nord - Nord instance
|
|
115
|
+
* @param admin - The user that will be signing actions.
|
|
116
|
+
* @param signFn - Function to sign messages with the admin's wallet.
|
|
117
|
+
*
|
|
118
|
+
* `signFn` must sign the _hex-encoded_ message, not the raw message itself, for
|
|
119
|
+
* the purpose of being compatible with Solana wallets.
|
|
120
|
+
*
|
|
121
|
+
* In practice, you will do something along the lines of:
|
|
122
|
+
*
|
|
123
|
+
* ```typescript
|
|
124
|
+
* (x) => wallet.signMessage(new TextEncoder().encode(x.toHex()));
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* For a software signing key, this might look more like:
|
|
128
|
+
*
|
|
129
|
+
* ```typescript
|
|
130
|
+
* (x) => nacl.sign.detached(new TextEncoder().encode(x.toHex()), sk);
|
|
131
|
+
* ``
|
|
132
|
+
*
|
|
133
|
+
* where `nacl` is the tweetnacl library.
|
|
134
|
+
*/
|
|
135
|
+
public static new({
|
|
136
|
+
nord,
|
|
137
|
+
admin,
|
|
138
|
+
signFn,
|
|
139
|
+
}: Readonly<{
|
|
140
|
+
nord: Nord;
|
|
141
|
+
admin: PublicKey;
|
|
142
|
+
signFn: (m: Uint8Array) => Promise<Uint8Array>;
|
|
143
|
+
}>): NordAdmin {
|
|
82
144
|
return new NordAdmin({
|
|
83
|
-
nord
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
sessionSignFn: user.sessionSignFn,
|
|
87
|
-
transactionSignFn: user.transactionSignFn,
|
|
88
|
-
connection: user.connection,
|
|
89
|
-
sessionId: user.sessionId,
|
|
90
|
-
sessionPubKey: new Uint8Array(user.sessionPubKey),
|
|
91
|
-
publicKey: user.publicKey,
|
|
92
|
-
signFn: adminSignFn,
|
|
145
|
+
nord,
|
|
146
|
+
admin,
|
|
147
|
+
signFn,
|
|
93
148
|
});
|
|
94
149
|
}
|
|
95
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Submit an action and append the admin signature before sending it to Nord.
|
|
153
|
+
*
|
|
154
|
+
* @param kind - Action payload describing the admin request
|
|
155
|
+
* @throws {NordError} If signing or submission fails
|
|
156
|
+
*/
|
|
96
157
|
private async submitAction(
|
|
97
158
|
kind: proto.Action["kind"],
|
|
98
159
|
): Promise<proto.Receipt> {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
160
|
+
const timestamp = await this.nord.getTimestamp();
|
|
161
|
+
const action = createAction(timestamp, 0, kind);
|
|
162
|
+
return sendAction(
|
|
163
|
+
this.nord.webServerUrl,
|
|
164
|
+
async (xs: Uint8Array) => {
|
|
165
|
+
const signature = await this.signFn(xs);
|
|
166
|
+
if (signature.length !== 64) {
|
|
167
|
+
throw new NordError("invalid signature length; must be 64 bytes");
|
|
168
|
+
}
|
|
169
|
+
return Uint8Array.from([...xs, ...signature]);
|
|
170
|
+
},
|
|
171
|
+
action,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Set acl permissions for a given user.
|
|
176
|
+
*
|
|
177
|
+
* If all roles are removed, the user is removed from the acl.
|
|
178
|
+
*
|
|
179
|
+
* @param target - User to update.
|
|
180
|
+
* @param addRoles - Roles to add to the user.
|
|
181
|
+
* @param removeRoles - Reles to remove from the user.
|
|
182
|
+
*/
|
|
183
|
+
async updateAcl({
|
|
184
|
+
target,
|
|
185
|
+
addRoles,
|
|
186
|
+
removeRoles,
|
|
187
|
+
}: Readonly<{
|
|
188
|
+
target: PublicKey;
|
|
189
|
+
addRoles: AclRole[];
|
|
190
|
+
removeRoles: AclRole[];
|
|
191
|
+
}>): Promise<{ actionId: bigint } & proto.Receipt_AclUpdated> {
|
|
192
|
+
const allRoles = addRoles.concat(removeRoles);
|
|
193
|
+
if (allRoles.length !== new Set(allRoles).size) {
|
|
194
|
+
throw new NordError("duplicate roles in acl update; must be unique");
|
|
111
195
|
}
|
|
196
|
+
|
|
197
|
+
let mask = 0;
|
|
198
|
+
let values = 0;
|
|
199
|
+
for (const role of allRoles) {
|
|
200
|
+
mask |= role;
|
|
201
|
+
}
|
|
202
|
+
for (const role of addRoles) {
|
|
203
|
+
values |= role;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const receipt = await this.submitAction({
|
|
207
|
+
case: "updateAcl",
|
|
208
|
+
value: create(proto.Action_UpdateAclSchema, {
|
|
209
|
+
aclPubkey: this.admin.toBytes(),
|
|
210
|
+
targetPubkey: target.toBytes(),
|
|
211
|
+
}),
|
|
212
|
+
});
|
|
213
|
+
expectReceiptKind(receipt, "aclUpdated", "update acl");
|
|
214
|
+
|
|
215
|
+
return { ...receipt.kind.value, actionId: receipt.actionId };
|
|
112
216
|
}
|
|
113
217
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
218
|
+
/**
|
|
219
|
+
* Register a new token that can be listed on Nord.
|
|
220
|
+
*
|
|
221
|
+
* @param params - Token configuration values
|
|
222
|
+
* @returns Action identifier and resulting token metadata
|
|
223
|
+
* @throws {NordError} If the action submission fails
|
|
224
|
+
*/
|
|
225
|
+
async createToken({
|
|
226
|
+
tokenDecimals,
|
|
227
|
+
weightBps,
|
|
228
|
+
viewSymbol,
|
|
229
|
+
oracleSymbol,
|
|
230
|
+
mintAddr,
|
|
231
|
+
}: CreateTokenParams): Promise<
|
|
232
|
+
{ actionId: bigint } & proto.Receipt_InsertTokenResult
|
|
233
|
+
> {
|
|
118
234
|
const receipt = await this.submitAction({
|
|
119
235
|
case: "createToken",
|
|
120
236
|
value: create(proto.Action_CreateTokenSchema, {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
237
|
+
aclPubkey: this.admin.toBytes(),
|
|
238
|
+
tokenDecimals,
|
|
239
|
+
weightBps,
|
|
240
|
+
viewSymbol,
|
|
241
|
+
oracleSymbol,
|
|
242
|
+
solAddr: mintAddr.toBytes(),
|
|
126
243
|
}),
|
|
127
244
|
});
|
|
128
|
-
|
|
245
|
+
expectReceiptKind(receipt, "insertTokenResult", "create token");
|
|
129
246
|
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
130
247
|
}
|
|
131
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Open a new market with the provided trading parameters.
|
|
251
|
+
*
|
|
252
|
+
* @param params - Market configuration to apply
|
|
253
|
+
* @returns Action identifier and resulting market metadata
|
|
254
|
+
* @throws {NordError} If the action submission fails
|
|
255
|
+
*/
|
|
132
256
|
async createMarket(
|
|
133
257
|
params: CreateMarketParams,
|
|
134
258
|
): Promise<{ actionId: bigint } & proto.Receipt_InsertMarketResult> {
|
|
135
259
|
const receipt = await this.submitAction({
|
|
136
260
|
case: "createMarket",
|
|
137
261
|
value: create(proto.Action_CreateMarketSchema, {
|
|
262
|
+
aclPubkey: this.admin.toBytes(),
|
|
138
263
|
sizeDecimals: params.sizeDecimals,
|
|
139
264
|
priceDecimals: params.priceDecimals,
|
|
140
265
|
imfBps: params.imfBps,
|
|
@@ -146,21 +271,48 @@ export class NordAdmin extends NordClient {
|
|
|
146
271
|
baseTokenId: params.baseTokenId,
|
|
147
272
|
}),
|
|
148
273
|
});
|
|
149
|
-
|
|
274
|
+
expectReceiptKind(receipt, "insertMarketResult", "create market");
|
|
150
275
|
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
151
276
|
}
|
|
152
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Update the Pyth guardian set used for verifying Wormhole messages.
|
|
280
|
+
*
|
|
281
|
+
* Each address must decode from a 20-byte hex string (with or without a
|
|
282
|
+
* leading `0x` prefix). The engine validates the supplied guardian set index
|
|
283
|
+
* before applying the update.
|
|
284
|
+
*
|
|
285
|
+
* @param params - Guardian set index and guardian addresses
|
|
286
|
+
* @returns Action identifier and guardian update receipt
|
|
287
|
+
* @throws {NordError} If the action submission fails
|
|
288
|
+
*/
|
|
153
289
|
async pythSetWormholeGuardians(
|
|
154
290
|
params: PythSetWormholeGuardiansParams,
|
|
155
291
|
): Promise<{ actionId: bigint } & proto.Receipt_UpdateGuardianSetResult> {
|
|
292
|
+
const addresses = params.addresses.map((address) => {
|
|
293
|
+
try {
|
|
294
|
+
const decoded = decodeHex(address);
|
|
295
|
+
if (decoded.length !== 20) {
|
|
296
|
+
throw new Error("guardian address must be 20 bytes");
|
|
297
|
+
}
|
|
298
|
+
return decoded;
|
|
299
|
+
} catch (e) {
|
|
300
|
+
throw new NordError(
|
|
301
|
+
"invalid guardian address; must be a 20 byte hex address",
|
|
302
|
+
{ cause: e },
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
156
307
|
const receipt = await this.submitAction({
|
|
157
308
|
case: "pythSetWormholeGuardians",
|
|
158
309
|
value: create(proto.Action_PythSetWormholeGuardiansSchema, {
|
|
310
|
+
aclPubkey: this.admin.toBytes(),
|
|
159
311
|
guardianSetIndex: params.guardianSetIndex,
|
|
160
|
-
addresses
|
|
312
|
+
addresses,
|
|
161
313
|
}),
|
|
162
314
|
});
|
|
163
|
-
|
|
315
|
+
expectReceiptKind(
|
|
164
316
|
receipt,
|
|
165
317
|
"updateGuardianSetResult",
|
|
166
318
|
"update wormhole guardians",
|
|
@@ -168,42 +320,85 @@ export class NordAdmin extends NordClient {
|
|
|
168
320
|
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
169
321
|
}
|
|
170
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Link an oracle symbol to a specific Pyth price feed.
|
|
325
|
+
*
|
|
326
|
+
* The price feed identifier must decode to 32 bytes (with or without a
|
|
327
|
+
* leading `0x` prefix). Use this call to create or update the mapping used
|
|
328
|
+
* by the oracle integration.
|
|
329
|
+
*
|
|
330
|
+
* @param params - Oracle symbol and price feed identifier
|
|
331
|
+
* @returns Action identifier and symbol feed receipt
|
|
332
|
+
* @throws {NordError} If the action submission fails
|
|
333
|
+
*/
|
|
171
334
|
async pythSetSymbolFeed(
|
|
172
335
|
params: PythSetSymbolFeedParams,
|
|
173
336
|
): Promise<{ actionId: bigint } & proto.Receipt_OracleSymbolFeedResult> {
|
|
337
|
+
let priceFeedId: Uint8Array;
|
|
338
|
+
try {
|
|
339
|
+
priceFeedId = decodeHex(params.priceFeedId);
|
|
340
|
+
if (priceFeedId.length !== 32) {
|
|
341
|
+
throw new Error("price feed id must be 32 bytes");
|
|
342
|
+
}
|
|
343
|
+
} catch (e) {
|
|
344
|
+
throw new NordError("invalid price feed id; must be a 32 byte hex id", {
|
|
345
|
+
cause: e,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
174
349
|
const receipt = await this.submitAction({
|
|
175
350
|
case: "pythSetSymbolFeed",
|
|
176
351
|
value: create(proto.Action_PythSetSymbolFeedSchema, {
|
|
352
|
+
aclPubkey: this.admin.toBytes(),
|
|
177
353
|
oracleSymbol: params.oracleSymbol,
|
|
178
|
-
priceFeedId
|
|
354
|
+
priceFeedId,
|
|
179
355
|
}),
|
|
180
356
|
});
|
|
181
|
-
|
|
182
|
-
receipt,
|
|
183
|
-
"oracleSymbolFeedResult",
|
|
184
|
-
"set symbol feed",
|
|
185
|
-
);
|
|
357
|
+
expectReceiptKind(receipt, "oracleSymbolFeedResult", "set symbol feed");
|
|
186
358
|
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
187
359
|
}
|
|
188
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Pause all trading activity on the exchange.
|
|
363
|
+
*
|
|
364
|
+
* @returns Action identifier confirming the pause
|
|
365
|
+
* @throws {NordError} If the action submission fails
|
|
366
|
+
*/
|
|
189
367
|
async pause(): Promise<{ actionId: bigint }> {
|
|
190
368
|
const receipt = await this.submitAction({
|
|
191
369
|
case: "pause",
|
|
192
|
-
value: create(proto.Action_PauseSchema, {
|
|
370
|
+
value: create(proto.Action_PauseSchema, {
|
|
371
|
+
aclPubkey: this.admin.toBytes(),
|
|
372
|
+
}),
|
|
193
373
|
});
|
|
194
|
-
|
|
374
|
+
expectReceiptKind(receipt, "paused", "pause");
|
|
195
375
|
return { actionId: receipt.actionId };
|
|
196
376
|
}
|
|
197
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Resume trading activity after a pause.
|
|
380
|
+
*
|
|
381
|
+
* @returns Action identifier confirming the unpause
|
|
382
|
+
* @throws {NordError} If the action submission fails
|
|
383
|
+
*/
|
|
198
384
|
async unpause(): Promise<{ actionId: bigint }> {
|
|
199
385
|
const receipt = await this.submitAction({
|
|
200
386
|
case: "unpause",
|
|
201
|
-
value: create(proto.Action_UnpauseSchema, {
|
|
387
|
+
value: create(proto.Action_UnpauseSchema, {
|
|
388
|
+
aclPubkey: this.admin.toBytes(),
|
|
389
|
+
}),
|
|
202
390
|
});
|
|
203
|
-
|
|
391
|
+
expectReceiptKind(receipt, "unpaused", "unpause");
|
|
204
392
|
return { actionId: receipt.actionId };
|
|
205
393
|
}
|
|
206
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Freeze an individual market, preventing new trades and orders.
|
|
397
|
+
*
|
|
398
|
+
* @param params - Target market identifier
|
|
399
|
+
* @returns Action identifier and freeze receipt
|
|
400
|
+
* @throws {NordError} If the action submission fails
|
|
401
|
+
*/
|
|
207
402
|
async freezeMarket(
|
|
208
403
|
params: FreezeMarketParams,
|
|
209
404
|
): Promise<{ actionId: bigint } & proto.Receipt_MarketFreezeUpdated> {
|
|
@@ -211,12 +406,20 @@ export class NordAdmin extends NordClient {
|
|
|
211
406
|
case: "freezeMarket",
|
|
212
407
|
value: create(proto.Action_FreezeMarketSchema, {
|
|
213
408
|
marketId: params.marketId,
|
|
409
|
+
aclPubkey: this.admin.toBytes(),
|
|
214
410
|
}),
|
|
215
411
|
});
|
|
216
|
-
|
|
412
|
+
expectReceiptKind(receipt, "marketFreezeUpdated", "freeze market");
|
|
217
413
|
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
218
414
|
}
|
|
219
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Unfreeze a market that was previously halted.
|
|
418
|
+
*
|
|
419
|
+
* @param params - Target market identifier
|
|
420
|
+
* @returns Action identifier and freeze receipt
|
|
421
|
+
* @throws {NordError} If the action submission fails
|
|
422
|
+
*/
|
|
220
423
|
async unfreezeMarket(
|
|
221
424
|
params: UnfreezeMarketParams,
|
|
222
425
|
): Promise<{ actionId: bigint } & proto.Receipt_MarketFreezeUpdated> {
|
|
@@ -224,9 +427,88 @@ export class NordAdmin extends NordClient {
|
|
|
224
427
|
case: "unfreezeMarket",
|
|
225
428
|
value: create(proto.Action_UnfreezeMarketSchema, {
|
|
226
429
|
marketId: params.marketId,
|
|
430
|
+
aclPubkey: this.admin.toBytes(),
|
|
431
|
+
}),
|
|
432
|
+
});
|
|
433
|
+
expectReceiptKind(receipt, "marketFreezeUpdated", "unfreeze market");
|
|
434
|
+
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Append a new fee tier to the account bracket configuration.
|
|
439
|
+
*
|
|
440
|
+
* - The engine supports at most 16 tiers (ids 0–15). Tier 0 is reserved for
|
|
441
|
+
* the default Nord fees; use `updateFeeTier` if you need to change it.
|
|
442
|
+
* - The first appended tier receives id 1, and subsequent tiers increment the id.
|
|
443
|
+
*
|
|
444
|
+
* @param params - Fee tier configuration to insert
|
|
445
|
+
* @returns Action identifier and fee tier addition receipt
|
|
446
|
+
* @throws {NordError} If the action submission fails or the new tier exceeds the maximum range (0-15).
|
|
447
|
+
*/
|
|
448
|
+
async addFeeTier(
|
|
449
|
+
params: AddFeeTierParams,
|
|
450
|
+
): Promise<{ actionId: bigint } & proto.Receipt_FeeTierAdded> {
|
|
451
|
+
const receipt = await this.submitAction({
|
|
452
|
+
case: "addFeeTier",
|
|
453
|
+
value: create(proto.Action_AddFeeTierSchema, {
|
|
454
|
+
aclPubkey: this.admin.toBytes(),
|
|
455
|
+
config: create(proto.FeeTierConfigSchema, params.config),
|
|
456
|
+
}),
|
|
457
|
+
});
|
|
458
|
+
expectReceiptKind(receipt, "feeTierAdded", "add fee tier");
|
|
459
|
+
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Update an existing fee tier with new maker/taker rates.
|
|
464
|
+
*
|
|
465
|
+
* Tier identifiers must already exist; attempting to update a missing tier
|
|
466
|
+
* causes the action to fail.
|
|
467
|
+
*
|
|
468
|
+
* @param params - Fee tier identifier and updated configuration
|
|
469
|
+
* @returns Action identifier and fee tier update receipt
|
|
470
|
+
* @throws {NordError} If the action submission fails or the tier ID exceeds the configured range.
|
|
471
|
+
*/
|
|
472
|
+
async updateFeeTier(
|
|
473
|
+
params: UpdateFeeTierParams,
|
|
474
|
+
): Promise<{ actionId: bigint } & proto.Receipt_FeeTierUpdated> {
|
|
475
|
+
const receipt = await this.submitAction({
|
|
476
|
+
case: "updateFeeTier",
|
|
477
|
+
value: create(proto.Action_UpdateFeeTierSchema, {
|
|
478
|
+
aclPubkey: this.admin.toBytes(),
|
|
479
|
+
id: params.tierId,
|
|
480
|
+
config: create(proto.FeeTierConfigSchema, params.config),
|
|
481
|
+
}),
|
|
482
|
+
});
|
|
483
|
+
expectReceiptKind(receipt, "feeTierUpdated", "update fee tier");
|
|
484
|
+
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Assign a fee tier to one or more accounts.
|
|
489
|
+
*
|
|
490
|
+
* The tier id must be within the configured range (0–15). Every account starts
|
|
491
|
+
* on tier 0; assigning it to another tier requires that tier to exist already.
|
|
492
|
+
* Invalid account ids or tier ids cause the action to fail.
|
|
493
|
+
*
|
|
494
|
+
* @param accounts - Account IDs to update
|
|
495
|
+
* @param tierId - Target fee tier identifier
|
|
496
|
+
* @returns Action identifier and accounts-tier receipt
|
|
497
|
+
* @throws {NordError} If the tier id exceeds the configured range or an account id is invalid.
|
|
498
|
+
*/
|
|
499
|
+
async updateAccountsTier(
|
|
500
|
+
accounts: number[],
|
|
501
|
+
tierId: number,
|
|
502
|
+
): Promise<{ actionId: bigint } & proto.Receipt_AccountsTierUpdated> {
|
|
503
|
+
const receipt = await this.submitAction({
|
|
504
|
+
case: "updateAccountsTier",
|
|
505
|
+
value: create(proto.Action_UpdateAccountsTierSchema, {
|
|
506
|
+
aclPubkey: this.admin.toBytes(),
|
|
507
|
+
accounts,
|
|
508
|
+
tierId,
|
|
227
509
|
}),
|
|
228
510
|
});
|
|
229
|
-
|
|
511
|
+
expectReceiptKind(receipt, "accountsTierUpdated", "update accounts tier");
|
|
230
512
|
return { actionId: receipt.actionId, ...receipt.kind.value };
|
|
231
513
|
}
|
|
232
514
|
}
|
|
@@ -2,15 +2,8 @@ import { Connection, PublicKey } from "@solana/web3.js";
|
|
|
2
2
|
import type { Transaction } from "@solana/web3.js";
|
|
3
3
|
import * as proto from "../../gen/nord_pb";
|
|
4
4
|
import { createAction, sendAction } from "../api/actions";
|
|
5
|
-
import { NordError } from "../utils/NordError";
|
|
6
5
|
import { Nord } from "./Nord";
|
|
7
6
|
|
|
8
|
-
type ReceiptKind = NonNullable<proto.Receipt["kind"]>;
|
|
9
|
-
type ExtractReceiptKind<K extends ReceiptKind["case"]> = Extract<
|
|
10
|
-
ReceiptKind,
|
|
11
|
-
{ case: K }
|
|
12
|
-
>;
|
|
13
|
-
|
|
14
7
|
export interface NordClientParams {
|
|
15
8
|
nord: Nord;
|
|
16
9
|
address: PublicKey;
|
|
@@ -83,23 +76,4 @@ export abstract class NordClient {
|
|
|
83
76
|
getSolanaPublicKey(): PublicKey {
|
|
84
77
|
return this.address;
|
|
85
78
|
}
|
|
86
|
-
|
|
87
|
-
protected expectReceiptKind<K extends ReceiptKind["case"]>(
|
|
88
|
-
receipt: proto.Receipt,
|
|
89
|
-
expected: K,
|
|
90
|
-
action: string,
|
|
91
|
-
): asserts receipt is proto.Receipt & { kind: ExtractReceiptKind<K> } {
|
|
92
|
-
if (receipt.kind?.case !== expected) {
|
|
93
|
-
const label = this.formatReceiptError(receipt);
|
|
94
|
-
throw new NordError(`Failed to ${action}: ${label}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
protected formatReceiptError(receipt: proto.Receipt): string {
|
|
99
|
-
if (receipt.kind?.case === "err") {
|
|
100
|
-
const err = receipt.kind.value;
|
|
101
|
-
return proto.Error[err] ?? err.toString();
|
|
102
|
-
}
|
|
103
|
-
return receipt.kind?.case ?? "unknown";
|
|
104
|
-
}
|
|
105
79
|
}
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
createSession,
|
|
40
40
|
revokeSession,
|
|
41
41
|
atomic,
|
|
42
|
+
expectReceiptKind,
|
|
42
43
|
AtomicSubaction,
|
|
43
44
|
} from "../api/actions";
|
|
44
45
|
import { NordError } from "../utils/NordError";
|
|
@@ -737,7 +738,7 @@ export class NordUser extends NordClient {
|
|
|
737
738
|
amount: scaledAmount,
|
|
738
739
|
}),
|
|
739
740
|
});
|
|
740
|
-
|
|
741
|
+
expectReceiptKind(receipt, "withdrawResult", "withdraw");
|
|
741
742
|
return { actionId: receipt.actionId };
|
|
742
743
|
} catch (error) {
|
|
743
744
|
throw new NordError(
|
|
@@ -800,7 +801,7 @@ export class NordUser extends NordClient {
|
|
|
800
801
|
: BigInt(params.clientOrderId),
|
|
801
802
|
}),
|
|
802
803
|
});
|
|
803
|
-
|
|
804
|
+
expectReceiptKind(receipt, "placeOrderResult", "place order");
|
|
804
805
|
const result = receipt.kind.value;
|
|
805
806
|
return {
|
|
806
807
|
actionId: receipt.actionId,
|
|
@@ -840,7 +841,7 @@ export class NordUser extends NordClient {
|
|
|
840
841
|
senderAccountId: accountId,
|
|
841
842
|
}),
|
|
842
843
|
});
|
|
843
|
-
|
|
844
|
+
expectReceiptKind(receipt, "cancelOrderResult", "cancel order");
|
|
844
845
|
return {
|
|
845
846
|
actionId: receipt.actionId,
|
|
846
847
|
orderId: receipt.kind.value.orderId,
|
|
@@ -900,7 +901,7 @@ export class NordUser extends NordClient {
|
|
|
900
901
|
accountId: params.accountId,
|
|
901
902
|
}),
|
|
902
903
|
});
|
|
903
|
-
|
|
904
|
+
expectReceiptKind(receipt, "triggerAdded", "add trigger");
|
|
904
905
|
return { actionId: receipt.actionId };
|
|
905
906
|
} catch (error) {
|
|
906
907
|
throw new NordError("Failed to add trigger", { cause: error });
|
|
@@ -939,7 +940,7 @@ export class NordUser extends NordClient {
|
|
|
939
940
|
accountId: params.accountId,
|
|
940
941
|
}),
|
|
941
942
|
});
|
|
942
|
-
|
|
943
|
+
expectReceiptKind(receipt, "triggerRemoved", "remove trigger");
|
|
943
944
|
return { actionId: receipt.actionId };
|
|
944
945
|
} catch (error) {
|
|
945
946
|
throw new NordError("Failed to remove trigger", { cause: error });
|
|
@@ -972,7 +973,7 @@ export class NordUser extends NordClient {
|
|
|
972
973
|
amount,
|
|
973
974
|
}),
|
|
974
975
|
});
|
|
975
|
-
|
|
976
|
+
expectReceiptKind(receipt, "transferred", "transfer tokens");
|
|
976
977
|
} catch (error) {
|
|
977
978
|
throw new NordError("Failed to transfer tokens", { cause: error });
|
|
978
979
|
}
|