gun-eth 1.4.36 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
package/dist/gun-eth.mjs DELETED
@@ -1,1416 +0,0 @@
1
- import Gun from 'gun';
2
- export { default as Gun, default } from 'gun';
3
- import { ethers } from 'ethers';
4
- import require$$0$1 from 'url';
5
- import require$$1 from 'path';
6
- import require$$2 from 'fs';
7
- import 'gun/sea.js';
8
-
9
- var STEALTH_ANNOUNCER_ADDRESS = "";
10
- var PROOF_OF_INTEGRITY_ADDRESS = "";
11
- var require$$0 = {
12
- STEALTH_ANNOUNCER_ADDRESS: STEALTH_ANNOUNCER_ADDRESS,
13
- PROOF_OF_INTEGRITY_ADDRESS: PROOF_OF_INTEGRITY_ADDRESS
14
- };
15
-
16
- let contractAddresses$1 = {
17
- PROOF_OF_INTEGRITY_ADDRESS: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
18
- STEALTH_ANNOUNCER_ADDRESS: "0x5FbDB2315678afecb367f032d93F642f64180aa3"
19
- };
20
-
21
- if (typeof window === 'undefined') {
22
- const { fileURLToPath } = require$$0$1;
23
- const { dirname } = require$$1;
24
- const { readFileSync } = require$$2;
25
- const { join } = require$$1;
26
-
27
- try {
28
- const __filename = fileURLToPath(import.meta.url);
29
- const __dirname = dirname(__filename);
30
- const rawdata = readFileSync(join(__dirname, 'contract-address.json'), 'utf8');
31
- contractAddresses$1 = JSON.parse(rawdata);
32
- console.log("Loaded contract addresses:", contractAddresses$1);
33
- } catch (error) {
34
- console.warn("Warning: contract-address.json not found or invalid");
35
- }
36
- }
37
-
38
- const LOCAL_CONFIG = {
39
- CHAIN_ID: 1337,
40
- PROOF_OF_INTEGRITY_ADDRESS: contractAddresses$1.PROOF_OF_INTEGRITY_ADDRESS,
41
- STEALTH_ANNOUNCER_ADDRESS: contractAddresses$1.STEALTH_ANNOUNCER_ADDRESS,
42
- RPC_URL: "http://127.0.0.1:8545",
43
- GUN_PEER: "http://localhost:8765/gun"
44
- };
45
-
46
- // Indirizzi di produzione per diverse chain
47
- const CHAIN_CONFIG = {
48
- optimismSepolia: {
49
- STEALTH_ANNOUNCER_ADDRESS: "0xD0F2e9DA59d2DFECFdE67CcF17300BB6093A72f8",
50
- PROOF_OF_INTEGRITY_ADDRESS: "0x...",
51
- RPC_URL: "https://sepolia.optimism.io",
52
- CHAIN_ID: 11155420
53
- },
54
- arbitrumSepolia: {
55
- STEALTH_ANNOUNCER_ADDRESS: "0x...",
56
- PROOF_OF_INTEGRITY_ADDRESS: "0x...",
57
- RPC_URL: "https://sepolia-rollup.arbitrum.io/rpc",
58
- CHAIN_ID: 421614
59
- },
60
- localhost: {
61
- RPC_URL: "http://127.0.0.1:8545",
62
- CHAIN_ID: 1337
63
- }
64
- };
65
-
66
- // Esporta gli indirizzi di default (Optimism Sepolia)
67
- CHAIN_CONFIG.optimismSepolia.STEALTH_ANNOUNCER_ADDRESS;
68
- CHAIN_CONFIG.optimismSepolia.PROOF_OF_INTEGRITY_ADDRESS;
69
-
70
- // Funzione per ottenere gli indirizzi corretti
71
- function getAddressesForChain(chainName) {
72
- let config;
73
-
74
- // Se è localhost, prova a caricare gli indirizzi locali
75
- if (chainName === 'localhost') {
76
- try {
77
- // Carica gli indirizzi dal file generato dal deploy locale
78
- const localAddresses = require$$0;
79
- config = {
80
- ...CHAIN_CONFIG.localhost,
81
- ...localAddresses
82
- };
83
- console.log("Using local addresses:", config);
84
- return config;
85
- } catch (err) {
86
- console.warn('No local addresses found');
87
- throw new Error('No local addresses found. Did you run local deployment?');
88
- }
89
- }
90
-
91
- // Altrimenti usa gli indirizzi di produzione
92
- config = CHAIN_CONFIG[chainName];
93
- if (!config) {
94
- throw new Error(`Chain ${chainName} not supported. Supported chains: ${Object.keys(CHAIN_CONFIG).join(', ')}`);
95
- }
96
-
97
- return config;
98
- }
99
-
100
- // Funzione helper per verificare se siamo in ambiente locale
101
- function isLocalEnvironment() {
102
- return process.env.NODE_ENV === 'development' &&
103
- typeof window !== 'undefined' &&
104
- window.location.hostname === 'localhost';
105
- }
106
-
107
- const STEALTH_ANNOUNCER_ABI = [
108
- {
109
- "inputs": [
110
- {
111
- "internalType": "address",
112
- "name": "_devAddress",
113
- "type": "address"
114
- }
115
- ],
116
- "stateMutability": "nonpayable",
117
- "type": "constructor"
118
- },
119
- {
120
- "anonymous": false,
121
- "inputs": [
122
- {
123
- "internalType": "string",
124
- "name": "senderPublicKey",
125
- "type": "string"
126
- },
127
- {
128
- "internalType": "string",
129
- "name": "spendingPublicKey",
130
- "type": "string"
131
- },
132
- {
133
- "internalType": "address",
134
- "name": "stealthAddress",
135
- "type": "address"
136
- },
137
- {
138
- "internalType": "uint256",
139
- "name": "timestamp",
140
- "type": "uint256"
141
- }
142
- ],
143
- "name": "StealthPaymentAnnounced",
144
- "type": "event"
145
- },
146
- {
147
- "anonymous": false,
148
- "inputs": [
149
- {
150
- "internalType": "address",
151
- "name": "newAddress",
152
- "type": "address"
153
- }
154
- ],
155
- "name": "DevAddressUpdated",
156
- "type": "event"
157
- },
158
- {
159
- "anonymous": false,
160
- "inputs": [
161
- {
162
- "internalType": "uint256",
163
- "name": "newFee",
164
- "type": "uint256"
165
- }
166
- ],
167
- "name": "DevFeeUpdated",
168
- "type": "event"
169
- },
170
- {
171
- "inputs": [
172
- {
173
- "internalType": "string",
174
- "name": "senderPublicKey",
175
- "type": "string"
176
- },
177
- {
178
- "internalType": "string",
179
- "name": "spendingPublicKey",
180
- "type": "string"
181
- },
182
- {
183
- "internalType": "address",
184
- "name": "stealthAddress",
185
- "type": "address"
186
- }
187
- ],
188
- "name": "announcePayment",
189
- "outputs": [],
190
- "stateMutability": "payable",
191
- "type": "function"
192
- },
193
- {
194
- "inputs": [],
195
- "name": "devAddress",
196
- "outputs": [
197
- {
198
- "internalType": "address",
199
- "name": "",
200
- "type": "address"
201
- }
202
- ],
203
- "stateMutability": "view",
204
- "type": "function"
205
- },
206
- {
207
- "inputs": [],
208
- "name": "devFee",
209
- "outputs": [
210
- {
211
- "internalType": "uint256",
212
- "name": "",
213
- "type": "uint256"
214
- }
215
- ],
216
- "stateMutability": "view",
217
- "type": "function"
218
- },
219
- {
220
- "inputs": [],
221
- "name": "getAnnouncementsCount",
222
- "outputs": [
223
- {
224
- "internalType": "uint256",
225
- "name": "",
226
- "type": "uint256"
227
- }
228
- ],
229
- "stateMutability": "view",
230
- "type": "function"
231
- },
232
- {
233
- "inputs": [
234
- {
235
- "internalType": "uint256",
236
- "name": "fromIndex",
237
- "type": "uint256"
238
- },
239
- {
240
- "internalType": "uint256",
241
- "name": "toIndex",
242
- "type": "uint256"
243
- }
244
- ],
245
- "name": "getAnnouncementsInRange",
246
- "outputs": [
247
- {
248
- "components": [
249
- {
250
- "internalType": "string",
251
- "name": "senderPublicKey",
252
- "type": "string"
253
- },
254
- {
255
- "internalType": "string",
256
- "name": "spendingPublicKey",
257
- "type": "string"
258
- },
259
- {
260
- "internalType": "address",
261
- "name": "stealthAddress",
262
- "type": "address"
263
- },
264
- {
265
- "internalType": "uint256",
266
- "name": "timestamp",
267
- "type": "uint256"
268
- }
269
- ],
270
- "internalType": "struct StealthAnnouncer.StealthAnnouncement[]",
271
- "name": "",
272
- "type": "tuple[]"
273
- }
274
- ],
275
- "stateMutability": "view",
276
- "type": "function"
277
- },
278
- {
279
- "inputs": [
280
- {
281
- "internalType": "uint256",
282
- "name": "_newFee",
283
- "type": "uint256"
284
- }
285
- ],
286
- "name": "updateDevFee",
287
- "outputs": [],
288
- "stateMutability": "nonpayable",
289
- "type": "function"
290
- },
291
- {
292
- "inputs": [
293
- {
294
- "internalType": "address",
295
- "name": "_newAddress",
296
- "type": "address"
297
- }
298
- ],
299
- "name": "updateDevAddress",
300
- "outputs": [],
301
- "stateMutability": "nonpayable",
302
- "type": "function"
303
- },
304
- {
305
- "inputs": [],
306
- "name": "withdrawStuckETH",
307
- "outputs": [],
308
- "stateMutability": "nonpayable",
309
- "type": "function"
310
- }
311
- ];
312
-
313
- const PROOF_OF_INTEGRITY_ABI = [
314
- {
315
- "inputs": [
316
- {
317
- "internalType": "bytes[]",
318
- "name": "nodeIds",
319
- "type": "bytes[]"
320
- },
321
- {
322
- "internalType": "bytes32[]",
323
- "name": "contentHashes",
324
- "type": "bytes32[]"
325
- }
326
- ],
327
- "name": "batchUpdateData",
328
- "outputs": [],
329
- "stateMutability": "nonpayable",
330
- "type": "function"
331
- },
332
- {
333
- "anonymous": false,
334
- "inputs": [
335
- {
336
- "indexed": true,
337
- "internalType": "bytes",
338
- "name": "nodeId",
339
- "type": "bytes"
340
- },
341
- {
342
- "indexed": false,
343
- "internalType": "bytes32",
344
- "name": "contentHash",
345
- "type": "bytes32"
346
- },
347
- {
348
- "indexed": false,
349
- "internalType": "address",
350
- "name": "updater",
351
- "type": "address"
352
- }
353
- ],
354
- "name": "DataUpdated",
355
- "type": "event"
356
- },
357
- {
358
- "inputs": [
359
- {
360
- "internalType": "bytes",
361
- "name": "nodeId",
362
- "type": "bytes"
363
- }
364
- ],
365
- "name": "getLatestRecord",
366
- "outputs": [
367
- {
368
- "internalType": "bytes32",
369
- "name": "",
370
- "type": "bytes32"
371
- },
372
- {
373
- "internalType": "uint256",
374
- "name": "",
375
- "type": "uint256"
376
- },
377
- {
378
- "internalType": "address",
379
- "name": "",
380
- "type": "address"
381
- }
382
- ],
383
- "stateMutability": "view",
384
- "type": "function"
385
- },
386
- {
387
- "inputs": [
388
- {
389
- "internalType": "bytes",
390
- "name": "nodeId",
391
- "type": "bytes"
392
- },
393
- {
394
- "internalType": "bytes32",
395
- "name": "contentHash",
396
- "type": "bytes32"
397
- }
398
- ],
399
- "name": "updateData",
400
- "outputs": [],
401
- "stateMutability": "nonpayable",
402
- "type": "function"
403
- },
404
- {
405
- "inputs": [
406
- {
407
- "internalType": "bytes",
408
- "name": "nodeId",
409
- "type": "bytes"
410
- },
411
- {
412
- "internalType": "bytes32",
413
- "name": "contentHash",
414
- "type": "bytes32"
415
- }
416
- ],
417
- "name": "verifyData",
418
- "outputs": [
419
- {
420
- "internalType": "bool",
421
- "name": "",
422
- "type": "bool"
423
- },
424
- {
425
- "internalType": "uint256",
426
- "name": "",
427
- "type": "uint256"
428
- },
429
- {
430
- "internalType": "address",
431
- "name": "",
432
- "type": "address"
433
- }
434
- ],
435
- "stateMutability": "view",
436
- "type": "function"
437
- }
438
- ];
439
-
440
- const SEA = Gun.SEA;
441
-
442
- let PROOF_CONTRACT_ADDRESS;
443
- let contractAddresses = {
444
- PROOF_OF_INTEGRITY_ADDRESS:
445
- CHAIN_CONFIG.optimismSepolia.PROOF_OF_INTEGRITY_ADDRESS,
446
- STEALTH_ANNOUNCER_ADDRESS:
447
- CHAIN_CONFIG.optimismSepolia.STEALTH_ANNOUNCER_ADDRESS,
448
- };
449
-
450
- // Funzione di inizializzazione per Node.js
451
- const initNodeEnvironment = async () => {
452
- if (typeof window === "undefined") {
453
- try {
454
- // Importazioni dinamiche
455
- const { fileURLToPath } = await import('url');
456
- const { dirname } = await import('path');
457
- const { readFile } = await import('fs/promises');
458
- const { join } = await import('path');
459
-
460
- const __filename = fileURLToPath(import.meta.url);
461
- const __dirname = dirname(__filename);
462
-
463
- try {
464
- const rawdata = await readFile(
465
- join(__dirname, "contract-address.json"),
466
- "utf8"
467
- );
468
- contractAddresses = JSON.parse(rawdata);
469
- console.log("Loaded contract addresses:", contractAddresses);
470
- } catch (err) {
471
- console.warn("Warning: contract-address.json not found or invalid");
472
- }
473
- } catch (error) {
474
- console.error("Error loading Node.js modules:", error);
475
- }
476
- }
477
- };
478
-
479
- // Inizializza solo in ambiente Node.js
480
- if (typeof window === "undefined") {
481
- initNodeEnvironment();
482
- }
483
-
484
- // =============================================
485
- // UTILITY FUNCTIONS
486
- // =============================================
487
- /**
488
- * Generates a random node ID for GunDB
489
- * @returns {string} A random hexadecimal string
490
- */
491
- function generateRandomId() {
492
- const randomBytes = ethers.randomBytes(32);
493
- return ethers.hexlify(randomBytes).slice(2);
494
- }
495
-
496
- /**
497
- * Generates a password from a signature.
498
- * @param {string} signature - The signature to derive the password from.
499
- * @returns {string|null} The generated password or null if generation fails.
500
- */
501
- function generatePassword(signature) {
502
- try {
503
- const signatureBytes = ethers.toUtf8Bytes(signature);
504
- const hash = ethers.keccak256(signatureBytes);
505
- console.log("Generated password:", hash);
506
- return hash;
507
- } catch (error) {
508
- console.error("Error generating password:", error);
509
- return null;
510
- }
511
- }
512
-
513
- /**
514
- * Converts a Gun private key to an Ethereum account.
515
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
516
- * @returns {Object} An object containing the Ethereum account and public key.
517
- */
518
- function gunToEthAccount(gunPrivateKey) {
519
- // Function to convert base64url to hex
520
- const base64UrlToHex = (base64url) => {
521
- const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
522
- const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
523
- const binary = atob(base64);
524
- return Array.from(binary, (char) =>
525
- char.charCodeAt(0).toString(16).padStart(2, "0")
526
- ).join("");
527
- };
528
-
529
- // Convert Gun private key to hex format
530
- const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
531
-
532
- // Create an Ethereum wallet from the private key
533
- const wallet = new ethers.Wallet(hexPrivateKey);
534
-
535
- // Get the public address (public key)
536
- const publicKey = wallet.address;
537
-
538
- return {
539
- account: wallet,
540
- publicKey: publicKey,
541
- privateKey: hexPrivateKey,
542
- };
543
- }
544
-
545
- /**
546
- * Gets an Ethereum signer based on current configuration
547
- * @returns {Promise<ethers.Signer>} The configured signer
548
- * @throws {Error} If no valid provider is found
549
- */
550
- const getSigner = async () => {
551
- if (rpcUrl && privateKey) {
552
- const provider = new ethers.JsonRpcProvider(rpcUrl, {
553
- chainId: LOCAL_CONFIG.CHAIN_ID,
554
- name: "localhost",
555
- });
556
- return new Wallet(privateKey, provider);
557
- } else if (
558
- typeof window !== "undefined" &&
559
- typeof window.ethereum !== "undefined"
560
- ) {
561
- await window.ethereum.request({ method: "eth_requestAccounts" });
562
- const provider = new ethers.BrowserProvider(window.ethereum);
563
- return provider.getSigner();
564
- } else {
565
- throw new Error("No valid Ethereum provider found");
566
- }
567
- };
568
-
569
- /**
570
- * Utility function to generate stealth address
571
- * @param {string} sharedSecret - The shared secret
572
- * @param {string} spendingPublicKey - The spending public key
573
- * @returns {Object} The stealth address and private key
574
- */
575
- function deriveStealthAddress(sharedSecret, spendingPublicKey) {
576
- try {
577
- const sharedSecretBytes = ethers.toUtf8Bytes(sharedSecret);
578
- const spendingPublicKeyBytes = ethers.toUtf8Bytes(spendingPublicKey);
579
-
580
- const stealthPrivateKey = ethers.keccak256(
581
- ethers.concat([sharedSecretBytes, spendingPublicKeyBytes])
582
- );
583
-
584
- const stealthWallet = new Wallet(stealthPrivateKey);
585
-
586
- console.log("Debug deriveStealthAddress:", {
587
- sharedSecretHex: stealthPrivateKey,
588
- spendingPublicKey,
589
- stealthPrivateKey,
590
- stealthAddress: stealthWallet.address,
591
- });
592
-
593
- return {
594
- stealthPrivateKey,
595
- stealthAddress: stealthWallet.address,
596
- wallet: stealthWallet,
597
- };
598
- } catch (error) {
599
- console.error("Error in deriveStealthAddress:", error);
600
- throw error;
601
- }
602
- }
603
-
604
- // =============================================
605
- // BASIC GUN-ETH CHAIN METHODS
606
- // =============================================
607
-
608
- // Set the message to sign
609
- Gun.chain.MESSAGE_TO_SIGN = MESSAGE_TO_SIGN;
610
-
611
- /**
612
- * Sets standalone configuration for Gun.
613
- * @param {string} newRpcUrl - The new RPC URL.
614
- * @param {string} newPrivateKey - The new private key.
615
- * @returns {Gun} The Gun instance for chaining.
616
- */
617
- Gun.chain.setSigner = function (newRpcUrl, newPrivateKey) {
618
- rpcUrl = newRpcUrl;
619
- privateKey = newPrivateKey;
620
- console.log("Standalone configuration set");
621
- return this;
622
- };
623
-
624
- Gun.chain.getSigner = getSigner();
625
-
626
- /**
627
- * Verifies an Ethereum signature.
628
- * @param {string} message - The original message that was signed.
629
- * @param {string} signature - The signature to verify.
630
- * @returns {Promise<string|null>} The recovered address or null if verification fails.
631
- */
632
- Gun.chain.verifySignature = async function (message, signature) {
633
- try {
634
- const recoveredAddress = ethers.verifyMessage(message, signature);
635
- return recoveredAddress;
636
- } catch (error) {
637
- console.error("Error verifying signature:", error);
638
- return null;
639
- }
640
- };
641
-
642
- /**
643
- * Generates a password from a signature.
644
- * @param {string} signature - The signature to derive the password from.
645
- * @returns {string|null} The generated password or null if generation fails.
646
- */
647
- Gun.chain.generatePassword = function (signature) {
648
- return generatePassword(signature);
649
- };
650
-
651
- /**
652
- * Creates an Ethereum signature for a given message.
653
- * @param {string} message - The message to sign.
654
- * @returns {Promise<string|null>} The signature or null if signing fails.
655
- */
656
- Gun.chain.createSignature = async function (message) {
657
- try {
658
- // Check if message matches MESSAGE_TO_SIGN
659
- if (message !== MESSAGE_TO_SIGN) {
660
- throw new Error("Invalid message, valid message is: " + MESSAGE_TO_SIGN);
661
- }
662
- const signer = await getSigner();
663
- const signature = await signer.signMessage(message);
664
- console.log("Signature created:", signature);
665
- return signature;
666
- } catch (error) {
667
- console.error("Error creating signature:", error);
668
- return null;
669
- }
670
- };
671
-
672
- // =============================================
673
- // KEY PAIR MANAGEMENT
674
- // =============================================
675
- /**
676
- * Creates and stores an encrypted key pair for a given address.
677
- * @param {string} address - The Ethereum address to associate with the key pair.
678
- * @param {string} signature - The signature to use for encryption.
679
- * @returns {Promise<void>}
680
- */
681
- Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
682
- try {
683
- const gun = this;
684
- const pair = await SEA.pair();
685
- const v_pair = await SEA.pair();
686
- const s_pair = await SEA.pair();
687
- const password = generatePassword(signature);
688
-
689
- // Save original SEA pairs
690
- const encryptedPair = await SEA.encrypt(JSON.stringify(pair), password);
691
- const encryptedV_pair = await SEA.encrypt(JSON.stringify(v_pair), password);
692
- const encryptedS_pair = await SEA.encrypt(JSON.stringify(s_pair), password);
693
-
694
- // Convert only to get Ethereum addresses
695
- const viewingAccount = gunToEthAccount(v_pair.priv);
696
- const spendingAccount = gunToEthAccount(s_pair.priv);
697
-
698
- gun
699
- .get("gun-eth")
700
- .get("users")
701
- .get(address)
702
- .put({
703
- pair: encryptedPair,
704
- v_pair: encryptedV_pair,
705
- s_pair: encryptedS_pair,
706
- publicKeys: {
707
- viewingPublicKey: v_pair.epub, // Use SEA encryption public key
708
- viewingPublicKey: v_pair.epub, // Use SEA encryption public key
709
- spendingPublicKey: spendingAccount.publicKey, // Use Ethereum address
710
- ethViewingAddress: viewingAccount.publicKey, // Also save Ethereum address
711
- },
712
- });
713
-
714
- console.log("Encrypted pairs and public keys stored for:", address);
715
- } catch (error) {
716
- console.error("Error creating and storing encrypted pair:", error);
717
- throw error;
718
- }
719
- };
720
-
721
- /**
722
- * Retrieves and decrypts a stored key pair for a given address.
723
- * @param {string} address - The Ethereum address associated with the key pair.
724
- * @param {string} signature - The signature to use for decryption.
725
- * @returns {Promise<Object|null>} The decrypted key pair or null if retrieval fails.
726
- */
727
- Gun.chain.getAndDecryptPair = async function (address, signature) {
728
- try {
729
- const gun = this;
730
- const encryptedData = await gun
731
- .get("gun-eth")
732
- .get("users")
733
- .get(address)
734
- .get("pair")
735
- .then();
736
- if (!encryptedData) {
737
- throw new Error("No encrypted data found for this address");
738
- }
739
- const password = generatePassword(signature);
740
- const decryptedPair = await SEA.decrypt(encryptedData, password);
741
- console.log(decryptedPair);
742
- return decryptedPair;
743
- } catch (error) {
744
- console.error("Error retrieving and decrypting pair:", error);
745
- return null;
746
- }
747
- };
748
-
749
- // =============================================
750
- // PROOF OF INTEGRITY
751
- // =============================================
752
- /**
753
- * Proof of Integrity
754
- * @param {string} chain - The blockchain to use (e.g., "optimismSepolia").
755
- * @param {string} nodeId - The ID of the node to verify or write.
756
- * @param {Object} data - The data to write (if writing).
757
- * @param {Function} callback - Callback function to handle the result.
758
- * @returns {Gun} The Gun instance for chaining.
759
- */
760
- Gun.chain.proof = function (chain, nodeId, data, callback) {
761
- console.log("Proof plugin called with:", { chain, nodeId, data });
762
-
763
- if (typeof callback !== "function") {
764
- console.error("Callback must be a function");
765
- return this;
766
- }
767
-
768
- try {
769
- // Se siamo in localhost e in development, usa automaticamente la chain locale
770
- const targetChain = isLocalEnvironment() ? "localhost" : chain;
771
- const chainConfig = getAddressesForChain(targetChain);
772
-
773
- console.log(`Using ${targetChain} configuration:`, chainConfig);
774
-
775
- // Usa gli indirizzi dalla configurazione
776
- const contract = new ethers.Contract(
777
- chainConfig.PROOF_OF_INTEGRITY_ADDRESS,
778
- PROOF_OF_INTEGRITY_ABI,
779
- signer
780
- );
781
-
782
- // Funzione per verificare on-chain
783
- const verifyOnChain = async (nodeId, contentHash) => {
784
- console.log("Verifying on chain:", { nodeId, contentHash });
785
- const signer = await getSigner();
786
- const contract = new Contract(
787
- PROOF_CONTRACT_ADDRESS,
788
- PROOF_OF_INTEGRITY_ABI,
789
- signer
790
- );
791
- const [isValid, timestamp, updater] = await contract.verifyData(
792
- ethers.toUtf8Bytes(nodeId),
793
- contentHash
794
- );
795
- console.log("Verification result:", { isValid, timestamp, updater });
796
- return { isValid, timestamp, updater };
797
- };
798
-
799
- // Funzione per scrivere on-chain
800
- const writeOnChain = async (nodeId, contentHash) => {
801
- console.log("Writing on chain:", { nodeId, contentHash });
802
- const signer = await getSigner();
803
- const contract = new Contract(
804
- PROOF_CONTRACT_ADDRESS,
805
- PROOF_OF_INTEGRITY_ABI,
806
- signer
807
- );
808
- const tx = await contract.updateData(toUtf8Bytes(nodeId), contentHash);
809
- console.log("Transaction sent:", tx.hash);
810
- const receipt = await tx.wait();
811
- console.log("Transaction confirmed:", receipt);
812
- return tx;
813
- };
814
-
815
- // Funzione per ottenere l'ultimo record
816
- const getLatestRecord = async (nodeId) => {
817
- const signer = await getSigner();
818
- const contract = new Contract(
819
- PROOF_CONTRACT_ADDRESS,
820
- PROOF_OF_INTEGRITY_ABI,
821
- signer
822
- );
823
- const [contentHash, timestamp, updater] = await contract.getLatestRecord(
824
- toUtf8Bytes(nodeId)
825
- );
826
- console.log("Latest record from blockchain:", {
827
- nodeId,
828
- contentHash,
829
- timestamp,
830
- updater,
831
- });
832
- return { contentHash, timestamp, updater };
833
- };
834
-
835
- if (nodeId && !data) {
836
- // Case 1: User passes only node
837
- gun.get(nodeId).once(async (existingData) => {
838
- if (!existingData) {
839
- if (callback) callback({ err: "Node not found in GunDB" });
840
- return;
841
- }
842
-
843
- console.log("existingData", existingData);
844
-
845
- // Use stored contentHash instead of recalculating
846
- const contentHash = existingData._contentHash;
847
- console.log("contentHash", contentHash);
848
-
849
- if (!contentHash) {
850
- if (callback)
851
- callback({ err: "No content hash found for this node" });
852
- return;
853
- }
854
-
855
- try {
856
- const { isValid, timestamp, updater } = await verifyOnChain(
857
- nodeId,
858
- contentHash
859
- );
860
- const latestRecord = await getLatestRecord(nodeId);
861
-
862
- if (isValid) {
863
- if (callback)
864
- callback({
865
- ok: true,
866
- message: "Data verified on blockchain",
867
- timestamp,
868
- updater,
869
- latestRecord,
870
- });
871
- } else {
872
- if (callback)
873
- callback({
874
- ok: false,
875
- message: "Data not verified on blockchain",
876
- latestRecord,
877
- });
878
- }
879
- } catch (error) {
880
- if (callback) callback({ err: error.message });
881
- }
882
- });
883
- } else if (data && !nodeId) {
884
- // Case 2: User passes only text (data)
885
- const newNodeId = generateRandomId();
886
- const dataString = JSON.stringify(data);
887
- const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
888
-
889
- gun
890
- .get(newNodeId)
891
- .put({ ...data, _contentHash: contentHash }, async (ack) => {
892
- console.log("ack", ack);
893
- if (ack.err) {
894
- if (callback) callback({ err: "Error saving data to GunDB" });
895
- return;
896
- }
897
-
898
- try {
899
- const tx = await writeOnChain(newNodeId, contentHash);
900
- if (callback)
901
- callback({
902
- ok: true,
903
- message: "Data written to GunDB and blockchain",
904
- nodeId: newNodeId,
905
- txHash: tx.hash,
906
- });
907
- } catch (error) {
908
- if (callback) callback({ err: error.message });
909
- }
910
- });
911
- } else {
912
- if (callback)
913
- callback({
914
- err: "Invalid input. Provide either nodeId or data, not both.",
915
- });
916
- }
917
-
918
- return gun;
919
- } catch (error) {
920
- callback({ err: error.message });
921
- return this;
922
- }
923
- };
924
-
925
- // =============================================
926
- // STEALTH ADDRESS CORE FUNCTIONS
927
- // =============================================
928
- /**
929
- * Converts a Gun private key to an Ethereum account.
930
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
931
- * @returns {Object} An object containing the Ethereum account and public key.
932
- */
933
- Gun.chain.gunToEthAccount = function (gunPrivateKey) {
934
- return gunToEthAccount(gunPrivateKey);
935
- };
936
-
937
- /**
938
- * Generate a stealth key and related key pairs
939
- * @param {string} recipientAddress - The recipient's Ethereum address
940
- * @param {string} signature - The sender's signature to access their keys
941
- * @returns {Promise<Object>} Object containing stealth addresses and keys
942
- */
943
- Gun.chain.generateStealthAddress = async function (
944
- recipientAddress,
945
- signature
946
- ) {
947
- try {
948
- const gun = this;
949
-
950
- // Get recipient's public keys
951
- const recipientData = await gun
952
- .get("gun-eth")
953
- .get("users")
954
- .get(recipientAddress)
955
- .get("publicKeys")
956
- .then();
957
-
958
- if (
959
- !recipientData ||
960
- !recipientData.viewingPublicKey ||
961
- !recipientData.spendingPublicKey
962
- ) {
963
- throw new Error("Recipient's public keys not found");
964
- }
965
-
966
- // Get sender's keys
967
- const senderAddress = await this.verifySignature(
968
- MESSAGE_TO_SIGN,
969
- signature
970
- );
971
- const password = generatePassword(signature);
972
-
973
- const senderData = await gun
974
- .get("gun-eth")
975
- .get("users")
976
- .get(senderAddress)
977
- .then();
978
-
979
- if (!senderData || !senderData.s_pair) {
980
- throw new Error("Sender's keys not found");
981
- }
982
-
983
- // Decrypt sender's spending pair
984
- let spendingKeyPair;
985
- try {
986
- const decryptedData = await SEA.decrypt(senderData.s_pair, password);
987
- spendingKeyPair =
988
- typeof decryptedData === "string"
989
- ? JSON.parse(decryptedData)
990
- : decryptedData;
991
- } catch (error) {
992
- console.error("Error decrypting spending pair:", error);
993
- throw new Error("Unable to decrypt spending pair");
994
- }
995
-
996
- // Generate shared secret using SEA ECDH with encryption public key
997
- const sharedSecret = await SEA.secret(
998
- recipientData.viewingPublicKey,
999
- spendingKeyPair
1000
- );
1001
-
1002
- if (!sharedSecret) {
1003
- throw new Error("Unable to generate shared secret");
1004
- }
1005
-
1006
- console.log("Generate shared secret:", sharedSecret);
1007
-
1008
- const { stealthAddress } = deriveStealthAddress(
1009
- sharedSecret,
1010
- recipientData.spendingPublicKey
1011
- );
1012
-
1013
- return {
1014
- stealthAddress,
1015
- senderPublicKey: spendingKeyPair.epub, // Use encryption public key
1016
- spendingPublicKey: recipientData.spendingPublicKey,
1017
- };
1018
- } catch (error) {
1019
- console.error("Error generating stealth address:", error);
1020
- throw error;
1021
- }
1022
- };
1023
-
1024
- /**
1025
- * Publish public keys needed to receive stealth payments
1026
- * @param {string} signature - The signature to authenticate the user
1027
- * @returns {Promise<void>}
1028
- */
1029
- Gun.chain.publishStealthKeys = async function (signature) {
1030
- try {
1031
- const gun = this;
1032
- const address = await this.verifySignature(MESSAGE_TO_SIGN, signature);
1033
- const password = generatePassword(signature);
1034
-
1035
- // Get encrypted key pairs
1036
- const encryptedData = await gun
1037
- .get("gun-eth")
1038
- .get("users")
1039
- .get(address)
1040
- .then();
1041
-
1042
- if (!encryptedData || !encryptedData.v_pair || !encryptedData.s_pair) {
1043
- throw new Error("Keys not found");
1044
- }
1045
-
1046
- // Decrypt viewing and spending pairs
1047
- const viewingKeyPair = JSON.parse(
1048
- await SEA.decrypt(encryptedData.v_pair, password)
1049
- );
1050
- const spendingKeyPair = JSON.parse(
1051
- await SEA.decrypt(encryptedData.s_pair, password)
1052
- );
1053
-
1054
- const viewingAccount = gunToEthAccount(viewingKeyPair.priv);
1055
- const spendingAccount = gunToEthAccount(spendingKeyPair.priv);
1056
-
1057
- // Publish only public keys
1058
- gun.get("gun-eth").get("users").get(address).get("publicKeys").put({
1059
- viewingPublicKey: viewingAccount.publicKey,
1060
- spendingPublicKey: spendingAccount.publicKey,
1061
- });
1062
-
1063
- console.log("Stealth public keys published successfully");
1064
- } catch (error) {
1065
- console.error("Error publishing stealth keys:", error);
1066
- throw error;
1067
- }
1068
- };
1069
-
1070
- // =============================================
1071
- // STEALTH PAYMENT FUNCTIONS
1072
- // =============================================
1073
- /**
1074
- * Recover funds from a stealth address
1075
- * @param {string} stealthAddress - The stealth address to recover funds from
1076
- * @param {string} senderPublicKey - The sender's public key used to generate the address
1077
- * @param {string} signature - The signature to decrypt private keys
1078
- * @returns {Promise<Object>} Object containing wallet to access funds
1079
- */
1080
- Gun.chain.recoverStealthFunds = async function (
1081
- stealthAddress,
1082
- senderPublicKey,
1083
- signature,
1084
- spendingPublicKey
1085
- ) {
1086
- try {
1087
- const gun = this;
1088
- const password = generatePassword(signature);
1089
-
1090
- // Get own key pairs
1091
- const myAddress = await this.verifySignature(MESSAGE_TO_SIGN, signature);
1092
- const encryptedData = await gun
1093
- .get("gun-eth")
1094
- .get("users")
1095
- .get(myAddress)
1096
- .then();
1097
-
1098
- if (!encryptedData || !encryptedData.v_pair || !encryptedData.s_pair) {
1099
- throw new Error("Keys not found");
1100
- }
1101
-
1102
- // Decrypt viewing and spending pairs
1103
- let viewingKeyPair;
1104
- try {
1105
- const decryptedViewingData = await SEA.decrypt(
1106
- encryptedData.v_pair,
1107
- password
1108
- );
1109
- viewingKeyPair =
1110
- typeof decryptedViewingData === "string"
1111
- ? JSON.parse(decryptedViewingData)
1112
- : decryptedViewingData;
1113
- } catch (error) {
1114
- console.error("Error decrypting keys:", error);
1115
- throw new Error("Unable to decrypt keys");
1116
- }
1117
-
1118
- // Generate shared secret using SEA ECDH
1119
- const sharedSecret = await SEA.secret(senderPublicKey, viewingKeyPair);
1120
-
1121
- if (!sharedSecret) {
1122
- throw new Error("Unable to generate shared secret");
1123
- }
1124
-
1125
- console.log("Recover shared secret:", sharedSecret);
1126
-
1127
- const { wallet, stealthAddress: recoveredAddress } = deriveStealthAddress(
1128
- sharedSecret,
1129
- spendingPublicKey
1130
- );
1131
-
1132
- // Verify address matches
1133
- if (recoveredAddress.toLowerCase() !== stealthAddress.toLowerCase()) {
1134
- console.error("Mismatch:", {
1135
- recovered: recoveredAddress,
1136
- expected: stealthAddress,
1137
- sharedSecret,
1138
- });
1139
- throw new Error("Recovered stealth address does not match");
1140
- }
1141
-
1142
- return {
1143
- wallet,
1144
- address: recoveredAddress,
1145
- };
1146
- } catch (error) {
1147
- console.error("Error recovering stealth funds:", error);
1148
- throw error;
1149
- }
1150
- };
1151
-
1152
- /**
1153
- * Announce a stealth payment
1154
- * @param {string} stealthAddress - The generated stealth address
1155
- * @param {string} senderPublicKey - The sender's public key
1156
- * @param {string} spendingPublicKey - The spending public key
1157
- * @param {string} signature - The sender's signature
1158
- * @returns {Promise<void>}
1159
- */
1160
- Gun.chain.announceStealthPayment = async function (
1161
- stealthAddress,
1162
- senderPublicKey,
1163
- spendingPublicKey,
1164
- signature,
1165
- options = { onChain: false, chain: "optimismSepolia" }
1166
- ) {
1167
- try {
1168
- const gun = this;
1169
- const senderAddress = await this.verifySignature(
1170
- MESSAGE_TO_SIGN,
1171
- signature
1172
- );
1173
-
1174
- if (options.onChain) {
1175
- // On-chain announcement
1176
- const signer = await getSigner();
1177
- const chainConfig = getAddressesForChain(options.chain);
1178
- const contractAddress = chainConfig.STEALTH_ANNOUNCER_ADDRESS;
1179
-
1180
- console.log("Using contract address:", contractAddress);
1181
-
1182
- const contract = new Contract(
1183
- contractAddress,
1184
- STEALTH_ANNOUNCER_ABI,
1185
- signer
1186
- );
1187
-
1188
- // Get dev fee from contract
1189
- const devFee = await contract.devFee();
1190
- console.log("Dev fee:", devFee.toString());
1191
-
1192
- // Call contract
1193
- const tx = await contract.announcePayment(
1194
- senderPublicKey,
1195
- spendingPublicKey,
1196
- stealthAddress,
1197
- { value: devFee }
1198
- );
1199
-
1200
- console.log("Transaction sent:", tx.hash);
1201
- const receipt = await tx.wait();
1202
- console.log("Transaction confirmed:", receipt.hash);
1203
-
1204
- console.log("Stealth payment announced on-chain (dev fee paid)");
1205
- } else {
1206
- // Off-chain announcement (GunDB)
1207
- gun.get("gun-eth").get("stealth-payments").set({
1208
- stealthAddress,
1209
- senderAddress,
1210
- senderPublicKey,
1211
- spendingPublicKey,
1212
- timestamp: Date.now(),
1213
- });
1214
- console.log("Stealth payment announced off-chain");
1215
- }
1216
- } catch (error) {
1217
- console.error("Error announcing stealth payment:", error);
1218
- console.error("Error details:", error.stack);
1219
- throw error;
1220
- }
1221
- };
1222
-
1223
- /**
1224
- * Get all stealth payments for an address
1225
- * @param {string} signature - The signature to authenticate the user
1226
- * @returns {Promise<Array>} List of stealth payments
1227
- */
1228
- Gun.chain.getStealthPayments = async function (
1229
- signature,
1230
- options = { source: "both" }
1231
- ) {
1232
- try {
1233
- const payments = [];
1234
-
1235
- if (options.source === "onChain" || options.source === "both") {
1236
- const signer = await getSigner();
1237
- const chainConfig = getAddressesForChain(
1238
- options.chain || "optimismSepolia"
1239
- );
1240
- const contractAddress = chainConfig.STEALTH_ANNOUNCER_ADDRESS;
1241
-
1242
- const contract = new Contract(
1243
- contractAddress,
1244
- STEALTH_ANNOUNCER_ABI,
1245
- signer
1246
- );
1247
-
1248
- try {
1249
- // Get total number of announcements
1250
- const totalAnnouncements = await contract.getAnnouncementsCount();
1251
- const totalCount = Number(totalAnnouncements.toString());
1252
- console.log("Total on-chain announcements:", totalCount);
1253
-
1254
- if (totalCount > 0) {
1255
- // Get announcements in batches of 100
1256
- const batchSize = 100;
1257
- const lastIndex = totalCount - 1;
1258
-
1259
- for (let i = 0; i <= lastIndex; i += batchSize) {
1260
- const toIndex = Math.min(i + batchSize - 1, lastIndex);
1261
- const batch = await contract.getAnnouncementsInRange(i, toIndex);
1262
-
1263
- // For each announcement, try to decrypt
1264
- for (const announcement of batch) {
1265
- try {
1266
- // Verify announcement is valid
1267
- if (
1268
- !announcement ||
1269
- !announcement.stealthAddress ||
1270
- !announcement.senderPublicKey ||
1271
- !announcement.spendingPublicKey
1272
- ) {
1273
- console.log("Invalid announcement:", announcement);
1274
- continue;
1275
- }
1276
-
1277
- // Try to recover funds to verify if announcement is for us
1278
- const recoveredWallet = await this.recoverStealthFunds(
1279
- announcement.stealthAddress,
1280
- announcement.senderPublicKey,
1281
- signature,
1282
- announcement.spendingPublicKey
1283
- );
1284
-
1285
- // If no errors thrown, announcement is for us
1286
- payments.push({
1287
- stealthAddress: announcement.stealthAddress,
1288
- senderPublicKey: announcement.senderPublicKey,
1289
- spendingPublicKey: announcement.spendingPublicKey,
1290
- timestamp: Number(announcement.timestamp),
1291
- source: "onChain",
1292
- wallet: recoveredWallet,
1293
- });
1294
- } catch (e) {
1295
- // Not for us, continue
1296
- console.log(
1297
- `Announcement not for us: ${announcement.stealthAddress}`
1298
- );
1299
- continue;
1300
- }
1301
- }
1302
- }
1303
- }
1304
- } catch (error) {
1305
- console.error("Error retrieving on-chain announcements:", error);
1306
- }
1307
- }
1308
-
1309
- if (options.source === "offChain" || options.source === "both") {
1310
- // Get off-chain payments
1311
- const gun = this;
1312
- const offChainPayments = await new Promise((resolve) => {
1313
- const p = [];
1314
- gun
1315
- .get("gun-eth")
1316
- .get("stealth-payments")
1317
- .get(recipientAddress)
1318
- .map()
1319
- .once((payment, id) => {
1320
- if (payment?.stealthAddress) {
1321
- p.push({ ...payment, id, source: "offChain" });
1322
- }
1323
- });
1324
- setTimeout(() => resolve(p), 2000);
1325
- });
1326
-
1327
- payments.push(...offChainPayments);
1328
- }
1329
-
1330
- console.log(`Found ${payments.length} stealth payments`);
1331
- return payments;
1332
- } catch (error) {
1333
- console.error("Error retrieving stealth payments:", error);
1334
- throw error;
1335
- }
1336
- };
1337
-
1338
- /**
1339
- * Clean up old stealth payments
1340
- * @param {string} recipientAddress - The recipient's address
1341
- * @returns {Promise<void>}
1342
- */
1343
- Gun.chain.cleanStealthPayments = async function (recipientAddress) {
1344
- try {
1345
- const gun = this;
1346
- const payments = await gun
1347
- .get("gun-eth")
1348
- .get("stealth-payments")
1349
- .get(recipientAddress)
1350
- .map()
1351
- .once()
1352
- .then();
1353
-
1354
- // Remove empty or invalid nodes
1355
- if (payments) {
1356
- Object.keys(payments).forEach(async (key) => {
1357
- const payment = payments[key];
1358
- if (
1359
- !payment ||
1360
- !payment.stealthAddress ||
1361
- !payment.senderPublicKey ||
1362
- !payment.spendingPublicKey
1363
- ) {
1364
- await gun
1365
- .get("gun-eth")
1366
- .get("stealth-payments")
1367
- .get(recipientAddress)
1368
- .get(key)
1369
- .put(null);
1370
- }
1371
- });
1372
- }
1373
- } catch (error) {
1374
- console.error("Error cleaning stealth payments:", error);
1375
- }
1376
- };
1377
-
1378
- // =============================================
1379
- // EXPORTS
1380
- // =============================================
1381
-
1382
- // Crea una classe GunEth che contiene tutti i metodi e le utility
1383
- class GunEth {
1384
- // Static utility methods
1385
- static generateRandomId = generateRandomId;
1386
- static generatePassword = generatePassword;
1387
- static gunToEthAccount = gunToEthAccount;
1388
- static getSigner = getSigner;
1389
- static deriveStealthAddress = deriveStealthAddress;
1390
-
1391
- // Chain methods
1392
- static chainMethods = {
1393
- setSigner: Gun.chain.setSigner,
1394
- getSigner: Gun.chain.getSigner,
1395
- verifySignature: Gun.chain.verifySignature,
1396
- generatePassword: Gun.chain.generatePassword,
1397
- createSignature: Gun.chain.createSignature,
1398
- createAndStoreEncryptedPair: Gun.chain.createAndStoreEncryptedPair,
1399
- getAndDecryptPair: Gun.chain.getAndDecryptPair,
1400
- proof: Gun.chain.proof,
1401
- gunToEthAccount: Gun.chain.gunToEthAccount,
1402
- generateStealthAddress: Gun.chain.generateStealthAddress,
1403
- publishStealthKeys: Gun.chain.publishStealthKeys,
1404
- recoverStealthFunds: Gun.chain.recoverStealthFunds,
1405
- announceStealthPayment: Gun.chain.announceStealthPayment,
1406
- getStealthPayments: Gun.chain.getStealthPayments,
1407
- cleanStealthPayments: Gun.chain.cleanStealthPayments,
1408
- };
1409
-
1410
- // Constants
1411
- static MESSAGE_TO_SIGN = MESSAGE_TO_SIGN;
1412
- static PROOF_CONTRACT_ADDRESS = PROOF_CONTRACT_ADDRESS;
1413
- static LOCAL_CONFIG = LOCAL_CONFIG;
1414
- }
1415
-
1416
- export { GunEth, deriveStealthAddress, generatePassword, generateRandomId, getSigner, gunToEthAccount, initNodeEnvironment };