Overview
Estimate gas prices, manage EIP-1559 fees, and optimize transaction costs.Prerequisites
- Dynamic SDK initialized (see Installation Guide)
- User authenticated (see Authentication Guide)
- Wallets available (see Wallet Creation)
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
- Send ETH Transactions - Send native ETH with gas management
- ERC-20 Token Transfers - Transfer tokens with gas optimization
- Smart Contract Interactions - Estimate gas for contract calls
- Network Management - Switch between networks