> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Smart Contract Interactions

> Learn how to interact with smart contracts using the Dynamic Flutter SDK.

## Overview

This guide covers how to interact with smart contracts on EVM chains, including reading contract state and executing contract functions using the web3dart package.

## Prerequisites

* Dynamic SDK initialized (see [Installation Guide](/flutter/quickstart))
* User authenticated (see [Authentication Guide](/flutter/authentication))
* EVM wallet available (see [Wallet Creation](/flutter/wallet-creation))
* Contract ABI and address
* `dynamic_sdk_web3dart` package installed

## Write to Contract

Execute state-changing functions on a smart contract:

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:dynamic_sdk_web3dart/dynamic_sdk_web3dart.dart';
import 'package:web3dart/web3dart.dart';

final sdk = DynamicSDK.instance;

Future<String> writeContract({
  required BaseWallet wallet,
  required String contractAddress,
  required String abiJson,
  required String functionName,
  required List<dynamic> parameters,
}) async {
  // Get network information
  final network = await sdk.wallets.getNetwork(wallet: wallet);
  final chainId = network.intValue()!;

  // Create public client
  final client = sdk.web3dart.createPublicClient(chainId: chainId);

  // Get gas price
  final gasPrice = await client.getGasPrice();

  // Create contract
  final contract = DeployedContract(
    ContractAbi.fromJson(abiJson, 'Contract'),
    EthereumAddress.fromHex(contractAddress),
  );

  final function = contract.function(functionName);

  // Create transaction
  final transaction = Transaction.callContract(
    contract: contract,
    function: function,
    parameters: parameters,
    maxFeePerGas: EtherAmount.inWei(
      gasPrice.getValueInUnitBI(EtherUnit.wei) * BigInt.from(2),
    ),
    maxPriorityFeePerGas: EtherAmount.inWei(
      gasPrice.getValueInUnitBI(EtherUnit.wei),
    ),
  );

  // Send transaction
  final txHash = await sdk.web3dart.sendTransaction(
    transaction: transaction,
    wallet: wallet,
  );

  print('Contract interaction successful!');
  print('Hash: $txHash');
  return txHash;
}
```

## Read Contract Data

Query contract state without sending a transaction:

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:dynamic_sdk_web3dart/dynamic_sdk_web3dart.dart';
import 'package:web3dart/web3dart.dart';

final sdk = DynamicSDK.instance;

Future<List<dynamic>> readContract({
  required int chainId,
  required String contractAddress,
  required String abiJson,
  required String functionName,
  List<dynamic> parameters = const [],
}) async {
  final client = sdk.web3dart.createPublicClient(chainId: chainId);

  final contract = DeployedContract(
    ContractAbi.fromJson(abiJson, 'Contract'),
    EthereumAddress.fromHex(contractAddress),
  );

  final function = contract.function(functionName);

  final result = await client.call(
    contract: contract,
    function: function,
    params: parameters,
  );

  print('Contract value: $result');
  return result;
}
```

## Complete Contract Interaction Widget

```dart theme={"system"}
import 'package:flutter/material.dart';
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:dynamic_sdk_web3dart/dynamic_sdk_web3dart.dart';
import 'package:web3dart/web3dart.dart';

class ContractInteractionWidget extends StatefulWidget {
  final BaseWallet wallet;
  final String contractAddress;
  final int chainId;

  const ContractInteractionWidget({
    Key? key,
    required this.wallet,
    required this.contractAddress,
    required this.chainId,
  }) : super(key: key);

  @override
  State<ContractInteractionWidget> createState() => _ContractInteractionWidgetState();
}

class _ContractInteractionWidgetState extends State<ContractInteractionWidget> {
  final sdk = DynamicSDK.instance;
  final _inputValueController = TextEditingController();

  String? currentValue;
  String? txHash;
  bool isLoading = false;
  String? error;

  // Example ABI for a simple storage contract
  final contractAbi = '''[
    {
      "constant": false,
      "inputs": [{"name": "value", "type": "uint256"}],
      "name": "setValue",
      "outputs": [],
      "type": "function"
    },
    {
      "constant": true,
      "inputs": [],
      "name": "getValue",
      "outputs": [{"name": "", "type": "uint256"}],
      "type": "function"
    }
  ]''';

  @override
  void initState() {
    super.initState();
    _readValue();
  }

  @override
  void dispose() {
    _inputValueController.dispose();
    super.dispose();
  }

  Future<void> _readValue() async {
    try {
      final client = sdk.web3dart.createPublicClient(chainId: widget.chainId);

      final contract = DeployedContract(
        ContractAbi.fromJson(contractAbi, 'Storage'),
        EthereumAddress.fromHex(widget.contractAddress),
      );

      final function = contract.function('getValue');

      final result = await client.call(
        contract: contract,
        function: function,
        params: [],
      );

      setState(() => currentValue = result.first.toString());
    } catch (e) {
      setState(() => error = 'Failed to read: $e');
    }
  }

  Future<void> _setValue() async {
    setState(() {
      isLoading = true;
      error = null;
      txHash = null;
    });

    try {
      final value = _inputValueController.text.trim();
      if (value.isEmpty) {
        throw Exception('Value is required');
      }

      // Get gas price
      final client = sdk.web3dart.createPublicClient(chainId: widget.chainId);
      final gasPrice = await client.getGasPrice();

      // Create contract
      final contract = DeployedContract(
        ContractAbi.fromJson(contractAbi, 'Storage'),
        EthereumAddress.fromHex(widget.contractAddress),
      );

      final function = contract.function('setValue');

      // Create transaction
      final transaction = Transaction.callContract(
        contract: contract,
        function: function,
        parameters: [BigInt.parse(value)],
        maxFeePerGas: EtherAmount.inWei(
          gasPrice.getValueInUnitBI(EtherUnit.wei) * BigInt.from(2),
        ),
        maxPriorityFeePerGas: EtherAmount.inWei(
          gasPrice.getValueInUnitBI(EtherUnit.wei),
        ),
      );

      // Send transaction
      final hash = await sdk.web3dart.sendTransaction(
        transaction: transaction,
        wallet: widget.wallet,
      );

      setState(() => txHash = hash);

      // Refresh the value after transaction
      await Future.delayed(const Duration(seconds: 2));
      await _readValue();
    } catch (e) {
      setState(() => error = 'Failed to set value: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Read section
          Container(
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: Colors.grey[200],
              borderRadius: BorderRadius.circular(8),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'Read Contract',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                if (currentValue != null)
                  Row(
                    children: [
                      const Text('Current Value: '),
                      Text(
                        currentValue!,
                        style: const TextStyle(fontWeight: FontWeight.bold),
                      ),
                    ],
                  ),
                const SizedBox(height: 8),
                OutlinedButton(
                  onPressed: _readValue,
                  child: const Text('Refresh Value'),
                ),
              ],
            ),
          ),
          const SizedBox(height: 24),
          const Divider(),
          const SizedBox(height: 24),
          // Write section
          Container(
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: Colors.grey[200],
              borderRadius: BorderRadius.circular(8),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'Write to Contract',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                TextField(
                  controller: _inputValueController,
                  decoration: const InputDecoration(
                    labelText: 'New value',
                    border: OutlineInputBorder(),
                  ),
                  keyboardType: TextInputType.number,
                ),
                const SizedBox(height: 8),
                ElevatedButton(
                  onPressed: isLoading ? null : _setValue,
                  child: isLoading
                      ? const SizedBox(
                          height: 20,
                          width: 20,
                          child: CircularProgressIndicator(strokeWidth: 2),
                        )
                      : const Text('Set Value'),
                ),
                if (txHash != null) ...[
                  const SizedBox(height: 8),
                  Container(
                    padding: const EdgeInsets.all(8),
                    decoration: BoxDecoration(
                      color: Colors.green.withOpacity(0.1),
                      borderRadius: BorderRadius.circular(4),
                    ),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        const Text(
                          'Transaction sent!',
                          style: TextStyle(color: Colors.green),
                        ),
                        Text(
                          'Hash: $txHash',
                          style: const TextStyle(fontSize: 12),
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                        ),
                      ],
                    ),
                  ),
                ],
                if (error != null) ...[
                  const SizedBox(height: 8),
                  Text(
                    error!,
                    style: const TextStyle(color: Colors.red, fontSize: 12),
                  ),
                ],
              ],
            ),
          ),
        ],
      ),
    );
  }
}
```

## Working with Contract ABIs

### Defining ABIs

ABIs (Application Binary Interfaces) define how to interact with smart contracts:

```dart theme={"system"}
// Simple storage contract ABI
const storageAbi = '''[
  {
    "constant": false,
    "inputs": [{"name": "value", "type": "uint256"}],
    "name": "store",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "retrieve",
    "outputs": [{"name": "", "type": "uint256"}],
    "type": "function"
  }
]''';

// ERC-721 NFT contract functions
const nftAbi = '''[
  {
    "constant": false,
    "inputs": [
      {"name": "to", "type": "address"},
      {"name": "tokenId", "type": "uint256"}
    ],
    "name": "mint",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [{"name": "owner", "type": "address"}],
    "name": "balanceOf",
    "outputs": [{"name": "", "type": "uint256"}],
    "type": "function"
  }
]''';
```

## Advanced Contract Interactions

### NFT Minting

```dart theme={"system"}
Future<String> mintNFT({
  required BaseWallet wallet,
  required String nftContractAddress,
  required String recipient,
  required String tokenId,
  required int chainId,
}) async {
  const nftAbi = '''[
    {
      "constant": false,
      "inputs": [
        {"name": "to", "type": "address"},
        {"name": "tokenId", "type": "uint256"}
      ],
      "name": "mint",
      "outputs": [],
      "type": "function"
    }
  ]''';

  final client = DynamicSDK.instance.web3dart.createPublicClient(chainId: chainId);
  final gasPrice = await client.getGasPrice();

  final contract = DeployedContract(
    ContractAbi.fromJson(nftAbi, 'NFT'),
    EthereumAddress.fromHex(nftContractAddress),
  );

  final mintFunction = contract.function('mint');

  final transaction = Transaction.callContract(
    contract: contract,
    function: mintFunction,
    parameters: [
      EthereumAddress.fromHex(recipient),
      BigInt.parse(tokenId),
    ],
    maxFeePerGas: EtherAmount.inWei(
      gasPrice.getValueInUnitBI(EtherUnit.wei) * BigInt.from(2),
    ),
    maxPriorityFeePerGas: EtherAmount.inWei(
      gasPrice.getValueInUnitBI(EtherUnit.wei),
    ),
  );

  return await DynamicSDK.instance.web3dart.sendTransaction(
    transaction: transaction,
    wallet: wallet,
  );
}
```

### Check Token Allowance

```dart theme={"system"}
Future<BigInt> checkAllowance({
  required String tokenAddress,
  required String ownerAddress,
  required String spenderAddress,
  required int chainId,
}) async {
  const erc20Abi = '''[
    {
      "constant": true,
      "inputs": [
        {"name": "_owner", "type": "address"},
        {"name": "_spender", "type": "address"}
      ],
      "name": "allowance",
      "outputs": [{"name": "", "type": "uint256"}],
      "type": "function"
    }
  ]''';

  final client = DynamicSDK.instance.web3dart.createPublicClient(chainId: chainId);

  final contract = DeployedContract(
    ContractAbi.fromJson(erc20Abi, 'ERC20'),
    EthereumAddress.fromHex(tokenAddress),
  );

  final allowanceFunction = contract.function('allowance');

  final result = await client.call(
    contract: contract,
    function: allowanceFunction,
    params: [
      EthereumAddress.fromHex(ownerAddress),
      EthereumAddress.fromHex(spenderAddress),
    ],
  );

  return result.first as BigInt;
}
```

## Best Practices

### 1. Validate Contract Addresses

```dart theme={"system"}
bool isValidEthereumAddress(String address) {
  final pattern = RegExp(r'^0x[a-fA-F0-9]{40}$');
  return pattern.hasMatch(address);
}

// Usage
if (!isValidEthereumAddress(contractAddress)) {
  throw Exception('Invalid contract address');
}
```

### 2. Handle Contract Errors

```dart theme={"system"}
Future<String?> callContractSafely({
  required BaseWallet wallet,
  required String contractAddress,
  required String abiJson,
  required String functionName,
  required List<dynamic> parameters,
}) async {
  try {
    return await writeContract(
      wallet: wallet,
      contractAddress: contractAddress,
      abiJson: abiJson,
      functionName: functionName,
      parameters: parameters,
    );
  } catch (e) {
    final errorDesc = e.toString().toLowerCase();

    if (errorDesc.contains('revert')) {
      print('Contract reverted. Check contract requirements.');
    } else if (errorDesc.contains('gas')) {
      print('Out of gas. Increase gas limit.');
    } else {
      print('Contract call failed: $e');
    }
    return null;
  }
}
```

### 3. Use Try-Catch for Read Operations

```dart theme={"system"}
Future<String?> safeReadContract({
  required int chainId,
  required String contractAddress,
  required String abiJson,
  required String functionName,
}) async {
  try {
    final result = await readContract(
      chainId: chainId,
      contractAddress: contractAddress,
      abiJson: abiJson,
      functionName: functionName,
    );
    return result.first.toString();
  } catch (e) {
    print('Failed to read contract: $e');
    return null;
  }
}
```

## Next Steps

* [Send ETH Transactions](/flutter/wallets/evm/send-eth) - Basic ETH transfers
* [ERC-20 Token Transfers](/flutter/wallets/evm/erc20-transfers) - Send tokens
* [Gas Management](/flutter/wallets/evm/gas-management) - Optimize gas costs
* [Message Signing](/flutter/wallets/evm/message-signing) - Sign messages for verification
