A comprehensive multi-blockchain faucet system developed for Vilnius University. This platform provides free testnet tokens for various blockchain networks, educational tools, and DApp hosting capabilities.
-
UTXO-Based Networks:
- KNF Coin (faculty's own blockchain)
- Bitcoin Testnet4
- Litecoin Testnet4
-
EVM-Compatible Networks:
- Ethereum Sepolia Testnet
- zkSync Sepolia Testnet
- Linea Sepolia Testnet
- Ethereum Hoodi Testnet
- Arbitrum Sepolia Testnet
- Polygon Amoy Testnet
-
SVM Networks (Solana runtime β Phantom wallet, Ed25519 addresses):
- Solana Devnet
-
Move Networks (Sui β any Wallet-Standard Sui wallet such as Slush or Suiet):
- Sui Testnet
-
ERC-20 Test Tokens (token-first β one page per token, across every chain it is deployed on):
- Chainlink (LINK) on Sepolia
- Blockchain Simulator: Interactive SHA-256 blockchain demonstration
- Transaction Graph Visualizer: Explore cryptocurrency transaction flows (See more)
- DApp Hosting: File browser and hosting for decentralized applications
- Docker and Docker Compose
- Infura API key (for EVM networks)
- Etherscan API key (optional, for the transaction graph)
git clone https://github.com/kaunofakultetas/crypto-faucet.git
cd crypto-faucetCopy the sample configuration:
cp docker-compose.yml.sample docker-compose.ymlCreate a .env file next to docker-compose.yml with your secrets (the compose file reads them via ${...} substitution):
INFURA_PROJECT_ID=your_infura_project_id
ETHERSCAN_API_KEY=your_etherscan_api_key
FAUCET_PRIVATE_KEY=your_faucet_wallet_private_key
DBGATE_PASSWORD=your_db_admin_passwordThen set the GUI login password in docker-compose.yml (service faucet-endpoint β APP_PASSWORD_1).
./runUpdateThisStack.sh- Main Interface:
http://<server-ip>(or your configured domain) - GUI Password: the
APP_PASSWORD_1you set above - Database Browser:
/dbgate(loginadmin+ yourDBGATE_PASSWORD)
| Variable | Description | Default | Required |
|---|---|---|---|
INFURA_PROJECT_ID |
Infura API project ID | - | β |
FAUCET_PRIVATE_KEY |
Private key of the faucet wallet β one secret, shared by EVM, ERC-20, UTXO, SVM and Move; it derives a DIFFERENT address per family (the Sui one is the key hashed as an Ed25519 seed), each funded separately | - | β |
DBGATE_PASSWORD |
Password of the /dbgate database browser |
- | β |
APP_PASSWORD_1 |
System GUI access password (set in compose, not .env) |
- | β |
ETHERSCAN_API_KEY |
Etherscan API key (transaction graph) | - | β |
APP_DEBUG |
Flask debug mode (development only) | false | β |
All networks and tokens are defined in _CONFIG/coins.py, which is mounted read-only into the backend container (./_CONFIG:/config) β so the coin catalog lives outside the images and can be changed without rebuilding anything:
_CONFIG/coins.pyholds five maps:EVM_NETWORK_CONFIGS,ERC20_TOKEN_CONFIGS,UTXO_NETWORK_CONFIGS,SVM_NETWORK_CONFIGS,MOVE_NETWORK_CONFIGS. Emptying a map disables that whole family (the navbar hides it). Each entry is sectioned by who consumes the settings (faucet/metamask/wallet/explorer). The file is validated on boot β a typo kills the start with a precise error indocker logs faucet-backendinstead of a silent fallback. The Infura key never sits in this file:<INFURA_PROJECT_ID>insiderpc_urlis substituted from the environment at startup._CONFIG/icons/<type>/<key>.svg(or.png/.webp) holds the asset icons, where<type>isevm/erc20/utxo/svm/moveand<key>is the entry's key in the maps (e.g.evm/sepolia.svg,erc20/LINK.svg,utxo/btc4.svg,svm/solanaDevnet.svg,move/suiTestnet.svg). Assets without an icon file automatically fall back to a colored dot in the UI.
The UTXO, SVM and Move entries name a coin / chain plus a network flavour (bitcoin + testnet, solana + devnet, sui + testnet); everything protocol-precise β address version bytes, fee rates, dust limits, lamport/MIST decimals, rent-exempt minimums, gas margins β lives in the backend's in-code registries (app/utxo_faucet/coins/, app/svm_faucet/chains/, app/move_faucet/chains/) and is never an operator setting. An unknown coin/chain, an unknown flavour, or an SVM chunk_size below the chain's rent-exempt minimum all fail the boot.
To add or change a coin: edit _CONFIG/coins.py, then docker restart faucet-backend (~3 s).
To add or change an icon: drop the file into _CONFIG/icons/ β it appears on the next page load, no restart at all.
- Navigate to
/faucet/utxo/{network}(e.g.,/faucet/utxo/btc4,/faucet/utxo/ltc4,/faucet/utxo/knf) - Enter your testnet address
- Click the request button
- Receive testnet cryptocurrency at your address
- Navigate to
/faucet/evm/{network}(e.g.,/faucet/evm/sepolia) - Connect your MetaMask wallet and switch to the network (the page adds it to MetaMask if missing)
- Sign the verification message β no transaction, the signature only proves you own the address
- Receive testnet ETH in your wallet
- Navigate to
/faucet/svm/{network}(e.g.,/faucet/svm/solanaDevnet) - Connect your Phantom wallet
- Put Phantom on Devnet β Settings (βοΈ) β Developer Settings β Testnet Mode, then pick Solana Devnet (not Solana) in the network list. Phantom cannot always be switched by the page, so the instructions stay on screen until it confirms the hop; coins always go to Devnet, and a wallet left on mainnet will not show them
- Sign the verification message β no transaction, the Ed25519 signature only proves you own the address
- Receive testnet SOL in your wallet
- Navigate to
/faucet/move/{network}(e.g.,/faucet/move/suiTestnet) - Connect your Sui wallet (Slush, Suiet or any other Wallet-Standard wallet β the page shows whichever it finds) β there is no network step, a Sui address is the same on every network
- Put the wallet on Testnet in its own network selector (it ships on Mainnet) β the page names the network to select and turns amber while the wallet reports another one
- Sign the verification message β no transaction, the signature only proves you own the address
- Receive testnet SUI in your wallet
- Navigate to
/faucet/erc20/{token}(e.g.,/faucet/erc20/LINK) β one page shows the token on every chain it is deployed on - Connect MetaMask and switch to one of the token's networks
- Hold some native crypto there first β receiving tokens is free, but using them costs gas, so each chain requires at least half the native faucet's chunk before its claim button unlocks (the page links to the right native faucet if you're short)
- Claim, then press "Rodyti MetaMask" on the chain card β it switches the wallet and imports the token contract, since freshly received ERC-20s are invisible in MetaMask until imported
- Access at
/sha256 - Interactive demonstration of blockchain concepts
- Mine blocks and explore hash functions
Transaction Graph (See more)
- Access at
/graph/{network}β reached from the graph button on an EVM faucet page - Visualize cryptocurrency transaction flows, one day at a time (the slider offers the days the faucet transacted on)
- Explore addresses and transaction relationships
- Only for EVM networks with an
explorersection in_CONFIG/coins.py(an Etherscan-style API)
- Upload static files via
/dappsfile browser - Host decentralized applications
- Manage hosted content
βββ _CONFIG/ # Operator-editable config (mounted into the backend)
β βββ coins.py # EVM / ERC-20 / UTXO / SVM / Move network & token definitions
β βββ icons/ # Crypto asset icons (evm/, erc20/, utxo/, svm/, move/)
βββ _DATA/ # Runtime data (SQLite, dapps, notes) β created on first run
βββ backend/ # Python Flask API
β βββ app/
β β βββ evm_faucet/ # Native EVM faucet + Etherscan explorer
β β βββ erc_faucet/ # ERC-20 token faucet
β β βββ utxo_faucet/ # UTXO faucet (Electrum-based)
β β βββ svm_faucet/ # SVM faucet (Solana JSON-RPC)
β β βββ move_faucet/ # Move faucet (Sui GraphQL)
β β βββ faucet_catalog/ # /api/faucet/catalog β every family in one payload (the navbar)
β β βββ cooldown.py # The per-address claim cooldown every family shares
β β βββ icons.py # /api/icons β serves _CONFIG/icons
β β βββ database/ # SQLite helpers
β βββ tests/ # Regression tests (see backend/tests/README.md)
β βββ tools/ # Operator tools (graph cache pruning)
β βββ main.py # Entry point β loads & validates _CONFIG/coins.py
βββ vite/ # React frontend (Vite + MUI + Tailwind)
βββ endpoint/ # Caddy ingress (login gate + routing)
βββ dapps/ # DApp hosting configs (filebrowser + caddy)
βββ docker-compose.yml # Container orchestration
All endpoints are GET; the request endpoints take their inputs as query parameters.
GET /api/utxo/networks- List supported UTXO networksGET /api/utxo/{network}/request-btc?address=- Request testnet coinsGET /api/utxo/{network}/faucet-balance- Check faucet balance
GET /api/evm/networks- List supported EVM networksGET /api/evm/{network}/request?address=&signature=&nonce=- Request testnet ETH (signature proves address ownership)GET /api/evm/{network}/faucet-balance- Check faucet balance
GET /api/svm/networks- List supported SVM networksGET /api/svm/{network}/request?address=&signature=&nonce=- Request testnet SOL (Ed25519 signature proves address ownership)GET /api/svm/{network}/faucet-balance- Check faucet balance
GET /api/move/networks- List supported Move networksGET /api/move/{network}/request?address=&signature=&nonce=- Request testnet SUI (Ed25519 signature proves address ownership)GET /api/move/{network}/faucet-balance- Check faucet balance
GET /api/erc20/tokens- List supported tokens and their networksGET /api/erc20/token/{symbol}?address=- One token across all its chains (balances, gas thresholds)GET /api/erc20/{network}/{token}/request?address=&signature=&nonce=- Request tokens on one chain
GET /api/faucet/catalog- Every family's network/token list in one payload (what the navbar loads)
GET /api/evm/{network}/get-stored-transactions?address=&from=&to=- Aggregated flows touching an address inside a[from, to)unix windowGET /api/evm/{network}/transaction-days?address=&tz_offset=- The days an address transacted on, in the browser's timezoneGET /api/evm/set-address-name?address=&name=- Label an address in the graph
GET /api/get-example-blockchain- The pre-mined example chain the simulator loads
GET /api/icons/{type}/{key}- Icon of a network or token (type:evm/erc20/utxo/svm/move)