Overview
Read from and write to smart contracts on EVM chains using the WriteContractInput API.Prerequisites
- Dynamic SDK initialized (see Installation Guide)
- User authenticated (see Authentication Guide)
- Wallets available (see Wallet Creation)
- Contract ABI (Application Binary Interface)
Write to a Contract
Copy
Ask AI
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.BaseWallet
import com.dynamic.sdk.android.Chains.EVM.WriteContractInput
import org.json.JSONArray
import org.json.JSONObject
val sdk = DynamicSDK.getInstance()
suspend fun writeContract(
wallet: BaseWallet,
contractAddress: String,
abi: List<Map<String, Any>>,
functionName: String,
args: List<Any>
) {
try {
val input = WriteContractInput(
address = contractAddress,
abi = abi,
functionName = functionName,
args = args
)
val txHash = sdk.evm.writeContract(wallet, input)
println("Transaction hash: $txHash")
} catch (e: Exception) {
println("Contract write failed: ${e.message}")
}
}
Common Contract Patterns
Minting NFTs
Copy
Ask AI
suspend fun mintNFT(
wallet: BaseWallet,
nftContractAddress: String,
recipientAddress: String
) {
val abi = parseAbiJson("""
[
{
"inputs": [
{ "internalType": "address", "name": "to", "type": "address" }
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
""")
val input = WriteContractInput(
address = nftContractAddress,
abi = abi,
functionName = "mint",
args = listOf(recipientAddress)
)
val txHash = sdk.evm.writeContract(wallet, input)
println("NFT minted! Transaction: $txHash")
}
Staking Tokens
Copy
Ask AI
suspend fun stakeTokens(
wallet: BaseWallet,
stakingContractAddress: String,
amount: BigInteger
) {
val abi = parseAbiJson("""
[
{
"inputs": [
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "stake",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
""")
val input = WriteContractInput(
address = stakingContractAddress,
abi = abi,
functionName = "stake",
args = listOf(amount.toString())
)
val txHash = sdk.evm.writeContract(wallet, input)
println("Tokens staked! Transaction: $txHash")
}
Swapping Tokens (DEX)
Copy
Ask AI
suspend fun swapTokens(
wallet: BaseWallet,
dexContractAddress: String,
tokenIn: String,
tokenOut: String,
amountIn: BigInteger,
minAmountOut: BigInteger,
deadline: Long
) {
val abi = parseAbiJson("""
[
{
"inputs": [
{ "internalType": "address", "name": "tokenIn", "type": "address" },
{ "internalType": "address", "name": "tokenOut", "type": "address" },
{ "internalType": "uint256", "name": "amountIn", "type": "uint256" },
{ "internalType": "uint256", "name": "minAmountOut", "type": "uint256" },
{ "internalType": "uint256", "name": "deadline", "type": "uint256" }
],
"name": "swap",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
""")
val input = WriteContractInput(
address = dexContractAddress,
abi = abi,
functionName = "swap",
args = listOf(
tokenIn,
tokenOut,
amountIn.toString(),
minAmountOut.toString(),
deadline.toString()
)
)
val txHash = sdk.evm.writeContract(wallet, input)
println("Tokens swapped! Transaction: $txHash")
}
Read from a Contract
Copy
Ask AI
import com.dynamic.sdk.android.Chains.EVM.ReadContractInput
suspend fun readContract(
wallet: BaseWallet,
contractAddress: String,
abi: List<Map<String, Any>>,
functionName: String,
args: List<Any> = emptyList()
): Any {
val input = ReadContractInput(
address = contractAddress,
abi = abi,
functionName = functionName,
args = args
)
return sdk.evm.readContract(wallet, input)
}
// Example: Get token balance
suspend fun getBalance(
wallet: BaseWallet,
tokenAddress: String,
ownerAddress: String
): String {
val abi = parseAbiJson("""
[
{
"inputs": [
{ "internalType": "address", "name": "account", "type": "address" }
],
"name": "balanceOf",
"outputs": [
{ "internalType": "uint256", "name": "", "type": "uint256" }
],
"stateMutability": "view",
"type": "function"
}
]
""")
val result = readContract(
wallet = wallet,
contractAddress = tokenAddress,
abi = abi,
functionName = "balanceOf",
args = listOf(ownerAddress)
)
return result.toString()
}
ABI Parsing Helper
Copy
Ask AI
object AbiParser {
fun parseAbiJson(abiString: String): List<Map<String, Any>> {
val jsonArray = JSONArray(abiString)
val result = mutableListOf<Map<String, Any>>()
for (i in 0 until jsonArray.length()) {
val jsonObj = jsonArray.getJSONObject(i)
result.add(jsonObjectToMap(jsonObj))
}
return result
}
private fun jsonObjectToMap(jsonObj: JSONObject): Map<String, Any> {
val map = mutableMapOf<String, Any>()
val keys = jsonObj.keys()
while (keys.hasNext()) {
val key = keys.next()
val value = jsonObj.get(key)
map[key] = jsonValueToAny(value)
}
return map
}
private fun jsonValueToAny(value: Any): Any {
return when (value) {
is JSONObject -> jsonObjectToMap(value)
is JSONArray -> {
val list = mutableListOf<Any>()
for (i in 0 until value.length()) {
list.add(jsonValueToAny(value.get(i)))
}
list
}
JSONObject.NULL -> "null"
else -> value
}
}
}
// Usage
val abi = AbiParser.parseAbiJson(abiJsonString)
Best Practices
- Validate contract addresses before calling
- Handle ABI parsing errors gracefully
- Cache ABIs to avoid repeated parsing
- Verify argument types match ABI
- Test contracts on testnet first
Error Handling
Copy
Ask AI
sealed class ContractError {
data class InvalidAbi(val message: String) : ContractError()
data class InvalidAddress(val message: String) : ContractError()
data class ExecutionFailed(val message: String) : ContractError()
data class InsufficientGas(val message: String) : ContractError()
data class Unknown(val message: String) : ContractError()
}
fun parseContractError(e: Exception): ContractError {
val errorMessage = e.message?.lowercase() ?: ""
return when {
errorMessage.contains("abi") || errorMessage.contains("parse") ->
ContractError.InvalidAbi("Invalid or malformed ABI")
errorMessage.contains("address") ->
ContractError.InvalidAddress("Invalid contract address")
errorMessage.contains("gas") ->
ContractError.InsufficientGas("Insufficient gas for execution")
errorMessage.contains("execution") || errorMessage.contains("revert") ->
ContractError.ExecutionFailed("Contract execution failed: ${e.message}")
else ->
ContractError.Unknown("Operation failed: ${e.message}")
}
}
// Usage in ViewModel
try {
val txHash = sdk.evm.writeContract(wallet, input)
_transactionHash.value = txHash
} catch (e: Exception) {
val error = parseContractError(e)
_errorMessage.value = when (error) {
is ContractError.InvalidAbi -> error.message
is ContractError.InvalidAddress -> error.message
is ContractError.ExecutionFailed -> error.message
is ContractError.InsufficientGas -> error.message
is ContractError.Unknown -> error.message
}
}
Troubleshooting
Invalid ABI: Ensure JSON is properly formatted with quotes and commas Type mismatch: Pass address/uint256 as String, bool as Boolean, bytes as hex String Contract reverts: Check requirements, permissions, input ranges, and contract stateWhat’s Next
- ERC-20 Token Transfers - Work with ERC-20 tokens
- Gas Management - Optimize gas usage
- Send ETH Transactions - Send native ETH
- Message Signing - Sign messages with wallets