@acta-markets/ts-sdk 0.0.4-beta → 0.0.6-beta
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/README.md +4 -1
- package/dist/cjs/events.test.js +6 -0
- package/dist/cjs/generated/types/actaEvent.js +6 -0
- package/dist/cjs/idl/acta_contract.json +17 -0
- package/dist/cjs/idl/hash.js +1 -1
- package/dist/cjs/ws/client.js +109 -14
- package/dist/cjs/ws/client.test.js +199 -3
- package/dist/cjs/ws/discovery.js +2 -0
- package/dist/cjs/ws/wirePolicy.js +16 -0
- package/dist/cjs/ws/wirePolicy.test.js +22 -0
- package/dist/events.test.js +6 -0
- package/dist/generated/types/actaEvent.d.ts +6 -0
- package/dist/generated/types/actaEvent.js +6 -0
- package/dist/idl/acta_contract.json +17 -0
- package/dist/idl/hash.d.ts +1 -1
- package/dist/idl/hash.js +1 -1
- package/dist/ws/client.d.ts +18 -13
- package/dist/ws/client.js +110 -15
- package/dist/ws/client.test.js +199 -3
- package/dist/ws/discovery.js +2 -0
- package/dist/ws/types.d.ts +70 -5
- package/dist/ws/wirePolicy.d.ts +6 -0
- package/dist/ws/wirePolicy.js +15 -0
- package/dist/ws/wirePolicy.test.d.ts +1 -0
- package/dist/ws/wirePolicy.test.js +20 -0
- package/package.json +1 -1
package/dist/ws/client.test.js
CHANGED
|
@@ -11,6 +11,7 @@ const WELCOME_MESSAGE = {
|
|
|
11
11
|
enabled_features: [],
|
|
12
12
|
},
|
|
13
13
|
};
|
|
14
|
+
const createdClients = [];
|
|
14
15
|
class MockWebSocket {
|
|
15
16
|
url;
|
|
16
17
|
readyState = WS_CONNECTING;
|
|
@@ -65,6 +66,7 @@ function makeHarness(overrides = {}) {
|
|
|
65
66
|
return currentSocket;
|
|
66
67
|
},
|
|
67
68
|
});
|
|
69
|
+
createdClients.push(client);
|
|
68
70
|
return {
|
|
69
71
|
client,
|
|
70
72
|
socket: () => {
|
|
@@ -81,6 +83,9 @@ async function flushMicrotasks() {
|
|
|
81
83
|
}
|
|
82
84
|
describe("ActaWsClient", () => {
|
|
83
85
|
afterEach(() => {
|
|
86
|
+
for (const client of createdClients.splice(0, createdClients.length)) {
|
|
87
|
+
client.disconnect();
|
|
88
|
+
}
|
|
84
89
|
jest.restoreAllMocks();
|
|
85
90
|
jest.useRealTimers();
|
|
86
91
|
});
|
|
@@ -167,13 +172,204 @@ describe("ActaWsClient", () => {
|
|
|
167
172
|
});
|
|
168
173
|
it("emits authError reason on AuthError", () => {
|
|
169
174
|
const { client } = makeHarness();
|
|
170
|
-
const
|
|
171
|
-
client.on("authError", (reason) =>
|
|
175
|
+
const errors = [];
|
|
176
|
+
client.on("authError", (reason, message) => errors.push({ reason, message }));
|
|
172
177
|
client.handleMessage({
|
|
173
178
|
type: "AuthError",
|
|
174
179
|
data: { reason: "invalid_signature", message: "bad signature bytes" },
|
|
175
180
|
});
|
|
176
|
-
expect(
|
|
181
|
+
expect(errors).toEqual([
|
|
182
|
+
{ reason: "invalid_signature", message: "bad signature bytes" },
|
|
183
|
+
]);
|
|
184
|
+
});
|
|
185
|
+
it("uses last auth session for resume-first reconnect", async () => {
|
|
186
|
+
const { client, socket } = makeHarness();
|
|
187
|
+
const auth = makeAuthProvider("WalletPubkey", "WalletSignature");
|
|
188
|
+
client.connectAndAuthenticate(auth);
|
|
189
|
+
let ws = socket();
|
|
190
|
+
ws.triggerOpen();
|
|
191
|
+
ws.triggerMessage(WELCOME_MESSAGE);
|
|
192
|
+
await flushMicrotasks();
|
|
193
|
+
ws.triggerMessage({
|
|
194
|
+
type: "AuthSuccess",
|
|
195
|
+
data: { session_id: "persisted-session", expires_at: 1_710_086_400 },
|
|
196
|
+
});
|
|
197
|
+
ws.triggerClose(1006, "network_drop");
|
|
198
|
+
client.connectAndAuthenticate(auth);
|
|
199
|
+
ws = socket();
|
|
200
|
+
ws.triggerOpen();
|
|
201
|
+
ws.triggerMessage(WELCOME_MESSAGE);
|
|
202
|
+
await flushMicrotasks();
|
|
203
|
+
const sentTypes = ws.sent.map((payload) => parseClientMessage(payload).type);
|
|
204
|
+
expect(sentTypes).toEqual(["Hello", "ResumeAuth"]);
|
|
205
|
+
const resumeAuth = parseClientMessage(ws.sent[1]);
|
|
206
|
+
expect(resumeAuth.type).toBe("ResumeAuth");
|
|
207
|
+
if (resumeAuth.type === "ResumeAuth") {
|
|
208
|
+
expect(resumeAuth.data.session_id).toBe("persisted-session");
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
it("sends Logout and emits logoutSuccess", () => {
|
|
212
|
+
const { client, socket } = makeHarness();
|
|
213
|
+
const events = [];
|
|
214
|
+
client.on("logoutSuccess", () => events.push("logoutSuccess"));
|
|
215
|
+
client.connectAnonymous();
|
|
216
|
+
const ws = socket();
|
|
217
|
+
ws.triggerOpen();
|
|
218
|
+
ws.triggerMessage(WELCOME_MESSAGE);
|
|
219
|
+
client.logout();
|
|
220
|
+
const sent = parseClientMessage(ws.sent[1]);
|
|
221
|
+
expect(sent.type).toBe("Logout");
|
|
222
|
+
ws.triggerMessage({ type: "LogoutSuccess", data: {} });
|
|
223
|
+
expect(events).toEqual(["logoutSuccess"]);
|
|
224
|
+
});
|
|
225
|
+
it("adds request_id to public request messages", () => {
|
|
226
|
+
const { client, socket } = makeHarness();
|
|
227
|
+
client.connectAnonymous();
|
|
228
|
+
const ws = socket();
|
|
229
|
+
ws.triggerOpen();
|
|
230
|
+
ws.triggerMessage(WELCOME_MESSAGE);
|
|
231
|
+
const getMarketsId = client.getMarkets();
|
|
232
|
+
const getMarketDescriptorsId = client.getMarketDescriptors();
|
|
233
|
+
const getTokensId = client.getTokens();
|
|
234
|
+
const getExpiriesId = client.getExpiries();
|
|
235
|
+
const getActiveRfqsId = client.getActiveRfqs();
|
|
236
|
+
const getIndicativePricesId = client.getIndicativePrices({
|
|
237
|
+
market: "market-1",
|
|
238
|
+
position_type: "covered_call",
|
|
239
|
+
});
|
|
240
|
+
const sent = ws.sent.slice(1).map(parseClientMessage);
|
|
241
|
+
const withRequestId = sent.filter((msg) => msg.type !== "Logout");
|
|
242
|
+
for (const msg of withRequestId) {
|
|
243
|
+
if ("data" in msg) {
|
|
244
|
+
const data = msg.data;
|
|
245
|
+
expect(typeof data.request_id).toBe("string");
|
|
246
|
+
expect(data.request_id.length).toBeGreaterThan(0);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
expect(sent[0].data.request_id).toBe(getMarketsId);
|
|
250
|
+
expect(sent[1].data.request_id).toBe(getMarketDescriptorsId);
|
|
251
|
+
expect(sent[2].data.request_id).toBe(getTokensId);
|
|
252
|
+
expect(sent[3].data.request_id).toBe(getExpiriesId);
|
|
253
|
+
expect(sent[4].data.request_id).toBe(getActiveRfqsId);
|
|
254
|
+
expect(sent[5].data.request_id).toBe(getIndicativePricesId);
|
|
255
|
+
});
|
|
256
|
+
it("adds request_id to authenticated request messages", () => {
|
|
257
|
+
const { client, socket } = makeHarness();
|
|
258
|
+
client.connectAnonymous();
|
|
259
|
+
const ws = socket();
|
|
260
|
+
ws.triggerOpen();
|
|
261
|
+
ws.triggerMessage(WELCOME_MESSAGE);
|
|
262
|
+
client.handleMessage({
|
|
263
|
+
type: "AuthSuccess",
|
|
264
|
+
data: { session_id: "session-id", expires_at: 1_710_086_400 },
|
|
265
|
+
});
|
|
266
|
+
const getPositionsId = client.getPositions();
|
|
267
|
+
const getMyActiveRfqsId = client.getMyActiveRfqs();
|
|
268
|
+
const getOrderStatusId = client.getOrderStatus("11".repeat(32));
|
|
269
|
+
const cancelRfqId = client.cancelRfq("rfq-1");
|
|
270
|
+
const cancelQuoteId = client.cancelQuote("rfq-2");
|
|
271
|
+
const sent = ws.sent.slice(1).map(parseClientMessage);
|
|
272
|
+
const authRequests = sent.filter((msg) => [
|
|
273
|
+
"GetPositions",
|
|
274
|
+
"GetMyActiveRfqs",
|
|
275
|
+
"GetOrderStatus",
|
|
276
|
+
"CancelRfq",
|
|
277
|
+
"CancelQuote",
|
|
278
|
+
].includes(msg.type));
|
|
279
|
+
expect(authRequests).toHaveLength(5);
|
|
280
|
+
for (const msg of authRequests) {
|
|
281
|
+
if ("data" in msg) {
|
|
282
|
+
const data = msg.data;
|
|
283
|
+
expect(typeof data.request_id).toBe("string");
|
|
284
|
+
expect(data.request_id.length).toBeGreaterThan(0);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
expect(authRequests[0].data.request_id).toBe(getPositionsId);
|
|
288
|
+
expect(authRequests[1].data.request_id).toBe(getMyActiveRfqsId);
|
|
289
|
+
expect(authRequests[2].data.request_id).toBe(getOrderStatusId);
|
|
290
|
+
expect(authRequests[3].data.request_id).toBe(cancelRfqId);
|
|
291
|
+
expect(authRequests[4].data.request_id).toBe(cancelQuoteId);
|
|
292
|
+
});
|
|
293
|
+
it("createRfq rejects when market descriptor is missing", async () => {
|
|
294
|
+
const { client, socket } = makeHarness();
|
|
295
|
+
client.connectAnonymous();
|
|
296
|
+
const ws = socket();
|
|
297
|
+
ws.triggerOpen();
|
|
298
|
+
ws.triggerMessage(WELCOME_MESSAGE);
|
|
299
|
+
client.handleMessage({
|
|
300
|
+
type: "AuthSuccess",
|
|
301
|
+
data: { session_id: "session-id", expires_at: 1_710_086_400 },
|
|
302
|
+
});
|
|
303
|
+
await expect(client.createRfq({
|
|
304
|
+
market: "market-1",
|
|
305
|
+
position_type: "covered_call",
|
|
306
|
+
strike: 100_000_000_000,
|
|
307
|
+
quantity: 2_000_000_000,
|
|
308
|
+
})).rejects.toThrow("Missing market descriptor");
|
|
309
|
+
const sentTypes = ws.sent.map((payload) => parseClientMessage(payload).type);
|
|
310
|
+
expect(sentTypes).not.toContain("RfqRequest");
|
|
311
|
+
});
|
|
312
|
+
it("createRfq validates quantity against market size rule", async () => {
|
|
313
|
+
const { client, socket } = makeHarness();
|
|
314
|
+
client.connectAnonymous();
|
|
315
|
+
const ws = socket();
|
|
316
|
+
ws.triggerOpen();
|
|
317
|
+
ws.triggerMessage(WELCOME_MESSAGE);
|
|
318
|
+
client.handleMessage({
|
|
319
|
+
type: "AuthSuccess",
|
|
320
|
+
data: { session_id: "session-id", expires_at: 1_710_086_400 },
|
|
321
|
+
});
|
|
322
|
+
client.handleMessage({
|
|
323
|
+
type: "MarketDescriptors",
|
|
324
|
+
data: {
|
|
325
|
+
request_id: "req-1",
|
|
326
|
+
markets: [
|
|
327
|
+
{
|
|
328
|
+
market: {
|
|
329
|
+
chain_id: 1,
|
|
330
|
+
program_id: "program-1",
|
|
331
|
+
market_pda: "market-1",
|
|
332
|
+
underlying_mint: "So11111111111111111111111111111111111111112",
|
|
333
|
+
quote_mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
334
|
+
expiry_ts: 1_800_000_000,
|
|
335
|
+
is_put: false,
|
|
336
|
+
collateral_mint: "So11111111111111111111111111111111111111112",
|
|
337
|
+
settlement_mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
338
|
+
},
|
|
339
|
+
underlying_oracle_pda: "oracle-underlying",
|
|
340
|
+
quote_oracle_pda: "oracle-quote",
|
|
341
|
+
underlying_decimals: 9,
|
|
342
|
+
quote_decimals: 6,
|
|
343
|
+
size_rule: {
|
|
344
|
+
min_size: 2_000_000_000,
|
|
345
|
+
max_size: 10_000_000_000,
|
|
346
|
+
step: 1_000_000_000,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
await expect(client.createRfq({
|
|
353
|
+
market: "market-1",
|
|
354
|
+
position_type: "covered_call",
|
|
355
|
+
strike: 100_000_000_000,
|
|
356
|
+
quantity: 2_500_000_000,
|
|
357
|
+
})).rejects.toThrow("(quantity - min_size) % step == 0");
|
|
358
|
+
await client.createRfq({
|
|
359
|
+
market: "market-1",
|
|
360
|
+
position_type: "covered_call",
|
|
361
|
+
strike: 100_000_000_000,
|
|
362
|
+
quantity: 3_000_000_000,
|
|
363
|
+
});
|
|
364
|
+
const sentMessages = ws.sent.map(parseClientMessage);
|
|
365
|
+
const rfqRequest = sentMessages
|
|
366
|
+
.filter((message) => message.type === "RfqRequest")
|
|
367
|
+
.at(-1);
|
|
368
|
+
expect(rfqRequest?.type).toBe("RfqRequest");
|
|
369
|
+
if (rfqRequest?.type === "RfqRequest") {
|
|
370
|
+
expect(rfqRequest.data.market).toBe("market-1");
|
|
371
|
+
expect(rfqRequest.data.quantity).toBe(3_000_000_000);
|
|
372
|
+
}
|
|
177
373
|
});
|
|
178
374
|
it("drop_oldest policy keeps the latest queued messages", () => {
|
|
179
375
|
const { client, socket } = makeHarness({
|
package/dist/ws/discovery.js
CHANGED
|
@@ -8,11 +8,13 @@ export function deriveTokenLists(markets) {
|
|
|
8
8
|
underlyings.set(uMint, {
|
|
9
9
|
mint: m.market.underlying_mint,
|
|
10
10
|
decimals: m.underlying_decimals,
|
|
11
|
+
size_rule: m.size_rule,
|
|
11
12
|
symbol: m.underlying_symbol,
|
|
12
13
|
});
|
|
13
14
|
const q = {
|
|
14
15
|
mint: m.market.quote_mint,
|
|
15
16
|
decimals: m.quote_decimals,
|
|
17
|
+
size_rule: m.size_rule,
|
|
16
18
|
symbol: m.quote_symbol,
|
|
17
19
|
};
|
|
18
20
|
const inner = quotesByUnderlying.get(uMint) ?? new Map();
|
package/dist/ws/types.d.ts
CHANGED
|
@@ -83,6 +83,8 @@ export type ClientMessage = {
|
|
|
83
83
|
data: {
|
|
84
84
|
session_id: string;
|
|
85
85
|
};
|
|
86
|
+
} | {
|
|
87
|
+
type: "Logout";
|
|
86
88
|
} | {
|
|
87
89
|
type: "AuthChallenge";
|
|
88
90
|
data: AuthChallengeData;
|
|
@@ -93,6 +95,7 @@ export type ClientMessage = {
|
|
|
93
95
|
type: "CancelQuote";
|
|
94
96
|
data: {
|
|
95
97
|
rfq_id: UuidString;
|
|
98
|
+
request_id: RequestId;
|
|
96
99
|
};
|
|
97
100
|
} | {
|
|
98
101
|
type: "IndicativePricesResponse";
|
|
@@ -113,14 +116,17 @@ export type ClientMessage = {
|
|
|
113
116
|
type: "CancelRfq";
|
|
114
117
|
data: {
|
|
115
118
|
rfq_id: UuidString;
|
|
119
|
+
request_id: RequestId;
|
|
116
120
|
};
|
|
117
121
|
} | {
|
|
118
122
|
type: "GetIndicativePrices";
|
|
119
123
|
data: GetIndicativePricesMessage;
|
|
120
124
|
} | {
|
|
121
125
|
type: "GetPositions";
|
|
126
|
+
data: GetPositionsMessage;
|
|
122
127
|
} | {
|
|
123
128
|
type: "GetMarkets";
|
|
129
|
+
data: GetMarketsMessage;
|
|
124
130
|
} | {
|
|
125
131
|
type: "GetMarketDescriptors";
|
|
126
132
|
data: GetMarketDescriptorsMessage;
|
|
@@ -132,11 +138,13 @@ export type ClientMessage = {
|
|
|
132
138
|
data: GetTokensMessage;
|
|
133
139
|
} | {
|
|
134
140
|
type: "GetMyActiveRfqs";
|
|
141
|
+
data: GetMyActiveRfqsMessage;
|
|
135
142
|
} | {
|
|
136
143
|
type: "GetOrderStatus";
|
|
137
144
|
data: GetOrderStatusMessage;
|
|
138
145
|
} | {
|
|
139
146
|
type: "GetActiveRfqs";
|
|
147
|
+
data: GetActiveRfqsMessage;
|
|
140
148
|
} | {
|
|
141
149
|
type: "GetMakerPositions";
|
|
142
150
|
data: GetMakerPositionsMessage;
|
|
@@ -148,8 +156,10 @@ export type ClientMessage = {
|
|
|
148
156
|
data: GetMarketsForMakerMessage;
|
|
149
157
|
} | {
|
|
150
158
|
type: "GetMakerBalances";
|
|
159
|
+
data: GetMakerBalancesMessage;
|
|
151
160
|
} | {
|
|
152
161
|
type: "GetSubscriptions";
|
|
162
|
+
data: GetSubscriptionsMessage;
|
|
153
163
|
} | {
|
|
154
164
|
type: "CancelAllQuotes";
|
|
155
165
|
data: CancelAllQuotesMessage;
|
|
@@ -168,29 +178,54 @@ export type ClientMessage = {
|
|
|
168
178
|
};
|
|
169
179
|
};
|
|
170
180
|
export type GetMarketDescriptorsMessage = {
|
|
181
|
+
request_id: RequestId;
|
|
171
182
|
active_only?: boolean;
|
|
172
183
|
};
|
|
173
184
|
export type GetTokensMessage = {
|
|
185
|
+
request_id: RequestId;
|
|
174
186
|
active_only?: boolean;
|
|
175
187
|
};
|
|
176
188
|
export type GetExpiriesMessage = {
|
|
189
|
+
request_id: RequestId;
|
|
177
190
|
underlying_mint?: Address<string>;
|
|
178
191
|
quote_mint?: Address<string>;
|
|
179
192
|
is_put?: boolean | null;
|
|
180
193
|
};
|
|
194
|
+
export type GetPositionsMessage = {
|
|
195
|
+
request_id: RequestId;
|
|
196
|
+
};
|
|
197
|
+
export type GetMarketsMessage = {
|
|
198
|
+
request_id: RequestId;
|
|
199
|
+
};
|
|
200
|
+
export type GetMyActiveRfqsMessage = {
|
|
201
|
+
request_id: RequestId;
|
|
202
|
+
};
|
|
203
|
+
export type GetActiveRfqsMessage = {
|
|
204
|
+
request_id: RequestId;
|
|
205
|
+
};
|
|
206
|
+
export type GetMakerBalancesMessage = {
|
|
207
|
+
request_id: RequestId;
|
|
208
|
+
};
|
|
209
|
+
export type GetSubscriptionsMessage = {
|
|
210
|
+
request_id: RequestId;
|
|
211
|
+
};
|
|
181
212
|
export type GetOrderStatusMessage = {
|
|
213
|
+
request_id: RequestId;
|
|
182
214
|
order_id: OrderIdHex32;
|
|
183
215
|
};
|
|
184
216
|
export type GetMakerPositionsMessage = {
|
|
217
|
+
request_id: RequestId;
|
|
185
218
|
market?: string;
|
|
186
219
|
underlying_mint?: string;
|
|
187
220
|
status?: string[];
|
|
188
221
|
min_expiry_ts?: WsU64;
|
|
189
222
|
};
|
|
190
223
|
export type GetMyQuotesMessage = {
|
|
224
|
+
request_id: RequestId;
|
|
191
225
|
active_only?: boolean;
|
|
192
226
|
};
|
|
193
227
|
export type GetMarketsForMakerMessage = {
|
|
228
|
+
request_id: RequestId;
|
|
194
229
|
underlying_mints?: string[];
|
|
195
230
|
quote_mints?: string[];
|
|
196
231
|
min_expiry_ts?: WsU64;
|
|
@@ -199,6 +234,7 @@ export type GetMarketsForMakerMessage = {
|
|
|
199
234
|
include_stats?: boolean;
|
|
200
235
|
};
|
|
201
236
|
export type CancelAllQuotesMessage = {
|
|
237
|
+
request_id: RequestId;
|
|
202
238
|
market?: string;
|
|
203
239
|
};
|
|
204
240
|
export type AuthChallengeData = {
|
|
@@ -268,6 +304,9 @@ export type ServerMessage = {
|
|
|
268
304
|
reason: ErrorMessage;
|
|
269
305
|
message?: string;
|
|
270
306
|
};
|
|
307
|
+
} | {
|
|
308
|
+
type: "LogoutSuccess";
|
|
309
|
+
data: Record<string, never>;
|
|
271
310
|
} | {
|
|
272
311
|
type: "RfqCreated";
|
|
273
312
|
data: RfqCreatedMessage;
|
|
@@ -319,6 +358,7 @@ export type ServerMessage = {
|
|
|
319
358
|
} | {
|
|
320
359
|
type: "ActiveRfqs";
|
|
321
360
|
data: {
|
|
361
|
+
request_id: RequestId;
|
|
322
362
|
rfqs: ActiveRfqInfo[];
|
|
323
363
|
};
|
|
324
364
|
} | {
|
|
@@ -429,13 +469,20 @@ export type MarketDescriptorInfo = {
|
|
|
429
469
|
quote_oracle_pda: PubkeyBase58;
|
|
430
470
|
underlying_decimals: number;
|
|
431
471
|
quote_decimals: number;
|
|
432
|
-
|
|
433
|
-
|
|
472
|
+
size_rule: PositionSizeRule;
|
|
473
|
+
underlying_symbol: string;
|
|
474
|
+
quote_symbol: string;
|
|
475
|
+
};
|
|
476
|
+
export type PositionSizeRule = {
|
|
477
|
+
min_size: WsU64;
|
|
478
|
+
max_size: WsU64;
|
|
479
|
+
step: WsU64;
|
|
434
480
|
};
|
|
435
481
|
export type TokenInfo = {
|
|
436
482
|
mint: Address<string>;
|
|
437
483
|
decimals: number;
|
|
438
|
-
|
|
484
|
+
size_rule: PositionSizeRule;
|
|
485
|
+
symbol: string;
|
|
439
486
|
};
|
|
440
487
|
export type RfqCreatedMessage = {
|
|
441
488
|
rfq_id: UuidString;
|
|
@@ -625,11 +672,14 @@ export type QuoteExpiredMessage = {
|
|
|
625
672
|
reason: QuoteExpiredReason;
|
|
626
673
|
};
|
|
627
674
|
export type MakerPositionsMessage = {
|
|
675
|
+
request_id: RequestId;
|
|
628
676
|
positions: MakerPositionInfo[];
|
|
629
677
|
};
|
|
630
678
|
export type MakerPositionInfo = {
|
|
631
679
|
pda: string;
|
|
632
680
|
market: string;
|
|
681
|
+
underlying_mint: string;
|
|
682
|
+
quote_mint: string;
|
|
633
683
|
position_type: PositionType;
|
|
634
684
|
status: PositionStatus;
|
|
635
685
|
strike: WsU64;
|
|
@@ -642,6 +692,7 @@ export type MakerPositionInfo = {
|
|
|
642
692
|
expiry_ts: WsU64;
|
|
643
693
|
};
|
|
644
694
|
export type MyQuotesMessage = {
|
|
695
|
+
request_id: RequestId;
|
|
645
696
|
quotes: MakerQuoteInfo[];
|
|
646
697
|
};
|
|
647
698
|
export type MakerQuoteInfo = {
|
|
@@ -657,6 +708,7 @@ export type MakerQuoteInfo = {
|
|
|
657
708
|
created_at: WsU64;
|
|
658
709
|
};
|
|
659
710
|
export type MakerMarketsMessage = {
|
|
711
|
+
request_id: RequestId;
|
|
660
712
|
markets: MakerMarketInfo[];
|
|
661
713
|
};
|
|
662
714
|
export type MakerMarketInfo = {
|
|
@@ -666,8 +718,8 @@ export type MakerMarketInfo = {
|
|
|
666
718
|
expiry_ts: WsU64;
|
|
667
719
|
is_put: boolean;
|
|
668
720
|
is_finalized: boolean;
|
|
669
|
-
underlying_symbol
|
|
670
|
-
quote_symbol
|
|
721
|
+
underlying_symbol: string;
|
|
722
|
+
quote_symbol: string;
|
|
671
723
|
stats?: MarketStats;
|
|
672
724
|
};
|
|
673
725
|
export type MarketStats = {
|
|
@@ -675,6 +727,7 @@ export type MarketStats = {
|
|
|
675
727
|
trades_24h: WsU32;
|
|
676
728
|
};
|
|
677
729
|
export type MakerBalancesMessage = {
|
|
730
|
+
request_id: RequestId;
|
|
678
731
|
balances_by_mint: Record<string, MakerMintBalance>;
|
|
679
732
|
};
|
|
680
733
|
export type MakerMintBalance = {
|
|
@@ -683,6 +736,7 @@ export type MakerMintBalance = {
|
|
|
683
736
|
available: WsU64;
|
|
684
737
|
};
|
|
685
738
|
export type SubscriptionsMessage = {
|
|
739
|
+
request_id: RequestId;
|
|
686
740
|
channels: WsChannel[];
|
|
687
741
|
markets?: string[];
|
|
688
742
|
};
|
|
@@ -702,10 +756,12 @@ export type IndicativePricesResponseMessage = {
|
|
|
702
756
|
}>;
|
|
703
757
|
};
|
|
704
758
|
export type GetIndicativePricesMessage = {
|
|
759
|
+
request_id: RequestId;
|
|
705
760
|
market: Address<string>;
|
|
706
761
|
position_type: PositionType;
|
|
707
762
|
};
|
|
708
763
|
export type IndicativePricesMessage = {
|
|
764
|
+
request_id: RequestId;
|
|
709
765
|
market: Address<string>;
|
|
710
766
|
position_type: PositionType;
|
|
711
767
|
updated_at: WsU64;
|
|
@@ -746,18 +802,23 @@ export type QuotesUpdateMessage = {
|
|
|
746
802
|
quotes: QuoteReceivedMessage[];
|
|
747
803
|
};
|
|
748
804
|
export type PositionsMessage = {
|
|
805
|
+
request_id: RequestId;
|
|
749
806
|
positions: PositionInfo[];
|
|
750
807
|
};
|
|
751
808
|
export type MarketsMessage = {
|
|
809
|
+
request_id: RequestId;
|
|
752
810
|
markets: MarketInfo[];
|
|
753
811
|
};
|
|
754
812
|
export type MarketDescriptorsMessage = {
|
|
813
|
+
request_id: RequestId;
|
|
755
814
|
markets: MarketDescriptorInfo[];
|
|
756
815
|
};
|
|
757
816
|
export type ExpiriesMessage = {
|
|
817
|
+
request_id: RequestId;
|
|
758
818
|
expiries_ts: WsU64[];
|
|
759
819
|
};
|
|
760
820
|
export type TokensMessage = {
|
|
821
|
+
request_id: RequestId;
|
|
761
822
|
underlyings: TokenInfo[];
|
|
762
823
|
quotes_by_underlying: Record<Address<string>, TokenInfo[]>;
|
|
763
824
|
};
|
|
@@ -769,9 +830,11 @@ export type SnapshotMessage = {
|
|
|
769
830
|
markets: MarketInfo[];
|
|
770
831
|
};
|
|
771
832
|
export type MyActiveRfqsMessage = {
|
|
833
|
+
request_id: RequestId;
|
|
772
834
|
rfqs: MyActiveRfqInfo[];
|
|
773
835
|
};
|
|
774
836
|
export type OrderStatusMessage = {
|
|
837
|
+
request_id: RequestId;
|
|
775
838
|
order_id: OrderIdHex32;
|
|
776
839
|
status: OrderStatusValue;
|
|
777
840
|
rfq_id?: UuidString | null;
|
|
@@ -817,6 +880,8 @@ export type RfqOrderOption = {
|
|
|
817
880
|
export type PositionInfo = {
|
|
818
881
|
pda: string;
|
|
819
882
|
market: string;
|
|
883
|
+
underlying_mint: string;
|
|
884
|
+
quote_mint: string;
|
|
820
885
|
position_type: PositionType;
|
|
821
886
|
status: PositionStatus;
|
|
822
887
|
strike: WsU64;
|
package/dist/ws/wirePolicy.d.ts
CHANGED
|
@@ -10,3 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function assertWsU64Safe(value: number, name: string): void;
|
|
12
12
|
export declare function assertWsI64Safe(value: number, name: string): void;
|
|
13
|
+
export type QuantitySizeRuleLike = {
|
|
14
|
+
min_size: number;
|
|
15
|
+
max_size: number;
|
|
16
|
+
step: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function validateQuantityBySizeRule(quantity: number, rule: QuantitySizeRuleLike, quantityName?: string): void;
|
package/dist/ws/wirePolicy.js
CHANGED
|
@@ -28,3 +28,18 @@ export function assertWsI64Safe(value, name) {
|
|
|
28
28
|
throw new Error(`${name} exceeds JS safe integer range; WS wire requires safe integers`);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
export function validateQuantityBySizeRule(quantity, rule, quantityName = "quantity") {
|
|
32
|
+
assertWsU64Safe(quantity, quantityName);
|
|
33
|
+
assertWsU64Safe(rule.min_size, "size_rule.min_size");
|
|
34
|
+
assertWsU64Safe(rule.max_size, "size_rule.max_size");
|
|
35
|
+
assertWsU64Safe(rule.step, "size_rule.step");
|
|
36
|
+
if (rule.max_size < rule.min_size) {
|
|
37
|
+
throw new Error(`invalid size_rule: max_size (${rule.max_size}) must be >= min_size (${rule.min_size})`);
|
|
38
|
+
}
|
|
39
|
+
if (quantity < rule.min_size || quantity > rule.max_size) {
|
|
40
|
+
throw new Error(`${quantityName} must be within [${rule.min_size}, ${rule.max_size}], got ${quantity}`);
|
|
41
|
+
}
|
|
42
|
+
if ((quantity - rule.min_size) % rule.step !== 0) {
|
|
43
|
+
throw new Error(`${quantityName} must satisfy (quantity - min_size) % step == 0 (quantity=${quantity}, min_size=${rule.min_size}, step=${rule.step})`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { validateQuantityBySizeRule } from "./wirePolicy";
|
|
2
|
+
describe("validateQuantityBySizeRule", () => {
|
|
3
|
+
const rule = {
|
|
4
|
+
min_size: 2_000_000_000,
|
|
5
|
+
max_size: 10_000_000_000,
|
|
6
|
+
step: 1_000_000_000,
|
|
7
|
+
};
|
|
8
|
+
it("accepts a valid quantity", () => {
|
|
9
|
+
expect(() => validateQuantityBySizeRule(6_000_000_000, rule)).not.toThrow();
|
|
10
|
+
});
|
|
11
|
+
it("rejects quantity below min", () => {
|
|
12
|
+
expect(() => validateQuantityBySizeRule(1_000_000_000, rule)).toThrow("must be within");
|
|
13
|
+
});
|
|
14
|
+
it("rejects quantity above max", () => {
|
|
15
|
+
expect(() => validateQuantityBySizeRule(11_000_000_000, rule)).toThrow("must be within");
|
|
16
|
+
});
|
|
17
|
+
it("rejects quantity with invalid step", () => {
|
|
18
|
+
expect(() => validateQuantityBySizeRule(2_500_000_000, rule)).toThrow("(quantity - min_size) % step == 0");
|
|
19
|
+
});
|
|
20
|
+
});
|