An interactive Node.js console for Ethereum smart contract development. Write, compile, deploy, and manage smart contracts directly from the shell with an intuitive, Solidity-focused developer experience.
Perfect for:
- Smart contract developers who want a fast, interactive workflow
- Rapid prototyping and testing of Solidity contracts
- Learning Ethereum development
- DeFi protocol development and testing
- Quick contract interactions and deployments
- Interactive REPL Shell β Custom async evaluation with topβlevel
awaitsupport in a Node.js REPL. - Solidity Compilation β Compile contracts with configurable optimization and
viaIRoptions. - Smart Contract Deployment β Deploy contracts to any EVM network using ethers v6.
- Contract Restoration β Recreate previously deployed or added contract proxies in a new session with
restoreContract(). - Wallet Management β Create, import, and manage wallets (regular & HD wallets, plus nodeβmanaged accounts).
- MultiβNetwork Support β Switch between blockchain networks with persistent provider configuration.
- Contract Proxy Wrapper β Enhanced contract interaction with a proxy that supports
from,value, gas options, EIPβ1559 fields, and custom data. - Contract Interactions β Call contract methods (read & write) with advanced options directly from the shell.
- ABI & Bytecode Generation β Organized artifacts (
artifacts,abis,bytecode,metadata) plus an aggregated ABI file. - Contract Flattening β Merge a Solidity entry file and its local or
node_modulesimports into one verifiable.solfile. - Node Signer Integration β Connect to nodeβmanaged accounts (Hardhat, Anvil, etc.).
- TypeScript Code Generation β Autoβgenerate TypeScript types from ABIs into a
types/directory under the build path. - Gas Optimization Controls β Configure optimizer enable flag, runs, and
viaIRglobally. - Persistent Configuration β Store provider, compiler config, wallets, and contracts in the
./ethershelldirectory. - Comprehensive JSDoc β Rich JSDoc comments for IDE autocompletion and type hints throughout the codebase.
# Install globally:
npm i -g ethershell
# Start EtherShell in the root directory of your project:
ethershell
# or
npx ethershellRun the CLI in your project root so EtherShell can find ./contracts and write ./ethershell and ./build data next to your project files.
# Start the console in the root directory of your project:
ethershell
# You should see:
# EtherShell>First, connect to a blockchain network.
// View current network
EtherShell> chainInfo()
{ URL: 'http://127.0.0.1:8545', name: 'unknown', chainId: 1337n }
// Switch to a different network
EtherShell> chain('https://sepolia.infura.io/v3/YOUR-PROJECT-ID')
{ URL: 'https://sepolia.infura.io/v3/...', name: 'sepolia', chainId: 11155111n }
// Get default network info
EtherShell> defaultChain()
{ URL: 'http://127.0.0.1:8545' }Internally, chain() updates the inβmemory provider and saves the selected endpoint to ./ethershell/config.json so it persists between sessions.
// Create a single random wallet
EtherShell> newWallet()
!WARNING!
The generated accounts are NOT safe. Do NOT use them on main net!
[
{
index: 0,
address: '0x1234...5678',
privateKey: '0xabcd...ef01',
type: 'user-generated',
contracts: []
}
]
// Create 5 new wallets at once
EtherShell> newWallet(5)
!WARNING!
The generated accounts are NOT safe. Do NOT use them on main net!
[
{ index: 0, address: '0x...', ... },
{ index: 1, address: '0x...', ... },
// ... 5 wallets total
]newWallet() persists wallets into ./ethershell/wallets.json.
// Import a single private key
EtherShell> addWallet('0xYourPrivateKeyHere')
// Import multiple private keys
EtherShell> addWallet([
'0xPrivateKey1...',
'0xPrivateKey2...',
'0xPrivateKey3...'
])EtherShell detects duplicate wallets by private key and throws if you attempt to reβadd an existing key.
// Create new HD wallet (10 derived accounts)
EtherShell> newHDWallet()
!WARNING!
The generated accounts are NOT safe. Do NOT use them on main net!
[
{ index: 0, address: '0x...', phrase: '...', path: "m/0", ... },
{ index: 1, address: '0x...', phrase: '...', path: "m/1", ... },
// ... 10 accounts
]
// Create 5 derived accounts from new random mnemonic
EtherShell> newHDWallet(5)
// Import HD wallet from known mnemonic
EtherShell> addHDWallet('word word word ... (12 or 24 words)', 10)
// View all HD wallets
EtherShell> hdWallets()
!WARNING!
The generated accounts are NOT safe. Do NOT use them on main net!
[ ... ]// For Hardhat, Anvil, or other nodes with unlocked accounts
EtherShell> connectWallet()
// This adds accounts managed by the node (e.g., Hardhat default accounts)// View regular (imported/generated) accounts
EtherShell> wallets()
// View all accounts (regular + HD + node-managed)
EtherShell> allWallets()
// View wallet details with balance and nonce
EtherShell> walletInfo(0)
// or by address
EtherShell> walletInfo('0x1234...5678')
// or multiple
EtherShell> walletInfo()[1][2]
// Change default account
EtherShell> changeDefWallet(0)
// or by address
EtherShell> changeDefWallet('0x1234...5678')
// or import and set as default in one command
EtherShell> changeDefWallet('0xPrivateKeyHere')// Delete by index
EtherShell> removeWallet(0)
// Delete by address
EtherShell> removeWallet('0x1234...5678')
// Delete by mnemonic (all derived accounts from this phrase)
EtherShell> removeWallet('word word word ...')
// Delete multiple by indices
EtherShell> removeWallet()[2][3]
// Delete all wallets
EtherShell> removeWallet()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.
// View current compiler version (bundled solc if no remote has been loaded)
EtherShell> compiler()
"0.8.xx+commit....Emscripten.clang"
// Switch to a different Solidity version
EtherShell> compUpdate('v0.8.29+commit.ab55807c')
Loaded solc version: 0.8.29+commit.ab55807c.Emscripten.clang
// Configure compilation options (gasOptimizer, viaIR, optimizerRuns)
EtherShell> compOpts(true, false, 1000)
β Compiler options updated:
Gas Optimizer: Enabled
ViaIR: Disabled
Optimizer Runs: 1000
// Get current compiler options
EtherShell> compInfo()
{
version: 'v0.8.29+commit.ab55807c',
optimizer: true,
viaIR: false,
optimizerRuns: 1000,
compilePath: './build'
}
// Get current config info
EtherShell> configInfo()
{
providerEndpoint: '...',
defaultWallet: { ... },
compiler: {
version: 'v0.8.29+commit.ab55807c',
optimizer: false,
viaIR: false,
optimizerRuns: 200,
compilePath: './build'
}
}
// Get default wallet
EtherShell> defWallet()
{ address: '0x...', ... }
// Change build output path
EtherShell> compPath('./custom-build')Compiler configuration is kept in memory and mirrored into config.json, including version, optimizer flags, runs, and the default compilePath.
// Compile all .sol files in ./contracts directory
EtherShell> build()
Contracts compiled into /path/to/build
Aggregated ABI generated at /path/to/build/aggregated.abi.json
TypeScript types generated in /path/to/build/types
// Compile a specific contract file
EtherShell> build('./contracts/MyToken.sol')
Contract compiled into /path/to/build
// Compile specific contracts from a file to a custom build directory
EtherShell> build('./contracts/MyToken.sol', ['MyToken', 'OtherContract'], './custom-build')
Contracts compiled into /path/to/custom-build
// Clean build directory
EtherShell> clean()
Directory deleted successfullyflatten() combines a root Solidity file with its imported dependencies into a single file. It supports relative imports and package imports from node_modules, such as OpenZeppelin contracts.
// Flatten a contract into <contract-name>.flattened.sol
EtherShell> flatten('./contracts/MyToken.sol')
Flattened contract written to /path/to/contracts/MyToken.flattened.sol
// Flatten into a custom output file
EtherShell> flatten('./contracts/MyToken.sol', './output/MyToken.flat.sol')
Flattened contract written to /path/to/output/MyToken.flat.solThe generated file contains one SPDX license identifier and one pragma solidity statement, followed by the dependency and entry-file source in dependency order. Review the result before verification or deployment, especially when imported files use different licenses.
Compiler Output Structure:
build/
βββ artifacts/ # Complete contract data with metadata
βββ abis/ # Contract ABIs (.abi.json files)
βββ bytecode/ # Contract bytecode (.bin files)
βββ metadata/ # Contract metadata (.metadata.json files)
βββ standard-json/ # Perβentry solc standard JSON inputs
βββ aggregated.abi.json # Flattened ABI array from all ABI files
βββ types/ # Auto-generated TypeScript types
TypeScript types are generated by scanning build/abis and writing .ts files plus an index.ts barrel export.
// Deploy contract without constructor args and with default wallet
// Arguments: contractName
EtherShell> deploy('contractName')
{
hash: '0x123abc...',
from: '0x1234...5678',
to: null,
address: '0xabcd...ef01',
name: 'contractName',
chain: 'sepolia',
chainId: 11155111n,
deployType: 'ethershell-deployed'
}
// Deploy MyToken contract with constructor args and default wallet
// Arguments: contractName, args[], walletIndex, [chainURL], [abiLocation], [bytecodeLocation]
EtherShell> deploy('MyToken', ['MyTokenName', 'MTK', 1000000])
{ ... }
// Deploy MyToken contract with constructor args and a non-default wallet
EtherShell> deploy('MyToken', ['MyTokenName', 'MTK', 1000000], 0)
{ ... }
// Deploy with custom chain
EtherShell> deploy('MyContract', ['arg1', 'arg2'], 0, 'https://custom-rpc.url')ABIs and bytecode are resolved from the build directory via paths persisted in ./ethershell local storage,.
// Add an already-deployed contract
// Arguments: contractName, contractAddress, walletIndex, abiPath, [chainURL]
EtherShell> addContract(
'USDT',
'0xdAC17F958D2ee523a2206206994597C13D831ec7',
0,
'./build/abis/USDT.abi.json'
)
{
index: 1,
name: 'USDT',
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
chain: 'mainnet',
chainId: 1n,
deployType: 'pre-deployed'
}
// Now you can interact with it:
EtherShell> USDT.balanceOf('0x1234...5678')
1000000000000000000nAll added/deployed contracts are also stored in ./ethershell/contracts.json.
Use restoreContract() after restarting EtherShell to make previously deployed or added contracts available by name again. It takes no arguments and recreates contract proxies from the saved names, addresses, and ABI paths in the contract registry loaded from ./ethershell/contracts.json at startup. It does not compile or redeploy contracts, send transactions, or restore on-chain state.
Start EtherShell from the same project root, then confirm the network and select an existing wallet with a private key before restoring:
// Connect to the network where the stored contracts are deployed
// Skip this if the saved RPC endpoint already points to the correct network
EtherShell> chain('http://127.0.0.1:8545')
// Select an existing imported/generated wallet or HD account
// Replace 0 with the appropriate wallet index
EtherShell> changeDefWallet(0)
// Restore all registered contracts into the current session
EtherShell> restoreContract()
Stored contracts instantiating ...
All stored contracts instantiated!
// A stored contract named MyToken is now available again
EtherShell> MyToken.balanceOf('0x1234...5678')Requirements and behavior:
- Keep
./ethershell/contracts.jsonand the ABI files referenced by its entries. Relative ABI paths are resolved from the current working directory; if you delete or move build artifacts, restore the ABI files at their recorded paths before running the command. - Every restored contract uses the current default wallet. The selected account must have a private key; node-managed accounts are not supported by this restoration path.
- All contracts are attached to the currently selected network. The command does not switch networks using the stored
chainorchainId, validate them against the active network, or recover a custom RPC URL previously passed todeploy()oraddContract(). Use this bulk command only when the stored contracts belong to the active network; for a mixed-network registry, attach contracts individually withaddContract(..., chainURL)instead. - The command restores all registry entries; it has no contract-name, wallet-index, or network arguments. On success, it prints the messages shown above rather than returning a contract list. Use
contracts()to inspect the registry. - Restoration uses the registry already loaded in memory. If you manually edit
contracts.jsonwhile EtherShell is running, restart the console to load those changes. - Run it once when resuming a session. Repeating it recreates the named proxies and appends contract references to the default wallet's in-memory contract list, but does not add duplicate entries to
contracts.json. If an entry fails to restore, the command stops at that error; contracts restored before the error remain available.
// Get all deployed/added contracts
EtherShell> contracts()
[
{
index: 0,
name: 'MyToken',
address: '0xabcd...ef01',
chain: 'sepolia',
chainId: 11155111n,
deployType: 'ethershell-deployed'
},
{
index: 1,
name: 'USDT',
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
chain: 'mainnet',
deployType: 'pre-deployed'
}
]
// Get contract by index
EtherShell> contracts(0)
// Get contract by address
EtherShell> contracts('0xabcd...ef01')
// Get contract by name
EtherShell> contracts('MyToken')contracts() reads enriched contract information (including live balances).
// Your deployed contracts are available as variables
EtherShell> MyToken
Contract { ... }
// Read-only functions
EtherShell> MyToken.name()
"MyTokenName"
EtherShell> MyToken.totalSupply()
1000000n
// State-changing functions
EtherShell> MyToken.transfer('0xRecipientAddress', 100)
ContractTransactionReceipt { ... }
// Call with advanced transaction options
EtherShell> MyToken.transfer('0xRecipientAddress', 100, {
from: '0xSenderAddress', // Switch signer (must be a non node-managed wallet)
value: 10000000000000n, // Send ETH (payable)
gasLimit: 500000,
maxFeePerGas: 100000000000n,
maxPriorityFeePerGas: 2000000000n,
nonce: 42,
chainId: 1
})Contract Proxy Options include:
from: change signer (must haveprivateKey)value: ETH value for payable functionsgasLimitorgasgasPricemaxFeePerGas,maxPriorityFeePerGasnoncechainIdaccessListtypecustomDatafor special networks (e.g., zkSync)[file:25]
// 1. Connect to network
EtherShell> chain('http://127.0.0.1:8545')
// 2. Create wallets
EtherShell> newWallet(2)
// 3. View wallets
EtherShell> wallets()
// 4. Configure compiler
EtherShell> compOpts(true, false, 1000)
// 5. Flatten dependencies into one file (optional)
EtherShell> flatten('./contracts/MyToken.sol')
// 6. Compile contracts
EtherShell> build()
// 7. Deploy contract
EtherShell> deploy('MyToken', ['TestToken', 'TEST', 1000000])
// 8. Interact with contract
EtherShell> MyToken.balanceOf('0x...')
// 9. Transfer tokens with custom options
EtherShell> tx = MyToken.transfer('0x...', 100, {
gasLimit: 100000,
maxFeePerGas: 50000000000n
})
// 10. Check balance again
EtherShell> MyToken.balanceOf('0x...')
// 11. View all contracts
EtherShell> contracts()To resume working with the same contracts after restarting EtherShell in this project, check the network and default wallet, then restore the saved contract proxies:
EtherShell> chainInfo()
EtherShell> defWallet()
EtherShell> restoreContract()
EtherShell> MyToken.balanceOf('0x...')See Restore Stored Contracts for prerequisites and network limitations.
| Command | Description |
|---|---|
chain(url) |
Connect to blockchain network |
chainInfo() |
Get current network info |
defaultChain() |
Get default network URL |
| Command | Description |
|---|---|
newWallet([count]) |
Create random wallets |
addWallet(privKey|keys) |
Import wallets from private keys |
newHDWallet([count]) |
Create HD wallet with random mnemonic |
addHDWallet(phrase, count) |
Import HD wallet from mnemonic |
connectWallet() |
Connect to nodeβmanaged accounts |
wallets() |
View regular accounts |
hdWallets() |
View HD accounts |
allWallets() |
View all accounts |
walletInfo(index|address|[indices]) |
Get wallet details (balance, nonce) |
changeDefWallet(pointer) |
Set default account |
removeWallet(pointer) |
Delete account(s) |
| Command | Description |
|---|---|
compiler() |
Get current Solidity version string |
compUpdate(version) |
Load specific Solidity version |
compOpts(gasOpt, viaIR, runs) |
Configure optimizer and viaIR |
compInfo() |
Get current compiler configuration |
configInfo() |
Get full configuration object |
defWallet() |
Get default account from config |
compPath(newPath) |
Change build output path |
build([path], [contracts], [output]) |
Compile contracts |
clean([path]) |
Delete build directory (default ./build) |
flatten(path, [output]) |
Flatten a Solidity file and its imports into one file |
| Command | Description |
|---|---|
deploy(name, [args], [index], [chainURL], [abiLocation], [bytecodeLocation]) |
Deploy new contract |
addContract(name, address, index, abiPath, [chainURL]) |
Add existing contract |
restoreContract() |
Restore all stored contract proxies with the current network and default wallet |
contracts([pointer]) |
List contracts or get a specific one |
- Node.js 16+
- npm or yarn
- Basic Solidity knowledge[file:31]
ethershell/
βββ bin/
β βββ cli.js # REPL entry point
βββ src/
β βββ services/
β β βββ build.js # Compiler management & build orchestration
β β βββ wallet.js # Wallet management (accounts, HD, node-managed)
β β βββ network.js # Network provider configuration
β β βββ addContracts.js # Deployment & contract registration
β β βββ contracts.js # Contract lookup helper
β β βββ configSync.js # Inβmemory <-> config.json synchronization
β β βββ files.js # File system utilities (clean build)
β βββ utils/
β βββ builder.js # Lowβlevel solc Standard JSON compiler wrapper
β βββ dir.js # Directory & import resolution utilities
β βββ accounter.js # Account storage and deletion utilities
β βββ contractProxy.js # Contract proxy wrapper with tx options
β βββ contractLister.js # Contracts registry and balance formatting
β βββ typeGenerator.js # TypeScript type generation from ABIs
β βββ replHelper.js # REPL customization & async eval
β βββ serialize.js # BigInt serialization for JSON
β βββ configFileUpdate.js # Helper to update provider in config.json
βββ contracts/ # Your Solidity contracts
βββ build/ # Compiled artifacts and types (output)
βββ ethershell/ # Persistent config, wallets, contracts, solc cache
βββ package.json
./ethershell contains config.json, wallets.json, contracts.json, and localStorage data used by the compiler and contract manager.
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.
// 1. Compile
EtherShell> build('./contracts/MyToken.sol')
// 2. Deploy
EtherShell> deploy('MyToken')
// 3. Interact
EtherShell> MyToken.balanceOf('0x...')
// 4. Transfer
EtherShell> MyToken.transfer('0x...', 100)EtherShell stores runtime data under the ./ethershell directory in your project root.
ethershell/
βββ config.json # Compiler and network settings + default wallet
βββ wallets.json # Imported/generated wallets
βββ contracts.json # Registered contracts and metadata
Configuration is updated automatically as you change network, compiler options, default wallet, deploy contracts, or add existing contracts.
{
"providerEndpoint": "http://127.0.0.1:8545",
"defaultWallet": {
"index": 0,
"address": "0x...",
"type": "user-generated"
},
"compiler": {
"version": "v0.8.29+commit.ab55807c",
"optimizer": false,
"viaIR": false,
"optimizerRuns": 200,
"compilePath": "./build"
}
}- Never use generated accounts on mainnet β they are for development and testing only.
- Keep private keys secret β do not commit wallets or config files containing secrets.
- Use testnets (e.g. Sepolia) for experimentation before going to mainnet.
- Always audit and review your contracts before production deployments.
Add to your .gitignore:
.env
node_modules/
build/
ethershell/
*.logThis prevents accidental commits of compiled artifacts and local wallet/config state.
Issue: Error: Cannot find module 'ethers'
Solution: npm installIssue: Cannot connect to network
- Check the RPC URL passed to
chain().[file:19] - Verify the network (Hardhat, Ganache, etc.) is running.
- For public RPCs, check your internet connection and provider limits.
Issue: Insufficient balance for gas
- Ensure the selected wallet has enough ETH for gas.
- Use testnet faucets when working on Sepolia or other testnets.
Issue: Contract not found in build artifacts
- Run
build()first.[file:16] - Check that contract names match exactly.
- Verify
.solfiles exist under./contracts. [file:23]
Issue: stored contract variables are unavailable after restarting EtherShell
- Start EtherShell from the same project root so it loads the correct
./ethershell/contracts.json. - Confirm the correct network with
chainInfo()and select a wallet with a private key usingchangeDefWallet(index). - Run
restoreContract()before calling stored contracts by name. - If restoration fails, check that the saved ABI paths still point to readable, valid ABI JSON files and that the RPC endpoint is reachable. After fixing an ABI path in
contracts.json, restart EtherShell before retrying. - If a local development chain was reset, restoring proxies does not bring back its deployed code or state; redeploy the contracts and use the new addresses.
Issue: TypeScript types not generated
- Ensure compilation succeeded.
- Confirm ABI files exist under
build/abis. - Check console output for errors thrown by
generateAllTypes(). [file:16][file:30]
Issue: error about { from } with nodeβmanaged account
- Nodeβmanaged accounts do not have private keys in EtherShell, so
{ from }cannot rebind them. - Import or generate a wallet with a private key and use that address in
frominstead. [file:20][file:25]
The source code uses detailed JSDoc comments:
@fileoverviewβ file purpose and module description@paramβ parameter types and descriptions@returnsβ return types@throwsβ error conditions@exampleβ usage samples[file:13][file:16][file:20][file:25]
You can generate static docs with JSDoc if desired:
npm install -g jsdoc
jsdoc src/ -r -d docs/Contributions are welcome:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/AmazingFeature). - Commit your changes.
- Push to your branch.
- Open a Pull Request.[file:31]
This project is licensed under the BUSLβ1.1 License β see the LICENSE file for details. [file:31]
- Issues: GitHub Issues
- Questions: GitHub Discussions
- Docs: This README and
ethershell-website.html[file:31][file:32]
Made with β€οΈ for Ethereum developers.
Happy hacking! π