> ## 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.

# Send ETH Transactions

> Learn how to send ETH transactions with the Dynamic Flutter SDK.

## Overview

This guide covers sending ETH transactions including transaction creation, signing, and sending with the Dynamic Flutter SDK using the web3dart package.

## Prerequisites

* Dynamic SDK initialized (see [Installation Guide](/flutter/quickstart))
* User authenticated (see [Authentication Guide](/flutter/authentication))
* Wallets available (see [Wallet Creation](/flutter/wallet-creation))
* Network configured (see [Networks](/flutter/wallets/general/network-management))
* `dynamic_sdk_web3dart` package installed

## Send ETH 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<String> sendTransaction({
  required BaseWallet wallet,
  required String recipientAddress,
  required double amountInEth,
}) async {
  // Convert ETH to Wei (1 ETH = 10^18 Wei)
  final amountInWei = (amountInEth * BigInt.from(10).pow(18).toDouble()).toInt();

  // Create transaction
  final transaction = Transaction(
    from: EthereumAddress.fromHex(wallet.address),
    to: EthereumAddress.fromHex(recipientAddress),
    value: EtherAmount.inWei(BigInt.from(amountInWei)),
  );

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

  print('Transaction sent!');
  print('Hash: $txHash');
  return txHash;
}
```

## Complete Send Transaction Widget

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

class SendTransactionWidget extends StatefulWidget {
  final BaseWallet wallet;

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

  @override
  State<SendTransactionWidget> createState() => _SendTransactionWidgetState();
}

class _SendTransactionWidgetState extends State<SendTransactionWidget> {
  final sdk = DynamicSDK.instance;
  final _recipientController = TextEditingController();
  final _amountController = TextEditingController();

  String? txHash;
  bool isLoading = false;
  String? errorMessage;

  @override
  void dispose() {
    _recipientController.dispose();
    _amountController.dispose();
    super.dispose();
  }

  Future<void> _sendTransaction() async {
    final recipient = _recipientController.text.trim();
    final amount = _amountController.text.trim();

    if (recipient.isEmpty || amount.isEmpty) {
      setState(() => errorMessage = 'Please fill all fields');
      return;
    }

    final amountDouble = double.tryParse(amount);
    if (amountDouble == null) {
      setState(() => errorMessage = 'Invalid amount');
      return;
    }

    setState(() {
      isLoading = true;
      errorMessage = null;
      txHash = null;
    });

    try {
      // Convert ETH to Wei
      final amountInWei = (amountDouble * BigInt.from(10).pow(18).toDouble()).toInt();

      // Create transaction
      final transaction = Transaction(
        from: EthereumAddress.fromHex(widget.wallet.address),
        to: EthereumAddress.fromHex(recipient),
        value: EtherAmount.inWei(BigInt.from(amountInWei)),
      );

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

      setState(() => txHash = hash);
    } catch (e) {
      setState(() => errorMessage = 'Failed: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          TextField(
            controller: _recipientController,
            decoration: const InputDecoration(
              labelText: 'Recipient address',
              border: OutlineInputBorder(),
            ),
            autocorrect: false,
            enableSuggestions: false,
          ),
          const SizedBox(height: 16),
          TextField(
            controller: _amountController,
            decoration: const InputDecoration(
              labelText: 'Amount (ETH)',
              border: OutlineInputBorder(),
            ),
            keyboardType: const TextInputType.numberWithOptions(decimal: true),
          ),
          const SizedBox(height: 16),
          ElevatedButton(
            onPressed: isLoading ? null : _sendTransaction,
            child: isLoading
                ? const SizedBox(
                    height: 20,
                    width: 20,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  )
                : const Text('Send Transaction'),
          ),
          if (txHash != null) ...[
            const SizedBox(height: 16),
            Container(
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.green.withOpacity(0.1),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'Transaction Sent!',
                    style: TextStyle(
                      fontSize: 16,
                      fontWeight: FontWeight.bold,
                      color: Colors.green,
                    ),
                  ),
                  const SizedBox(height: 8),
                  Text(
                    'Hash: $txHash',
                    style: const TextStyle(
                      fontSize: 12,
                      fontFamily: 'monospace',
                    ),
                    maxLines: 2,
                    overflow: TextOverflow.ellipsis,
                  ),
                  const SizedBox(height: 8),
                  TextButton(
                    onPressed: () {
                      Clipboard.setData(ClipboardData(text: txHash!));
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(content: Text('Hash copied!')),
                      );
                    },
                    child: const Text('Copy Hash'),
                  ),
                ],
              ),
            ),
          ],
          if (errorMessage != null) ...[
            const SizedBox(height: 16),
            Text(
              errorMessage!,
              style: const TextStyle(color: Colors.red, fontSize: 12),
            ),
          ],
        ],
      ),
    );
  }
}
```

## Sign Transaction (Without Sending)

To sign a transaction without broadcasting it, use `signEthereumTransaction`:

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

final sdk = DynamicSDK.instance;

Future<void> signTransactionOnly(BaseWallet wallet) async {
  try {
    final signedTx = await sdk.wallets.signEthereumTransaction(
      wallet: wallet,
      to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bDd7',
      value: '0.001',
      gasLimit: '21000',
      maxPriorityFeePerGas: '2',
      maxFeePerGas: '50',
    );
    print('Signed transaction: $signedTx');
  } catch (e) {
    print('Failed to sign: $e');
  }
}
```

## Best Practices

### 1. Validate Input

```dart theme={"system"}
String? validateAddress(String address) {
  if (address.isEmpty) {
    return 'Address is required';
  }

  if (!address.startsWith('0x') || address.length != 42) {
    return 'Invalid Ethereum address';
  }

  return null;
}

String? validateAmount(String amount) {
  if (amount.isEmpty) {
    return 'Amount is required';
  }

  final value = double.tryParse(amount);
  if (value == null || value <= 0) {
    return 'Invalid amount';
  }

  return null;
}
```

### 2. Handle Transaction Errors

```dart theme={"system"}
Future<String?> sendTransactionWithErrorHandling({
  required BaseWallet wallet,
  required String recipient,
  required double amount,
}) async {
  try {
    final amountInWei = (amount * BigInt.from(10).pow(18).toDouble()).toInt();

    final transaction = Transaction(
      from: EthereumAddress.fromHex(wallet.address),
      to: EthereumAddress.fromHex(recipient),
      value: EtherAmount.inWei(BigInt.from(amountInWei)),
    );

    final txHash = await DynamicSDK.instance.web3dart.sendTransaction(
      transaction: transaction,
      wallet: wallet,
    );

    return txHash;
  } catch (e) {
    final errorStr = e.toString().toLowerCase();

    if (errorStr.contains('insufficient')) {
      throw Exception('Insufficient funds for transaction');
    } else if (errorStr.contains('gas')) {
      throw Exception('Gas estimation failed. Try increasing gas limit.');
    } else if (errorStr.contains('rejected') || errorStr.contains('denied')) {
      throw Exception('Transaction was rejected');
    } else if (errorStr.contains('network')) {
      throw Exception('Network error. Please check your connection.');
    } else {
      throw Exception('Transaction failed. Please try again.');
    }
  }
}
```

### 3. Show Transaction Status

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

class TransactionStatusWidget extends StatelessWidget {
  final String txHash;
  final int chainId;

  const TransactionStatusWidget({
    Key? key,
    required this.txHash,
    required this.chainId,
  }) : super(key: key);

  String get explorerUrl {
    // Map chain ID to block explorer
    switch (chainId) {
      case 1:
        return 'https://etherscan.io/tx/$txHash';
      case 84532:
        return 'https://sepolia.basescan.org/tx/$txHash';
      case 137:
        return 'https://polygonscan.com/tx/$txHash';
      default:
        return '';
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text(
          'Transaction Submitted',
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 8),
        Text(
          txHash,
          style: const TextStyle(fontSize: 12),
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
        ),
        if (explorerUrl.isNotEmpty) ...[
          const SizedBox(height: 8),
          TextButton(
            onPressed: () async {
              final uri = Uri.parse(explorerUrl);
              if (await canLaunchUrl(uri)) {
                await launchUrl(uri);
              }
            },
            child: const Text('View on Explorer'),
          ),
        ],
      ],
    );
  }
}
```

### 4. Confirm Before Sending

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

class ConfirmTransactionDialog extends StatelessWidget {
  final String recipient;
  final String amount;
  final VoidCallback onConfirm;

  const ConfirmTransactionDialog({
    Key? key,
    required this.recipient,
    required this.amount,
    required this.onConfirm,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('Confirm Transaction'),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('To: $recipient'),
          const SizedBox(height: 8),
          Text('Amount: $amount ETH'),
        ],
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.of(context).pop(),
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: () {
            Navigator.of(context).pop();
            onConfirm();
          },
          child: const Text('Confirm'),
        ),
      ],
    );
  }
}
```

## Error Handling

### Common Transaction Errors

```dart theme={"system"}
String getErrorMessage(dynamic error) {
  final errorStr = error.toString().toLowerCase();

  if (errorStr.contains('insufficient')) {
    return 'Insufficient balance for this transaction';
  } else if (errorStr.contains('gas')) {
    return 'Gas estimation failed. Try increasing gas limit.';
  } else if (errorStr.contains('rejected') || errorStr.contains('denied')) {
    return 'Transaction was rejected';
  } else if (errorStr.contains('network')) {
    return 'Network error. Please check your connection.';
  } else {
    return 'Transaction failed. Please try again.';
  }
}

// Usage
try {
  await sendTransaction(
    wallet: wallet,
    recipientAddress: recipient,
    amountInEth: amount,
  );
} catch (e) {
  final message = getErrorMessage(e);
  print(message);
}
```

## Next Steps

* [ERC-20 Token Transfers](/flutter/wallets/evm/erc20-transfers) - Send ERC-20 tokens
* [Smart Contract Interactions](/flutter/wallets/evm/smart-contracts) - Interact with smart contracts
* [Gas Management](/flutter/wallets/evm/gas-management) - Gas estimation and optimization
* [Message Signing](/flutter/wallets/evm/message-signing) - Sign messages and typed data
