Skip to main content

Overview

Estimate gas prices, manage EIP-1559 fees, and optimize transaction costs.

Prerequisites

Get Current Gas Price

import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.BaseWallet
import java.math.BigInteger

val sdk = DynamicSDK.getInstance()

suspend fun getCurrentGasPrice(chainId: Int): BigInteger {
    val client = sdk.evm.createPublicClient(chainId)
    val gasPrice = client.getGasPrice()
    println("Current gas price: $gasPrice Wei")
    return gasPrice
}

EIP-1559 Fee Management

import com.dynamic.sdk.android.Chains.EVM.EthereumTransaction
import com.dynamic.sdk.android.Chains.EVM.convertEthToWei

suspend fun createEip1559Transaction(
    wallet: BaseWallet,
    to: String,
    amount: String,
    chainId: Int
): EthereumTransaction {
    val client = sdk.evm.createPublicClient(chainId)
    val gasPrice = client.getGasPrice()
    val maxPriorityFeePerGas = gasPrice
    val maxFeePerGas = gasPrice * BigInteger.valueOf(2)

    return EthereumTransaction(
        from = wallet.address,
        to = to,
        value = convertEthToWei(amount),
        gas = BigInteger.valueOf(21000),
        maxFeePerGas = maxFeePerGas,
        maxPriorityFeePerGas = maxPriorityFeePerGas
    )
}

Gas Estimation ViewModel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.BaseWallet
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.math.BigDecimal
import java.math.BigInteger

data class GasEstimate(
    val gasLimit: BigInteger,
    val gasPrice: BigInteger,
    val maxFeePerGas: BigInteger,
    val maxPriorityFeePerGas: BigInteger,
    val estimatedCostInWei: BigInteger,
    val estimatedCostInEth: String
)

class GasEstimationViewModel(private val wallet: BaseWallet) : ViewModel() {
    private val sdk = DynamicSDK.getInstance()

    private val _gasEstimate = MutableStateFlow<GasEstimate?>(null)
    val gasEstimate: StateFlow<GasEstimate?> = _gasEstimate.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    private val _errorMessage = MutableStateFlow<String?>(null)
    val errorMessage: StateFlow<String?> = _errorMessage.asStateFlow()

    fun estimateGas(
        chainId: Int,
        gasLimit: BigInteger = BigInteger.valueOf(21000)
    ) {
        viewModelScope.launch {
            _isLoading.value = true
            _errorMessage.value = null

            try {
                val client = sdk.evm.createPublicClient(chainId)
                val gasPrice = client.getGasPrice()
                val maxPriorityFeePerGas = gasPrice
                val maxFeePerGas = gasPrice * BigInteger.valueOf(2)
                val estimatedCostWei = gasLimit * maxFeePerGas
                val weiPerEth = BigDecimal("1000000000000000000")
                val estimatedCostEth = BigDecimal(estimatedCostWei).divide(weiPerEth).toPlainString()

                _gasEstimate.value = GasEstimate(
                    gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas,
                    estimatedCostWei, estimatedCostEth
                )
            } catch (e: Exception) {
                _errorMessage.value = "Failed to estimate gas: ${e.message}"
            }

            _isLoading.value = false
        }
    }

    fun convertWeiToGwei(wei: BigInteger): String {
        val weiPerGwei = BigDecimal("1000000000")
        return BigDecimal(wei).divide(weiPerGwei).toPlainString()
    }

    fun convertGweiToWei(gwei: String): BigInteger {
        val weiPerGwei = BigDecimal("1000000000")
        return BigDecimal(gwei).multiply(weiPerGwei).toBigInteger()
    }
}

Jetpack Compose UI for Gas Display

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun GasEstimateCard(gasEstimate: GasEstimate?) {
    gasEstimate?.let { estimate ->
        Card(
            modifier = Modifier.fillMaxWidth(),
            colors = CardDefaults.cardColors(
                containerColor = MaterialTheme.colorScheme.surfaceVariant
            )
        ) {
            Column(modifier = Modifier.padding(16.dp)) {
                Text(
                    text = "Gas Estimate",
                    style = MaterialTheme.typography.titleMedium
                )

                Spacer(modifier = Modifier.height(12.dp))

                GasInfoRow(
                    label = "Gas Limit",
                    value = estimate.gasLimit.toString()
                )

                GasInfoRow(
                    label = "Max Fee Per Gas",
                    value = "${convertWeiToGwei(estimate.maxFeePerGas)} Gwei"
                )

                GasInfoRow(
                    label = "Max Priority Fee",
                    value = "${convertWeiToGwei(estimate.maxPriorityFeePerGas)} Gwei"
                )

                Divider(modifier = Modifier.padding(vertical = 8.dp))

                GasInfoRow(
                    label = "Estimated Cost",
                    value = "${estimate.estimatedCostInEth} ETH",
                    valueStyle = MaterialTheme.typography.titleSmall
                )
            }
        }
    }
}

@Composable
fun GasInfoRow(
    label: String,
    value: String,
    valueStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyMedium
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            text = label,
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant
        )
        Text(
            text = value,
            style = valueStyle
        )
    }
}

fun convertWeiToGwei(wei: BigInteger): String {
    val weiPerGwei = BigDecimal("1000000000")
    return BigDecimal(wei).divide(weiPerGwei).setScale(2).toPlainString()
}

Gas Optimization Strategies

1. Standard Gas Limits by Transaction Type

object GasLimits {
    val ETH_TRANSFER = BigInteger.valueOf(21000)
    val ERC20_TRANSFER = BigInteger.valueOf(65000)
    val ERC721_TRANSFER = BigInteger.valueOf(85000)
    val UNISWAP_SWAP = BigInteger.valueOf(200000)
    val CONTRACT_DEPLOYMENT = BigInteger.valueOf(1000000)

    fun forTransactionType(type: TransactionType): BigInteger {
        return when (type) {
            TransactionType.ETH_TRANSFER -> ETH_TRANSFER
            TransactionType.ERC20_TRANSFER -> ERC20_TRANSFER
            TransactionType.ERC721_TRANSFER -> ERC721_TRANSFER
            TransactionType.UNISWAP_SWAP -> UNISWAP_SWAP
            TransactionType.CONTRACT_DEPLOYMENT -> CONTRACT_DEPLOYMENT
        }
    }
}

enum class TransactionType {
    ETH_TRANSFER,
    ERC20_TRANSFER,
    ERC721_TRANSFER,
    UNISWAP_SWAP,
    CONTRACT_DEPLOYMENT
}

2. Dynamic Gas Price Adjustment

suspend fun getGasWithStrategy(
    chainId: Int,
    strategy: GasStrategy
): Pair<BigInteger, BigInteger> {
    val client = sdk.evm.createPublicClient(chainId)
    val baseGasPrice = client.getGasPrice()

    return when (strategy) {
        GasStrategy.SLOW -> {
            val maxFeePerGas = baseGasPrice * BigInteger.valueOf(1)
            val maxPriorityFeePerGas = baseGasPrice / BigInteger.valueOf(2)
            Pair(maxFeePerGas, maxPriorityFeePerGas)
        }
        GasStrategy.STANDARD -> {
            val maxFeePerGas = baseGasPrice * BigInteger.valueOf(2)
            val maxPriorityFeePerGas = baseGasPrice
            Pair(maxFeePerGas, maxPriorityFeePerGas)
        }
        GasStrategy.FAST -> {
            val maxFeePerGas = baseGasPrice * BigInteger.valueOf(3)
            val maxPriorityFeePerGas = baseGasPrice * BigInteger.valueOf(2)
            Pair(maxFeePerGas, maxPriorityFeePerGas)
        }
    }
}

enum class GasStrategy {
    SLOW,
    STANDARD,
    FAST
}

3. Gas Strategy Selector UI

@Composable
fun GasStrategySelector(
    selectedStrategy: GasStrategy,
    onStrategyChange: (GasStrategy) -> Unit,
    gasEstimates: Map<GasStrategy, String>
) {
    Column(modifier = Modifier.fillMaxWidth()) {
        Text(
            text = "Transaction Speed",
            style = MaterialTheme.typography.titleMedium
        )

        Spacer(modifier = Modifier.height(8.dp))

        GasStrategy.values().forEach { strategy ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 4.dp),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                RadioButton(
                    selected = selectedStrategy == strategy,
                    onClick = { onStrategyChange(strategy) }
                )

                Column(modifier = Modifier.weight(1f)) {
                    Text(
                        text = strategy.name.lowercase().replaceFirstChar { it.uppercase() },
                        style = MaterialTheme.typography.bodyLarge
                    )
                    Text(
                        text = gasEstimates[strategy] ?: "Calculating...",
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.onSurfaceVariant
                    )
                }
            }
        }
    }
}

Unit Conversion Utilities

object GasConverter {
    private val WEI_PER_GWEI = BigDecimal("1000000000")
    private val WEI_PER_ETH = BigDecimal("1000000000000000000")

    fun weiToGwei(wei: BigInteger): String {
        return BigDecimal(wei)
            .divide(WEI_PER_GWEI)
            .setScale(2, java.math.RoundingMode.HALF_UP)
            .toPlainString()
    }

    fun gweiToWei(gwei: String): BigInteger {
        return BigDecimal(gwei)
            .multiply(WEI_PER_GWEI)
            .toBigInteger()
    }

    fun weiToEth(wei: BigInteger): String {
        return BigDecimal(wei)
            .divide(WEI_PER_ETH)
            .setScale(6, java.math.RoundingMode.HALF_UP)
            .toPlainString()
    }

    fun ethToWei(eth: String): BigInteger {
        return BigDecimal(eth)
            .multiply(WEI_PER_ETH)
            .toBigInteger()
    }

    fun calculateTransactionCost(gasLimit: BigInteger, gasPrice: BigInteger): String {
        val costInWei = gasLimit * gasPrice
        return weiToEth(costInWei)
    }
}

// Usage
val gasPriceInGwei = GasConverter.weiToGwei(gasPrice)
val costInEth = GasConverter.calculateTransactionCost(gasLimit, maxFeePerGas)

Best Practices

  • Always show gas estimates before transactions
  • Include total cost (amount + gas) in confirmations
  • Adjust gas price based on urgency (low/medium/high)
  • Set maximum gas limits to prevent excessive fees
  • Monitor gas prices for network congestion
  • Validate gas limits are within acceptable range

Error Handling

sealed class GasError {
    data class PriceTooHigh(val gasPrice: BigInteger, val maxAllowed: BigInteger) : GasError()
    data class InsufficientFunds(val required: BigInteger, val available: BigInteger) : GasError()
    data class NetworkError(val message: String) : GasError()
    data class Unknown(val message: String) : GasError()
}

suspend fun checkGasAffordability(
    wallet: BaseWallet,
    chainId: Int,
    gasLimit: BigInteger,
    gasPrice: BigInteger
): Result<Unit> {
    return try {
        val client = sdk.evm.createPublicClient(chainId)
        val balance = client.getBalance(wallet.address)

        val gasCost = gasLimit * gasPrice

        if (balance < gasCost) {
            Result.failure(
                Exception("Insufficient funds for gas: ${GasConverter.weiToEth(gasCost)} ETH required")
            )
        } else {
            Result.success(Unit)
        }
    } catch (e: Exception) {
        Result.failure(e)
    }
}

Troubleshooting

Gas price too low: Increase multiplier from 2x to 3x Out of gas: Increase gas limit, especially for contract calls Contract gas estimation: Use higher estimates for complex operations (swap, mint)

What’s Next