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.
- 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. - 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 successfullyCompiler 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.
// 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. Compile contracts
EtherShell> build()
// 6. Deploy contract
EtherShell> deploy('MyToken', ['TestToken', 'TEST', 1000000])
// 7. Interact with contract
EtherShell> MyToken.balanceOf('0x...')
// 8. Transfer tokens with custom options
EtherShell> tx = MyToken.transfer('0x...', 100, {
gasLimit: 100000,
maxFeePerGas: 50000000000n
})
// 9. Check balance again
EtherShell> MyToken.balanceOf('0x...')
// 10. View all contracts
EtherShell> contracts()| 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) |
| Command | Description |
|---|---|
deploy(name, [args], [index], [chainURL], [abiLocation], [bytecodeLocation]) |
Deploy new contract |
addContract(name, address, index, abiPath, [chainURL]) |
Add existing contract |
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: 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! π