Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Send ETH by creating transactions, estimating gas, and handling responses.

Prerequisites

Send ETH Transaction

import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.BaseWallet
import com.dynamic.sdk.android.Chains.EVM.EthereumTransaction
import com.dynamic.sdk.android.Chains.EVM.convertEthToWei
import kotlinx.coroutines.launch
import java.math.BigInteger

val sdk = DynamicSDK.getInstance()

suspend fun sendTransaction(wallet: BaseWallet, to: String, amountInEth: String) {
    try {
        val chainId = 1
        val client = sdk.evm.createPublicClient(chainId)
        val gasPrice = client.getGasPrice()
        val maxFeePerGas = gasPrice * BigInteger.valueOf(2)
        val maxPriorityFeePerGas = gasPrice
        val weiAmount = convertEthToWei(amountInEth)

        val transaction = EthereumTransaction(
            from = wallet.address,
            to = to,
            value = weiAmount,
            gas = BigInteger.valueOf(21000),
            maxFeePerGas = maxFeePerGas,
            maxPriorityFeePerGas = maxPriorityFeePerGas
        )

        val txHash = sdk.evm.sendTransaction(transaction, wallet)
        println("Transaction sent!")
        println("Hash: $txHash")
    } catch (e: Exception) {
        println("Transaction failed: ${e.message}")
    }
}

Sign Transaction (Without Sending)

import com.dynamic.sdk.android.Chains.EVM.signEthereumTransaction

suspend fun signTransactionOnly(wallet: BaseWallet) {
    try {
        val signedTx = sdk.wallets.signEthereumTransaction(
            wallet = wallet,
            to = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bDd7",
            value = "0.001",
            gasLimit = "21000",
            maxPriorityFeePerGas = "2",
            maxFeePerGas = "50"
        )
        println("Signed transaction: $signedTx")
    } catch (e: Exception) {
        println("Failed to sign: ${e.message}")
    }
}

Best Practices

  • Always get current gas price before sending
  • Add buffer for price fluctuations (2x multiplier)
  • Handle common errors: insufficient funds, gas too low, invalid address
  • Validate address format before sending
  • Show transaction confirmations to users
  • Display transaction status with explorer links

Handle Transaction Errors

try {
    val txHash = sdk.evm.sendTransaction(transaction, wallet)
} catch (e: Exception) {
    when {
        e.message?.contains("insufficient", ignoreCase = true) == true -> {
            showError("Insufficient funds for transaction")
        }
        e.message?.contains("gas", ignoreCase = true) == true -> {
            showError("Gas estimation failed. Try increasing gas limit.")
        }
        else -> {
            showError("Transaction failed: ${e.message}")
        }
    }
}

Show Transaction Status

@Composable
fun TransactionStatusView(txHash: String, chainId: Int) {
    val explorerUrl = when (chainId) {
        1 -> "https://etherscan.io/tx/$txHash"
        11155111 -> "https://sepolia.etherscan.io/tx/$txHash"
        84532 -> "https://sepolia.basescan.org/tx/$txHash"
        137 -> "https://polygonscan.com/tx/$txHash"
        else -> null
    }

    Column {
        Text("Transaction Submitted", style = MaterialTheme.typography.headlineSmall)
        Text(
            text = txHash,
            style = MaterialTheme.typography.bodySmall,
            maxLines = 1
        )

        explorerUrl?.let { url ->
            Button(onClick = { /* Open URL */ }) {
                Text("View on Explorer")
            }
        }
    }
}

Validate Address Format

fun isValidEthereumAddress(address: String): Boolean {
    return address.matches(Regex("^0x[a-fA-F0-9]{40}$"))
}

Error Handling

Common Transaction Errors

sealed class TransactionError {
    data class InsufficientFunds(val message: String) : TransactionError()
    data class GasEstimationFailed(val message: String) : TransactionError()
    data class UserRejected(val message: String) : TransactionError()
    data class NetworkError(val message: String) : TransactionError()
    data class InvalidAddress(val message: String) : TransactionError()
    data class Unknown(val message: String) : TransactionError()
}

fun parseTransactionError(e: Exception): TransactionError {
    val errorMessage = e.message?.lowercase() ?: ""

    return when {
        errorMessage.contains("insufficient") ->
            TransactionError.InsufficientFunds("Insufficient balance for this transaction")
        errorMessage.contains("gas") ->
            TransactionError.GasEstimationFailed("Gas estimation failed. Try increasing gas limit.")
        errorMessage.contains("rejected") || errorMessage.contains("denied") ->
            TransactionError.UserRejected("Transaction was rejected")
        errorMessage.contains("network") ->
            TransactionError.NetworkError("Network error. Please check your connection.")
        errorMessage.contains("invalid") && errorMessage.contains("address") ->
            TransactionError.InvalidAddress("Invalid recipient address")
        else ->
            TransactionError.Unknown("Transaction failed. Please try again.")
    }
}

// Usage in ViewModel
try {
    val txHash = sdk.evm.sendTransaction(transaction, wallet)
    _transactionHash.value = txHash
} catch (e: Exception) {
    val error = parseTransactionError(e)
    _errorMessage.value = when (error) {
        is TransactionError.InsufficientFunds -> error.message
        is TransactionError.GasEstimationFailed -> error.message
        is TransactionError.UserRejected -> error.message
        is TransactionError.NetworkError -> error.message
        is TransactionError.InvalidAddress -> error.message
        is TransactionError.Unknown -> error.message
    }
}

Troubleshooting

Transaction not confirming: Increase gas price multiplier to 3x Insufficient funds: Check balance includes gas fees before sending Nonce issues: SDK handles nonces automatically, manually specify only if needed

What’s Next