gun-eth 1.2.14 โ†’ 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -5,18 +5,19 @@
5
5
 
6
6
  ## Table of Contents
7
7
 
8
- 1. [Description](#description)
9
- 2. [Smart Contract](#smart-contract)
10
- 3. [Key Features](#key-features)
11
- 4. [How to Install](#how-to-install)
12
- 5. [How to Use](#how-to-use)
13
- 6. [Core Functions](#core-functions)
14
- 7. [SHINE](#shine)
15
- 8. [Standalone Mode](#standalone-mode)
16
- 9. [Security Considerations](#security-considerations)
17
- 10. [Contributing](#contributing)
18
- 11. [License](#license)
19
- 12. [Contact](#contact)
8
+ 1. [DESCRIPTION](#description)
9
+ 2. [SMART CONTRACT](#smart-contract)
10
+ 3. [KEY FEATURES](#key-features)
11
+ 4. [HOW TO INSTALL](#how-to-install)
12
+ 5. [HOW TO USE](#how-to-use)
13
+ 6. [HOW IT WORKS](#how-it-works)
14
+ 7. [CORE FUNCTIONS](#core-functions)
15
+ 8. [SHINE](#shine)
16
+ 9. [STANDALONE MODE](#standalone-mode)
17
+ 10. [SECURITY CONSIDERATIONS](#security-considerations)
18
+ 11. [CONTRIBUTING](#contributing)
19
+ 12. [LICENSE](#license)
20
+ 13. [CONTACT](#contact)
20
21
 
21
22
  ## DESCRIPTION
22
23
 
@@ -60,6 +61,19 @@ Learn more about Gun.js [here](https://gun.eco/docs/Getting-Started).
60
61
 
61
62
  Learn more about plugin implementation [here](https://github.com/amark/gun/wiki/Adding-Methods-to-the-Gun-Chain#abstraction-layers).
62
63
 
64
+
65
+ ## HOW IT WORKS
66
+
67
+ ### Create KeyPair
68
+
69
+
70
+ [![](https://mermaid.ink/img/pako:eNpdUUtuwjAQvcrIGzZwgSwqJSRQhEorwqZNWLjxkFgkduSPEAJu1Fv0Yp0khaj1wh6P3s_jCyu0QBawQ61PRcWNg12cKxhWmK2T97dwtYVlskm24W71utnDbPYE0SWVpUIDJdLOHQIHSx3uvEE4SVdBJS2ceF2juz0Eo458Tb-_rjDPlnfqGs8tl2agpUm4f-DnvVmcJaow59aBq_6hR09vpSqh9OqvQtwrJGQ2sBU9F7TqgHEEEzpm6KoJEGjiLRrbl1wIg9aOMkkvs8hSp8kLhzgo4PgbRnaKn3E0MhY945miiz2bsgZNw6WgSV86SM4oTIM5C6gU3Bxz1rfVjbDcO52eVcECZzxOmdG-rFhw4LWlm28FDS2WvDS8eXRRSIr2Mnxm_6dT1nL1ofUdc_sBNpWchQ?type=png)](https://mermaid.live/edit#pako:eNpdUUtuwjAQvcrIGzZwgSwqJSRQhEorwqZNWLjxkFgkduSPEAJu1Fv0Yp0khaj1wh6P3s_jCyu0QBawQ61PRcWNg12cKxhWmK2T97dwtYVlskm24W71utnDbPYE0SWVpUIDJdLOHQIHSx3uvEE4SVdBJS2ceF2juz0Eo458Tb-_rjDPlnfqGs8tl2agpUm4f-DnvVmcJaow59aBq_6hR09vpSqh9OqvQtwrJGQ2sBU9F7TqgHEEEzpm6KoJEGjiLRrbl1wIg9aOMkkvs8hSp8kLhzgo4PgbRnaKn3E0MhY945miiz2bsgZNw6WgSV86SM4oTIM5C6gU3Bxz1rfVjbDcO52eVcECZzxOmdG-rFhw4LWlm28FDS2WvDS8eXRRSIr2Mnxm_6dT1nL1ofUdc_sBNpWchQ)
71
+
72
+ ### Retrive KeyPair
73
+ ----
74
+
75
+ [![](https://mermaid.ink/img/pako:eNplUsluwjAQ_ZWRz_ADObQCEiggOLAc2iQHN56ABbGjsU2FAv_erBBBLs7Yb5lnT8ESLZB5LD3rv-TIycLOjxSU3yjc7kabnQcbtCTxgrDEa84lxTAcfsC42BskyElfpEADXAhCU65KgJEHxa0j_Lw3WuOKcvtGc4NJ-NBDldA1tyjg1ChDSjqDmVP-OG6Ik9rLL4J3qHZKdPr-Uz8IfayxD6QzUh26RnvNtRZBbTEtWprUCoxLkjJL6s6dwfRpMKsCOFIg8KWnuI9d6xt8dVAk0uTBXvHfM4LVHTfut18x5i-M9spBadskjvsXWjEWL4yVNHXe7j00vSWe1YmXYbD2nw7Uvkrn8NWAmmLeLxZNwQYsQ8q4FOX4FNVRxOwRM4yYV_4KTqeI1dvqXmK5s3p7VQnzLDkcMNLucGReys-mrFwuuEVf8gPx7LGLQlpNq2ZC60EdsJyrH607zP0f6c7pXw?type=png)](https://mermaid.live/edit#pako:eNplUsluwjAQ_ZWRz_ADObQCEiggOLAc2iQHN56ABbGjsU2FAv_erBBBLs7Yb5lnT8ESLZB5LD3rv-TIycLOjxSU3yjc7kabnQcbtCTxgrDEa84lxTAcfsC42BskyElfpEADXAhCU65KgJEHxa0j_Lw3WuOKcvtGc4NJ-NBDldA1tyjg1ChDSjqDmVP-OG6Ik9rLL4J3qHZKdPr-Uz8IfayxD6QzUh26RnvNtRZBbTEtWprUCoxLkjJL6s6dwfRpMKsCOFIg8KWnuI9d6xt8dVAk0uTBXvHfM4LVHTfut18x5i-M9spBadskjvsXWjEWL4yVNHXe7j00vSWe1YmXYbD2nw7Uvkrn8NWAmmLeLxZNwQYsQ8q4FOX4FNVRxOwRM4yYV_4KTqeI1dvqXmK5s3p7VQnzLDkcMNLucGReys-mrFwuuEVf8gPx7LGLQlpNq2ZC60EdsJyrH607zP0f6c7pXw)
76
+
63
77
  ## CORE FUNCTIONS
64
78
 
65
79
  - `verifySignature(message, signature)`: Verifies an Ethereum signature for a given message.
@@ -114,6 +128,11 @@ Learn more about plugin implementation [here](https://github.com/amark/gun/wiki/
114
128
 
115
129
  SHINE (Secure Hash Integrity Network Ethereum) provides a mechanism for verifying data integrity using Ethereum and Gun.js.
116
130
 
131
+
132
+ [![](https://mermaid.ink/img/pako:eNplk1GTmjAQx79KJs_oICAiD9epQoX25Dq1006LPqRkPTMHiRODdx763S8Ez7N3PCXZ__73txvS4EJQwCFel-Kx2BCp0M9oyZH-PucL1e4XSZrF6BdItmYFUUzwFer1btCkSfm2ViHi2iGln05d2qQNHv_A7oim-Q9QksEeECWKoLUUFZrVPJqsOu3UGEVNZKKi5heX6M0lzuMnJUmhUCG4Aq4SstusrmWZOKIvecxpiIwTF6pzO6tiU2aWmx4OHYvgPd0u42fJzEiSjmRPSkYvilek5A0pzWegUEkU7BSSUAhJu-b-laJ4uPZNLoBf3wHuzUDhlTE1AN-uRO8E19Nt_W41AwepGRCHx_MlnLW3xmyeTyW08Y9zmxtBpm9YSECM_3crmQne5b8l08mCf-zqzii-d7CZrm5GumvN6ApbuAJZEUb1b9W0CUusNlDBEod6SYl8WGJzzE9aS2olFgde4FDJGiwsRX2_weGalDu9q7faGiJG7iWpLqdbwnHY4Ccc9pxxf-wEA9exPc92A384tPABh8NB33WDwdCxfcfxAy84WfhZCG0x6HuuZ49HI98PRjpojywMlGn2efcSzIMwNf6ahBbr9AJRuPoz?type=png)](https://mermaid.live/edit#pako:eNplk1GTmjAQx79KJs_oICAiD9epQoX25Dq1006LPqRkPTMHiRODdx763S8Ez7N3PCXZ__73txvS4EJQwCFel-Kx2BCp0M9oyZH-PucL1e4XSZrF6BdItmYFUUzwFer1btCkSfm2ViHi2iGln05d2qQNHv_A7oim-Q9QksEeECWKoLUUFZrVPJqsOu3UGEVNZKKi5heX6M0lzuMnJUmhUCG4Aq4SstusrmWZOKIvecxpiIwTF6pzO6tiU2aWmx4OHYvgPd0u42fJzEiSjmRPSkYvilek5A0pzWegUEkU7BSSUAhJu-b-laJ4uPZNLoBf3wHuzUDhlTE1AN-uRO8E19Nt_W41AwepGRCHx_MlnLW3xmyeTyW08Y9zmxtBpm9YSECM_3crmQne5b8l08mCf-zqzii-d7CZrm5GumvN6ApbuAJZEUb1b9W0CUusNlDBEod6SYl8WGJzzE9aS2olFgde4FDJGiwsRX2_weGalDu9q7faGiJG7iWpLqdbwnHY4Ccc9pxxf-wEA9exPc92A384tPABh8NB33WDwdCxfcfxAy84WfhZCG0x6HuuZ49HI98PRjpojywMlGn2efcSzIMwNf6ahBbr9AJRuPoz)
133
+
134
+
135
+
117
136
  #### SHINE Contract Configuration
118
137
 
119
138
  Currently, SHINE supports only the Optimism Sepolia network. The contract address is managed internally:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gun-eth",
3
- "version": "1.2.14",
3
+ "version": "1.3.1",
4
4
  "description": "A GunDB plugin for Ethereum, and Web3",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
@@ -0,0 +1,163 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>๐Ÿ’Ž Ethereum to ๐Ÿ”ซ Gun Key Pair Demo</title>
6
+ <!-- Include Gun -->
7
+ <script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
8
+ <!-- Include SEA (part of Gun) -->
9
+ <script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
10
+ <!-- Include Ethers.js -->
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.umd.min.js"></script>
12
+ <!-- Include the Gun-Eth plugin -->
13
+ <script src="https://cdn.jsdelivr.net/npm/gun-eth/src/gun-eth.js""></script>
14
+ <style>
15
+ body {
16
+ font-family: Arial, sans-serif;
17
+ margin: 0;
18
+ padding: 20px;
19
+ background-color: #f0f0f0;
20
+ }
21
+ .container {
22
+ max-width: 400px;
23
+ background-color: white;
24
+ border: 1px solid #000;
25
+ padding: 10px;
26
+ }
27
+ h1,
28
+ h2 {
29
+ margin: 0 0 10px 0;
30
+ font-size: 16px;
31
+ }
32
+ form, div {
33
+ margin-bottom: 15px;
34
+ }
35
+ input,
36
+ button {
37
+ display: inline-block;
38
+ margin: 2px 0;
39
+ padding: 2px;
40
+ font-size: 12px;
41
+ }
42
+ input {
43
+ width: 150px;
44
+ }
45
+ button {
46
+ width: 100px;
47
+ }
48
+ label {
49
+ display: inline-block;
50
+ width: 70px;
51
+ font-size: 12px;
52
+ }
53
+ #configStatus,
54
+ .result {
55
+ font-weight: bold;
56
+ margin-bottom: 10px;
57
+ }
58
+ .result {
59
+ color: blue;
60
+ }
61
+ </style>
62
+ </head>
63
+ <body>
64
+ <div class="container">
65
+ <h1>๐Ÿ’Ž Ethereum to ๐Ÿ”ซ Gun Key Pair Demo</h1>
66
+
67
+ <form id="configForm">
68
+ <h2>๐Ÿ”ง Configuration</h2>
69
+ <label for="rpcUrl">๐ŸŒ RPC URL:</label>
70
+ <br>
71
+ <input type="text" id="rpcUrl" name="rpcUrl" required>
72
+ <br>
73
+ <label for="privateKey">๐Ÿ”‘ Private Key:</label>
74
+ <br>
75
+ <input type="password" id="privateKey" name="privateKey" required>
76
+ <br>
77
+ <button type="submit">โš™๏ธ Configure</button>
78
+ </form>
79
+
80
+ <div id="createPairSection">
81
+ <h2>๐Ÿ”’ Create and Store Encrypted Pair</h2>
82
+ <button id="createPairBtn">๐Ÿ” Create Pair</button>
83
+ </div>
84
+
85
+ <div id="retrievePairSection">
86
+ <h2>๐Ÿ”“ Retrieve and Decrypt Pair</h2>
87
+ <button id="retrievePairBtn">๐Ÿ”Ž Retrieve Pair</button>
88
+ </div>
89
+
90
+ <div id="resultSection">
91
+ <h2>๐Ÿ“Š Result</h2>
92
+ <p id="resultText"></p>
93
+ </div>
94
+ </div>
95
+
96
+ <script>
97
+ let gun;
98
+ const MESSAGE_TO_SIGN = "GunDB access with Ethereum";
99
+
100
+ document.getElementById('configForm').addEventListener('submit', function(event) {
101
+ event.preventDefault();
102
+ const rpcUrl = document.getElementById('rpcUrl').value;
103
+ const privateKey = document.getElementById('privateKey').value;
104
+
105
+ gun = Gun();
106
+ gun.setStandaloneConfig(rpcUrl, privateKey);
107
+
108
+ console.log('Standalone configuration set');
109
+ document.getElementById('resultText').innerText = 'โœ… Configuration set successfully';
110
+ });
111
+
112
+ document.getElementById('createPairBtn').addEventListener('click', async function() {
113
+ if (!gun) {
114
+ alert('โš ๏ธ Please configure Gun first');
115
+ return;
116
+ }
117
+
118
+ try {
119
+ const signature = await gun.createSignature(MESSAGE_TO_SIGN);
120
+ if (!signature) {
121
+ throw new Error('Failed to create signature');
122
+ }
123
+
124
+ const signer = new ethers.Wallet(document.getElementById('privateKey').value);
125
+ const address = await signer.getAddress();
126
+
127
+ await gun.createAndStoreEncryptedPair(address, signature);
128
+
129
+ document.getElementById('resultText').innerText = 'โœ… Encrypted pair created and stored successfully';
130
+ } catch (error) {
131
+ document.getElementById('resultText').innerText = 'โŒ Error: ' + error.message;
132
+ }
133
+ });
134
+
135
+ document.getElementById('retrievePairBtn').addEventListener('click', async function() {
136
+ if (!gun) {
137
+ alert('โš ๏ธ Please configure Gun first');
138
+ return;
139
+ }
140
+
141
+ try {
142
+ const signature = await gun.createSignature(MESSAGE_TO_SIGN);
143
+ if (!signature) {
144
+ throw new Error('Failed to create signature');
145
+ }
146
+
147
+ const signer = new ethers.Wallet(document.getElementById('privateKey').value);
148
+ const address = await signer.getAddress();
149
+
150
+ const pair = await gun.getAndDecryptPair(address, signature);
151
+
152
+ document.getElementById('resultText').innerHTML = `
153
+ <strong>๐Ÿ”‘ Retrieved pair:</strong>
154
+ <br>
155
+ ${JSON.stringify(pair, null, 2)}
156
+ `;
157
+ } catch (error) {
158
+ document.getElementById('resultText').innerText = 'โŒ Error: ' + error.message;
159
+ }
160
+ });
161
+ </script>
162
+ </body>
163
+ </html>
@@ -0,0 +1,164 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Gun to Ethereum Address Demo</title>
6
+ <!-- Include Gun -->
7
+ <script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
8
+ <!-- Include SEA (part of Gun) -->
9
+ <script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
10
+ <!-- Include Ethers.js -->
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.umd.min.js"></script>
12
+ <!-- Include the Gun-Eth plugin -->
13
+ <script src="https://cdn.jsdelivr.net/npm/gun-eth/src/gun-eth.js""></script>
14
+ <style>
15
+ body {
16
+ font-family: Arial, sans-serif;
17
+ margin: 0;
18
+ padding: 20px;
19
+ background-color: #f0f0f0;
20
+ }
21
+ .container {
22
+ max-width: 400px;
23
+ background-color: white;
24
+ border: 1px solid #000;
25
+ padding: 10px;
26
+ }
27
+ h1,
28
+ h2 {
29
+ margin: 0 0 10px 0;
30
+ font-size: 16px;
31
+ }
32
+ form {
33
+ margin-bottom: 15px;
34
+ }
35
+ input,
36
+ button {
37
+ display: inline-block;
38
+ margin: 2px 0;
39
+ padding: 2px;
40
+ font-size: 12px;
41
+ }
42
+ input {
43
+ width: 150px;
44
+ }
45
+ button {
46
+ width: 100px;
47
+ }
48
+ label {
49
+ display: inline-block;
50
+ width: 70px;
51
+ font-size: 12px;
52
+ }
53
+ #configStatus,
54
+ .result {
55
+ font-weight: bold;
56
+ margin-bottom: 10px;
57
+ }
58
+ .result {
59
+ color: blue;
60
+ }
61
+ .disclaimer {
62
+ background-color: #ffffd0;
63
+ border: 1px solid #e6e600;
64
+ padding: 10px;
65
+ margin-bottom: 15px;
66
+ font-size: 14px;
67
+ }
68
+ </style>
69
+ </head>
70
+ <body>
71
+ <div class="container">
72
+ <h1>๐Ÿ”ซ Gun to ๐Ÿ’Ž Ethereum Address Demo</h1>
73
+
74
+ <form id="gunUserForm">
75
+ <h2>๐Ÿ‘ค User Login/Registration</h2>
76
+ <label for="gunAlias">๐Ÿ‘ค Alias:</label>
77
+ <br>
78
+ <input type="text" id="gunAlias" name="gunAlias" required>
79
+ <br>
80
+ <label for="gunPassword">๐Ÿ”‘ Password:</label>
81
+ <br>
82
+ <input type="password" id="gunPassword" name="gunPassword" required>
83
+ <br>
84
+ <button type="submit">๐Ÿš€ Create/Login</button>
85
+ </form>
86
+
87
+ <div id="conversionSection" style="display:none;">
88
+ <h2>๐Ÿ”„ Convert Gun User to Ethereum Address</h2>
89
+ <button id="convertBtn">๐Ÿ”„ Convert
90
+ </div>
91
+
92
+ <div id="resultSection">
93
+ <h2>Result</h2>
94
+ <p id="resultText"></p>
95
+ </div>
96
+ </div>
97
+
98
+ <script>
99
+ let gun = Gun();
100
+ let currentUser = null;
101
+
102
+ document.getElementById('gunUserForm').addEventListener('submit', function(event) {
103
+ event.preventDefault();
104
+ const alias = document.getElementById('gunAlias').value;
105
+ const password = document.getElementById('gunPassword').value;
106
+
107
+ console.log('Attempting to create or login user:', alias);
108
+
109
+ gun.user().create(alias, password, function(ack) {
110
+ if (ack.err) {
111
+ console.log('User creation failed, trying to login:', ack.err);
112
+ gun.user().auth(alias, password, function(authAck) {
113
+ if (authAck.err) {
114
+ document.getElementById('resultText').innerText = 'Error logging in: ' + authAck.err;
115
+ } else {
116
+ currentUser = gun.user();
117
+ document.getElementById('resultText').innerText = 'User logged in successfully.';
118
+ document.getElementById('conversionSection').style.display = 'block';
119
+ }
120
+ });
121
+ } else {
122
+ console.log('User created successfully:', ack);
123
+ currentUser = gun.user();
124
+ document.getElementById('resultText').innerText = 'User created and logged in successfully.';
125
+ document.getElementById('conversionSection').style.display = 'block';
126
+ }
127
+ });
128
+ });
129
+
130
+ document.getElementById('convertBtn').addEventListener('click', function() {
131
+ if (!currentUser) {
132
+ alert('Please login first');
133
+ return;
134
+ }
135
+
136
+ console.log('Current user:', currentUser);
137
+ console.log('User pair:', currentUser._.sea);
138
+
139
+ try {
140
+ const pair = currentUser._.sea;
141
+ if (!pair || !pair.priv) {
142
+ throw new Error('Unable to retrieve Gun user private key');
143
+ }
144
+
145
+ const ethAccount = gun.gunToEthAccount(pair.priv);
146
+
147
+ document.getElementById('resultText').innerHTML = `
148
+ <strong>Gun User</strong>
149
+ <br>
150
+ ${currentUser.is.alias}
151
+ <br>
152
+ <br>
153
+ <strong>Ethereum Address:</strong>
154
+ <br>
155
+ ${ethAccount.publicKey}
156
+ `;
157
+ } catch (error) {
158
+ document.getElementById('resultText').innerText = 'Error: ' + error.message;
159
+ console.error('Conversion error:', error);
160
+ }
161
+ });
162
+ </script>
163
+ </body>
164
+ </html>
@@ -0,0 +1,256 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Demo Gun-Eth Plugin</title>
6
+ <!-- Include Gun -->
7
+ <script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
8
+ <!-- Include SEA (part of Gun) -->
9
+ <script src="https://cdn.jsdelivr.net/npm/gun/sea.js"></script>
10
+ <!-- Include Ethers.js -->
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.umd.min.js"></script>
12
+ <!-- Include the Gun-Eth plugin -->
13
+ <script src="https://cdn.jsdelivr.net/npm/gun-eth/src/gun-eth.js""></script>
14
+ <style>
15
+ body {
16
+ font-family: Arial, sans-serif;
17
+ margin: 0;
18
+ padding: 20px;
19
+ background-color: #f0f0f0;
20
+ }
21
+ .container {
22
+ max-width: 400px;
23
+ background-color: white;
24
+ border: 1px solid #000;
25
+ padding: 10px;
26
+ }
27
+ h1,
28
+ h2 {
29
+ margin: 0 0 10px 0;
30
+ font-size: 16px;
31
+ }
32
+ form {
33
+ margin-bottom: 15px;
34
+ }
35
+ input,
36
+ button {
37
+ display: inline-block;
38
+ margin: 2px 0;
39
+ padding: 2px;
40
+ font-size: 12px;
41
+ }
42
+ input {
43
+ width: 150px;
44
+ }
45
+ button {
46
+ width: 70px;
47
+ }
48
+ label {
49
+ display: inline-block;
50
+ width: 70px;
51
+ font-size: 12px;
52
+ }
53
+ #configStatus,
54
+ .result {
55
+ font-weight: bold;
56
+ margin-bottom: 10px;
57
+ }
58
+ .result {
59
+ color: blue;
60
+ }
61
+ .disclaimer {
62
+ background-color: #ffffd0;
63
+ border: 1px solid #e6e600;
64
+ padding: 10px;
65
+ margin-bottom: 15px;
66
+ font-size: 14px;
67
+ }
68
+ </style>
69
+ </head>
70
+ <body>
71
+ <div class="container">
72
+ <div class="disclaimer">
73
+ <strong>โš ๏ธ</strong> This example is available only on the
74
+ <strong>Optimism Sepolia</strong> chain.
75
+ </div>
76
+ <h1>๐Ÿ’Ž SHINE (Secure Hash Integriry Network Ethereum)</h1>
77
+ <hr />
78
+ <h2>๐Ÿ”ง Configuration</h2>
79
+ <form id="configForm">
80
+ <label for="rpcUrl">RPC URL:</label>
81
+ <input type="text" id="rpcUrl" name="rpcUrl" required />
82
+ <br />
83
+ <label for="privateKey">Private Key:</label>
84
+ <input type="password" id="privateKey" name="privateKey" required />
85
+ <br />
86
+ <button type="submit">Configure</button>
87
+ </form>
88
+ <div id="configStatus">Status: Not Configured</div>
89
+ <hr />
90
+
91
+ <h2>๐Ÿ”ด Write Data to Blockchain</h2>
92
+ <form id="writeForm">
93
+ <label for="data">Data</label>
94
+ <br />
95
+ <input type="text" id="data" name="data" required />
96
+ <br />
97
+ <button type="submit">Write</button>
98
+ </form>
99
+ <div id="writeResult" class="result"></div>
100
+ <hr />
101
+
102
+ <h2>๐Ÿ” Verify Data on Blockchain</h2>
103
+ <form id="verifyForm">
104
+ <label for="nodeId">Node ID</label>
105
+ <br />
106
+ <input type="text" id="nodeId" name="nodeId" required />
107
+ <br />
108
+ <button type="submit">Verify</button>
109
+ </form>
110
+ <div id="verifyResult" class="result"></div>
111
+ <hr />
112
+
113
+ <h2>๐Ÿ”„ Update Node Data in Gun</h2>
114
+ <form id="updateForm">
115
+ <label for="updateNodeId">Node ID</label>
116
+ <br />
117
+ <input type="text" id="updateNodeId" name="updateNodeId" required />
118
+ <br />
119
+ <label for="editMessage">New Data</label>
120
+ <br />
121
+ <input type="text" id="editMessage" name="editMessage" required />
122
+ <br />
123
+ <button type="submit">Update</button>
124
+ </form>
125
+ <div id="updateResult" class="result"></div>
126
+ </div>
127
+
128
+ <script>
129
+ let gun;
130
+ let isConfigured = false;
131
+
132
+ function updateConfigStatus() {
133
+ const statusElement = document.getElementById("configStatus");
134
+ statusElement.textContent = isConfigured
135
+ ? "Status: Configured"
136
+ : "Status: Not Configured";
137
+ statusElement.style.color = isConfigured ? "green" : "red";
138
+ }
139
+
140
+ document
141
+ .getElementById("configForm")
142
+ .addEventListener("submit", function (event) {
143
+ event.preventDefault();
144
+ const rpcUrl = document.getElementById("rpcUrl").value;
145
+ const privateKey = document.getElementById("privateKey").value;
146
+
147
+ // Configure Gun with the provided details
148
+ gun = Gun();
149
+ gun.setStandaloneConfig(rpcUrl, privateKey);
150
+
151
+ console.log("Standalone configuration set");
152
+ isConfigured = true;
153
+ updateConfigStatus();
154
+ });
155
+
156
+ document
157
+ .getElementById("writeForm")
158
+ .addEventListener("submit", function (event) {
159
+ event.preventDefault();
160
+ if (!isConfigured) {
161
+ document.getElementById("writeResult").textContent =
162
+ "Please configure Gun first.";
163
+ return;
164
+ }
165
+ const data = document.getElementById("data").value;
166
+ const resultElement = document.getElementById("writeResult");
167
+ resultElement.textContent = "Writing data...";
168
+
169
+ // Assicurati che `data` sia un oggetto JSON valido
170
+
171
+ gun.shine("optimismSepolia", null, data, function (result) {
172
+ console.log("Shine result:", result);
173
+ if (result.ok) {
174
+ resultElement.textContent =
175
+ "Data successfully written: " + result.nodeId;
176
+ } else {
177
+ resultElement.textContent = "Error writing data: " + result.err;
178
+ }
179
+ });
180
+ });
181
+
182
+ document
183
+ .getElementById("verifyForm")
184
+ .addEventListener("submit", function (event) {
185
+ event.preventDefault();
186
+ if (!isConfigured) {
187
+ document.getElementById("verifyResult").textContent =
188
+ "Please configure Gun first.";
189
+ return;
190
+ }
191
+ const nodeId = document.getElementById("nodeId").value;
192
+ const resultElement = document.getElementById("verifyResult");
193
+ resultElement.textContent = "Verifying data...";
194
+
195
+ gun.shine("optimismSepolia", nodeId, null, function (result) {
196
+ console.log("Verification result:", result);
197
+ if (result.ok) {
198
+ resultElement.textContent =
199
+ "Data successfully verified: " + result.message;
200
+ } else {
201
+ resultElement.textContent =
202
+ "Error verifying data: " + result.message;
203
+ }
204
+ });
205
+ });
206
+
207
+ document
208
+ .getElementById("updateForm")
209
+ .addEventListener("submit", function (event) {
210
+ event.preventDefault();
211
+ if (!isConfigured) {
212
+ document.getElementById("updateResult").textContent =
213
+ "Please configure Gun first.";
214
+ return;
215
+ }
216
+ const nodeId = document.getElementById("updateNodeId").value;
217
+ const editMessage = document.getElementById("editMessage").value;
218
+ const resultElement = document.getElementById("updateResult");
219
+ resultElement.textContent = "Updating data...";
220
+
221
+ gun.get(nodeId).once(function (existingData) {
222
+ // Prepara i nuovi dati
223
+ const newData = {
224
+ message: editMessage,
225
+ _contentHash: existingData._contentHash, // Manteniamo il contentHash precedente per il calcolo
226
+ };
227
+
228
+ // Calcola il nuovo contentHash
229
+ const dataString = JSON.stringify(editMessage);
230
+ const newContentHash = ethers.keccak256(
231
+ ethers.toUtf8Bytes(dataString)
232
+ );
233
+
234
+ // Aggiorna i dati con il nuovo contentHash
235
+ newData._contentHash = newContentHash;
236
+
237
+ // Update data in Gun
238
+ gun.get(nodeId).put(newData, function (ack) {
239
+ if (ack.err) {
240
+ console.error("Error updating data in Gun:", ack.err);
241
+ resultElement.textContent =
242
+ "Error updating data in Gun: " + ack.err;
243
+ } else {
244
+ console.log("Data successfully updated in Gun:", nodeId);
245
+ resultElement.textContent =
246
+ "Data successfully updated in Gun: " + nodeId;
247
+ }
248
+ });
249
+ });
250
+ });
251
+
252
+ // Inizializza lo stato di configurazione
253
+ updateConfigStatus();
254
+ </script>
255
+ </body>
256
+ </html>
package/src/gun-eth.js ADDED
@@ -0,0 +1,571 @@
1
+ (function (root, factory) {
2
+ if (typeof define === "function" && define.amd) {
3
+ define(["gun", "gun/sea", "ethers"], factory);
4
+ } else if (typeof module === "object" && module.exports) {
5
+ module.exports = factory(require("gun/gun"), require("gun/sea"), require("ethers"));
6
+ } else {
7
+ factory(root.Gun, root.SEA, root.ethers);
8
+ }
9
+ })(typeof self !== "undefined" ? self : this, function (Gun, SEA, ethers) {
10
+ console.log('Factory del plugin Gun-Eth chiamata');
11
+
12
+ const MESSAGE_TO_SIGN = "Accesso a GunDB con Ethereum";
13
+
14
+
15
+ // Funzione per verificare se ethers รจ disponibile
16
+ function checkEthers() {
17
+ if (typeof ethers === 'undefined') {
18
+ console.error('Ethers.js non รจ disponibile. Assicurati che sia caricato prima di questo script.');
19
+ return false;
20
+ }
21
+ console.log('Ethers version:', ethers.version);
22
+ return true;
23
+ }
24
+
25
+ // Variabili globali
26
+ let SHINE_ABI = [
27
+ {
28
+ "anonymous": false,
29
+ "inputs": [
30
+ {
31
+ "indexed": true,
32
+ "internalType": "bytes",
33
+ "name": "nodeId",
34
+ "type": "bytes"
35
+ },
36
+ {
37
+ "indexed": false,
38
+ "internalType": "bytes32",
39
+ "name": "contentHash",
40
+ "type": "bytes32"
41
+ },
42
+ {
43
+ "indexed": false,
44
+ "internalType": "address",
45
+ "name": "updater",
46
+ "type": "address"
47
+ }
48
+ ],
49
+ "name": "DataUpdated",
50
+ "type": "event"
51
+ },
52
+ {
53
+ "inputs": [
54
+ {
55
+ "internalType": "bytes[]",
56
+ "name": "nodeIds",
57
+ "type": "bytes[]"
58
+ },
59
+ {
60
+ "internalType": "bytes32[]",
61
+ "name": "contentHashes",
62
+ "type": "bytes32[]"
63
+ }
64
+ ],
65
+ "name": "batchUpdateData",
66
+ "outputs": [],
67
+ "stateMutability": "nonpayable",
68
+ "type": "function"
69
+ },
70
+ {
71
+ "inputs": [
72
+ {
73
+ "internalType": "bytes",
74
+ "name": "nodeId",
75
+ "type": "bytes"
76
+ }
77
+ ],
78
+ "name": "getLatestRecord",
79
+ "outputs": [
80
+ {
81
+ "internalType": "bytes32",
82
+ "name": "",
83
+ "type": "bytes32"
84
+ },
85
+ {
86
+ "internalType": "uint256",
87
+ "name": "",
88
+ "type": "uint256"
89
+ },
90
+ {
91
+ "internalType": "address",
92
+ "name": "",
93
+ "type": "address"
94
+ }
95
+ ],
96
+ "stateMutability": "view",
97
+ "type": "function"
98
+ },
99
+ {
100
+ "inputs": [
101
+ {
102
+ "internalType": "bytes",
103
+ "name": "",
104
+ "type": "bytes"
105
+ }
106
+ ],
107
+ "name": "nodeData",
108
+ "outputs": [
109
+ {
110
+ "internalType": "bytes32",
111
+ "name": "contentHash",
112
+ "type": "bytes32"
113
+ },
114
+ {
115
+ "internalType": "uint256",
116
+ "name": "timestamp",
117
+ "type": "uint256"
118
+ },
119
+ {
120
+ "internalType": "address",
121
+ "name": "updater",
122
+ "type": "address"
123
+ }
124
+ ],
125
+ "stateMutability": "view",
126
+ "type": "function"
127
+ },
128
+ {
129
+ "inputs": [
130
+ {
131
+ "internalType": "bytes",
132
+ "name": "nodeId",
133
+ "type": "bytes"
134
+ },
135
+ {
136
+ "internalType": "bytes32",
137
+ "name": "contentHash",
138
+ "type": "bytes32"
139
+ }
140
+ ],
141
+ "name": "updateData",
142
+ "outputs": [],
143
+ "stateMutability": "nonpayable",
144
+ "type": "function"
145
+ },
146
+ {
147
+ "inputs": [
148
+ {
149
+ "internalType": "bytes",
150
+ "name": "nodeId",
151
+ "type": "bytes"
152
+ },
153
+ {
154
+ "internalType": "bytes32",
155
+ "name": "contentHash",
156
+ "type": "bytes32"
157
+ }
158
+ ],
159
+ "name": "verifyData",
160
+ "outputs": [
161
+ {
162
+ "internalType": "bool",
163
+ "name": "",
164
+ "type": "bool"
165
+ },
166
+ {
167
+ "internalType": "uint256",
168
+ "name": "",
169
+ "type": "uint256"
170
+ },
171
+ {
172
+ "internalType": "address",
173
+ "name": "",
174
+ "type": "address"
175
+ }
176
+ ],
177
+ "stateMutability": "view",
178
+ "type": "function"
179
+ }
180
+ ]
181
+
182
+ let SHINE_OPTIMISM_SEPOLIA = "0x43D838b683F772F08f321E5FA265ad3e333BE9C2";
183
+ let SHINE_CONTRACT_ADDRESS;
184
+ let customToken = "";
185
+ let rpcUrl = "";
186
+ let privateKey = "";
187
+
188
+ /**
189
+ * Funzione per ottenere il signer
190
+ * @returns {Promise<ethers.Signer>} Il signer.
191
+ */
192
+ const getSigner = async () => {
193
+ if (rpcUrl && privateKey) {
194
+ // Modalitร  standalone
195
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
196
+ return new ethers.Wallet(privateKey, provider);
197
+ } else if (
198
+ typeof window !== "undefined" &&
199
+ typeof window.ethereum !== "undefined"
200
+ ) {
201
+ // Modalitร  browser
202
+ await window.ethereum.request({ method: "eth_requestAccounts" });
203
+ const provider = new ethers.BrowserProvider(window.ethereum);
204
+ return provider.getSigner();
205
+ } else {
206
+ throw new Error("No valid Ethereum provider found");
207
+ }
208
+ };
209
+
210
+ /**
211
+ * Sets standalone configuration for Gun.
212
+ * @param {string} newRpcUrl - The new RPC URL.
213
+ * @param {string} newPrivateKey - The new private key.
214
+ * @returns {Gun} The Gun instance for chaining.
215
+ */
216
+ Gun.chain.setStandaloneConfig = function (newRpcUrl, newPrivateKey) {
217
+ rpcUrl = newRpcUrl;
218
+ privateKey = newPrivateKey;
219
+ console.log("Standalone configuration set");
220
+ return this;
221
+ };
222
+
223
+ /**
224
+ * Sets a custom token for Gun operations.
225
+ * @param {string} token - The token to be set.
226
+ * @returns {Gun} The Gun instance for chaining.
227
+ */
228
+ Gun.chain.setToken = function (token) {
229
+ if (typeof token === "string" && token.length > 0) {
230
+ customToken = token;
231
+ console.log("Token set successfully:", token);
232
+ } else {
233
+ console.error("Invalid token. Must be a non-empty string.");
234
+ }
235
+ return this;
236
+ };
237
+
238
+ /**
239
+ * Retrieves the current custom token.
240
+ * @returns {string} The current custom token.
241
+ */
242
+ Gun.chain.getToken = function () {
243
+ return customToken;
244
+ };
245
+
246
+ // Add custom token to all 'put' operations
247
+ Gun.on("put", function (msg) {
248
+ const to = this.to;
249
+ msg.headers = {
250
+ token: customToken,
251
+ };
252
+ to.next(msg);
253
+ });
254
+
255
+ /**
256
+ * Verifies an Ethereum signature.
257
+ * @param {string} message - The original message that was signed.
258
+ * @param {string} signature - The signature to verify.
259
+ * @returns {Promise<string|null>} The recovered address or null if verification fails.
260
+ */
261
+ Gun.chain.verifySignature = async function (message, signature) {
262
+ try {
263
+ const recoveredAddress = ethers.verifyMessage(message, signature);
264
+ return recoveredAddress;
265
+ } catch (error) {
266
+ console.error("Error verifying signature:", error);
267
+ return null;
268
+ }
269
+ };
270
+
271
+ /**
272
+ * Generates a password from a signature.
273
+ * @param {string} signature - The signature to derive the password from.
274
+ * @returns {string|null} The generated password or null if generation fails.
275
+ */
276
+ Gun.chain.generatePassword = function (signature) {
277
+ try {
278
+ const hexSignature = ethers.hexlify(signature);
279
+ const hash = ethers.keccak256(hexSignature);
280
+ console.log("Generated password:", hash);
281
+ return hash;
282
+ } catch (error) {
283
+ console.error("Error generating password:", error);
284
+ return null;
285
+ }
286
+ };
287
+
288
+ /**
289
+ * Creates an Ethereum signature for a given message.
290
+ * @param {string} message - The message to sign.
291
+ * @returns {Promise<string|null>} The signature or null if signing fails.
292
+ */
293
+ Gun.chain.createSignature = async function (message) {
294
+ try {
295
+ // Verifica se il messaggio รจ uguale a MESSAGE_TO_SIGN
296
+ if (message !== MESSAGE_TO_SIGN) {
297
+ throw new Error("Invalid message, valid message is: " + MESSAGE_TO_SIGN);
298
+ }
299
+ const signer = await getSigner();
300
+ const signature = await signer.signMessage(message);
301
+ console.log("Signature created:", signature);
302
+ return signature;
303
+ } catch (error) {
304
+ console.error("Error creating signature:", error);
305
+ return null;
306
+ }
307
+ };
308
+
309
+ /**
310
+ * Creates and stores an encrypted key pair for a given address.
311
+ * @param {string} address - The Ethereum address to associate with the key pair.
312
+ * @param {string} signature - The signature to use for encryption.
313
+ * @returns {Promise<void>}
314
+ */
315
+ Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
316
+ try {
317
+ const gun = this;
318
+ const pair = await SEA.pair();
319
+ const encryptedPair = await SEA.encrypt(JSON.stringify(pair), signature);
320
+ await gun.get("gun-eth").get("users").get(address).put({ encryptedPair });
321
+ console.log("Encrypted pair stored for:", address);
322
+ } catch (error) {
323
+ console.error("Error creating and storing encrypted pair:", error);
324
+ }
325
+ };
326
+
327
+ /**
328
+ * Retrieves and decrypts a stored key pair for a given address.
329
+ * @param {string} address - The Ethereum address associated with the key pair.
330
+ * @param {string} signature - The signature to use for decryption.
331
+ * @returns {Promise<Object|null>} The decrypted key pair or null if retrieval fails.
332
+ */
333
+ Gun.chain.getAndDecryptPair = async function (address, signature) {
334
+ try {
335
+ const gun = this;
336
+ const encryptedData = await gun
337
+ .get("gun-eth")
338
+ .get("users")
339
+ .get(address)
340
+ .get("encryptedPair")
341
+ .then();
342
+ if (!encryptedData) {
343
+ throw new Error("No encrypted data found for this address");
344
+ }
345
+ const decryptedPair = await SEA.decrypt(encryptedData, signature);
346
+ console.log(decryptedPair);
347
+ return decryptedPair;
348
+ } catch (error) {
349
+ console.error("Error retrieving and decrypting pair:", error);
350
+ return null;
351
+ }
352
+ };
353
+
354
+ /**
355
+ * SHINE (Secure Hybrid Information and Network Environment) functionality.
356
+ * @param {string} chain - The blockchain to use (e.g., "optimismSepolia").
357
+ * @param {string} nodeId - The ID of the node to verify or write.
358
+ * @param {Object} data - The data to write (if writing).
359
+ * @param {Function} callback - Callback function to handle the result.
360
+ * @returns {Gun} The Gun instance for chaining.
361
+ */
362
+ Gun.chain.shine = function (chain, nodeId, data, callback) {
363
+ console.log("SHINE plugin called with:", { chain, nodeId, data });
364
+
365
+ if (!checkEthers()) {
366
+ if (callback) callback({ err: "Ethers.js non รจ disponibile" });
367
+ return this;
368
+ }
369
+
370
+ if (typeof callback !== "function") {
371
+ console.error("Callback must be a function");
372
+ return this;
373
+ }
374
+
375
+ const gun = this;
376
+
377
+ // Seleziona l'indirizzo basato sulla catena
378
+ if (chain === "optimismSepolia") {
379
+ SHINE_CONTRACT_ADDRESS = SHINE_OPTIMISM_SEPOLIA;
380
+ } else {
381
+ throw new Error("Chain not supported");
382
+ }
383
+
384
+ // Funzione per verificare on-chain
385
+ const verifyOnChain = async (nodeId, contentHash) => {
386
+ console.log("Verifying on chain:", { nodeId, contentHash });
387
+ const signer = await getSigner();
388
+ const contract = new ethers.Contract(
389
+ SHINE_CONTRACT_ADDRESS,
390
+ SHINE_ABI,
391
+ signer
392
+ );
393
+ const [isValid, timestamp, updater] = await contract.verifyData(
394
+ ethers.toUtf8Bytes(nodeId),
395
+ contentHash
396
+ );
397
+ console.log("Verification result:", { isValid, timestamp, updater });
398
+ return { isValid, timestamp, updater };
399
+ };
400
+
401
+ // Funzione per scrivere on-chain
402
+ const writeOnChain = async (nodeId, contentHash) => {
403
+ console.log("Writing on chain:", { nodeId, contentHash });
404
+ const signer = await getSigner();
405
+ const contract = new ethers.Contract(
406
+ SHINE_CONTRACT_ADDRESS,
407
+ SHINE_ABI,
408
+ signer
409
+ );
410
+ const tx = await contract.updateData(
411
+ ethers.toUtf8Bytes(nodeId),
412
+ contentHash
413
+ );
414
+ console.log("Transaction sent:", tx.hash);
415
+ const receipt = await tx.wait();
416
+ console.log("Transaction confirmed:", receipt);
417
+ return tx;
418
+ };
419
+
420
+ // Nuova funzione per ottenere l'ultimo record dalla blockchain
421
+ const getLatestRecord = async (nodeId) => {
422
+ const signer = await getSigner();
423
+ const contract = new ethers.Contract(
424
+ SHINE_CONTRACT_ADDRESS,
425
+ SHINE_ABI,
426
+ signer
427
+ );
428
+ const [contentHash, timestamp, updater] = await contract.getLatestRecord(
429
+ ethers.toUtf8Bytes(nodeId)
430
+ );
431
+ console.log("Latest record from blockchain:", {
432
+ nodeId,
433
+ contentHash,
434
+ timestamp,
435
+ updater,
436
+ });
437
+ return { contentHash, timestamp, updater };
438
+ };
439
+
440
+ // Processo SHINE
441
+ if (nodeId && !data) {
442
+ // Caso 1: Utente passa solo il nodo
443
+ gun.get(nodeId).once(async (existingData) => {
444
+ if (!existingData) {
445
+ if (callback) callback({ err: "Node not found in GunDB" });
446
+ return;
447
+ }
448
+
449
+ console.log("existingData", existingData);
450
+
451
+ // Usa il contentHash memorizzato invece di ricalcolarlo
452
+ const contentHash = existingData._contentHash;
453
+ console.log("contentHash", contentHash);
454
+
455
+ if (!contentHash) {
456
+ if (callback) callback({ err: "No content hash found for this node" });
457
+ return;
458
+ }
459
+
460
+ try {
461
+ const { isValid, timestamp, updater } = await verifyOnChain(
462
+ nodeId,
463
+ contentHash
464
+ );
465
+ const latestRecord = await getLatestRecord(nodeId);
466
+
467
+ if (isValid) {
468
+ if (callback)
469
+ callback({
470
+ ok: true,
471
+ message: "Data verified on blockchain",
472
+ timestamp,
473
+ updater,
474
+ latestRecord,
475
+ });
476
+ } else {
477
+ if (callback)
478
+ callback({
479
+ ok: false,
480
+ message: "Data not verified on blockchain",
481
+ latestRecord,
482
+ });
483
+ }
484
+ } catch (error) {
485
+ if (callback) callback({ err: error.message });
486
+ }
487
+ });
488
+ } else if (data && !nodeId) {
489
+ // Caso 2: Utente passa solo il testo (data)
490
+ const newNodeId = Gun.text.random();
491
+ const dataString = JSON.stringify(data);
492
+ const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
493
+
494
+ gun
495
+ .get(newNodeId)
496
+ .put({ ...data, _contentHash: contentHash }, async (ack) => {
497
+ console.log("ack", ack);
498
+ if (ack.err) {
499
+ if (callback) callback({ err: "Error saving data to GunDB" });
500
+ return;
501
+ }
502
+
503
+ try {
504
+ const tx = await writeOnChain(newNodeId, contentHash);
505
+ if (callback)
506
+ callback({
507
+ ok: true,
508
+ message: "Data written to GunDB and blockchain",
509
+ nodeId: newNodeId,
510
+ txHash: tx.hash,
511
+ });
512
+ } catch (error) {
513
+ if (callback) callback({ err: error.message });
514
+ }
515
+ });
516
+ } else {
517
+ if (callback)
518
+ callback({
519
+ err: "Invalid input. Provide either nodeId or data, not both.",
520
+ });
521
+ }
522
+
523
+ return gun;
524
+ };
525
+
526
+ /**
527
+ * Converts a Gun private key to an Ethereum account.
528
+ * @param {string} gunPrivateKey - The Gun private key in base64url format.
529
+ * @returns {Object} An object containing the Ethereum account and public key.
530
+ */
531
+ Gun.chain.gunToEthAccount = function (gunPrivateKey) {
532
+ // Function to convert base64url to hex
533
+ const base64UrlToHex = (base64url) => {
534
+ const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
535
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
536
+ const binary = atob(base64);
537
+ return Array.from(binary, (char) =>
538
+ char.charCodeAt(0).toString(16).padStart(2, "0")
539
+ ).join("");
540
+ };
541
+
542
+ // Convert Gun private key to hex format
543
+ const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
544
+
545
+ // Create an Ethereum wallet from the private key
546
+ const wallet = new ethers.Wallet(hexPrivateKey);
547
+
548
+ // Get the public address (public key)
549
+ const publicKey = wallet.address;
550
+
551
+ return {
552
+ account: wallet,
553
+ publicKey: publicKey,
554
+ };
555
+ };
556
+
557
+ /**
558
+ * Sets SHINE configuration.
559
+ * @param {Object} abi - The ABI of the SHINE contract.
560
+ * @param {string} optimismSepoliaAddress - The contract address on Optimism Sepolia.
561
+ * @returns {Gun} The Gun instance for chaining.
562
+ */
563
+ Gun.chain.setShineConfig = function (abi, optimismSepoliaAddress) {
564
+ SHINE_ABI = abi;
565
+ SHINE_OPTIMISM_SEPOLIA = optimismSepoliaAddress;
566
+ console.log("SHINE configuration set");
567
+ return this;
568
+ };
569
+
570
+ console.log('Plugin Gun-Eth caricato con successo');
571
+ });
package/src/index.js CHANGED
@@ -12,6 +12,8 @@ let customToken = "";
12
12
  let rpcUrl = "";
13
13
  let privateKey = "";
14
14
 
15
+ const MESSAGE_TO_SIGN = "Accesso a GunDB con Ethereum";
16
+
15
17
  /**
16
18
  * Funzione per ottenere il signer
17
19
  * @returns {Promise<ethers.Signer>} Il signer.
@@ -119,6 +121,10 @@ Gun.chain.generatePassword = function (signature) {
119
121
  */
120
122
  Gun.chain.createSignature = async function (message) {
121
123
  try {
124
+ // Verifica se il messaggio รจ uguale a MESSAGE_TO_SIGN
125
+ if (message !== MESSAGE_TO_SIGN) {
126
+ throw new Error("Invalid message, valid message is: " + MESSAGE_TO_SIGN);
127
+ }
122
128
  const signer = await getSigner();
123
129
  const signature = await signer.signMessage(message);
124
130
  console.log("Signature created:", signature);
@@ -340,4 +346,34 @@ Gun.chain.shine = function (chain, nodeId, data, callback) {
340
346
  return gun;
341
347
  };
342
348
 
349
+
350
+ /**
351
+ * Converts a Gun private key to an Ethereum account.
352
+ * @param {string} gunPrivateKey - The Gun private key in base64url format.
353
+ * @returns {Object} An object containing the Ethereum account and public key.
354
+ */
355
+ Gun.chain.gunToEthAccount = function(gunPrivateKey) {
356
+ // Function to convert base64url to hex
357
+ const base64UrlToHex = (base64url) => {
358
+ const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
359
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
360
+ const binary = atob(base64);
361
+ return Array.from(binary, (char) => char.charCodeAt(0).toString(16).padStart(2, "0")).join("");
362
+ };
363
+
364
+ // Convert Gun private key to hex format
365
+ const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
366
+
367
+ // Create an Ethereum wallet from the private key
368
+ const wallet = new ethers.Wallet(hexPrivateKey);
369
+
370
+ // Get the public address (public key)
371
+ const publicKey = wallet.address;
372
+
373
+ return {
374
+ account: wallet,
375
+ publicKey: publicKey
376
+ };
377
+ };
378
+
343
379
  module.exports = Gun;