toss-expo-sdk 1.0.1 → 1.0.4

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.
Files changed (36) hide show
  1. package/README.md +68 -65
  2. package/lib/module/client/NonceAccountManager.js +2 -2
  3. package/lib/module/client/NonceAccountManager.js.map +1 -1
  4. package/lib/module/examples/enhancedFeaturesFlow.js +233 -0
  5. package/lib/module/examples/enhancedFeaturesFlow.js.map +1 -0
  6. package/lib/module/examples/offlinePaymentFlow.js +27 -27
  7. package/lib/module/examples/offlinePaymentFlow.js.map +1 -1
  8. package/lib/module/index.js +13 -1
  9. package/lib/module/index.js.map +1 -1
  10. package/lib/module/qr.js +2 -2
  11. package/lib/module/reconciliation.js +4 -4
  12. package/lib/module/reconciliation.js.map +1 -1
  13. package/lib/module/services/authService.js +3 -3
  14. package/lib/module/services/authService.js.map +1 -1
  15. package/lib/module/utils/compression.js +210 -0
  16. package/lib/module/utils/compression.js.map +1 -0
  17. package/lib/module/wifi.js +311 -0
  18. package/lib/module/wifi.js.map +1 -0
  19. package/lib/typescript/src/examples/enhancedFeaturesFlow.d.ts +45 -0
  20. package/lib/typescript/src/examples/enhancedFeaturesFlow.d.ts.map +1 -0
  21. package/lib/typescript/src/index.d.ts +7 -1
  22. package/lib/typescript/src/index.d.ts.map +1 -1
  23. package/lib/typescript/src/utils/compression.d.ts +52 -0
  24. package/lib/typescript/src/utils/compression.d.ts.map +1 -0
  25. package/lib/typescript/src/wifi.d.ts +116 -0
  26. package/lib/typescript/src/wifi.d.ts.map +1 -0
  27. package/package.json +1 -1
  28. package/src/client/NonceAccountManager.ts +2 -2
  29. package/src/examples/enhancedFeaturesFlow.ts +272 -0
  30. package/src/examples/offlinePaymentFlow.ts +27 -27
  31. package/src/index.tsx +26 -1
  32. package/src/qr.tsx +2 -2
  33. package/src/reconciliation.ts +4 -4
  34. package/src/services/authService.ts +3 -3
  35. package/src/utils/compression.ts +247 -0
  36. package/src/wifi.ts +401 -0
package/src/index.tsx CHANGED
@@ -45,6 +45,7 @@ export {
45
45
  processIntentsForSync,
46
46
  filterExpiredIntents,
47
47
  } from './intentManager';
48
+ export { verifyIntent } from './intent';
48
49
 
49
50
  // Storage
50
51
  export {
@@ -52,6 +53,15 @@ export {
52
53
  getPendingIntents,
53
54
  clearPendingIntents,
54
55
  } from './storage';
56
+ // Secure storage (hardware-backed)
57
+ export {
58
+ secureStoreIntent,
59
+ getSecureIntent,
60
+ getAllSecureIntents,
61
+ removeSecureIntent,
62
+ clearAllSecureIntents,
63
+ cleanupExpiredIntents,
64
+ } from './storage/secureStorage';
55
65
 
56
66
  // Transport methods (enhanced with fragmentation)
57
67
  export {
@@ -66,7 +76,10 @@ export { initNFC, readNFCUser, writeUserToNFC, writeIntentToNFC } from './nfc';
66
76
  export { QRScanner } from './qr';
67
77
 
68
78
  // Client
69
- export { TossClient, type TossConfig } from './client/TossClient';
79
+ import { TossClient, type TossConfig } from './client/TossClient';
80
+ export { TossClient, type TossConfig };
81
+ // Convenience helper for createClient (matches README)
82
+ export const createClient = TossClient.createClient;
70
83
  export type { TossUser } from './types/tossUser';
71
84
  export { WalletProvider, useWallet } from './contexts/WalletContext';
72
85
 
@@ -101,3 +114,15 @@ export {
101
114
  type IntentExchangeRequest,
102
115
  type IntentExchangeResponse,
103
116
  } from './discovery';
117
+
118
+ // Compression utilities
119
+ export {
120
+ compressMetadata,
121
+ decompressMetadata,
122
+ compressIntentMetadata,
123
+ decompressIntentMetadata,
124
+ estimateCompressionSavings,
125
+ } from './utils/compression';
126
+
127
+ // WiFi Direct transport
128
+ export { WiFiDirectTransport, SmartTransportSelector } from './wifi';
package/src/qr.tsx CHANGED
@@ -16,10 +16,10 @@ export function QRScanner({ onScan }: QRScannerProps) {
16
16
  const permission = useCameraPermission();
17
17
 
18
18
  const codeScanner = useCodeScanner({
19
- codeTypes: ['qr'], //correct CodeType
19
+ codeTypes: ['qr'], // correct CodeType
20
20
  onCodeScanned: (codes: Code[]) => {
21
21
  const code = codes[0];
22
- if (!code?.value) return; //undefined-safe
22
+ if (!code?.value) return; // undefined-safe
23
23
 
24
24
  onScan(code.value);
25
25
  },
@@ -343,7 +343,7 @@ export async function submitTransactionToArciumMXE(
343
343
  provider
344
344
  );
345
345
 
346
- msg?.('🔐 Intent parameters encrypted with Arcium MXE');
346
+ msg?.(' Intent parameters encrypted with Arcium MXE');
347
347
 
348
348
  // PRODUCTION: Build MXE submission instruction
349
349
  // Per TOSS Paper Section 7: "Arcium operates strictly before onchain execution"
@@ -371,7 +371,7 @@ export async function submitTransactionToArciumMXE(
371
371
  ]);
372
372
 
373
373
  msg?.(
374
- '🔐 Encrypted data prepared for MXE program (size: ' +
374
+ ' Encrypted data prepared for MXE program (size: ' +
375
375
  encryptedDataBuffer.length +
376
376
  ' bytes)'
377
377
  );
@@ -393,7 +393,7 @@ export async function submitTransactionToArciumMXE(
393
393
  };
394
394
 
395
395
  msg?.(
396
- '📤 Submitting encrypted intent to MXE program for confidential execution'
396
+ ' Submitting encrypted intent to MXE program for confidential execution'
397
397
  );
398
398
 
399
399
  // PRODUCTION: Build transaction with MXE instruction
@@ -421,7 +421,7 @@ export async function submitTransactionToArciumMXE(
421
421
  maxRetries
422
422
  );
423
423
 
424
- msg?.(' MXE transaction submitted - encrypted execution in progress');
424
+ msg?.(' MXE transaction submitted - encrypted execution in progress');
425
425
  msg?.(' Signature: ' + mxeSignature);
426
426
  msg?.(' Intent details remain confidential until settlement');
427
427
 
@@ -166,7 +166,7 @@ export class AuthService {
166
166
  ): Promise<void> {
167
167
  if (!useBiometrics) {
168
168
  throw new Error(
169
- ' SECURITY ERROR: Biometric protection is mandatory for wallet security'
169
+ ' SECURITY ERROR: Biometric protection is mandatory for wallet security'
170
170
  );
171
171
  }
172
172
 
@@ -176,7 +176,7 @@ export class AuthService {
176
176
 
177
177
  if (!hasHardware || !isEnrolled) {
178
178
  throw new Error(
179
- ' Biometric authentication required but not configured on device'
179
+ ' Biometric authentication required but not configured on device'
180
180
  );
181
181
  }
182
182
 
@@ -264,7 +264,7 @@ export class AuthService {
264
264
 
265
265
  if (!hasHardware || !isEnrolled) {
266
266
  throw new Error(
267
- ' Biometric authentication required but not configured on this device'
267
+ ' Biometric authentication required but not configured on this device'
268
268
  );
269
269
  }
270
270
 
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Compression Utilities for TOSS
3
+ *
4
+ * Implements safe, deterministic compression for metadata only.
5
+ * Transaction bytes are NEVER compressed to preserve determinism.
6
+ *
7
+ * Production-ready with official APIs.
8
+ */
9
+
10
+ import { Buffer } from 'buffer';
11
+
12
+ /**
13
+ * Simple DEFLATE-based compression using native zlib
14
+ * (Available in Node.js and via polyfills in React Native)
15
+ *
16
+ * For metadata compression: memos, recipient names, etc.
17
+ * NOT for transaction signatures or amounts!
18
+ */
19
+ export interface CompressionResult {
20
+ compressed: boolean;
21
+ data: Uint8Array;
22
+ originalSize: number;
23
+ compressedSize: number;
24
+ compressionRatio: number;
25
+ }
26
+
27
+ /**
28
+ * Compress metadata safely
29
+ * Only compresses if compression saves >10% and data >200 bytes
30
+ *
31
+ * Safe for: memo text, user names, descriptions
32
+ * UNSAFE for: signatures, amounts, transaction data
33
+ */
34
+ export async function compressMetadata(
35
+ data: string
36
+ ): Promise<CompressionResult> {
37
+ try {
38
+ const originalBuffer = Buffer.from(data, 'utf-8');
39
+ const originalSize = originalBuffer.length;
40
+
41
+ // Only compress if it's worth it
42
+ if (originalSize < 200) {
43
+ return {
44
+ compressed: false,
45
+ data: new Uint8Array(originalBuffer),
46
+ originalSize,
47
+ compressedSize: originalSize,
48
+ compressionRatio: 1.0,
49
+ };
50
+ }
51
+
52
+ // Use native compression if available (Node.js)
53
+ // For React Native, this will use a polyfill
54
+ const zlib = await importZlib();
55
+
56
+ const compressed = await new Promise<Buffer>((resolve, reject) => {
57
+ zlib.deflate(originalBuffer, (err: any, result: Buffer) => {
58
+ if (err) reject(err);
59
+ else resolve(result);
60
+ });
61
+ });
62
+
63
+ const compressedSize = compressed.length;
64
+ const compressionRatio = compressedSize / originalSize;
65
+
66
+ // Only use compression if it saves >10%
67
+ if (compressionRatio > 0.9) {
68
+ return {
69
+ compressed: false,
70
+ data: new Uint8Array(originalBuffer),
71
+ originalSize,
72
+ compressedSize: originalSize,
73
+ compressionRatio: 1.0,
74
+ };
75
+ }
76
+
77
+ return {
78
+ compressed: true,
79
+ data: new Uint8Array(compressed),
80
+ originalSize,
81
+ compressedSize,
82
+ compressionRatio,
83
+ };
84
+ } catch (error) {
85
+ // Graceful fallback: if compression fails, return uncompressed
86
+ console.warn('Compression failed, returning uncompressed:', error);
87
+ const buffer = Buffer.from(data, 'utf-8');
88
+ return {
89
+ compressed: false,
90
+ data: new Uint8Array(buffer),
91
+ originalSize: buffer.length,
92
+ compressedSize: buffer.length,
93
+ compressionRatio: 1.0,
94
+ };
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Decompress metadata
100
+ */
101
+ export async function decompressMetadata(data: Uint8Array): Promise<string> {
102
+ try {
103
+ const zlib = await importZlib();
104
+
105
+ const decompressed = await new Promise<Buffer>((resolve, reject) => {
106
+ zlib.inflate(Buffer.from(data), (err: any, result: Buffer) => {
107
+ if (err) reject(err);
108
+ else resolve(result);
109
+ });
110
+ });
111
+
112
+ return decompressed.toString('utf-8');
113
+ } catch (error) {
114
+ console.warn('Decompression failed:', error);
115
+ // Assume data was not compressed
116
+ return Buffer.from(data).toString('utf-8');
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Compress intent metadata for efficient transmission
122
+ * Returns the original intent with compressed metadata
123
+ */
124
+ export async function compressIntentMetadata(
125
+ intentMetadata: Record<string, any>
126
+ ): Promise<{
127
+ original: Record<string, any>;
128
+ compressed: Record<string, any>;
129
+ savings: number;
130
+ }> {
131
+ const compressed: Record<string, any> = {};
132
+ let totalOriginal = 0;
133
+ let totalCompressed = 0;
134
+
135
+ for (const [key, value] of Object.entries(intentMetadata)) {
136
+ if (typeof value === 'string') {
137
+ const result = await compressMetadata(value);
138
+ totalOriginal += result.originalSize;
139
+ totalCompressed += result.compressedSize;
140
+
141
+ if (result.compressed) {
142
+ compressed[key] = {
143
+ __compressed: true,
144
+ data: Array.from(result.data),
145
+ };
146
+ } else {
147
+ compressed[key] = value;
148
+ }
149
+ } else {
150
+ compressed[key] = value;
151
+ }
152
+ }
153
+
154
+ const savings =
155
+ totalOriginal > 0 ? ((totalOriginal - totalCompressed) / totalOriginal) * 100 : 0;
156
+
157
+ return {
158
+ original: intentMetadata,
159
+ compressed,
160
+ savings: Math.round(savings),
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Decompress intent metadata
166
+ */
167
+ export async function decompressIntentMetadata(
168
+ compressedMetadata: Record<string, any>
169
+ ): Promise<Record<string, any>> {
170
+ const decompressed: Record<string, any> = {};
171
+
172
+ for (const [key, value] of Object.entries(compressedMetadata)) {
173
+ if (
174
+ typeof value === 'object' &&
175
+ value !== null &&
176
+ value.__compressed === true
177
+ ) {
178
+ const buffer = new Uint8Array(value.data);
179
+ decompressed[key] = await decompressMetadata(buffer);
180
+ } else {
181
+ decompressed[key] = value;
182
+ }
183
+ }
184
+
185
+ return decompressed;
186
+ }
187
+
188
+ /**
189
+ * Lazy-load zlib to avoid breaking in environments without it
190
+ */
191
+ async function importZlib(): Promise<any> {
192
+ // In Node.js, use native zlib
193
+ if (typeof require !== 'undefined') {
194
+ try {
195
+ return require('zlib');
196
+ } catch {
197
+ // Fall through to polyfill
198
+ }
199
+ }
200
+
201
+ // For React Native, try to use a polyfill
202
+ // This can be pako (pako-zlib) or similar
203
+ try {
204
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
+ const pako = await import('pako' as string) as any;
206
+ return {
207
+ deflate: (data: Buffer, callback: any) => {
208
+ try {
209
+ const compressed = pako.deflate(data);
210
+ callback(null, Buffer.from(compressed));
211
+ } catch (err) {
212
+ callback(err);
213
+ }
214
+ },
215
+ inflate: (data: Buffer, callback: any) => {
216
+ try {
217
+ const decompressed = pako.inflate(data);
218
+ callback(null, Buffer.from(decompressed));
219
+ } catch (err) {
220
+ callback(err);
221
+ }
222
+ },
223
+ };
224
+ } catch {
225
+ throw new Error(
226
+ 'Compression not available in this environment. Install pako or use Node.js.'
227
+ );
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Calculate size savings for a metadata object
233
+ */
234
+ export function estimateCompressionSavings(
235
+ metadata: Record<string, any>
236
+ ): number {
237
+ let totalSize = 0;
238
+
239
+ for (const value of Object.values(metadata)) {
240
+ if (typeof value === 'string') {
241
+ totalSize += value.length;
242
+ }
243
+ }
244
+
245
+ // Conservative estimate: ~35% savings on text (typical for DEFLATE)
246
+ return Math.round(totalSize * 0.35);
247
+ }