@taphubhq/sdk-core 0.15.1 → 0.15.2
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/index.cjs +112 -2
- package/dist/index.d.mts +86 -2
- package/dist/index.d.ts +86 -2
- package/dist/index.js +112 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -885,6 +885,10 @@ var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
|
|
|
885
885
|
var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
|
|
886
886
|
var TOPIC_PREFIX = "game";
|
|
887
887
|
var MARKET_TOPIC_PREFIX = "market";
|
|
888
|
+
var WALLET_TOPIC_PREFIX = "wallet";
|
|
889
|
+
function walletBalanceTopic(userId) {
|
|
890
|
+
return `${WALLET_TOPIC_PREFIX}/users/${userId}/balance`;
|
|
891
|
+
}
|
|
888
892
|
function agencyPairStatsTopic(aid, gamePairId) {
|
|
889
893
|
return `public/agency/${aid}/pair/${gamePairId}/stats`;
|
|
890
894
|
}
|
|
@@ -905,6 +909,7 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
905
909
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
906
910
|
const candleSubscriptions = /* @__PURE__ */ new Map();
|
|
907
911
|
const statsSubscriptions = /* @__PURE__ */ new Map();
|
|
912
|
+
const walletSubscriptions = /* @__PURE__ */ new Map();
|
|
908
913
|
const { onLifecycle } = opts;
|
|
909
914
|
let connectStartedAt = 0;
|
|
910
915
|
function fireLifecycle(event) {
|
|
@@ -973,6 +978,18 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
973
978
|
statsSub.onStats(receivedTopic, payload2);
|
|
974
979
|
return;
|
|
975
980
|
}
|
|
981
|
+
const walletSub = [...walletSubscriptions.values()].find((s) => s.topic === receivedTopic);
|
|
982
|
+
if (walletSub) {
|
|
983
|
+
let payload2;
|
|
984
|
+
try {
|
|
985
|
+
payload2 = JSON.parse(message.toString());
|
|
986
|
+
} catch {
|
|
987
|
+
walletSub.onError(new Error(`Invalid JSON on topic ${receivedTopic}`));
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
walletSub.onMessage(receivedTopic, payload2);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
976
993
|
let matched;
|
|
977
994
|
for (const sub of subscriptions.values()) {
|
|
978
995
|
if (sub.topics.includes(receivedTopic)) {
|
|
@@ -1047,6 +1064,19 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
1047
1064
|
if (client) client.unsubscribe(entry.topic);
|
|
1048
1065
|
statsSubscriptions.delete(topic);
|
|
1049
1066
|
},
|
|
1067
|
+
subscribeWallet(userId, onMessage, onError) {
|
|
1068
|
+
if (walletSubscriptions.has(userId)) return;
|
|
1069
|
+
const topic = walletBalanceTopic(userId);
|
|
1070
|
+
const mqttClient = ensureConnected();
|
|
1071
|
+
walletSubscriptions.set(userId, { topic, onMessage, onError });
|
|
1072
|
+
mqttClient.subscribe(topic, { qos: 0 });
|
|
1073
|
+
},
|
|
1074
|
+
unsubscribeWallet(userId) {
|
|
1075
|
+
const entry = walletSubscriptions.get(userId);
|
|
1076
|
+
if (!entry) return;
|
|
1077
|
+
if (client) client.unsubscribe(entry.topic);
|
|
1078
|
+
walletSubscriptions.delete(userId);
|
|
1079
|
+
},
|
|
1050
1080
|
unsubscribeAll(gameId, userId) {
|
|
1051
1081
|
const matches = entriesForGame(gameId);
|
|
1052
1082
|
if (matches.length === 0) return;
|
|
@@ -1084,10 +1114,14 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
1084
1114
|
for (const entry of statsSubscriptions.values()) {
|
|
1085
1115
|
client.unsubscribe(entry.topic);
|
|
1086
1116
|
}
|
|
1117
|
+
for (const entry of walletSubscriptions.values()) {
|
|
1118
|
+
client.unsubscribe(entry.topic);
|
|
1119
|
+
}
|
|
1087
1120
|
}
|
|
1088
1121
|
subscriptions.clear();
|
|
1089
1122
|
candleSubscriptions.clear();
|
|
1090
1123
|
statsSubscriptions.clear();
|
|
1124
|
+
walletSubscriptions.clear();
|
|
1091
1125
|
if (client) {
|
|
1092
1126
|
client.end(true);
|
|
1093
1127
|
client = null;
|
|
@@ -1097,7 +1131,7 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
1097
1131
|
}
|
|
1098
1132
|
|
|
1099
1133
|
// src/modules/realtime/index.ts
|
|
1100
|
-
var
|
|
1134
|
+
var import_eventemitter33 = __toESM(require("eventemitter3"));
|
|
1101
1135
|
|
|
1102
1136
|
// src/modules/realtime/GameChannel.ts
|
|
1103
1137
|
var import_eventemitter3 = __toESM(require("eventemitter3"));
|
|
@@ -1109,6 +1143,16 @@ var GameChannel = class extends import_eventemitter3.default {
|
|
|
1109
1143
|
}
|
|
1110
1144
|
};
|
|
1111
1145
|
|
|
1146
|
+
// src/modules/realtime/WalletChannel.ts
|
|
1147
|
+
var import_eventemitter32 = __toESM(require("eventemitter3"));
|
|
1148
|
+
var WalletChannel = class extends import_eventemitter32.default {
|
|
1149
|
+
userId;
|
|
1150
|
+
constructor(userId) {
|
|
1151
|
+
super();
|
|
1152
|
+
this.userId = userId;
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1112
1156
|
// src/modules/realtime/index.ts
|
|
1113
1157
|
var TOPIC_SUFFIX_CANDLE = "candle";
|
|
1114
1158
|
var TOPIC_SUFFIX_BID_RESULT = "bid_result";
|
|
@@ -1168,6 +1212,16 @@ function mapWireBalanceUpdate(raw) {
|
|
|
1168
1212
|
balance: p.balance
|
|
1169
1213
|
};
|
|
1170
1214
|
}
|
|
1215
|
+
function mapWireWalletBalance(raw) {
|
|
1216
|
+
const p = raw;
|
|
1217
|
+
return {
|
|
1218
|
+
id: p.id,
|
|
1219
|
+
balance: p.balance,
|
|
1220
|
+
reservedAmount: p.reserved_amount,
|
|
1221
|
+
currency: p.currency,
|
|
1222
|
+
reason: p.reason ?? ""
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1171
1225
|
function mapWireConfig(raw) {
|
|
1172
1226
|
const p = raw;
|
|
1173
1227
|
return {
|
|
@@ -1205,9 +1259,11 @@ function mapWireToEvent(topic, payload) {
|
|
|
1205
1259
|
function normaliseUserId2(userId) {
|
|
1206
1260
|
return userId && userId !== "" ? userId : null;
|
|
1207
1261
|
}
|
|
1208
|
-
var RealtimeModule = class extends
|
|
1262
|
+
var RealtimeModule = class extends import_eventemitter33.default {
|
|
1209
1263
|
#transport;
|
|
1210
1264
|
#entries = /* @__PURE__ */ new Map();
|
|
1265
|
+
#walletEntries = /* @__PURE__ */ new Map();
|
|
1266
|
+
// keyed by userId
|
|
1211
1267
|
#agencyId;
|
|
1212
1268
|
constructor(mqttEndpointOrOptions) {
|
|
1213
1269
|
super();
|
|
@@ -1287,6 +1343,56 @@ var RealtimeModule = class extends import_eventemitter32.default {
|
|
|
1287
1343
|
target.channel.removeAllListeners();
|
|
1288
1344
|
this.#transport.unsubscribeAll(gameId, target.userId);
|
|
1289
1345
|
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Subscribe to the user-scoped wallet balance stream for `userId`, topic
|
|
1348
|
+
* `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
|
|
1349
|
+
* `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
|
|
1350
|
+
* reserve / commit / cancel / bid settlement) — independent of any game.
|
|
1351
|
+
*
|
|
1352
|
+
* Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
|
|
1353
|
+
* calls return the same channel and the topic is torn down only when balanced
|
|
1354
|
+
* `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
|
|
1355
|
+
* fully independent of game subscriptions.
|
|
1356
|
+
*/
|
|
1357
|
+
subscribeWallet(userId) {
|
|
1358
|
+
const cleanUserId = normaliseUserId2(userId);
|
|
1359
|
+
if (!cleanUserId) {
|
|
1360
|
+
throw new TaphubError("userId is required to subscribe to wallet balance", {
|
|
1361
|
+
code: "UserIdRequired"
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
const existing = this.#walletEntries.get(cleanUserId);
|
|
1365
|
+
if (existing) {
|
|
1366
|
+
existing.refcount += 1;
|
|
1367
|
+
return existing.channel;
|
|
1368
|
+
}
|
|
1369
|
+
const channel = new WalletChannel(cleanUserId);
|
|
1370
|
+
const onMessage = (_topic, payload) => {
|
|
1371
|
+
channel.emit("walletBalanceUpdate", mapWireWalletBalance(payload));
|
|
1372
|
+
};
|
|
1373
|
+
const onError = (err) => {
|
|
1374
|
+
channel.emit("error", err);
|
|
1375
|
+
};
|
|
1376
|
+
this.#walletEntries.set(cleanUserId, { userId: cleanUserId, channel, refcount: 1 });
|
|
1377
|
+
this.#transport.subscribeWallet(cleanUserId, onMessage, onError);
|
|
1378
|
+
return channel;
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
|
|
1382
|
+
* topic and removes the `WalletChannel` only when the count reaches zero.
|
|
1383
|
+
* No-op if there is no active wallet subscription for `userId`.
|
|
1384
|
+
*/
|
|
1385
|
+
unsubscribeWallet(userId) {
|
|
1386
|
+
const cleanUserId = normaliseUserId2(userId);
|
|
1387
|
+
if (!cleanUserId) return;
|
|
1388
|
+
const entry = this.#walletEntries.get(cleanUserId);
|
|
1389
|
+
if (!entry) return;
|
|
1390
|
+
entry.refcount -= 1;
|
|
1391
|
+
if (entry.refcount > 0) return;
|
|
1392
|
+
this.#walletEntries.delete(cleanUserId);
|
|
1393
|
+
entry.channel.removeAllListeners();
|
|
1394
|
+
this.#transport.unsubscribeWallet(cleanUserId);
|
|
1395
|
+
}
|
|
1290
1396
|
/**
|
|
1291
1397
|
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
1292
1398
|
* Independent of game/agency — candle is market-wide public data.
|
|
@@ -1330,6 +1436,10 @@ var RealtimeModule = class extends import_eventemitter32.default {
|
|
|
1330
1436
|
entry.channel.removeAllListeners();
|
|
1331
1437
|
}
|
|
1332
1438
|
this.#entries.clear();
|
|
1439
|
+
for (const entry of this.#walletEntries.values()) {
|
|
1440
|
+
entry.channel.removeAllListeners();
|
|
1441
|
+
}
|
|
1442
|
+
this.#walletEntries.clear();
|
|
1333
1443
|
this.#transport.close();
|
|
1334
1444
|
}
|
|
1335
1445
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -482,7 +482,19 @@ interface MqttWireAgencyPairStats {
|
|
|
482
482
|
maxCoef: number;
|
|
483
483
|
ts: number;
|
|
484
484
|
}
|
|
485
|
-
|
|
485
|
+
/**
|
|
486
|
+
* Wire payload on the user-scoped wallet topic `wallet/users/{userId}/balance`,
|
|
487
|
+
* published by `taphub-user-service` (`streams/wallet.go`). All fields are JSON
|
|
488
|
+
* strings. `reserved_amount` is snake_case on the wire.
|
|
489
|
+
*/
|
|
490
|
+
interface MqttWireWalletBalance {
|
|
491
|
+
id: string;
|
|
492
|
+
balance: string;
|
|
493
|
+
reserved_amount: string;
|
|
494
|
+
currency: string;
|
|
495
|
+
reason: string;
|
|
496
|
+
}
|
|
497
|
+
type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats | MqttWireWalletBalance;
|
|
486
498
|
type MqttMessageHandler = (gameId: string, topic: string, payload: MqttWirePayload) => void;
|
|
487
499
|
type MqttErrorHandler = (err: Error) => void;
|
|
488
500
|
type MqttLifecycleEvent = {
|
|
@@ -503,6 +515,7 @@ type MqttLifecycleEvent = {
|
|
|
503
515
|
type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
|
|
504
516
|
type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
505
517
|
type MqttAgencyPairStatsHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
518
|
+
type MqttWalletMessageHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
506
519
|
interface MqttTransport {
|
|
507
520
|
/**
|
|
508
521
|
* Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
|
|
@@ -536,6 +549,15 @@ interface MqttTransport {
|
|
|
536
549
|
subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
|
|
537
550
|
/** Tear down the stats subscription for an `(aid, gamePairId)`. */
|
|
538
551
|
unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
|
|
552
|
+
/**
|
|
553
|
+
* Subscribe to the user-scoped wallet balance stream, topic
|
|
554
|
+
* `wallet/users/{userId}/balance`. Independent of any game subscription — NOT
|
|
555
|
+
* torn down by `unsubscribeAll`. Idempotent: repeat calls for the same
|
|
556
|
+
* `userId` are deduplicated. Subscribed at QoS 0 to match the publisher.
|
|
557
|
+
*/
|
|
558
|
+
subscribeWallet(userId: string, onMessage: MqttWalletMessageHandler, onError: MqttErrorHandler): void;
|
|
559
|
+
/** Tear down the wallet subscription for a `userId`. No-op if not subscribed. */
|
|
560
|
+
unsubscribeWallet(userId: string): void;
|
|
539
561
|
close(): void;
|
|
540
562
|
}
|
|
541
563
|
|
|
@@ -598,6 +620,36 @@ interface MqttBalanceEvent {
|
|
|
598
620
|
userId: string;
|
|
599
621
|
balance: string;
|
|
600
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* Cause of a wallet balance change. Byte-mirrors `BalanceUpdateReason` in the
|
|
625
|
+
* backend (`taphub-user-service` `streams/wallet.go`). `''` is the documented
|
|
626
|
+
* unspecified/legacy value. Treat unknown reasons defensively (enum fallback) —
|
|
627
|
+
* the SDK passes through values it does not recognise rather than dropping the
|
|
628
|
+
* event.
|
|
629
|
+
*/
|
|
630
|
+
type WalletBalanceReason = 'transfer' | 'refund' | 'reserve' | 'commit' | 'cancel' | 'bid_placed' | 'bid_won' | '';
|
|
631
|
+
/**
|
|
632
|
+
* Wallet-level balance event from the user-scoped MQTT topic
|
|
633
|
+
* `wallet/users/{userId}/balance`. Fires on deposit / agency transfer / refund /
|
|
634
|
+
* reserve / commit / cancel / bid settlement — i.e. every balance change, with
|
|
635
|
+
* NO game context (unlike the game-scoped `MqttBalanceEvent`).
|
|
636
|
+
*
|
|
637
|
+
* Money fields (`balance`, `reservedAmount`) are kept as wire strings verbatim;
|
|
638
|
+
* the SDK does NOT coerce decimal money to `number`. Consumers coerce at the
|
|
639
|
+
* app edge if they need a number.
|
|
640
|
+
*/
|
|
641
|
+
interface MqttWalletBalanceEvent {
|
|
642
|
+
/** Wallet id (the backend `id` field). */
|
|
643
|
+
id: string;
|
|
644
|
+
/** Spendable balance, as the raw wire string. */
|
|
645
|
+
balance: string;
|
|
646
|
+
/** Amount held by PENDING reservations, as the raw wire string. */
|
|
647
|
+
reservedAmount: string;
|
|
648
|
+
/** Wallet currency (e.g. "USDC.e"). */
|
|
649
|
+
currency: string;
|
|
650
|
+
/** Cause of the change; unknown values are passed through as-is. */
|
|
651
|
+
reason: WalletBalanceReason;
|
|
652
|
+
}
|
|
601
653
|
interface MqttConfigEvent {
|
|
602
654
|
minBidAmount: number;
|
|
603
655
|
maxBidAmount: number;
|
|
@@ -638,6 +690,20 @@ declare class GameChannel extends EventEmitter<GameChannelEvents> {
|
|
|
638
690
|
constructor(gameId: string);
|
|
639
691
|
}
|
|
640
692
|
|
|
693
|
+
interface WalletChannelEvents {
|
|
694
|
+
walletBalanceUpdate: [MqttWalletBalanceEvent];
|
|
695
|
+
error: [Error];
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* User-scoped wallet channel. Emits `walletBalanceUpdate` for every balance
|
|
699
|
+
* change on `wallet/users/{userId}/balance`, independent of any game. Mirror of
|
|
700
|
+
* {@link GameChannel} but keyed by `userId` rather than `gameId`.
|
|
701
|
+
*/
|
|
702
|
+
declare class WalletChannel extends EventEmitter<WalletChannelEvents> {
|
|
703
|
+
readonly userId: string;
|
|
704
|
+
constructor(userId: string);
|
|
705
|
+
}
|
|
706
|
+
|
|
641
707
|
interface RealtimeModuleOptions {
|
|
642
708
|
mqttEndpoint: string;
|
|
643
709
|
/**
|
|
@@ -665,6 +731,24 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
|
|
|
665
731
|
get _transport(): MqttTransport;
|
|
666
732
|
subscribe(gameId: string, userId?: string | null): GameChannel;
|
|
667
733
|
unsubscribe(gameId: string, userId?: string | null): void;
|
|
734
|
+
/**
|
|
735
|
+
* Subscribe to the user-scoped wallet balance stream for `userId`, topic
|
|
736
|
+
* `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
|
|
737
|
+
* `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
|
|
738
|
+
* reserve / commit / cancel / bid settlement) — independent of any game.
|
|
739
|
+
*
|
|
740
|
+
* Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
|
|
741
|
+
* calls return the same channel and the topic is torn down only when balanced
|
|
742
|
+
* `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
|
|
743
|
+
* fully independent of game subscriptions.
|
|
744
|
+
*/
|
|
745
|
+
subscribeWallet(userId: string): WalletChannel;
|
|
746
|
+
/**
|
|
747
|
+
* Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
|
|
748
|
+
* topic and removes the `WalletChannel` only when the count reaches zero.
|
|
749
|
+
* No-op if there is no active wallet subscription for `userId`.
|
|
750
|
+
*/
|
|
751
|
+
unsubscribeWallet(userId: string): void;
|
|
668
752
|
/**
|
|
669
753
|
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
670
754
|
* Independent of game/agency — candle is market-wide public data.
|
|
@@ -901,4 +985,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
|
|
|
901
985
|
declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
902
986
|
declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
903
987
|
|
|
904
|
-
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
|
|
988
|
+
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
|
package/dist/index.d.ts
CHANGED
|
@@ -482,7 +482,19 @@ interface MqttWireAgencyPairStats {
|
|
|
482
482
|
maxCoef: number;
|
|
483
483
|
ts: number;
|
|
484
484
|
}
|
|
485
|
-
|
|
485
|
+
/**
|
|
486
|
+
* Wire payload on the user-scoped wallet topic `wallet/users/{userId}/balance`,
|
|
487
|
+
* published by `taphub-user-service` (`streams/wallet.go`). All fields are JSON
|
|
488
|
+
* strings. `reserved_amount` is snake_case on the wire.
|
|
489
|
+
*/
|
|
490
|
+
interface MqttWireWalletBalance {
|
|
491
|
+
id: string;
|
|
492
|
+
balance: string;
|
|
493
|
+
reserved_amount: string;
|
|
494
|
+
currency: string;
|
|
495
|
+
reason: string;
|
|
496
|
+
}
|
|
497
|
+
type MqttWirePayload = MqttWireCandle | MqttWireBidResult | MqttWireBalanceUpdate | MqttWireConfig | MqttWireAgencyPairStats | MqttWireWalletBalance;
|
|
486
498
|
type MqttMessageHandler = (gameId: string, topic: string, payload: MqttWirePayload) => void;
|
|
487
499
|
type MqttErrorHandler = (err: Error) => void;
|
|
488
500
|
type MqttLifecycleEvent = {
|
|
@@ -503,6 +515,7 @@ type MqttLifecycleEvent = {
|
|
|
503
515
|
type MqttLifecycleHook = (event: MqttLifecycleEvent) => void;
|
|
504
516
|
type MqttCandleHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
505
517
|
type MqttAgencyPairStatsHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
518
|
+
type MqttWalletMessageHandler = (topic: string, payload: MqttWirePayload) => void;
|
|
506
519
|
interface MqttTransport {
|
|
507
520
|
/**
|
|
508
521
|
* Subscribe to a game's MQTT topics (config, ideal_config, bid_result, balance)
|
|
@@ -536,6 +549,15 @@ interface MqttTransport {
|
|
|
536
549
|
subscribeAgencyPairStats(aid: string, gamePairId: string, onStats: MqttAgencyPairStatsHandler): void;
|
|
537
550
|
/** Tear down the stats subscription for an `(aid, gamePairId)`. */
|
|
538
551
|
unsubscribeAgencyPairStats(aid: string, gamePairId: string): void;
|
|
552
|
+
/**
|
|
553
|
+
* Subscribe to the user-scoped wallet balance stream, topic
|
|
554
|
+
* `wallet/users/{userId}/balance`. Independent of any game subscription — NOT
|
|
555
|
+
* torn down by `unsubscribeAll`. Idempotent: repeat calls for the same
|
|
556
|
+
* `userId` are deduplicated. Subscribed at QoS 0 to match the publisher.
|
|
557
|
+
*/
|
|
558
|
+
subscribeWallet(userId: string, onMessage: MqttWalletMessageHandler, onError: MqttErrorHandler): void;
|
|
559
|
+
/** Tear down the wallet subscription for a `userId`. No-op if not subscribed. */
|
|
560
|
+
unsubscribeWallet(userId: string): void;
|
|
539
561
|
close(): void;
|
|
540
562
|
}
|
|
541
563
|
|
|
@@ -598,6 +620,36 @@ interface MqttBalanceEvent {
|
|
|
598
620
|
userId: string;
|
|
599
621
|
balance: string;
|
|
600
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* Cause of a wallet balance change. Byte-mirrors `BalanceUpdateReason` in the
|
|
625
|
+
* backend (`taphub-user-service` `streams/wallet.go`). `''` is the documented
|
|
626
|
+
* unspecified/legacy value. Treat unknown reasons defensively (enum fallback) —
|
|
627
|
+
* the SDK passes through values it does not recognise rather than dropping the
|
|
628
|
+
* event.
|
|
629
|
+
*/
|
|
630
|
+
type WalletBalanceReason = 'transfer' | 'refund' | 'reserve' | 'commit' | 'cancel' | 'bid_placed' | 'bid_won' | '';
|
|
631
|
+
/**
|
|
632
|
+
* Wallet-level balance event from the user-scoped MQTT topic
|
|
633
|
+
* `wallet/users/{userId}/balance`. Fires on deposit / agency transfer / refund /
|
|
634
|
+
* reserve / commit / cancel / bid settlement — i.e. every balance change, with
|
|
635
|
+
* NO game context (unlike the game-scoped `MqttBalanceEvent`).
|
|
636
|
+
*
|
|
637
|
+
* Money fields (`balance`, `reservedAmount`) are kept as wire strings verbatim;
|
|
638
|
+
* the SDK does NOT coerce decimal money to `number`. Consumers coerce at the
|
|
639
|
+
* app edge if they need a number.
|
|
640
|
+
*/
|
|
641
|
+
interface MqttWalletBalanceEvent {
|
|
642
|
+
/** Wallet id (the backend `id` field). */
|
|
643
|
+
id: string;
|
|
644
|
+
/** Spendable balance, as the raw wire string. */
|
|
645
|
+
balance: string;
|
|
646
|
+
/** Amount held by PENDING reservations, as the raw wire string. */
|
|
647
|
+
reservedAmount: string;
|
|
648
|
+
/** Wallet currency (e.g. "USDC.e"). */
|
|
649
|
+
currency: string;
|
|
650
|
+
/** Cause of the change; unknown values are passed through as-is. */
|
|
651
|
+
reason: WalletBalanceReason;
|
|
652
|
+
}
|
|
601
653
|
interface MqttConfigEvent {
|
|
602
654
|
minBidAmount: number;
|
|
603
655
|
maxBidAmount: number;
|
|
@@ -638,6 +690,20 @@ declare class GameChannel extends EventEmitter<GameChannelEvents> {
|
|
|
638
690
|
constructor(gameId: string);
|
|
639
691
|
}
|
|
640
692
|
|
|
693
|
+
interface WalletChannelEvents {
|
|
694
|
+
walletBalanceUpdate: [MqttWalletBalanceEvent];
|
|
695
|
+
error: [Error];
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* User-scoped wallet channel. Emits `walletBalanceUpdate` for every balance
|
|
699
|
+
* change on `wallet/users/{userId}/balance`, independent of any game. Mirror of
|
|
700
|
+
* {@link GameChannel} but keyed by `userId` rather than `gameId`.
|
|
701
|
+
*/
|
|
702
|
+
declare class WalletChannel extends EventEmitter<WalletChannelEvents> {
|
|
703
|
+
readonly userId: string;
|
|
704
|
+
constructor(userId: string);
|
|
705
|
+
}
|
|
706
|
+
|
|
641
707
|
interface RealtimeModuleOptions {
|
|
642
708
|
mqttEndpoint: string;
|
|
643
709
|
/**
|
|
@@ -665,6 +731,24 @@ declare class RealtimeModule extends EventEmitter<RealtimeModuleEvents> {
|
|
|
665
731
|
get _transport(): MqttTransport;
|
|
666
732
|
subscribe(gameId: string, userId?: string | null): GameChannel;
|
|
667
733
|
unsubscribe(gameId: string, userId?: string | null): void;
|
|
734
|
+
/**
|
|
735
|
+
* Subscribe to the user-scoped wallet balance stream for `userId`, topic
|
|
736
|
+
* `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
|
|
737
|
+
* `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
|
|
738
|
+
* reserve / commit / cancel / bid settlement) — independent of any game.
|
|
739
|
+
*
|
|
740
|
+
* Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
|
|
741
|
+
* calls return the same channel and the topic is torn down only when balanced
|
|
742
|
+
* `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
|
|
743
|
+
* fully independent of game subscriptions.
|
|
744
|
+
*/
|
|
745
|
+
subscribeWallet(userId: string): WalletChannel;
|
|
746
|
+
/**
|
|
747
|
+
* Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
|
|
748
|
+
* topic and removes the `WalletChannel` only when the count reaches zero.
|
|
749
|
+
* No-op if there is no active wallet subscription for `userId`.
|
|
750
|
+
*/
|
|
751
|
+
unsubscribeWallet(userId: string): void;
|
|
668
752
|
/**
|
|
669
753
|
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
670
754
|
* Independent of game/agency — candle is market-wide public data.
|
|
@@ -901,4 +985,4 @@ declare function adaptiveSimpson(f: (x: number) => number, a: number, b: number,
|
|
|
901
985
|
declare function calculateProbWin(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
902
986
|
declare function calculateProbWin_v2(time1: number, time2: number, _price1: number, price2: number, creationTime: number, currentPrice: number, volatility: number): number;
|
|
903
987
|
|
|
904
|
-
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
|
|
988
|
+
export { type AgencyPairFilter, AgencyPairModule, type AgencyPairStats, type AgencyPairStatus, AuthModule, type BackendHealth, type Bid, BidModule, type BidStatus, type CancelBidResult, type Candle, type Constraints, type Currency, type Game, GameChannel, type GameConfig, GameModule, type GamePairInfo, type GridConfig, type LeaderboardEntry, LeaderboardModule, type LeaderboardPeriod, type LeaderboardSortBy, LocaleModule, type LocaleRefreshResult, type LocaleResponse, type LocaleTranslations, type LoginResult, type Me, type MqttAcceptedBid, type MqttAgencyPairStatsEvent, type MqttBalanceEvent, type MqttBidAcceptedEvent, type MqttBidLostEvent, type MqttBidWonEvent, type MqttCandleEvent, type MqttConfigEvent, type MqttIdealConfigEvent, type MqttWalletBalanceEvent, type NetworkLevel, type NetworkQuality, NetworkQualityMonitor, type PlaceBidInput, RealtimeModule, type Sample, type SampleReason, type SignalSource, TaphubAuthError, TaphubClient, type TaphubClientConfig, TaphubError, TaphubEventBus, type TaphubEventMap, TaphubNetworkError, TaphubServerError, TaphubSlippageError, TaphubStorage, type TaphubStorageAdapter, TaphubValidationError, type User, UserModule, type UserPnL, type UserPnLPeriod, type Wallet as UserWallet, type Wallet$1 as Wallet, type WalletBalanceReason, WalletChannel, adaptiveSimpson, autoDetectStorage, calculateProbWin, calculateProbWin_v2, errorFunction, isCancelled, isLoss, isPending, isTerminal, isWin, normalCDF, normalPDF };
|
package/dist/index.js
CHANGED
|
@@ -820,6 +820,10 @@ var GAME_SCOPED_SUFFIXES = ["config", "ideal_config"];
|
|
|
820
820
|
var USER_SCOPED_SUFFIXES = ["bid_result", "balance_update"];
|
|
821
821
|
var TOPIC_PREFIX = "game";
|
|
822
822
|
var MARKET_TOPIC_PREFIX = "market";
|
|
823
|
+
var WALLET_TOPIC_PREFIX = "wallet";
|
|
824
|
+
function walletBalanceTopic(userId) {
|
|
825
|
+
return `${WALLET_TOPIC_PREFIX}/users/${userId}/balance`;
|
|
826
|
+
}
|
|
823
827
|
function agencyPairStatsTopic(aid, gamePairId) {
|
|
824
828
|
return `public/agency/${aid}/pair/${gamePairId}/stats`;
|
|
825
829
|
}
|
|
@@ -840,6 +844,7 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
840
844
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
841
845
|
const candleSubscriptions = /* @__PURE__ */ new Map();
|
|
842
846
|
const statsSubscriptions = /* @__PURE__ */ new Map();
|
|
847
|
+
const walletSubscriptions = /* @__PURE__ */ new Map();
|
|
843
848
|
const { onLifecycle } = opts;
|
|
844
849
|
let connectStartedAt = 0;
|
|
845
850
|
function fireLifecycle(event) {
|
|
@@ -908,6 +913,18 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
908
913
|
statsSub.onStats(receivedTopic, payload2);
|
|
909
914
|
return;
|
|
910
915
|
}
|
|
916
|
+
const walletSub = [...walletSubscriptions.values()].find((s) => s.topic === receivedTopic);
|
|
917
|
+
if (walletSub) {
|
|
918
|
+
let payload2;
|
|
919
|
+
try {
|
|
920
|
+
payload2 = JSON.parse(message.toString());
|
|
921
|
+
} catch {
|
|
922
|
+
walletSub.onError(new Error(`Invalid JSON on topic ${receivedTopic}`));
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
walletSub.onMessage(receivedTopic, payload2);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
911
928
|
let matched;
|
|
912
929
|
for (const sub of subscriptions.values()) {
|
|
913
930
|
if (sub.topics.includes(receivedTopic)) {
|
|
@@ -982,6 +999,19 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
982
999
|
if (client) client.unsubscribe(entry.topic);
|
|
983
1000
|
statsSubscriptions.delete(topic);
|
|
984
1001
|
},
|
|
1002
|
+
subscribeWallet(userId, onMessage, onError) {
|
|
1003
|
+
if (walletSubscriptions.has(userId)) return;
|
|
1004
|
+
const topic = walletBalanceTopic(userId);
|
|
1005
|
+
const mqttClient = ensureConnected();
|
|
1006
|
+
walletSubscriptions.set(userId, { topic, onMessage, onError });
|
|
1007
|
+
mqttClient.subscribe(topic, { qos: 0 });
|
|
1008
|
+
},
|
|
1009
|
+
unsubscribeWallet(userId) {
|
|
1010
|
+
const entry = walletSubscriptions.get(userId);
|
|
1011
|
+
if (!entry) return;
|
|
1012
|
+
if (client) client.unsubscribe(entry.topic);
|
|
1013
|
+
walletSubscriptions.delete(userId);
|
|
1014
|
+
},
|
|
985
1015
|
unsubscribeAll(gameId, userId) {
|
|
986
1016
|
const matches = entriesForGame(gameId);
|
|
987
1017
|
if (matches.length === 0) return;
|
|
@@ -1019,10 +1049,14 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
1019
1049
|
for (const entry of statsSubscriptions.values()) {
|
|
1020
1050
|
client.unsubscribe(entry.topic);
|
|
1021
1051
|
}
|
|
1052
|
+
for (const entry of walletSubscriptions.values()) {
|
|
1053
|
+
client.unsubscribe(entry.topic);
|
|
1054
|
+
}
|
|
1022
1055
|
}
|
|
1023
1056
|
subscriptions.clear();
|
|
1024
1057
|
candleSubscriptions.clear();
|
|
1025
1058
|
statsSubscriptions.clear();
|
|
1059
|
+
walletSubscriptions.clear();
|
|
1026
1060
|
if (client) {
|
|
1027
1061
|
client.end(true);
|
|
1028
1062
|
client = null;
|
|
@@ -1032,7 +1066,7 @@ function createMqttTransport(endpoint, opts = {}) {
|
|
|
1032
1066
|
}
|
|
1033
1067
|
|
|
1034
1068
|
// src/modules/realtime/index.ts
|
|
1035
|
-
import
|
|
1069
|
+
import EventEmitter3 from "eventemitter3";
|
|
1036
1070
|
|
|
1037
1071
|
// src/modules/realtime/GameChannel.ts
|
|
1038
1072
|
import EventEmitter from "eventemitter3";
|
|
@@ -1044,6 +1078,16 @@ var GameChannel = class extends EventEmitter {
|
|
|
1044
1078
|
}
|
|
1045
1079
|
};
|
|
1046
1080
|
|
|
1081
|
+
// src/modules/realtime/WalletChannel.ts
|
|
1082
|
+
import EventEmitter2 from "eventemitter3";
|
|
1083
|
+
var WalletChannel = class extends EventEmitter2 {
|
|
1084
|
+
userId;
|
|
1085
|
+
constructor(userId) {
|
|
1086
|
+
super();
|
|
1087
|
+
this.userId = userId;
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1047
1091
|
// src/modules/realtime/index.ts
|
|
1048
1092
|
var TOPIC_SUFFIX_CANDLE = "candle";
|
|
1049
1093
|
var TOPIC_SUFFIX_BID_RESULT = "bid_result";
|
|
@@ -1103,6 +1147,16 @@ function mapWireBalanceUpdate(raw) {
|
|
|
1103
1147
|
balance: p.balance
|
|
1104
1148
|
};
|
|
1105
1149
|
}
|
|
1150
|
+
function mapWireWalletBalance(raw) {
|
|
1151
|
+
const p = raw;
|
|
1152
|
+
return {
|
|
1153
|
+
id: p.id,
|
|
1154
|
+
balance: p.balance,
|
|
1155
|
+
reservedAmount: p.reserved_amount,
|
|
1156
|
+
currency: p.currency,
|
|
1157
|
+
reason: p.reason ?? ""
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1106
1160
|
function mapWireConfig(raw) {
|
|
1107
1161
|
const p = raw;
|
|
1108
1162
|
return {
|
|
@@ -1140,9 +1194,11 @@ function mapWireToEvent(topic, payload) {
|
|
|
1140
1194
|
function normaliseUserId2(userId) {
|
|
1141
1195
|
return userId && userId !== "" ? userId : null;
|
|
1142
1196
|
}
|
|
1143
|
-
var RealtimeModule = class extends
|
|
1197
|
+
var RealtimeModule = class extends EventEmitter3 {
|
|
1144
1198
|
#transport;
|
|
1145
1199
|
#entries = /* @__PURE__ */ new Map();
|
|
1200
|
+
#walletEntries = /* @__PURE__ */ new Map();
|
|
1201
|
+
// keyed by userId
|
|
1146
1202
|
#agencyId;
|
|
1147
1203
|
constructor(mqttEndpointOrOptions) {
|
|
1148
1204
|
super();
|
|
@@ -1222,6 +1278,56 @@ var RealtimeModule = class extends EventEmitter2 {
|
|
|
1222
1278
|
target.channel.removeAllListeners();
|
|
1223
1279
|
this.#transport.unsubscribeAll(gameId, target.userId);
|
|
1224
1280
|
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Subscribe to the user-scoped wallet balance stream for `userId`, topic
|
|
1283
|
+
* `wallet/users/{userId}/balance`. Returns a `WalletChannel` that emits
|
|
1284
|
+
* `walletBalanceUpdate` on every balance change (deposit / transfer / refund /
|
|
1285
|
+
* reserve / commit / cancel / bid settlement) — independent of any game.
|
|
1286
|
+
*
|
|
1287
|
+
* Reference-counted by `userId`, mirroring `subscribe`/`unsubscribe`: repeat
|
|
1288
|
+
* calls return the same channel and the topic is torn down only when balanced
|
|
1289
|
+
* `unsubscribeWallet` calls bring the count to zero. The wallet lifecycle is
|
|
1290
|
+
* fully independent of game subscriptions.
|
|
1291
|
+
*/
|
|
1292
|
+
subscribeWallet(userId) {
|
|
1293
|
+
const cleanUserId = normaliseUserId2(userId);
|
|
1294
|
+
if (!cleanUserId) {
|
|
1295
|
+
throw new TaphubError("userId is required to subscribe to wallet balance", {
|
|
1296
|
+
code: "UserIdRequired"
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
const existing = this.#walletEntries.get(cleanUserId);
|
|
1300
|
+
if (existing) {
|
|
1301
|
+
existing.refcount += 1;
|
|
1302
|
+
return existing.channel;
|
|
1303
|
+
}
|
|
1304
|
+
const channel = new WalletChannel(cleanUserId);
|
|
1305
|
+
const onMessage = (_topic, payload) => {
|
|
1306
|
+
channel.emit("walletBalanceUpdate", mapWireWalletBalance(payload));
|
|
1307
|
+
};
|
|
1308
|
+
const onError = (err) => {
|
|
1309
|
+
channel.emit("error", err);
|
|
1310
|
+
};
|
|
1311
|
+
this.#walletEntries.set(cleanUserId, { userId: cleanUserId, channel, refcount: 1 });
|
|
1312
|
+
this.#transport.subscribeWallet(cleanUserId, onMessage, onError);
|
|
1313
|
+
return channel;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Decrement the wallet subscription refcount for `userId`. Tears down the MQTT
|
|
1317
|
+
* topic and removes the `WalletChannel` only when the count reaches zero.
|
|
1318
|
+
* No-op if there is no active wallet subscription for `userId`.
|
|
1319
|
+
*/
|
|
1320
|
+
unsubscribeWallet(userId) {
|
|
1321
|
+
const cleanUserId = normaliseUserId2(userId);
|
|
1322
|
+
if (!cleanUserId) return;
|
|
1323
|
+
const entry = this.#walletEntries.get(cleanUserId);
|
|
1324
|
+
if (!entry) return;
|
|
1325
|
+
entry.refcount -= 1;
|
|
1326
|
+
if (entry.refcount > 0) return;
|
|
1327
|
+
this.#walletEntries.delete(cleanUserId);
|
|
1328
|
+
entry.channel.removeAllListeners();
|
|
1329
|
+
this.#transport.unsubscribeWallet(cleanUserId);
|
|
1330
|
+
}
|
|
1225
1331
|
/**
|
|
1226
1332
|
* Subscribe to public market candle data for a pair (e.g. "ETH/USD").
|
|
1227
1333
|
* Independent of game/agency — candle is market-wide public data.
|
|
@@ -1265,6 +1371,10 @@ var RealtimeModule = class extends EventEmitter2 {
|
|
|
1265
1371
|
entry.channel.removeAllListeners();
|
|
1266
1372
|
}
|
|
1267
1373
|
this.#entries.clear();
|
|
1374
|
+
for (const entry of this.#walletEntries.values()) {
|
|
1375
|
+
entry.channel.removeAllListeners();
|
|
1376
|
+
}
|
|
1377
|
+
this.#walletEntries.clear();
|
|
1268
1378
|
this.#transport.close();
|
|
1269
1379
|
}
|
|
1270
1380
|
};
|