toss-expo-sdk 0.1.1 → 1.0.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.
Files changed (93) hide show
  1. package/README.md +490 -81
  2. package/lib/module/ble.js +59 -4
  3. package/lib/module/ble.js.map +1 -1
  4. package/lib/module/client/BLETransactionHandler.js +277 -0
  5. package/lib/module/client/BLETransactionHandler.js.map +1 -0
  6. package/lib/module/client/NonceAccountManager.js +364 -0
  7. package/lib/module/client/NonceAccountManager.js.map +1 -0
  8. package/lib/module/client/TossClient.js +27 -44
  9. package/lib/module/client/TossClient.js.map +1 -1
  10. package/lib/module/contexts/WalletContext.js +4 -4
  11. package/lib/module/contexts/WalletContext.js.map +1 -1
  12. package/lib/module/discovery.js +35 -8
  13. package/lib/module/discovery.js.map +1 -1
  14. package/lib/module/examples/offlinePaymentFlow.js +27 -2
  15. package/lib/module/examples/offlinePaymentFlow.js.map +1 -1
  16. package/lib/module/hooks/useOfflineBLETransactions.js +314 -0
  17. package/lib/module/hooks/useOfflineBLETransactions.js.map +1 -0
  18. package/lib/module/index.js +13 -8
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/module/intent.js +198 -0
  21. package/lib/module/intent.js.map +1 -1
  22. package/lib/module/nfc.js +1 -1
  23. package/lib/module/noise.js +176 -1
  24. package/lib/module/noise.js.map +1 -1
  25. package/lib/module/reconciliation.js +155 -0
  26. package/lib/module/reconciliation.js.map +1 -1
  27. package/lib/module/services/authService.js +164 -1
  28. package/lib/module/services/authService.js.map +1 -1
  29. package/lib/module/storage/secureStorage.js +102 -0
  30. package/lib/module/storage/secureStorage.js.map +1 -1
  31. package/lib/module/storage.js +4 -4
  32. package/lib/module/sync.js +25 -1
  33. package/lib/module/sync.js.map +1 -1
  34. package/lib/module/types/nonceAccount.js +2 -0
  35. package/lib/module/types/nonceAccount.js.map +1 -0
  36. package/lib/module/types/tossUser.js +16 -1
  37. package/lib/module/types/tossUser.js.map +1 -1
  38. package/lib/typescript/src/__tests__/solana-program-simple.test.d.ts +8 -0
  39. package/lib/typescript/src/__tests__/solana-program-simple.test.d.ts.map +1 -0
  40. package/lib/typescript/src/ble.d.ts +31 -2
  41. package/lib/typescript/src/ble.d.ts.map +1 -1
  42. package/lib/typescript/src/client/BLETransactionHandler.d.ts +98 -0
  43. package/lib/typescript/src/client/BLETransactionHandler.d.ts.map +1 -0
  44. package/lib/typescript/src/client/NonceAccountManager.d.ts +82 -0
  45. package/lib/typescript/src/client/NonceAccountManager.d.ts.map +1 -0
  46. package/lib/typescript/src/client/TossClient.d.ts +10 -12
  47. package/lib/typescript/src/client/TossClient.d.ts.map +1 -1
  48. package/lib/typescript/src/discovery.d.ts +8 -2
  49. package/lib/typescript/src/discovery.d.ts.map +1 -1
  50. package/lib/typescript/src/examples/offlinePaymentFlow.d.ts +9 -1
  51. package/lib/typescript/src/examples/offlinePaymentFlow.d.ts.map +1 -1
  52. package/lib/typescript/src/hooks/useOfflineBLETransactions.d.ts +91 -0
  53. package/lib/typescript/src/hooks/useOfflineBLETransactions.d.ts.map +1 -0
  54. package/lib/typescript/src/index.d.ts +11 -4
  55. package/lib/typescript/src/index.d.ts.map +1 -1
  56. package/lib/typescript/src/intent.d.ts +26 -0
  57. package/lib/typescript/src/intent.d.ts.map +1 -1
  58. package/lib/typescript/src/noise.d.ts +62 -0
  59. package/lib/typescript/src/noise.d.ts.map +1 -1
  60. package/lib/typescript/src/reconciliation.d.ts +6 -0
  61. package/lib/typescript/src/reconciliation.d.ts.map +1 -1
  62. package/lib/typescript/src/services/authService.d.ts +26 -1
  63. package/lib/typescript/src/services/authService.d.ts.map +1 -1
  64. package/lib/typescript/src/storage/secureStorage.d.ts +16 -0
  65. package/lib/typescript/src/storage/secureStorage.d.ts.map +1 -1
  66. package/lib/typescript/src/sync.d.ts +6 -1
  67. package/lib/typescript/src/sync.d.ts.map +1 -1
  68. package/lib/typescript/src/types/nonceAccount.d.ts +59 -0
  69. package/lib/typescript/src/types/nonceAccount.d.ts.map +1 -0
  70. package/lib/typescript/src/types/tossUser.d.ts +16 -0
  71. package/lib/typescript/src/types/tossUser.d.ts.map +1 -1
  72. package/package.json +12 -1
  73. package/src/__tests__/reconciliation.test.tsx +7 -1
  74. package/src/__tests__/solana-program-simple.test.ts +256 -0
  75. package/src/ble.ts +105 -4
  76. package/src/client/BLETransactionHandler.ts +364 -0
  77. package/src/client/NonceAccountManager.ts +444 -0
  78. package/src/client/TossClient.ts +36 -49
  79. package/src/contexts/WalletContext.tsx +4 -4
  80. package/src/discovery.ts +46 -8
  81. package/src/examples/offlinePaymentFlow.ts +48 -2
  82. package/src/hooks/useOfflineBLETransactions.ts +438 -0
  83. package/src/index.tsx +49 -7
  84. package/src/intent.ts +254 -0
  85. package/src/nfc.ts +4 -4
  86. package/src/noise.ts +239 -1
  87. package/src/reconciliation.ts +184 -0
  88. package/src/services/authService.ts +188 -1
  89. package/src/storage/secureStorage.ts +142 -4
  90. package/src/storage.ts +4 -4
  91. package/src/sync.ts +40 -0
  92. package/src/types/nonceAccount.ts +75 -0
  93. package/src/types/tossUser.ts +35 -2
@@ -1 +1 @@
1
- {"version":3,"file":"reconciliation.d.ts","sourceRoot":"","sources":["../../../src/reconciliation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,UAAU,EACV,SAAS,EACT,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,UAAU,CAAC;AAQ3D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAwF7C;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,WAAW,CAAC,CA6CtB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,EACtB,UAAU,GAAE,MAAU,GACrB,OAAO,CAAC,MAAM,CAAC,CAgDjB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAwC3B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAyD7B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAyBnD;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,mBAAmB,CAAC,CAiB9B"}
1
+ {"version":3,"file":"reconciliation.d.ts","sourceRoot":"","sources":["../../../src/reconciliation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,UAAU,EACV,SAAS,EACT,WAAW,EAEZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,UAAU,CAAC;AAe3D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAuI7C;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,WAAW,CAAC,CA6CtB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,UAAU,EACtB,UAAU,GAAE,MAAU,GACrB,OAAO,CAAC,MAAM,CAAC,CAgDjB;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,SAAS,EACvB,QAAQ,EAAE,GAAG,EAAE,iBAAiB;AAChC,UAAU,GAAE,MAAU,GACrB,OAAO,CAAC,MAAM,CAAC,CAsHjB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAwC3B;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAyD7B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAyBnD;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,mBAAmB,CAAC,CAiB9B"}
@@ -1,4 +1,4 @@
1
- import { Keypair, PublicKey } from '@solana/web3.js';
1
+ import { Keypair, PublicKey, Connection } from '@solana/web3.js';
2
2
  import type { TossUser } from '../types/tossUser';
3
3
  export declare const SESSION_KEY = "toss_user_session";
4
4
  type UserSession = {
@@ -50,6 +50,31 @@ export declare class AuthService {
50
50
  * Only use for logout or account deletion
51
51
  */
52
52
  static deleteWalletPermanently(): Promise<void>;
53
+ /**
54
+ * Create a secure durable nonce account for offline transactions
55
+ * REQUIRES biometric authentication for maximum security
56
+ *
57
+ * This creates a nonce account that enables:
58
+ * - Offline transaction creation (with replay protection)
59
+ * - Biometric-protected signing
60
+ * - Encrypted storage with Noise Protocol support
61
+ */
62
+ static createSecureNonceAccount(user: TossUser, connection: Connection, userKeypair: Keypair): Promise<TossUser>;
63
+ /**
64
+ * Enable offline transactions for a user with nonce account support
65
+ * Ensures all security measures are in place
66
+ */
67
+ static enableOfflineTransactions(user: TossUser): Promise<TossUser>;
68
+ /**
69
+ * Verify nonce account is accessible and valid
70
+ * Requires biometric authentication
71
+ */
72
+ static verifyNonceAccountAccess(userId: string): Promise<boolean>;
73
+ /**
74
+ * Revoke nonce account (security measure)
75
+ * Requires biometric verification
76
+ */
77
+ static revokeNonceAccount(userId: string, user: TossUser): Promise<TossUser>;
53
78
  }
54
79
  export {};
55
80
  //# sourceMappingURL=authService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../../../src/services/authService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlD,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAI/C,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,WAAW;WACT,gBAAgB,CAC3B,aAAa,EAAE,MAAM,EACrB,WAAW,GAAE,OAAe,GAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;WAwCvC,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;WAIhD,UAAU,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;WAKzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;WAKxB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;WAQpC,0BAA0B,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA2DlE;;;;;;;;;;;OAWG;WACU,qBAAqB,CAChC,OAAO,EAAE,OAAO,EAChB,aAAa,GAAE,OAAc,GAC5B,OAAO,CAAC,IAAI,CAAC;IAoChB;;;OAGG;WACU,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAKxD;;;OAGG;WACU,uBAAuB,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAajE;;;OAGG;WACU,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKlD;;;OAGG;WACU,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;CAKtD"}
1
+ {"version":3,"file":"authService.d.ts","sourceRoot":"","sources":["../../../../src/services/authService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAIlD,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAK/C,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,WAAW;WACT,gBAAgB,CAC3B,aAAa,EAAE,MAAM,EACrB,WAAW,GAAE,OAAe,GAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;WA8CvC,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;WAIhD,UAAU,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;WAKzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;WAKxB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;WAQpC,0BAA0B,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IA2DlE;;;;;;;;;;;OAWG;WACU,qBAAqB,CAChC,OAAO,EAAE,OAAO,EAChB,aAAa,GAAE,OAAc,GAC5B,OAAO,CAAC,IAAI,CAAC;IAoChB;;;OAGG;WACU,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAKxD;;;OAGG;WACU,uBAAuB,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAajE;;;OAGG;WACU,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKlD;;;OAGG;WACU,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrD;;;;;;;;OAQG;WACU,wBAAwB,CACnC,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,OAAO,GACnB,OAAO,CAAC,QAAQ,CAAC;IA2EpB;;;OAGG;WACU,yBAAyB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAuBzE;;;OAGG;WACU,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBvE;;;OAGG;WACU,kBAAkB,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,QAAQ,CAAC;CA4BrB"}
@@ -4,4 +4,20 @@ export declare function getSecureIntent(intentId: string): Promise<SolanaIntent
4
4
  export declare function getAllSecureIntents(): Promise<SolanaIntent[]>;
5
5
  export declare function removeSecureIntent(intentId: string): Promise<void>;
6
6
  export declare function clearAllSecureIntents(): Promise<void>;
7
+ /**
8
+ * GAP #1: Cleanup expired intents from local storage
9
+ * Per TOSS Paper Section 8: Local state is append-only until settlement confirmation
10
+ */
11
+ export declare function cleanupExpiredIntents(): Promise<number>;
12
+ export interface ReconciliationStateData {
13
+ userId: string;
14
+ lastSyncTime: number;
15
+ lastSyncSlot: number;
16
+ processedIntents: string[];
17
+ failedIntents: string[];
18
+ conflictingIntents: string[];
19
+ }
20
+ export declare function saveReconciliationState(userId: string, state: Partial<ReconciliationStateData>): Promise<void>;
21
+ export declare function getReconciliationState(userId: string): Promise<ReconciliationStateData>;
22
+ export declare function updateReconciliationState(userId: string, intentId: string, status: 'processed' | 'failed' | 'conflicted'): Promise<void>;
7
23
  //# sourceMappingURL=secureStorage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"secureStorage.d.ts","sourceRoot":"","sources":["../../../../src/storage/secureStorage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAoB9C,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3E;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAY9B;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAanE;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAU3D"}
1
+ {"version":3,"file":"secureStorage.d.ts","sourceRoot":"","sources":["../../../../src/storage/secureStorage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAoB9C,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3E;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAY9B;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAanE;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE;AAED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAU3D;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAmB7D;AAQD,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,CAAC,uBAAuB,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,uBAAuB,CAAC,CAuBlC;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,YAAY,GAC5C,OAAO,CAAC,IAAI,CAAC,CAgCf"}
@@ -4,6 +4,8 @@
4
4
  * Implements Section 9 of the TOSS Technical Paper:
5
5
  * Upon regaining connectivity, devices initiate reconciliation with onchain state.
6
6
  * All offline artifacts are verified onchain and settled with deterministic outcomes.
7
+ *
8
+ * GAP #2 FIX: Track synchronization state persistently
7
9
  */
8
10
  import { Connection, PublicKey } from '@solana/web3.js';
9
11
  import { type SettlementResult, type ReconciliationState } from './reconciliation';
@@ -27,11 +29,14 @@ export interface SyncResult {
27
29
  * 2. Settle all pending intents
28
30
  * 3. Update local state with results
29
31
  *
32
+ * GAP #2 FIX: Persist reconciliation state for future queries
33
+ *
30
34
  * @param connection Connection to Solana RPC
35
+ * @param userId User ID for state tracking (required for persistence)
31
36
  * @param feePayer Optional fee payer keypair public key
32
37
  * @returns Detailed sync results including conflicts and settlements
33
38
  */
34
- export declare function syncToChain(connection: Connection, feePayer?: PublicKey): Promise<SyncResult>;
39
+ export declare function syncToChain(connection: Connection, userId?: string, feePayer?: PublicKey): Promise<SyncResult>;
35
40
  /**
36
41
  * Lightweight sync to check status without settling
37
42
  * Useful for monitoring or UI updates without committing to settlements
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,UAAU;IAEzB,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;IAE1C,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IAEtC,iBAAiB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAE5D,mBAAmB,EAAE,mBAAmB,CAAC;IAEzC,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,UAAU,EACtB,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,UAAU,CAAC,CAyCrB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,mBAAmB,CAAC,CAE9B"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,WAAW,UAAU;IAEzB,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;IAE1C,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IAEtC,iBAAiB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAE5D,mBAAmB,EAAE,mBAAmB,CAAC;IAEzC,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,SAAS,GACnB,OAAO,CAAC,UAAU,CAAC,CA0ErB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,mBAAmB,CAAC,CAE9B"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Represents a durable nonce account for offline transaction support
3
+ * Enables secure offline transaction creation with replay protection
4
+ */
5
+ export interface NonceAccountInfo {
6
+ address: string;
7
+ owner: string;
8
+ authorizedSigner: string;
9
+ currentNonce: number;
10
+ lastUsedNonce: number;
11
+ blockhash: string;
12
+ isBiometricProtected: boolean;
13
+ createdAt: number;
14
+ lastModified: number;
15
+ isStoredSecurely: boolean;
16
+ encryptedData?: string;
17
+ minRentLamports?: number;
18
+ status?: 'active' | 'expired' | 'revoked';
19
+ }
20
+ /**
21
+ * Options for creating a durable nonce account
22
+ */
23
+ export interface CreateNonceAccountOptions {
24
+ requireBiometric?: boolean;
25
+ securityLevel?: 'standard' | 'high' | 'maximum';
26
+ minNonceCount?: number;
27
+ maxNonceCount?: number;
28
+ persistToSecureStorage?: boolean;
29
+ allowCloudBackup?: boolean;
30
+ autoRenew?: boolean;
31
+ expiryDays?: number;
32
+ }
33
+ /**
34
+ * Nonce account cache entry for efficient offline usage
35
+ */
36
+ export interface NonceAccountCacheEntry {
37
+ accountInfo: NonceAccountInfo;
38
+ nonces: number[];
39
+ expiresAt: number;
40
+ }
41
+ /**
42
+ * Represents a transaction prepared for offline use with nonce account
43
+ */
44
+ export interface OfflineTransaction {
45
+ id: string;
46
+ nonceAccount: string;
47
+ nonce: number;
48
+ transaction: string;
49
+ signature?: string;
50
+ status: 'prepared' | 'signed' | 'submitted' | 'confirmed' | 'failed';
51
+ createdAt: number;
52
+ expiresAt: number;
53
+ metadata?: {
54
+ description?: string;
55
+ tags?: string[];
56
+ [key: string]: any;
57
+ };
58
+ }
59
+ //# sourceMappingURL=nonceAccount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nonceAccount.d.ts","sourceRoot":"","sources":["../../../../src/types/nonceAccount.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAE/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IAGzB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAGlB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IAGrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IAExC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAGhD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAG3B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;CACH"}
@@ -1,6 +1,7 @@
1
1
  import { PublicKey } from '@solana/web3.js';
2
2
  /**
3
3
  * Represents a TOSS wallet user in the ecosystem
4
+ * Enhanced with secure nonce account support for offline transactions
4
5
  */
5
6
  export type TossUser = {
6
7
  userId: string;
@@ -11,12 +12,25 @@ export type TossUser = {
11
12
  isVerified: boolean;
12
13
  createdAt: string;
13
14
  };
15
+ nonceAccount?: {
16
+ address: PublicKey;
17
+ authorizedSigner: PublicKey;
18
+ isBiometricProtected: boolean;
19
+ expiresAt?: number;
20
+ status: 'active' | 'expired' | 'revoked';
21
+ };
14
22
  device: {
15
23
  id: string;
16
24
  name?: string;
17
25
  lastActive: string;
18
26
  client: 'mobile' | 'web' | 'desktop';
19
27
  };
28
+ security: {
29
+ biometricEnabled: boolean;
30
+ biometricSalt?: string;
31
+ nonceAccountRequiresBiometric: boolean;
32
+ lastBiometricVerification?: number;
33
+ };
20
34
  status: 'active' | 'inactive' | 'restricted';
21
35
  lastSeen: string;
22
36
  tossFeatures: {
@@ -24,6 +38,8 @@ export type TossUser = {
24
38
  canReceive: boolean;
25
39
  isPrivateTxEnabled: boolean;
26
40
  maxTransactionAmount: number;
41
+ offlineTransactionsEnabled?: boolean;
42
+ nonceAccountEnabled?: boolean;
27
43
  };
28
44
  createdAt: string;
29
45
  updatedAt: string;
@@ -1 +1 @@
1
- {"version":3,"file":"tossUser.d.ts","sourceRoot":"","sources":["../../../../src/types/tossUser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IAErB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,MAAM,EAAE;QACN,SAAS,EAAE,SAAS,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAGF,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;KACtC,CAAC;IAGF,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;IAC7C,QAAQ,EAAE,MAAM,CAAC;IAGjB,YAAY,EAAE;QACZ,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,OAAO,CAAC;QACpB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,oBAAoB,EAAE,MAAM,CAAC;KAC9B,CAAC;IAGF,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,QAAQ,EACR,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAC5C,GAAG;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAGF,eAAO,MAAM,eAAe,EAAE,QAyB7B,CAAC"}
1
+ {"version":3,"file":"tossUser.d.ts","sourceRoot":"","sources":["../../../../src/types/tossUser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IAErB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,MAAM,EAAE;QACN,SAAS,EAAE,SAAS,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAGF,YAAY,CAAC,EAAE;QACb,OAAO,EAAE,SAAS,CAAC;QACnB,gBAAgB,EAAE,SAAS,CAAC;QAC5B,oBAAoB,EAAE,OAAO,CAAC;QAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;KAC1C,CAAC;IAGF,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;KACtC,CAAC;IAGF,QAAQ,EAAE;QACR,gBAAgB,EAAE,OAAO,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,6BAA6B,EAAE,OAAO,CAAC;QACvC,yBAAyB,CAAC,EAAE,MAAM,CAAC;KACpC,CAAC;IAGF,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC;IAC7C,QAAQ,EAAE,MAAM,CAAC;IAGjB,YAAY,EAAE;QACZ,OAAO,EAAE,OAAO,CAAC;QACjB,UAAU,EAAE,OAAO,CAAC;QACpB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;QACrC,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,CAAC;IAGF,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,QAAQ,EACR,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAC5C,GAAG;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAGF,eAAO,MAAM,eAAe,EAAE,QAsC7B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toss-expo-sdk",
3
- "version": "0.1.1",
3
+ "version": "1.0.1",
4
4
  "description": "The official React Native SDK for The Offline Solana Stack (TOSS)",
5
5
  "type": "module",
6
6
  "main": "./lib/module/index.js",
@@ -91,6 +91,7 @@
91
91
  "del-cli": "^6.0.0",
92
92
  "eslint": "^9.35.0",
93
93
  "eslint-config-prettier": "^10.1.8",
94
+ "eslint-plugin-ft-flow": "^3.0.11",
94
95
  "eslint-plugin-prettier": "^5.5.4",
95
96
  "eslint-plugin-react-native": "^5.0.0",
96
97
  "jest": "^29.7.0",
@@ -136,6 +137,16 @@
136
137
  "modulePathIgnorePatterns": [
137
138
  "<rootDir>/example/node_modules",
138
139
  "<rootDir>/lib/"
140
+ ],
141
+ "transformIgnorePatterns": [
142
+ "node_modules/(?!(react-native|\\@react-native|\\@solana|@arcium-hq|@chainsafe)/)"
143
+ ],
144
+ "transform": {
145
+ "^.+\\.(js|ts|tsx|mjs)$": "babel-jest"
146
+ },
147
+ "extensionsToTreatAsEsm": [
148
+ ".ts",
149
+ ".tsx"
139
150
  ]
140
151
  },
141
152
  "commitlint": {
@@ -180,7 +180,8 @@ describe('Discovery Module', () => {
180
180
 
181
181
  // Manually set old timestamp
182
182
  discovery.registerPeer(peer);
183
- discovery['discoveredPeers'].get('peer_timeout')!.lastSeen =
183
+ // Access private storage for testing via cast to any
184
+ (discovery as any).discoveredPeers.get('peer_timeout')!.lastSeen =
184
185
  Date.now() - 6 * 60 * 1000;
185
186
 
186
187
  const active = discovery.getActivePeers();
@@ -214,6 +215,11 @@ describe('Discovery Module', () => {
214
215
  protocol = new IntentExchangeProtocol();
215
216
  });
216
217
 
218
+ afterEach(() => {
219
+ // Ensure any timers or sessions are cleaned up so Jest can exit
220
+ protocol.dispose();
221
+ });
222
+
217
223
  it('should create an exchange request', () => {
218
224
  const intent: SolanaIntent = {
219
225
  id: 'intent_test',
@@ -0,0 +1,256 @@
1
+ /**
2
+ * TOSS Solana Program Integration Tests (Simplified)
3
+ *
4
+ * Tests for the toss-intent-processor program
5
+ * Gap #5: Onchain Intent Verification
6
+ */
7
+
8
+ import { Connection, Keypair } from '@solana/web3.js';
9
+ import { createIntent, verifyIntent } from '../intent';
10
+ import { NonceAccountManager } from '../client/NonceAccountManager';
11
+
12
+ describe('TOSS Solana Intent Processor Program', () => {
13
+ let connection: Connection;
14
+ let senderKeypair: Keypair;
15
+ let recipientKeypair: Keypair;
16
+ let nonceManager: NonceAccountManager;
17
+
18
+ beforeAll(() => {
19
+ // Use Devnet for testing
20
+ connection = new Connection('https://api.devnet.solana.com', 'confirmed');
21
+ senderKeypair = Keypair.generate();
22
+ recipientKeypair = Keypair.generate();
23
+ nonceManager = new NonceAccountManager(connection);
24
+ });
25
+
26
+ describe('Intent Signature Verification', () => {
27
+ it('should create a valid, verifiable intent', async () => {
28
+ // Create intent
29
+ const intent = await createIntent(
30
+ senderKeypair,
31
+ recipientKeypair.publicKey,
32
+ 1000000,
33
+ connection,
34
+ { expiresIn: 60 * 60 }
35
+ );
36
+
37
+ // Verify the intent can be verified
38
+ expect(intent.signature).toBeDefined();
39
+ expect(intent.signature.length).toBeGreaterThan(0);
40
+
41
+ // Verify intent signature locally (client-side)
42
+ const isValid = await verifyIntent(intent, connection);
43
+ expect(isValid).toBe(true);
44
+ });
45
+
46
+ it('should reject modified intent', async () => {
47
+ const intent = await createIntent(
48
+ senderKeypair,
49
+ recipientKeypair.publicKey,
50
+ 1000000,
51
+ connection
52
+ );
53
+
54
+ // Modify the intent (would fail signature check)
55
+ const modifiedIntent = { ...intent, amount: 2000000 };
56
+
57
+ // Modified intent should fail verification
58
+ const isValid = await verifyIntent(modifiedIntent, connection);
59
+ expect(isValid).toBe(false);
60
+ });
61
+
62
+ it('should reject expired intent', async () => {
63
+ // Create an intent that's already expired
64
+ const intent = await createIntent(
65
+ senderKeypair,
66
+ recipientKeypair.publicKey,
67
+ 1000000,
68
+ connection,
69
+ { expiresIn: -100 } // Already expired
70
+ );
71
+
72
+ // Expired intent should fail
73
+ const isValid = await verifyIntent(intent, connection);
74
+ expect(isValid).toBe(false);
75
+ });
76
+ });
77
+
78
+ describe('Intent Program Data Structure', () => {
79
+ it('should verify intent data structure matches program expectations', async () => {
80
+ const intent = await createIntent(
81
+ senderKeypair,
82
+ recipientKeypair.publicKey,
83
+ 5000000,
84
+ connection
85
+ );
86
+
87
+ // Verify all required fields for onchain program
88
+ expect(intent.from).toBeDefined();
89
+ expect(intent.to).toBeDefined();
90
+ expect(intent.amount).toBeGreaterThan(0);
91
+ expect(intent.nonce).toBeGreaterThanOrEqual(0);
92
+ expect(intent.expiry).toBeGreaterThan(Date.now() / 1000);
93
+ expect(intent.signature).toBeDefined();
94
+ expect(intent.blockhash).toBeDefined();
95
+ });
96
+
97
+ it('should enforce expiry constraints', async () => {
98
+ const now = Math.floor(Date.now() / 1000);
99
+ const intent = await createIntent(
100
+ senderKeypair,
101
+ recipientKeypair.publicKey,
102
+ 1000000,
103
+ connection,
104
+ { expiresIn: 3600 } // 1 hour
105
+ );
106
+
107
+ // Expiry should be in future
108
+ expect(intent.expiry).toBeGreaterThan(now);
109
+ expect(intent.expiry - now).toBeCloseTo(3600, -1);
110
+ });
111
+ });
112
+
113
+ describe('Deterministic Settlement', () => {
114
+ it('should handle settlement with proper sequencing', async () => {
115
+ // Create multiple intents
116
+ const intent1 = await createIntent(
117
+ senderKeypair,
118
+ recipientKeypair.publicKey,
119
+ 1000000,
120
+ connection
121
+ );
122
+
123
+ const intent2 = await createIntent(
124
+ senderKeypair,
125
+ recipientKeypair.publicKey,
126
+ 2000000,
127
+ connection
128
+ );
129
+
130
+ // Both should be valid
131
+ const isValid1 = await verifyIntent(intent1, connection);
132
+ const isValid2 = await verifyIntent(intent2, connection);
133
+
134
+ expect(isValid1).toBe(true);
135
+ expect(isValid2).toBe(true);
136
+
137
+ // Different nonces ensure ordering
138
+ expect(intent1.nonce).not.toBe(intent2.nonce);
139
+ });
140
+
141
+ it('should reject duplicate nonce', async () => {
142
+ const intent = await createIntent(
143
+ senderKeypair,
144
+ recipientKeypair.publicKey,
145
+ 1000000,
146
+ connection
147
+ );
148
+
149
+ // Try to create another intent with same nonce (would fail in practice)
150
+ const intentDupe = { ...intent };
151
+
152
+ // Both have same nonce - would be rejected onchain
153
+ expect(intent.nonce).toBe(intentDupe.nonce);
154
+ });
155
+ });
156
+
157
+ describe('Program Constraints', () => {
158
+ it('should validate amount is positive', async () => {
159
+ const intent = await createIntent(
160
+ senderKeypair,
161
+ recipientKeypair.publicKey,
162
+ 1000000,
163
+ connection
164
+ );
165
+
166
+ // Intent amount should be positive
167
+ expect(intent.amount).toBeGreaterThan(0);
168
+ });
169
+
170
+ it('should allow large amounts within u64 bounds', async () => {
171
+ const largeAmount = Math.floor(Number.MAX_SAFE_INTEGER / 2);
172
+ const intent = await createIntent(
173
+ senderKeypair,
174
+ recipientKeypair.publicKey,
175
+ largeAmount,
176
+ connection
177
+ );
178
+
179
+ expect(intent.amount).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER);
180
+ });
181
+ });
182
+
183
+ describe('Nonce Account Integration', () => {
184
+ it('should create nonce account for replay protection', async () => {
185
+ const nonceAuthority = Keypair.generate();
186
+
187
+ const mockTossUser: any = {
188
+ userId: 'test-user-nonce',
189
+ username: 'testusernonce',
190
+ wallet: {
191
+ publicKey: senderKeypair.publicKey.toBase58(),
192
+ isVerified: true,
193
+ },
194
+ security: {
195
+ biometricEnabled: true,
196
+ nonceAccountRequiresBiometric: true,
197
+ },
198
+ tossFeatures: {
199
+ canSend: true,
200
+ canReceive: true,
201
+ isPrivateTxEnabled: false,
202
+ maxTransactionAmount: 10000000,
203
+ offlineTransactionsEnabled: true,
204
+ nonceAccountEnabled: true,
205
+ },
206
+ };
207
+
208
+ const nonceAccountInfo = await nonceManager.createNonceAccount(
209
+ mockTossUser,
210
+ nonceAuthority,
211
+ senderKeypair.publicKey,
212
+ { requireBiometric: true }
213
+ );
214
+
215
+ expect(nonceAccountInfo).toBeDefined();
216
+ expect(nonceAccountInfo.address).toBeDefined();
217
+ expect(nonceAccountInfo.isBiometricProtected).toBe(true);
218
+ });
219
+
220
+ it('should validate nonce account is active', async () => {
221
+ const nonceAuthority = Keypair.generate();
222
+
223
+ const mockTossUser: any = {
224
+ userId: 'test-user-validate',
225
+ username: 'testuservalidate',
226
+ wallet: {
227
+ publicKey: senderKeypair.publicKey.toBase58(),
228
+ isVerified: true,
229
+ },
230
+ security: {
231
+ biometricEnabled: true,
232
+ nonceAccountRequiresBiometric: true,
233
+ },
234
+ tossFeatures: {
235
+ canSend: true,
236
+ canReceive: true,
237
+ isPrivateTxEnabled: false,
238
+ maxTransactionAmount: 10000000,
239
+ offlineTransactionsEnabled: true,
240
+ nonceAccountEnabled: true,
241
+ },
242
+ };
243
+
244
+ const nonceAccountInfo = await nonceManager.createNonceAccount(
245
+ mockTossUser,
246
+ nonceAuthority,
247
+ senderKeypair.publicKey,
248
+ { requireBiometric: true }
249
+ );
250
+
251
+ // Nonce account should be valid
252
+ const isValid = nonceManager.isNonceAccountValid(nonceAccountInfo);
253
+ expect(isValid).toBe(true);
254
+ });
255
+ });
256
+ });