Overview
This guide covers transaction operations including sending ETH/SOL, ERC20 tokens, signing transactions, and smart contract interactions with the Dynamic Swift SDK.Prerequisites
- Dynamic SDK initialized (see Installation Guide)
- User authenticated (see Authentication Guide)
- Wallets available (see Wallet Creation)
- Network configured (see Networks)
EVM Transactions
Send ETH Transaction
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func sendTransaction(wallet: BaseWallet, to: String, amountInWei: Int) async {
do {
// Get current gas price
let chainId = 1 // Ethereum mainnet, or use your target chain
let client = try await sdk.evm.createPublicClient(chainId: chainId)
let gasPrice = try await client.getGasPrice()
// Create transaction
let transaction = EthereumTransaction(
to: to,
value: amountInWei,
gasLimit: 21000, // Standard ETH transfer
maxFeePerGas: Int(gasPrice.value * 2),
maxPriorityFeePerGas: Int(gasPrice.value)
)
// Send transaction
let txHash = try await sdk.evm.sendTransaction(
transaction: transaction,
wallet: wallet
)
print("Transaction sent!")
print("Hash: \(txHash)")
} catch {
print("Transaction failed: \(error)")
}
}
Complete Send Transaction Example
Copy
Ask AI
import SwiftUI
import DynamicSDKSwift
struct SendTransactionView: View {
let wallet: BaseWallet
@State private var recipient = ""
@State private var amount = ""
@State private var txHash: String?
@State private var isLoading = false
@State private var errorMessage: String?
private let sdk = DynamicSDK.instance()
var body: some View {
VStack(spacing: 16) {
TextField("Recipient address", text: $recipient)
.textFieldStyle(.roundedBorder)
.autocapitalization(.none)
TextField("Amount (ETH)", text: $amount)
.textFieldStyle(.roundedBorder)
.keyboardType(.decimalPad)
Button("Send Transaction") {
sendTransaction()
}
.disabled(recipient.isEmpty || amount.isEmpty || isLoading)
.buttonStyle(.borderedProminent)
if isLoading {
ProgressView("Sending...")
}
if let hash = txHash {
VStack(alignment: .leading, spacing: 8) {
Text("Transaction Sent!")
.font(.headline)
.foregroundColor(.green)
Text("Hash: \(hash)")
.font(.system(.caption, design: .monospaced))
.lineLimit(2)
Button("Copy Hash") {
UIPasteboard.general.string = hash
}
.font(.caption)
}
.padding()
.background(Color.green.opacity(0.1))
.cornerRadius(8)
}
if let error = errorMessage {
Text(error)
.foregroundColor(.red)
.font(.caption)
}
}
.padding()
}
private func sendTransaction() {
guard let amountDouble = Double(amount) else {
errorMessage = "Invalid amount"
return
}
isLoading = true
errorMessage = nil
txHash = nil
Task { @MainActor in
do {
// Convert ETH to Wei (1 ETH = 10^18 Wei)
let amountInWei = Int(amountDouble * pow(10, 18))
// Get gas price
let chainId = 84532 // Base Sepolia - adjust as needed
let client = try await sdk.evm.createPublicClient(chainId: chainId)
let gasPrice = try await client.getGasPrice()
// Create and send transaction
let transaction = EthereumTransaction(
to: recipient,
value: amountInWei,
gasLimit: 21000,
maxFeePerGas: Int(gasPrice.value * 2),
maxPriorityFeePerGas: Int(gasPrice.value)
)
txHash = try await sdk.evm.sendTransaction(
transaction: transaction,
wallet: wallet
)
} catch {
errorMessage = "Failed: \(error.localizedDescription)"
}
isLoading = false
}
}
}
Sign Transaction (Without Sending)
To sign a transaction without broadcasting it:Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func signTransaction(wallet: BaseWallet) async {
do {
let transaction = EthereumTransaction(
to: "0x...",
value: 1000000000000000, // 0.001 ETH in Wei
gasLimit: 21000,
maxFeePerGas: 20000000000,
maxPriorityFeePerGas: 1000000000
)
// Sign without sending
let signedTx = try await sdk.evm.signTransaction(
transaction: transaction,
wallet: wallet
)
print("Signed transaction: \(signedTx)")
} catch {
print("Failed to sign: \(error)")
}
}
ERC20 Token Transfers
Send ERC20 Tokens
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func sendERC20(
wallet: BaseWallet,
tokenAddress: String,
recipient: String,
amount: String // Amount in token's smallest unit
) async {
do {
let input = WriteContractInput(
contractAddress: tokenAddress,
functionName: "transfer",
args: [recipient, amount],
abi: Erc20.abi
)
let txHash = try await sdk.evm.writeContract(
wallet: wallet,
input: input
)
print("ERC20 transfer sent!")
print("Hash: \(txHash)")
} catch {
print("Failed to send ERC20: \(error)")
}
}
Complete ERC20 Transfer Example
Copy
Ask AI
import SwiftUI
import DynamicSDKSwift
struct SendERC20View: View {
let wallet: BaseWallet
@State private var tokenAddress = ""
@State private var recipient = ""
@State private var amount = ""
@State private var txHash: String?
@State private var isLoading = false
@State private var errorMessage: String?
private let sdk = DynamicSDK.instance()
var body: some View {
VStack(spacing: 16) {
TextField("Token contract address", text: $tokenAddress)
.textFieldStyle(.roundedBorder)
.autocapitalization(.none)
TextField("Recipient address", text: $recipient)
.textFieldStyle(.roundedBorder)
.autocapitalization(.none)
TextField("Amount (in smallest unit)", text: $amount)
.textFieldStyle(.roundedBorder)
.keyboardType(.numberPad)
Button("Send Tokens") {
sendTokens()
}
.disabled(tokenAddress.isEmpty || recipient.isEmpty || amount.isEmpty || isLoading)
.buttonStyle(.borderedProminent)
if isLoading {
ProgressView("Sending...")
}
if let hash = txHash {
Text("Transaction: \(hash)")
.font(.caption)
.foregroundColor(.green)
}
if let error = errorMessage {
Text(error)
.foregroundColor(.red)
.font(.caption)
}
}
.padding()
}
private func sendTokens() {
isLoading = true
errorMessage = nil
txHash = nil
Task { @MainActor in
do {
let input = WriteContractInput(
contractAddress: tokenAddress,
functionName: "transfer",
args: [recipient, amount],
abi: Erc20.abi
)
txHash = try await sdk.evm.writeContract(
wallet: wallet,
input: input
)
} catch {
errorMessage = "Failed: \(error.localizedDescription)"
}
isLoading = false
}
}
}
Smart Contract Interactions
Write to Contract
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func writeContract(wallet: BaseWallet) async {
// Example: Calling a custom contract function
let customAbi = """
[
{
"inputs": [
{"name": "value", "type": "uint256"}
],
"name": "setValue",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
"""
do {
let input = WriteContractInput(
contractAddress: "0xYourContractAddress",
functionName: "setValue",
args: ["12345"],
abi: customAbi
)
let txHash = try await sdk.evm.writeContract(
wallet: wallet,
input: input
)
print("Contract interaction successful!")
print("Hash: \(txHash)")
} catch {
print("Contract call failed: \(error)")
}
}
Solana Transactions
Sign and Send Solana Transaction
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func sendSolanaTransaction(wallet: BaseWallet, base64Transaction: String) async {
do {
// Create connection and signer
let connection = try await sdk.solana.createConnection()
let signer = try await sdk.solana.createSigner(wallet: wallet)
// Get latest blockhash
let blockhash = try await connection.getLatestBlockhash()
print("Latest blockhash: \(blockhash)")
// Sign and send transaction
let signature = try await signer.signAndSendEncodedTransaction(
base64Transaction: base64Transaction
)
print("Solana transaction sent!")
print("Signature: \(signature)")
} catch {
print("Solana transaction failed: \(error)")
}
}
Sign Solana Message
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func signSolanaMessage(wallet: BaseWallet, message: String) async {
do {
let signer = try await sdk.solana.createSigner(wallet: wallet)
let signature = try await signer.signMessage(message: message)
print("Message signed!")
print("Signature: \(signature)")
} catch {
print("Failed to sign message: \(error)")
}
}
Sign Solana Transaction (Without Sending)
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func signSolanaTransaction(wallet: BaseWallet, base64Transaction: String) async {
do {
let signer = try await sdk.solana.createSigner(wallet: wallet)
let signedTx = try await signer.signEncodedTransaction(
base64Transaction: base64Transaction
)
print("Transaction signed!")
print("Signed transaction: \(signedTx)")
} catch {
print("Failed to sign transaction: \(error)")
}
}
Transaction Helpers
Get Gas Price
Copy
Ask AI
import DynamicSDKSwift
let sdk = DynamicSDK.instance()
func getGasPrice(chainId: Int) async -> Int? {
do {
let client = try await sdk.evm.createPublicClient(chainId: chainId)
let gasPrice = try await client.getGasPrice()
return Int(gasPrice.value)
} catch {
print("Failed to get gas price: \(error)")
return nil
}
}
Estimate Gas
For contract interactions, you may want to estimate gas:Copy
Ask AI
// Gas limits for common operations:
// - ETH transfer: 21000
// - ERC20 transfer: ~65000
// - Complex contract calls: varies (estimate or use higher limit)
Best Practices
1. Always Get Current Gas Price
Copy
Ask AI
let client = try await sdk.evm.createPublicClient(chainId: chainId)
let gasPrice = try await client.getGasPrice()
// Add buffer for price fluctuations
let maxFeePerGas = Int(gasPrice.value * 2)
2. Handle Transaction Errors
Copy
Ask AI
do {
let txHash = try await sdk.evm.sendTransaction(
transaction: transaction,
wallet: wallet
)
} catch {
// Common errors:
// - Insufficient funds
// - Gas price too low
// - Invalid recipient address
// - Network issues
if error.localizedDescription.contains("insufficient") {
showError("Insufficient funds for transaction")
} else {
showError("Transaction failed: \(error.localizedDescription)")
}
}
3. Show Transaction Status
Copy
Ask AI
struct TransactionStatusView: View {
let txHash: String
let chainId: Int
var explorerUrl: String {
// Map chain ID to block explorer
switch chainId {
case 1: return "https://etherscan.io/tx/\(txHash)"
case 84532: return "https://sepolia.basescan.org/tx/\(txHash)"
case 137: return "https://polygonscan.com/tx/\(txHash)"
default: return ""
}
}
var body: some View {
VStack {
Text("Transaction Submitted")
.font(.headline)
Text(txHash)
.font(.caption)
.lineLimit(1)
.truncationMode(.middle)
if !explorerUrl.isEmpty {
Link("View on Explorer", destination: URL(string: explorerUrl)!)
.font(.caption)
}
}
}
}
4. Confirm Before Sending
Copy
Ask AI
struct ConfirmTransactionView: View {
let recipient: String
let amount: String
let onConfirm: () -> Void
let onCancel: () -> Void
var body: some View {
VStack(spacing: 16) {
Text("Confirm Transaction")
.font(.headline)
VStack(alignment: .leading) {
Text("To: \(recipient)")
Text("Amount: \(amount) ETH")
}
.font(.caption)
HStack {
Button("Cancel", action: onCancel)
.buttonStyle(.bordered)
Button("Confirm", action: onConfirm)
.buttonStyle(.borderedProminent)
}
}
.padding()
}
}
Error Handling
Transaction-Related Errors
Copy
Ask AI
do {
let txHash = try await sdk.evm.sendTransaction(
transaction: transaction,
wallet: wallet
)
} catch {
let errorMessage: String
let errorDesc = error.localizedDescription.lowercased()
if errorDesc.contains("insufficient") {
errorMessage = "Insufficient balance for this transaction"
} else if errorDesc.contains("gas") {
errorMessage = "Gas estimation failed. Try increasing gas limit."
} else if errorDesc.contains("rejected") || errorDesc.contains("denied") {
errorMessage = "Transaction was rejected"
} else if errorDesc.contains("network") {
errorMessage = "Network error. Please check your connection."
} else {
errorMessage = "Transaction failed. Please try again."
}
showAlert(errorMessage)
}