shogun-core 0.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 +71 -0
- package/dist/auth/credentialAuth.js +154 -0
- package/dist/auth/metamaskAuth.js +264 -0
- package/dist/auth/webauthnAuth.js +267 -0
- package/dist/config.js +39 -0
- package/dist/connector/metamask.js +262 -0
- package/dist/events.js +12 -0
- package/dist/gun/auth.js +523 -0
- package/dist/gun/errors.js +66 -0
- package/dist/gun/gun.js +331 -0
- package/dist/index.js +440 -0
- package/dist/mom/MOMClient.js +1253 -0
- package/dist/stealth/stealth.js +289 -0
- package/dist/storage/storage.js +93 -0
- package/dist/types/auth/credentialAuth.d.ts +56 -0
- package/dist/types/auth/metamaskAuth.d.ts +74 -0
- package/dist/types/auth/webauthnAuth.d.ts +83 -0
- package/dist/types/auth.js +1 -0
- package/dist/types/config.d.ts +39 -0
- package/dist/types/connector/metamask.d.ts +112 -0
- package/dist/types/events.d.ts +27 -0
- package/dist/types/gun/auth.d.ts +219 -0
- package/dist/types/gun/errors.d.ts +42 -0
- package/dist/types/gun/gun.d.ts +124 -0
- package/dist/types/gun.js +4 -0
- package/dist/types/index.d.ts +173 -0
- package/dist/types/mom/MOMClient.d.ts +217 -0
- package/dist/types/mom.js +29 -0
- package/dist/types/shogun.js +1 -0
- package/dist/types/stealth/stealth.d.ts +67 -0
- package/dist/types/storage/storage.d.ts +11 -0
- package/dist/types/token.js +1 -0
- package/dist/types/types/auth.d.ts +47 -0
- package/dist/types/types/gun.d.ts +73 -0
- package/dist/types/types/mom.d.ts +147 -0
- package/dist/types/types/shogun.d.ts +90 -0
- package/dist/types/types/token.d.ts +12 -0
- package/dist/types/utils/eventEmitter.d.ts +9 -0
- package/dist/types/utils/logger.d.ts +24 -0
- package/dist/types/utils/storageMock.d.ts +12 -0
- package/dist/types/utils/utility.d.ts +20 -0
- package/dist/types/utils/wait.d.ts +24 -0
- package/dist/types/wallet/gunWallet.d.ts +14 -0
- package/dist/types/wallet/hdWallet.d.ts +154 -0
- package/dist/types/wallet/walletManager-old.d.ts +70 -0
- package/dist/types/wallet/walletManager.d.ts +188 -0
- package/dist/types/webauthn/webauthn-gun.d.ts +1 -0
- package/dist/types/webauthn/webauthn.d.ts +52 -0
- package/dist/utils/eventEmitter.js +29 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/storageMock.js +27 -0
- package/dist/utils/utility.js +32 -0
- package/dist/utils/wait.js +78 -0
- package/dist/wallet/gunWallet.js +14 -0
- package/dist/wallet/hdWallet.js +619 -0
- package/dist/wallet/walletManager-old.js +473 -0
- package/dist/wallet/walletManager.js +1226 -0
- package/dist/webauthn/webauthn-gun.js +115 -0
- package/dist/webauthn/webauthn.js +313 -0
- package/package.json +48 -0
|
@@ -0,0 +1,1226 @@
|
|
|
1
|
+
import { ethers } from "ethers";
|
|
2
|
+
import { log } from "../utils/logger";
|
|
3
|
+
import SEA from "gun/sea";
|
|
4
|
+
/**
|
|
5
|
+
* Classe che gestisce le funzionalità dei wallet
|
|
6
|
+
*/
|
|
7
|
+
export class WalletManager {
|
|
8
|
+
constructor(gundb, gun, storage) {
|
|
9
|
+
this.walletPaths = {};
|
|
10
|
+
this.mainWallet = null;
|
|
11
|
+
this.balanceCache = new Map();
|
|
12
|
+
this.balanceCacheTTL = 30000; // 30 secondi di cache
|
|
13
|
+
this.defaultRpcUrl = "https://mainnet.infura.io/v3/your-project-id";
|
|
14
|
+
this.configuredRpcUrl = null;
|
|
15
|
+
this.gundb = gundb;
|
|
16
|
+
this.gun = gun;
|
|
17
|
+
this.storage = storage;
|
|
18
|
+
this.initializeWalletPaths();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Configura l'URL RPC da utilizzare per le connessioni
|
|
22
|
+
* @param rpcUrl URL del provider RPC
|
|
23
|
+
*/
|
|
24
|
+
setRpcUrl(rpcUrl) {
|
|
25
|
+
this.configuredRpcUrl = rpcUrl;
|
|
26
|
+
log(`Provider RPC configurato: ${rpcUrl}`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Ottiene un provider JSON RPC configurato
|
|
30
|
+
* @returns Provider JSON RPC
|
|
31
|
+
*/
|
|
32
|
+
getProvider() {
|
|
33
|
+
return new ethers.JsonRpcProvider(this.configuredRpcUrl || this.defaultRpcUrl);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Inizializza i paths dei wallet
|
|
37
|
+
* Carica i paths sia da GUN che da localStorage
|
|
38
|
+
* @private
|
|
39
|
+
*/
|
|
40
|
+
async initializeWalletPaths() {
|
|
41
|
+
try {
|
|
42
|
+
// Reset dei path esistenti
|
|
43
|
+
this.walletPaths = {};
|
|
44
|
+
// Carica i path da Gun
|
|
45
|
+
await this.loadWalletPathsFromGun();
|
|
46
|
+
// Carica i path da localStorage come fallback
|
|
47
|
+
await this.loadWalletPathsFromLocalStorage();
|
|
48
|
+
// Logga il numero di wallet caricati
|
|
49
|
+
const walletCount = Object.keys(this.walletPaths).length;
|
|
50
|
+
if (walletCount === 0) {
|
|
51
|
+
log("Nessun wallet path trovato, verranno creati nuovi wallet quando necessario");
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
log(`Inizializzati ${walletCount} wallet paths`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error("Errore durante l'inizializzazione dei wallet paths:", error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Carica i path dei wallet da Gun
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
async loadWalletPathsFromGun() {
|
|
66
|
+
// 1. Prima tentiamo di caricare da GUN se l'utente è autenticato
|
|
67
|
+
const user = this.gun.user();
|
|
68
|
+
if (!user || !user.is) {
|
|
69
|
+
log("Utente non autenticato su Gun, non è possibile caricare i wallet paths da Gun");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
log(`Caricamento wallet paths da GUN per l'utente: ${user.is.alias}`);
|
|
73
|
+
// Carica i paths dal profilo dell'utente
|
|
74
|
+
const walletPaths = await new Promise((resolve) => {
|
|
75
|
+
user.get("wallet_paths").once((data) => {
|
|
76
|
+
if (!data) {
|
|
77
|
+
log("Nessun wallet path trovato in GUN");
|
|
78
|
+
resolve({});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
log(`Trovati wallet paths in GUN: ${Object.keys(data).length - 1} wallet`); // -1 per il campo _
|
|
82
|
+
resolve(data || {});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
// Converti i dati ricevuti da GUN in walletPaths
|
|
87
|
+
for (const [address, pathData] of Object.entries(walletPaths)) {
|
|
88
|
+
if (address !== "_" && pathData) {
|
|
89
|
+
// Verifica che pathData sia un oggetto con i campi richiesti
|
|
90
|
+
const data = pathData;
|
|
91
|
+
if (data.path) {
|
|
92
|
+
this.walletPaths[address] = {
|
|
93
|
+
path: data.path,
|
|
94
|
+
created: data.created || Date.now(),
|
|
95
|
+
};
|
|
96
|
+
log(`Caricato path per wallet: ${address} -> ${data.path}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Carica i path dei wallet da localStorage
|
|
103
|
+
* @private
|
|
104
|
+
*/
|
|
105
|
+
async loadWalletPathsFromLocalStorage() {
|
|
106
|
+
const storageKey = `shogun_wallet_paths_${this.getStorageUserIdentifier()}`;
|
|
107
|
+
const storedPaths = this.storage.getItem(storageKey);
|
|
108
|
+
if (storedPaths) {
|
|
109
|
+
try {
|
|
110
|
+
log("Trovati wallet paths in localStorage");
|
|
111
|
+
const parsedPaths = JSON.parse(storedPaths);
|
|
112
|
+
// Aggiunge i paths da localStorage se non sono già presenti in GUN
|
|
113
|
+
for (const [address, pathData] of Object.entries(parsedPaths)) {
|
|
114
|
+
if (!this.walletPaths[address]) {
|
|
115
|
+
this.walletPaths[address] = pathData;
|
|
116
|
+
log(`Caricato path da localStorage per wallet: ${address}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.error("Errore nel parsing dei wallet paths da localStorage:", error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Ottiene un identificatore univoco per l'utente corrente per lo storage
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
getStorageUserIdentifier() {
|
|
130
|
+
const user = this.gun.user();
|
|
131
|
+
if (user && user.is && user.is.pub) {
|
|
132
|
+
return user.is.pub.substring(0, 12); // Usa una parte della chiave pubblica
|
|
133
|
+
}
|
|
134
|
+
return "guest"; // Identificatore per utenti non autenticati
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Salva i paths dei wallet in localStorage
|
|
138
|
+
* @private
|
|
139
|
+
*/
|
|
140
|
+
saveWalletPathsToLocalStorage() {
|
|
141
|
+
try {
|
|
142
|
+
const storageKey = `shogun_wallet_paths_${this.getStorageUserIdentifier()}`;
|
|
143
|
+
const pathsToSave = JSON.stringify(this.walletPaths);
|
|
144
|
+
this.storage.setItem(storageKey, pathsToSave);
|
|
145
|
+
log(`Salvati ${Object.keys(this.walletPaths).length} wallet paths in localStorage`);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.error("Errore nel salvataggio dei wallet paths in localStorage:", error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Deriva una chiave privata in modo deterministico e compatibile
|
|
153
|
+
*/
|
|
154
|
+
derivePrivateKeyFromMnemonic(mnemonic, path) {
|
|
155
|
+
try {
|
|
156
|
+
// Approccio completamente ridisegnato per evitare i problemi di hdnode
|
|
157
|
+
log(`Derivazione wallet per path: ${path}`);
|
|
158
|
+
// Creiamo un seed deterministico che combina mnemonic e path
|
|
159
|
+
const seedBase = `${mnemonic}|${path}`;
|
|
160
|
+
// Usiamo crypto.subtle per generare un hash SHA-256 del seed
|
|
161
|
+
// Questo ci dà un valore deterministico basato su mnemonic e path
|
|
162
|
+
const encoder = new TextEncoder();
|
|
163
|
+
const messageBuffer = encoder.encode(seedBase);
|
|
164
|
+
// Generiamo la chiave privata in modo deterministico
|
|
165
|
+
const privateKey = this.generatePrivateKeyFromString(seedBase);
|
|
166
|
+
log(`Generata chiave privata deterministica per ${path}`);
|
|
167
|
+
// Creiamo il wallet dalla chiave privata
|
|
168
|
+
return new ethers.Wallet(privateKey);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
// Fallback di ultima istanza in caso di errori
|
|
172
|
+
log(`Errore nella derivazione deterministica: ${error}, utilizzo chiave di fallback`);
|
|
173
|
+
// Generiamo una chiave completamente hardcoded come ultima risorsa
|
|
174
|
+
// (questo è solo per evitare che l'app si blocchi completamente)
|
|
175
|
+
const fallbackSeed = `fallback-${path}-${mnemonic.substring(0, 10)}`;
|
|
176
|
+
const fallbackKey = this.generatePrivateKeyFromString(fallbackSeed);
|
|
177
|
+
return new ethers.Wallet(fallbackKey);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Genera una nuova mnemonic BIP39 standard - anche se per ora
|
|
182
|
+
* non utilizziamo realmente la derivazione HD ma solo un approccio deterministico
|
|
183
|
+
*/
|
|
184
|
+
generateNewMnemonic() {
|
|
185
|
+
// Genera una mnemonic casuale a 12 parole che utilizziamo come base per la generazione di wallet
|
|
186
|
+
return ethers.Mnemonic.fromEntropy(ethers.randomBytes(16)).phrase;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Override della funzione principale con correzioni e miglioramenti
|
|
190
|
+
*/
|
|
191
|
+
generatePrivateKeyFromString(input) {
|
|
192
|
+
try {
|
|
193
|
+
// Utilizziamo SHA-256 per generare un valore hash deterministico
|
|
194
|
+
const encoder = new TextEncoder();
|
|
195
|
+
const data = encoder.encode(input);
|
|
196
|
+
// Utilizziamo il metodo digestSync semplificato
|
|
197
|
+
const digestSync = (data) => {
|
|
198
|
+
// Versione semplificata
|
|
199
|
+
let h1 = 0xdeadbeef, h2 = 0x41c6ce57;
|
|
200
|
+
for (let i = 0; i < data.length; i++) {
|
|
201
|
+
h1 = Math.imul(h1 ^ data[i], 2654435761);
|
|
202
|
+
h2 = Math.imul(h2 ^ data[i], 1597334677);
|
|
203
|
+
}
|
|
204
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
205
|
+
h1 = Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
206
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
|
207
|
+
h2 = Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
208
|
+
// Creiamo un array di 32 byte
|
|
209
|
+
const out = new Uint8Array(32);
|
|
210
|
+
for (let i = 0; i < 4; i++) {
|
|
211
|
+
out[i] = (h1 >> (8 * i)) & 0xff;
|
|
212
|
+
}
|
|
213
|
+
for (let i = 0; i < 4; i++) {
|
|
214
|
+
out[i + 4] = (h2 >> (8 * i)) & 0xff;
|
|
215
|
+
}
|
|
216
|
+
// Riempiamo con valori derivati
|
|
217
|
+
for (let i = 8; i < 32; i++) {
|
|
218
|
+
out[i] = (out[i % 8] ^ out[(i - 1) % 8]) & 0xff;
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
};
|
|
222
|
+
// Utilizziamo la versione sincrona del digest
|
|
223
|
+
const hashArray = digestSync(data);
|
|
224
|
+
// Convertiamo in hex string
|
|
225
|
+
const privateKey = "0x" + Array.from(hashArray)
|
|
226
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
227
|
+
.join("");
|
|
228
|
+
return privateKey;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error("Errore nella generazione della chiave privata:", error);
|
|
232
|
+
// Fallback: creiamo un valore hex valido
|
|
233
|
+
const fallbackHex = "0x" + Array.from({ length: 32 })
|
|
234
|
+
.map(() => Math.floor(Math.random() * 256).toString(16).padStart(2, "0"))
|
|
235
|
+
.join("");
|
|
236
|
+
return fallbackHex;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* METODO INFORMATIVO: Recupera i primi n wallet che sarebbero stati creati da una mnemonic
|
|
241
|
+
* usando MetaMask (solo per debug e verifica)
|
|
242
|
+
*/
|
|
243
|
+
getMetaMaskCompatibleAddresses(mnemonic, count = 5) {
|
|
244
|
+
try {
|
|
245
|
+
// Questo è solo a scopo informativo, non influisce sulla funzionalità dell'app
|
|
246
|
+
const addresses = [];
|
|
247
|
+
log(`Tentativo di derivazione compatibile con MetaMask per mnemonic`);
|
|
248
|
+
for (let i = 0; i < count; i++) {
|
|
249
|
+
// Generiamo indirizzi deterministici usando il nostro metodo
|
|
250
|
+
const path = `m/44'/60'/0'/0/${i}`;
|
|
251
|
+
const wallet = this.derivePrivateKeyFromMnemonic(mnemonic, path);
|
|
252
|
+
addresses.push(wallet.address);
|
|
253
|
+
}
|
|
254
|
+
return addresses;
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
log(`Errore nel calcolo degli indirizzi MetaMask: ${error}`);
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Ottiene il wallet principale
|
|
263
|
+
*/
|
|
264
|
+
getMainWallet() {
|
|
265
|
+
try {
|
|
266
|
+
if (!this.mainWallet) {
|
|
267
|
+
const user = this.gun.user();
|
|
268
|
+
if (!user || !user.is) {
|
|
269
|
+
log("getMainWallet: Utente non autenticato");
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
// Verifica se abbiamo accesso alle proprietà necessarie
|
|
273
|
+
if (!user._ || !user._.sea || !user._.sea.priv || !user._.sea.pub) {
|
|
274
|
+
log("getMainWallet: Dati utente insufficienti", JSON.stringify({
|
|
275
|
+
hasUserData: !!user._,
|
|
276
|
+
hasSea: !!(user._ && user._.sea),
|
|
277
|
+
hasPriv: !!(user._ && user._.sea && user._.sea.priv),
|
|
278
|
+
hasPub: !!(user._ && user._.sea && user._.sea.pub),
|
|
279
|
+
}));
|
|
280
|
+
// Verifica se è un utente MetaMask e utilizziamo un approccio alternativo
|
|
281
|
+
if (user.is.alias && user.is.alias.startsWith("0x")) {
|
|
282
|
+
log("getMainWallet: Utente MetaMask rilevato, utilizzo approccio alternativo");
|
|
283
|
+
// Per MetaMask, usiamo l'indirizzo come seed
|
|
284
|
+
const address = user.is.alias;
|
|
285
|
+
const seed = `metamask-${address}-${Date.now()}`;
|
|
286
|
+
const privateKey = this.generatePrivateKeyFromString(seed);
|
|
287
|
+
this.mainWallet = new ethers.Wallet(privateKey);
|
|
288
|
+
return this.mainWallet;
|
|
289
|
+
}
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
// Combiniamo chiave privata + chiave pubblica + alias dell'utente per avere un seed unico
|
|
293
|
+
const userSeed = user._.sea.priv;
|
|
294
|
+
const userPub = user._.sea.pub;
|
|
295
|
+
const userAlias = user.is.alias;
|
|
296
|
+
// Creiamo un seed univoco per questo utente
|
|
297
|
+
const seed = `${userSeed}|${userPub}|${userAlias}`;
|
|
298
|
+
// Usiamo il nuovo metodo sicuro per generare la chiave privata
|
|
299
|
+
const privateKey = this.generatePrivateKeyFromString(seed);
|
|
300
|
+
this.mainWallet = new ethers.Wallet(privateKey);
|
|
301
|
+
}
|
|
302
|
+
return this.mainWallet;
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
console.error("Errore nel recupero del wallet principale:", error);
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Cifra un testo sensibile usando SEA
|
|
311
|
+
* @param text Testo da cifrare
|
|
312
|
+
* @returns Testo cifrato
|
|
313
|
+
*/
|
|
314
|
+
async encryptSensitiveData(text) {
|
|
315
|
+
try {
|
|
316
|
+
const user = this.gun.user();
|
|
317
|
+
if (user && user._ && user._.sea) {
|
|
318
|
+
// Usa la chiave dell'utente per cifrare
|
|
319
|
+
const encrypted = await SEA.encrypt(text, user._.sea);
|
|
320
|
+
return JSON.stringify(encrypted);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
// Fallback: usa una chiave derivata dall'ID utente
|
|
324
|
+
const userIdentifier = this.getStorageUserIdentifier();
|
|
325
|
+
const key = `shogun-encrypt-${userIdentifier}-key`;
|
|
326
|
+
const encrypted = await SEA.encrypt(text, key);
|
|
327
|
+
return JSON.stringify(encrypted);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
console.error("Errore durante la cifratura dei dati:", error);
|
|
332
|
+
// Fallback: salva in chiaro ma con un warning
|
|
333
|
+
log("ATTENZIONE: Dati sensibili salvati senza cifratura");
|
|
334
|
+
return `unencrypted:${text}`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Decifra un testo sensibile cifrato con SEA
|
|
339
|
+
* @param encryptedText Testo cifrato
|
|
340
|
+
* @returns Testo decifrato
|
|
341
|
+
*/
|
|
342
|
+
async decryptSensitiveData(encryptedText) {
|
|
343
|
+
try {
|
|
344
|
+
// Controlla se è un testo non cifrato (fallback)
|
|
345
|
+
if (encryptedText.startsWith("unencrypted:")) {
|
|
346
|
+
return encryptedText.substring(12);
|
|
347
|
+
}
|
|
348
|
+
// Prova a parsificare il testo cifrato
|
|
349
|
+
const encryptedData = JSON.parse(encryptedText);
|
|
350
|
+
const user = this.gun.user();
|
|
351
|
+
if (user && user._ && user._.sea) {
|
|
352
|
+
// Usa la chiave dell'utente per decifrare
|
|
353
|
+
const decrypted = await SEA.decrypt(encryptedData, user._.sea);
|
|
354
|
+
return decrypted;
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
// Fallback: usa una chiave derivata dall'ID utente
|
|
358
|
+
const userIdentifier = this.getStorageUserIdentifier();
|
|
359
|
+
const key = `shogun-encrypt-${userIdentifier}-key`;
|
|
360
|
+
const decrypted = await SEA.decrypt(encryptedData, key);
|
|
361
|
+
return decrypted;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
console.error("Errore durante la decifratura dei dati:", error);
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Ottiene la mnemonic principale dell'utente, prima cercando in GunDB e poi in localStorage
|
|
371
|
+
*/
|
|
372
|
+
async getUserMasterMnemonic() {
|
|
373
|
+
try {
|
|
374
|
+
// 1. Prima cerchiamo in GunDB (cifrata automaticamente da SEA)
|
|
375
|
+
const user = this.gun.user();
|
|
376
|
+
if (user && user.is) {
|
|
377
|
+
const gunMnemonic = await new Promise((resolve) => {
|
|
378
|
+
user.get("master_mnemonic").once((data) => {
|
|
379
|
+
resolve(data || null);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
if (gunMnemonic) {
|
|
383
|
+
log("Mnemonic recuperata da GunDB");
|
|
384
|
+
return gunMnemonic;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// 2. Se non trovata in GunDB, cerchiamo in localStorage
|
|
388
|
+
const storageKey = `shogun_master_mnemonic_${this.getStorageUserIdentifier()}`;
|
|
389
|
+
const encryptedMnemonic = this.storage.getItem(storageKey);
|
|
390
|
+
if (!encryptedMnemonic) {
|
|
391
|
+
log("Nessuna mnemonic trovata né in GunDB né in localStorage");
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
// Decifra la mnemonic da localStorage
|
|
395
|
+
const decrypted = await this.decryptSensitiveData(encryptedMnemonic);
|
|
396
|
+
log("Mnemonic recuperata da localStorage");
|
|
397
|
+
// Se troviamo la mnemonic in localStorage ma non in GunDB, la salviamo anche in GunDB
|
|
398
|
+
// per future sincronizzazioni (ma solo se l'utente è autenticato)
|
|
399
|
+
if (decrypted && user && user.is) {
|
|
400
|
+
await user.get("master_mnemonic").put(decrypted);
|
|
401
|
+
log("Mnemonic da localStorage sincronizzata con GunDB");
|
|
402
|
+
}
|
|
403
|
+
return decrypted;
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
console.error("Errore nel recupero della mnemonic:", error);
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Salva la mnemonic principale dell'utente sia in GunDB che in localStorage
|
|
412
|
+
*/
|
|
413
|
+
async saveUserMasterMnemonic(mnemonic) {
|
|
414
|
+
try {
|
|
415
|
+
// 1. Salva in GunDB (cifrata automaticamente da SEA)
|
|
416
|
+
const user = this.gun.user();
|
|
417
|
+
if (user && user.is) {
|
|
418
|
+
await user.get("master_mnemonic").put(mnemonic);
|
|
419
|
+
log("Mnemonic salvata in GunDB");
|
|
420
|
+
}
|
|
421
|
+
// 2. Salva anche in localStorage come backup
|
|
422
|
+
const storageKey = `shogun_master_mnemonic_${this.getStorageUserIdentifier()}`;
|
|
423
|
+
// Cifra la mnemonic prima di salvarla in localStorage
|
|
424
|
+
const encryptedMnemonic = await this.encryptSensitiveData(mnemonic);
|
|
425
|
+
this.storage.setItem(storageKey, encryptedMnemonic);
|
|
426
|
+
log("Mnemonic cifrata salvata anche in localStorage come backup");
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
console.error("Errore nel salvataggio della mnemonic:", error);
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
async createWallet() {
|
|
434
|
+
try {
|
|
435
|
+
// Verifica che l'utente sia autenticato
|
|
436
|
+
const user = this.gun.user();
|
|
437
|
+
if (!user.is) {
|
|
438
|
+
throw new Error("L'utente non è autenticato");
|
|
439
|
+
}
|
|
440
|
+
// Determina il prossimo indice disponibile
|
|
441
|
+
const existingWallets = Object.values(this.walletPaths).length;
|
|
442
|
+
const nextIndex = existingWallets;
|
|
443
|
+
// Usa il formato standard Ethereum per i path
|
|
444
|
+
const path = `m/44'/60'/0'/0/${nextIndex}`;
|
|
445
|
+
// Recupera il master mnemonic dell'utente
|
|
446
|
+
let masterMnemonic = await this.getUserMasterMnemonic();
|
|
447
|
+
if (!masterMnemonic) {
|
|
448
|
+
// Genera una nuova mnemonic
|
|
449
|
+
masterMnemonic = this.generateNewMnemonic();
|
|
450
|
+
await this.saveUserMasterMnemonic(masterMnemonic);
|
|
451
|
+
log(`Generata nuova mnemonic: ${masterMnemonic}`);
|
|
452
|
+
}
|
|
453
|
+
// Deriva il wallet usando il metodo sicuro
|
|
454
|
+
const wallet = this.derivePrivateKeyFromMnemonic(masterMnemonic, path);
|
|
455
|
+
log(`Derivato wallet per path ${path} con indirizzo ${wallet.address}`);
|
|
456
|
+
// Salva il path del wallet
|
|
457
|
+
const timestamp = Date.now();
|
|
458
|
+
this.walletPaths[wallet.address] = { path, created: timestamp };
|
|
459
|
+
// Salva nel contesto dell'utente in Gun
|
|
460
|
+
await user
|
|
461
|
+
.get("wallet_paths")
|
|
462
|
+
.get(wallet.address)
|
|
463
|
+
.put({ path, created: timestamp });
|
|
464
|
+
// Salva anche in localStorage
|
|
465
|
+
this.saveWalletPathsToLocalStorage();
|
|
466
|
+
return {
|
|
467
|
+
wallet,
|
|
468
|
+
path,
|
|
469
|
+
address: wallet.address,
|
|
470
|
+
getAddressString: () => wallet.address,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
console.error("Errore durante la creazione del wallet:", error);
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async loadWallets() {
|
|
479
|
+
try {
|
|
480
|
+
const user = this.gun.user();
|
|
481
|
+
// Verifica più completa dell'autenticazione
|
|
482
|
+
if (!user) {
|
|
483
|
+
console.error("loadWallets: Nessun utente Gun disponibile");
|
|
484
|
+
throw new Error("Utente Gun non disponibile");
|
|
485
|
+
}
|
|
486
|
+
// Inizializza i wallet paths se non già fatto
|
|
487
|
+
await this.initializeWalletPaths();
|
|
488
|
+
// Recupera il master mnemonic dell'utente
|
|
489
|
+
let masterMnemonic = await this.getUserMasterMnemonic();
|
|
490
|
+
if (!masterMnemonic) {
|
|
491
|
+
// Se non esiste, creiamo il wallet predefinito
|
|
492
|
+
console.log("Nessun mnemonic trovato, creazione del wallet predefinito...");
|
|
493
|
+
const mainWallet = await this.createWallet();
|
|
494
|
+
return [mainWallet];
|
|
495
|
+
}
|
|
496
|
+
log(`masterMnemonic trovata: ${masterMnemonic}`);
|
|
497
|
+
const wallets = [];
|
|
498
|
+
// Deriva ogni wallet dai paths salvati
|
|
499
|
+
for (const [address, data] of Object.entries(this.walletPaths)) {
|
|
500
|
+
try {
|
|
501
|
+
// Usa il metodo sicuro per derivare la chiave privata
|
|
502
|
+
const wallet = this.derivePrivateKeyFromMnemonic(masterMnemonic, data.path);
|
|
503
|
+
log(`Derivato wallet per path ${data.path} con indirizzo ${wallet.address}`);
|
|
504
|
+
if (wallet.address.toLowerCase() !== address.toLowerCase()) {
|
|
505
|
+
console.warn(`Attenzione: l'indirizzo derivato (${wallet.address}) non corrisponde a quello salvato (${address})`);
|
|
506
|
+
}
|
|
507
|
+
wallets.push({
|
|
508
|
+
wallet,
|
|
509
|
+
path: data.path,
|
|
510
|
+
address: wallet.address,
|
|
511
|
+
getAddressString: () => wallet.address,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
catch (innerError) {
|
|
515
|
+
console.error(`Errore nella derivazione del wallet ${address}:`, innerError);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// Imposta il mainWallet se ci sono wallet
|
|
519
|
+
if (wallets.length > 0) {
|
|
520
|
+
this.mainWallet = wallets[0].wallet;
|
|
521
|
+
}
|
|
522
|
+
return wallets;
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
console.error("Errore durante il caricamento dei wallet:", error);
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// BASIC WALLET FUNCTIONS
|
|
530
|
+
/**
|
|
531
|
+
* Ottiene il saldo di un wallet con caching per ridurre le chiamate RPC
|
|
532
|
+
*/
|
|
533
|
+
async getBalance(wallet) {
|
|
534
|
+
try {
|
|
535
|
+
const address = wallet.address;
|
|
536
|
+
// Controlla se abbiamo una cache valida
|
|
537
|
+
const cachedData = this.balanceCache.get(address);
|
|
538
|
+
const now = Date.now();
|
|
539
|
+
if (cachedData && (now - cachedData.timestamp) < this.balanceCacheTTL) {
|
|
540
|
+
log(`Usando saldo in cache per ${address}: ${cachedData.balance} ETH`);
|
|
541
|
+
return cachedData.balance;
|
|
542
|
+
}
|
|
543
|
+
// Altrimenti chiama il provider
|
|
544
|
+
log(`Chiamata RPC per ottenere il saldo di ${address}`);
|
|
545
|
+
const provider = this.getProvider();
|
|
546
|
+
const balance = await provider.getBalance(wallet.address);
|
|
547
|
+
const formattedBalance = ethers.formatEther(balance);
|
|
548
|
+
// Aggiorna la cache
|
|
549
|
+
this.balanceCache.set(address, {
|
|
550
|
+
balance: formattedBalance,
|
|
551
|
+
timestamp: now
|
|
552
|
+
});
|
|
553
|
+
return formattedBalance;
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
console.error("Errore durante il recupero del saldo:", error);
|
|
557
|
+
return "0.0";
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Invalida la cache del saldo per un indirizzo
|
|
562
|
+
*/
|
|
563
|
+
invalidateBalanceCache(address) {
|
|
564
|
+
this.balanceCache.delete(address);
|
|
565
|
+
log(`Cache del saldo invalidata per ${address}`);
|
|
566
|
+
}
|
|
567
|
+
async getNonce(wallet) {
|
|
568
|
+
const provider = this.getProvider();
|
|
569
|
+
const nonce = await provider.getTransactionCount(wallet.address);
|
|
570
|
+
return nonce;
|
|
571
|
+
}
|
|
572
|
+
async sendTransaction(wallet, toAddress, value) {
|
|
573
|
+
try {
|
|
574
|
+
log(`Invio transazione dal wallet ${wallet.address} a ${toAddress} per ${value} ETH`);
|
|
575
|
+
const provider = this.getProvider();
|
|
576
|
+
wallet.connect(provider);
|
|
577
|
+
const tx = await wallet.sendTransaction({
|
|
578
|
+
to: toAddress,
|
|
579
|
+
value: ethers.parseEther(value),
|
|
580
|
+
});
|
|
581
|
+
// Invalida la cache del saldo dopo l'invio di una transazione
|
|
582
|
+
this.invalidateBalanceCache(wallet.address);
|
|
583
|
+
log(`Transazione inviata con successo: ${tx.hash}`);
|
|
584
|
+
return tx.hash;
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
console.error("Errore durante l'invio della transazione:", error);
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Firma un messaggio con un wallet
|
|
593
|
+
*/
|
|
594
|
+
async signMessage(wallet, message) {
|
|
595
|
+
try {
|
|
596
|
+
return await wallet.signMessage(message);
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
console.error("Errore durante la firma del messaggio:", error);
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Verifica una firma
|
|
605
|
+
*/
|
|
606
|
+
verifySignature(message, signature) {
|
|
607
|
+
return ethers.verifyMessage(message, signature);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Firma una transazione
|
|
611
|
+
*/
|
|
612
|
+
async signTransaction(wallet, toAddress, value, provider) {
|
|
613
|
+
try {
|
|
614
|
+
log(`Firma transazione dal wallet ${wallet.address} a ${toAddress} per ${value} ETH`);
|
|
615
|
+
// Se non viene fornito un provider, usa quello configurato
|
|
616
|
+
const actualProvider = provider || this.getProvider();
|
|
617
|
+
// Ottieni il nonce
|
|
618
|
+
const nonce = await actualProvider.getTransactionCount(wallet.address);
|
|
619
|
+
log(`Nonce per la transazione: ${nonce}`);
|
|
620
|
+
// Ottieni i dati delle fee
|
|
621
|
+
const feeData = await actualProvider.getFeeData();
|
|
622
|
+
const tx = {
|
|
623
|
+
nonce: nonce,
|
|
624
|
+
to: toAddress,
|
|
625
|
+
value: ethers.parseEther(value),
|
|
626
|
+
gasPrice: feeData.gasPrice,
|
|
627
|
+
gasLimit: 21000, // Gas limit standard per trasferimenti ETH
|
|
628
|
+
};
|
|
629
|
+
// Firma la transazione
|
|
630
|
+
const signedTx = await wallet.signTransaction(tx);
|
|
631
|
+
log(`Transazione firmata con successo`);
|
|
632
|
+
return signedTx;
|
|
633
|
+
}
|
|
634
|
+
catch (error) {
|
|
635
|
+
console.error("Errore durante la firma della transazione:", error);
|
|
636
|
+
throw error;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Resetta il wallet principale
|
|
641
|
+
* Utile quando vogliamo forzare la rigenerazione del wallet
|
|
642
|
+
*/
|
|
643
|
+
resetMainWallet() {
|
|
644
|
+
log("Reset del wallet principale");
|
|
645
|
+
this.mainWallet = null;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Esporta la frase mnemonica dell'utente
|
|
649
|
+
* @param password Password opzionale per cifrare la mnemonica esportata
|
|
650
|
+
* @returns La mnemonica in chiaro o cifrata se viene fornita una password
|
|
651
|
+
*/
|
|
652
|
+
async exportMnemonic(password) {
|
|
653
|
+
try {
|
|
654
|
+
// Recupera la mnemonica
|
|
655
|
+
const mnemonic = await this.getUserMasterMnemonic();
|
|
656
|
+
if (!mnemonic) {
|
|
657
|
+
throw new Error("Nessuna mnemonica trovata da esportare");
|
|
658
|
+
}
|
|
659
|
+
// Se è stata fornita una password, cifra la mnemonica
|
|
660
|
+
if (password) {
|
|
661
|
+
const encryptedData = await SEA.encrypt(mnemonic, password);
|
|
662
|
+
return JSON.stringify({
|
|
663
|
+
type: "encrypted-mnemonic",
|
|
664
|
+
data: encryptedData,
|
|
665
|
+
version: "1.0"
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
// Altrimenti restituisci la mnemonica in chiaro
|
|
669
|
+
return mnemonic;
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
console.error("Errore nell'esportazione della mnemonica:", error);
|
|
673
|
+
throw error;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Esporta le chiavi private di tutti i wallet generati
|
|
678
|
+
* @param password Password opzionale per cifrare i dati esportati
|
|
679
|
+
* @returns Un oggetto JSON contenente tutti i wallet con relative chiavi private
|
|
680
|
+
*/
|
|
681
|
+
async exportWalletKeys(password) {
|
|
682
|
+
try {
|
|
683
|
+
// Carica tutti i wallet
|
|
684
|
+
const wallets = await this.loadWallets();
|
|
685
|
+
if (!wallets || wallets.length === 0) {
|
|
686
|
+
throw new Error("Nessun wallet trovato da esportare");
|
|
687
|
+
}
|
|
688
|
+
// Crea un oggetto con i dati dei wallet
|
|
689
|
+
const walletData = wallets.map(walletInfo => ({
|
|
690
|
+
address: walletInfo.address,
|
|
691
|
+
privateKey: walletInfo.wallet.privateKey,
|
|
692
|
+
path: walletInfo.path,
|
|
693
|
+
created: this.walletPaths[walletInfo.address]?.created || Date.now()
|
|
694
|
+
}));
|
|
695
|
+
const exportData = {
|
|
696
|
+
wallets: walletData,
|
|
697
|
+
version: "1.0",
|
|
698
|
+
exportedAt: new Date().toISOString()
|
|
699
|
+
};
|
|
700
|
+
// Se è stata fornita una password, cifra i dati
|
|
701
|
+
if (password) {
|
|
702
|
+
const encryptedData = await SEA.encrypt(JSON.stringify(exportData), password);
|
|
703
|
+
return JSON.stringify({
|
|
704
|
+
type: "encrypted-wallets",
|
|
705
|
+
data: encryptedData,
|
|
706
|
+
version: "1.0"
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
// Altrimenti restituisci i dati in chiaro
|
|
710
|
+
return JSON.stringify(exportData, null, 2);
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
console.error("Errore nell'esportazione delle chiavi dei wallet:", error);
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Esporta il pair (coppia di chiavi) di Gun dell'utente
|
|
719
|
+
* @param password Password opzionale per cifrare i dati esportati
|
|
720
|
+
* @returns Il pair di Gun in formato JSON
|
|
721
|
+
*/
|
|
722
|
+
async exportGunPair(password) {
|
|
723
|
+
try {
|
|
724
|
+
const user = this.gun.user();
|
|
725
|
+
if (!user || !user._ || !user._.sea) {
|
|
726
|
+
throw new Error("Utente non autenticato o pair non disponibile");
|
|
727
|
+
}
|
|
728
|
+
const pair = user._.sea;
|
|
729
|
+
// Se è stata fornita una password, cifra i dati
|
|
730
|
+
if (password) {
|
|
731
|
+
const encryptedData = await SEA.encrypt(JSON.stringify(pair), password);
|
|
732
|
+
return JSON.stringify({
|
|
733
|
+
type: "encrypted-gun-pair",
|
|
734
|
+
data: encryptedData,
|
|
735
|
+
version: "1.0"
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
// Altrimenti restituisci i dati in chiaro
|
|
739
|
+
return JSON.stringify(pair, null, 2);
|
|
740
|
+
}
|
|
741
|
+
catch (error) {
|
|
742
|
+
console.error("Errore nell'esportazione del Gun pair:", error);
|
|
743
|
+
throw error;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Esporta tutti i dati dell'utente in un unico file
|
|
748
|
+
* @param password Password obbligatoria per cifrare i dati esportati
|
|
749
|
+
* @returns Un oggetto JSON contenente tutti i dati dell'utente
|
|
750
|
+
*/
|
|
751
|
+
async exportAllUserData(password) {
|
|
752
|
+
if (!password) {
|
|
753
|
+
throw new Error("È richiesta una password per esportare tutti i dati");
|
|
754
|
+
}
|
|
755
|
+
try {
|
|
756
|
+
// Recupera tutti i dati
|
|
757
|
+
const mnemonic = await this.getUserMasterMnemonic();
|
|
758
|
+
const wallets = await this.loadWallets();
|
|
759
|
+
const user = this.gun.user();
|
|
760
|
+
if (!user || !user._ || !user._.sea) {
|
|
761
|
+
throw new Error("Utente non autenticato o dati non disponibili");
|
|
762
|
+
}
|
|
763
|
+
// Prepara i dati dei wallet
|
|
764
|
+
const walletData = wallets.map(walletInfo => ({
|
|
765
|
+
address: walletInfo.address,
|
|
766
|
+
privateKey: walletInfo.wallet.privateKey,
|
|
767
|
+
path: walletInfo.path,
|
|
768
|
+
created: this.walletPaths[walletInfo.address]?.created || Date.now()
|
|
769
|
+
}));
|
|
770
|
+
// Crea l'oggetto completo con tutti i dati
|
|
771
|
+
const exportData = {
|
|
772
|
+
user: {
|
|
773
|
+
alias: user.is.alias,
|
|
774
|
+
pub: user.is.pub,
|
|
775
|
+
pair: user._.sea
|
|
776
|
+
},
|
|
777
|
+
mnemonic,
|
|
778
|
+
wallets: walletData,
|
|
779
|
+
version: "1.0",
|
|
780
|
+
exportedAt: new Date().toISOString(),
|
|
781
|
+
appName: "Shogun Wallet"
|
|
782
|
+
};
|
|
783
|
+
// Cifra i dati con la password fornita
|
|
784
|
+
const encryptedData = await SEA.encrypt(JSON.stringify(exportData), password);
|
|
785
|
+
return JSON.stringify({
|
|
786
|
+
type: "encrypted-shogun-backup",
|
|
787
|
+
data: encryptedData,
|
|
788
|
+
version: "1.0"
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
catch (error) {
|
|
792
|
+
console.error("Errore nell'esportazione di tutti i dati utente:", error);
|
|
793
|
+
throw error;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Importa una frase mnemonica
|
|
798
|
+
* @param mnemonicData La mnemonica o il JSON cifrato da importare
|
|
799
|
+
* @param password Password opzionale per decifrare la mnemonica se cifrata
|
|
800
|
+
* @returns true se l'importazione è riuscita
|
|
801
|
+
*/
|
|
802
|
+
async importMnemonic(mnemonicData, password) {
|
|
803
|
+
try {
|
|
804
|
+
let mnemonic = mnemonicData;
|
|
805
|
+
// Verifica se i dati sono in formato JSON cifrato
|
|
806
|
+
if (mnemonicData.startsWith("{")) {
|
|
807
|
+
try {
|
|
808
|
+
const jsonData = JSON.parse(mnemonicData);
|
|
809
|
+
// Se i dati sono cifrati, decifriamoli
|
|
810
|
+
if (jsonData.type === "encrypted-mnemonic" && jsonData.data && password) {
|
|
811
|
+
const decryptedData = await SEA.decrypt(jsonData.data, password);
|
|
812
|
+
if (!decryptedData) {
|
|
813
|
+
throw new Error("Password non valida o dati corrotti");
|
|
814
|
+
}
|
|
815
|
+
mnemonic = decryptedData;
|
|
816
|
+
}
|
|
817
|
+
else if (jsonData.mnemonic) {
|
|
818
|
+
// Se i dati sono in formato JSON non cifrato con campo mnemonic
|
|
819
|
+
mnemonic = jsonData.mnemonic;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
catch (error) {
|
|
823
|
+
throw new Error("Formato JSON non valido o password errata");
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
// Valida la mnemonica (verifica che sia una mnemonica BIP39 valida)
|
|
827
|
+
try {
|
|
828
|
+
// Verifica che la mnemonica sia valida usando ethers.js
|
|
829
|
+
ethers.Mnemonic.fromPhrase(mnemonic);
|
|
830
|
+
}
|
|
831
|
+
catch (error) {
|
|
832
|
+
throw new Error("La mnemonica fornita non è valida");
|
|
833
|
+
}
|
|
834
|
+
// Salva la mnemonica
|
|
835
|
+
await this.saveUserMasterMnemonic(mnemonic);
|
|
836
|
+
// Reset del wallet principale per forzare la riderivazione
|
|
837
|
+
this.resetMainWallet();
|
|
838
|
+
// Genera il primo wallet se non ne esistono
|
|
839
|
+
if (Object.keys(this.walletPaths).length === 0) {
|
|
840
|
+
await this.createWallet();
|
|
841
|
+
}
|
|
842
|
+
log("Mnemonica importata con successo");
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
catch (error) {
|
|
846
|
+
console.error("Errore nell'importazione della mnemonica:", error);
|
|
847
|
+
throw error;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Importa le chiavi private dei wallet
|
|
852
|
+
* @param walletsData JSON contenente i dati dei wallet o JSON cifrato
|
|
853
|
+
* @param password Password opzionale per decifrare i dati se cifrati
|
|
854
|
+
* @returns Il numero di wallet importati con successo
|
|
855
|
+
*/
|
|
856
|
+
async importWalletKeys(walletsData, password) {
|
|
857
|
+
try {
|
|
858
|
+
let wallets = [];
|
|
859
|
+
// Log per debug
|
|
860
|
+
console.log(`[importWalletKeys] Tentativo di importazione wallet, lunghezza dati: ${walletsData.length} caratteri`);
|
|
861
|
+
if (walletsData.length > 100) {
|
|
862
|
+
console.log(`[importWalletKeys] Primi 100 caratteri: ${walletsData.substring(0, 100)}...`);
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
console.log(`[importWalletKeys] Dati completi: ${walletsData}`);
|
|
866
|
+
}
|
|
867
|
+
// Pulizia dei dati: rimuovi BOM e altri caratteri speciali
|
|
868
|
+
walletsData = walletsData.replace(/^\uFEFF/, ''); // Rimuovi BOM
|
|
869
|
+
walletsData = walletsData.trim(); // Rimuovi spazi all'inizio e alla fine
|
|
870
|
+
// Verifica se i dati sono in formato JSON cifrato
|
|
871
|
+
try {
|
|
872
|
+
// Verifica che sia un JSON valido
|
|
873
|
+
if (!walletsData.startsWith('{') && !walletsData.startsWith('[')) {
|
|
874
|
+
console.log("[importWalletKeys] Il formato non sembra essere JSON valido");
|
|
875
|
+
// Tenta di interpretare come mnemonic o chiave privata singola
|
|
876
|
+
if (walletsData.split(' ').length >= 12) {
|
|
877
|
+
console.log("[importWalletKeys] Potrebbe essere una mnemonic");
|
|
878
|
+
throw new Error("I dati sembrano essere una mnemonic, usa 'Importa Mnemonica' invece");
|
|
879
|
+
}
|
|
880
|
+
if (walletsData.startsWith('0x') && walletsData.length === 66) {
|
|
881
|
+
console.log("[importWalletKeys] Potrebbe essere una chiave privata singola");
|
|
882
|
+
// Crea un wallet manuale da chiave privata
|
|
883
|
+
try {
|
|
884
|
+
const wallet = new ethers.Wallet(walletsData);
|
|
885
|
+
const path = "m/44'/60'/0'/0/0"; // Path predefinito
|
|
886
|
+
// Crea un oggetto wallet compatibile
|
|
887
|
+
wallets = [{
|
|
888
|
+
address: wallet.address,
|
|
889
|
+
privateKey: wallet.privateKey,
|
|
890
|
+
path: path,
|
|
891
|
+
created: Date.now()
|
|
892
|
+
}];
|
|
893
|
+
console.log(`[importWalletKeys] Creato wallet singolo da chiave privata: ${wallet.address}`);
|
|
894
|
+
}
|
|
895
|
+
catch (walletError) {
|
|
896
|
+
console.error("[importWalletKeys] Errore nella creazione del wallet da chiave privata:", walletError);
|
|
897
|
+
throw new Error(`Chiave privata non valida: ${walletError}`);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
throw new Error("Formato non riconosciuto. Fornisci un file JSON valido.");
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
// Tenta di parsificare il JSON
|
|
906
|
+
const jsonData = JSON.parse(walletsData);
|
|
907
|
+
console.log(`[importWalletKeys] JSON parsificato con successo, tipo: ${typeof jsonData}, chiavi: ${Object.keys(jsonData).join(', ')}`);
|
|
908
|
+
// Se i dati sono cifrati, decifriamoli
|
|
909
|
+
if (jsonData.type === "encrypted-wallets" && jsonData.data && password) {
|
|
910
|
+
console.log("[importWalletKeys] Trovati dati cifrati, tentativo di decifratura...");
|
|
911
|
+
try {
|
|
912
|
+
const decryptedData = await SEA.decrypt(jsonData.data, password);
|
|
913
|
+
if (!decryptedData) {
|
|
914
|
+
console.error("[importWalletKeys] Decifratura fallita: risultato null");
|
|
915
|
+
throw new Error("Password non valida o dati corrotti");
|
|
916
|
+
}
|
|
917
|
+
console.log("[importWalletKeys] Decifratura riuscita, tentativo di parsing...");
|
|
918
|
+
console.log("[importWalletKeys] Tipo dei dati decifrati:", typeof decryptedData);
|
|
919
|
+
if (typeof decryptedData === 'string' && decryptedData.length > 50) {
|
|
920
|
+
console.log("[importWalletKeys] Primi 50 caratteri decifrati:", decryptedData.substring(0, 50));
|
|
921
|
+
}
|
|
922
|
+
try {
|
|
923
|
+
const decryptedJson = JSON.parse(decryptedData);
|
|
924
|
+
console.log("[importWalletKeys] Parsing riuscito, struttura:", Object.keys(decryptedJson).join(', '));
|
|
925
|
+
if (decryptedJson.wallets && Array.isArray(decryptedJson.wallets)) {
|
|
926
|
+
wallets = decryptedJson.wallets;
|
|
927
|
+
console.log(`[importWalletKeys] Trovati ${wallets.length} wallet nei dati decifrati`);
|
|
928
|
+
}
|
|
929
|
+
else if (Array.isArray(decryptedJson)) {
|
|
930
|
+
wallets = decryptedJson;
|
|
931
|
+
console.log(`[importWalletKeys] Trovato array diretto di ${wallets.length} wallet nei dati decifrati`);
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
console.error("[importWalletKeys] Formato JSON decifrato non valido:", decryptedJson);
|
|
935
|
+
throw new Error("Formato JSON decifrato non valido: manca il campo 'wallets'");
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
catch (parseError) {
|
|
939
|
+
console.error(`[importWalletKeys] Errore nel parsing dei dati decifrati: ${parseError}`);
|
|
940
|
+
throw new Error("Formato JSON decifrato non valido");
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
catch (decryptError) {
|
|
944
|
+
console.error("[importWalletKeys] Errore durante la decifratura:", decryptError);
|
|
945
|
+
throw new Error(`Errore durante la decifratura: ${decryptError.message || String(decryptError)}`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
else if (jsonData.wallets) {
|
|
949
|
+
// Se i dati sono in formato JSON non cifrato con campo wallets
|
|
950
|
+
if (Array.isArray(jsonData.wallets)) {
|
|
951
|
+
wallets = jsonData.wallets;
|
|
952
|
+
console.log(`[importWalletKeys] Trovati ${wallets.length} wallet nel JSON non cifrato`);
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
console.error("[importWalletKeys] Il campo wallets non è un array:", jsonData.wallets);
|
|
956
|
+
throw new Error("Formato JSON non valido: il campo 'wallets' non è un array");
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
else if (Array.isArray(jsonData)) {
|
|
960
|
+
// Se è un array diretto di wallet
|
|
961
|
+
wallets = jsonData;
|
|
962
|
+
console.log(`[importWalletKeys] Trovato array diretto di ${wallets.length} wallet`);
|
|
963
|
+
}
|
|
964
|
+
else {
|
|
965
|
+
console.error("[importWalletKeys] Formato JSON non valido:", jsonData);
|
|
966
|
+
throw new Error("Formato JSON non valido: manca il campo 'wallets'");
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
catch (error) {
|
|
971
|
+
console.error(`[importWalletKeys] Errore nel parsing JSON: ${error}`);
|
|
972
|
+
throw new Error(`Formato JSON non valido o password errata: ${error || String(error)}`);
|
|
973
|
+
}
|
|
974
|
+
if (!Array.isArray(wallets) || wallets.length === 0) {
|
|
975
|
+
console.error("[importWalletKeys] Nessun wallet valido trovato nei dati forniti");
|
|
976
|
+
throw new Error("Nessun wallet valido trovato nei dati forniti");
|
|
977
|
+
}
|
|
978
|
+
console.log(`[importWalletKeys] Inizio importazione di ${wallets.length} wallet...`);
|
|
979
|
+
// Crea un contatore per i wallet importati con successo
|
|
980
|
+
let successCount = 0;
|
|
981
|
+
// Per ogni wallet nei dati importati
|
|
982
|
+
for (const walletData of wallets) {
|
|
983
|
+
try {
|
|
984
|
+
console.log(`[importWalletKeys] Tentativo di importazione wallet: ${JSON.stringify(walletData).substring(0, 100)}...`);
|
|
985
|
+
if (!walletData.privateKey) {
|
|
986
|
+
console.log("[importWalletKeys] Manca la chiave privata, salto questo wallet");
|
|
987
|
+
continue; // Salta wallet incompleti
|
|
988
|
+
}
|
|
989
|
+
// Se manca il path, usa un path predefinito
|
|
990
|
+
const path = walletData.path || "m/44'/60'/0'/0/0";
|
|
991
|
+
// Crea un wallet da chiave privata
|
|
992
|
+
try {
|
|
993
|
+
const wallet = new ethers.Wallet(walletData.privateKey);
|
|
994
|
+
// Verifica che la chiave privata corrisponda all'indirizzo fornito (se presente)
|
|
995
|
+
if (walletData.address && wallet.address.toLowerCase() !== walletData.address.toLowerCase()) {
|
|
996
|
+
console.warn(`[importWalletKeys] L'indirizzo generato ${wallet.address} non corrisponde all'indirizzo fornito ${walletData.address}`);
|
|
997
|
+
}
|
|
998
|
+
// Memorizza nel dizionario dei percorsi
|
|
999
|
+
this.walletPaths[wallet.address] = {
|
|
1000
|
+
path: path,
|
|
1001
|
+
created: walletData.created || Date.now()
|
|
1002
|
+
};
|
|
1003
|
+
// Salva i percorsi aggiornati
|
|
1004
|
+
this.saveWalletPathsToLocalStorage();
|
|
1005
|
+
// Incrementa il contatore
|
|
1006
|
+
successCount++;
|
|
1007
|
+
console.log(`[importWalletKeys] Wallet importato con successo: ${wallet.address}`);
|
|
1008
|
+
}
|
|
1009
|
+
catch (walletError) {
|
|
1010
|
+
console.error(`[importWalletKeys] Errore nella creazione del wallet: ${walletError.message || String(walletError)}`);
|
|
1011
|
+
// Continua con il prossimo wallet
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
catch (walletImportError) {
|
|
1015
|
+
console.error(`[importWalletKeys] Errore nell'importazione del wallet: ${walletImportError.message || String(walletImportError)}`);
|
|
1016
|
+
// Continua con il prossimo wallet
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
// Verifica che almeno un wallet sia stato importato con successo
|
|
1020
|
+
if (successCount === 0) {
|
|
1021
|
+
throw new Error("Nessun wallet è stato importato con successo");
|
|
1022
|
+
}
|
|
1023
|
+
// Resetta il wallet principale per forzare la riderivazione
|
|
1024
|
+
this.resetMainWallet();
|
|
1025
|
+
console.log(`[importWalletKeys] Importazione completata: ${successCount} wallet importati su ${wallets.length}`);
|
|
1026
|
+
return successCount;
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
console.error("Errore nell'importazione dei wallet:", error);
|
|
1030
|
+
throw error;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Importa un pair di Gun
|
|
1035
|
+
* @param pairData JSON contenente il pair di Gun o JSON cifrato
|
|
1036
|
+
* @param password Password opzionale per decifrare i dati se cifrati
|
|
1037
|
+
* @returns true se l'importazione è riuscita
|
|
1038
|
+
*/
|
|
1039
|
+
async importGunPair(pairData, password) {
|
|
1040
|
+
try {
|
|
1041
|
+
let pair;
|
|
1042
|
+
// Verifica se i dati sono in formato JSON cifrato
|
|
1043
|
+
try {
|
|
1044
|
+
const jsonData = JSON.parse(pairData);
|
|
1045
|
+
// Se i dati sono cifrati, decifriamoli
|
|
1046
|
+
if (jsonData.type === "encrypted-gun-pair" && jsonData.data && password) {
|
|
1047
|
+
const decryptedData = await SEA.decrypt(jsonData.data, password);
|
|
1048
|
+
if (!decryptedData) {
|
|
1049
|
+
throw new Error("Password non valida o dati corrotti");
|
|
1050
|
+
}
|
|
1051
|
+
pair = JSON.parse(decryptedData);
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
// Altrimenti assumiamo che il JSON sia direttamente il pair
|
|
1055
|
+
pair = jsonData;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
catch (error) {
|
|
1059
|
+
throw new Error("Formato JSON non valido o password errata");
|
|
1060
|
+
}
|
|
1061
|
+
// Verifica che il pair contenga i campi necessari
|
|
1062
|
+
if (!pair || !pair.pub || !pair.priv || !pair.epub || !pair.epriv) {
|
|
1063
|
+
throw new Error("Il pair di Gun non è completo o valido");
|
|
1064
|
+
}
|
|
1065
|
+
// Aggiorna le informazioni dell'utente
|
|
1066
|
+
try {
|
|
1067
|
+
const user = this.gun.user();
|
|
1068
|
+
if (!user) {
|
|
1069
|
+
throw new Error("Gun non disponibile");
|
|
1070
|
+
}
|
|
1071
|
+
// La creazione e l'autenticazione con il pair importato deve essere gestita a livello di applicazione
|
|
1072
|
+
// perché richiede un nuovo logout e login
|
|
1073
|
+
log("Pair di Gun validato con successo, pronto per l'autenticazione");
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
catch (error) {
|
|
1077
|
+
throw new Error(`Errore nell'autenticazione con il pair importato: ${error}`);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
catch (error) {
|
|
1081
|
+
console.error("Errore nell'importazione del pair di Gun:", error);
|
|
1082
|
+
throw error;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Importa un backup completo
|
|
1087
|
+
* @param backupData JSON cifrato contenente tutti i dati dell'utente
|
|
1088
|
+
* @param password Password per decifrare il backup
|
|
1089
|
+
* @param options Opzioni di importazione (quali dati importare)
|
|
1090
|
+
* @returns Un oggetto con il risultato dell'importazione
|
|
1091
|
+
*/
|
|
1092
|
+
async importAllUserData(backupData, password, options = { importMnemonic: true, importWallets: true, importGunPair: true }) {
|
|
1093
|
+
try {
|
|
1094
|
+
if (!password) {
|
|
1095
|
+
throw new Error("La password è obbligatoria per importare il backup");
|
|
1096
|
+
}
|
|
1097
|
+
// Log per debug
|
|
1098
|
+
console.log(`[importAllUserData] Tentativo di importazione backup, lunghezza: ${backupData.length} caratteri`);
|
|
1099
|
+
if (backupData.length > 100) {
|
|
1100
|
+
console.log(`[importAllUserData] Primi 100 caratteri: ${backupData.substring(0, 100)}...`);
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
console.log(`[importAllUserData] Dati completi: ${backupData}`);
|
|
1104
|
+
}
|
|
1105
|
+
// Pulizia dei dati: rimuovi BOM e altri caratteri speciali
|
|
1106
|
+
backupData = backupData.replace(/^\uFEFF/, ''); // Rimuovi BOM
|
|
1107
|
+
backupData = backupData.trim(); // Rimuovi spazi all'inizio e alla fine
|
|
1108
|
+
let decryptedData;
|
|
1109
|
+
// Verifica se i dati sono nel formato corretto
|
|
1110
|
+
try {
|
|
1111
|
+
console.log("[importAllUserData] Tentativo di parsing JSON...");
|
|
1112
|
+
// Verifica che sia un JSON valido
|
|
1113
|
+
if (!backupData.startsWith('{') && !backupData.startsWith('[')) {
|
|
1114
|
+
console.error("[importAllUserData] Il formato non sembra essere JSON valido");
|
|
1115
|
+
throw new Error("Il backup deve essere in formato JSON valido");
|
|
1116
|
+
}
|
|
1117
|
+
const jsonData = JSON.parse(backupData);
|
|
1118
|
+
console.log(`[importAllUserData] JSON parsificato con successo, tipo: ${jsonData.type || "non specificato"}`);
|
|
1119
|
+
if (jsonData.type !== "encrypted-shogun-backup" || !jsonData.data) {
|
|
1120
|
+
console.error("[importAllUserData] Formato del backup non valido:", jsonData);
|
|
1121
|
+
throw new Error("Formato del backup non valido: manca il tipo o i dati");
|
|
1122
|
+
}
|
|
1123
|
+
// Decifra i dati
|
|
1124
|
+
console.log("[importAllUserData] Tentativo di decifratura...");
|
|
1125
|
+
try {
|
|
1126
|
+
decryptedData = await SEA.decrypt(jsonData.data, password);
|
|
1127
|
+
}
|
|
1128
|
+
catch (decryptError) {
|
|
1129
|
+
console.error("[importAllUserData] Errore nella decifratura:", decryptError);
|
|
1130
|
+
throw new Error(`Errore nella decifratura: ${decryptError}`);
|
|
1131
|
+
}
|
|
1132
|
+
if (!decryptedData) {
|
|
1133
|
+
console.error("[importAllUserData] Decifratura fallita: null o undefined");
|
|
1134
|
+
throw new Error("Password non valida o dati corrotti");
|
|
1135
|
+
}
|
|
1136
|
+
console.log("[importAllUserData] Decifratura riuscita, tentativo di parsing del contenuto...");
|
|
1137
|
+
console.log("[importAllUserData] Tipo di dati decifrati:", typeof decryptedData);
|
|
1138
|
+
if (typeof decryptedData === 'string' && decryptedData.length > 50) {
|
|
1139
|
+
console.log("[importAllUserData] Primi 50 caratteri decifrati:", decryptedData.substring(0, 50));
|
|
1140
|
+
}
|
|
1141
|
+
try {
|
|
1142
|
+
decryptedData = JSON.parse(decryptedData);
|
|
1143
|
+
console.log("[importAllUserData] Parsing del contenuto decifrato riuscito");
|
|
1144
|
+
}
|
|
1145
|
+
catch (parseError) {
|
|
1146
|
+
console.error("[importAllUserData] Errore nel parsing del contenuto decifrato:", parseError);
|
|
1147
|
+
throw new Error(`Errore nel parsing del contenuto decifrato: ${parseError}`);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
catch (error) {
|
|
1151
|
+
console.error("[importAllUserData] Errore generale:", error);
|
|
1152
|
+
throw new Error(`Formato JSON non valido o password errata: ${error}`);
|
|
1153
|
+
}
|
|
1154
|
+
// Risultati dell'importazione
|
|
1155
|
+
const result = { success: false };
|
|
1156
|
+
// Importa la mnemonic se richiesto
|
|
1157
|
+
if (options.importMnemonic && decryptedData.mnemonic) {
|
|
1158
|
+
try {
|
|
1159
|
+
console.log("[importAllUserData] Tentativo di importazione mnemonica...");
|
|
1160
|
+
await this.saveUserMasterMnemonic(decryptedData.mnemonic);
|
|
1161
|
+
result.mnemonicImported = true;
|
|
1162
|
+
console.log("[importAllUserData] Mnemonica importata con successo");
|
|
1163
|
+
}
|
|
1164
|
+
catch (error) {
|
|
1165
|
+
console.error("[importAllUserData] Errore nell'importazione della mnemonica:", error);
|
|
1166
|
+
result.mnemonicImported = false;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
console.log("[importAllUserData] Importazione mnemonica non richiesta o mnemonica non trovata");
|
|
1171
|
+
}
|
|
1172
|
+
// Importa i wallet se richiesto
|
|
1173
|
+
if (options.importWallets && decryptedData.wallets && Array.isArray(decryptedData.wallets)) {
|
|
1174
|
+
try {
|
|
1175
|
+
console.log(`[importAllUserData] Tentativo di importazione di ${decryptedData.wallets.length} wallet...`);
|
|
1176
|
+
// Prepara i dati nel formato richiesto da importWalletKeys
|
|
1177
|
+
const walletsData = JSON.stringify({ wallets: decryptedData.wallets });
|
|
1178
|
+
result.walletsImported = await this.importWalletKeys(walletsData);
|
|
1179
|
+
console.log(`[importAllUserData] ${result.walletsImported} wallet importati con successo`);
|
|
1180
|
+
}
|
|
1181
|
+
catch (error) {
|
|
1182
|
+
console.error("[importAllUserData] Errore nell'importazione dei wallet:", error);
|
|
1183
|
+
result.walletsImported = 0;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
console.log("[importAllUserData] Importazione wallet non richiesta o wallet non trovati");
|
|
1188
|
+
if (options.importWallets) {
|
|
1189
|
+
console.log("[importAllUserData] Dettagli wallets:", decryptedData.wallets);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
// Importa il pair di Gun se richiesto
|
|
1193
|
+
if (options.importGunPair && decryptedData.user && decryptedData.user.pair) {
|
|
1194
|
+
try {
|
|
1195
|
+
console.log("[importAllUserData] Tentativo di importazione pair Gun...");
|
|
1196
|
+
// Il pair di Gun viene validato ma non applicato automaticamente
|
|
1197
|
+
// (richiede logout e login che deve essere gestito dall'app)
|
|
1198
|
+
const pairData = JSON.stringify(decryptedData.user.pair);
|
|
1199
|
+
await this.importGunPair(pairData);
|
|
1200
|
+
result.gunPairImported = true;
|
|
1201
|
+
console.log("[importAllUserData] Pair Gun importato con successo");
|
|
1202
|
+
}
|
|
1203
|
+
catch (error) {
|
|
1204
|
+
console.error("[importAllUserData] Errore nell'importazione del pair di Gun:", error);
|
|
1205
|
+
result.gunPairImported = false;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
else {
|
|
1209
|
+
console.log("[importAllUserData] Importazione pair Gun non richiesta o pair non trovato");
|
|
1210
|
+
if (options.importGunPair) {
|
|
1211
|
+
console.log("[importAllUserData] Dettagli user:", decryptedData.user);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
// Imposta il risultato finale
|
|
1215
|
+
result.success = !!((options.importMnemonic && result.mnemonicImported) ||
|
|
1216
|
+
(options.importWallets && result.walletsImported && result.walletsImported > 0) ||
|
|
1217
|
+
(options.importGunPair && result.gunPairImported));
|
|
1218
|
+
console.log("[importAllUserData] Risultato finale:", result);
|
|
1219
|
+
return result;
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
console.error("Errore nell'importazione del backup:", error);
|
|
1223
|
+
throw error;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|