A dual-chain faucet for distributing native tokens on Cosmos SDK chains with EVM support. Built for frequently-resetting devnets with balance-based limiting.
- Dual Environment Support: Simultaneous Cosmos SDK + EVM native token distribution
- Single Native Token: Distributes the chain's native token to both Bech32 and EVM addresses
- Balance-Based Limiting: Only tops up wallets below a configurable threshold (default: 10 tokens)
- Secure Key Management: Mnemonic-based dual address derivation (eth_secp256k1)
- Modern UI: Vue 3 frontend with Keplr and MetaMask/WalletConnect integration
- Transaction History: Persistent tracking with explorer links
- Production Ready: Docker support, Vercel/Fly.io compatible
- Node.js >= 18.0.0
- npm or yarn
git clone <repository-url>
cd devnet-faucet
npm installcp .env.example .envEdit .env with your mnemonic:
# Required: 12-word mnemonic for wallet derivation
MNEMONIC="your twelve word mnemonic phrase here"Edit config.js to set your network endpoints, chain IDs, and native token configuration:
const config = {
blockchain: {
name: 'your-chain',
ids: {
chainId: 1234, // EVM chain ID
cosmosChainId: 'chain-1', // Cosmos chain ID
},
endpoints: {
rpc_endpoint: 'https://rpc.yourchain.com:26657',
evm_endpoint: 'https://rpc.yourchain.com:8545',
// ... other endpoints
},
sender: {
option: {
prefix: 'yourprefix', // Bech32 address prefix
},
},
tx: {
amounts: [{
denom: 'utoken',
symbol: 'TOKEN',
name: 'Your Token',
amount: '1000000000000000000',
decimals: 18,
}],
},
},
};npm start # Start faucet server on port 8088Development mode with hot reload:
npm run dev # Start Vite dev server on port 3000Core Components:
faucet.js- Express server with API endpointsconfig.js- Network configuration and native token settingssrc/SecureKeyManager.js- Key derivation and address management
Balance-Based Limiting:
- Faucet checks recipient's current balance before sending
- Only tops up wallets below the configured threshold (default: 10 tokens)
- Prevents abuse while allowing legitimate users to get tokens when needed
Structure:
src/main.js- Vue app initializationsrc/App.vue- Root component with wallet integrationsrc/components/- Vue components (Header, Tabs, Modals, etc.)src/composables/- Vue composables for state management
Wallet Support:
- Cosmos: Keplr wallet (desktop + mobile)
- EVM: MetaMask, WalletConnect, Reown AppKit
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
Vue.js web interface |
| GET | /health |
Health check endpoint |
| GET | /send/:address |
Request tokens (Bech32 or EVM address) |
| GET | /config.json |
Network configuration for frontend |
| GET | /balance/cosmos |
Faucet Cosmos token balance |
| GET | /balance/evm |
Faucet EVM token balance |
- Cosmos/Bech32:
{prefix}1...(e.g.,republic1...,cosmos1...) - EVM:
0x...(hex, 42 chars)
Both formats derived from same mnemonic using eth_secp256k1.
Server:
npm start # Start production server
npm run dev # Start Vite dev server with HMRBuilding:
npm run build # Build frontend for production
npm run preview # Preview production buildTesting & Linting:
npm test # Run all tests (Vitest)
npm run test:watch # Run tests in watch mode
npm run test:coverage # Generate coverage report
npm run lint # Check code with Biome
npm run lint:fix # Auto-fix linting issues
npm run check # Run linting + testsDocker:
npm run docker:build # Build Docker image
npm run docker # Run Docker containerdevnet-faucet/
|-- faucet.js # Main Express server
|-- config.js # Network configuration
|-- biome.json # Biome configuration
|-- vitest.config.js # Vitest configuration
|-- vite.config.js # Vite build configuration
|
|-- src/ # Source files
| |-- main.js # Vue app entry
| |-- App.vue # Root Vue component
| |-- SecureKeyManager.js # Key management
| |-- logRotation.js # Log management
| |
| |-- components/ # Vue components
| | |-- Header.vue
| | |-- FaucetBalances.vue
| | |-- TransactionModal.vue
| | |-- tabs/
| | |-- FaucetTab.vue
| | |-- NetworkInfo.vue
| | |-- RecentTransactionsTab.vue
| |
| |-- composables/ # Vue composables
| | |-- useConfig.js
| | |-- useWalletStore.js
| | |-- useTransactions.js
| |
| |-- assets/ # CSS and assets
|
|-- scripts/ # Utility scripts
| |-- deploy-to-fly.sh
| |-- docker-run.sh
| |-- query-ibc-denoms.js
|
|-- tests/ # Test suite
|-- SecureKeyManager.test.js
|-- config.test.js
|-- faucet.test.js
Required:
MNEMONIC- 12-word mnemonic phrase
Optional:
PORT- Server port (default: 8088)VITE_REOWN_PROJECT_ID- WalletConnect project ID
npm i -g vercel
vercel
vercel env add MNEMONICcurl -L https://fly.io/install.sh | sh
fly apps create your-faucet-name
fly secrets set MNEMONIC="your twelve word mnemonic phrase here"
fly deployRequired Fly.io Secrets:
MNEMONIC- 12-word mnemonic phrase for wallet derivation (REQUIRED - app will not start without this)
npm run docker:build
docker run -p 8088:8088 -e MNEMONIC="..." devnet-faucet# Check faucet balance
curl http://localhost:8088/balance/evm
curl http://localhost:8088/balance/cosmos
# Test API endpoint
curl http://localhost:8088/config.json
# Health check
curl http://localhost:8088/healthMIT
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
npm test - Run linting:
npm run lint:fix - Submit a pull request