A utility has been implemented to automatically fund new Stellar testnet accounts using Friendbot. The solution is idempotent, mainnet-safe (rejects funding), and provides robust error handling.
The project was restructured semantically under src/friendbot/:
src/friendbot/
├── mod.rs # Registers config and utils
├── config/
│ ├── mod.rs # NetworkConfig (testnet/futurenet/mainnet)
│ ├── tests.rs # Configuration tests
│ └── test_helpers.rs # Shared mutex to prevent race conditions
└── utils/
├── mod.rs # Registers friendbot and types
├── friendbot.rs # fund_account()
├── types.rs # StellarError enum
└── tests.rs # Friendbot tests
Location: src/friendbot/utils/friendbot.rs
pub fn fund_account(public_key: &str) -> Result<(), StellarError>Features:
- ✅ Performs HTTP GET request to Friendbot
- ✅ Mainnet Guard: rejects funding on mainnet (returns
FriendbotNotAvailable) - ✅ Idempotent: treats "createAccountAlreadyExist" (HTTP 400) as success
- ✅ Robust network/Friendbot error handling
- ✅ Uses
STELLAR_NETWORKenv var for network selection
Supported Networks:
- Testnet:
https://friendbot.stellar.org/?addr={public_key} - Futurenet:
https://friendbot-futurenet.stellar.org/?addr={public_key} - Mainnet: ❌ Error (no Friendbot)
Type: StellarError (in src/friendbot/utils/types.rs)
pub enum StellarError {
FriendbotNotAvailable { network: String },
HttpRequestFailed(String),
FriendbotError { status: u16, body: String },
}Implements Display and std::error::Error for compatibility.
Location: src/friendbot/config/mod.rs
| Network | Passphrase | Horizon | Soroban RPC | Friendbot |
|---|---|---|---|---|
| Testnet | "Test SDF Network ; September 2015" | horizon-testnet.stellar.org | soroban-testnet.stellar.org | ✅ |
| Futurenet | "Test SDF Future Network ; October 2022" | horizon-futurenet.stellar.org | rpc-futurenet.stellar.org | ✅ |
| Mainnet | "Public Global Stellar Network ; September 2015" | horizon.stellar.org | soroban-rpc.mainnet.stellar.gateway.fm | ❌ |
Selected via STELLAR_NETWORK env var (default: testnet).
Location: src/friendbot/utils/tests.rs and src/friendbot/config/tests.rs
Coverage:
- ✅
fund_account()rejects mainnet - ✅ Error display mentions supported networks
- ✅ NetworkConfig has correct URLs
- ✅ Friendbot available on testnet/futurenet, not mainnet
- ✅ Error variants display HTTP status and body
Total: 15 tests (14 pass, 0 fail after fixing race conditions)
Problem: Parallel tests corrupting STELLAR_NETWORK env var
Solution: Shared mutex in src/friendbot/config/test_helpers.rs
pub static ENV_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
pub fn with_network<F: FnOnce()>(value: &str, f: F)
pub fn without_network<F: FnOnce()>(f: F)All tests touching env vars use these helpers → guaranteed serialization across modules.
Cargo.toml:
[dependencies]
reqwest = { version = "0.12", features = ["blocking"] }Location: Makefile in repository root
Main targets:
make wasm # Build WASM
make build # Build workspace
make test # Run tests
make deploy # Deploy contract (requires soroban CLI)
make fund ADDR=G... # Fund address via Friendbot
make invoke FUNC=ping # Invoke contract method
make fmt # Format code
make lint # Clippy strict
make clean # Clean artifacts✅ fund_account() successfully funds new testnet account
✅ Calling on mainnet returns error (FriendbotNotAvailable)
✅ Already-funded → idempotent (no errors)
✅ Imports corrected after reorganization
✅ Tests separated into own files
✅ Race conditions eliminated
✅ Dead code warnings resolved
✅ Makefile with targets for complete workflow
use contract_fox::friendbot::utils::friendbot::fund_account;
// Testnet (default)
fund_account("GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN")?;
// Futurenet
std::env::set_var("STELLAR_NETWORK", "futurenet");
fund_account("GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN")?;
// Mainnet → Error: FriendbotNotAvailable
std::env::set_var("STELLAR_NETWORK", "mainnet");
assert!(fund_account("...").is_err());# Build WASM
make wasm CONTRACT_PKG=contract-fox
# Deploy
make deploy NETWORK=testnet
# Fund address
make fund ADDR=GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN
# Invoke contract
make invoke FUNC=ping| Before | After | Reason |
|---|---|---|
src/config/ |
src/friendbot/config/ |
Semantic organization |
src/utils/friendbot.rs |
src/friendbot/utils/friendbot.rs |
Dedicated module |
| Inline tests | Separated in *.rs |
Better maintainability |
Local ENV_MUTEX per module |
Shared test_helpers.rs |
Fix race conditions |
| No Makefile | Complete Makefile |
Simplified workflow |
# Check compilation
cargo check
# Run all tests
cargo test --workspace
# With Makefile
make test
make fmt
make lint
# Build WASM (requires target)
rustup target add wasm32-unknown-unknown
make wasm
# Deploy (requires soroban CLI)
cargo install soroban-cli
make deploy NETWORK=testnet- Integrate with deployment CLI tool (
cargo run -p stellaraid-tools) - Add more verbose logging with
tracing/log - Support configurable Friendbot timeout
- Cache funding results in local database
- Integration tests with Soroban sandbox
- The
STELLAR_NETWORKenv var is case-insensitive - Testnet is default if variable not set
- Friendbot responds in JSON; basic parsing (checks for "createAccountAlreadyExist")
- reqwest blocking API (synchronous) for simplicity
- All test helpers use
OnceLockfor lazy init-once
Status: ✅ Ready for PR Issue: Friendbot utility implementation Branch: (per your workflow)