@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.
@@ -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 reasons = [];
171
- client.on("authError", (reason) => reasons.push(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(reasons).toEqual(["invalid_signature"]);
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({
@@ -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();
@@ -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
- underlying_symbol?: string;
433
- quote_symbol?: string;
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
- symbol?: string;
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?: string;
670
- quote_symbol?: string;
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;
@@ -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;
@@ -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
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acta-markets/ts-sdk",
3
- "version": "0.0.4-beta",
3
+ "version": "0.0.6-beta",
4
4
  "description": "TypeScript SDK for Acta Protocol",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",