Overview
This guide covers sending SOL, building transactions, and signing transactions on Solana.Prerequisites
- Dynamic SDK initialized (see Quickstart)
- User authenticated (see Authentication)
- Solana wallet available (see Wallet Creation)
- SolanaWeb3 package installed
Send SOL
import DynamicSDKSwift
import SolanaWeb3
let sdk = DynamicSDK.instance()
func sendSOL(
wallet: BaseWallet,
recipient: String,
amountInSOL: Double
) async throws -> String {
// Create connection and signer
let connection = try sdk.solana.createConnection()
let signer = sdk.solana.createSigner(wallet: wallet)
// Get latest blockhash
let blockhash = try await connection.getLatestBlockhash()
// Convert SOL to lamports (1 SOL = 10^9 lamports)
let lamports = UInt64(amountInSOL * 1_000_000_000)
// Create versioned transfer transaction using the builder
let transaction = try SolanaWeb3.SolanaTransactionBuilder.createVersionedTransferTransaction(
from: wallet.address,
to: recipient,
lamports: lamports,
recentBlockhash: blockhash.blockhash
)
// Sign and send
return try await signer.signAndSendEncodedTransaction(
base64Transaction: transaction.serializeToBase64()
)
}
SolanaWeb3 Dependency: Add the
SolanaWeb3 package from solana-mobile/SolanaSwift to build Solana transactions.Sign Transaction
Sign a base64-encoded transaction without sending:let sdk = DynamicSDK.instance()
func signTransaction(wallet: BaseWallet, base64Transaction: String) async throws -> String {
let signer = sdk.solana.createSigner(wallet: wallet)
return try await signer.signEncodedTransaction(base64Transaction: base64Transaction)
}
Sign and Send Pre-Built Transaction
If you already have a base64-encoded transaction:let sdk = DynamicSDK.instance()
func signAndSendTransaction(wallet: BaseWallet, base64Transaction: String) async throws -> String {
let signer = sdk.solana.createSigner(wallet: wallet)
return try await signer.signAndSendEncodedTransaction(base64Transaction: base64Transaction)
}
Complete Send SOL View
import SwiftUI
import DynamicSDKSwift
import SolanaWeb3
struct SolanaSendView: View {
let wallet: BaseWallet
@State private var recipient = ""
@State private var amount = ""
@State private var signature: String?
@State private var isLoading = false
@State private var error: String?
private let sdk = DynamicSDK.instance()
var body: some View {
VStack(spacing: 16) {
Text("From: \(formatAddress(wallet.address))")
.font(.caption)
.foregroundColor(.secondary)
TextField("Recipient Address", text: $recipient)
.textFieldStyle(.roundedBorder)
.autocapitalization(.none)
TextField("Amount (SOL)", text: $amount)
.textFieldStyle(.roundedBorder)
.keyboardType(.decimalPad)
Button("Send SOL") { sendTransaction() }
.buttonStyle(.borderedProminent)
.disabled(recipient.isEmpty || amount.isEmpty || isLoading)
if isLoading {
ProgressView("Sending...")
}
if let signature = signature {
VStack(spacing: 8) {
Text("Success!")
.foregroundColor(.green)
.fontWeight(.semibold)
Text(signature)
.font(.caption)
.lineLimit(1)
Link("View on Explorer",
destination: URL(string: "https://explorer.solana.com/tx/\(signature)?cluster=devnet")!)
.font(.caption)
}
.padding()
.background(Color.green.opacity(0.1))
.cornerRadius(8)
}
if let error = error {
Text(error)
.foregroundColor(.red)
.font(.caption)
}
}
.padding()
.navigationTitle("Send SOL")
}
private func formatAddress(_ address: String) -> String {
"\(address.prefix(6))...\(address.suffix(4))"
}
private func sendTransaction() {
guard let amountDouble = Double(amount) else {
error = "Invalid amount"
return
}
isLoading = true
error = nil
signature = nil
Task { @MainActor in
do {
// Create connection and signer
let connection = try sdk.solana.createConnection()
let signer = sdk.solana.createSigner(wallet: wallet)
// Get latest blockhash
let blockhash = try await connection.getLatestBlockhash()
// Convert SOL to lamports
let lamports = UInt64(amountDouble * 1_000_000_000)
// Create versioned transfer transaction using the builder
let transaction = try SolanaWeb3.SolanaTransactionBuilder.createVersionedTransferTransaction(
from: wallet.address,
to: recipient,
lamports: lamports,
recentBlockhash: blockhash.blockhash
)
// Sign and send
signature = try await signer.signAndSendEncodedTransaction(
base64Transaction: transaction.serializeToBase64()
)
} catch {
self.error = error.localizedDescription
}
isLoading = false
}
}
}
Lamports Conversion
Solana uses lamports as its smallest unit (1 SOL = 10^9 lamports):// Convert SOL to lamports
func solToLamports(_ sol: Double) -> UInt64 {
return UInt64(sol * 1_000_000_000)
}
// Convert lamports to SOL
func lamportsToSol(_ lamports: UInt64) -> Double {
return Double(lamports) / 1_000_000_000
}
// Usage
let amount = 1.5 // SOL
let lamports = solToLamports(amount)
print("\(amount) SOL = \(lamports) lamports")
Transaction Explorer Links
func explorerURL(signature: String, cluster: String = "devnet") -> URL? {
URL(string: "https://explorer.solana.com/tx/\(signature)?cluster=\(cluster)")
}
// Usage
if let url = explorerURL(signature: txSignature, cluster: "mainnet-beta") {
// Open in browser or display link
}
Signer Methods
| Method | Description |
|---|---|
createSigner(wallet:) | Create a signer for a Solana wallet |
signMessage(message:) | Sign an arbitrary message |
signEncodedTransaction(base64Transaction:) | Sign a transaction without sending |
signAndSendEncodedTransaction(base64Transaction:) | Sign and broadcast a transaction |
Error Handling
do {
let signature = try await sendSOL(
wallet: wallet,
recipient: recipient,
amountInSOL: 1.0
)
} catch {
let errorDesc = error.localizedDescription.lowercased()
if errorDesc.contains("insufficient") {
print("Insufficient balance")
} else if errorDesc.contains("blockhash") {
print("Blockhash expired, try again")
} else {
print("Transaction failed: \(error)")
}
}
Devnet Faucet
For testing, get free devnet SOL from the Solana Faucet.Next Steps
- Solana Connection - Connection setup
- Sign Solana Messages - Sign messages
- Token Balances - Get SOL balance