ethershell 0.1.0-alpha.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/LICENSE +55 -0
- package/README.md +647 -0
- package/bin/cli.js +88 -0
- package/package.json +49 -0
- package/src/services/addContracts.js +221 -0
- package/src/services/build.js +157 -0
- package/src/services/contracts.js +43 -0
- package/src/services/files.js +40 -0
- package/src/services/network.js +113 -0
- package/src/services/wallet.js +375 -0
- package/src/utils/accounter.js +213 -0
- package/src/utils/builder.js +169 -0
- package/src/utils/contractLister.js +37 -0
- package/src/utils/dir.js +81 -0
- package/src/utils/replHelper.js +45 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Solidity compiler builder utilities
|
|
3
|
+
* @description Handles Solidity compiler version loading, contract compilation,
|
|
4
|
+
* and artifact generation including ABIs, bytecode, and metadata.
|
|
5
|
+
* @module builder
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import solc from 'solc';
|
|
11
|
+
import { check, findImports } from './dir.js';
|
|
12
|
+
import { getCompilerOptions } from '../services/build.js';
|
|
13
|
+
import { LocalStorage } from 'node-localstorage';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Local storage instance for persisting compiler artifacts paths
|
|
17
|
+
* @type {LocalStorage}
|
|
18
|
+
*/
|
|
19
|
+
const localStorage = new LocalStorage('./localStorage');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load a specific version of the Solidity compiler
|
|
23
|
+
* @param {string} version - Solidity compiler version identifier
|
|
24
|
+
* @returns {Promise<Object>} Promise resolving to solc instance
|
|
25
|
+
* @throws {Error} If version loading fails
|
|
26
|
+
* @example
|
|
27
|
+
* loadSolcVersion('v0.8.20+commit.a1b79de6');
|
|
28
|
+
*/
|
|
29
|
+
export function loadSolcVersion(version){
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
solc.loadRemoteVersion(version, (err, solcInstance) => {
|
|
32
|
+
if (err) reject(err);
|
|
33
|
+
else resolve(solcInstance);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Set and load a specific Solidity compiler version
|
|
40
|
+
* @async
|
|
41
|
+
* @param {string} version - Solidity compiler version identifier
|
|
42
|
+
* @param {Object} solcInstance - Current solc instance
|
|
43
|
+
* @returns {Promise<Object>} New solc instance with the specified version
|
|
44
|
+
* @throws {Error} If version loading fails
|
|
45
|
+
* @example
|
|
46
|
+
* setVersion('v0.8.20+commit.a1b79de6', solcInstance);
|
|
47
|
+
*/
|
|
48
|
+
export async function setVersion(version, solcInstance){
|
|
49
|
+
solcInstance = await new Promise((resolve, reject) => {
|
|
50
|
+
solc.loadRemoteVersion(version, (err, solcSpecificVersion) => {
|
|
51
|
+
if (err) reject(err);
|
|
52
|
+
else resolve(solcSpecificVersion);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
const newVersion = solcInstance.version();
|
|
56
|
+
console.log('Loaded solc version:', newVersion);
|
|
57
|
+
return solcInstance;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build (compile) a Solidity contract and save artifacts
|
|
62
|
+
* @param {string} fullPath - Full path to the .sol file
|
|
63
|
+
* @param {Array<string>} [selectedContracts=[]] - Array of contract names to compile from the file
|
|
64
|
+
* @param {string} buildPath - Output directory for compilation artifacts
|
|
65
|
+
* @returns {void}
|
|
66
|
+
* @throws {Error} If compilation fails or produces errors
|
|
67
|
+
* @description Compiles Solidity contracts and saves artifacts in organized subdirectories:
|
|
68
|
+
* - artifacts/: Complete contract data
|
|
69
|
+
* - abis/: Contract ABIs
|
|
70
|
+
* - bytecode/: Contract bytecode
|
|
71
|
+
* - metadata/: Contract metadata
|
|
72
|
+
* @example
|
|
73
|
+
* build('./contracts/MyToken.sol', ['MyToken'], './build');
|
|
74
|
+
*/
|
|
75
|
+
export function build(fullPath, selectedContracts, buildPath){
|
|
76
|
+
if(!selectedContracts){
|
|
77
|
+
selectedContracts = [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const compilerConfig = getCompilerOptions();
|
|
81
|
+
|
|
82
|
+
// Get the directory containing the contract
|
|
83
|
+
const contractDir = path.dirname(fullPath);
|
|
84
|
+
const filename = path.basename(fullPath, '.sol');
|
|
85
|
+
const source = fs.readFileSync(fullPath, 'utf8');
|
|
86
|
+
|
|
87
|
+
const input = {
|
|
88
|
+
language: 'Solidity',
|
|
89
|
+
sources: {
|
|
90
|
+
[`${filename}`]: {
|
|
91
|
+
content: source,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
settings: {
|
|
95
|
+
outputSelection: {
|
|
96
|
+
'*': {
|
|
97
|
+
'*': ['*'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Apply global compiler configuration
|
|
104
|
+
if (compilerConfig.optimizer) {
|
|
105
|
+
input.settings.optimizer = {
|
|
106
|
+
enabled: true,
|
|
107
|
+
runs: compilerConfig.optimizerRuns
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (compilerConfig.viaIR) {
|
|
111
|
+
input.settings.viaIR = true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Compile with import callback
|
|
115
|
+
const output = JSON.parse(
|
|
116
|
+
solc.compile(
|
|
117
|
+
JSON.stringify(input),
|
|
118
|
+
{ import: (importPath) => findImports(importPath, contractDir) }
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if(output.errors) {
|
|
123
|
+
// Filter out warnings, only throw on actual errors
|
|
124
|
+
const errors = output.errors.filter(err => err.severity === 'error');
|
|
125
|
+
if(errors.length > 0) {
|
|
126
|
+
throw errors;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Generate sub-paths of build
|
|
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 subPaths = [
|
|
136
|
+
artifacts,
|
|
137
|
+
abis,
|
|
138
|
+
bytecode,
|
|
139
|
+
metadata
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
// Ensure all sub-paths exist
|
|
143
|
+
subPaths.forEach(check);
|
|
144
|
+
const allContracts = Object.keys(output.contracts[`${filename}`]);
|
|
145
|
+
const contractsToSave = selectedContracts.length > 0 ? allContracts.filter(
|
|
146
|
+
(contractName) => {
|
|
147
|
+
return selectedContracts.includes(contractName);
|
|
148
|
+
}
|
|
149
|
+
): allContracts;
|
|
150
|
+
contractsToSave.forEach((contractName) => {
|
|
151
|
+
const contractsData = output.contracts[`${filename}`][contractName];
|
|
152
|
+
// Save on artifacts
|
|
153
|
+
const artifactsPath = path.join(artifacts, `${contractName}.json`);
|
|
154
|
+
fs.writeFileSync(artifactsPath, JSON.stringify(contractsData, null, 2));
|
|
155
|
+
// Save on abis
|
|
156
|
+
const abisPath = path.join(abis, `${contractName}.abi.json`);
|
|
157
|
+
fs.writeFileSync(abisPath, JSON.stringify(contractsData.abi, null, 2));
|
|
158
|
+
// Save on bytecode
|
|
159
|
+
const bytecodePath = path.join(bytecode, `${contractName}.bin`);
|
|
160
|
+
fs.writeFileSync(bytecodePath, JSON.stringify(contractsData.evm.bytecode, null, 2));
|
|
161
|
+
// Save on metadata
|
|
162
|
+
const metadataPath = path.join(metadata, `${contractName}.metadata.json`);
|
|
163
|
+
fs.writeFileSync(metadataPath, JSON.stringify(contractsData.metadata, null, 2));
|
|
164
|
+
// Store abis and bytecode on local storage
|
|
165
|
+
localStorage.setItem(`${contractName}_abi`, abisPath);
|
|
166
|
+
localStorage.setItem(`${contractName}_bytecode`, bytecodePath);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Contract listing utilities
|
|
3
|
+
* @description Provides utilities to retrieve and format contract information
|
|
4
|
+
* from the contracts map, including balance information.
|
|
5
|
+
* @module contractLister
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { contracts } from '../services/addContracts.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get array of all contracts with their information
|
|
12
|
+
* @async
|
|
13
|
+
* @returns {Promise<Array<Object>>} Array of contract information objects
|
|
14
|
+
* @description Retrieves all contracts from the contracts map and formats them
|
|
15
|
+
* into a simplified array with essential information including current balance
|
|
16
|
+
* @example
|
|
17
|
+
* const contractList = await getContArr();
|
|
18
|
+
* // Returns: [{ index: 0, name: 'MyToken', address: '0x...', chain: 'sepolia', ... }]
|
|
19
|
+
*/
|
|
20
|
+
export async function getContArr() {
|
|
21
|
+
let contractsArray = [];
|
|
22
|
+
|
|
23
|
+
for (const x of contracts.values()) {
|
|
24
|
+
let contract = {
|
|
25
|
+
index: x.index,
|
|
26
|
+
name: x.name,
|
|
27
|
+
address: x.target,
|
|
28
|
+
chain: x.chain,
|
|
29
|
+
chainId: x.chainId,
|
|
30
|
+
deployType: x.deployType,
|
|
31
|
+
balance: await x.provider.getBalance(x.target)
|
|
32
|
+
}
|
|
33
|
+
contractsArray.push(contract);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return contractsArray;
|
|
37
|
+
}
|
package/src/utils/dir.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Directory and file utilities for Solidity projects
|
|
3
|
+
* @description Provides utilities for directory management, collecting Solidity files,
|
|
4
|
+
* and resolving contract imports during compilation.
|
|
5
|
+
* @module dir
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if directory exists and create it if it doesn't
|
|
13
|
+
* @param {string} dir - Directory path to check/create
|
|
14
|
+
* @returns {void}
|
|
15
|
+
* @example
|
|
16
|
+
* check('./build/artifacts');
|
|
17
|
+
*/
|
|
18
|
+
export function check(dir){
|
|
19
|
+
if(!fs.existsSync(dir)){
|
|
20
|
+
fs.mkdirSync(dir, {recursive: true});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Recursively collect all Solidity files from a directory
|
|
26
|
+
* @param {string} dirPath - Root directory to search for .sol files
|
|
27
|
+
* @returns {Array<string>} Array of absolute paths to .sol files
|
|
28
|
+
* @example
|
|
29
|
+
* const solFiles = collectSolFiles('./contracts');
|
|
30
|
+
* // Returns: ['./contracts/Token.sol', './contracts/utils/SafeMath.sol']
|
|
31
|
+
*/
|
|
32
|
+
export function collectSolFiles(dirPath) {
|
|
33
|
+
let solFiles = [];
|
|
34
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
35
|
+
|
|
36
|
+
for(const entry of entries) {
|
|
37
|
+
const entryPath = path.join(dirPath, entry.name);
|
|
38
|
+
|
|
39
|
+
if(entry.isDirectory()) {
|
|
40
|
+
// Recursively collect from subdirectories
|
|
41
|
+
solFiles = solFiles.concat(collectSolFiles(entryPath));
|
|
42
|
+
} else if(entry.isFile() && path.extname(entry.name) === '.sol') {
|
|
43
|
+
solFiles.push(entryPath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return solFiles;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve Solidity import paths for the compiler
|
|
51
|
+
* @param {string} importPath - Import path from Solidity import statement
|
|
52
|
+
* @param {string} basePath - Base directory of the importing file
|
|
53
|
+
* @returns {Object} Object with 'contents' property containing file content or 'error' property
|
|
54
|
+
* @example
|
|
55
|
+
* const result = findImports('./Token.sol', './contracts');
|
|
56
|
+
* // Returns: { contents: "pragma solidity ^0.8.0;..." } or { error: "File not found" }
|
|
57
|
+
*/
|
|
58
|
+
export function findImports(importPath, basePath) {
|
|
59
|
+
try {
|
|
60
|
+
// Resolve relative to the current file's directory
|
|
61
|
+
const resolvedPath = path.resolve(basePath, importPath);
|
|
62
|
+
|
|
63
|
+
if (fs.existsSync(resolvedPath)) {
|
|
64
|
+
const content = fs.readFileSync(resolvedPath, 'utf8');
|
|
65
|
+
return { contents: content };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// If not found, try relative to contracts root
|
|
69
|
+
const contractsRoot = path.resolve('.', 'contracts');
|
|
70
|
+
const rootPath = path.resolve(contractsRoot, importPath);
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(rootPath)) {
|
|
73
|
+
const content = fs.readFileSync(rootPath, 'utf8');
|
|
74
|
+
return { contents: content };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { error: 'File not found' };
|
|
78
|
+
} catch (e) {
|
|
79
|
+
return { error: e.message };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Custom REPL evaluation utilities
|
|
3
|
+
* @description Provides custom async evaluation function for the Node.js REPL,
|
|
4
|
+
* enabling await syntax in the interactive shell without async wrapper functions.
|
|
5
|
+
* @module replHelper
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import vm from 'vm';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Custom async evaluation function for REPL
|
|
12
|
+
* @async
|
|
13
|
+
* @param {string} cmd - Command to evaluate
|
|
14
|
+
* @param {Object} context - REPL context object
|
|
15
|
+
* @param {string} filename - Filename for error reporting
|
|
16
|
+
* @param {Function} callback - Callback function(err, result)
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
* @description Evaluates commands in REPL context and automatically awaits Promise results
|
|
19
|
+
* @example
|
|
20
|
+
* // Used internally by REPL to enable top-level await
|
|
21
|
+
* customEval('await provider.getBlockNumber()', context, 'repl', callback);
|
|
22
|
+
*/
|
|
23
|
+
export async function customEval(cmd, context, filename, callback) {
|
|
24
|
+
try {
|
|
25
|
+
// Use vm to evaluate the command
|
|
26
|
+
const script = new vm.Script(cmd, {
|
|
27
|
+
filename: filename,
|
|
28
|
+
displayErrors: false
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let result = script.runInContext(context, {
|
|
32
|
+
displayErrors: false,
|
|
33
|
+
breakOnSigint: true
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// If result is a Promise, await it
|
|
37
|
+
if (result && typeof result.then === 'function') {
|
|
38
|
+
result = await result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
callback(null, result);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
callback(err);
|
|
44
|
+
}
|
|
45
|
+
}
|