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.
@@ -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
+ }
@@ -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
+ }