Skip to main content

Overview

This guide covers balance operations for both EVM and Solana chains, including balance retrieval, conversion utilities, and formatting.

Prerequisites

Get Wallet Balance

The SDK provides a unified method to get balance for any wallet type:
import 'package:dynamic_sdk/dynamic_sdk.dart';

final sdk = DynamicSDK.instance;

Future<String> getBalance(BaseWallet wallet) async {
  try {
    final balance = await sdk.wallets.getBalance(wallet: wallet);
    print('Balance: $balance');
    return balance;
  } catch (e) {
    print('Failed to get balance: $e');
    rethrow;
  }
}

Display Balance with Flutter

import 'package:flutter/material.dart';
import 'package:dynamic_sdk/dynamic_sdk.dart';

class WalletBalanceWidget extends StatefulWidget {
  final BaseWallet wallet;

  const WalletBalanceWidget({Key? key, required this.wallet}) : super(key: key);

  @override
  State<WalletBalanceWidget> createState() => _WalletBalanceWidgetState();
}

class _WalletBalanceWidgetState extends State<WalletBalanceWidget> {
  final sdk = DynamicSDK.instance;
  String? balance;
  bool isLoading = false;
  String? errorMessage;

  @override
  void initState() {
    super.initState();
    _fetchBalance();
  }

  Future<void> _fetchBalance() async {
    setState(() {
      isLoading = true;
      errorMessage = null;
    });

    try {
      final balanceValue = await sdk.wallets.getBalance(wallet: widget.wallet);
      setState(() => balance = balanceValue);
    } catch (e) {
      setState(() => errorMessage = 'Failed to get balance: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'Balance',
          style: TextStyle(fontSize: 12, color: Colors.grey),
        ),
        const SizedBox(height: 8),
        if (isLoading)
          const Row(
            children: [
              SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(strokeWidth: 2),
              ),
              SizedBox(width: 8),
              Text('Loading...', style: TextStyle(color: Colors.grey)),
            ],
          )
        else if (balance != null)
          Text(
            balance!,
            style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          )
        else if (errorMessage != null)
          Text(
            errorMessage!,
            style: const TextStyle(fontSize: 12, color: Colors.red),
          ),
      ],
    );
  }
}

EVM Balance Conversions

Wei to ETH Conversion

import 'package:web3dart/web3dart.dart';

/// Convert Wei to ETH
String weiToEth(BigInt wei, {int decimals = 6}) {
  final ethValue = wei / BigInt.from(10).pow(18);
  return ethValue.toStringAsFixed(decimals);
}

// Usage
final balanceWei = BigInt.from(1500000000000000000); // 1.5 ETH
final balanceEth = weiToEth(balanceWei);
print('Balance: $balanceEth ETH');

ETH to Wei Conversion

import 'package:web3dart/web3dart.dart';

/// Convert ETH to Wei
BigInt ethToWei(double eth) {
  return BigInt.from(eth * 1e18);
}

// Usage
final ethAmount = 1.5;
final weiAmount = ethToWei(ethAmount);
print('$ethAmount ETH = $weiAmount Wei');

Gwei Conversions

/// Convert Wei to Gwei
double weiToGwei(BigInt wei) {
  return wei.toDouble() / 1e9; // 1 Gwei = 10^9 Wei
}

/// Convert Gwei to Wei
BigInt gweiToWei(double gwei) {
  return BigInt.from(gwei * 1e9);
}

// Usage for gas prices
final gasPriceGwei = 50.0;
final gasPriceWei = gweiToWei(gasPriceGwei);
print('Gas Price: $gasPriceGwei Gwei = $gasPriceWei Wei');

Balance Formatting

/// Format EVM balance with appropriate units
String formatEvmBalance(BigInt wei) {
  final eth = wei.toDouble() / 1e18;

  if (eth >= 1.0) {
    return '${eth.toStringAsFixed(4)} ETH';
  } else if (eth >= 0.001) {
    return '${eth.toStringAsFixed(6)} ETH';
  } else {
    final gwei = wei.toDouble() / 1e9;
    return '${gwei.toStringAsFixed(2)} Gwei';
  }
}

Solana Balance Conversions

Solana uses lamports as its smallest unit, where 1 SOL = 10^9 lamports (9 decimal places).

Lamports to SOL Conversion

/// Convert lamports to SOL
String lamportsToSol(int lamports, {int decimals = 6}) {
  final solValue = lamports / 1e9;
  return solValue.toStringAsFixed(decimals);
}

// Usage
final balanceLamports = 1500000000; // 1.5 SOL
final balanceSol = lamportsToSol(balanceLamports);
print('Balance: $balanceSol SOL');

SOL to Lamports Conversion

/// Convert SOL to lamports
int solToLamports(double sol) {
  return (sol * 1e9).toInt();
}

// Usage
final solAmount = 1.5;
final lamportsAmount = solToLamports(solAmount);
print('$solAmount SOL = $lamportsAmount lamports');

SOL Balance Formatting

/// Format SOL balance for display
String formatSolBalance(int lamports) {
  final sol = lamports / 1e9;

  if (sol >= 1.0) {
    return '${sol.toStringAsFixed(4)} SOL';
  } else if (sol >= 0.001) {
    return '${sol.toStringAsFixed(6)} SOL';
  } else {
    return '${sol.toStringAsFixed(9)} SOL';
  }
}

Multi-Chain Balance Display

Handle balances for both EVM and Solana wallets:
import 'package:dynamic_sdk/dynamic_sdk.dart';

final sdk = DynamicSDK.instance;

Future<void> displayBalance(BaseWallet wallet) async {
  try {
    final balance = await sdk.wallets.getBalance(wallet: wallet);
    final chain = wallet.chain.toUpperCase();

    switch (chain) {
      case 'EVM':
        print('Balance: $balance (in Wei)');
        break;
      case 'SOL':
        print('Balance: $balance (in lamports)');
        break;
      default:
        print('Balance: $balance');
    }
  } catch (e) {
    print('Failed to get balance: $e');
  }
}

// Display all wallet balances
Future<void> displayAllBalances() async {
  for (final wallet in sdk.wallets.userWallets) {
    print('${wallet.chain} wallet: ${wallet.address}');
    await displayBalance(wallet);
  }
}

Conversion Helper Class

/// Helper class for balance conversions
class BalanceConverter {
  /// EVM conversions
  static double weiToEth(BigInt wei) {
    return wei.toDouble() / 1e18;
  }

  static BigInt ethToWei(double eth) {
    return BigInt.from(eth * 1e18);
  }

  static double weiToGwei(BigInt wei) {
    return wei.toDouble() / 1e9;
  }

  /// Solana conversions
  static double lamportsToSol(int lamports) {
    return lamports / 1e9;
  }

  static int solToLamports(double sol) {
    return (sol * 1e9).toInt();
  }

  /// Format for display
  static String formatBalance({
    required String value,
    required String chain,
    int decimals = 4,
  }) {
    switch (chain.toUpperCase()) {
      case 'EVM':
        final wei = BigInt.tryParse(value);
        if (wei != null) {
          final eth = weiToEth(wei);
          return '${eth.toStringAsFixed(decimals)} ETH';
        }
        break;
      case 'SOL':
        final lamports = int.tryParse(value);
        if (lamports != null) {
          final sol = lamportsToSol(lamports);
          return '${sol.toStringAsFixed(decimals)} SOL';
        }
        break;
    }
    return value;
  }
}

// Usage
final formatted = BalanceConverter.formatBalance(
  value: '1500000000000000000',
  chain: 'EVM',
  decimals: 4,
);
print(formatted); // "1.5000 ETH"

Common Token Decimals

ChainTokenDecimalsUnit
EVMETH18Wei
EVMUSDC6Smallest unit
EVMUSDT6Smallest unit
EVMWBTC8Smallest unit
SolanaSOL9Lamports

Best Practices

1. Cache Balances

import 'package:flutter/foundation.dart';
import 'package:dynamic_sdk/dynamic_sdk.dart';

class BalanceCache extends ChangeNotifier {
  final Map<String, String> _balances = {};
  final sdk = DynamicSDK.instance;

  Map<String, String> get balances => _balances;

  Future<void> refreshBalance(BaseWallet wallet) async {
    try {
      final balance = await sdk.wallets.getBalance(wallet: wallet);
      _balances[wallet.address] = balance;
      notifyListeners();
    } catch (e) {
      print('Failed to refresh balance: $e');
    }
  }

  Future<void> refreshAllBalances() async {
    for (final wallet in sdk.wallets.userWallets) {
      await refreshBalance(wallet);
    }
  }
}

2. Handle Precision

// Use BigInt for EVM to avoid precision loss
final wei = BigInt.parse('1500000000000000000'); // 1.5 ETH
final eth = BalanceConverter.weiToEth(wei);

// Use int for Solana
final lamports = 1500000000; // 1.5 SOL
final sol = BalanceConverter.lamportsToSol(lamports);

3. Refresh After Transactions

Future<void> sendTransactionAndRefresh(BaseWallet wallet) async {
  try {
    // Send transaction
    final txHash = await sendTransaction(wallet: wallet);
    print('Transaction sent: $txHash');

    // Wait a moment for blockchain confirmation
    await Future.delayed(const Duration(seconds: 2));

    // Refresh balance
    final newBalance = await sdk.wallets.getBalance(wallet: wallet);
    print('New balance: $newBalance');
  } catch (e) {
    print('Error: $e');
  }
}

FutureBuilder Example

import 'package:flutter/material.dart';
import 'package:dynamic_sdk/dynamic_sdk.dart';

class BalanceDisplay extends StatelessWidget {
  final BaseWallet wallet;

  const BalanceDisplay({Key? key, required this.wallet}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: DynamicSDK.instance.wallets.getBalance(wallet: wallet),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const CircularProgressIndicator();
        }

        if (snapshot.hasError) {
          return Text(
            'Error: ${snapshot.error}',
            style: const TextStyle(color: Colors.red),
          );
        }

        if (snapshot.hasData) {
          final formatted = BalanceConverter.formatBalance(
            value: snapshot.data!,
            chain: wallet.chain,
          );
          return Text(
            formatted,
            style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          );
        }

        return const Text('No data');
      },
    );
  }
}

Next Steps