This implementation adds support for the native Stellar asset (XLM) alongside standard SAC (Stellar Asset Contract) tokens like USDC.
is_native_token(): Detects if an address represents the native XLM assettransfer_tokens(): Handles transfers for both native XLM and SAC tokensget_token_balance(): Gets balances for both token typesget_native_token_address(): Test helper for creating native token addressesenforce_xlm_gas_reserve(): Prevents draining the last 5 XLM needed for future transactions
apply_provider_claim(): Now uses the new transfer_tokens helpertop_up(): Now uses the new transfer_tokens helper
test_prepaid_meter_flow_with_native_xlm(): Tests prepaid meters with native XLMtest_postpaid_meter_flow_with_native_xlm(): Tests postpaid meters with native XLM
The is_native_token() function treats the contract address as native for internal flows and
checks token metadata for symbol == "XLM" or "NATIVE":
fn is_native_token(env: &Env, token_address: &Address) -> bool {
if token_address == &env.current_contract_address() {
return true;
}
let client = token::Client::new(env, token_address);
let symbol = client.symbol();
symbol == soroban_sdk::String::from_str(env, "XLM")
|| symbol == soroban_sdk::String::from_str(env, "NATIVE")
}The transfer_tokens() function routes transfers based on token type:
fn transfer_tokens(env: &Env, token_address: &Address, from: &Address, to: &Address, amount: &i128) {
if is_native_token(token_address) {
// For native XLM, use the built-in transfer function
env.token().transfer(from, to, amount);
} else {
// For SAC tokens, use the token contract
let client = token::Client::new(env, token_address);
client.transfer(from, to, amount);
}
}When funding streams with native XLM, the contract enforces a 5 XLM minimum reserve in the payer's wallet to ensure they can always submit stop/update transactions.
To run the tests, you'll need a proper Rust build environment with Visual Studio Build Tools:
cd contracts/utility_contracts
cargo testThe tests verify:
- Meter registration with native XLM
- Top-up functionality with native XLM
- Claim operations with native XLM
- Unit deduction with native XLM
- Both prepaid and postpaid billing modes
- Proper balance tracking for all parties
- All existing SAC token functionality remains unchanged
- Existing tests continue to work with SAC tokens
- No breaking changes to the contract interface
- ✅ Native Stellar XLM
- ✅ Standard SAC tokens (USDC, custom tokens, etc.)
- ✅ Mixed deployments (some meters using XLM, others using SAC tokens)
// Get native token address
let native_token = get_native_token_address(&env);
// Register meter with native XLM
let meter_id = client.register_meter(&user, &provider, &rate, &native_token);// Register meter with SAC token (existing functionality)
let meter_id = client.register_meter(&user, &provider, &rate, &sac_token_address);-
Production Deployment: In production, the native token address patterns should be updated to match the actual mainnet/testnet native asset addresses.
-
Security: The implementation maintains the same security guarantees for both token types.
-
Gas Efficiency: Native XLM operations are more gas-efficient as they don't require external contract calls.
-
Testing: The test environment uses a special "NATIVE_TOKEN" address to simulate native XLM behavior.