toss-expo-sdk 0.1.0
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/LICENSE +20 -0
- package/README.md +292 -0
- package/lib/module/ble.js +103 -0
- package/lib/module/ble.js.map +1 -0
- package/lib/module/client/TossClient.js +324 -0
- package/lib/module/client/TossClient.js.map +1 -0
- package/lib/module/client/index.js +4 -0
- package/lib/module/client/index.js.map +1 -0
- package/lib/module/contexts/WalletContext.js +99 -0
- package/lib/module/contexts/WalletContext.js.map +1 -0
- package/lib/module/discovery.js +434 -0
- package/lib/module/discovery.js.map +1 -0
- package/lib/module/errors.js +47 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/examples/offlinePaymentFlow.js +234 -0
- package/lib/module/examples/offlinePaymentFlow.js.map +1 -0
- package/lib/module/index.js +32 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/intent.js +223 -0
- package/lib/module/intent.js.map +1 -0
- package/lib/module/intentManager.js +145 -0
- package/lib/module/intentManager.js.map +1 -0
- package/lib/module/internal/arciumHelper.js +50 -0
- package/lib/module/internal/arciumHelper.js.map +1 -0
- package/lib/module/nfc.js +54 -0
- package/lib/module/nfc.js.map +1 -0
- package/lib/module/noise.js +14 -0
- package/lib/module/noise.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/qr.js +57 -0
- package/lib/module/qr.js.map +1 -0
- package/lib/module/reconciliation.js +329 -0
- package/lib/module/reconciliation.js.map +1 -0
- package/lib/module/services/authService.js +205 -0
- package/lib/module/services/authService.js.map +1 -0
- package/lib/module/storage/secureStorage.js +89 -0
- package/lib/module/storage/secureStorage.js.map +1 -0
- package/lib/module/storage.js +16 -0
- package/lib/module/storage.js.map +1 -0
- package/lib/module/sync.js +64 -0
- package/lib/module/sync.js.map +1 -0
- package/lib/module/types/tossUser.js +41 -0
- package/lib/module/types/tossUser.js.map +1 -0
- package/lib/module/utils/nonceUtils.js +38 -0
- package/lib/module/utils/nonceUtils.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/__tests__/index.test.d.ts +1 -0
- package/lib/typescript/src/__tests__/index.test.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/reconciliation.test.d.ts +6 -0
- package/lib/typescript/src/__tests__/reconciliation.test.d.ts.map +1 -0
- package/lib/typescript/src/ble.d.ts +10 -0
- package/lib/typescript/src/ble.d.ts.map +1 -0
- package/lib/typescript/src/client/TossClient.d.ts +110 -0
- package/lib/typescript/src/client/TossClient.d.ts.map +1 -0
- package/lib/typescript/src/client/index.d.ts +3 -0
- package/lib/typescript/src/client/index.d.ts.map +1 -0
- package/lib/typescript/src/contexts/WalletContext.d.ts +20 -0
- package/lib/typescript/src/contexts/WalletContext.d.ts.map +1 -0
- package/lib/typescript/src/discovery.d.ts +188 -0
- package/lib/typescript/src/discovery.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +27 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/examples/offlinePaymentFlow.d.ts +48 -0
- package/lib/typescript/src/examples/offlinePaymentFlow.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +13 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/intent.d.ts +84 -0
- package/lib/typescript/src/intent.d.ts.map +1 -0
- package/lib/typescript/src/intentManager.d.ts +46 -0
- package/lib/typescript/src/intentManager.d.ts.map +1 -0
- package/lib/typescript/src/internal/arciumHelper.d.ts +19 -0
- package/lib/typescript/src/internal/arciumHelper.d.ts.map +1 -0
- package/lib/typescript/src/nfc.d.ts +7 -0
- package/lib/typescript/src/nfc.d.ts.map +1 -0
- package/lib/typescript/src/noise.d.ts +5 -0
- package/lib/typescript/src/noise.d.ts.map +1 -0
- package/lib/typescript/src/qr.d.ts +6 -0
- package/lib/typescript/src/qr.d.ts.map +1 -0
- package/lib/typescript/src/reconciliation.d.ts +65 -0
- package/lib/typescript/src/reconciliation.d.ts.map +1 -0
- package/lib/typescript/src/services/authService.d.ts +55 -0
- package/lib/typescript/src/services/authService.d.ts.map +1 -0
- package/lib/typescript/src/storage/secureStorage.d.ts +7 -0
- package/lib/typescript/src/storage/secureStorage.d.ts.map +1 -0
- package/lib/typescript/src/storage.d.ts +4 -0
- package/lib/typescript/src/storage.d.ts.map +1 -0
- package/lib/typescript/src/sync.d.ts +40 -0
- package/lib/typescript/src/sync.d.ts.map +1 -0
- package/lib/typescript/src/types/tossUser.d.ts +39 -0
- package/lib/typescript/src/types/tossUser.d.ts.map +1 -0
- package/lib/typescript/src/utils/nonceUtils.d.ts +8 -0
- package/lib/typescript/src/utils/nonceUtils.d.ts.map +1 -0
- package/package.json +176 -0
- package/src/__tests__/index.test.tsx +1 -0
- package/src/__tests__/reconciliation.test.tsx +361 -0
- package/src/ble.ts +138 -0
- package/src/client/TossClient.ts +435 -0
- package/src/client/index.ts +2 -0
- package/src/contexts/WalletContext.tsx +127 -0
- package/src/discovery.ts +542 -0
- package/src/errors.ts +51 -0
- package/src/examples/offlinePaymentFlow.ts +331 -0
- package/src/index.tsx +61 -0
- package/src/intent.ts +328 -0
- package/src/intentManager.ts +164 -0
- package/src/internal/arciumHelper.ts +58 -0
- package/src/nfc.ts +57 -0
- package/src/noise.ts +9 -0
- package/src/qr.tsx +65 -0
- package/src/reconciliation.ts +421 -0
- package/src/services/authService.ts +238 -0
- package/src/storage/secureStorage.ts +100 -0
- package/src/storage.ts +17 -0
- package/src/sync.ts +101 -0
- package/src/types/tossUser.ts +81 -0
- package/src/utils/nonceUtils.ts +56 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import * as SecureStore from 'expo-secure-store';
|
|
4
|
+
import { Keypair, PublicKey } from '@solana/web3.js';
|
|
5
|
+
import * as LocalAuthentication from 'expo-local-authentication';
|
|
6
|
+
import crypto from 'crypto';
|
|
7
|
+
export const SESSION_KEY = 'toss_user_session';
|
|
8
|
+
const WALLET_KEY = 'toss_encrypted_wallet';
|
|
9
|
+
const BIOMETRIC_SALT_KEY = 'toss_biometric_salt';
|
|
10
|
+
export class AuthService {
|
|
11
|
+
static async signInWithWallet(walletAddress, isTemporary = false) {
|
|
12
|
+
// In a real implementation, this would call your backend
|
|
13
|
+
const session = {
|
|
14
|
+
id: `sess_${Date.now()}`,
|
|
15
|
+
token: `token_${Math.random().toString(36).substr(2, 9)}`,
|
|
16
|
+
expiresAt: isTemporary ? Date.now() + 1000 * 60 * 60 * 24 // 24 hours for temporary
|
|
17
|
+
: Date.now() + 1000 * 60 * 60 * 24 * 30,
|
|
18
|
+
// 30 days
|
|
19
|
+
walletAddress
|
|
20
|
+
};
|
|
21
|
+
const user = {
|
|
22
|
+
userId: `user_${walletAddress.slice(0, 8)}`,
|
|
23
|
+
username: `user_${walletAddress.slice(0, 6)}`,
|
|
24
|
+
wallet: {
|
|
25
|
+
publicKey: new PublicKey(walletAddress),
|
|
26
|
+
isVerified: false,
|
|
27
|
+
createdAt: new Date().toISOString()
|
|
28
|
+
},
|
|
29
|
+
device: {
|
|
30
|
+
id: 'device_id_here',
|
|
31
|
+
// You'd get this from the device
|
|
32
|
+
lastActive: new Date().toISOString(),
|
|
33
|
+
client: 'mobile'
|
|
34
|
+
},
|
|
35
|
+
status: 'active',
|
|
36
|
+
lastSeen: new Date().toISOString(),
|
|
37
|
+
tossFeatures: {
|
|
38
|
+
canSend: true,
|
|
39
|
+
canReceive: true,
|
|
40
|
+
isPrivateTxEnabled: true,
|
|
41
|
+
maxTransactionAmount: 10000
|
|
42
|
+
},
|
|
43
|
+
createdAt: new Date().toISOString(),
|
|
44
|
+
updatedAt: new Date().toISOString()
|
|
45
|
+
};
|
|
46
|
+
await this.saveSession(session);
|
|
47
|
+
return {
|
|
48
|
+
user,
|
|
49
|
+
session
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
static async saveSession(session) {
|
|
53
|
+
await SecureStore.setItemAsync(SESSION_KEY, JSON.stringify(session));
|
|
54
|
+
}
|
|
55
|
+
static async getSession() {
|
|
56
|
+
const session = await SecureStore.getItemAsync(SESSION_KEY);
|
|
57
|
+
return session ? JSON.parse(session) : null;
|
|
58
|
+
}
|
|
59
|
+
static async signOut() {
|
|
60
|
+
await SecureStore.deleteItemAsync(SESSION_KEY);
|
|
61
|
+
await SecureStore.deleteItemAsync(WALLET_KEY);
|
|
62
|
+
}
|
|
63
|
+
static async isWalletUnlocked() {
|
|
64
|
+
const isAvailable = await SecureStore.isAvailableAsync();
|
|
65
|
+
if (!isAvailable) return false;
|
|
66
|
+
const item = await SecureStore.getItemAsync(WALLET_KEY);
|
|
67
|
+
return item !== null;
|
|
68
|
+
}
|
|
69
|
+
static async unlockWalletWithBiometrics() {
|
|
70
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
71
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
72
|
+
if (!hasHardware || !isEnrolled) {
|
|
73
|
+
throw new Error('Biometric authentication required but not available on this device');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// REQUIRED: Biometric authentication before key access
|
|
77
|
+
const result = await LocalAuthentication.authenticateAsync({
|
|
78
|
+
promptMessage: 'Biometric authentication required to access wallet',
|
|
79
|
+
fallbackLabel: 'Enter PIN',
|
|
80
|
+
disableDeviceFallback: false
|
|
81
|
+
});
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
throw new Error('Biometric authentication failed - access denied');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Only after successful biometric: retrieve encrypted keypair
|
|
87
|
+
const encrypted = await SecureStore.getItemAsync(WALLET_KEY);
|
|
88
|
+
if (!encrypted) {
|
|
89
|
+
throw new Error('Wallet not found - ensure wallet is set up first');
|
|
90
|
+
}
|
|
91
|
+
const salt = await SecureStore.getItemAsync(BIOMETRIC_SALT_KEY);
|
|
92
|
+
if (!salt) {
|
|
93
|
+
throw new Error('Wallet configuration corrupted');
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const decryptedData = JSON.parse(encrypted);
|
|
97
|
+
if (!decryptedData.publicKey || !decryptedData.secretKey) {
|
|
98
|
+
throw new Error('Invalid wallet data');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Reconstruct keypair from encrypted storage
|
|
102
|
+
const secretKeyArray = new Uint8Array(decryptedData.secretKey);
|
|
103
|
+
const keypair = Keypair.fromSecretKey(secretKeyArray);
|
|
104
|
+
|
|
105
|
+
// Verify keypair integrity
|
|
106
|
+
if (keypair.publicKey.toString() !== decryptedData.publicKey) {
|
|
107
|
+
throw new Error('Keypair verification failed - wallet may be corrupted');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Keypair returned but never exported/stored externally
|
|
111
|
+
return keypair;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
throw new Error(`Failed to unlock wallet: ${error instanceof Error ? error.message : String(error)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Setup biometric-protected wallet (REQUIRED for security)
|
|
119
|
+
*
|
|
120
|
+
* SECURITY CRITICAL:
|
|
121
|
+
* - Private keypair is encrypted and stored in hardware-secure storage
|
|
122
|
+
* - Private key NEVER accessible without biometric authentication
|
|
123
|
+
* - User CANNOT export, backup, or access seed phrase
|
|
124
|
+
* - Keypair is device-specific and non-custodial
|
|
125
|
+
*
|
|
126
|
+
* @param keypair User's Solana keypair (never re-used or exported)
|
|
127
|
+
* @param useBiometrics Must be true (biometric is mandatory, not optional)
|
|
128
|
+
*/
|
|
129
|
+
static async setupWalletProtection(keypair, useBiometrics = true) {
|
|
130
|
+
if (!useBiometrics) {
|
|
131
|
+
throw new Error('❌ SECURITY ERROR: Biometric protection is mandatory for wallet security');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Verify biometric is available on device
|
|
135
|
+
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
136
|
+
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
137
|
+
if (!hasHardware || !isEnrolled) {
|
|
138
|
+
throw new Error('❌ Biometric authentication required but not configured on device');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Generate unique salt for this wallet
|
|
142
|
+
const salt = crypto.getRandomValues(new Uint8Array(16)).toString();
|
|
143
|
+
await SecureStore.setItemAsync(BIOMETRIC_SALT_KEY, salt);
|
|
144
|
+
|
|
145
|
+
// Encrypt and store keypair in hardware-backed secure storage
|
|
146
|
+
const walletData = {
|
|
147
|
+
publicKey: keypair.publicKey.toString(),
|
|
148
|
+
secretKey: Array.from(keypair.secretKey),
|
|
149
|
+
// Serializable format only
|
|
150
|
+
createdAt: Date.now(),
|
|
151
|
+
biometricRequired: true,
|
|
152
|
+
nonCustodial: true,
|
|
153
|
+
deviceSpecific: true,
|
|
154
|
+
exportable: false // Explicitly non-exportable
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Store in Secure Enclave (iOS) or Keymaster (Android)
|
|
158
|
+
await SecureStore.setItemAsync(WALLET_KEY, JSON.stringify(walletData));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Verify wallet is stored securely (requires biometric to access)
|
|
163
|
+
* @returns true if wallet exists and requires biometric
|
|
164
|
+
*/
|
|
165
|
+
static async isKeypairStoredSecurely() {
|
|
166
|
+
const stored = await SecureStore.getItemAsync(WALLET_KEY);
|
|
167
|
+
return stored !== null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get public key only (NO AUTHENTICATION REQUIRED - public key is safe)
|
|
172
|
+
* Use this for displaying wallet address, sending funds to, etc.
|
|
173
|
+
*/
|
|
174
|
+
static async getPublicKeyWithoutAuth() {
|
|
175
|
+
try {
|
|
176
|
+
const encrypted = await SecureStore.getItemAsync(WALLET_KEY);
|
|
177
|
+
if (!encrypted) return null;
|
|
178
|
+
const data = JSON.parse(encrypted);
|
|
179
|
+
return new PublicKey(data.publicKey);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error('Failed to get public key:', error);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Lock wallet from memory (does NOT delete stored keypair)
|
|
188
|
+
* Keypair remains encrypted in secure storage
|
|
189
|
+
*/
|
|
190
|
+
static async lockWalletFromMemory() {
|
|
191
|
+
// This is handled by WalletContext clearing the keypair state
|
|
192
|
+
// The encrypted keypair stays in SecureStore
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Permanently delete wallet (IRREVERSIBLE)
|
|
197
|
+
* Only use for logout or account deletion
|
|
198
|
+
*/
|
|
199
|
+
static async deleteWalletPermanently() {
|
|
200
|
+
await SecureStore.deleteItemAsync(WALLET_KEY);
|
|
201
|
+
await SecureStore.deleteItemAsync(BIOMETRIC_SALT_KEY);
|
|
202
|
+
await SecureStore.deleteItemAsync(SESSION_KEY);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=authService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["SecureStore","Keypair","PublicKey","LocalAuthentication","crypto","SESSION_KEY","WALLET_KEY","BIOMETRIC_SALT_KEY","AuthService","signInWithWallet","walletAddress","isTemporary","session","id","Date","now","token","Math","random","toString","substr","expiresAt","user","userId","slice","username","wallet","publicKey","isVerified","createdAt","toISOString","device","lastActive","client","status","lastSeen","tossFeatures","canSend","canReceive","isPrivateTxEnabled","maxTransactionAmount","updatedAt","saveSession","setItemAsync","JSON","stringify","getSession","getItemAsync","parse","signOut","deleteItemAsync","isWalletUnlocked","isAvailable","isAvailableAsync","item","unlockWalletWithBiometrics","hasHardware","hasHardwareAsync","isEnrolled","isEnrolledAsync","Error","result","authenticateAsync","promptMessage","fallbackLabel","disableDeviceFallback","success","encrypted","salt","decryptedData","secretKey","secretKeyArray","Uint8Array","keypair","fromSecretKey","error","message","String","setupWalletProtection","useBiometrics","getRandomValues","walletData","Array","from","biometricRequired","nonCustodial","deviceSpecific","exportable","isKeypairStoredSecurely","stored","getPublicKeyWithoutAuth","data","console","lockWalletFromMemory","deleteWalletPermanently"],"sourceRoot":"../../../src","sources":["services/authService.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import * as SecureStore from 'expo-secure-store';
|
|
4
|
+
import { StorageError } from "../errors.js";
|
|
5
|
+
const STORAGE_PREFIX = 'toss_intent_';
|
|
6
|
+
|
|
7
|
+
// Helper function to get all keys
|
|
8
|
+
async function getAllKeys() {
|
|
9
|
+
// expo-secure-store doesn't have a direct way to get all keys,
|
|
10
|
+
// so we'll need to track them manually
|
|
11
|
+
const keys = await SecureStore.getItemAsync(`${STORAGE_PREFIX}_keys`);
|
|
12
|
+
return keys ? JSON.parse(keys) : [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Helper function to save all keys
|
|
16
|
+
async function saveKeys(keys) {
|
|
17
|
+
await SecureStore.setItemAsync(`${STORAGE_PREFIX}_keys`, JSON.stringify(keys));
|
|
18
|
+
}
|
|
19
|
+
export async function secureStoreIntent(intent) {
|
|
20
|
+
try {
|
|
21
|
+
const key = `${STORAGE_PREFIX}${intent.id}`;
|
|
22
|
+
await SecureStore.setItemAsync(key, JSON.stringify(intent));
|
|
23
|
+
|
|
24
|
+
// Update the keys list
|
|
25
|
+
const keys = await getAllKeys();
|
|
26
|
+
if (!keys.includes(key)) {
|
|
27
|
+
keys.push(key);
|
|
28
|
+
await saveKeys(keys);
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new StorageError('Failed to store intent securely', {
|
|
32
|
+
cause: error,
|
|
33
|
+
intentId: intent.id
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function getSecureIntent(intentId) {
|
|
38
|
+
try {
|
|
39
|
+
const value = await SecureStore.getItemAsync(`${STORAGE_PREFIX}${intentId}`);
|
|
40
|
+
return value ? JSON.parse(value) : null;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new StorageError('Failed to retrieve intent', {
|
|
43
|
+
cause: error,
|
|
44
|
+
intentId
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function getAllSecureIntents() {
|
|
49
|
+
try {
|
|
50
|
+
const keys = await getAllKeys();
|
|
51
|
+
const intents = await Promise.all(keys.map(async key => {
|
|
52
|
+
const value = await SecureStore.getItemAsync(key);
|
|
53
|
+
return value ? JSON.parse(value) : null;
|
|
54
|
+
}));
|
|
55
|
+
return intents.filter(Boolean);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new StorageError('Failed to retrieve all intents', {
|
|
58
|
+
cause: error
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function removeSecureIntent(intentId) {
|
|
63
|
+
try {
|
|
64
|
+
const key = `${STORAGE_PREFIX}${intentId}`;
|
|
65
|
+
await SecureStore.deleteItemAsync(key);
|
|
66
|
+
|
|
67
|
+
// Update the keys list
|
|
68
|
+
const keys = await getAllKeys();
|
|
69
|
+
const updatedKeys = keys.filter(k => k !== key);
|
|
70
|
+
await saveKeys(updatedKeys);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new StorageError('Failed to remove intent', {
|
|
73
|
+
cause: error,
|
|
74
|
+
intentId
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export async function clearAllSecureIntents() {
|
|
79
|
+
try {
|
|
80
|
+
const keys = await getAllKeys();
|
|
81
|
+
await Promise.all(keys.map(key => SecureStore.deleteItemAsync(key)));
|
|
82
|
+
await saveKeys([]);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new StorageError('Failed to clear all intents', {
|
|
85
|
+
cause: error
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=secureStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["SecureStore","StorageError","STORAGE_PREFIX","getAllKeys","keys","getItemAsync","JSON","parse","saveKeys","setItemAsync","stringify","secureStoreIntent","intent","key","id","includes","push","error","cause","intentId","getSecureIntent","value","getAllSecureIntents","intents","Promise","all","map","filter","Boolean","removeSecureIntent","deleteItemAsync","updatedKeys","k","clearAllSecureIntents"],"sourceRoot":"../../../src","sources":["storage/secureStorage.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
4
|
+
const INTENTS_KEY = "TOSS_PENDING_INTENTS";
|
|
5
|
+
export async function storePendingIntent(intent) {
|
|
6
|
+
const current = JSON.parse((await AsyncStorage.getItem(INTENTS_KEY)) || "[]");
|
|
7
|
+
current.push(intent);
|
|
8
|
+
await AsyncStorage.setItem(INTENTS_KEY, JSON.stringify(current));
|
|
9
|
+
}
|
|
10
|
+
export async function getPendingIntents() {
|
|
11
|
+
return JSON.parse((await AsyncStorage.getItem(INTENTS_KEY)) || "[]");
|
|
12
|
+
}
|
|
13
|
+
export async function clearPendingIntents() {
|
|
14
|
+
await AsyncStorage.removeItem(INTENTS_KEY);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["AsyncStorage","INTENTS_KEY","storePendingIntent","intent","current","JSON","parse","getItem","push","setItem","stringify","getPendingIntents","clearPendingIntents","removeItem"],"sourceRoot":"../../src","sources":["storage.ts"],"mappings":";;AAAA,OAAOA,YAAY,MAAM,2CAA2C;AAEpE,MAAMC,WAAW,GAAG,sBAAsB;AAE1C,OAAO,eAAeC,kBAAkBA,CAACC,MAAW,EAAE;EACpD,MAAMC,OAAO,GAAGC,IAAI,CAACC,KAAK,CAAC,CAAC,MAAMN,YAAY,CAACO,OAAO,CAACN,WAAW,CAAC,KAAK,IAAI,CAAC;EAC7EG,OAAO,CAACI,IAAI,CAACL,MAAM,CAAC;EACpB,MAAMH,YAAY,CAACS,OAAO,CAACR,WAAW,EAAEI,IAAI,CAACK,SAAS,CAACN,OAAO,CAAC,CAAC;AAClE;AAEA,OAAO,eAAeO,iBAAiBA,CAAA,EAAG;EACxC,OAAON,IAAI,CAACC,KAAK,CAAC,CAAC,MAAMN,YAAY,CAACO,OAAO,CAACN,WAAW,CAAC,KAAK,IAAI,CAAC;AACtE;AAEA,OAAO,eAAeW,mBAAmBA,CAAA,EAAG;EAC1C,MAAMZ,YAAY,CAACa,UAAU,CAACZ,WAAW,CAAC;AAC5C","ignoreList":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Synchronisation with Solana Blockchain
|
|
5
|
+
*
|
|
6
|
+
* Implements Section 9 of the TOSS Technical Paper:
|
|
7
|
+
* Upon regaining connectivity, devices initiate reconciliation with onchain state.
|
|
8
|
+
* All offline artifacts are verified onchain and settled with deterministic outcomes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { reconcilePendingIntents, detectConflicts, getReconciliationState } from "./reconciliation.js";
|
|
12
|
+
import { NetworkError } from "./errors.js";
|
|
13
|
+
/**
|
|
14
|
+
* Full sync and reconciliation with the Solana blockchain
|
|
15
|
+
*
|
|
16
|
+
* This is the primary function for TOSS settlement. When a device regains
|
|
17
|
+
* connectivity, it calls this to:
|
|
18
|
+
* 1. Detect any conflicts with onchain state
|
|
19
|
+
* 2. Settle all pending intents
|
|
20
|
+
* 3. Update local state with results
|
|
21
|
+
*
|
|
22
|
+
* @param connection Connection to Solana RPC
|
|
23
|
+
* @param feePayer Optional fee payer keypair public key
|
|
24
|
+
* @returns Detailed sync results including conflicts and settlements
|
|
25
|
+
*/
|
|
26
|
+
export async function syncToChain(connection, feePayer) {
|
|
27
|
+
const syncTimestamp = Math.floor(Date.now() / 1000);
|
|
28
|
+
try {
|
|
29
|
+
// Step 1: Detect any conflicts with onchain state
|
|
30
|
+
const detectedConflicts = await detectConflicts(connection);
|
|
31
|
+
|
|
32
|
+
// Step 2: Reconcile and settle all pending intents
|
|
33
|
+
const allSettlementResults = await reconcilePendingIntents(connection, feePayer);
|
|
34
|
+
|
|
35
|
+
// Step 3: Separate successful and failed settlements
|
|
36
|
+
const successfulSettlements = allSettlementResults.filter(r => r.status === 'success');
|
|
37
|
+
const failedSettlements = allSettlementResults.filter(r => r.status !== 'success');
|
|
38
|
+
|
|
39
|
+
// Step 4: Get final reconciliation state
|
|
40
|
+
const reconciliationState = await getReconciliationState(connection);
|
|
41
|
+
const isComplete = failedSettlements.length === 0 && detectedConflicts.length === 0;
|
|
42
|
+
return {
|
|
43
|
+
successfulSettlements,
|
|
44
|
+
failedSettlements,
|
|
45
|
+
detectedConflicts,
|
|
46
|
+
reconciliationState,
|
|
47
|
+
syncTimestamp,
|
|
48
|
+
isComplete
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new NetworkError(`Sync to chain failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
52
|
+
cause: error
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Lightweight sync to check status without settling
|
|
59
|
+
* Useful for monitoring or UI updates without committing to settlements
|
|
60
|
+
*/
|
|
61
|
+
export async function checkSyncStatus(connection) {
|
|
62
|
+
return getReconciliationState(connection);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["reconcilePendingIntents","detectConflicts","getReconciliationState","NetworkError","syncToChain","connection","feePayer","syncTimestamp","Math","floor","Date","now","detectedConflicts","allSettlementResults","successfulSettlements","filter","r","status","failedSettlements","reconciliationState","isComplete","length","error","Error","message","String","cause","checkSyncStatus"],"sourceRoot":"../../src","sources":["sync.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA,SACEA,uBAAuB,EACvBC,eAAe,EACfC,sBAAsB,QAGjB,qBAAkB;AACzB,SAASC,YAAY,QAAQ,aAAU;AAiBvC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,WAAWA,CAC/BC,UAAsB,EACtBC,QAAoB,EACC;EACrB,MAAMC,aAAa,GAAGC,IAAI,CAACC,KAAK,CAACC,IAAI,CAACC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;EAEnD,IAAI;IACF;IACA,MAAMC,iBAAiB,GAAG,MAAMX,eAAe,CAACI,UAAU,CAAC;;IAE3D;IACA,MAAMQ,oBAAoB,GAAG,MAAMb,uBAAuB,CACxDK,UAAU,EACVC,QACF,CAAC;;IAED;IACA,MAAMQ,qBAAqB,GAAGD,oBAAoB,CAACE,MAAM,CACtDC,CAAC,IAAKA,CAAC,CAACC,MAAM,KAAK,SACtB,CAAC;IACD,MAAMC,iBAAiB,GAAGL,oBAAoB,CAACE,MAAM,CAClDC,CAAC,IAAKA,CAAC,CAACC,MAAM,KAAK,SACtB,CAAC;;IAED;IACA,MAAME,mBAAmB,GAAG,MAAMjB,sBAAsB,CAACG,UAAU,CAAC;IAEpE,MAAMe,UAAU,GACdF,iBAAiB,CAACG,MAAM,KAAK,CAAC,IAAIT,iBAAiB,CAACS,MAAM,KAAK,CAAC;IAElE,OAAO;MACLP,qBAAqB;MACrBI,iBAAiB;MACjBN,iBAAiB;MACjBO,mBAAmB;MACnBZ,aAAa;MACba;IACF,CAAC;EACH,CAAC,CAAC,OAAOE,KAAK,EAAE;IACd,MAAM,IAAInB,YAAY,CACpB,yBAAyBmB,KAAK,YAAYC,KAAK,GAAGD,KAAK,CAACE,OAAO,GAAGC,MAAM,CAACH,KAAK,CAAC,EAAE,EACjF;MAAEI,KAAK,EAAEJ;IAAM,CACjB,CAAC;EACH;AACF;;AAEA;AACA;AACA;AACA;AACA,OAAO,eAAeK,eAAeA,CACnCtB,UAAsB,EACQ;EAC9B,OAAOH,sBAAsB,CAACG,UAAU,CAAC;AAC3C","ignoreList":[]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { PublicKey } from '@solana/web3.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a TOSS wallet user in the ecosystem
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Minimal user info for transaction context
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Example usage
|
|
14
|
+
export const exampleTossUser = {
|
|
15
|
+
userId: 'toss_abc123',
|
|
16
|
+
username: '@alice',
|
|
17
|
+
displayName: 'Alice',
|
|
18
|
+
wallet: {
|
|
19
|
+
publicKey: new PublicKey('11111111111111111111111111111111'),
|
|
20
|
+
// Example key
|
|
21
|
+
isVerified: true,
|
|
22
|
+
createdAt: '2023-01-01T00:00:00Z'
|
|
23
|
+
},
|
|
24
|
+
device: {
|
|
25
|
+
id: 'dev_xyz789',
|
|
26
|
+
name: 'Alice iPhone',
|
|
27
|
+
lastActive: new Date().toISOString(),
|
|
28
|
+
client: 'mobile'
|
|
29
|
+
},
|
|
30
|
+
status: 'active',
|
|
31
|
+
lastSeen: new Date().toISOString(),
|
|
32
|
+
tossFeatures: {
|
|
33
|
+
canSend: true,
|
|
34
|
+
canReceive: true,
|
|
35
|
+
isPrivateTxEnabled: true,
|
|
36
|
+
maxTransactionAmount: 1000000000 // 1 SOL in lamports
|
|
37
|
+
},
|
|
38
|
+
createdAt: '2023-01-01T00:00:00Z',
|
|
39
|
+
updatedAt: new Date().toISOString()
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=tossUser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["PublicKey","exampleTossUser","userId","username","displayName","wallet","publicKey","isVerified","createdAt","device","id","name","lastActive","Date","toISOString","client","status","lastSeen","tossFeatures","canSend","canReceive","isPrivateTxEnabled","maxTransactionAmount","updatedAt"],"sourceRoot":"../../../src","sources":["types/tossUser.ts"],"mappings":";;AAAA,SAASA,SAAS,QAAQ,iBAAiB;;AAE3C;AACA;AACA;;AAuCA;AACA;AACA;;AASA;AACA,OAAO,MAAMC,eAAyB,GAAG;EACvCC,MAAM,EAAE,aAAa;EACrBC,QAAQ,EAAE,QAAQ;EAClBC,WAAW,EAAE,OAAO;EACpBC,MAAM,EAAE;IACNC,SAAS,EAAE,IAAIN,SAAS,CAAC,kCAAkC,CAAC;IAAE;IAC9DO,UAAU,EAAE,IAAI;IAChBC,SAAS,EAAE;EACb,CAAC;EACDC,MAAM,EAAE;IACNC,EAAE,EAAE,YAAY;IAChBC,IAAI,EAAE,cAAc;IACpBC,UAAU,EAAE,IAAIC,IAAI,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC;IACpCC,MAAM,EAAE;EACV,CAAC;EACDC,MAAM,EAAE,QAAQ;EAChBC,QAAQ,EAAE,IAAIJ,IAAI,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC;EAClCI,YAAY,EAAE;IACZC,OAAO,EAAE,IAAI;IACbC,UAAU,EAAE,IAAI;IAChBC,kBAAkB,EAAE,IAAI;IACxBC,oBAAoB,EAAE,UAAU,CAAE;EACpC,CAAC;EACDd,SAAS,EAAE,sBAAsB;EACjCe,SAAS,EAAE,IAAIV,IAAI,CAAC,CAAC,CAACC,WAAW,CAAC;AACpC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { SystemProgram, Transaction, Keypair, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
4
|
+
export async function createNonceAccount(connection, feePayer, nonceAccount = Keypair.generate(), amount = 1 * LAMPORTS_PER_SOL // 1 SOL should be enough for many transactions
|
|
5
|
+
) {
|
|
6
|
+
const nonceAuth = feePayer.publicKey;
|
|
7
|
+
const tx = new Transaction().add(SystemProgram.createAccount({
|
|
8
|
+
fromPubkey: feePayer.publicKey,
|
|
9
|
+
newAccountPubkey: nonceAccount.publicKey,
|
|
10
|
+
lamports: amount,
|
|
11
|
+
space: 80,
|
|
12
|
+
// Size of nonce account
|
|
13
|
+
programId: SystemProgram.programId
|
|
14
|
+
}), SystemProgram.nonceInitialize({
|
|
15
|
+
noncePubkey: nonceAccount.publicKey,
|
|
16
|
+
authorizedPubkey: nonceAuth
|
|
17
|
+
}));
|
|
18
|
+
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
19
|
+
tx.feePayer = feePayer.publicKey;
|
|
20
|
+
tx.sign(feePayer, nonceAccount);
|
|
21
|
+
await connection.sendRawTransaction(tx.serialize());
|
|
22
|
+
return {
|
|
23
|
+
nonceAccount,
|
|
24
|
+
nonceAuth
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export async function getNonce(connection, nonceAccount) {
|
|
28
|
+
const accountInfo = await connection.getAccountInfo(nonceAccount);
|
|
29
|
+
if (!accountInfo) throw new Error('Nonce account not found');
|
|
30
|
+
return accountInfo.data.slice(32, 64).toString('hex');
|
|
31
|
+
}
|
|
32
|
+
export function createNonceAdvanceInstruction(noncePubkey, authorizedPubkey) {
|
|
33
|
+
return SystemProgram.nonceAdvance({
|
|
34
|
+
noncePubkey,
|
|
35
|
+
authorizedPubkey
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=nonceUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["SystemProgram","Transaction","Keypair","LAMPORTS_PER_SOL","createNonceAccount","connection","feePayer","nonceAccount","generate","amount","nonceAuth","publicKey","tx","add","createAccount","fromPubkey","newAccountPubkey","lamports","space","programId","nonceInitialize","noncePubkey","authorizedPubkey","recentBlockhash","getLatestBlockhash","blockhash","sign","sendRawTransaction","serialize","getNonce","accountInfo","getAccountInfo","Error","data","slice","toString","createNonceAdvanceInstruction","nonceAdvance"],"sourceRoot":"../../../src","sources":["utils/nonceUtils.ts"],"mappings":";;AAAA,SACEA,aAAa,EAEbC,WAAW,EAEXC,OAAO,EACPC,gBAAgB,QACX,iBAAiB;AAExB,OAAO,eAAeC,kBAAkBA,CACtCC,UAAsB,EACtBC,QAAiB,EACjBC,YAAqB,GAAGL,OAAO,CAACM,QAAQ,CAAC,CAAC,EAC1CC,MAAM,GAAG,CAAC,GAAGN,gBAAgB,CAAC;AAAA,EAC4B;EAC1D,MAAMO,SAAS,GAAGJ,QAAQ,CAACK,SAAS;EACpC,MAAMC,EAAE,GAAG,IAAIX,WAAW,CAAC,CAAC,CAACY,GAAG,CAC9Bb,aAAa,CAACc,aAAa,CAAC;IAC1BC,UAAU,EAAET,QAAQ,CAACK,SAAS;IAC9BK,gBAAgB,EAAET,YAAY,CAACI,SAAS;IACxCM,QAAQ,EAAER,MAAM;IAChBS,KAAK,EAAE,EAAE;IAAE;IACXC,SAAS,EAAEnB,aAAa,CAACmB;EAC3B,CAAC,CAAC,EACFnB,aAAa,CAACoB,eAAe,CAAC;IAC5BC,WAAW,EAAEd,YAAY,CAACI,SAAS;IACnCW,gBAAgB,EAAEZ;EACpB,CAAC,CACH,CAAC;EAEDE,EAAE,CAACW,eAAe,GAAG,CAAC,MAAMlB,UAAU,CAACmB,kBAAkB,CAAC,CAAC,EAAEC,SAAS;EACtEb,EAAE,CAACN,QAAQ,GAAGA,QAAQ,CAACK,SAAS;EAChCC,EAAE,CAACc,IAAI,CAACpB,QAAQ,EAAEC,YAAY,CAAC;EAE/B,MAAMF,UAAU,CAACsB,kBAAkB,CAACf,EAAE,CAACgB,SAAS,CAAC,CAAC,CAAC;EACnD,OAAO;IAAErB,YAAY;IAAEG;EAAU,CAAC;AACpC;AAEA,OAAO,eAAemB,QAAQA,CAC5BxB,UAAsB,EACtBE,YAAuB,EACN;EACjB,MAAMuB,WAAW,GAAG,MAAMzB,UAAU,CAAC0B,cAAc,CAACxB,YAAY,CAAC;EACjE,IAAI,CAACuB,WAAW,EAAE,MAAM,IAAIE,KAAK,CAAC,yBAAyB,CAAC;EAC5D,OAAOF,WAAW,CAACG,IAAI,CAACC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAACC,QAAQ,CAAC,KAAK,CAAC;AACvD;AAEA,OAAO,SAASC,6BAA6BA,CAC3Cf,WAAsB,EACtBC,gBAA2B,EAC3B;EACA,OAAOtB,aAAa,CAACqC,YAAY,CAAC;IAChChB,WAAW;IACXC;EACF,CAAC,CAAC;AACJ","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=index.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/index.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconciliation.test.d.ts","sourceRoot":"","sources":["../../../../src/__tests__/reconciliation.test.tsx"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Device } from 'react-native-ble-plx';
|
|
2
|
+
import type { TossUser } from './types/tossUser';
|
|
3
|
+
import type { SolanaIntent } from './intent';
|
|
4
|
+
export declare function requestBLEPermissions(): Promise<void>;
|
|
5
|
+
export declare function startTossScan(onUserFound: (user: TossUser, device: Device) => void, onIntentFound: (intent: SolanaIntent, device: Device) => void): void;
|
|
6
|
+
export declare function advertiseUser(user: TossUser): Promise<void>;
|
|
7
|
+
export declare function stopAdvertising(): Promise<void>;
|
|
8
|
+
export declare function sendIntentToDevice(deviceId: string, intent: SolanaIntent): Promise<void>;
|
|
9
|
+
export declare function stopScan(): void;
|
|
10
|
+
//# sourceMappingURL=ble.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ble.d.ts","sourceRoot":"","sources":["../../../src/ble.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAQ7C,wBAAsB,qBAAqB,kBAQ1C;AAUD,wBAAgB,aAAa,CAC3B,WAAW,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,EACrD,aAAa,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,QA0C9D;AAKD,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,iBAuBjD;AAED,wBAAsB,eAAe,kBASpC;AAGD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,iBAcrB;AAGD,wBAAgB,QAAQ,SAEvB"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Keypair, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import type { SolanaIntent, IntentStatus } from '../intent';
|
|
3
|
+
import { type SyncResult } from '../sync';
|
|
4
|
+
export type TossConfig = {
|
|
5
|
+
projectId: string;
|
|
6
|
+
mode?: 'devnet' | 'testnet' | 'mainnet-beta';
|
|
7
|
+
privateTransactions?: boolean;
|
|
8
|
+
provider?: any;
|
|
9
|
+
sync?: {
|
|
10
|
+
syncBackupDb?: boolean;
|
|
11
|
+
dbUrl?: string;
|
|
12
|
+
};
|
|
13
|
+
rpcUrl?: string;
|
|
14
|
+
maxRetries?: number;
|
|
15
|
+
retryDelay?: number;
|
|
16
|
+
feePayer?: Keypair;
|
|
17
|
+
};
|
|
18
|
+
export declare class TossClient {
|
|
19
|
+
private connection;
|
|
20
|
+
private config;
|
|
21
|
+
private nonceAccount?;
|
|
22
|
+
private nonceAuth?;
|
|
23
|
+
private walletContext;
|
|
24
|
+
static createClient(config: TossConfig): TossClient;
|
|
25
|
+
private constructor();
|
|
26
|
+
private getDefaultRpcUrl;
|
|
27
|
+
private withRetry;
|
|
28
|
+
/**
|
|
29
|
+
* Initialize a nonce account for durable transactions
|
|
30
|
+
* @param amount SOL amount to fund the nonce account with (default: 1 SOL)
|
|
31
|
+
* @returns Object containing nonce account and authority public keys
|
|
32
|
+
*/
|
|
33
|
+
initializeNonceAccount(amount?: number): Promise<{
|
|
34
|
+
nonceAccount: string;
|
|
35
|
+
nonceAuth: string;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Get the current nonce value from the nonce account
|
|
39
|
+
* @returns The current nonce value as a base58 string
|
|
40
|
+
*/
|
|
41
|
+
getCurrentNonce(): Promise<string>;
|
|
42
|
+
createIntent(sender: Keypair | 'current', recipient: PublicKey | string, amount: number, feePayer?: PublicKey | string, options?: {
|
|
43
|
+
expiresIn?: number;
|
|
44
|
+
nonce?: number;
|
|
45
|
+
useDurableNonce?: boolean;
|
|
46
|
+
memo?: string;
|
|
47
|
+
}): Promise<SolanaIntent>;
|
|
48
|
+
getIntents(): Promise<SolanaIntent[]>;
|
|
49
|
+
updateIntentStatus(intentId: string, status: IntentStatus, error?: string): Promise<SolanaIntent | null>;
|
|
50
|
+
sync(): Promise<SolanaIntent[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Full synchronisation with Solana blockchain (TOSS Section 9)
|
|
53
|
+
*
|
|
54
|
+
* Performs complete reconciliation:
|
|
55
|
+
* - Detects conflicts with onchain state
|
|
56
|
+
* - Settles all pending intents
|
|
57
|
+
* - Updates local state deterministically
|
|
58
|
+
*
|
|
59
|
+
* @returns Complete sync results including settlements, conflicts, and final state
|
|
60
|
+
*/
|
|
61
|
+
fullSync(): Promise<SyncResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Check synchronisation status without settling
|
|
64
|
+
*
|
|
65
|
+
* Lightweight operation to query current reconciliation state
|
|
66
|
+
* without committing any settlements to the blockchain.
|
|
67
|
+
*/
|
|
68
|
+
checkSyncStatus(): Promise<import("..").ReconciliationState>;
|
|
69
|
+
/**
|
|
70
|
+
* Detect conflicts between local intents and onchain state
|
|
71
|
+
*
|
|
72
|
+
* Useful for monitoring and alerting users to potential issues
|
|
73
|
+
* before attempting settlement.
|
|
74
|
+
*/
|
|
75
|
+
detectIntentConflicts(): Promise<{
|
|
76
|
+
intentId: string;
|
|
77
|
+
conflict: string;
|
|
78
|
+
}[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Get current reconciliation state
|
|
81
|
+
*
|
|
82
|
+
* Returns summary of processed, failed, and conflicting intents
|
|
83
|
+
* for UI updates or logging.
|
|
84
|
+
*/
|
|
85
|
+
getReconciliationStatus(): Promise<import("..").ReconciliationState>;
|
|
86
|
+
/**
|
|
87
|
+
* Create an intent from the current user's wallet
|
|
88
|
+
*/
|
|
89
|
+
createUserIntent(recipient: PublicKey | string, amount: number, options?: {
|
|
90
|
+
memo?: string;
|
|
91
|
+
useDurableNonce?: boolean;
|
|
92
|
+
}): Promise<SolanaIntent>;
|
|
93
|
+
/**
|
|
94
|
+
* Get the current user's wallet address
|
|
95
|
+
*/
|
|
96
|
+
getCurrentUserAddress(): string | null;
|
|
97
|
+
/**
|
|
98
|
+
* Check if the wallet is currently unlocked
|
|
99
|
+
*/
|
|
100
|
+
isWalletUnlocked(): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Lock the wallet
|
|
103
|
+
*/
|
|
104
|
+
lockWallet(): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Sign out the current user
|
|
107
|
+
*/
|
|
108
|
+
signOut(): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=TossClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TossClient.d.ts","sourceRoot":"","sources":["../../../../src/client/TossClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAW5D,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAGxE,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,cAAc,CAAC;IAC7C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,IAAI,CAAC,EAAE;QACL,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAOF,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAGZ;IACF,OAAO,CAAC,YAAY,CAAC,CAAU;IAC/B,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,aAAa,CAA+B;IAEpD,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU;IAInD,OAAO;IAoBP,OAAO,CAAC,gBAAgB;YASV,SAAS;IAgCvB;;;;OAIG;IACG,sBAAsB,CAC1B,MAAM,SAAI,GACT,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAuBvD;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IASlC,YAAY,CAChB,MAAM,EAAE,OAAO,GAAG,SAAS,EAC3B,SAAS,EAAE,SAAS,GAAG,MAAM,EAC7B,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,EAC7B,OAAO,GAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;KACV,GACL,OAAO,CAAC,YAAY,CAAC;IAmElB,UAAU,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAWrC,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,EACpB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IA6BzB,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IA4BrC;;;;;;;;;OASG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAcrC;;;;;OAKG;IACG,eAAe;IAMrB;;;;;OAKG;IACG,qBAAqB;;;;IAM3B;;;;;OAKG;IACG,uBAAuB;IAM7B;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,SAAS,GAAG,MAAM,EAC7B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,eAAe,CAAC,EAAE,OAAO,CAAC;KACtB,GACL,OAAO,CAAC,YAAY,CAAC;IAkCxB;;OAEG;IACH,qBAAqB,IAAI,MAAM,GAAG,IAAI;IAItC;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|