ethershell 0.1.0-alpha.8 → 0.1.1-alpha.15

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
@@ -29,18 +29,22 @@ An interactive Node.js console for Ethereum smart contract development. Write, c
29
29
  ### Installation
30
30
 
31
31
  ```bash
32
- # Install globally
32
+ # Install globally:
33
33
  npm i -g ethershell
34
34
 
35
- # Start EtherShell
36
- npm start
35
+ # Start EtherShell:
36
+ ethershell
37
+
38
+ #or
39
+
40
+ npx ethershell
37
41
  ```
38
42
 
39
43
  ### Basic Usage
40
44
 
41
45
  ```bash
42
46
  # Start the console
43
- npm start
47
+ ethershell
44
48
 
45
49
  # You should see:
46
50
  # EtherShell>
@@ -54,7 +58,7 @@ First, connect to a blockchain network:
54
58
 
55
59
  ```javascript
56
60
  // View current network
57
- EtherShell> chain()
61
+ EtherShell> chainInfo()
58
62
  { URL: 'http://127.0.0.1:8545', name: 'unknown', chainId: 1337n }
59
63
 
60
64
  // Switch to a different network
package/bin/cli.js CHANGED
@@ -35,6 +35,7 @@ import {
35
35
 
36
36
  import { deploy, add } from '../src/services/addContracts.js';
37
37
  import { getContracts } from '../src/services/contracts.js';
38
+ import { getConfigInfo } from '../src/services/config.js';
38
39
 
39
40
  /**
40
41
  * REPL instance for EtherShell interactive environment
@@ -53,7 +54,7 @@ export const r = repl.start({
53
54
 
54
55
  // Network commands
55
56
  r.context.chain = set;
56
- r.context.chain = get;
57
+ r.context.chainInfo = get;
57
58
  r.context.defaultChain = getDefault;
58
59
 
59
60
  // Compile commands
@@ -63,6 +64,9 @@ r.context.compInfo = getCompilerOptions;
63
64
  r.context.compOpts = compilerOptions;
64
65
  r.context.build = compile;
65
66
 
67
+ // Config commands
68
+ r.context.configInfo = getConfigInfo;
69
+
66
70
  // Clean build folder
67
71
  r.context.clean = deleteDirectory;
68
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ethershell",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.1-alpha.15",
4
4
  "description": "Interactive JavaScript console for Ethereum smart contract management",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Alireza Kiakojouri (alirezaethdev@gmail.com)",
@@ -8,13 +8,39 @@
8
8
  import path from 'path';
9
9
  import solc from 'solc';
10
10
  import { check, collectSolFiles } from '../utils/dir.js';
11
- import { setVersion, build } from '../utils/builder.js';
11
+ import {
12
+ setVersion,
13
+ build,
14
+ loadSolcVersion,
15
+ extractLoadableVersion
16
+ } from '../utils/builder.js';
17
+ import fs from 'fs';
12
18
 
13
19
  /**
14
- * Current Solidity compiler instance
20
+ * Stored compiler config path
21
+ * @type {string}
22
+ */
23
+ const compConfigPath = './localStorage/compilerConfig.json';
24
+
25
+ /**
26
+ * Global compiler configuration state
27
+ * @type {Object}
28
+ * @property {Object} currentSolcInstance - Current Solidity compiler instance
29
+ * @property {boolean} optimizer - Whether gas optimizer is enabled
30
+ * @property {number} optimizerRuns - Number of optimizer runs
31
+ * @property {boolean} viaIR - Whether to use IR-based code generation
32
+ */
33
+ let compConfig = {};
34
+
35
+ /**
36
+ * JSON file fields of compiler configuration
15
37
  * @type {Object}
38
+ * @property {string} version - Current Solidity compiler version
39
+ * @property {boolean} optimizer - Whether gas optimizer is enabled
40
+ * @property {number} optimizerRuns - Number of optimizer runs
41
+ * @property {boolean} viaIR - Whether to use IR-based code generation
16
42
  */
17
- let currentSolcInstance = solc; // default local compiler
43
+ export let compConfigFile = {};
18
44
 
19
45
  /**
20
46
  * Global compiler configuration state
@@ -23,11 +49,37 @@ let currentSolcInstance = solc; // default local compiler
23
49
  * @property {number} optimizerRuns - Number of optimizer runs
24
50
  * @property {boolean} viaIR - Whether to use IR-based code generation
25
51
  */
26
- let compilerConfig = {
27
- optimizer: false,
28
- optimizerRuns: 200,
29
- viaIR: false
30
- };
52
+ let storedCompConfig;
53
+
54
+ // Load config file
55
+ if(fs.existsSync(compConfigPath)){
56
+ storedCompConfig = JSON.parse(fs.readFileSync(compConfigPath));
57
+ } else {
58
+ storedCompConfig = null;
59
+ }
60
+
61
+ // Initialize global configuration of compiler
62
+ if(storedCompConfig){
63
+ compConfigFile = storedCompConfig;
64
+ compConfig.currentSolcInstance = await loadSolcVersion(compConfigFile.version);
65
+ compConfig.optimizer = compConfigFile.optimizer;
66
+ compConfig.viaIR = compConfigFile.viaIR;
67
+ compConfig.optimizerRuns = compConfigFile.optimizerRuns;
68
+ } else {
69
+ compConfig = {
70
+ currentSolcInstance: solc, // default local compiler
71
+ optimizer: false,
72
+ viaIR: false,
73
+ optimizerRuns: 200
74
+ }
75
+ compConfigFile.version = extractLoadableVersion(compConfig.currentSolcInstance.version());
76
+ compConfigFile.optimizer = compConfig.optimizer;
77
+ compConfigFile.viaIR = compConfig.viaIR;
78
+ compConfigFile.optimizerRuns = compConfig.optimizerRuns;
79
+
80
+ // Update config file
81
+ fs.writeFileSync(compConfigPath, JSON.stringify(compConfigFile, null, 2));
82
+ }
31
83
 
32
84
  /**
33
85
  * Update the Solidity compiler to a specific version
@@ -40,7 +92,12 @@ let compilerConfig = {
40
92
  */
41
93
  export async function updateCompiler(version){
42
94
  try{
43
- currentSolcInstance = await setVersion(version, currentSolcInstance);
95
+ // Update global configuration
96
+ compConfig.currentSolcInstance = await setVersion(version, compConfig.currentSolcInstance);
97
+
98
+ // Update config file
99
+ compConfigFile.version = extractLoadableVersion(compConfig.currentSolcInstance.version());
100
+ fs.writeFileSync(compConfigPath, JSON.stringify(compConfigFile, null, 2));
44
101
  } catch(err) {
45
102
  console.error(err);
46
103
  }
@@ -53,7 +110,7 @@ export async function updateCompiler(version){
53
110
  * const version = currentCompiler(); // Returns: "0.8.20+commit.a1b79de6.Emscripten.clang"
54
111
  */
55
112
  export function currentCompiler(){
56
- return currentSolcInstance.version();
113
+ return compConfig.currentSolcInstance.version();
57
114
  }
58
115
 
59
116
  /**
@@ -80,19 +137,24 @@ export function compilerOptions(gasOptimizer, viaIR, optimizerRuns = 200) {
80
137
  }
81
138
 
82
139
  // Update global configuration
83
- compilerConfig.optimizer = gasOptimizer;
84
- compilerConfig.viaIR = viaIR;
85
- compilerConfig.optimizerRuns = optimizerRuns;
140
+ compConfig.optimizer = gasOptimizer;
141
+ compConfig.viaIR = viaIR;
142
+ compConfig.optimizerRuns = optimizerRuns;
143
+
144
+ compConfigFile.optimizer = compConfig.optimizer;
145
+ compConfigFile.viaIR = compConfig.viaIR;
146
+ compConfigFile.optimizerRuns = compConfig.optimizerRuns;
147
+
148
+ // Update config file
149
+ fs.writeFileSync(compConfigPath, JSON.stringify(compConfigFile, null, 2));
86
150
 
87
151
  // Provide user feedback
88
152
  console.log('✓ Compiler options updated:');
89
- console.log(` Gas Optimizer: ${compilerConfig.optimizer ? 'Enabled' : 'Disabled'}`);
90
- if (compilerConfig.optimizer) {
91
- console.log(` Optimizer Runs: ${compilerConfig.optimizerRuns}`);
153
+ console.log(` Gas Optimizer: ${compConfig.optimizer ? 'Enabled' : 'Disabled'}`);
154
+ if (compConfig.optimizer) {
155
+ console.log(` Optimizer Runs: ${compConfig.optimizerRuns}`);
92
156
  }
93
- console.log(` ViaIR: ${compilerConfig.viaIR ? 'Enabled' : 'Disabled'}`);
94
-
95
- return compilerConfig;
157
+ console.log(` ViaIR: ${compConfig.viaIR ? 'Enabled' : 'Disabled'}`);
96
158
  } catch (error) {
97
159
  console.error('Error setting compiler options:', error.message);
98
160
  return null;
@@ -106,7 +168,7 @@ export function compilerOptions(gasOptimizer, viaIR, optimizerRuns = 200) {
106
168
  * const opts = getCompilerOptions();
107
169
  */
108
170
  export function getCompilerOptions() {
109
- return { ...compilerConfig };
171
+ return { ...compConfig };
110
172
  }
111
173
 
112
174
  /**
@@ -0,0 +1,5 @@
1
+ import { compConfigFile } from './build.js';
2
+
3
+ export function getConfigInfo() {
4
+ console.log(compConfigFile);
5
+ }
@@ -12,22 +12,18 @@ import {
12
12
  deleteByIndex,
13
13
  deleteByIndexArr,
14
14
  getAccountInfo,
15
- detectDupWallet
15
+ detectDupWallet,
16
+ updateWalletJSON,
17
+ getWalletJSON,
18
+ updateAccountMemory
16
19
  } from '../utils/accounter.js';
17
- import fs from 'fs';
18
-
19
-
20
- /**
21
- * Object containing all stored accounts (imported, generated, HD, and node-managed)
22
- * @type {Object}
23
- */
24
- let walletJSON;
25
20
 
26
21
  /**
27
22
  * Array containing all accounts (imported, generated, HD, and node-managed)
28
23
  * @type {Array<Object>}
29
24
  */
30
25
  export let allAccounts = [];
26
+ allAccounts = getWalletJSON();
31
27
 
32
28
  /**
33
29
  * Array containing only regular accounts (imported and generated)
@@ -41,6 +37,11 @@ export let accounts = [];
41
37
  */
42
38
  export let hdAccounts = [];
43
39
 
40
+ // Update accounts & hdAccounts arrays
41
+ const { memAccArr, memHDAccArr } = updateAccountMemory(allAccounts);
42
+ accounts = memAccArr;
43
+ hdAccounts = memHDAccArr;
44
+
44
45
  /**
45
46
  * Add accounts from private key(s)
46
47
  * @param {string|Array<string>} privKeyArr - Single private key or array of private keys
@@ -58,6 +59,7 @@ export function addAccounts(privKeyArr) {
58
59
  throw `You need to add at least one private key. If you have no private key you can create new accounts by 'newAccounts()'! `;
59
60
  }
60
61
 
62
+ let newAccObj;
61
63
  const dupWallet = detectDupWallet(privKeyArr);
62
64
 
63
65
  if(dupWallet.status) {
@@ -68,38 +70,33 @@ export function addAccounts(privKeyArr) {
68
70
 
69
71
  if(typeof privKeyArr === 'string'){
70
72
  const newAccount = new ethers.Wallet(privKeyArr, provider);
71
- allAccounts.push({
73
+ newAccObj = {
72
74
  index: allAccounts.length,
73
75
  address: newAccount.address,
74
76
  privateKey: privKeyArr,
75
77
  type: 'user-imported',
76
78
  contracts: []
77
- });
78
- accounts.push({
79
- index: allAccounts.length - 1,
80
- address: newAccount.address,
81
- privateKey: privKeyArr,
82
- type: 'user-imported',
83
- contracts: []
84
- });
79
+ }
80
+ allAccounts.push(newAccObj);
81
+ updateWalletJSON(allAccounts);
82
+ newAccObj.index = allAccounts.length - 1;
83
+ accounts.push(newAccObj);
85
84
  console.log(allAccounts[newFrom]);
86
85
  }
87
86
 
88
87
  if(Array.isArray(privKeyArr)){
89
88
  privKeyArr.map(privKey => {
90
89
  const newAccount = new ethers.Wallet(privKey, provider);
91
- allAccounts.push({
90
+ newAccObj = {
92
91
  index: allAccounts.length,
93
92
  address: newAccount.address,
94
93
  privateKey: privKey,
95
94
  contracts: []
96
- });
97
- accounts.push({
98
- index: allAccounts.length - 1,
99
- address: newAccount.address,
100
- privateKey: privKey,
101
- contracts: []
102
- });
95
+ }
96
+ allAccounts.push(newAccObj);
97
+ updateWalletJSON(allAccounts);
98
+ newAccObj.index = allAccounts.length - 1;
99
+ accounts.push(newAccObj);
103
100
  });
104
101
 
105
102
  console.log(allAccounts.slice(newFrom));
@@ -115,6 +112,7 @@ export function addAccounts(privKeyArr) {
115
112
  */
116
113
  export function addHD(phrase, count = 10) {
117
114
  const newFrom = allAccounts.length;
115
+ let newAccObj;
118
116
  const hdNode = ethers.HDNodeWallet.fromPhrase(phrase);
119
117
 
120
118
  const existingPhrase = allAccounts.find(wallet => wallet.phrase == phrase);
@@ -128,7 +126,7 @@ export function addHD(phrase, count = 10) {
128
126
  if(dupHDWallet.status) {
129
127
  throw `Error: Wallets may NOT be duplicated! You are adding wallet index ${dupHDWallet.index} again!`
130
128
  } else {
131
- allAccounts.push({
129
+ newAccObj = {
132
130
  index: allAccounts.length,
133
131
  address: hdWallet.address,
134
132
  phrase: hdWallet.mnemonic.phrase,
@@ -137,20 +135,13 @@ export function addHD(phrase, count = 10) {
137
135
  path: hdWallet.path,
138
136
  depth: hdWallet.depth,
139
137
  contracts: []
140
- });
141
- hdAccounts.push({
142
- index: allAccounts.length - 1,
143
- address: hdWallet.address,
144
- phrase: hdWallet.mnemonic.phrase,
145
- privateKey: hdWallet.privateKey,
146
- type: 'user-imported',
147
- path: hdWallet.path,
148
- depth: hdWallet.depth,
149
- contracts: []
150
- });
138
+ },
139
+ allAccounts.push(newAccObj);
140
+ newAccObj.index = allAccounts.length - 1;
141
+ hdAccounts.push(newAccObj);
151
142
  }
152
143
  }
153
-
144
+ updateWalletJSON(allAccounts);
154
145
  console.log(`!WARNING!\n The generated accounts are NOT safe. Do NOT use them on main net!`);
155
146
  console.log(allAccounts.slice(newFrom));
156
147
  }
@@ -164,25 +155,22 @@ export function addHD(phrase, count = 10) {
164
155
  export function createAccounts(count = 1) {
165
156
  const newAccounts = Array.from({length: count}, () => ethers.Wallet.createRandom());
166
157
  const newFrom = accounts.length;
158
+ let newAccObj;
167
159
 
168
160
  for(let i = 0; i < newAccounts.length; i++) {
169
-
170
- allAccounts.push({
161
+ newAccObj = {
171
162
  index: allAccounts.length,
172
163
  address: newAccounts[i].address,
173
164
  privateKey: newAccounts[i].privateKey,
174
165
  type: 'user-generated',
175
166
  contracts: []
176
- });
177
- accounts.push({
178
- index: allAccounts.length - 1,
179
- address: newAccounts[i].address,
180
- privateKey: newAccounts[i].privateKey,
181
- type: 'user-generated',
182
- contracts: []
183
- })
167
+ }
168
+ allAccounts.push(newAccObj);
169
+ newAccObj.index = allAccounts.length - 1;
170
+ accounts.push(newAccObj);
184
171
 
185
172
  }
173
+ updateWalletJSON(allAccounts);
186
174
  console.log(`!WARNING!\n The generated accounts are NOT safe. Do NOT use them on main net!`);
187
175
  console.log(allAccounts.slice(newFrom));
188
176
  }
@@ -196,10 +184,11 @@ export function createAccounts(count = 1) {
196
184
  export function createHD(count = 10) {
197
185
  const hdNode = ethers.HDNodeWallet.createRandom();
198
186
  const newFrom = allAccounts.length;
187
+ let newAccObj;
199
188
 
200
189
  for (let i = 0; i < count; i++) {
201
190
  const hdWallet = hdNode.derivePath(i.toString());
202
- allAccounts.push({
191
+ newAccObj = {
203
192
  index: allAccounts.length,
204
193
  address: hdWallet.address,
205
194
  phrase: hdWallet.mnemonic.phrase,
@@ -210,21 +199,12 @@ export function createHD(count = 10) {
210
199
  path: hdWallet.path,
211
200
  depth: hdWallet.depth,
212
201
  contracts: []
213
- });
214
- hdAccounts.push({
215
- index: allAccounts.length - 1,
216
- address: hdWallet.address,
217
- phrase: hdWallet.mnemonic.phrase,
218
- privateKey: hdWallet.privateKey,
219
- type: 'user-generated',
220
- nonce: 0,
221
- balance: 0,
222
- path: hdWallet.path,
223
- depth: hdWallet.depth,
224
- contracts: []
225
- });
202
+ }
203
+ allAccounts.push(newAccObj);
204
+ newAccObj.index = allAccounts.length - 1;
205
+ hdAccounts.push(newAccObj);
226
206
  }
227
-
207
+ updateWalletJSON(allAccounts);
228
208
  console.log(`!WARNING!\n The generated accounts are NOT safe. Do NOT use them on main net!`);
229
209
  console.log(allAccounts.slice(newFrom));
230
210
  }
@@ -342,6 +322,7 @@ export async function connectWallet() {
342
322
  signer: accSigner // Store signer reference
343
323
  })
344
324
  }
325
+ updateWalletJSON(allAccounts);
345
326
  } catch(err) {
346
327
  console.error(err);
347
328
  }
@@ -7,6 +7,13 @@
7
7
 
8
8
  import { allAccounts, accounts, hdAccounts } from '../services/wallet.js';
9
9
  import { provider } from '../services/network.js';
10
+ import fs from 'fs';
11
+
12
+ /**
13
+ * The path which in wallets json file will be saved.
14
+ * @type {String}
15
+ */
16
+ const walletJSONPath = './localStorage/wallets.json';
10
17
 
11
18
  /**
12
19
  * Delete account(s) by index
@@ -22,6 +29,8 @@ export function deleteByIndex(index) {
22
29
  deleteByIndexArr(index);
23
30
  } else if (typeof index === 'number') {
24
31
  _deleteBySingIndex(index);
32
+ } else if (index === null || index === undefined) {
33
+ _deleteAll();
25
34
  }
26
35
  }
27
36
 
@@ -82,6 +91,46 @@ export function detectDupWallet(privKeyArr) {
82
91
  }
83
92
  }
84
93
 
94
+ /**
95
+ * Writes/Updates wallets json file
96
+ * @param {Array<string>} walletArr - Account array
97
+ * @example
98
+ * updateWalletJSON([{
99
+ index: allAccounts.length,
100
+ address: newAccount.address,
101
+ privateKey: privKeyArr,
102
+ type: 'user-imported',
103
+ contracts: []
104
+ }]);
105
+ */
106
+ export function updateWalletJSON(walletArr) {
107
+ fs.writeFileSync(walletJSONPath, JSON.stringify(walletArr, null, 2));
108
+ }
109
+
110
+ /**
111
+ * Returns wallets' object from saved json file
112
+ * @returns {Object}
113
+ */
114
+ export function getWalletJSON() {
115
+ return JSON.parse(fs.readFileSync(walletJSONPath));
116
+ }
117
+
118
+ export function updateAccountMemory(allAccArr) {
119
+ let memAccArr = [];
120
+ let memHDAccArr = [];
121
+ for(let i = 0; i < allAccArr.length; i++) {
122
+ if(allAccArr[i].phrase) {
123
+ memHDAccArr.push(allAccArr[i])
124
+ } else {
125
+ memAccArr.push(allAccArr[i]);
126
+ }
127
+ }
128
+ return {
129
+ memAccArr,
130
+ memHDAccArr
131
+ }
132
+ }
133
+
85
134
  /**
86
135
  * Get information for multiple accounts (internal)
87
136
  * @private
@@ -127,13 +176,6 @@ async function _getAccountInfo(_index) {
127
176
  * @returns {void}
128
177
  */
129
178
  function _deleteBySingIndex(_index) {
130
- if (_index === null || _index === undefined) {
131
- // Clear all arrays
132
- allAccounts.splice(0);
133
- accounts.splice(0);
134
- hdAccounts.splice(0);
135
- return;
136
- }
137
179
 
138
180
  // Find and remove from allAccounts
139
181
  const accountIndex = allAccounts.findIndex(acc => acc.index === _index);
@@ -144,6 +186,9 @@ function _deleteBySingIndex(_index) {
144
186
  for (let i = accountIndex; i < allAccounts.length; i++) {
145
187
  allAccounts[i].index = i;
146
188
  }
189
+
190
+ // Update wallet json file
191
+ fs.writeFileSync(walletJSONPath, JSON.stringify(allAccounts, null, 2));
147
192
 
148
193
  // Remove from accounts array if it exists there
149
194
  const regularIndex = accounts.findIndex(acc => acc.index === _index);
@@ -210,4 +255,17 @@ function _findDupWalletByArr(privKeyArr) {
210
255
  return {
211
256
  status: false
212
257
  }
258
+ }
259
+
260
+ /**
261
+ * Removes all accounts from storage and memory
262
+ * @private
263
+ * @returns {null}
264
+ */
265
+ function _deleteAll() {
266
+ allAccounts.splice(0);
267
+ accounts.splice(0);
268
+ hdAccounts.splice(0);
269
+ fs.writeFileSync(walletJSONPath, JSON.stringify([], null, 2));
270
+ return;
213
271
  }
@@ -167,3 +167,18 @@ export function build(fullPath, selectedContracts, buildPath){
167
167
  });
168
168
  }
169
169
 
170
+ /**
171
+ * Extract loadable version format from full version string
172
+ * @param {string} fullVersion - Full version string (e.g., "0.8.20+commit.a1b79de6.Emscripten.clang")
173
+ * @returns {string} Loadable version format (e.g., "v0.8.20+commit.a1b79de6")
174
+ * @example
175
+ * extractLoadableVersion("0.8.20+commit.a1b79de6.Emscripten.clang"); // Returns: "v0.8.20+commit.a1b79de6"
176
+ */
177
+ export function extractLoadableVersion(fullVersion) {
178
+ // Match version number and commit hash from full version string
179
+ const match = fullVersion.match(/(\d+\.\d+\.\d+)\+commit\.([a-f0-9]+)/);
180
+ if (!match) {
181
+ throw new Error(`Unable to extract version from: ${fullVersion}`);
182
+ }
183
+ return `v${match[1]}+commit.${match[2]}`;
184
+ }