gun-eth 1.2.14 โ†’ 1.3.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 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;