ethershell 0.2.4-beta.0 → 0.2.6-beta.0
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 +223 -294
- package/package.json +1 -1
- package/src/utils/builder.js +105 -50
package/README.md
CHANGED
|
@@ -13,19 +13,19 @@ An interactive Node.js console for Ethereum smart contract development. Write, c
|
|
|
13
13
|
|
|
14
14
|
## ✨ Features
|
|
15
15
|
|
|
16
|
-
- **Interactive REPL Shell**
|
|
17
|
-
- **Solidity Compilation**
|
|
18
|
-
- **Smart Contract Deployment**
|
|
19
|
-
- **Wallet Management**
|
|
20
|
-
- **Multi
|
|
21
|
-
- **Contract Proxy Wrapper**
|
|
22
|
-
- **Contract Interactions**
|
|
23
|
-
- **ABI & Bytecode Generation**
|
|
24
|
-
- **Node Signer Integration**
|
|
25
|
-
- **TypeScript Code Generation**
|
|
26
|
-
- **Gas Optimization**
|
|
27
|
-
- **Persistent Configuration**
|
|
28
|
-
- **Comprehensive JSDoc**
|
|
16
|
+
- **Interactive REPL Shell** – Custom async evaluation with top‑level `await` support in a Node.js REPL.
|
|
17
|
+
- **Solidity Compilation** – Compile contracts with configurable optimization and `viaIR` options.
|
|
18
|
+
- **Smart Contract Deployment** – Deploy contracts to any EVM network using ethers v6.
|
|
19
|
+
- **Wallet Management** – Create, import, and manage wallets (regular & HD wallets, plus node‑managed accounts).
|
|
20
|
+
- **Multi‑Network Support** – Switch between blockchain networks with persistent provider configuration.
|
|
21
|
+
- **Contract Proxy Wrapper** – Enhanced contract interaction with a proxy that supports `from`, `value`, gas options, EIP‑1559 fields, and custom data.
|
|
22
|
+
- **Contract Interactions** – Call contract methods (read & write) with advanced options directly from the shell.
|
|
23
|
+
- **ABI & Bytecode Generation** – Organized artifacts (`artifacts`, `abis`, `bytecode`, `metadata`) plus an aggregated ABI file.
|
|
24
|
+
- **Node Signer Integration** – Connect to node‑managed accounts (Hardhat, Anvil, etc.).
|
|
25
|
+
- **TypeScript Code Generation** – Auto‑generate TypeScript types from ABIs into a `types/` directory under the build path.
|
|
26
|
+
- **Gas Optimization Controls** – Configure optimizer enable flag, runs, and `viaIR` globally.
|
|
27
|
+
- **Persistent Configuration** – Store provider, compiler config, wallets, and contracts in the `./ethershell` directory.
|
|
28
|
+
- **Comprehensive JSDoc** – Rich JSDoc comments for IDE autocompletion and type hints throughout the codebase.
|
|
29
29
|
|
|
30
30
|
## 🚀 Quick Start
|
|
31
31
|
|
|
@@ -36,13 +36,15 @@ An interactive Node.js console for Ethereum smart contract development. Write, c
|
|
|
36
36
|
npm i -g ethershell
|
|
37
37
|
|
|
38
38
|
# Start EtherShell in the root directory of your project:
|
|
39
|
-
ethershell
|
|
39
|
+
ethershell
|
|
40
40
|
|
|
41
|
-
#or
|
|
41
|
+
# or
|
|
42
42
|
|
|
43
43
|
npx ethershell
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
Run the CLI in your project root so EtherShell can find `./contracts` and write `./ethershell` and `./build` data next to your project files.
|
|
47
|
+
|
|
46
48
|
### Basic Usage
|
|
47
49
|
|
|
48
50
|
```bash
|
|
@@ -53,11 +55,11 @@ ethershell
|
|
|
53
55
|
# EtherShell>
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
## 📖 Step
|
|
58
|
+
## 📖 Step‑by‑Step Guide
|
|
57
59
|
|
|
58
60
|
### 1. Network Configuration
|
|
59
61
|
|
|
60
|
-
First, connect to a blockchain network
|
|
62
|
+
First, connect to a blockchain network.
|
|
61
63
|
|
|
62
64
|
```javascript
|
|
63
65
|
// View current network
|
|
@@ -73,6 +75,8 @@ EtherShell> defaultChain()
|
|
|
73
75
|
{ URL: 'http://127.0.0.1:8545' }
|
|
74
76
|
```
|
|
75
77
|
|
|
78
|
+
Internally, `chain()` updates the in‑memory provider and saves the selected endpoint to `./ethershell/config.json` so it persists between sessions.
|
|
79
|
+
|
|
76
80
|
### 2. Wallet Management
|
|
77
81
|
|
|
78
82
|
#### Create New Wallets
|
|
@@ -103,6 +107,8 @@ EtherShell> newWallet(5)
|
|
|
103
107
|
]
|
|
104
108
|
```
|
|
105
109
|
|
|
110
|
+
`newWallet()` persists wallets into `./ethershell/wallets.json`.
|
|
111
|
+
|
|
106
112
|
#### Import Existing Wallets
|
|
107
113
|
|
|
108
114
|
```javascript
|
|
@@ -117,6 +123,8 @@ EtherShell> addWallet([
|
|
|
117
123
|
])
|
|
118
124
|
```
|
|
119
125
|
|
|
126
|
+
EtherShell detects duplicate wallets by private key and throws if you attempt to re‑add an existing key.
|
|
127
|
+
|
|
120
128
|
#### HD Wallet Management
|
|
121
129
|
|
|
122
130
|
```javascript
|
|
@@ -140,16 +148,16 @@ EtherShell> addHDWallet('word word word ... (12 or 24 words)', 10)
|
|
|
140
148
|
EtherShell> hdWallets()
|
|
141
149
|
!WARNING!
|
|
142
150
|
The generated accounts are NOT safe. Do NOT use them on main net!
|
|
143
|
-
[...]
|
|
151
|
+
[ ... ]
|
|
144
152
|
```
|
|
145
153
|
|
|
146
|
-
#### Connect to Node
|
|
154
|
+
#### Connect to Node‑Managed Accounts
|
|
147
155
|
|
|
148
156
|
```javascript
|
|
149
|
-
// For
|
|
157
|
+
// For Hardhat, Anvil, or other nodes with unlocked accounts
|
|
150
158
|
EtherShell> connectWallet()
|
|
151
159
|
|
|
152
|
-
// This adds accounts managed by the node (e.g.,
|
|
160
|
+
// This adds accounts managed by the node (e.g., Hardhat default accounts)
|
|
153
161
|
```
|
|
154
162
|
|
|
155
163
|
#### View Wallets
|
|
@@ -166,7 +174,7 @@ EtherShell> walletInfo(0)
|
|
|
166
174
|
// or by address
|
|
167
175
|
EtherShell> walletInfo('0x1234...5678')
|
|
168
176
|
// or multiple
|
|
169
|
-
EtherShell> walletInfo([
|
|
177
|
+
EtherShell> walletInfo()[1][2]
|
|
170
178
|
|
|
171
179
|
// Change default account
|
|
172
180
|
EtherShell> changeDefWallet(0)
|
|
@@ -189,39 +197,47 @@ EtherShell> removeWallet('0x1234...5678')
|
|
|
189
197
|
EtherShell> removeWallet('word word word ...')
|
|
190
198
|
|
|
191
199
|
// Delete multiple by indices
|
|
192
|
-
EtherShell> removeWallet([
|
|
200
|
+
EtherShell> removeWallet()[2][3]
|
|
193
201
|
|
|
194
202
|
// Delete all wallets
|
|
195
203
|
EtherShell> removeWallet()
|
|
196
204
|
```
|
|
197
205
|
|
|
206
|
+
Deletion updates in‑memory arrays and rewrites `./ethershell/wallets.json`; if the removed wallet is the default one, the default entry in `config.json` is also cleared.
|
|
207
|
+
|
|
198
208
|
### 3. Solidity Compilation
|
|
199
209
|
|
|
200
210
|
#### Configure Compiler
|
|
201
211
|
|
|
202
212
|
```javascript
|
|
203
|
-
// View current compiler version
|
|
213
|
+
// View current compiler version (bundled solc if no remote has been loaded)
|
|
204
214
|
EtherShell> compiler()
|
|
205
|
-
"0.8.
|
|
215
|
+
"0.8.xx+commit....Emscripten.clang"
|
|
206
216
|
|
|
207
217
|
// Switch to a different Solidity version
|
|
208
|
-
EtherShell> compUpdate('v0.8.
|
|
209
|
-
Loaded solc version: 0.8.
|
|
218
|
+
EtherShell> compUpdate('v0.8.29+commit.ab55807c')
|
|
219
|
+
Loaded solc version: 0.8.29+commit.ab55807c.Emscripten.clang
|
|
210
220
|
|
|
211
221
|
// Configure compilation options (gasOptimizer, viaIR, optimizerRuns)
|
|
212
222
|
EtherShell> compOpts(true, false, 1000)
|
|
213
223
|
✓ Compiler options updated:
|
|
214
224
|
Gas Optimizer: Enabled
|
|
215
|
-
Optimizer Runs: 1000
|
|
216
225
|
ViaIR: Disabled
|
|
226
|
+
Optimizer Runs: 1000
|
|
217
227
|
|
|
218
|
-
// Get current options
|
|
228
|
+
// Get current compiler options
|
|
219
229
|
EtherShell> compInfo()
|
|
220
|
-
{
|
|
230
|
+
{
|
|
231
|
+
version: 'v0.8.29+commit.ab55807c',
|
|
232
|
+
optimizer: true,
|
|
233
|
+
viaIR: false,
|
|
234
|
+
optimizerRuns: 1000,
|
|
235
|
+
compilePath: './build'
|
|
236
|
+
}
|
|
221
237
|
|
|
222
238
|
// Get current config info
|
|
223
239
|
EtherShell> configInfo()
|
|
224
|
-
{
|
|
240
|
+
{
|
|
225
241
|
providerEndpoint: '...',
|
|
226
242
|
defaultWallet: { ... },
|
|
227
243
|
compiler: {
|
|
@@ -241,37 +257,45 @@ EtherShell> defWallet()
|
|
|
241
257
|
EtherShell> compPath('./custom-build')
|
|
242
258
|
```
|
|
243
259
|
|
|
260
|
+
Compiler configuration is kept in memory and mirrored into `config.json`, including version, optimizer flags, runs, and the default `compilePath`.
|
|
261
|
+
|
|
244
262
|
#### Compile Contracts
|
|
245
263
|
|
|
246
264
|
```javascript
|
|
247
265
|
// Compile all .sol files in ./contracts directory
|
|
248
266
|
EtherShell> build()
|
|
249
267
|
Contracts compiled into /path/to/build
|
|
268
|
+
Aggregated ABI generated at /path/to/build/aggregated.abi.json
|
|
250
269
|
TypeScript types generated in /path/to/build/types
|
|
251
270
|
|
|
252
271
|
// Compile a specific contract file
|
|
253
272
|
EtherShell> build('./contracts/MyToken.sol')
|
|
254
273
|
Contract compiled into /path/to/build
|
|
255
274
|
|
|
256
|
-
// Compile specific contracts from a file
|
|
275
|
+
// Compile specific contracts from a file to a custom build directory
|
|
257
276
|
EtherShell> build('./contracts/MyToken.sol', ['MyToken', 'OtherContract'], './custom-build')
|
|
258
277
|
Contracts compiled into /path/to/custom-build
|
|
259
278
|
|
|
260
279
|
// Clean build directory
|
|
261
280
|
EtherShell> clean()
|
|
262
281
|
Directory deleted successfully
|
|
263
|
-
```
|
|
282
|
+
```
|
|
264
283
|
|
|
265
284
|
**Compiler Output Structure:**
|
|
266
|
-
|
|
285
|
+
|
|
286
|
+
```text
|
|
267
287
|
build/
|
|
268
|
-
├── artifacts/
|
|
269
|
-
├── abis/
|
|
270
|
-
├── bytecode/
|
|
271
|
-
├── metadata/
|
|
272
|
-
|
|
288
|
+
├── artifacts/ # Complete contract data with metadata
|
|
289
|
+
├── abis/ # Contract ABIs (.abi.json files)
|
|
290
|
+
├── bytecode/ # Contract bytecode (.bin files)
|
|
291
|
+
├── metadata/ # Contract metadata (.metadata.json files)
|
|
292
|
+
├── standard-json/ # Per‑entry solc standard JSON inputs
|
|
293
|
+
├── aggregated.abi.json # Flattened ABI array from all ABI files
|
|
294
|
+
└── types/ # Auto-generated TypeScript types
|
|
273
295
|
```
|
|
274
296
|
|
|
297
|
+
TypeScript types are generated by scanning `build/abis` and writing `.ts` files plus an `index.ts` barrel export.
|
|
298
|
+
|
|
275
299
|
### 4. Smart Contract Deployment
|
|
276
300
|
|
|
277
301
|
#### Deploy New Contracts
|
|
@@ -294,50 +318,29 @@ EtherShell> deploy('contractName')
|
|
|
294
318
|
// Deploy MyToken contract with constructor args and default wallet
|
|
295
319
|
// Arguments: contractName, args[], walletIndex, [chainURL], [abiLocation], [bytecodeLocation]
|
|
296
320
|
EtherShell> deploy('MyToken', ['MyTokenName', 'MTK', 1000000])
|
|
297
|
-
{
|
|
298
|
-
hash: '0x123abc...',
|
|
299
|
-
from: '0x1234...5678',
|
|
300
|
-
to: null,
|
|
301
|
-
address: '0xabcd...ef01',
|
|
302
|
-
name: 'MyToken',
|
|
303
|
-
chain: 'sepolia',
|
|
304
|
-
chainId: 11155111n,
|
|
305
|
-
deployType: 'ethershell-deployed'
|
|
306
|
-
}
|
|
321
|
+
{ ... }
|
|
307
322
|
|
|
308
323
|
// Deploy MyToken contract with constructor args and a non-default wallet
|
|
309
|
-
// Arguments: contractName, args[], walletIndex, [chainURL], [abiLocation], [bytecodeLocation]
|
|
310
324
|
EtherShell> deploy('MyToken', ['MyTokenName', 'MTK', 1000000], 0)
|
|
311
|
-
{
|
|
312
|
-
hash: '0x123abc...',
|
|
313
|
-
from: '0x1234...5678',
|
|
314
|
-
to: null,
|
|
315
|
-
address: '0xabcd...ef01',
|
|
316
|
-
name: 'MyToken',
|
|
317
|
-
chain: 'sepolia',
|
|
318
|
-
chainId: 11155111n,
|
|
319
|
-
deployType: 'ethershell-deployed'
|
|
320
|
-
}
|
|
325
|
+
{ ... }
|
|
321
326
|
|
|
322
327
|
// Deploy with custom chain
|
|
323
328
|
EtherShell> deploy('MyContract', ['arg1', 'arg2'], 0, 'https://custom-rpc.url')
|
|
324
|
-
|
|
325
|
-
// The deployed contract is automatically added to console context as a proxy
|
|
326
|
-
EtherShell> MyToken
|
|
327
|
-
Contract {
|
|
328
|
-
target: '0xabcd...ef01',
|
|
329
|
-
interface: Interface { ... },
|
|
330
|
-
runner: Signer { ... },
|
|
331
|
-
// ... contract methods available
|
|
332
|
-
}
|
|
333
329
|
```
|
|
334
330
|
|
|
331
|
+
ABIs and bytecode are resolved from the build directory via paths persisted in `./ethershell` local storage,.
|
|
332
|
+
|
|
335
333
|
#### Add Existing Contracts
|
|
336
334
|
|
|
337
335
|
```javascript
|
|
338
336
|
// Add an already-deployed contract
|
|
339
337
|
// Arguments: contractName, contractAddress, walletIndex, abiPath, [chainURL]
|
|
340
|
-
EtherShell> addContract(
|
|
338
|
+
EtherShell> addContract(
|
|
339
|
+
'USDT',
|
|
340
|
+
'0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
|
341
|
+
0,
|
|
342
|
+
'./build/abis/USDT.abi.json'
|
|
343
|
+
)
|
|
341
344
|
{
|
|
342
345
|
index: 1,
|
|
343
346
|
name: 'USDT',
|
|
@@ -347,11 +350,13 @@ EtherShell> addContract('USDT', '0xdAC17F958D2ee523a2206206994597C13D831ec7', 0,
|
|
|
347
350
|
deployType: 'pre-deployed'
|
|
348
351
|
}
|
|
349
352
|
|
|
350
|
-
// Now you can interact with it
|
|
353
|
+
// Now you can interact with it:
|
|
351
354
|
EtherShell> USDT.balanceOf('0x1234...5678')
|
|
352
|
-
|
|
355
|
+
1000000000000000000n
|
|
353
356
|
```
|
|
354
357
|
|
|
358
|
+
All added/deployed contracts are also stored in `./ethershell/contracts.json`.
|
|
359
|
+
|
|
355
360
|
### 5. Contract Interaction
|
|
356
361
|
|
|
357
362
|
#### View Contracts
|
|
@@ -387,6 +392,7 @@ EtherShell> contracts('0xabcd...ef01')
|
|
|
387
392
|
EtherShell> contracts('MyToken')
|
|
388
393
|
```
|
|
389
394
|
|
|
395
|
+
`contracts()` reads enriched contract information (including live balances).
|
|
390
396
|
#### Call Contract Functions
|
|
391
397
|
|
|
392
398
|
```javascript
|
|
@@ -394,50 +400,43 @@ EtherShell> contracts('MyToken')
|
|
|
394
400
|
EtherShell> MyToken
|
|
395
401
|
Contract { ... }
|
|
396
402
|
|
|
397
|
-
// Read-only functions
|
|
403
|
+
// Read-only functions
|
|
398
404
|
EtherShell> MyToken.name()
|
|
399
405
|
"MyTokenName"
|
|
400
406
|
|
|
401
407
|
EtherShell> MyToken.totalSupply()
|
|
402
408
|
1000000n
|
|
403
409
|
|
|
404
|
-
// State-changing functions
|
|
410
|
+
// State-changing functions
|
|
405
411
|
EtherShell> MyToken.transfer('0xRecipientAddress', 100)
|
|
406
|
-
|
|
412
|
+
ContractTransactionReceipt { ... }
|
|
407
413
|
|
|
408
414
|
// Call with advanced transaction options
|
|
409
415
|
EtherShell> MyToken.transfer('0xRecipientAddress', 100, {
|
|
410
|
-
from: '0xSenderAddress',
|
|
411
|
-
value: 10000000000000n,
|
|
412
|
-
gasLimit: 500000,
|
|
413
|
-
maxFeePerGas: 100000000000n,
|
|
416
|
+
from: '0xSenderAddress', // Switch signer (must be a non node-managed wallet)
|
|
417
|
+
value: 10000000000000n, // Send ETH (payable)
|
|
418
|
+
gasLimit: 500000,
|
|
419
|
+
maxFeePerGas: 100000000000n,
|
|
414
420
|
maxPriorityFeePerGas: 2000000000n,
|
|
415
421
|
nonce: 42,
|
|
416
422
|
chainId: 1
|
|
417
423
|
})
|
|
418
|
-
|
|
419
|
-
// Check balance
|
|
420
|
-
EtherShell> MyToken.balanceOf('0x1234...5678')
|
|
421
|
-
100n
|
|
422
424
|
```
|
|
423
425
|
|
|
424
|
-
**Contract Proxy Options:**
|
|
425
|
-
The contract proxy wrapper supports these transaction options:
|
|
426
|
-
- `from`: Change the signer/sender for the transaction
|
|
427
|
-
- `value`: ETH amount to send (for payable functions)
|
|
428
|
-
- `gasLimit` / `gas`: Maximum gas to use
|
|
429
|
-
- `gasPrice`: Legacy transaction gas price
|
|
430
|
-
- `maxFeePerGas`: EIP-1559 max fee per gas
|
|
431
|
-
- `maxPriorityFeePerGas`: EIP-1559 priority fee per gas
|
|
432
|
-
- `nonce`: Transaction nonce for ordering
|
|
433
|
-
- `chainId`: Network chain ID
|
|
434
|
-
- `accessList`: EIP-2930 access list
|
|
435
|
-
- `type`: Transaction type (0, 1, 2, or 3)
|
|
436
|
-
- `customData`: Custom data for special networks (zkSync)
|
|
426
|
+
**Contract Proxy Options include:**
|
|
437
427
|
|
|
438
|
-
|
|
428
|
+
- `from`: change signer (must have `privateKey`)
|
|
429
|
+
- `value`: ETH value for payable functions
|
|
430
|
+
- `gasLimit` or `gas`
|
|
431
|
+
- `gasPrice`
|
|
432
|
+
- `maxFeePerGas`, `maxPriorityFeePerGas`
|
|
433
|
+
- `nonce`
|
|
434
|
+
- `chainId`
|
|
435
|
+
- `accessList`
|
|
436
|
+
- `type`
|
|
437
|
+
- `customData` for special networks (e.g., zkSync)[file:25]
|
|
439
438
|
|
|
440
|
-
|
|
439
|
+
## 🎯 Complete Usage Example
|
|
441
440
|
|
|
442
441
|
```javascript
|
|
443
442
|
// 1. Connect to network
|
|
@@ -460,7 +459,6 @@ EtherShell> deploy('MyToken', ['TestToken', 'TEST', 1000000])
|
|
|
460
459
|
|
|
461
460
|
// 7. Interact with contract
|
|
462
461
|
EtherShell> MyToken.balanceOf('0x...')
|
|
463
|
-
1000000000000000000000000n
|
|
464
462
|
|
|
465
463
|
// 8. Transfer tokens with custom options
|
|
466
464
|
EtherShell> tx = MyToken.transfer('0x...', 100, {
|
|
@@ -478,129 +476,98 @@ EtherShell> contracts()
|
|
|
478
476
|
## 📋 Command Reference
|
|
479
477
|
|
|
480
478
|
### Network Commands
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
| `
|
|
485
|
-
| `
|
|
479
|
+
|
|
480
|
+
| Command | Description |
|
|
481
|
+
|------------------|-------------------------------------|
|
|
482
|
+
| `chain(url)` | Connect to blockchain network |
|
|
483
|
+
| `chainInfo()` | Get current network info |
|
|
484
|
+
| `defaultChain()` | Get default network URL |[file:13][file:19]
|
|
486
485
|
|
|
487
486
|
### Wallet Commands
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
| `
|
|
492
|
-
| `
|
|
493
|
-
| `
|
|
494
|
-
| `
|
|
495
|
-
| `
|
|
496
|
-
| `
|
|
497
|
-
| `
|
|
498
|
-
| `
|
|
499
|
-
| `
|
|
500
|
-
| `
|
|
487
|
+
|
|
488
|
+
| Command | Description |
|
|
489
|
+
|-------------------------------------------|--------------------------------------------------|
|
|
490
|
+
| `newWallet([count])` | Create random wallets |
|
|
491
|
+
| `addWallet(privKey\|keys)` | Import wallets from private keys |
|
|
492
|
+
| `newHDWallet([count])` | Create HD wallet with random mnemonic |
|
|
493
|
+
| `addHDWallet(phrase, count)` | Import HD wallet from mnemonic |
|
|
494
|
+
| `connectWallet()` | Connect to node‑managed accounts |
|
|
495
|
+
| `wallets()` | View regular accounts |
|
|
496
|
+
| `hdWallets()` | View HD accounts |
|
|
497
|
+
| `allWallets()` | View all accounts |
|
|
498
|
+
| `walletInfo(index\|address\|[indices])` | Get wallet details (balance, nonce) |
|
|
499
|
+
| `changeDefWallet(pointer)` | Set default account |
|
|
500
|
+
| `removeWallet(pointer)` | Delete account(s) |[file:13][file:20][file:22]
|
|
501
501
|
|
|
502
502
|
### Compiler Commands
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
| `
|
|
507
|
-
| `
|
|
508
|
-
| `
|
|
509
|
-
| `
|
|
510
|
-
| `
|
|
511
|
-
| `
|
|
512
|
-
| `
|
|
513
|
-
| `
|
|
503
|
+
|
|
504
|
+
| Command | Description |
|
|
505
|
+
|-------------------------------------------------|------------------------------------------|
|
|
506
|
+
| `compiler()` | Get current Solidity version string |
|
|
507
|
+
| `compUpdate(version)` | Load specific Solidity version |
|
|
508
|
+
| `compOpts(gasOpt, viaIR, runs)` | Configure optimizer and viaIR |
|
|
509
|
+
| `compInfo()` | Get current compiler configuration |
|
|
510
|
+
| `configInfo()` | Get full configuration object |
|
|
511
|
+
| `defWallet()` | Get default account from config |
|
|
512
|
+
| `compPath(newPath)` | Change build output path |
|
|
513
|
+
| `build([path], [contracts], [output])` | Compile contracts |
|
|
514
|
+
| `clean([path])` | Delete build directory (default `./build`) |[file:13][file:16][file:21][file:17]
|
|
514
515
|
|
|
515
516
|
### Contract Commands
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
| `
|
|
520
|
-
| `
|
|
517
|
+
|
|
518
|
+
| Command | Description |
|
|
519
|
+
|-------------------------------------------------------------------------|----------------------------------|
|
|
520
|
+
| `deploy(name, [args], [index], [chainURL], [abiLocation], [bytecodeLocation])` | Deploy new contract |
|
|
521
|
+
| `addContract(name, address, index, abiPath, [chainURL])` | Add existing contract |
|
|
522
|
+
| `contracts([pointer])` | List contracts or get a specific one |[file:13][file:15][file:18]
|
|
521
523
|
|
|
522
524
|
## 🛠️ Setup for Development
|
|
523
525
|
|
|
524
526
|
### Prerequisites
|
|
525
|
-
- Node.js 16+
|
|
526
|
-
- npm or yarn
|
|
527
|
-
- Basic Solidity knowledge
|
|
528
|
-
|
|
529
|
-
### Development Setup
|
|
530
|
-
|
|
531
|
-
```bash
|
|
532
|
-
# Install dev dependencies
|
|
533
|
-
npm install --save-dev @types/node typescript
|
|
534
527
|
|
|
535
|
-
|
|
536
|
-
npm
|
|
528
|
+
- Node.js 16+
|
|
529
|
+
- npm or yarn
|
|
530
|
+
- Basic Solidity knowledge[file:31]
|
|
537
531
|
|
|
538
|
-
# Run tests (if available)
|
|
539
|
-
npm test
|
|
540
|
-
```
|
|
541
532
|
|
|
542
533
|
### Project Structure
|
|
543
534
|
|
|
544
|
-
```
|
|
535
|
+
```text
|
|
545
536
|
ethershell/
|
|
546
537
|
├── bin/
|
|
547
|
-
│ └── cli.js
|
|
538
|
+
│ └── cli.js # REPL entry point
|
|
548
539
|
├── src/
|
|
549
540
|
│ ├── services/
|
|
550
|
-
│ │ ├── build.js
|
|
551
|
-
│ │ ├── wallet.js
|
|
552
|
-
│ │ ├── network.js
|
|
553
|
-
│ │ ├── addContracts.js
|
|
554
|
-
│ │ ├── contracts.js
|
|
555
|
-
│ │ ├──
|
|
556
|
-
│ │ └── files.js
|
|
541
|
+
│ │ ├── build.js # Compiler management & build orchestration
|
|
542
|
+
│ │ ├── wallet.js # Wallet management (accounts, HD, node-managed)
|
|
543
|
+
│ │ ├── network.js # Network provider configuration
|
|
544
|
+
│ │ ├── addContracts.js # Deployment & contract registration
|
|
545
|
+
│ │ ├── contracts.js # Contract lookup helper
|
|
546
|
+
│ │ ├── configSync.js # In‑memory <-> config.json synchronization
|
|
547
|
+
│ │ └── files.js # File system utilities (clean build)
|
|
557
548
|
│ └── utils/
|
|
558
|
-
│ ├── builder.js
|
|
559
|
-
│ ├── dir.js
|
|
560
|
-
│ ├── accounter.js
|
|
561
|
-
│ ├── contractProxy.js
|
|
562
|
-
│ ├── contractLister.js
|
|
563
|
-
│ ├── typeGenerator.js
|
|
564
|
-
│ ├── replHelper.js
|
|
565
|
-
│ ├── serialize.js
|
|
566
|
-
│ └── configFileUpdate.js #
|
|
567
|
-
├── contracts/
|
|
568
|
-
├── build/ # Compiled artifacts
|
|
569
|
-
├──
|
|
549
|
+
│ ├── builder.js # Low‑level solc Standard JSON compiler wrapper
|
|
550
|
+
│ ├── dir.js # Directory & import resolution utilities
|
|
551
|
+
│ ├── accounter.js # Account storage and deletion utilities
|
|
552
|
+
│ ├── contractProxy.js # Contract proxy wrapper with tx options
|
|
553
|
+
│ ├── contractLister.js # Contracts registry and balance formatting
|
|
554
|
+
│ ├── typeGenerator.js # TypeScript type generation from ABIs
|
|
555
|
+
│ ├── replHelper.js # REPL customization & async eval
|
|
556
|
+
│ ├── serialize.js # BigInt serialization for JSON
|
|
557
|
+
│ └── configFileUpdate.js # Helper to update provider in config.json
|
|
558
|
+
├── contracts/ # Your Solidity contracts
|
|
559
|
+
├── build/ # Compiled artifacts and types (output)
|
|
560
|
+
├── ethershell/ # Persistent config, wallets, contracts, solc cache
|
|
570
561
|
└── package.json
|
|
571
562
|
```
|
|
572
563
|
|
|
564
|
+
`./ethershell` contains `config.json`, `wallets.json`, `contracts.json`, and localStorage data used by the compiler and contract manager.
|
|
565
|
+
|
|
573
566
|
## 📚 Example Contracts
|
|
574
567
|
|
|
575
|
-
### Simple ERC20 Token
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
// contracts/MyToken.sol
|
|
579
|
-
pragma solidity ^0.8.20;
|
|
580
|
-
|
|
581
|
-
contract MyToken {
|
|
582
|
-
string public name = "My Token";
|
|
583
|
-
string public symbol = "MTK";
|
|
584
|
-
uint8 public decimals = 18;
|
|
585
|
-
uint256 public totalSupply = 1000000 * 10 ** uint256(decimals);
|
|
586
|
-
|
|
587
|
-
mapping(address => uint256) public balanceOf;
|
|
588
|
-
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
589
|
-
|
|
590
|
-
constructor() {
|
|
591
|
-
balanceOf[msg.sender] = totalSupply;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
function transfer(address to, uint256 value) public returns (bool) {
|
|
595
|
-
require(to != address(0));
|
|
596
|
-
require(balanceOf[msg.sender] >= value);
|
|
597
|
-
balanceOf[msg.sender] -= value;
|
|
598
|
-
balanceOf[to] += value;
|
|
599
|
-
emit Transfer(msg.sender, to, value);
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
```
|
|
568
|
+
### Simple ERC20‑Style Token
|
|
569
|
+
|
|
570
|
+
You can keep the existing sample token or plug in OpenZeppelin contracts; EtherShell will handle `@openzeppelin/...` imports as long as they are installed in `node_modules`.
|
|
604
571
|
|
|
605
572
|
### Deployment Example
|
|
606
573
|
|
|
@@ -613,7 +580,6 @@ EtherShell> deploy('MyToken')
|
|
|
613
580
|
|
|
614
581
|
// 3. Interact
|
|
615
582
|
EtherShell> MyToken.balanceOf('0x...')
|
|
616
|
-
1000000000000000000000000n
|
|
617
583
|
|
|
618
584
|
// 4. Transfer
|
|
619
585
|
EtherShell> MyToken.transfer('0x...', 100)
|
|
@@ -623,17 +589,18 @@ EtherShell> MyToken.transfer('0x...', 100)
|
|
|
623
589
|
|
|
624
590
|
### Persistent Storage
|
|
625
591
|
|
|
626
|
-
EtherShell stores
|
|
592
|
+
EtherShell stores runtime data under the `./ethershell` directory in your project root.
|
|
627
593
|
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
├── config.json
|
|
631
|
-
├── wallets.json
|
|
594
|
+
```text
|
|
595
|
+
ethershell/
|
|
596
|
+
├── config.json # Compiler and network settings + default wallet
|
|
597
|
+
├── wallets.json # Imported/generated wallets
|
|
598
|
+
└── contracts.json # Registered contracts and metadata
|
|
632
599
|
```
|
|
633
600
|
|
|
634
|
-
|
|
601
|
+
Configuration is updated automatically as you change network, compiler options, default wallet, deploy contracts, or add existing contracts.
|
|
635
602
|
|
|
636
|
-
|
|
603
|
+
### Configuration File Example
|
|
637
604
|
|
|
638
605
|
```json
|
|
639
606
|
{
|
|
@@ -644,7 +611,7 @@ The `config.json` file contains:
|
|
|
644
611
|
"type": "user-generated"
|
|
645
612
|
},
|
|
646
613
|
"compiler": {
|
|
647
|
-
"version": "v0.8.
|
|
614
|
+
"version": "v0.8.29+commit.ab55807c",
|
|
648
615
|
"optimizer": false,
|
|
649
616
|
"viaIR": false,
|
|
650
617
|
"optimizerRuns": 200,
|
|
@@ -653,101 +620,75 @@ The `config.json` file contains:
|
|
|
653
620
|
}
|
|
654
621
|
```
|
|
655
622
|
|
|
656
|
-
### Environment Variables (Optional)
|
|
657
|
-
|
|
658
|
-
Create a `.env` file (optional):
|
|
659
|
-
|
|
660
|
-
```env
|
|
661
|
-
# Network
|
|
662
|
-
RPC_URL=http://127.0.0.1:8545
|
|
663
|
-
|
|
664
|
-
# Build
|
|
665
|
-
BUILD_PATH=./build
|
|
666
|
-
|
|
667
|
-
# Contracts
|
|
668
|
-
CONTRACTS_PATH=./contracts
|
|
669
|
-
|
|
670
|
-
# Compiler
|
|
671
|
-
COMPILER_VERSION=0.8.20+commit.a1b79de6
|
|
672
|
-
OPTIMIZER_ENABLED=true
|
|
673
|
-
OPTIMIZER_RUNS=200
|
|
674
|
-
VIAIR_ENABLED=false
|
|
675
|
-
```
|
|
676
|
-
|
|
677
623
|
## 🔒 Security Warnings
|
|
678
624
|
|
|
679
625
|
⚠️ **IMPORTANT SECURITY NOTES:**
|
|
680
626
|
|
|
681
|
-
1.
|
|
682
|
-
2.
|
|
683
|
-
3.
|
|
684
|
-
4.
|
|
685
|
-
|
|
627
|
+
1. Never use generated accounts on mainnet – they are for development and testing only.
|
|
628
|
+
2. Keep private keys secret – do not commit wallets or config files containing secrets.
|
|
629
|
+
3. Use testnets (e.g. Sepolia) for experimentation before going to mainnet.
|
|
630
|
+
4. Always audit and review your contracts before production deployments.
|
|
631
|
+
|
|
632
|
+
Add to your `.gitignore`:
|
|
686
633
|
|
|
687
634
|
```bash
|
|
688
|
-
# Add to .gitignore
|
|
689
635
|
.env
|
|
690
|
-
.env.local
|
|
691
636
|
node_modules/
|
|
692
637
|
build/
|
|
693
|
-
|
|
638
|
+
ethershell/
|
|
694
639
|
*.log
|
|
695
640
|
```
|
|
696
641
|
|
|
697
|
-
|
|
642
|
+
This prevents accidental commits of compiled artifacts and local wallet/config state.
|
|
698
643
|
|
|
699
|
-
|
|
644
|
+
## 🐛 Troubleshooting
|
|
700
645
|
|
|
701
646
|
**Issue: `Error: Cannot find module 'ethers'`**
|
|
647
|
+
|
|
702
648
|
```bash
|
|
703
649
|
Solution: npm install
|
|
704
650
|
```
|
|
705
651
|
|
|
706
652
|
**Issue: `Cannot connect to network`**
|
|
707
|
-
|
|
708
|
-
- Check RPC URL
|
|
709
|
-
- Verify network
|
|
710
|
-
-
|
|
711
|
-
```
|
|
653
|
+
|
|
654
|
+
- Check the RPC URL passed to `chain()`.[file:19]
|
|
655
|
+
- Verify the network (Hardhat, Ganache, etc.) is running.
|
|
656
|
+
- For public RPCs, check your internet connection and provider limits.
|
|
712
657
|
|
|
713
658
|
**Issue: `Insufficient balance for gas`**
|
|
714
|
-
|
|
715
|
-
- Ensure wallet has enough ETH for gas
|
|
716
|
-
- Use testnet
|
|
717
|
-
- Check gas prices (sepolia is usually cheap)
|
|
718
|
-
```
|
|
659
|
+
|
|
660
|
+
- Ensure the selected wallet has enough ETH for gas.
|
|
661
|
+
- Use testnet faucets when working on Sepolia or other testnets.
|
|
719
662
|
|
|
720
663
|
**Issue: `Contract not found in build artifacts`**
|
|
721
|
-
```bash
|
|
722
|
-
- Run build() first
|
|
723
|
-
- Check contract names match exactly
|
|
724
|
-
- Verify .sol file exists in ./contracts
|
|
725
|
-
```
|
|
726
664
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
-
|
|
730
|
-
- Only imported/generated wallets with private keys support 'from'
|
|
731
|
-
- Use .connect() method for node-managed accounts
|
|
732
|
-
```
|
|
665
|
+
- Run `build()` first.[file:16]
|
|
666
|
+
- Check that contract names match exactly.
|
|
667
|
+
- Verify `.sol` files exist under `./contracts`. [file:23]
|
|
733
668
|
|
|
734
669
|
**Issue: `TypeScript types not generated`**
|
|
735
|
-
|
|
736
|
-
- Ensure
|
|
737
|
-
-
|
|
738
|
-
-
|
|
739
|
-
|
|
670
|
+
|
|
671
|
+
- Ensure compilation succeeded.
|
|
672
|
+
- Confirm ABI files exist under `build/abis`.
|
|
673
|
+
- Check console output for errors thrown by `generateAllTypes()`. [file:16][file:30]
|
|
674
|
+
|
|
675
|
+
**Issue: error about `{ from }` with node‑managed account**
|
|
676
|
+
|
|
677
|
+
- Node‑managed accounts do not have private keys in EtherShell, so `{ from }` cannot rebind them.
|
|
678
|
+
- Import or generate a wallet with a private key and use that address in `from` instead. [file:20][file:25]
|
|
740
679
|
|
|
741
680
|
## 📖 API Documentation
|
|
742
681
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
-
|
|
746
|
-
-
|
|
747
|
-
-
|
|
748
|
-
-
|
|
682
|
+
The source code uses detailed JSDoc comments:
|
|
683
|
+
|
|
684
|
+
- `@fileoverview` – file purpose and module description
|
|
685
|
+
- `@param` – parameter types and descriptions
|
|
686
|
+
- `@returns` – return types
|
|
687
|
+
- `@throws` – error conditions
|
|
688
|
+
- `@example` – usage samples[file:13][file:16][file:20][file:25]
|
|
689
|
+
|
|
690
|
+
You can generate static docs with JSDoc if desired:
|
|
749
691
|
|
|
750
|
-
Generate HTML docs:
|
|
751
692
|
```bash
|
|
752
693
|
npm install -g jsdoc
|
|
753
694
|
jsdoc src/ -r -d docs/
|
|
@@ -755,38 +696,26 @@ jsdoc src/ -r -d docs/
|
|
|
755
696
|
|
|
756
697
|
## 🤝 Contributing
|
|
757
698
|
|
|
758
|
-
Contributions are welcome
|
|
699
|
+
Contributions are welcome:
|
|
759
700
|
|
|
760
|
-
1. Fork the repository
|
|
761
|
-
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
|
762
|
-
3. Commit changes
|
|
763
|
-
4. Push to branch
|
|
764
|
-
5. Open a Pull Request
|
|
701
|
+
1. Fork the repository.
|
|
702
|
+
2. Create a feature branch (`git checkout -b feature/AmazingFeature`).
|
|
703
|
+
3. Commit your changes.
|
|
704
|
+
4. Push to your branch.
|
|
705
|
+
5. Open a Pull Request.[file:31]
|
|
765
706
|
|
|
766
707
|
## 📄 License
|
|
767
708
|
|
|
768
|
-
This project is licensed under the BUSL
|
|
709
|
+
This project is licensed under the BUSL‑1.1 License – see the `LICENSE` file for details. [file:31]
|
|
769
710
|
|
|
770
711
|
## 💬 Support & Community
|
|
771
712
|
|
|
772
|
-
-
|
|
773
|
-
-
|
|
774
|
-
-
|
|
775
|
-
|
|
776
|
-
## 🎓 Learning Resources
|
|
777
|
-
|
|
778
|
-
- [Solidity Documentation](https://docs.soliditylang.org/)
|
|
779
|
-
- [Ethers.js Documentation](https://docs.ethers.org/)
|
|
780
|
-
- [Ethereum Development Guide](https://ethereum.org/en/developers/docs/)
|
|
781
|
-
- [Hardhat Documentation](https://hardhat.org/docs)
|
|
782
|
-
|
|
783
|
-
## 📞 Contact
|
|
784
|
-
|
|
785
|
-
- GitHub: [@yourusername](https://github.com/yourusername)
|
|
786
|
-
- Email: your.email@example.com
|
|
713
|
+
- Issues: GitHub Issues
|
|
714
|
+
- Questions: GitHub Discussions
|
|
715
|
+
- Docs: This README and `ethershell-website.html`[file:31][file:32]
|
|
787
716
|
|
|
788
717
|
---
|
|
789
718
|
|
|
790
|
-
**Made with ❤️ for Ethereum developers
|
|
719
|
+
**Made with ❤️ for Ethereum developers.**
|
|
791
720
|
|
|
792
|
-
Happy
|
|
721
|
+
Happy hacking! 🚀
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ethershell",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.6-beta.0",
|
|
5
5
|
"description": "Interactive JavaScript console for Ethereum smart contract management",
|
|
6
6
|
"author": "Alireza Kiakojouri (alirezaethdev@gmail.com)",
|
|
7
7
|
"repository": {
|
package/src/utils/builder.js
CHANGED
|
@@ -21,12 +21,12 @@ const localStorage = new LocalStorage('./ethershell');
|
|
|
21
21
|
/**
|
|
22
22
|
* Load a specific version of the Solidity compiler
|
|
23
23
|
* @param {string} version - Solidity compiler version identifier
|
|
24
|
-
* @returns {Promise
|
|
24
|
+
* @returns {Promise} Promise resolving to solc instance
|
|
25
25
|
* @throws {Error} If version loading fails
|
|
26
26
|
* @example
|
|
27
27
|
* loadSolcVersion('v0.8.20+commit.a1b79de6');
|
|
28
28
|
*/
|
|
29
|
-
export function loadSolcVersion(version){
|
|
29
|
+
export function loadSolcVersion(version) {
|
|
30
30
|
return new Promise((resolve, reject) => {
|
|
31
31
|
solc.loadRemoteVersion(version, (err, solcInstance) => {
|
|
32
32
|
if (err) reject(err);
|
|
@@ -40,12 +40,12 @@ export function loadSolcVersion(version){
|
|
|
40
40
|
* @async
|
|
41
41
|
* @param {string} version - Solidity compiler version identifier
|
|
42
42
|
* @param {Object} solcInstance - Current solc instance
|
|
43
|
-
* @returns {Promise
|
|
43
|
+
* @returns {Promise} New solc instance with the specified version
|
|
44
44
|
* @throws {Error} If version loading fails
|
|
45
45
|
* @example
|
|
46
46
|
* setVersion('v0.8.20+commit.a1b79de6', solcInstance);
|
|
47
47
|
*/
|
|
48
|
-
export async function setVersion(version, solcInstance){
|
|
48
|
+
export async function setVersion(version, solcInstance) {
|
|
49
49
|
solcInstance = await new Promise((resolve, reject) => {
|
|
50
50
|
solc.loadRemoteVersion(version, (err, solcSpecificVersion) => {
|
|
51
51
|
if (err) reject(err);
|
|
@@ -60,7 +60,7 @@ export async function setVersion(version, solcInstance){
|
|
|
60
60
|
/**
|
|
61
61
|
* Build (compile) a Solidity contract and save artifacts
|
|
62
62
|
* @param {string} fullPath - Full path to the .sol file
|
|
63
|
-
* @param {Array
|
|
63
|
+
* @param {Array} [selectedContracts=[]] - Array of contract names to compile from the file
|
|
64
64
|
* @param {string} buildPath - Output directory for compilation artifacts
|
|
65
65
|
* @returns {void}
|
|
66
66
|
* @throws {Error} If compilation fails or produces errors
|
|
@@ -72,16 +72,16 @@ export async function setVersion(version, solcInstance){
|
|
|
72
72
|
* @example
|
|
73
73
|
* build('./contracts/MyToken.sol', ['MyToken'], './build');
|
|
74
74
|
*/
|
|
75
|
-
export function build(fullPath, selectedContracts, buildPath){
|
|
76
|
-
if(!selectedContracts){
|
|
75
|
+
export function build(fullPath, selectedContracts, buildPath) {
|
|
76
|
+
if (!selectedContracts) {
|
|
77
77
|
selectedContracts = [];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const compilerConfig = getCompilerOptions();
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
// Get the directory containing the contract
|
|
83
83
|
const contractDir = path.dirname(fullPath);
|
|
84
|
-
const filename = path.basename(fullPath);
|
|
84
|
+
const filename = path.basename(fullPath); // keep extension here
|
|
85
85
|
const basename = path.basename(fullPath, '.sol');
|
|
86
86
|
|
|
87
87
|
// Build full standard JSON input, including all imports
|
|
@@ -111,7 +111,7 @@ export function build(fullPath, selectedContracts, buildPath){
|
|
|
111
111
|
input.settings.viaIR = true;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// Compile with import callback
|
|
114
|
+
// Compile with import callback (used for anything not in `sources`)
|
|
115
115
|
const output = JSON.parse(
|
|
116
116
|
solc.compile(
|
|
117
117
|
JSON.stringify(input),
|
|
@@ -119,56 +119,66 @@ export function build(fullPath, selectedContracts, buildPath){
|
|
|
119
119
|
)
|
|
120
120
|
);
|
|
121
121
|
|
|
122
|
-
if(output.errors) {
|
|
122
|
+
if (output.errors) {
|
|
123
123
|
// Filter out warnings, only throw on actual errors
|
|
124
|
-
const errors = output.errors.filter(err => err.severity === 'error');
|
|
125
|
-
if(errors.length > 0) {
|
|
124
|
+
const errors = output.errors.filter((err) => err.severity === 'error');
|
|
125
|
+
if (errors.length > 0) {
|
|
126
126
|
throw errors;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
130
|
// Generate sub-paths of build
|
|
133
|
-
const artifacts = path.join(buildPath, 'artifacts');
|
|
134
|
-
const abis = path.join(buildPath, 'abis');
|
|
135
|
-
const bytecode = path.join(buildPath, 'bytecode');
|
|
136
|
-
const metadata = path.join(buildPath, 'metadata');
|
|
137
|
-
const standardJsonDir = path.join(buildPath, 'standard-json');
|
|
138
|
-
const subPaths = [
|
|
139
|
-
artifacts,
|
|
140
|
-
abis,
|
|
141
|
-
bytecode,
|
|
142
|
-
metadata,
|
|
143
|
-
standardJsonDir
|
|
144
|
-
];
|
|
131
|
+
const artifacts = path.join(buildPath, 'artifacts');
|
|
132
|
+
const abis = path.join(buildPath, 'abis');
|
|
133
|
+
const bytecode = path.join(buildPath, 'bytecode');
|
|
134
|
+
const metadata = path.join(buildPath, 'metadata');
|
|
135
|
+
const standardJsonDir = path.join(buildPath, 'standard-json');
|
|
136
|
+
const subPaths = [artifacts, abis, bytecode, metadata, standardJsonDir];
|
|
145
137
|
|
|
146
138
|
// Ensure all sub-paths exist
|
|
147
139
|
subPaths.forEach(check);
|
|
140
|
+
|
|
148
141
|
const allContracts = Object.keys(output.contracts[`${filename}`]);
|
|
149
|
-
const contractsToSave =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
142
|
+
const contractsToSave =
|
|
143
|
+
selectedContracts.length > 0
|
|
144
|
+
? allContracts.filter((contractName) =>
|
|
145
|
+
selectedContracts.includes(contractName)
|
|
146
|
+
)
|
|
147
|
+
: allContracts;
|
|
148
|
+
|
|
154
149
|
contractsToSave.forEach((contractName) => {
|
|
155
150
|
const contractsData = output.contracts[`${filename}`][contractName];
|
|
156
|
-
|
|
151
|
+
|
|
152
|
+
// Save artifacts
|
|
157
153
|
const artifactsPath = path.join(artifacts, `${contractName}.json`);
|
|
158
154
|
fs.writeFileSync(artifactsPath, JSON.stringify(contractsData, null, 2));
|
|
159
|
-
|
|
155
|
+
|
|
156
|
+
// Save ABI
|
|
160
157
|
const abisPath = path.join(abis, `${contractName}.abi.json`);
|
|
161
158
|
fs.writeFileSync(abisPath, JSON.stringify(contractsData.abi, null, 2));
|
|
162
|
-
|
|
159
|
+
|
|
160
|
+
// Save bytecode
|
|
163
161
|
const bytecodePath = path.join(bytecode, `${contractName}.bin`);
|
|
164
|
-
fs.writeFileSync(
|
|
165
|
-
|
|
162
|
+
fs.writeFileSync(
|
|
163
|
+
bytecodePath,
|
|
164
|
+
JSON.stringify(contractsData.evm.bytecode, null, 2)
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Save metadata
|
|
166
168
|
const metadataPath = path.join(metadata, `${contractName}.metadata.json`);
|
|
167
|
-
fs.writeFileSync(
|
|
169
|
+
fs.writeFileSync(
|
|
170
|
+
metadataPath,
|
|
171
|
+
JSON.stringify(contractsData.metadata, null, 2)
|
|
172
|
+
);
|
|
173
|
+
|
|
168
174
|
// Save standard JSON input for this entry file
|
|
169
|
-
const standardJsonPath = path.join(
|
|
175
|
+
const standardJsonPath = path.join(
|
|
176
|
+
standardJsonDir,
|
|
177
|
+
`${basename}.standard-input.json`
|
|
178
|
+
);
|
|
170
179
|
fs.writeFileSync(standardJsonPath, JSON.stringify(input, null, 2));
|
|
171
|
-
|
|
180
|
+
|
|
181
|
+
// Store ABIs and bytecode in local storage
|
|
172
182
|
localStorage.setItem(`${contractName}_abi`, abisPath);
|
|
173
183
|
localStorage.setItem(`${contractName}_bytecode`, bytecodePath);
|
|
174
184
|
});
|
|
@@ -179,7 +189,7 @@ const standardJsonDir = path.join(buildPath, 'standard-json');
|
|
|
179
189
|
* @param {string} fullVersion - Full version string (e.g., "0.8.20+commit.a1b79de6.Emscripten.clang")
|
|
180
190
|
* @returns {string} Loadable version format (e.g., "v0.8.20+commit.a1b79de6")
|
|
181
191
|
* @example
|
|
182
|
-
* extractLoadableVersion("0.8.20+commit.a1b79de6.Emscripten.clang"); //
|
|
192
|
+
* extractLoadableVersion("0.8.20+commit.a1b79de6.Emscripten.clang"); // "v0.8.20+commit.a1b79de6"
|
|
183
193
|
*/
|
|
184
194
|
export function extractLoadableVersion(fullVersion) {
|
|
185
195
|
// Match version number and commit hash from full version string
|
|
@@ -191,6 +201,12 @@ export function extractLoadableVersion(fullVersion) {
|
|
|
191
201
|
}
|
|
192
202
|
|
|
193
203
|
/*=========================== HELPER =============================*/
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Resolve a filesystem path for an import.
|
|
207
|
+
* - Relative imports: resolved from `fromDir`
|
|
208
|
+
* - Package imports (starting with '@'): from project root `node_modules`
|
|
209
|
+
*/
|
|
194
210
|
function resolveImportPath(importPath, fromDir) {
|
|
195
211
|
if (importPath.startsWith('./') || importPath.startsWith('../')) {
|
|
196
212
|
return path.resolve(fromDir, importPath);
|
|
@@ -201,21 +217,57 @@ function resolveImportPath(importPath, fromDir) {
|
|
|
201
217
|
return path.resolve(process.cwd(), 'node_modules', importPath);
|
|
202
218
|
}
|
|
203
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Compute a canonical logical name for Standard JSON "sources".
|
|
222
|
+
*
|
|
223
|
+
* Rules:
|
|
224
|
+
* - If importPath starts with '@', it is anchored at node_modules, so the
|
|
225
|
+
* canonical name is exactly the import string (e.g. '@openzeppelin/...').
|
|
226
|
+
* - Otherwise, it is resolved relative to the parent canonical name.
|
|
227
|
+
*/
|
|
228
|
+
function computeCanonicalName(parentCanonical, importPath) {
|
|
229
|
+
// Always anchor @-imports at the package root
|
|
230
|
+
if (importPath.startsWith('@')) {
|
|
231
|
+
return importPath;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const parentDir = path.posix.dirname(parentCanonical || '');
|
|
235
|
+
if (!parentDir || parentDir === '.') {
|
|
236
|
+
return path.posix.normalize(importPath);
|
|
237
|
+
}
|
|
238
|
+
return path.posix.normalize(path.posix.join(parentDir, importPath));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Collect all Solidity sources reachable from an entry file and build
|
|
243
|
+
* a Standard JSON "sources" object with canonical logical names that
|
|
244
|
+
* match solc's internal resolution.
|
|
245
|
+
*
|
|
246
|
+
* @param {string} entryPath - Absolute path to the entry .sol file
|
|
247
|
+
* @param {string} logicalEntryName - Initial logical name (e.g. 'MainVestingWalletCliff.sol')
|
|
248
|
+
* @returns {Object} Standard JSON "sources" map
|
|
249
|
+
*/
|
|
204
250
|
function collectSourcesForStandardJson(entryPath, logicalEntryName) {
|
|
205
251
|
const visited = new Set();
|
|
206
252
|
const sources = {};
|
|
207
253
|
|
|
208
|
-
|
|
254
|
+
/**
|
|
255
|
+
* @param {string} absPath - Absolute filesystem path
|
|
256
|
+
* @param {string} canonicalName - Logical name used as key in `sources`
|
|
257
|
+
*/
|
|
258
|
+
function processFile(absPath, canonicalName) {
|
|
209
259
|
if (visited.has(absPath)) return;
|
|
210
260
|
visited.add(absPath);
|
|
211
261
|
|
|
212
262
|
if (!fs.existsSync(absPath)) {
|
|
213
|
-
console.warn(
|
|
263
|
+
console.warn(
|
|
264
|
+
`Warning: cannot resolve import "${canonicalName}" at path "${absPath}"`
|
|
265
|
+
);
|
|
214
266
|
return;
|
|
215
267
|
}
|
|
216
268
|
|
|
217
269
|
const content = fs.readFileSync(absPath, 'utf8');
|
|
218
|
-
sources[
|
|
270
|
+
sources[canonicalName] = { content };
|
|
219
271
|
|
|
220
272
|
const importRegex =
|
|
221
273
|
/import\s+(?:(?:["']([^"']+)["'])|(?:.*?\sfrom\s+["']([^"']+)["']))\s*;/g;
|
|
@@ -226,13 +278,16 @@ function collectSourcesForStandardJson(entryPath, logicalEntryName) {
|
|
|
226
278
|
if (!importPath) continue;
|
|
227
279
|
|
|
228
280
|
const resolved = resolveImportPath(importPath, path.dirname(absPath));
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
281
|
+
|
|
282
|
+
const childCanonicalName = computeCanonicalName(canonicalName, importPath);
|
|
283
|
+
|
|
284
|
+
processFile(resolved, childCanonicalName);
|
|
232
285
|
}
|
|
233
286
|
}
|
|
234
287
|
|
|
235
|
-
|
|
288
|
+
const entryCanonicalName = path.posix.normalize(
|
|
289
|
+
logicalEntryName || path.basename(entryPath)
|
|
290
|
+
);
|
|
291
|
+
processFile(entryPath, entryCanonicalName);
|
|
236
292
|
return sources;
|
|
237
|
-
}
|
|
238
|
-
|
|
293
|
+
}
|