@acta-markets/ts-sdk 0.0.20-beta → 0.0.22-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.
@@ -7,7 +7,7 @@
7
7
  * @see https://github.com/codama-idl/codama
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.ACTA_CONTRACT_ERROR__MARKET_NOT_EXPIRED = exports.ACTA_CONTRACT_ERROR__DUPLICATE_ACCOUNT = exports.ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS = exports.ACTA_CONTRACT_ERROR__ORACLE_SOURCE_LOCKED = exports.ACTA_CONTRACT_ERROR__ORACLE_ALREADY_UPDATED = exports.ACTA_CONTRACT_ERROR__ORACLE_CLOSE_TOO_EARLY = exports.ACTA_CONTRACT_ERROR__UNAUTHORIZED = exports.ACTA_CONTRACT_ERROR__INVALID_TIMESTAMP = exports.ACTA_CONTRACT_ERROR__INVALID_PROGRAM_ID = exports.ACTA_CONTRACT_ERROR__INSUFFICIENT_FUNDS = exports.ACTA_CONTRACT_ERROR__MINT_MISMATCH = exports.ACTA_CONTRACT_ERROR__MATH_OVERFLOW = exports.ACTA_CONTRACT_ERROR__INVALID_INSTRUCTION_DATA = exports.ACTA_CONTRACT_ERROR__SIGNED_MESSAGE_MISMATCH = exports.ACTA_CONTRACT_ERROR__SIGNER_MISMATCH = exports.ACTA_CONTRACT_ERROR__INVALID_SIGNATURE_COUNT = exports.ACTA_CONTRACT_ERROR__INVALID_ORDER_ID = exports.ACTA_CONTRACT_ERROR__CANNOT_LIQUIDATE_FUNDED = exports.ACTA_CONTRACT_ERROR__POSITION_TYPE_MISMATCH = exports.ACTA_CONTRACT_ERROR__POSITION_NOT_ITM = exports.ACTA_CONTRACT_ERROR__POSITION_ALREADY_SETTLED = exports.ACTA_CONTRACT_ERROR__POSITION_NOT_FUNDED = exports.ACTA_CONTRACT_ERROR__POSITION_ALREADY_FUNDED = exports.ACTA_CONTRACT_ERROR__POSITION_NOT_OPEN = exports.ACTA_CONTRACT_ERROR__POSITION_ALREADY_OPEN = exports.ACTA_CONTRACT_ERROR__ORACLE_INVALID_PRICE = exports.ACTA_CONTRACT_ERROR__ORACLE_INACTIVE = exports.ACTA_CONTRACT_ERROR__ORACLE_STALE = exports.ACTA_CONTRACT_ERROR__ORACLE_EXPIRY_MISMATCH = exports.ACTA_CONTRACT_ERROR__INVALID_ORACLE_ACCOUNT = exports.ACTA_CONTRACT_ERROR__INVALID_ORACLE_TYPE = exports.ACTA_CONTRACT_ERROR__MARKET_NOT_EMPTY = exports.ACTA_CONTRACT_ERROR__MARKET_EXPIRED = exports.ACTA_CONTRACT_ERROR__MARKET_NOT_FINALIZED = exports.ACTA_CONTRACT_ERROR__MARKET_FINALIZED = exports.ACTA_CONTRACT_ERROR__MAKER_NOT_OWNER = exports.ACTA_CONTRACT_ERROR__MAKER_NOT_REGISTERED = exports.ACTA_CONTRACT_ERROR__MAKER_ALREADY_REGISTERED = exports.ACTA_CONTRACT_ERROR__ACCOUNT_ALREADY_INITIALIZED = exports.ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_DATA = exports.ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_COUNT = exports.ACTA_CONTRACT_ERROR__ACCOUNT_NOT_INITIALIZED = exports.ACTA_CONTRACT_ERROR__INVALID_PDA = exports.ACTA_CONTRACT_ERROR__INVALID_OWNER = exports.ACTA_CONTRACT_ERROR__NOT_SIGNED = void 0;
10
+ exports.ACTA_CONTRACT_ERROR__ORACLE_NOT_EXPIRED_YET = exports.ACTA_CONTRACT_ERROR__MARKET_NOT_EXPIRED = exports.ACTA_CONTRACT_ERROR__DUPLICATE_ACCOUNT = exports.ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS = exports.ACTA_CONTRACT_ERROR__ORACLE_SOURCE_LOCKED = exports.ACTA_CONTRACT_ERROR__ORACLE_ALREADY_UPDATED = exports.ACTA_CONTRACT_ERROR__ORACLE_CLOSE_TOO_EARLY = exports.ACTA_CONTRACT_ERROR__UNAUTHORIZED = exports.ACTA_CONTRACT_ERROR__INVALID_TIMESTAMP = exports.ACTA_CONTRACT_ERROR__INVALID_PROGRAM_ID = exports.ACTA_CONTRACT_ERROR__INSUFFICIENT_FUNDS = exports.ACTA_CONTRACT_ERROR__MINT_MISMATCH = exports.ACTA_CONTRACT_ERROR__MATH_OVERFLOW = exports.ACTA_CONTRACT_ERROR__INVALID_INSTRUCTION_DATA = exports.ACTA_CONTRACT_ERROR__SIGNED_MESSAGE_MISMATCH = exports.ACTA_CONTRACT_ERROR__SIGNER_MISMATCH = exports.ACTA_CONTRACT_ERROR__INVALID_SIGNATURE_COUNT = exports.ACTA_CONTRACT_ERROR__INVALID_ORDER_ID = exports.ACTA_CONTRACT_ERROR__CANNOT_LIQUIDATE_FUNDED = exports.ACTA_CONTRACT_ERROR__POSITION_TYPE_MISMATCH = exports.ACTA_CONTRACT_ERROR__POSITION_NOT_ITM = exports.ACTA_CONTRACT_ERROR__POSITION_ALREADY_SETTLED = exports.ACTA_CONTRACT_ERROR__POSITION_NOT_FUNDED = exports.ACTA_CONTRACT_ERROR__POSITION_ALREADY_FUNDED = exports.ACTA_CONTRACT_ERROR__POSITION_NOT_OPEN = exports.ACTA_CONTRACT_ERROR__POSITION_ALREADY_OPEN = exports.ACTA_CONTRACT_ERROR__ORACLE_INVALID_PRICE = exports.ACTA_CONTRACT_ERROR__ORACLE_INACTIVE = exports.ACTA_CONTRACT_ERROR__ORACLE_STALE = exports.ACTA_CONTRACT_ERROR__ORACLE_EXPIRY_MISMATCH = exports.ACTA_CONTRACT_ERROR__INVALID_ORACLE_ACCOUNT = exports.ACTA_CONTRACT_ERROR__INVALID_ORACLE_TYPE = exports.ACTA_CONTRACT_ERROR__MARKET_NOT_EMPTY = exports.ACTA_CONTRACT_ERROR__MARKET_EXPIRED = exports.ACTA_CONTRACT_ERROR__MARKET_NOT_FINALIZED = exports.ACTA_CONTRACT_ERROR__MARKET_FINALIZED = exports.ACTA_CONTRACT_ERROR__MAKER_NOT_OWNER = exports.ACTA_CONTRACT_ERROR__MAKER_NOT_REGISTERED = exports.ACTA_CONTRACT_ERROR__MAKER_ALREADY_REGISTERED = exports.ACTA_CONTRACT_ERROR__ACCOUNT_ALREADY_INITIALIZED = exports.ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_DATA = exports.ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_COUNT = exports.ACTA_CONTRACT_ERROR__ACCOUNT_NOT_INITIALIZED = exports.ACTA_CONTRACT_ERROR__INVALID_PDA = exports.ACTA_CONTRACT_ERROR__INVALID_OWNER = exports.ACTA_CONTRACT_ERROR__NOT_SIGNED = void 0;
11
11
  exports.getActaContractErrorMessage = getActaContractErrorMessage;
12
12
  exports.isActaContractError = isActaContractError;
13
13
  const kit_1 = require("@solana/kit");
@@ -102,6 +102,8 @@ exports.ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS = 0x432; // 1074
102
102
  exports.ACTA_CONTRACT_ERROR__DUPLICATE_ACCOUNT = 0x433; // 1075
103
103
  /** MarketNotExpired: Market has not expired yet */
104
104
  exports.ACTA_CONTRACT_ERROR__MARKET_NOT_EXPIRED = 0x434; // 1076
105
+ /** OracleNotExpiredYet: Oracle has not expired yet (cannot update price before expiry) */
106
+ exports.ACTA_CONTRACT_ERROR__ORACLE_NOT_EXPIRED_YET = 0x435; // 1077
105
107
  let actaContractErrorMessages;
106
108
  if (process.env.NODE_ENV !== "production") {
107
109
  actaContractErrorMessages = {
@@ -138,6 +140,7 @@ if (process.env.NODE_ENV !== "production") {
138
140
  [exports.ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS]: `Oracle is still linked to active markets`,
139
141
  [exports.ACTA_CONTRACT_ERROR__ORACLE_INACTIVE]: `Oracle inactive`,
140
142
  [exports.ACTA_CONTRACT_ERROR__ORACLE_INVALID_PRICE]: `Oracle price invalid (zero or negative)`,
143
+ [exports.ACTA_CONTRACT_ERROR__ORACLE_NOT_EXPIRED_YET]: `Oracle has not expired yet (cannot update price before expiry)`,
141
144
  [exports.ACTA_CONTRACT_ERROR__ORACLE_SOURCE_LOCKED]: `Oracle source fields are locked after first update`,
142
145
  [exports.ACTA_CONTRACT_ERROR__ORACLE_STALE]: `Oracle price is stale (updated_at before expiry)`,
143
146
  [exports.ACTA_CONTRACT_ERROR__POSITION_ALREADY_FUNDED]: `Position already funded by maker`,
@@ -2312,6 +2312,11 @@
2312
2312
  "code": 1076,
2313
2313
  "name": "MarketNotExpired",
2314
2314
  "msg": "Market has not expired yet"
2315
+ },
2316
+ {
2317
+ "code": 1077,
2318
+ "name": "OracleNotExpiredYet",
2319
+ "msg": "Oracle has not expired yet (cannot update price before expiry)"
2315
2320
  }
2316
2321
  ]
2317
2322
  }
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ACTA_IDL_SHA256 = void 0;
4
- exports.ACTA_IDL_SHA256 = "34f93515df3ceed9854715fb73e9fe0de615383e5ff3f0d627ecd029e65e5688";
4
+ exports.ACTA_IDL_SHA256 = "2ccf0a77f682ff60b8201e2bca7f4bd026500a2fb9637d46dad7a8de3b2ee313";
@@ -7,6 +7,9 @@
7
7
  * - Client signs UTF-8 bytes of that text and responds:
8
8
  * `AuthChallenge { challenge, signature: base58(ed25519(utf8(challenge))), pubkey }`
9
9
  *
10
+ * The same `signMessage` primitive is reused for any UTF-8-bytes signing
11
+ * (e.g. `acta:redeem:v1:{pubkey}:{code}` for invite redemption).
12
+ *
10
13
  * Source of truth (server):
11
14
  * - rust-backend/rfq-server/src/server/ws.rs
12
15
  * - rust-backend/rfq-server/src/session/handler.rs
@@ -52,9 +55,9 @@ class KeypairAuthProvider {
52
55
  async getPublicKey() {
53
56
  return this.address;
54
57
  }
55
- async signChallenge(challenge) {
56
- const challengeBytes = utf8ToBytes(challenge);
57
- const signatureBytes = await (0, keys_1.signBytes)(this.privateKey, challengeBytes);
58
+ async signMessage(message) {
59
+ const messageBytes = utf8ToBytes(message);
60
+ const signatureBytes = await (0, keys_1.signBytes)(this.privateKey, messageBytes);
58
61
  return bytesToBase58(signatureBytes);
59
62
  }
60
63
  }
@@ -76,10 +79,10 @@ class WalletAuthProvider {
76
79
  throw new Error("Wallet not connected (missing public key)");
77
80
  return pk;
78
81
  }
79
- async signChallenge(challenge) {
80
- const challengeBytes = utf8ToBytes(challenge);
82
+ async signMessage(message) {
83
+ const messageBytes = utf8ToBytes(message);
81
84
  // Some wallet APIs expect a mutable `Uint8Array`; codecs return `ReadonlyUint8Array`.
82
- const sig = await this.wallet.signMessage(new Uint8Array(challengeBytes));
85
+ const sig = await this.wallet.signMessage(new Uint8Array(messageBytes));
83
86
  return bytesToBase58(sig);
84
87
  }
85
88
  }
@@ -89,16 +92,16 @@ exports.WalletAuthProvider = WalletAuthProvider;
89
92
  */
90
93
  class CustomAuthProvider {
91
94
  getPublicKeyFn;
92
- signChallengeFn;
93
- constructor(getPublicKey, signChallenge) {
95
+ signMessageFn;
96
+ constructor(getPublicKey, signMessage) {
94
97
  this.getPublicKeyFn = getPublicKey;
95
- this.signChallengeFn = signChallenge;
98
+ this.signMessageFn = signMessage;
96
99
  }
97
100
  async getPublicKey() {
98
101
  return this.getPublicKeyFn();
99
102
  }
100
- async signChallenge(challenge) {
101
- return this.signChallengeFn(challenge);
103
+ async signMessage(message) {
104
+ return this.signMessageFn(message);
102
105
  }
103
106
  }
104
107
  exports.CustomAuthProvider = CustomAuthProvider;
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.ActaWsClient = void 0;
5
5
  const flows_1 = require("./flows");
6
6
  const wirePolicy_1 = require("./wirePolicy");
7
+ const referral_1 = require("./referral");
7
8
  function toGenericServerError(err) {
8
9
  const message = err instanceof Error ? err.message : String(err);
9
10
  return { type: "generic", data: { code: "client_error", message } };
@@ -110,8 +111,8 @@ class ActaWsClient extends TypedEventEmitter {
110
111
  pingTimer = null;
111
112
  shouldReconnect = true;
112
113
  subscribedChannels = new Set(["rfqs"]);
113
- subscribedMarkets = new Set();
114
- hasMarketScope = false;
114
+ underlyingMintScope = null; // null = all
115
+ quoteMintScope = null; // null = all
115
116
  marketDescriptorsByMarket = new Map();
116
117
  state = {
117
118
  stats: null,
@@ -339,9 +340,118 @@ class ActaWsClient extends TypedEventEmitter {
339
340
  });
340
341
  return requestId;
341
342
  }
343
+ /** Maker-only: get balances per deposited token (total, locked, available). */
344
+ getMakerBalances() {
345
+ this.ensureAuthenticated();
346
+ const requestId = this.nextRequestId();
347
+ this.send({
348
+ type: "GetMakerBalances",
349
+ data: { request_id: requestId },
350
+ });
351
+ return requestId;
352
+ }
353
+ /** Maker-only: get open positions with optional filters. */
354
+ getMakerPositions(args) {
355
+ this.ensureAuthenticated();
356
+ const requestId = this.nextRequestId();
357
+ const data = {
358
+ request_id: requestId,
359
+ ...args,
360
+ };
361
+ this.send({ type: "GetMakerPositions", data });
362
+ return requestId;
363
+ }
364
+ /** Maker-only: get trade history with keyset pagination. */
365
+ getMyTrades(args) {
366
+ this.ensureAuthenticated();
367
+ const requestId = this.nextRequestId();
368
+ const data = {
369
+ request_id: requestId,
370
+ ...args,
371
+ };
372
+ this.send({ type: "GetMyTrades", data });
373
+ return requestId;
374
+ }
375
+ /** Maker-only: get position, notional, and balance caps. */
376
+ getMyCaps() {
377
+ this.ensureAuthenticated();
378
+ const requestId = this.nextRequestId();
379
+ this.send({
380
+ type: "GetMyCaps",
381
+ data: { request_id: requestId },
382
+ });
383
+ return requestId;
384
+ }
385
+ /** Maker-only: get markets where maker has deposits, with optional filters and stats. */
386
+ getMarketsForMaker(args) {
387
+ this.ensureAuthenticated();
388
+ const requestId = this.nextRequestId();
389
+ const data = {
390
+ request_id: requestId,
391
+ ...args,
392
+ };
393
+ this.send({ type: "GetMarketsForMaker", data });
394
+ return requestId;
395
+ }
396
+ /** Maker-only: get submitted quotes with status. */
397
+ getMyQuotes(args) {
398
+ this.ensureAuthenticated();
399
+ const requestId = this.nextRequestId();
400
+ const data = {
401
+ request_id: requestId,
402
+ active_only: args?.active_only ?? true,
403
+ };
404
+ this.send({ type: "GetMyQuotes", data });
405
+ return requestId;
406
+ }
342
407
  logout() {
343
408
  this.send({ type: "Logout" });
344
409
  }
410
+ // ========================================================================
411
+ // Referral / invite
412
+ // ========================================================================
413
+ /**
414
+ * Redeem an invite code. Authentication is proven by the session;
415
+ * no additional signature is required. Validation runs client-side —
416
+ * invalid inputs throw `ReferralCodeError` without a round-trip.
417
+ */
418
+ redeemInvite(rawCode) {
419
+ this.ensureAuthenticated();
420
+ const parsed = (0, referral_1.parseReferralCode)(rawCode);
421
+ if (!parsed.ok)
422
+ throw new referral_1.ReferralCodeError(parsed.error);
423
+ const requestId = this.nextRequestId();
424
+ this.send({
425
+ type: "RedeemInvite",
426
+ data: { request_id: requestId, code: parsed.code },
427
+ });
428
+ return requestId;
429
+ }
430
+ /**
431
+ * Claim a vanity referral code (one-shot per taker). Validation
432
+ * runs client-side — invalid inputs throw `ReferralCodeError`.
433
+ */
434
+ async claimReferralCode(rawCode) {
435
+ this.ensureAuthenticated();
436
+ const parsed = (0, referral_1.parseReferralCode)(rawCode);
437
+ if (!parsed.ok)
438
+ throw new referral_1.ReferralCodeError(parsed.error);
439
+ const requestId = this.nextRequestId();
440
+ this.send({
441
+ type: "ClaimReferralCode",
442
+ data: { request_id: requestId, code: parsed.code },
443
+ });
444
+ return requestId;
445
+ }
446
+ getMyReferralInfo() {
447
+ this.ensureAuthenticated();
448
+ const requestId = this.nextRequestId();
449
+ this.send({
450
+ type: "GetMyReferralInfo",
451
+ data: { request_id: requestId },
452
+ });
453
+ return requestId;
454
+ }
345
455
  getOrderStatus(orderIdHex) {
346
456
  this.ensureAuthenticated();
347
457
  const requestId = this.nextRequestId();
@@ -395,21 +505,25 @@ class ActaWsClient extends TypedEventEmitter {
395
505
  });
396
506
  return requestId;
397
507
  }
398
- subscribe(channels, markets) {
508
+ subscribe(channels, opts) {
399
509
  this.ensureAuthenticated();
400
510
  const request_id = this.nextRequestId();
401
511
  for (const c of channels)
402
512
  this.subscribedChannels.add(c);
403
- if (markets) {
404
- this.hasMarketScope = true;
405
- this.subscribedMarkets = new Set(markets);
513
+ if (opts?.underlying_mints) {
514
+ this.underlyingMintScope = new Set(opts.underlying_mints);
406
515
  }
407
- this.send({
408
- type: "Subscribe",
409
- data: this.hasMarketScope
410
- ? { request_id, channels, markets: Array.from(this.subscribedMarkets) }
411
- : { request_id, channels },
412
- });
516
+ if (opts?.quote_mints) {
517
+ this.quoteMintScope = new Set(opts.quote_mints);
518
+ }
519
+ const data = { request_id, channels };
520
+ if (this.underlyingMintScope) {
521
+ data.underlying_mints = Array.from(this.underlyingMintScope);
522
+ }
523
+ if (this.quoteMintScope) {
524
+ data.quote_mints = Array.from(this.quoteMintScope);
525
+ }
526
+ this.send({ type: "Subscribe", data });
413
527
  return request_id;
414
528
  }
415
529
  unsubscribe(channels) {
@@ -423,6 +537,58 @@ class ActaWsClient extends TypedEventEmitter {
423
537
  });
424
538
  return request_id;
425
539
  }
540
+ addMints(opts) {
541
+ this.ensureAuthenticated();
542
+ const request_id = this.nextRequestId();
543
+ if (opts.underlying_mints) {
544
+ if (!this.underlyingMintScope)
545
+ this.underlyingMintScope = new Set();
546
+ for (const m of opts.underlying_mints)
547
+ this.underlyingMintScope.add(m);
548
+ }
549
+ if (opts.quote_mints) {
550
+ if (!this.quoteMintScope)
551
+ this.quoteMintScope = new Set();
552
+ for (const m of opts.quote_mints)
553
+ this.quoteMintScope.add(m);
554
+ }
555
+ this.send({ type: "AddMints", data: { request_id, ...opts } });
556
+ return request_id;
557
+ }
558
+ removeMints(opts) {
559
+ this.ensureAuthenticated();
560
+ const request_id = this.nextRequestId();
561
+ if (opts.underlying_mints && this.underlyingMintScope) {
562
+ for (const m of opts.underlying_mints)
563
+ this.underlyingMintScope.delete(m);
564
+ if (this.underlyingMintScope.size === 0)
565
+ this.underlyingMintScope = null;
566
+ }
567
+ if (opts.quote_mints && this.quoteMintScope) {
568
+ for (const m of opts.quote_mints)
569
+ this.quoteMintScope.delete(m);
570
+ if (this.quoteMintScope.size === 0)
571
+ this.quoteMintScope = null;
572
+ }
573
+ this.send({ type: "RemoveMints", data: { request_id, ...opts } });
574
+ return request_id;
575
+ }
576
+ addChannels(channels) {
577
+ this.ensureAuthenticated();
578
+ const request_id = this.nextRequestId();
579
+ for (const c of channels)
580
+ this.subscribedChannels.add(c);
581
+ this.send({ type: "AddChannels", data: { request_id, channels } });
582
+ return request_id;
583
+ }
584
+ removeChannels(channels) {
585
+ this.ensureAuthenticated();
586
+ const request_id = this.nextRequestId();
587
+ for (const c of channels)
588
+ this.subscribedChannels.delete(c);
589
+ this.send({ type: "RemoveChannels", data: { request_id, channels } });
590
+ return request_id;
591
+ }
426
592
  ping() {
427
593
  if (this.ws?.readyState === WS_OPEN) {
428
594
  this.send({ type: "Ping" });
@@ -573,6 +739,21 @@ class ActaWsClient extends TypedEventEmitter {
573
739
  case "MyCaps":
574
740
  this.emit("myCaps", message.data);
575
741
  break;
742
+ case "MakerBalances":
743
+ this.emit("makerBalances", message.data);
744
+ break;
745
+ case "MakerPositions":
746
+ this.emit("makerPositions", message.data);
747
+ break;
748
+ case "MyTrades":
749
+ this.emit("myTrades", message.data);
750
+ break;
751
+ case "MakerMarkets":
752
+ this.emit("makerMarkets", message.data);
753
+ break;
754
+ case "MyQuotes":
755
+ this.emit("myQuotes", message.data);
756
+ break;
576
757
  case "MyActiveRfqs":
577
758
  this.handleMyActiveRfqs(message.data);
578
759
  break;
@@ -730,6 +911,30 @@ class ActaWsClient extends TypedEventEmitter {
730
911
  case "UnsubscribeAck":
731
912
  this.emit("unsubscribeAck", message.data);
732
913
  break;
914
+ case "SubscriptionUpdated":
915
+ {
916
+ const d = message.data;
917
+ // Sync local state from the server's authoritative response.
918
+ this.subscribedChannels = new Set(d.channels);
919
+ this.underlyingMintScope =
920
+ d.underlying_mints != null ? new Set(d.underlying_mints) : null;
921
+ this.quoteMintScope =
922
+ d.quote_mints != null ? new Set(d.quote_mints) : null;
923
+ this.emit("subscriptionUpdated", d);
924
+ }
925
+ break;
926
+ case "RequireInvite":
927
+ this.emit("requireInvite");
928
+ break;
929
+ case "InviteRedeemed":
930
+ this.emit("inviteRedeemed", message.data);
931
+ break;
932
+ case "ReferralCodeClaimed":
933
+ this.emit("referralCodeClaimed", message.data);
934
+ break;
935
+ case "MyReferralInfo":
936
+ this.emit("myReferralInfo", message.data);
937
+ break;
733
938
  }
734
939
  }
735
940
  /** Taker-only: request current indicative prices for a market + position_type. */
@@ -755,7 +960,7 @@ class ActaWsClient extends TypedEventEmitter {
755
960
  try {
756
961
  const [pubkey, signature] = await Promise.all([
757
962
  this.authProvider.getPublicKey(),
758
- this.authProvider.signChallenge(challenge),
963
+ this.authProvider.signMessage(challenge),
759
964
  ]);
760
965
  this.send({
761
966
  type: "AuthChallenge",
@@ -815,9 +1020,13 @@ class ActaWsClient extends TypedEventEmitter {
815
1020
  if (this.subscribedChannels.size > 0) {
816
1021
  const channels = Array.from(this.subscribedChannels);
817
1022
  const request_id = this.nextRequestId();
818
- const data = this.hasMarketScope
819
- ? { request_id, channels, markets: Array.from(this.subscribedMarkets) }
820
- : { request_id, channels };
1023
+ const data = { request_id, channels };
1024
+ if (this.underlyingMintScope) {
1025
+ data.underlying_mints = Array.from(this.underlyingMintScope);
1026
+ }
1027
+ if (this.quoteMintScope) {
1028
+ data.quote_mints = Array.from(this.quoteMintScope);
1029
+ }
821
1030
  this.send({ type: "Subscribe", data });
822
1031
  }
823
1032
  }
@@ -53,7 +53,7 @@ function parseClientMessage(payload) {
53
53
  function makeAuthProvider(pubkey = "pubkey", signature = "signature") {
54
54
  return {
55
55
  getPublicKey: jest.fn().mockResolvedValue(pubkey),
56
- signChallenge: jest.fn().mockResolvedValue(signature),
56
+ signMessage: jest.fn().mockResolvedValue(signature),
57
57
  };
58
58
  }
59
59
  function makeHarness(overrides = {}) {
@@ -22,3 +22,4 @@ __exportStar(require("./sponsoredTx"), exports);
22
22
  __exportStar(require("./apy"), exports);
23
23
  __exportStar(require("./discovery"), exports);
24
24
  __exportStar(require("./sizing"), exports);
25
+ __exportStar(require("./referral"), exports);
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * Referral / invite redemption helpers.
4
+ *
5
+ * Mirrors the server-side validation defined in
6
+ * rust-backend/acta-types/src/invite.rs (ReferralCode::parse).
7
+ * Client-side validation must stay bit-identical to the server on
8
+ * the same input. A hardcoded fixture table is kept here and on the
9
+ * rust side; if either drifts, the cross-impl test will catch it.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ReferralCodeError = exports.REFERRAL_CODE_MAX_LEN = exports.REFERRAL_CODE_MIN_LEN = void 0;
13
+ exports.normalizeReferralCode = normalizeReferralCode;
14
+ exports.parseReferralCode = parseReferralCode;
15
+ exports.REFERRAL_CODE_MIN_LEN = 4;
16
+ exports.REFERRAL_CODE_MAX_LEN = 16;
17
+ class ReferralCodeError extends Error {
18
+ detail;
19
+ constructor(detail) {
20
+ super(detail.kind === "length"
21
+ ? `code length must be between ${detail.min} and ${detail.max}`
22
+ : "code must contain only ASCII letters and digits");
23
+ this.detail = detail;
24
+ this.name = "ReferralCodeError";
25
+ }
26
+ }
27
+ exports.ReferralCodeError = ReferralCodeError;
28
+ /**
29
+ * Trim + ASCII uppercase. No length or charset check. Useful for
30
+ * showing a live preview as the user types.
31
+ */
32
+ function normalizeReferralCode(input) {
33
+ return input.trim().toUpperCase();
34
+ }
35
+ /**
36
+ * Parse + validate + normalize a user-supplied referral code.
37
+ * Mirrors `ReferralCode::parse` on the server. A successful result
38
+ * carries the canonical branded `ReferralCode`.
39
+ */
40
+ function parseReferralCode(input) {
41
+ const normalized = normalizeReferralCode(input);
42
+ if (normalized.length < exports.REFERRAL_CODE_MIN_LEN ||
43
+ normalized.length > exports.REFERRAL_CODE_MAX_LEN) {
44
+ return {
45
+ ok: false,
46
+ error: {
47
+ kind: "length",
48
+ min: exports.REFERRAL_CODE_MIN_LEN,
49
+ max: exports.REFERRAL_CODE_MAX_LEN,
50
+ },
51
+ };
52
+ }
53
+ if (!/^[A-Z0-9]+$/.test(normalized)) {
54
+ return { ok: false, error: { kind: "charset" } };
55
+ }
56
+ return { ok: true, code: normalized };
57
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const referral_1 = require("./referral");
4
+ // =============================================================================
5
+ // Cross-impl fixture.
6
+ //
7
+ // Bit-for-bit identical to the fixture in
8
+ // rust-backend/acta-types/src/invite.rs::tests
9
+ // If either side changes, update both.
10
+ // =============================================================================
11
+ const ACCEPT_FIXTURES = [
12
+ ["NIKITA", "NIKITA"],
13
+ ["nikita", "NIKITA"],
14
+ [" abc1 ", "ABC1"],
15
+ ["ABCD", "ABCD"],
16
+ ["1234567890ABCDEF", "1234567890ABCDEF"],
17
+ ];
18
+ describe("parseReferralCode", () => {
19
+ test.each(ACCEPT_FIXTURES)("accepts %j → %j", (input, expected) => {
20
+ const res = (0, referral_1.parseReferralCode)(input);
21
+ expect(res.ok).toBe(true);
22
+ if (res.ok)
23
+ expect(res.code).toBe(expected);
24
+ });
25
+ test("rejects too short", () => {
26
+ const res = (0, referral_1.parseReferralCode)("abc");
27
+ expect(res.ok).toBe(false);
28
+ if (!res.ok)
29
+ expect(res.error.kind).toBe("length");
30
+ });
31
+ test("rejects too long", () => {
32
+ const res = (0, referral_1.parseReferralCode)("A".repeat(17));
33
+ expect(res.ok).toBe(false);
34
+ if (!res.ok)
35
+ expect(res.error.kind).toBe("length");
36
+ });
37
+ test("rejects non-ascii", () => {
38
+ const res = (0, referral_1.parseReferralCode)("абвгд");
39
+ expect(res.ok).toBe(false);
40
+ // Cyrillic passes length but fails charset.
41
+ if (!res.ok)
42
+ expect(res.error.kind).toBe("charset");
43
+ });
44
+ test("rejects punctuation", () => {
45
+ const res = (0, referral_1.parseReferralCode)("ABC-12");
46
+ expect(res.ok).toBe(false);
47
+ if (!res.ok)
48
+ expect(res.error.kind).toBe("charset");
49
+ });
50
+ });
51
+ describe("normalizeReferralCode", () => {
52
+ test("trims and uppercases", () => {
53
+ expect((0, referral_1.normalizeReferralCode)(" nikita ")).toBe("NIKITA");
54
+ });
55
+ });
@@ -96,7 +96,9 @@ export declare const ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS = 1074;
96
96
  export declare const ACTA_CONTRACT_ERROR__DUPLICATE_ACCOUNT = 1075;
97
97
  /** MarketNotExpired: Market has not expired yet */
98
98
  export declare const ACTA_CONTRACT_ERROR__MARKET_NOT_EXPIRED = 1076;
99
- export type ActaContractError = typeof ACTA_CONTRACT_ERROR__ACCOUNT_ALREADY_INITIALIZED | typeof ACTA_CONTRACT_ERROR__ACCOUNT_NOT_INITIALIZED | typeof ACTA_CONTRACT_ERROR__CANNOT_LIQUIDATE_FUNDED | typeof ACTA_CONTRACT_ERROR__DUPLICATE_ACCOUNT | typeof ACTA_CONTRACT_ERROR__INSUFFICIENT_FUNDS | typeof ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_COUNT | typeof ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_DATA | typeof ACTA_CONTRACT_ERROR__INVALID_INSTRUCTION_DATA | typeof ACTA_CONTRACT_ERROR__INVALID_ORACLE_ACCOUNT | typeof ACTA_CONTRACT_ERROR__INVALID_ORACLE_TYPE | typeof ACTA_CONTRACT_ERROR__INVALID_ORDER_ID | typeof ACTA_CONTRACT_ERROR__INVALID_OWNER | typeof ACTA_CONTRACT_ERROR__INVALID_PDA | typeof ACTA_CONTRACT_ERROR__INVALID_PROGRAM_ID | typeof ACTA_CONTRACT_ERROR__INVALID_SIGNATURE_COUNT | typeof ACTA_CONTRACT_ERROR__INVALID_TIMESTAMP | typeof ACTA_CONTRACT_ERROR__MAKER_ALREADY_REGISTERED | typeof ACTA_CONTRACT_ERROR__MAKER_NOT_OWNER | typeof ACTA_CONTRACT_ERROR__MAKER_NOT_REGISTERED | typeof ACTA_CONTRACT_ERROR__MARKET_EXPIRED | typeof ACTA_CONTRACT_ERROR__MARKET_FINALIZED | typeof ACTA_CONTRACT_ERROR__MARKET_NOT_EMPTY | typeof ACTA_CONTRACT_ERROR__MARKET_NOT_EXPIRED | typeof ACTA_CONTRACT_ERROR__MARKET_NOT_FINALIZED | typeof ACTA_CONTRACT_ERROR__MATH_OVERFLOW | typeof ACTA_CONTRACT_ERROR__MINT_MISMATCH | typeof ACTA_CONTRACT_ERROR__NOT_SIGNED | typeof ACTA_CONTRACT_ERROR__ORACLE_ALREADY_UPDATED | typeof ACTA_CONTRACT_ERROR__ORACLE_CLOSE_TOO_EARLY | typeof ACTA_CONTRACT_ERROR__ORACLE_EXPIRY_MISMATCH | typeof ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS | typeof ACTA_CONTRACT_ERROR__ORACLE_INACTIVE | typeof ACTA_CONTRACT_ERROR__ORACLE_INVALID_PRICE | typeof ACTA_CONTRACT_ERROR__ORACLE_SOURCE_LOCKED | typeof ACTA_CONTRACT_ERROR__ORACLE_STALE | typeof ACTA_CONTRACT_ERROR__POSITION_ALREADY_FUNDED | typeof ACTA_CONTRACT_ERROR__POSITION_ALREADY_OPEN | typeof ACTA_CONTRACT_ERROR__POSITION_ALREADY_SETTLED | typeof ACTA_CONTRACT_ERROR__POSITION_NOT_FUNDED | typeof ACTA_CONTRACT_ERROR__POSITION_NOT_ITM | typeof ACTA_CONTRACT_ERROR__POSITION_NOT_OPEN | typeof ACTA_CONTRACT_ERROR__POSITION_TYPE_MISMATCH | typeof ACTA_CONTRACT_ERROR__SIGNED_MESSAGE_MISMATCH | typeof ACTA_CONTRACT_ERROR__SIGNER_MISMATCH | typeof ACTA_CONTRACT_ERROR__UNAUTHORIZED;
99
+ /** OracleNotExpiredYet: Oracle has not expired yet (cannot update price before expiry) */
100
+ export declare const ACTA_CONTRACT_ERROR__ORACLE_NOT_EXPIRED_YET = 1077;
101
+ export type ActaContractError = typeof ACTA_CONTRACT_ERROR__ACCOUNT_ALREADY_INITIALIZED | typeof ACTA_CONTRACT_ERROR__ACCOUNT_NOT_INITIALIZED | typeof ACTA_CONTRACT_ERROR__CANNOT_LIQUIDATE_FUNDED | typeof ACTA_CONTRACT_ERROR__DUPLICATE_ACCOUNT | typeof ACTA_CONTRACT_ERROR__INSUFFICIENT_FUNDS | typeof ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_COUNT | typeof ACTA_CONTRACT_ERROR__INVALID_ACCOUNT_DATA | typeof ACTA_CONTRACT_ERROR__INVALID_INSTRUCTION_DATA | typeof ACTA_CONTRACT_ERROR__INVALID_ORACLE_ACCOUNT | typeof ACTA_CONTRACT_ERROR__INVALID_ORACLE_TYPE | typeof ACTA_CONTRACT_ERROR__INVALID_ORDER_ID | typeof ACTA_CONTRACT_ERROR__INVALID_OWNER | typeof ACTA_CONTRACT_ERROR__INVALID_PDA | typeof ACTA_CONTRACT_ERROR__INVALID_PROGRAM_ID | typeof ACTA_CONTRACT_ERROR__INVALID_SIGNATURE_COUNT | typeof ACTA_CONTRACT_ERROR__INVALID_TIMESTAMP | typeof ACTA_CONTRACT_ERROR__MAKER_ALREADY_REGISTERED | typeof ACTA_CONTRACT_ERROR__MAKER_NOT_OWNER | typeof ACTA_CONTRACT_ERROR__MAKER_NOT_REGISTERED | typeof ACTA_CONTRACT_ERROR__MARKET_EXPIRED | typeof ACTA_CONTRACT_ERROR__MARKET_FINALIZED | typeof ACTA_CONTRACT_ERROR__MARKET_NOT_EMPTY | typeof ACTA_CONTRACT_ERROR__MARKET_NOT_EXPIRED | typeof ACTA_CONTRACT_ERROR__MARKET_NOT_FINALIZED | typeof ACTA_CONTRACT_ERROR__MATH_OVERFLOW | typeof ACTA_CONTRACT_ERROR__MINT_MISMATCH | typeof ACTA_CONTRACT_ERROR__NOT_SIGNED | typeof ACTA_CONTRACT_ERROR__ORACLE_ALREADY_UPDATED | typeof ACTA_CONTRACT_ERROR__ORACLE_CLOSE_TOO_EARLY | typeof ACTA_CONTRACT_ERROR__ORACLE_EXPIRY_MISMATCH | typeof ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS | typeof ACTA_CONTRACT_ERROR__ORACLE_INACTIVE | typeof ACTA_CONTRACT_ERROR__ORACLE_INVALID_PRICE | typeof ACTA_CONTRACT_ERROR__ORACLE_NOT_EXPIRED_YET | typeof ACTA_CONTRACT_ERROR__ORACLE_SOURCE_LOCKED | typeof ACTA_CONTRACT_ERROR__ORACLE_STALE | typeof ACTA_CONTRACT_ERROR__POSITION_ALREADY_FUNDED | typeof ACTA_CONTRACT_ERROR__POSITION_ALREADY_OPEN | typeof ACTA_CONTRACT_ERROR__POSITION_ALREADY_SETTLED | typeof ACTA_CONTRACT_ERROR__POSITION_NOT_FUNDED | typeof ACTA_CONTRACT_ERROR__POSITION_NOT_ITM | typeof ACTA_CONTRACT_ERROR__POSITION_NOT_OPEN | typeof ACTA_CONTRACT_ERROR__POSITION_TYPE_MISMATCH | typeof ACTA_CONTRACT_ERROR__SIGNED_MESSAGE_MISMATCH | typeof ACTA_CONTRACT_ERROR__SIGNER_MISMATCH | typeof ACTA_CONTRACT_ERROR__UNAUTHORIZED;
100
102
  export declare function getActaContractErrorMessage(code: ActaContractError): string;
101
103
  export declare function isActaContractError<TProgramErrorCode extends ActaContractError>(error: unknown, transactionMessage: {
102
104
  instructions: Record<number, {
@@ -97,6 +97,8 @@ export const ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS = 0x432; // 1074
97
97
  export const ACTA_CONTRACT_ERROR__DUPLICATE_ACCOUNT = 0x433; // 1075
98
98
  /** MarketNotExpired: Market has not expired yet */
99
99
  export const ACTA_CONTRACT_ERROR__MARKET_NOT_EXPIRED = 0x434; // 1076
100
+ /** OracleNotExpiredYet: Oracle has not expired yet (cannot update price before expiry) */
101
+ export const ACTA_CONTRACT_ERROR__ORACLE_NOT_EXPIRED_YET = 0x435; // 1077
100
102
  let actaContractErrorMessages;
101
103
  if (process.env.NODE_ENV !== "production") {
102
104
  actaContractErrorMessages = {
@@ -133,6 +135,7 @@ if (process.env.NODE_ENV !== "production") {
133
135
  [ACTA_CONTRACT_ERROR__ORACLE_HAS_ACTIVE_MARKETS]: `Oracle is still linked to active markets`,
134
136
  [ACTA_CONTRACT_ERROR__ORACLE_INACTIVE]: `Oracle inactive`,
135
137
  [ACTA_CONTRACT_ERROR__ORACLE_INVALID_PRICE]: `Oracle price invalid (zero or negative)`,
138
+ [ACTA_CONTRACT_ERROR__ORACLE_NOT_EXPIRED_YET]: `Oracle has not expired yet (cannot update price before expiry)`,
136
139
  [ACTA_CONTRACT_ERROR__ORACLE_SOURCE_LOCKED]: `Oracle source fields are locked after first update`,
137
140
  [ACTA_CONTRACT_ERROR__ORACLE_STALE]: `Oracle price is stale (updated_at before expiry)`,
138
141
  [ACTA_CONTRACT_ERROR__POSITION_ALREADY_FUNDED]: `Position already funded by maker`,
@@ -2312,6 +2312,11 @@
2312
2312
  "code": 1076,
2313
2313
  "name": "MarketNotExpired",
2314
2314
  "msg": "Market has not expired yet"
2315
+ },
2316
+ {
2317
+ "code": 1077,
2318
+ "name": "OracleNotExpiredYet",
2319
+ "msg": "Oracle has not expired yet (cannot update price before expiry)"
2315
2320
  }
2316
2321
  ]
2317
2322
  }
@@ -1 +1 @@
1
- export declare const ACTA_IDL_SHA256 = "34f93515df3ceed9854715fb73e9fe0de615383e5ff3f0d627ecd029e65e5688";
1
+ export declare const ACTA_IDL_SHA256 = "2ccf0a77f682ff60b8201e2bca7f4bd026500a2fb9637d46dad7a8de3b2ee313";
package/dist/idl/hash.js CHANGED
@@ -1 +1 @@
1
- export const ACTA_IDL_SHA256 = "34f93515df3ceed9854715fb73e9fe0de615383e5ff3f0d627ecd029e65e5688";
1
+ export const ACTA_IDL_SHA256 = "2ccf0a77f682ff60b8201e2bca7f4bd026500a2fb9637d46dad7a8de3b2ee313";
package/dist/ws/auth.d.ts CHANGED
@@ -6,6 +6,9 @@
6
6
  * - Client signs UTF-8 bytes of that text and responds:
7
7
  * `AuthChallenge { challenge, signature: base58(ed25519(utf8(challenge))), pubkey }`
8
8
  *
9
+ * The same `signMessage` primitive is reused for any UTF-8-bytes signing
10
+ * (e.g. `acta:redeem:v1:{pubkey}:{code}` for invite redemption).
11
+ *
9
12
  * Source of truth (server):
10
13
  * - rust-backend/rfq-server/src/server/ws.rs
11
14
  * - rust-backend/rfq-server/src/session/handler.rs
@@ -13,8 +16,8 @@
13
16
  export interface AuthProvider {
14
17
  /** Base58-encoded public key (address). */
15
18
  getPublicKey(): Promise<string>;
16
- /** Base58-encoded ed25519 signature over UTF-8 bytes of the challenge text. */
17
- signChallenge(challenge: string): Promise<string>;
19
+ /** Base58-encoded ed25519 signature over UTF-8 bytes of the message. */
20
+ signMessage(message: string): Promise<string>;
18
21
  }
19
22
  /**
20
23
  * Auth provider backed by a `CryptoKeyPair`.
@@ -31,7 +34,7 @@ export declare class KeypairAuthProvider implements AuthProvider {
31
34
  /** Create from Solana CLI keypair JSON (array of numbers). */
32
35
  static fromJson(json: number[] | string): Promise<KeypairAuthProvider>;
33
36
  getPublicKey(): Promise<string>;
34
- signChallenge(challenge: string): Promise<string>;
37
+ signMessage(message: string): Promise<string>;
35
38
  }
36
39
  /**
37
40
  * Wallet-like provider (browser wallets, custom signers, etc.)
@@ -43,7 +46,7 @@ export declare class WalletAuthProvider implements AuthProvider {
43
46
  private readonly wallet;
44
47
  constructor(wallet: WalletLike);
45
48
  getPublicKey(): Promise<string>;
46
- signChallenge(challenge: string): Promise<string>;
49
+ signMessage(message: string): Promise<string>;
47
50
  }
48
51
  export type WalletLike = {
49
52
  /** Preferred: base58 address string. */
@@ -60,8 +63,8 @@ export type WalletLike = {
60
63
  */
61
64
  export declare class CustomAuthProvider implements AuthProvider {
62
65
  private readonly getPublicKeyFn;
63
- private readonly signChallengeFn;
64
- constructor(getPublicKey: () => Promise<string>, signChallenge: (challenge: string) => Promise<string>);
66
+ private readonly signMessageFn;
67
+ constructor(getPublicKey: () => Promise<string>, signMessage: (message: string) => Promise<string>);
65
68
  getPublicKey(): Promise<string>;
66
- signChallenge(challenge: string): Promise<string>;
69
+ signMessage(message: string): Promise<string>;
67
70
  }