@explorins/pers-sdk-react-native 2.2.0-beta.1 → 2.2.1

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.
@@ -10,9 +10,42 @@ import type { TokenBalance, TokenBalanceRequest } from '@explorins/pers-sdk/web3
10
10
  * This composite type combines blockchain balance data with token metadata,
11
11
  * making it easy to display token information in UI components without
12
12
  * needing to join data from multiple sources.
13
+ *
14
+ * ## CRITICAL: Token ID for Transactions
15
+ *
16
+ * For ERC721 (stamps) and ERC1155 (rewards), use `tokenBalance.tokenId` as the
17
+ * `contractTokenId` when creating transactions. This is the **on-chain token ID**
18
+ * assigned by the blockchain, NOT the metadata definition ID.
19
+ *
20
+ * | Token Type | tokenId meaning | Example |
21
+ * |------------|-----------------|---------|
22
+ * | ERC20 | `null` or `""` | N/A (fungible points) |
23
+ * | ERC721 | NFT instance ID | `"46"` (on-chain ID) |
24
+ * | ERC1155 | Token type ID | `"16"` (semi-fungible type) |
25
+ *
26
+ * @example Correct usage for token burn transaction
27
+ * ```typescript
28
+ * const stampBalance = tokenBalances.find(b => b.token.type === 'ERC721');
29
+ *
30
+ * const request = buildPOSBurnRequest({
31
+ * amount: 1,
32
+ * contractAddress: stampBalance.token.contractAddress,
33
+ * contractTokenId: stampBalance.tokenBalance.tokenId, // ✅ Correct
34
+ * chainId: stampBalance.token.chainId,
35
+ * userId: userId,
36
+ * businessId: businessId
37
+ * });
38
+ * ```
39
+ *
40
+ * @see {@link https://docs.explorins.com/pers-sdk/troubleshooting#stamp-voucher-transaction-fails | Troubleshooting: Stamp Transaction Fails}
13
41
  */
14
42
  export interface TokenBalanceWithToken {
15
- /** The balance data from blockchain */
43
+ /**
44
+ * The balance data from blockchain
45
+ *
46
+ * **Important:** For NFT operations, use `tokenBalance.tokenId` as the `contractTokenId`
47
+ * in transaction requests. This is the actual on-chain ID, not the metadata definition.
48
+ */
16
49
  tokenBalance: TokenBalance;
17
50
  /** The token definition (contract, symbol, metadata, etc.) */
18
51
  token: TokenDTO;
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { TokenDTO, PaginatedResponseDTO } from '@explorins/pers-sdk';
3
+ import type { TokenDTO, TokenMetadataDTO, PaginatedResponseDTO, TokenMetadataFilterOptions, RewardsFilterOptions, StampsFilterOptions } from '@explorins/pers-sdk';
4
4
 
5
5
  /**
6
6
  * React hook for token operations in the PERS SDK
@@ -201,6 +201,102 @@ export const useTokens = () => {
201
201
  }
202
202
  }, [sdk, isInitialized]);
203
203
 
204
+ /**
205
+ * Get all token metadata with filtering and pagination
206
+ *
207
+ * Retrieves token metadata (rewards/stamps) with server-side filtering.
208
+ * Use tokenType: 'ERC1155' for rewards, 'ERC721' for stamps.
209
+ *
210
+ * @param options - Filter and pagination options
211
+ * @returns Promise resolving to paginated token metadata
212
+ * @throws Error if SDK is not initialized
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * const { getTokenMetadata } = useTokens();
217
+ * const rewards = await getTokenMetadata({
218
+ * tokenType: 'ERC1155',
219
+ * active: true,
220
+ * limit: 10
221
+ * });
222
+ * ```
223
+ */
224
+ const getTokenMetadata = useCallback(async (options?: TokenMetadataFilterOptions): Promise<PaginatedResponseDTO<TokenMetadataDTO>> => {
225
+ if (!isInitialized || !sdk) {
226
+ throw new Error('SDK not initialized. Call initialize() first.');
227
+ }
228
+
229
+ try {
230
+ const result = await sdk.tokens.getTokenMetadata(options);
231
+ return result;
232
+ } catch (error) {
233
+ console.error('Failed to fetch token metadata:', error);
234
+ throw error;
235
+ }
236
+ }, [sdk, isInitialized]);
237
+
238
+ /**
239
+ * Get rewards (ERC1155 token metadata) with filtering and pagination
240
+ *
241
+ * Convenience method for fetching reward metadata. Rewards are ERC1155 tokens
242
+ * that represent collectible items, vouchers, or other redeemable rewards.
243
+ *
244
+ * @param options - Filter and pagination options
245
+ * @returns Promise resolving to paginated reward metadata
246
+ * @throws Error if SDK is not initialized
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * const { getRewards } = useTokens();
251
+ * const rewards = await getRewards({ active: true, limit: 10 });
252
+ * console.log(`Found ${rewards.pagination.total} rewards`);
253
+ * ```
254
+ */
255
+ const getRewards = useCallback(async (options?: RewardsFilterOptions): Promise<PaginatedResponseDTO<TokenMetadataDTO>> => {
256
+ if (!isInitialized || !sdk) {
257
+ throw new Error('SDK not initialized. Call initialize() first.');
258
+ }
259
+
260
+ try {
261
+ const result = await sdk.tokens.getRewards(options);
262
+ return result;
263
+ } catch (error) {
264
+ console.error('Failed to fetch rewards:', error);
265
+ throw error;
266
+ }
267
+ }, [sdk, isInitialized]);
268
+
269
+ /**
270
+ * Get stamps (ERC721 token metadata) with filtering and pagination
271
+ *
272
+ * Convenience method for fetching stamp metadata. Stamps are ERC721 tokens
273
+ * that represent achievements, badges, or collectible status markers.
274
+ *
275
+ * @param options - Filter and pagination options
276
+ * @returns Promise resolving to paginated stamp metadata
277
+ * @throws Error if SDK is not initialized
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * const { getStamps } = useTokens();
282
+ * const stamps = await getStamps({ active: true, limit: 10 });
283
+ * console.log(`Found ${stamps.pagination.total} stamps`);
284
+ * ```
285
+ */
286
+ const getStamps = useCallback(async (options?: StampsFilterOptions): Promise<PaginatedResponseDTO<TokenMetadataDTO>> => {
287
+ if (!isInitialized || !sdk) {
288
+ throw new Error('SDK not initialized. Call initialize() first.');
289
+ }
290
+
291
+ try {
292
+ const result = await sdk.tokens.getStamps(options);
293
+ return result;
294
+ } catch (error) {
295
+ console.error('Failed to fetch stamps:', error);
296
+ throw error;
297
+ }
298
+ }, [sdk, isInitialized]);
299
+
204
300
  return {
205
301
  getTokens,
206
302
  getActiveCreditToken,
@@ -208,6 +304,9 @@ export const useTokens = () => {
208
304
  getTokenTypes,
209
305
  getStatusTokens,
210
306
  getTokenByContract,
307
+ getTokenMetadata,
308
+ getRewards,
309
+ getStamps,
211
310
  isAvailable: isInitialized && !!sdk?.tokens,
212
311
  };
213
312
  };
@@ -27,11 +27,45 @@ export type { PersConfig } from '@explorins/pers-sdk/core';
27
27
  /**
28
28
  * Context interface for PERS SDK React Native integration
29
29
  *
30
+ * ## Session Restoration Behavior
31
+ *
32
+ * Session restoration is **asynchronous**. When the app starts:
33
+ * 1. `isInitialized` becomes `true` when SDK is ready
34
+ * 2. Session restoration happens automatically
35
+ * 3. `isAuthenticated` and `user` update when restoration completes
36
+ *
37
+ * **Always check `isInitialized` AND `isAuthenticated` before using `user`:**
38
+ *
39
+ * ```tsx
40
+ * const { user, isInitialized, isAuthenticated } = usePersSDK();
41
+ *
42
+ * if (!isInitialized) return <LoadingSpinner />;
43
+ * if (!isAuthenticated || !user) return <LoginScreen />;
44
+ * return <WalletContent user={user} />;
45
+ * ```
46
+ *
47
+ * **Why `user` might be null on app restart:**
48
+ * - Access tokens expire after 1 hour (auto-refreshed if app is active)
49
+ * - Refresh tokens expire after 30 days (user must re-login)
50
+ * - Session restoration is in progress
51
+ *
52
+ * Listen for auth events to debug:
53
+ * ```typescript
54
+ * sdk.events.subscribe((event) => {
55
+ * console.log('[Auth Event]', event.type, event.details);
56
+ * }, { domains: ['authentication'], replay: true });
57
+ * ```
58
+ *
59
+ * | Event | Meaning |
60
+ * |-------|---------|
61
+ * | `session_restored` | Tokens valid, user logged in |
62
+ * | `session_restoration_failed` | Tokens expired, needs re-login |
63
+ *
30
64
  * @property sdk - Main SDK instance (null until initialized)
31
65
  * @property authProvider - Platform-specific auth provider
32
- * @property isInitialized - Whether SDK has been initialized
66
+ * @property isInitialized - Whether SDK has been initialized (not whether user is authenticated!)
33
67
  * @property isAuthenticated - Whether user is currently authenticated
34
- * @property user - Current user data (null if not authenticated)
68
+ * @property user - Current user data (null if not authenticated OR restoration in progress)
35
69
  */
36
70
  export interface PersSDKContext {
37
71
  // Main SDK instance
@@ -168,7 +202,7 @@ export const PersSDKProvider: React.FC<{
168
202
  }
169
203
  }
170
204
 
171
- if (event.type === 'logout_success' || event.type === 'session_restoration_failed' || event.code === 'AUTH_FAILED') {
205
+ if (event.type === 'logout_success' || event.type === 'session_restoration_failed') {
172
206
  setUser(null);
173
207
  setIsAuthenticated(false);
174
208
  }