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.
- package/README.md +490 -81
- package/lib/module/ble.js +59 -4
- package/lib/module/ble.js.map +1 -1
- package/lib/module/client/BLETransactionHandler.js +277 -0
- package/lib/module/client/BLETransactionHandler.js.map +1 -0
- package/lib/module/client/NonceAccountManager.js +364 -0
- package/lib/module/client/NonceAccountManager.js.map +1 -0
- package/lib/module/client/TossClient.js +27 -44
- package/lib/module/client/TossClient.js.map +1 -1
- package/lib/module/contexts/WalletContext.js +4 -4
- package/lib/module/contexts/WalletContext.js.map +1 -1
- package/lib/module/discovery.js +35 -8
- package/lib/module/discovery.js.map +1 -1
- package/lib/module/examples/offlinePaymentFlow.js +27 -2
- package/lib/module/examples/offlinePaymentFlow.js.map +1 -1
- package/lib/module/hooks/useOfflineBLETransactions.js +314 -0
- package/lib/module/hooks/useOfflineBLETransactions.js.map +1 -0
- package/lib/module/index.js +13 -8
- package/lib/module/index.js.map +1 -1
- package/lib/module/intent.js +198 -0
- package/lib/module/intent.js.map +1 -1
- package/lib/module/nfc.js +1 -1
- package/lib/module/noise.js +176 -1
- package/lib/module/noise.js.map +1 -1
- package/lib/module/reconciliation.js +155 -0
- package/lib/module/reconciliation.js.map +1 -1
- package/lib/module/services/authService.js +164 -1
- package/lib/module/services/authService.js.map +1 -1
- package/lib/module/storage/secureStorage.js +102 -0
- package/lib/module/storage/secureStorage.js.map +1 -1
- package/lib/module/storage.js +4 -4
- package/lib/module/sync.js +25 -1
- package/lib/module/sync.js.map +1 -1
- package/lib/module/types/nonceAccount.js +2 -0
- package/lib/module/types/nonceAccount.js.map +1 -0
- package/lib/module/types/tossUser.js +16 -1
- package/lib/module/types/tossUser.js.map +1 -1
- package/lib/typescript/src/__tests__/solana-program-simple.test.d.ts +8 -0
- package/lib/typescript/src/__tests__/solana-program-simple.test.d.ts.map +1 -0
- package/lib/typescript/src/ble.d.ts +31 -2
- package/lib/typescript/src/ble.d.ts.map +1 -1
- package/lib/typescript/src/client/BLETransactionHandler.d.ts +98 -0
- package/lib/typescript/src/client/BLETransactionHandler.d.ts.map +1 -0
- package/lib/typescript/src/client/NonceAccountManager.d.ts +82 -0
- package/lib/typescript/src/client/NonceAccountManager.d.ts.map +1 -0
- package/lib/typescript/src/client/TossClient.d.ts +10 -12
- package/lib/typescript/src/client/TossClient.d.ts.map +1 -1
- package/lib/typescript/src/discovery.d.ts +8 -2
- package/lib/typescript/src/discovery.d.ts.map +1 -1
- package/lib/typescript/src/examples/offlinePaymentFlow.d.ts +9 -1
- package/lib/typescript/src/examples/offlinePaymentFlow.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useOfflineBLETransactions.d.ts +91 -0
- package/lib/typescript/src/hooks/useOfflineBLETransactions.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +11 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/intent.d.ts +26 -0
- package/lib/typescript/src/intent.d.ts.map +1 -1
- package/lib/typescript/src/noise.d.ts +62 -0
- package/lib/typescript/src/noise.d.ts.map +1 -1
- package/lib/typescript/src/reconciliation.d.ts +6 -0
- package/lib/typescript/src/reconciliation.d.ts.map +1 -1
- package/lib/typescript/src/services/authService.d.ts +26 -1
- package/lib/typescript/src/services/authService.d.ts.map +1 -1
- package/lib/typescript/src/storage/secureStorage.d.ts +16 -0
- package/lib/typescript/src/storage/secureStorage.d.ts.map +1 -1
- package/lib/typescript/src/sync.d.ts +6 -1
- package/lib/typescript/src/sync.d.ts.map +1 -1
- package/lib/typescript/src/types/nonceAccount.d.ts +59 -0
- package/lib/typescript/src/types/nonceAccount.d.ts.map +1 -0
- package/lib/typescript/src/types/tossUser.d.ts +16 -0
- package/lib/typescript/src/types/tossUser.d.ts.map +1 -1
- package/package.json +12 -1
- package/src/__tests__/reconciliation.test.tsx +7 -1
- package/src/__tests__/solana-program-simple.test.ts +256 -0
- package/src/ble.ts +105 -4
- package/src/client/BLETransactionHandler.ts +364 -0
- package/src/client/NonceAccountManager.ts +444 -0
- package/src/client/TossClient.ts +36 -49
- package/src/contexts/WalletContext.tsx +4 -4
- package/src/discovery.ts +46 -8
- package/src/examples/offlinePaymentFlow.ts +48 -2
- package/src/hooks/useOfflineBLETransactions.ts +438 -0
- package/src/index.tsx +49 -7
- package/src/intent.ts +254 -0
- package/src/nfc.ts +4 -4
- package/src/noise.ts +239 -1
- package/src/reconciliation.ts +184 -0
- package/src/services/authService.ts +188 -1
- package/src/storage/secureStorage.ts +142 -4
- package/src/storage.ts +4 -4
- package/src/sync.ts +40 -0
- package/src/types/nonceAccount.ts +75 -0
- 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;
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
+
});
|