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 DynamicSDKSwift

let sdk = DynamicSDK.instance()

func getBalance(wallet: BaseWallet) async {
    do {
        let balance = try await sdk.wallets.getBalance(wallet: wallet)
        print("Balance: \(balance)")
    } catch {
        print("Failed to get balance: \(error)")
    }
}

Display Balance with SwiftUI

import SwiftUI
import DynamicSDKSwift

@MainActor
class WalletBalanceViewModel: ObservableObject {
    @Published var balance: String?
    @Published var isLoading = false
    @Published var errorMessage: String?

    private let sdk = DynamicSDK.instance()

    func fetchBalance(wallet: BaseWallet) {
        isLoading = true
        errorMessage = nil

        Task {
            do {
                balance = try await sdk.wallets.getBalance(wallet: wallet)
            } catch {
                errorMessage = "Failed to get balance: \(error.localizedDescription)"
            }
            isLoading = false
        }
    }
}

struct WalletBalanceView: View {
    let wallet: BaseWallet
    @StateObject private var viewModel = WalletBalanceViewModel()

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Balance")
                .font(.caption)
                .foregroundColor(.secondary)

            if viewModel.isLoading {
                HStack {
                    ProgressView()
                    Text("Loading...")
                        .foregroundColor(.secondary)
                }
            } else if let balance = viewModel.balance {
                Text(balance)
                    .font(.headline)
            } else if let error = viewModel.errorMessage {
                Text(error)
                    .foregroundColor(.red)
                    .font(.caption)
            }
        }
        .onAppear {
            viewModel.fetchBalance(wallet: wallet)
        }
    }
}

EVM Balance Conversions

Wei to ETH Conversion

import BigInt

/// Convert Wei to ETH
func weiToEth(_ wei: BigUInt, decimals: Int = 6) -> String {
    let etherValue = Double(String(wei)) ?? 0.0
    let ethValue = etherValue / pow(10.0, 18.0)
    return String(format: "%.\(decimals)f", ethValue)
}

// Usage
let balanceWei: BigUInt = 1500000000000000000 // 1.5 ETH
let balanceEth = weiToEth(balanceWei)
print("Balance: \(balanceEth) ETH")

ETH to Wei Conversion

import BigInt

/// Convert ETH to Wei
func ethToWei(_ eth: Double) -> BigUInt {
    let weiValue = eth * pow(10.0, 18.0)
    return BigUInt(weiValue)
}

// Usage
let ethAmount = 1.5
let weiAmount = ethToWei(ethAmount)
print("\(ethAmount) ETH = \(weiAmount) Wei")

Gwei Conversions

import BigInt

/// Convert Wei to Gwei
func weiToGwei(_ wei: BigUInt) -> Double {
    let gweiValue = Double(String(wei)) ?? 0.0
    return gweiValue / pow(10.0, 9.0) // 1 Gwei = 10^9 Wei
}

/// Convert Gwei to Wei
func gweiToWei(_ gwei: Double) -> BigUInt {
    let weiValue = gwei * pow(10.0, 9.0)
    return BigUInt(weiValue)
}

// Usage for gas prices
let gasPriceGwei = 50.0
let gasPriceWei = gweiToWei(gasPriceGwei)
print("Gas Price: \(gasPriceGwei) Gwei = \(gasPriceWei) Wei")

Balance Formatting

import BigInt

/// Format EVM balance with appropriate units
func formatEvmBalance(_ wei: BigUInt) -> String {
    let ethValue = Double(String(wei)) ?? 0.0
    let eth = ethValue / pow(10.0, 18.0)

    if eth >= 1.0 {
        return String(format: "%.4f ETH", eth)
    } else if eth >= 0.001 {
        return String(format: "%.6f ETH", eth)
    } else {
        let gwei = ethValue / pow(10.0, 9.0)
        return String(format: "%.2f Gwei", 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
func lamportsToSol(_ lamports: UInt64, decimals: Int = 6) -> String {
    let solValue = Double(lamports) / 1_000_000_000.0
    return String(format: "%.\(decimals)f", solValue)
}

// Usage
let balanceLamports: UInt64 = 1_500_000_000 // 1.5 SOL
let balanceSol = lamportsToSol(balanceLamports)
print("Balance: \(balanceSol) SOL")

SOL to Lamports Conversion

/// Convert SOL to lamports
func solToLamports(_ sol: Double) -> UInt64 {
    return UInt64(sol * 1_000_000_000)
}

// Usage
let solAmount = 1.5
let lamportsAmount = solToLamports(solAmount)
print("\(solAmount) SOL = \(lamportsAmount) lamports")

SOL Balance Formatting

/// Format SOL balance for display
func formatSolBalance(_ lamports: UInt64) -> String {
    let sol = Double(lamports) / 1_000_000_000.0

    if sol >= 1.0 {
        return String(format: "%.4f SOL", sol)
    } else if sol >= 0.001 {
        return String(format: "%.6f SOL", sol)
    } else {
        return String(format: "%.9f SOL", sol)
    }
}

Multi-Chain Balance Display

Handle balances for both EVM and Solana wallets:
import DynamicSDKSwift

let sdk = DynamicSDK.instance()

func displayBalance(wallet: BaseWallet) async {
    do {
        let balance = try await sdk.wallets.getBalance(wallet: wallet)
        let chain = wallet.chain.uppercased()

        switch chain {
        case "EVM":
            print("Balance: \(balance) (in Wei)")
        case "SOL":
            print("Balance: \(balance) (in lamports)")
        default:
            print("Balance: \(balance)")
        }
    } catch {
        print("Failed to get balance: \(error)")
    }
}

// Display all wallet balances
func displayAllBalances() async {
    for wallet in sdk.wallets.userWallets {
        print("\(wallet.chain) wallet: \(wallet.address)")
        await displayBalance(wallet: wallet)
    }
}

Conversion Helper Class

import BigInt

/// Helper class for balance conversions
struct BalanceConverter {
    /// EVM conversions
    static func weiToEth(_ wei: BigUInt) -> Double {
        let ethValue = Double(String(wei)) ?? 0.0
        return ethValue / pow(10.0, 18.0)
    }

    static func ethToWei(_ eth: Double) -> BigUInt {
        return BigUInt(eth * pow(10.0, 18.0))
    }

    static func weiToGwei(_ wei: BigUInt) -> Double {
        let gweiValue = Double(String(wei)) ?? 0.0
        return gweiValue / pow(10.0, 9.0)
    }

    /// Solana conversions
    static func lamportsToSol(_ lamports: UInt64) -> Double {
        return Double(lamports) / 1_000_000_000.0
    }

    static func solToLamports(_ sol: Double) -> UInt64 {
        return UInt64(sol * 1_000_000_000)
    }

    /// Format for display
    static func formatBalance(
        value: String,
        chain: String,
        decimals: Int = 4
    ) -> String {
        switch chain.uppercased() {
        case "EVM":
            if let wei = BigUInt(value) {
                let eth = weiToEth(wei)
                return String(format: "%.\(decimals)f ETH", eth)
            }
        case "SOL":
            if let lamports = UInt64(value) {
                let sol = lamportsToSol(lamports)
                return String(format: "%.\(decimals)f SOL", sol)
            }
        default:
            break
        }
        return value
    }
}

// Usage
let 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

@MainActor
class BalanceCache: ObservableObject {
    @Published var balances: [String: String] = [:]

    private let sdk = DynamicSDK.instance()

    func refreshBalance(for wallet: BaseWallet) async {
        do {
            let balance = try await sdk.wallets.getBalance(wallet: wallet)
            balances[wallet.address] = balance
        } catch {
            print("Failed to refresh balance: \(error)")
        }
    }

    func refreshAllBalances() async {
        for wallet in sdk.wallets.userWallets {
            await refreshBalance(for: wallet)
        }
    }
}

2. Handle Precision

// Use BigUInt for EVM to avoid precision loss
import BigInt

let wei = BigUInt("1500000000000000000")! // 1.5 ETH
let eth = BalanceConverter.weiToEth(wei)

// Use UInt64 for Solana
let lamports: UInt64 = 1_500_000_000 // 1.5 SOL
let sol = BalanceConverter.lamportsToSol(lamports)

3. Refresh After Transactions

func sendTransactionAndRefresh(wallet: BaseWallet) async {
    do {
        // Send transaction
        let txHash = try await sendTransaction(wallet: wallet)

        // Wait a moment for blockchain confirmation
        try await Task.sleep(nanoseconds: 2_000_000_000) // 2 seconds

        // Refresh balance
        let newBalance = try await sdk.wallets.getBalance(wallet: wallet)
        print("New balance: \(newBalance)")
    } catch {
        print("Error: \(error)")
    }
}

Next Steps