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
package/bin/cli.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview EtherShell - Interactive CLI for Ethereum smart contract development
|
|
5
|
+
* @description Main entry point for the EtherShell REPL environment that provides
|
|
6
|
+
* an interactive command-line interface for compiling, deploying, and managing
|
|
7
|
+
* Ethereum smart contracts and wallets.
|
|
8
|
+
* @module cli
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import repl from 'repl';
|
|
12
|
+
import util from 'util';
|
|
13
|
+
import { customEval } from '../src/utils/replHelper.js';
|
|
14
|
+
import {
|
|
15
|
+
updateCompiler,
|
|
16
|
+
currentCompiler,
|
|
17
|
+
compilerOptions,
|
|
18
|
+
getCompilerOptions,
|
|
19
|
+
compile
|
|
20
|
+
} from '../src/services/build.js';
|
|
21
|
+
import { set, get, getDefault } from '../src/services/network.js';
|
|
22
|
+
import { deleteDirectory } from '../src/services/files.js';
|
|
23
|
+
import {
|
|
24
|
+
addAccounts,
|
|
25
|
+
getAccounts,
|
|
26
|
+
createAccounts,
|
|
27
|
+
deleteAccount,
|
|
28
|
+
createHD,
|
|
29
|
+
getHDAccounts,
|
|
30
|
+
addHD,
|
|
31
|
+
getAllAccounts,
|
|
32
|
+
connectWallet,
|
|
33
|
+
getWalletInfo
|
|
34
|
+
} from '../src/services/wallet.js';
|
|
35
|
+
|
|
36
|
+
import { deploy, add } from '../src/services/addContracts.js';
|
|
37
|
+
import { getContracts } from '../src/services/contracts.js';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* REPL instance for EtherShell interactive environment
|
|
41
|
+
* @type {repl.REPLServer}
|
|
42
|
+
* @description Creates and configures the REPL server with custom evaluation
|
|
43
|
+
* and output formatting
|
|
44
|
+
*/
|
|
45
|
+
export const r = repl.start({
|
|
46
|
+
prompt: 'EtherShell> ',
|
|
47
|
+
ignoreUndefined: true,
|
|
48
|
+
eval: customEval,
|
|
49
|
+
writer: output => {
|
|
50
|
+
return util.inspect(output, { colors: true, depth: null });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Network commands
|
|
55
|
+
r.context.chain = set;
|
|
56
|
+
r.context.chain = get;
|
|
57
|
+
r.context.defaultChain = getDefault;
|
|
58
|
+
|
|
59
|
+
// Compile commands
|
|
60
|
+
r.context.compiler = currentCompiler;
|
|
61
|
+
r.context.compUpdate = updateCompiler;
|
|
62
|
+
r.context.compInfo = getCompilerOptions;
|
|
63
|
+
r.context.compOpts = compilerOptions;
|
|
64
|
+
r.context.build = compile;
|
|
65
|
+
|
|
66
|
+
// Clean build folder
|
|
67
|
+
r.context.clean = deleteDirectory;
|
|
68
|
+
|
|
69
|
+
// Set wallet
|
|
70
|
+
r.context.addWallet = addAccounts;
|
|
71
|
+
r.context.addHDWallet = addHD;
|
|
72
|
+
r.context.newWallet = createAccounts;
|
|
73
|
+
r.context.newHDWallet = createHD;
|
|
74
|
+
r.context.removeWallet = deleteAccount;
|
|
75
|
+
r.context.connectWallet = connectWallet;
|
|
76
|
+
|
|
77
|
+
// View wallets
|
|
78
|
+
r.context.wallets = getAccounts;
|
|
79
|
+
r.context.allWallets = getAllAccounts;
|
|
80
|
+
r.context.hdWallets = getHDAccounts;
|
|
81
|
+
r.context.walletInfo = getWalletInfo
|
|
82
|
+
|
|
83
|
+
// Add contract
|
|
84
|
+
r.context.deploy = deploy;
|
|
85
|
+
r.context.addContract = add;
|
|
86
|
+
|
|
87
|
+
// Contract
|
|
88
|
+
r.context.contracts = getContracts;
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ethershell",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Interactive JavaScript console for Ethereum smart contract management",
|
|
5
|
+
"author": "Alireza Kiakojouri (alirezaethdev@gmail.com)",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/AlirezaEthDev/EtherShell"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/AlirezaEthDev/EtherShell/issues"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/AlirezaEthDev/EtherShell",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"ethershell": "./bin/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node bin/cli.js",
|
|
20
|
+
"dev": "nodemon bin/cli.js",
|
|
21
|
+
"test": "echo \"No tests yet\" && exit 0",
|
|
22
|
+
"lint": "echo \"No linting configured\" && exit 0",
|
|
23
|
+
"prepublishOnly": "npm test"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"ethereum",
|
|
30
|
+
"cli",
|
|
31
|
+
"smart-contracts",
|
|
32
|
+
"solidity",
|
|
33
|
+
"blockchain"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"commander": "^12.1.0",
|
|
37
|
+
"ethers": "^6.13.0",
|
|
38
|
+
"node-localstorage": "^3.0.5",
|
|
39
|
+
"solc": "^0.8.29",
|
|
40
|
+
"vm": "^0.1.0"
|
|
41
|
+
},
|
|
42
|
+
"preferGlobal": true,
|
|
43
|
+
"files": [
|
|
44
|
+
"bin/",
|
|
45
|
+
"src/",
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"README.md"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Smart contract deployment and management
|
|
3
|
+
* @description Provides functions to deploy new smart contracts and add existing
|
|
4
|
+
* contracts to the EtherShell environment. Manages contract instances and integrates
|
|
5
|
+
* them with the REPL context.
|
|
6
|
+
* @module addContracts
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ethers } from 'ethers';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import { provider } from './network.js';
|
|
12
|
+
import { allAccounts, accounts, hdAccounts } from './wallet.js';
|
|
13
|
+
import { LocalStorage } from 'node-localstorage';
|
|
14
|
+
import { r } from '../../bin/cli.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Local storage instance for persisting contract metadata
|
|
18
|
+
* @type {LocalStorage}
|
|
19
|
+
*/
|
|
20
|
+
const localStorage = new LocalStorage('./localStorage');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Map of all deployed and added contracts
|
|
24
|
+
* @type {Map<string, ethers.Contract>}
|
|
25
|
+
*/
|
|
26
|
+
export const contracts = new Map();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Deploy a new smart contract to the blockchain
|
|
30
|
+
* @async
|
|
31
|
+
* @param {string} contractName - Name of the contract to deploy
|
|
32
|
+
* @param {Array} [args=[]] - Constructor arguments for the contract
|
|
33
|
+
* @param {number} [accIndex=0] - Index of the account to deploy from
|
|
34
|
+
* @param {string} [chain] - Optional custom chain URL
|
|
35
|
+
* @param {string} [abiLoc] - Optional custom ABI file location
|
|
36
|
+
* @param {string} [bytecodeLoc] - Optional custom bytecode file location
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
* @throws {Error} If contract name is empty, account index is out of range, or deployment fails
|
|
39
|
+
* @example
|
|
40
|
+
* deploy('MyToken', [1000000], 0);
|
|
41
|
+
*/
|
|
42
|
+
export async function deploy(contractName, args, accIndex, chain, abiLoc, bytecodeLoc) {
|
|
43
|
+
try {
|
|
44
|
+
let currentProvider;
|
|
45
|
+
let connectedChain;
|
|
46
|
+
|
|
47
|
+
if(!contractName) {
|
|
48
|
+
throw new Error('Contract name is empty');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const contractArgs = args || [];
|
|
52
|
+
|
|
53
|
+
if(accIndex > allAccounts.length - 1) {
|
|
54
|
+
throw new Error('Wallet index is out of range');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if(!accIndex) {
|
|
58
|
+
accIndex = 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if(chain) {
|
|
62
|
+
currentProvider = new ethers.JsonRpcProvider(chain);
|
|
63
|
+
} else {
|
|
64
|
+
currentProvider = provider;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let wallet = new ethers.Wallet(allAccounts[accIndex].privateKey, currentProvider);
|
|
68
|
+
connectedChain = await currentProvider.getNetwork();
|
|
69
|
+
|
|
70
|
+
const abiPath = abiLoc || localStorage.getItem(`${contractName}_abi`);
|
|
71
|
+
const bytecodePath = bytecodeLoc || localStorage.getItem(`${contractName}_bytecode`);
|
|
72
|
+
|
|
73
|
+
const abi = JSON.parse(fs.readFileSync(abiPath, 'utf8'));
|
|
74
|
+
const bytecode = JSON.parse(fs.readFileSync(bytecodePath, 'utf8'));
|
|
75
|
+
|
|
76
|
+
const factory = new ethers.ContractFactory(abi, bytecode, wallet);
|
|
77
|
+
const deployTx = await factory.deploy(...contractArgs);
|
|
78
|
+
await deployTx.waitForDeployment();
|
|
79
|
+
|
|
80
|
+
// Update deployer contract list
|
|
81
|
+
const contSpec = {
|
|
82
|
+
address: deployTx.target,
|
|
83
|
+
deployedOn: connectedChain.name,
|
|
84
|
+
chainId: connectedChain.chainId
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
allAccounts[accIndex].contracts.push(contSpec);
|
|
88
|
+
|
|
89
|
+
const accountsIndex = accounts.findIndex(wallet => wallet.index == accIndex);
|
|
90
|
+
if(accountsIndex >= 0) {
|
|
91
|
+
accounts[accountsIndex].contracts.push(contSpec);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const hdIndex = hdAccounts.findIndex(wallet => wallet.index == accIndex);
|
|
95
|
+
if(hdIndex >= 0) {
|
|
96
|
+
hdAccounts[hdIndex].contracts.push(contSpec);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Extend contract object
|
|
100
|
+
deployTx.index = Array.from(contracts.values()).length;
|
|
101
|
+
deployTx.name = contractName;
|
|
102
|
+
deployTx.chain = connectedChain.name;
|
|
103
|
+
deployTx.chainId = connectedChain.chainId;
|
|
104
|
+
deployTx.deployType = 'ethershell-deployed',
|
|
105
|
+
deployTx.provider = currentProvider;
|
|
106
|
+
|
|
107
|
+
// Add to contract list
|
|
108
|
+
contracts.set(contractName, deployTx);
|
|
109
|
+
|
|
110
|
+
// Add to REPL context
|
|
111
|
+
r.context[contractName] = deployTx;
|
|
112
|
+
|
|
113
|
+
const deployHash = deployTx.deploymentTransaction().hash;
|
|
114
|
+
const tx = await provider.getTransaction(deployHash);
|
|
115
|
+
delete tx.data;
|
|
116
|
+
|
|
117
|
+
// Extend transaction object
|
|
118
|
+
tx.ethershellIndex = deployTx.index;
|
|
119
|
+
tx.address = deployTx.target;
|
|
120
|
+
tx.name = deployTx.name;
|
|
121
|
+
tx.chain = deployTx.chain;
|
|
122
|
+
tx.deployType = deployTx.deployType;
|
|
123
|
+
|
|
124
|
+
console.log(tx);
|
|
125
|
+
} catch(err) {
|
|
126
|
+
console.error(err);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Add an existing deployed contract to EtherShell
|
|
132
|
+
* @async
|
|
133
|
+
* @param {string} contractName - Name to assign to the contract
|
|
134
|
+
* @param {string} contractAddr - Address of the deployed contract
|
|
135
|
+
* @param {number} [accIndex=0] - Index of the account to interact with the contract
|
|
136
|
+
* @param {string} abiLoc - Path to the contract ABI file
|
|
137
|
+
* @param {string} [chain] - Optional custom chain URL
|
|
138
|
+
* @returns {Promise<void>}
|
|
139
|
+
* @throws {Error} If contract address or ABI location is null/undefined
|
|
140
|
+
* @example
|
|
141
|
+
* add('USDT', '0xdac17f958d2ee523a2206206994597c13d831ec7', 0, './abis/USDT.json');
|
|
142
|
+
*/
|
|
143
|
+
export async function add(contractName, contractAddr, accIndex, abiLoc, chain) {
|
|
144
|
+
try {
|
|
145
|
+
let currentProvider;
|
|
146
|
+
let connectedChain;
|
|
147
|
+
|
|
148
|
+
if(!contractAddr) {
|
|
149
|
+
throw new Error('Contract address may not be null or undefined!');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if(!accIndex) {
|
|
153
|
+
accIndex = 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if(chain) {
|
|
157
|
+
currentProvider = new ethers.JsonRpcProvider(chain);
|
|
158
|
+
} else {
|
|
159
|
+
currentProvider = provider;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let wallet = new ethers.Wallet(allAccounts[accIndex].privateKey, currentProvider);
|
|
163
|
+
connectedChain = await currentProvider.getNetwork();
|
|
164
|
+
|
|
165
|
+
if(!abiLoc) {
|
|
166
|
+
throw new Error('ABI path may not be null or undefined!');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const abi = JSON.parse(fs.readFileSync(abiLoc, 'utf8'));
|
|
170
|
+
|
|
171
|
+
const newContract = new ethers.Contract(contractAddr, abi, wallet);
|
|
172
|
+
|
|
173
|
+
// Update deployer contract list
|
|
174
|
+
const contSpec = {
|
|
175
|
+
address: newContract.target,
|
|
176
|
+
deployedOn: connectedChain.name,
|
|
177
|
+
chainId: connectedChain.chainId
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
allAccounts[accIndex].contracts.push(contSpec);
|
|
181
|
+
|
|
182
|
+
const accountsIndex = accounts.findIndex(wallet => wallet.index == accIndex);
|
|
183
|
+
if(accountsIndex >= 0) {
|
|
184
|
+
accounts[accountsIndex].contracts.push(contSpec);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const hdIndex = hdAccounts.findIndex(wallet => wallet.index == accIndex);
|
|
188
|
+
if(hdIndex >= 0) {
|
|
189
|
+
hdAccounts[hdIndex].contracts.push(contSpec);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Extend contract object
|
|
193
|
+
newContract.index = Array.from(contracts.values()).length;
|
|
194
|
+
newContract.name = contractName;
|
|
195
|
+
newContract.chain = connectedChain.name;
|
|
196
|
+
newContract.chainId = connectedChain.chainId;
|
|
197
|
+
newContract.deployType = 'pre-deployed',
|
|
198
|
+
newContract.provider = currentProvider;
|
|
199
|
+
|
|
200
|
+
// Add to contract list
|
|
201
|
+
contracts.set(contractName, newContract);
|
|
202
|
+
|
|
203
|
+
// Add to REPL context
|
|
204
|
+
r.context[contractName] = newContract;
|
|
205
|
+
|
|
206
|
+
// Add result
|
|
207
|
+
const result = {
|
|
208
|
+
index: newContract.index,
|
|
209
|
+
name: newContract.name,
|
|
210
|
+
address: newContract.target,
|
|
211
|
+
chain: newContract.chain,
|
|
212
|
+
chainId: newContract.chainId,
|
|
213
|
+
deployType: newContract.deployType,
|
|
214
|
+
provider: newContract.provider
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log(result);
|
|
218
|
+
} catch(err) {
|
|
219
|
+
console.error(err);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Solidity compiler management and contract compilation
|
|
3
|
+
* @description Manages Solidity compiler versions, compilation settings, and
|
|
4
|
+
* provides functions to compile smart contracts with customizable options.
|
|
5
|
+
* @module build
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import solc from 'solc';
|
|
10
|
+
import { check, collectSolFiles } from '../utils/dir.js';
|
|
11
|
+
import { setVersion, build } from '../utils/builder.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Current Solidity compiler instance
|
|
15
|
+
* @type {Object}
|
|
16
|
+
*/
|
|
17
|
+
let currentSolcInstance = solc; // default local compiler
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Global compiler configuration state
|
|
21
|
+
* @type {Object}
|
|
22
|
+
* @property {boolean} optimizer - Whether gas optimizer is enabled
|
|
23
|
+
* @property {number} optimizerRuns - Number of optimizer runs
|
|
24
|
+
* @property {boolean} viaIR - Whether to use IR-based code generation
|
|
25
|
+
*/
|
|
26
|
+
let compilerConfig = {
|
|
27
|
+
optimizer: false,
|
|
28
|
+
optimizerRuns: 200,
|
|
29
|
+
viaIR: false
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Update the Solidity compiler to a specific version
|
|
34
|
+
* @async
|
|
35
|
+
* @param {string} version - Solidity version (e.g., 'v0.8.20+commit.a1b79de6')
|
|
36
|
+
* @returns {Promise<void>}
|
|
37
|
+
* @throws {Error} If the specified version cannot be loaded
|
|
38
|
+
* @example
|
|
39
|
+
* await updateCompiler('v0.8.20+commit.a1b79de6');
|
|
40
|
+
*/
|
|
41
|
+
export async function updateCompiler(version){
|
|
42
|
+
try{
|
|
43
|
+
currentSolcInstance = await setVersion(version, currentSolcInstance);
|
|
44
|
+
} catch(err) {
|
|
45
|
+
console.error(err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the current compiler version
|
|
51
|
+
* @returns {string} Current Solidity compiler version string
|
|
52
|
+
* @example
|
|
53
|
+
* const version = currentCompiler(); // Returns: "0.8.20+commit.a1b79de6.Emscripten.clang"
|
|
54
|
+
*/
|
|
55
|
+
export function currentCompiler(){
|
|
56
|
+
return currentSolcInstance.version();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Configure compiler optimization options
|
|
61
|
+
* @param {boolean} gasOptimizer - Enable or disable gas optimization
|
|
62
|
+
* @param {boolean} viaIR - Enable or disable IR-based code generation
|
|
63
|
+
* @param {number} [optimizerRuns=200] - Number of times the optimizer should run
|
|
64
|
+
* @returns {Object|null} Updated compiler configuration or null on error
|
|
65
|
+
* @throws {Error} If parameters are invalid
|
|
66
|
+
* @example
|
|
67
|
+
* compilerOptions(true, false, 1000);
|
|
68
|
+
*/
|
|
69
|
+
export function compilerOptions(gasOptimizer, viaIR, optimizerRuns = 200) {
|
|
70
|
+
try {
|
|
71
|
+
// Validate input parameters
|
|
72
|
+
if (typeof gasOptimizer !== 'boolean') {
|
|
73
|
+
throw new Error('Gas optimizer parameter must be a boolean');
|
|
74
|
+
}
|
|
75
|
+
if (typeof viaIR !== 'boolean') {
|
|
76
|
+
throw new Error('ViaIR parameter must be a boolean');
|
|
77
|
+
}
|
|
78
|
+
if (typeof optimizerRuns !== 'number' || optimizerRuns < 1) {
|
|
79
|
+
throw new Error('Optimizer runs must be a positive number');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Update global configuration
|
|
83
|
+
compilerConfig.optimizer = gasOptimizer;
|
|
84
|
+
compilerConfig.viaIR = viaIR;
|
|
85
|
+
compilerConfig.optimizerRuns = optimizerRuns;
|
|
86
|
+
|
|
87
|
+
// Provide user feedback
|
|
88
|
+
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}`);
|
|
92
|
+
}
|
|
93
|
+
console.log(` ViaIR: ${compilerConfig.viaIR ? 'Enabled' : 'Disabled'}`);
|
|
94
|
+
|
|
95
|
+
return compilerConfig;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Error setting compiler options:', error.message);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get current compiler options
|
|
104
|
+
* @returns {Object} Copy of current compiler configuration
|
|
105
|
+
* @example
|
|
106
|
+
* const opts = getCompilerOptions();
|
|
107
|
+
*/
|
|
108
|
+
export function getCompilerOptions() {
|
|
109
|
+
return { ...compilerConfig };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Compile Solidity smart contract(s)
|
|
114
|
+
* @param {string} [fullPath] - Path to contract file or directory. Defaults to './contracts'
|
|
115
|
+
* @param {Array<string>} [selectedContracts] - Array of specific contract names to compile
|
|
116
|
+
* @param {string} [buildPath] - Output directory for compiled artifacts. Defaults to './build'
|
|
117
|
+
* @returns {void}
|
|
118
|
+
* @throws {Error} If no contracts found or compilation fails
|
|
119
|
+
* @example
|
|
120
|
+
* compile(); // Compile all contracts in './contracts'
|
|
121
|
+
* compile('./contracts/MyToken.sol'); // Compile specific file
|
|
122
|
+
* compile('./contracts', ['MyToken'], './output'); // Compile specific contracts to custom path
|
|
123
|
+
*/
|
|
124
|
+
export function compile(fullPath, selectedContracts, buildPath){
|
|
125
|
+
try{
|
|
126
|
+
// Set default path if buildPath is undefined
|
|
127
|
+
if(!buildPath){
|
|
128
|
+
buildPath = path.resolve('.', 'build');
|
|
129
|
+
[buildPath].forEach(check);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let fileExt;
|
|
133
|
+
if(!fullPath) {
|
|
134
|
+
fullPath = path.resolve('.', 'contracts');
|
|
135
|
+
} else {
|
|
136
|
+
fileExt = path.extname(fullPath);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if(!fileExt){
|
|
140
|
+
const solFiles = collectSolFiles(fullPath);
|
|
141
|
+
|
|
142
|
+
if(!solFiles.length){
|
|
143
|
+
throw 'There is no smart contract in the directory!';
|
|
144
|
+
} else {
|
|
145
|
+
for(let i = 0; i < solFiles.length; i++){
|
|
146
|
+
build(solFiles[i], selectedContracts, buildPath);
|
|
147
|
+
}
|
|
148
|
+
console.log(`Contracts compiled into ${path.resolve(buildPath)}`);
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
build(fullPath, selectedContracts, buildPath);
|
|
152
|
+
console.log(`Contract compiled into ${path.resolve(buildPath)}`);
|
|
153
|
+
}
|
|
154
|
+
} catch(err){
|
|
155
|
+
console.error(err);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Contract retrieval and management
|
|
3
|
+
* @description Provides functions to retrieve contract information from the
|
|
4
|
+
* contract registry by various identifiers (index, address, or name).
|
|
5
|
+
* @module contracts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ethers } from 'ethers';
|
|
9
|
+
import { getContArr } from '../utils/contractLister.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get contract(s) information
|
|
13
|
+
* @async
|
|
14
|
+
* @param {number|string|null} [contPointer] - Contract identifier (index, address, or name).
|
|
15
|
+
* If omitted, returns all contracts.
|
|
16
|
+
* @returns {Promise<void>}
|
|
17
|
+
* @throws {Error} If the input is not valid
|
|
18
|
+
* @example
|
|
19
|
+
* await getContracts(); // Get all contracts
|
|
20
|
+
* await getContracts(0); // Get contract by index
|
|
21
|
+
* await getContracts('0x1234...'); // Get contract by address
|
|
22
|
+
* await getContracts('MyToken'); // Get contract by name
|
|
23
|
+
*/
|
|
24
|
+
export async function getContracts(contPointer) {
|
|
25
|
+
const contArray = await getContArr();
|
|
26
|
+
let result;
|
|
27
|
+
|
|
28
|
+
if(!contPointer && contPointer != 0) {
|
|
29
|
+
result = contArray;
|
|
30
|
+
} else if(typeof contPointer === 'number') {
|
|
31
|
+
result = contArray[contPointer];
|
|
32
|
+
} else if(ethers.isAddress(contPointer)) {
|
|
33
|
+
const index = contArray.findIndex(contract => contract.address == contPointer);
|
|
34
|
+
result = contArray[index];
|
|
35
|
+
} else if(typeof contPointer === 'string') {
|
|
36
|
+
const index = contArray.findIndex(contract => contract.name == contPointer);
|
|
37
|
+
result = contArray[index];
|
|
38
|
+
} else {
|
|
39
|
+
throw new Error('Input is NOT valid!');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log(result);
|
|
43
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview File system utilities for directory management
|
|
3
|
+
* @description Provides utilities for deleting directories recursively,
|
|
4
|
+
* primarily used for cleaning build directories.
|
|
5
|
+
* @module files
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Delete a directory recursively
|
|
13
|
+
* @param {string} [dirPath] - Path to the directory to delete. Defaults to './build'
|
|
14
|
+
* @returns {void}
|
|
15
|
+
* @example
|
|
16
|
+
* deleteDirectory('../build');
|
|
17
|
+
* deleteDirectory(); // Deletes default './build' directory
|
|
18
|
+
*/
|
|
19
|
+
export function deleteDirectory(dirPath){
|
|
20
|
+
try {
|
|
21
|
+
if(!dirPath){
|
|
22
|
+
dirPath = path.resolve('..', 'build');
|
|
23
|
+
}
|
|
24
|
+
// Check if the directory exists
|
|
25
|
+
if(!fs.existsSync(dirPath)){
|
|
26
|
+
console.log('Path is not a directory');
|
|
27
|
+
return; }
|
|
28
|
+
|
|
29
|
+
// For Node.js 14.14.0+ (recommended)
|
|
30
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
31
|
+
|
|
32
|
+
console.log('Directory deleted successfully');
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (err.code === 'ENOENT') {
|
|
35
|
+
console.log('Directory does not exist');
|
|
36
|
+
} else {
|
|
37
|
+
console.error('Error deleting directory:', err);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|