Overview

This guide covers transaction operations including creation, sending, signing, and transaction management with the Dynamic Swift SDK.

Prerequisites

Transaction Operations

Create Transaction

let fromAddress = ethereumWallet.address
let toAddress = EthereumAddress("0xRecipientAddress")
let amount = BigUInt(1000000000000000000) // 1 ETH in wei
let gasPrice = try await networkClient.eth_gasPriceBigInt()
let gasLimit = BigUInt(21_000)
let chainId = SupportedEthereumNetwork.sepoliaTestnet.chainConfig.chainId

let transaction = EthereumTransaction(
    from: fromAddress,
    to: toAddress,
    value: amount,
    data: Data(),
    nonce: nil,
    gasPrice: gasPrice,
    gasLimit: gasLimit,
    chainId: chainId
)

Send ETH Transaction

// Transaction parameters
let amount = BigUInt(10000000000000000) // 0.01 ETH in wei
let recipient = EthereumAddress("0xd4f748199B91c22095150d2d4Cca3Fe6175B0CbA")
let chainId = SupportedEthereumNetwork.sepoliaTestnet.chainConfig.chainId

do {
    // Get network client for Sepolia
    let networkClient: BaseEthereumClient = try await ethereumWallet.getNetworkClient(for: chainId)

    // Get current gas price and set gas limit
    let gasPrice = try await networkClient.eth_gasPriceBigInt()
    let gasLimit = BigUInt(21_000) // Standard ETH transfer

    // Create transaction
    let transaction = EthereumTransaction(
        from: ethereumWallet.address,
        to: recipient,
        value: amount,
        data: Data(),
        nonce: nil,
        gasPrice: gasPrice,
        gasLimit: gasLimit,
        chainId: chainId
    )

    // Send transaction
    let txHash = try await ethereumWallet.sendTransaction(transaction)

    print("✅ Transaction sent!")
    print("🔗 Transaction Hash: \(txHash)")

    // Get network details for block explorer URL
    if let supportedNetwork = SupportedEthereumNetwork.fromChainId(chainId) {
        let networkConfig = supportedNetwork.chainConfig
        if let explorerUrl = networkConfig.blockExplorerUrls.first {
            print("🔍 View on \(networkConfig.name): \(explorerUrl)/tx/\(txHash)")
        }
    }
} catch {
    print("❌ Transaction failed: \(error)")
}

Send Transaction

do {
    let txHash = try await ethereumWallet.sendTransaction(transaction)
    print("Transaction Hash: \(txHash)")
    
    // Get network details for block explorer URL
    if let supportedNetwork = SupportedEthereumNetwork.fromChainId(chainId) {
        let networkConfig = supportedNetwork.chainConfig
        if let explorerUrl = networkConfig.blockExplorerUrls.first {
            print("🔍 View on \(networkConfig.name): \(explorerUrl)/tx/\(txHash)")
        }
    }
} catch {
    print("Transaction failed: \(error)")
}

Sign Transaction

do {
    let signedTransaction = try await ethereumWallet.sign(transaction: transaction)
    print("Transaction signed successfully")
    print("Signature: \(signedTransaction)")
} catch {
    print("Failed to sign transaction: \(error)")
}

Transaction Types

ETH Transfer Transaction

// Simple ETH transfer
func createETHTransfer(
    to recipient: String,
    amount: BigUInt,
    gasPrice: BigUInt,
    gasLimit: BigUInt = BigUInt(21_000)
) -> EthereumTransaction {
    
    let recipientAddress = EthereumAddress(recipient)
    
    return EthereumTransaction(
        from: ethereumWallet.address,
        to: recipientAddress,
        value: amount,
        data: Data(), // Empty data for ETH transfer
        nonce: nil,
        gasPrice: gasPrice,
        gasLimit: gasLimit,
        chainId: SupportedEthereumNetwork.sepoliaTestnet.chainConfig.chainId
    )
}

// Usage
let transaction = createETHTransfer(
    to: "0xRecipientAddress",
    amount: BigUInt(1000000000000000000), // 1 ETH
    gasPrice: gasPrice
)

Contract Interaction Transaction

// Contract interaction with data
func createContractTransaction(
    to contractAddress: String,
    data: Data,
    value: BigUInt = BigUInt(0),
    gasPrice: BigUInt,
    gasLimit: BigUInt
) -> EthereumTransaction {
    
    let contractAddr = EthereumAddress(contractAddress)
    
    return EthereumTransaction(
        from: ethereumWallet.address,
        to: contractAddr,
        value: value,
        data: data, // Contract function call data
        nonce: nil,
        gasPrice: gasPrice,
        gasLimit: gasLimit,
        chainId: SupportedEthereumNetwork.sepoliaTestnet.chainConfig.chainId
    )
}

// Usage
let contractData = Data() // ABI-encoded function call
let transaction = createContractTransaction(
    to: "0xContractAddress",
    data: contractData,
    gasPrice: gasPrice,
    gasLimit: BigUInt(100_000)
)

Transaction Management

Transaction Builder

// Transaction builder for complex transactions
class TransactionBuilder {
    private var from: EthereumAddress?
    private var to: EthereumAddress?
    private var value: BigUInt = BigUInt(0)
    private var data: Data = Data()
    private var gasPrice: BigUInt?
    private var gasLimit: BigUInt?
    private var chainId: UInt64?
    
    func from(_ address: EthereumAddress) -> TransactionBuilder {
        self.from = address
        return self
    }
    
    func to(_ address: EthereumAddress) -> TransactionBuilder {
        self.to = address
        return self
    }
    
    func value(_ amount: BigUInt) -> TransactionBuilder {
        self.value = amount
        return self
    }
    
    func data(_ transactionData: Data) -> TransactionBuilder {
        self.data = transactionData
        return self
    }
    
    func gasPrice(_ price: BigUInt) -> TransactionBuilder {
        self.gasPrice = price
        return self
    }
    
    func gasLimit(_ limit: BigUInt) -> TransactionBuilder {
        self.gasLimit = limit
        return self
    }
    
    func chainId(_ id: UInt64) -> TransactionBuilder {
        self.chainId = id
        return self
    }
    
    func build() -> EthereumTransaction? {
        guard let from = from,
              let to = to,
              let gasPrice = gasPrice,
              let gasLimit = gasLimit,
              let chainId = chainId else {
            return nil
        }
        
        return EthereumTransaction(
            from: from,
            to: to,
            value: value,
            data: data,
            nonce: nil,
            gasPrice: gasPrice,
            gasLimit: gasLimit,
            chainId: chainId
        )
    }
}

// Usage
let transaction = TransactionBuilder()
    .from(ethereumWallet.address)
    .to(EthereumAddress("0xRecipientAddress"))
    .value(BigUInt(1000000000000000000))
    .gasPrice(gasPrice)
    .gasLimit(BigUInt(21_000))
    .chainId(SupportedEthereumNetwork.sepoliaTestnet.chainConfig.chainId)
    .build()

Transaction Validation

// Validate transaction before sending
func validateTransaction(_ transaction: EthereumTransaction) -> Bool {
    // Check if addresses are valid
    guard transaction.from != EthereumAddress.zero,
          transaction.to != EthereumAddress.zero else {
        print("❌ Invalid addresses")
        return false
    }
    
    // Check if gas price is reasonable
    guard transaction.gasPrice > BigUInt(0) else {
        print("❌ Invalid gas price")
        return false
    }
    
    // Check if gas limit is reasonable
    guard transaction.gasLimit >= BigUInt(21_000) else {
        print("❌ Gas limit too low")
        return false
    }
    
    // Check if chain ID is valid
    guard transaction.chainId > 0 else {
        print("❌ Invalid chain ID")
        return false
    }
    
    return true
}

// Usage
guard validateTransaction(transaction) else {
    print("❌ Transaction validation failed")
    return
}

Transaction Monitoring

Track Transaction Status

// Monitor transaction confirmation
func monitorTransaction(_ txHash: String) async {
    let networkClient = try await ethereumWallet.getNetworkClient(for: chainId)
    
    while true {
        do {
            let receipt = try await networkClient.eth_getTransactionReceipt(txHash)
            
            if let receipt = receipt {
                print("✅ Transaction confirmed!")
                print("Block number: \(receipt.blockNumber)")
                print("Gas used: \(receipt.gasUsed)")
                print("Status: \(receipt.status == 1 ? "Success" : "Failed")")
                break
            } else {
                print("⏳ Transaction pending...")
                try await Task.sleep(nanoseconds: 5_000_000_000) // Wait 5 seconds
            }
        } catch {
            print("❌ Failed to check transaction: \(error)")
            break
        }
    }
}

// Usage
let txHash = try await ethereumWallet.sendTransaction(transaction)
await monitorTransaction(txHash)

Best Practices

1. Always Validate Transactions

// Validate before sending
guard validateTransaction(transaction) else {
    print("❌ Transaction validation failed")
    return
}

// Send transaction
let txHash = try await ethereumWallet.sendTransaction(transaction)

2. Use Transaction Builder

// Use builder pattern for complex transactions
let transaction = TransactionBuilder()
    .from(ethereumWallet.address)
    .to(recipientAddress)
    .value(amount)
    .gasPrice(gasPrice)
    .gasLimit(gasLimit)
    .chainId(chainId)
    .build()

3. Monitor Transaction Status

// Always monitor transaction status
let txHash = try await ethereumWallet.sendTransaction(transaction)
await monitorTransaction(txHash)

4. Explorer URL Generation

Generate dynamic block explorer URLs for transaction tracking:
let txHash = try await ethereumWallet.sendTransaction(transaction)
print("✅ Transaction sent!")
print("🔗 Transaction Hash: \(txHash)")

// Get network details for block explorer URL
if let supportedNetwork = SupportedEthereumNetwork.fromChainId(chainId) {
    let networkConfig = supportedNetwork.chainConfig
    if let explorerUrl = networkConfig.blockExplorerUrls.first {
        print("🔍 View on \(networkConfig.name): \(explorerUrl)/tx/\(txHash)")
    }
}

Error Handling

do {
    let txHash = try await ethereumWallet.sendTransaction(transaction)
} catch {
    if let nsError = error as NSError? {
        switch nsError.code {
        case 2001:
            print("Insufficient balance for transaction")
        case 2002:
            print("Invalid transaction parameters")
        case 2003:
            print("Network connection failed")
        case 2004:
            print("Gas price too low")
        case 2005:
            print("Transaction rejected by network")
        default:
            print("Transaction error: \(error)")
        }
    }
}

Next Steps

After mastering transactions, you can: