Skip to main content

Overview

This guide covers how wallets are created and managed with the Dynamic Swift SDK. Wallets are automatically created for users after authentication when embedded wallets are enabled in your Dynamic dashboard.

Prerequisites

Automatic Wallet Creation

When embedded wallets are enabled, wallets are automatically created for users after they authenticate. You don’t need to manually create wallets - the SDK handles this for you.

Listening for Wallet Creation

Use the userWalletsChanges publisher to know when wallets are ready:
import DynamicSDKSwift
import Combine

@MainActor
class WalletManager: ObservableObject {
    @Published var wallets: [BaseWallet] = []
    @Published var isCreatingWallets = false

    private let sdk = DynamicSDK.instance()
    private var cancellables = Set<AnyCancellable>()

    func startListening() {
        // Get current wallets
        wallets = sdk.wallets.userWallets

        // If user is authenticated but no wallets yet, they're being created
        if sdk.auth.authenticatedUser != nil && wallets.isEmpty {
            isCreatingWallets = true
        }

        // Listen for wallet updates
        sdk.wallets.userWalletsChanges
            .receive(on: DispatchQueue.main)
            .sink { [weak self] newWallets in
                guard let self else { return }
                self.wallets = newWallets

                if !newWallets.isEmpty {
                    self.isCreatingWallets = false
                }
            }
            .store(in: &cancellables)
    }
}

Displaying Wallets

import SwiftUI
import DynamicSDKSwift

struct WalletsView: View {
    @StateObject private var walletManager = WalletManager()

    var body: some View {
        VStack {
            if walletManager.isCreatingWallets {
                HStack {
                    ProgressView()
                    Text("Creating wallets...")
                }
            } else if walletManager.wallets.isEmpty {
                Text("No wallets available")
            } else {
                ForEach(walletManager.wallets, id: \.address) { wallet in
                    WalletCard(wallet: wallet)
                }
            }
        }
        .onAppear {
            walletManager.startListening()
        }
    }
}

struct WalletCard: View {
    let wallet: BaseWallet

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                Text(wallet.walletName ?? "Wallet")
                    .font(.headline)
                Spacer()
                Text(wallet.chain.uppercased())
                    .font(.caption)
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
                    .background(Color.blue.opacity(0.2))
                    .cornerRadius(4)
            }

            Text(wallet.address)
                .font(.caption)
                .foregroundColor(.secondary)
                .lineLimit(1)
                .truncationMode(.middle)
        }
        .padding()
        .background(Color(.systemGray6))
        .cornerRadius(12)
    }
}

Wallet Types

The SDK supports multiple wallet types:

EVM Wallets

For Ethereum and EVM-compatible chains (Polygon, Base, Arbitrum, etc.):
let evmWallets = sdk.wallets.userWallets.filter {
    $0.chain.uppercased() == "EVM"
}

Solana Wallets

For Solana blockchain:
let solanaWallets = sdk.wallets.userWallets.filter {
    $0.chain.uppercased() == "SOL"
}

Wallet Properties

Each BaseWallet has the following properties:
let wallet: BaseWallet

// Wallet address
let address = wallet.address  // "0x..." for EVM, base58 for Solana

// Chain type
let chain = wallet.chain  // "EVM" or "SOL"

// Wallet name (optional)
let name = wallet.walletName  // e.g., "turnkey"

// Wallet ID (for API operations)
let id = wallet.id  // Used for setPrimary, etc.
For complete wallet management API documentation including all available methods, see the Wallet Management Reference.

Setting Primary Wallet

You can set a wallet as the user’s primary wallet:
import DynamicSDKSwift

let sdk = DynamicSDK.instance()

func setPrimaryWallet(wallet: BaseWallet) async {
    guard let walletId = wallet.id else {
        print("Wallet has no ID")
        return
    }

    do {
        try await sdk.wallets.setPrimary(walletId: walletId)
        print("Primary wallet set successfully")
    } catch {
        print("Failed to set primary wallet: \(error)")
    }
}

Multi-Chain Support

When you have both EVM and Solana enabled in your dashboard, users will automatically get wallets for both chains:
import SwiftUI
import DynamicSDKSwift

struct MultiChainWalletsView: View {
    let wallets: [BaseWallet]

    var evmWallets: [BaseWallet] {
        wallets.filter { $0.chain.uppercased() == "EVM" }
    }

    var solanaWallets: [BaseWallet] {
        wallets.filter { $0.chain.uppercased() == "SOL" }
    }

    var body: some View {
        List {
            if !evmWallets.isEmpty {
                Section("EVM Wallets") {
                    ForEach(evmWallets, id: \.address) { wallet in
                        WalletRow(wallet: wallet)
                    }
                }
            }

            if !solanaWallets.isEmpty {
                Section("Solana Wallets") {
                    ForEach(solanaWallets, id: \.address) { wallet in
                        WalletRow(wallet: wallet)
                    }
                }
            }
        }
    }
}

struct WalletRow: View {
    let wallet: BaseWallet

    var body: some View {
        VStack(alignment: .leading) {
            Text(wallet.walletName ?? "Wallet")
                .font(.headline)
            Text(wallet.address)
                .font(.caption)
                .foregroundColor(.secondary)
        }
    }
}

Complete Example with Timeout Handling

Handle cases where wallet creation might take time or fail:
@MainActor
class WalletManager: ObservableObject {
    @Published var wallets: [BaseWallet] = []
    @Published var isCreatingWallets = false
    @Published var creationTimedOut = false

    private let sdk = DynamicSDK.instance()
    private var cancellables = Set<AnyCancellable>()
    private var timeoutTask: Task<Void, Never>?

    func startListening() {
        wallets = sdk.wallets.userWallets

        if sdk.auth.authenticatedUser != nil && wallets.isEmpty {
            isCreatingWallets = true

            // Set a timeout for wallet creation
            timeoutTask = Task {
                try? await Task.sleep(nanoseconds: 15_000_000_000) // 15 seconds
                if wallets.isEmpty {
                    isCreatingWallets = false
                    creationTimedOut = true
                }
            }
        }

        sdk.wallets.userWalletsChanges
            .receive(on: DispatchQueue.main)
            .sink { [weak self] newWallets in
                guard let self else { return }
                self.wallets = newWallets

                if !newWallets.isEmpty {
                    self.isCreatingWallets = false
                    self.creationTimedOut = false
                    self.timeoutTask?.cancel()
                }
            }
            .store(in: &cancellables)
    }
}

Next Steps

After wallets are created, you can: