@explorins/pers-sdk-react-native 2.1.7 → 2.1.8

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.
@@ -29,7 +29,11 @@ export interface UseTokenBalancesOptions {
29
29
  availableTokens?: TokenDTO[];
30
30
  /** Whether to automatically load balances on mount/auth/token changes */
31
31
  autoLoad?: boolean;
32
- /** Optional refresh interval in milliseconds (0 = disabled) */
32
+ /**
33
+ * Fallback polling interval in milliseconds (0 = disabled)
34
+ * @deprecated Use wallet events instead (refreshOnWalletEvents). Polling kept as fallback only.
35
+ * @default 0
36
+ */
33
37
  refreshInterval?: number;
34
38
  /**
35
39
  * Auto-refresh balances when wallet events are received (Transfer, Approval, etc.)
@@ -120,18 +124,7 @@ export interface UseTokenBalancesResult {
120
124
  * ```
121
125
  *
122
126
  * @example
123
- * **With Auto-Refresh:**
124
- * ```typescript
125
- * const { tokenBalances, isLoading } = useTokenBalances({
126
- * accountAddress: walletAddress!,
127
- * availableTokens,
128
- * autoLoad: true,
129
- * refreshInterval: 30000 // Refresh every 30 seconds
130
- * });
131
- * ```
132
- *
133
- * @example
134
- * **With Wallet Events (Real-time):**
127
+ * **With Wallet Events (Real-time):
135
128
  * ```typescript
136
129
  * // Auto-refresh on Transfer, Approval, and other blockchain events
137
130
  * const { tokenBalances } = useTokenBalances({
@@ -1 +1 @@
1
- {"version":3,"file":"useTokenBalances.d.ts","sourceRoot":"","sources":["../../src/hooks/useTokenBalances.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,0BAA0B,CAAC;AAElF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,YAAY,EAAE,YAAY,CAAC;IAC3B,8DAA8D;IAC9D,KAAK,EAAE,QAAQ,CAAC;IAChB,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,0DAA0D;IAC1D,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,uCAAuC;IACvC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,sFAAsF;IACtF,WAAW,EAAE,OAAO,CAAC;IACrB,0CAA0C;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CAgMzF"}
1
+ {"version":3,"file":"useTokenBalances.d.ts","sourceRoot":"","sources":["../../src/hooks/useTokenBalances.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,0BAA0B,CAAC;AAElF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,YAAY,EAAE,YAAY,CAAC;IAC3B,8DAA8D;IAC9D,KAAK,EAAE,QAAQ,CAAC;IAChB,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,0DAA0D;IAC1D,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,uCAAuC;IACvC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,sFAAsF;IACtF,WAAW,EAAE,OAAO,CAAC;IACrB,0CAA0C;IAC1C,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuFG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CAqLzF"}
@@ -61,18 +61,7 @@ import { NativeTokenTypes } from '@explorins/pers-shared';
61
61
  * ```
62
62
  *
63
63
  * @example
64
- * **With Auto-Refresh:**
65
- * ```typescript
66
- * const { tokenBalances, isLoading } = useTokenBalances({
67
- * accountAddress: walletAddress!,
68
- * availableTokens,
69
- * autoLoad: true,
70
- * refreshInterval: 30000 // Refresh every 30 seconds
71
- * });
72
- * ```
73
- *
74
- * @example
75
- * **With Wallet Events (Real-time):**
64
+ * **With Wallet Events (Real-time):
76
65
  * ```typescript
77
66
  * // Auto-refresh on Transfer, Approval, and other blockchain events
78
67
  * const { tokenBalances } = useTokenBalances({
@@ -207,28 +196,18 @@ export function useTokenBalances(options) {
207
196
  loadBalances();
208
197
  }
209
198
  }, [autoLoad, isAvailable, availableTokens.length, loadBalances]);
210
- // Event-driven refresh: listen for transaction events instead of polling
199
+ // Deprecated: Fallback polling interval (prefer wallet events)
200
+ // Kept for backward compatibility and edge cases where events might not work
211
201
  useEffect(() => {
212
- if (!sdk || refreshInterval <= 0 || !isAvailable)
202
+ if (refreshInterval <= 0 || !isAvailable)
213
203
  return;
214
- // Subscribe to transaction domain events
215
- const unsubscribe = sdk.events.subscribe((event) => {
216
- if (event.domain === 'transaction' && event.type === 'transaction_completed') {
217
- console.log('[useTokenBalances] Transaction completed, refreshing balances...');
218
- loadBalances();
219
- }
220
- }, { domains: ['transaction'] });
221
- // Also set up a fallback polling interval (much longer than before)
222
- // This handles cases where events might be missed
223
- const fallbackInterval = Math.max(refreshInterval, 60000); // Minimum 1 minute
224
204
  const intervalId = setInterval(() => {
225
205
  loadBalances();
226
- }, fallbackInterval);
206
+ }, refreshInterval);
227
207
  return () => {
228
- unsubscribe();
229
208
  clearInterval(intervalId);
230
209
  };
231
- }, [sdk, refreshInterval, isAvailable, loadBalances]);
210
+ }, [refreshInterval, isAvailable, loadBalances]);
232
211
  // Wallet events refresh: listen for real-time blockchain events
233
212
  // Refreshes on ANY wallet domain event (Transfer, TransferSingle, etc.)
234
213
  // Debouncing prevents excessive API calls during rapid events
package/dist/index.js CHANGED
@@ -9602,6 +9602,12 @@ class PersApiClient {
9602
9602
  try {
9603
9603
  const refreshSuccessful = await this.refreshManager.attemptInternalRefresh();
9604
9604
  if (refreshSuccessful) {
9605
+ // Emit TOKEN_REFRESHED event so WS clients can retry if they gave up
9606
+ this._events?.emitSuccess({
9607
+ type: 'token_refreshed',
9608
+ domain: 'authentication',
9609
+ userMessage: 'Authentication token refreshed',
9610
+ });
9605
9611
  return await this.request(method, endpoint, body, { ...options, retryCount: 1 });
9606
9612
  }
9607
9613
  }
@@ -9886,7 +9892,7 @@ class PersEventEmitter {
9886
9892
  * ```typescript
9887
9893
  * sdk.events.emitSuccess({
9888
9894
  * domain: 'transaction', // Only business domains allowed
9889
- * type: 'CONFIRMED',
9895
+ * type: 'transaction_confirmed',
9890
9896
  * userMessage: 'Transaction confirmed!'
9891
9897
  * });
9892
9898
  * ```
@@ -9913,7 +9919,7 @@ class PersEventEmitter {
9913
9919
  * ```typescript
9914
9920
  * sdk.events.emitError({
9915
9921
  * domain: 'validation', // Technical domains allowed
9916
- * type: 'INVALID_INPUT',
9922
+ * type: 'validation_failed',
9917
9923
  * userMessage: 'Please check your input'
9918
9924
  * });
9919
9925
  * ```
@@ -10047,7 +10053,7 @@ class AuthManager {
10047
10053
  : await authService.loginUser(jwtToken);
10048
10054
  this.events?.emitSuccess({
10049
10055
  domain: 'authentication',
10050
- type: 'LOGIN_SUCCESS',
10056
+ type: 'login_success',
10051
10057
  userMessage: 'Successfully logged in'
10052
10058
  });
10053
10059
  return result;
@@ -10420,7 +10426,7 @@ class UserManager {
10420
10426
  const result = await this.userService.updateRemoteUser(userData);
10421
10427
  this.events?.emitSuccess({
10422
10428
  domain: 'user',
10423
- type: 'PROFILE_UPDATED',
10429
+ type: 'profile_updated',
10424
10430
  userMessage: 'Profile updated successfully',
10425
10431
  details: { userId: result.id }
10426
10432
  });
@@ -10700,7 +10706,7 @@ class UserManager {
10700
10706
  const result = await this.userService.deleteUser(identifier);
10701
10707
  this.events?.emitSuccess({
10702
10708
  domain: 'user',
10703
- type: 'USER_DELETED',
10709
+ type: 'user_deleted',
10704
10710
  userMessage: 'User deleted successfully',
10705
10711
  details: { identifier }
10706
10712
  });
@@ -10733,7 +10739,7 @@ class UserManager {
10733
10739
  const result = await this.userService.restoreUser(identifier);
10734
10740
  this.events?.emitSuccess({
10735
10741
  domain: 'user',
10736
- type: 'USER_RESTORED',
10742
+ type: 'user_restored',
10737
10743
  userMessage: 'User restored successfully',
10738
10744
  details: { identifier, userId: result.id }
10739
10745
  });
@@ -11423,7 +11429,7 @@ class BusinessManager {
11423
11429
  const result = await this.businessService.createBusinessByDisplayName(displayName);
11424
11430
  this.events?.emitSuccess({
11425
11431
  domain: 'business',
11426
- type: 'BUSINESS_CREATED',
11432
+ type: 'business_created',
11427
11433
  userMessage: 'Business created successfully',
11428
11434
  details: { businessId: result.id, displayName: result.displayName }
11429
11435
  });
@@ -11458,7 +11464,7 @@ class BusinessManager {
11458
11464
  const result = await this.businessService.updateBusiness(businessId, businessData);
11459
11465
  this.events?.emitSuccess({
11460
11466
  domain: 'business',
11461
- type: 'BUSINESS_UPDATED',
11467
+ type: 'business_updated',
11462
11468
  userMessage: 'Business updated successfully',
11463
11469
  details: { businessId: result.id, displayName: result.displayName }
11464
11470
  });
@@ -11630,7 +11636,7 @@ class BusinessManager {
11630
11636
  const result = await this.membershipService.updateMemberRole(businessId, userId, newRole);
11631
11637
  this.events?.emitSuccess({
11632
11638
  domain: 'business',
11633
- type: 'MEMBERSHIP_UPDATED',
11639
+ type: 'membership_updated',
11634
11640
  userMessage: 'Membership role updated',
11635
11641
  details: { businessId, userId, role: newRole }
11636
11642
  });
@@ -11908,7 +11914,7 @@ class CampaignManager {
11908
11914
  const result = await this.campaignService.claimCampaign(claimRequest);
11909
11915
  this.events?.emitSuccess({
11910
11916
  domain: 'campaign',
11911
- type: 'CLAIM_SUCCESS',
11917
+ type: 'claim_success',
11912
11918
  userMessage: 'Campaign reward claimed successfully',
11913
11919
  details: { campaignId: claimRequest.campaignId }
11914
11920
  });
@@ -12176,7 +12182,7 @@ class CampaignManager {
12176
12182
  const result = await this.campaignService.createCampaignTrigger(data);
12177
12183
  this.events?.emitSuccess({
12178
12184
  domain: 'campaign',
12179
- type: 'CAMPAIGN_TRIGGER_CREATED',
12185
+ type: 'campaign_trigger_created',
12180
12186
  userMessage: `Trigger "${data.name}" created successfully`,
12181
12187
  details: { triggerId: result.id, name: data.name }
12182
12188
  });
@@ -12201,7 +12207,7 @@ class CampaignManager {
12201
12207
  const result = await this.campaignService.updateCampaignTrigger(triggerId, data);
12202
12208
  this.events?.emitSuccess({
12203
12209
  domain: 'campaign',
12204
- type: 'CAMPAIGN_TRIGGER_UPDATED',
12210
+ type: 'campaign_trigger_updated',
12205
12211
  userMessage: 'Trigger updated successfully',
12206
12212
  details: { triggerId, updates: Object.keys(data) }
12207
12213
  });
@@ -12222,7 +12228,7 @@ class CampaignManager {
12222
12228
  const result = await this.campaignService.deleteCampaignTrigger(triggerId);
12223
12229
  this.events?.emitSuccess({
12224
12230
  domain: 'campaign',
12225
- type: 'CAMPAIGN_TRIGGER_DELETED',
12231
+ type: 'campaign_trigger_deleted',
12226
12232
  userMessage: 'Trigger deleted successfully',
12227
12233
  details: { triggerId }
12228
12234
  });
@@ -12251,7 +12257,7 @@ class CampaignManager {
12251
12257
  const result = await this.campaignService.setCampaignTrigger(campaignId, triggerId);
12252
12258
  this.events?.emitSuccess({
12253
12259
  domain: 'campaign',
12254
- type: 'CAMPAIGN_TRIGGER_ASSIGNED',
12260
+ type: 'campaign_trigger_assigned',
12255
12261
  userMessage: 'Trigger assigned to campaign',
12256
12262
  details: { campaignId, triggerId }
12257
12263
  });
@@ -12280,7 +12286,7 @@ class CampaignManager {
12280
12286
  const result = await this.campaignService.removeCampaignTrigger(campaignId, triggerId);
12281
12287
  this.events?.emitSuccess({
12282
12288
  domain: 'campaign',
12283
- type: 'CAMPAIGN_TRIGGER_REMOVED',
12289
+ type: 'campaign_trigger_removed',
12284
12290
  userMessage: 'Trigger removed from campaign',
12285
12291
  details: { campaignId, triggerId }
12286
12292
  });
@@ -12616,7 +12622,7 @@ class CampaignManager {
12616
12622
  const result = await this.campaignService.assignTriggerSourceToCampaign(campaignId, triggerSourceId);
12617
12623
  this.events?.emitSuccess({
12618
12624
  domain: 'campaign',
12619
- type: 'TRIGGER_SOURCE_ASSIGNED',
12625
+ type: 'trigger_source_assigned',
12620
12626
  userMessage: 'Trigger source assigned to campaign',
12621
12627
  details: { campaignId, triggerSourceId }
12622
12628
  });
@@ -12964,7 +12970,7 @@ class RedemptionManager {
12964
12970
  const result = await this.redemptionService.redeemRedemption(redemptionId);
12965
12971
  this.events?.emitSuccess({
12966
12972
  domain: 'redemption',
12967
- type: 'REDEEM_SUCCESS',
12973
+ type: 'redeem_success',
12968
12974
  userMessage: 'Reward redeemed successfully',
12969
12975
  details: { redemptionId }
12970
12976
  });
@@ -13482,7 +13488,7 @@ class TransactionManager {
13482
13488
  const result = await this.transactionService.createTransaction(transactionData);
13483
13489
  this.events?.emitSuccess({
13484
13490
  domain: 'transaction',
13485
- type: 'TRANSACTION_CREATED',
13491
+ type: 'transaction_created',
13486
13492
  userMessage: 'Transaction created successfully',
13487
13493
  details: { transactionId: result.transaction?.id }
13488
13494
  });
@@ -13721,7 +13727,7 @@ class TransactionManager {
13721
13727
  const result = await this.transactionService.submitSignedTransaction(signedTxData);
13722
13728
  this.events?.emitSuccess({
13723
13729
  domain: 'transaction',
13724
- type: 'TRANSACTION_SUBMITTED',
13730
+ type: 'transaction_submitted',
13725
13731
  userMessage: 'Transaction submitted successfully',
13726
13732
  details: { transactionId: result.transaction?.id }
13727
13733
  });
@@ -15645,7 +15651,7 @@ class TriggerSourceManager {
15645
15651
  const result = await this.triggerSourceService.createTriggerSource(triggerSource);
15646
15652
  this.events?.emitSuccess({
15647
15653
  domain: 'trigger-source',
15648
- type: 'TRIGGER_SOURCE_CREATED',
15654
+ type: 'trigger_source_created',
15649
15655
  userMessage: 'Trigger source created successfully',
15650
15656
  details: { triggerSourceId: result.id, type: result.type }
15651
15657
  });
@@ -15677,7 +15683,7 @@ class TriggerSourceManager {
15677
15683
  const result = await this.triggerSourceService.updateTriggerSource(triggerSourceId, triggerSource);
15678
15684
  this.events?.emitSuccess({
15679
15685
  domain: 'trigger-source',
15680
- type: 'TRIGGER_SOURCE_UPDATED',
15686
+ type: 'trigger_source_updated',
15681
15687
  userMessage: 'Trigger source updated successfully',
15682
15688
  details: { triggerSourceId }
15683
15689
  });
@@ -15704,7 +15710,7 @@ class TriggerSourceManager {
15704
15710
  const result = await this.triggerSourceService.deleteTriggerSource(triggerSourceId);
15705
15711
  this.events?.emitSuccess({
15706
15712
  domain: 'trigger-source',
15707
- type: 'TRIGGER_SOURCE_DELETED',
15713
+ type: 'trigger_source_deleted',
15708
15714
  userMessage: 'Trigger source deleted successfully',
15709
15715
  details: { triggerSourceId }
15710
15716
  });
@@ -16178,7 +16184,7 @@ class WebhookManager {
16178
16184
  const result = await this.webhookService.createWebhook(webhook);
16179
16185
  this.events?.emitSuccess({
16180
16186
  domain: 'webhook',
16181
- type: 'WEBHOOK_CREATED',
16187
+ type: 'webhook_created',
16182
16188
  userMessage: 'Webhook created successfully',
16183
16189
  details: { webhookId: result.id, name: result.name }
16184
16190
  });
@@ -16195,7 +16201,7 @@ class WebhookManager {
16195
16201
  const result = await this.webhookService.updateWebhook(webhookId, webhook);
16196
16202
  this.events?.emitSuccess({
16197
16203
  domain: 'webhook',
16198
- type: 'WEBHOOK_UPDATED',
16204
+ type: 'webhook_updated',
16199
16205
  userMessage: 'Webhook updated successfully',
16200
16206
  details: { webhookId }
16201
16207
  });
@@ -16223,7 +16229,7 @@ class WebhookManager {
16223
16229
  const result = await this.webhookService.deleteWebhook(webhookId);
16224
16230
  this.events?.emitSuccess({
16225
16231
  domain: 'webhook',
16226
- type: 'WEBHOOK_DELETED',
16232
+ type: 'webhook_deleted',
16227
16233
  userMessage: 'Webhook deleted successfully',
16228
16234
  details: { webhookId }
16229
16235
  });
@@ -16280,7 +16286,7 @@ class WebhookManager {
16280
16286
  const result = await this.webhookService.post(hookId, body);
16281
16287
  this.events?.emitSuccess({
16282
16288
  domain: 'webhook',
16283
- type: 'WEBHOOK_TRIGGERED',
16289
+ type: 'webhook_triggered',
16284
16290
  userMessage: 'Webhook triggered',
16285
16291
  details: { hookId, executionId: result.executionId }
16286
16292
  });
@@ -16844,12 +16850,22 @@ class WalletEventsManager {
16844
16850
  this.client = null;
16845
16851
  this.pendingHandlers = [];
16846
16852
  this.unsubscribes = [];
16853
+ // Track if WS gave up reconnecting (so we can retry on token refresh)
16854
+ this.reconnectGaveUp = false;
16855
+ this.lastSubscriptions = { wallets: [], chains: [] };
16847
16856
  this.config = {
16848
16857
  autoReconnect: true,
16849
16858
  connectionTimeout: 30000,
16850
16859
  debug: false,
16851
16860
  ...config,
16852
16861
  };
16862
+ // Subscribe to auth events to retry WS connection when tokens are refreshed
16863
+ this.authEventUnsubscribe = this.eventEmitter.subscribe((event) => {
16864
+ if (event.type === 'token_refreshed' && this.reconnectGaveUp) {
16865
+ // Tokens were refreshed and WS had given up - retry connection
16866
+ this.retryAfterTokenRefresh();
16867
+ }
16868
+ }, { domains: ['authentication'], levels: ['success'] });
16853
16869
  }
16854
16870
  /**
16855
16871
  * Connect to real-time wallet events
@@ -16878,8 +16894,11 @@ class WalletEventsManager {
16878
16894
  const wsUrl = this.config.wsUrl
16879
16895
  || sdkConfig.walletEventsWsUrl
16880
16896
  || buildWalletEventsWsUrl(sdkConfig.environment);
16881
- // Create token refresher that fetches fresh token from auth provider
16897
+ // Create token refresher that ensures valid token before reconnecting
16898
+ // This triggers actual token refresh if expired, not just retrieval
16882
16899
  const tokenRefresher = async () => {
16900
+ // Ensure token is refreshed if expired (calls refresh API if needed)
16901
+ await this.apiClient.ensureValidToken();
16883
16902
  const freshToken = await authProvider.getToken();
16884
16903
  if (!freshToken) {
16885
16904
  throw new Error('Failed to refresh token');
@@ -16894,6 +16913,32 @@ class WalletEventsManager {
16894
16913
  tokenRefresher,
16895
16914
  });
16896
16915
  await this.client.connect(token);
16916
+ // Reset gave-up flag on successful connection
16917
+ this.reconnectGaveUp = false;
16918
+ // Track previous state to detect reconnections and give-ups
16919
+ let previousState = 'connected';
16920
+ // Clean up previous state change listener (prevent duplicate listeners)
16921
+ this.stateChangeUnsubscribe?.();
16922
+ // Listen for state changes
16923
+ this.stateChangeUnsubscribe = this.client.onStateChange((state) => {
16924
+ if (state === 'disconnected' && previousState === 'reconnecting') {
16925
+ // Transitioned from reconnecting to disconnected = exhausted all attempts
16926
+ this.reconnectGaveUp = true;
16927
+ }
16928
+ else if (state === 'connected') {
16929
+ this.reconnectGaveUp = false;
16930
+ // Emit reconnected event so apps can refresh state (catch missed events)
16931
+ if (previousState === 'reconnecting') {
16932
+ this.eventEmitter.emitSuccess({
16933
+ type: 'wallet_reconnected',
16934
+ domain: 'wallet',
16935
+ userMessage: 'Wallet events reconnected',
16936
+ details: { previousState }
16937
+ });
16938
+ }
16939
+ }
16940
+ previousState = state;
16941
+ });
16897
16942
  // Clear previous unsubscribes on new connection (prevent memory leak)
16898
16943
  this.unsubscribes.forEach(unsub => unsub());
16899
16944
  this.unsubscribes = [];
@@ -16910,11 +16955,26 @@ class WalletEventsManager {
16910
16955
  * Disconnect from real-time events
16911
16956
  */
16912
16957
  disconnect() {
16958
+ // Clear gave-up flag to prevent auto-reconnect after intentional disconnect
16959
+ this.reconnectGaveUp = false;
16960
+ this.stateChangeUnsubscribe?.();
16961
+ this.stateChangeUnsubscribe = undefined;
16913
16962
  this.unsubscribes.forEach(unsub => unsub());
16914
16963
  this.unsubscribes = [];
16915
16964
  this.client?.disconnect();
16916
16965
  this.client = null;
16917
16966
  }
16967
+ /**
16968
+ * Full cleanup - disconnect and remove all subscriptions including auth listener
16969
+ * Call this when completely done with the manager (e.g., SDK disposal)
16970
+ */
16971
+ destroy() {
16972
+ this.disconnect();
16973
+ this.authEventUnsubscribe?.();
16974
+ this.authEventUnsubscribe = undefined;
16975
+ this.reconnectGaveUp = false;
16976
+ this.lastSubscriptions = { wallets: [], chains: [] };
16977
+ }
16918
16978
  // ─────────────────────────────────────────────────────────────────────────────
16919
16979
  // Subscription Methods (v1.2.0)
16920
16980
  // ─────────────────────────────────────────────────────────────────────────────
@@ -16939,6 +16999,8 @@ class WalletEventsManager {
16939
16999
  if (!this.client) {
16940
17000
  throw new Error('Not connected. Call connect() first.');
16941
17001
  }
17002
+ // Save subscriptions for auto-resubscribe on reconnect
17003
+ this.lastSubscriptions.wallets = wallets;
16942
17004
  return this.client.subscribeWallets(wallets);
16943
17005
  }
16944
17006
  /**
@@ -16960,6 +17022,8 @@ class WalletEventsManager {
16960
17022
  if (!this.client) {
16961
17023
  throw new Error('Not connected. Call connect() first.');
16962
17024
  }
17025
+ // Save subscriptions for auto-resubscribe on reconnect
17026
+ this.lastSubscriptions.chains = chains;
16963
17027
  return this.client.subscribeChains(chains);
16964
17028
  }
16965
17029
  /**
@@ -17049,6 +17113,36 @@ class WalletEventsManager {
17049
17113
  isConnected() {
17050
17114
  return this.getState() === 'connected';
17051
17115
  }
17116
+ /**
17117
+ * Check if WS reconnection gave up (exhausted all attempts)
17118
+ * Used to determine if we should retry on token refresh
17119
+ */
17120
+ hasReconnectGivenUp() {
17121
+ return this.reconnectGaveUp;
17122
+ }
17123
+ /**
17124
+ * Retry connection after token refresh
17125
+ * Called when auth tokens are refreshed and WS had previously given up
17126
+ * Automatically resubscribes to previous subscriptions
17127
+ */
17128
+ async retryAfterTokenRefresh() {
17129
+ if (!this.reconnectGaveUp) {
17130
+ return; // Not in gave-up state, nothing to retry
17131
+ }
17132
+ try {
17133
+ await this.connect();
17134
+ // Restore previous subscriptions
17135
+ if (this.lastSubscriptions.wallets.length > 0) {
17136
+ await this.subscribeWallets(this.lastSubscriptions.wallets);
17137
+ }
17138
+ if (this.lastSubscriptions.chains.length > 0) {
17139
+ await this.subscribeChains(this.lastSubscriptions.chains);
17140
+ }
17141
+ }
17142
+ catch (error) {
17143
+ console.warn('[WalletEventsManager] Retry after token refresh failed:', error);
17144
+ }
17145
+ }
17052
17146
  /**
17053
17147
  * Get connection info (wallets, active chains)
17054
17148
  */
@@ -17253,7 +17347,7 @@ class PersSDK {
17253
17347
  }
17254
17348
  // Listen for login success events (both fresh login and session restoration)
17255
17349
  this._events.subscribe((event) => {
17256
- if (event.level === 'success' && (event.type === 'LOGIN_SUCCESS' || event.type === 'session_restored')) {
17350
+ if (event.level === 'success' && (event.type === 'login_success' || event.type === 'session_restored')) {
17257
17351
  // Auto-connect and subscribe to wallet events (fire and forget, log errors)
17258
17352
  this.connectWalletEvents().catch((err) => {
17259
17353
  console.warn('[PersSDK] Failed to auto-connect wallet events:', err.message);
@@ -39975,18 +40069,7 @@ const useWeb3 = () => {
39975
40069
  * ```
39976
40070
  *
39977
40071
  * @example
39978
- * **With Auto-Refresh:**
39979
- * ```typescript
39980
- * const { tokenBalances, isLoading } = useTokenBalances({
39981
- * accountAddress: walletAddress!,
39982
- * availableTokens,
39983
- * autoLoad: true,
39984
- * refreshInterval: 30000 // Refresh every 30 seconds
39985
- * });
39986
- * ```
39987
- *
39988
- * @example
39989
- * **With Wallet Events (Real-time):**
40072
+ * **With Wallet Events (Real-time):
39990
40073
  * ```typescript
39991
40074
  * // Auto-refresh on Transfer, Approval, and other blockchain events
39992
40075
  * const { tokenBalances } = useTokenBalances({
@@ -40121,28 +40204,18 @@ function useTokenBalances(options) {
40121
40204
  loadBalances();
40122
40205
  }
40123
40206
  }, [autoLoad, isAvailable, availableTokens.length, loadBalances]);
40124
- // Event-driven refresh: listen for transaction events instead of polling
40207
+ // Deprecated: Fallback polling interval (prefer wallet events)
40208
+ // Kept for backward compatibility and edge cases where events might not work
40125
40209
  react.useEffect(() => {
40126
- if (!sdk || refreshInterval <= 0 || !isAvailable)
40210
+ if (refreshInterval <= 0 || !isAvailable)
40127
40211
  return;
40128
- // Subscribe to transaction domain events
40129
- const unsubscribe = sdk.events.subscribe((event) => {
40130
- if (event.domain === 'transaction' && event.type === 'transaction_completed') {
40131
- console.log('[useTokenBalances] Transaction completed, refreshing balances...');
40132
- loadBalances();
40133
- }
40134
- }, { domains: ['transaction'] });
40135
- // Also set up a fallback polling interval (much longer than before)
40136
- // This handles cases where events might be missed
40137
- const fallbackInterval = Math.max(refreshInterval, 60000); // Minimum 1 minute
40138
40212
  const intervalId = setInterval(() => {
40139
40213
  loadBalances();
40140
- }, fallbackInterval);
40214
+ }, refreshInterval);
40141
40215
  return () => {
40142
- unsubscribe();
40143
40216
  clearInterval(intervalId);
40144
40217
  };
40145
- }, [sdk, refreshInterval, isAvailable, loadBalances]);
40218
+ }, [refreshInterval, isAvailable, loadBalances]);
40146
40219
  // Wallet events refresh: listen for real-time blockchain events
40147
40220
  // Refreshes on ANY wallet domain event (Transfer, TransferSingle, etc.)
40148
40221
  // Debouncing prevents excessive API calls during rapid events