Skip to main content

EVM Operations (sdk.evm)

createPublicClient

Create a public client for interacting with an EVM chain.
EvmPublicClient createPublicClient({required int chainId})

Parameters

  • chainId (int) - The chain ID (e.g., 1 for Ethereum mainnet, 137 for Polygon)

Returns

  • EvmPublicClient - Public client for the chain

Example

final sdk = DynamicSDK.instance;
final client = sdk.evm.createPublicClient(chainId: 1); // Ethereum mainnet

getGasPrice

Get the current gas price for a chain.
Future<GasPrice> getGasPrice()

Returns

  • GasPrice - Current gas price information

Example

final client = sdk.evm.createPublicClient(chainId: 1);

try {
  final gasPrice = await client.getGasPrice();
  print('Gas price: ${gasPrice.maxFeePerGas}');
} catch (e) {
  print('Failed to get gas price: $e');
}

sendTransaction

Send a transaction on an EVM chain.
Future<String> sendTransaction(BaseWallet wallet, EthereumTransaction transaction)

Parameters

  • wallet (BaseWallet) - The wallet to send from
  • transaction (EthereumTransaction) - Transaction details

Returns

  • String - Transaction hash

Example

final sdk = DynamicSDK.instance;

try {
  final transaction = EthereumTransaction(
    to: recipientAddress,
    value: '1000000000000000', // 0.001 ETH in Wei
    gasLimit: 21000,
  );
  final txHash = await sdk.evm.sendTransaction(wallet, transaction);
  print('Transaction sent: $txHash');
} catch (e) {
  print('Transaction failed: $e');
}

Complete Example with Gas Estimation

class SendTransactionScreen extends StatefulWidget {
  final BaseWallet wallet;

  SendTransactionScreen({required this.wallet});

  @override
  _SendTransactionScreenState createState() => _SendTransactionScreenState();
}

class _SendTransactionScreenState extends State<SendTransactionScreen> {
  final sdk = DynamicSDK.instance;
  final recipientController = TextEditingController();
  final amountController = TextEditingController();
  bool isLoading = false;
  String? txHash;

  Future<void> sendTransaction() async {
    setState(() => isLoading = true);

    try {
      // Get current network
      final network = await sdk.wallets.getNetwork(widget.wallet);
      final chainId = network.chainId!;

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

      // Convert ETH to Wei (multiply by 10^18)
      final amount = double.parse(amountController.text);
      final weiAmount = (amount * 1e18).toStringAsFixed(0);

      // Create transaction
      final transaction = EthereumTransaction(
        to: recipientController.text,
        value: weiAmount,
        gasLimit: 21000,
        maxFeePerGas: gasPrice.maxFeePerGas,
        maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
      );

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

      setState(() {
        txHash = hash;
        isLoading = false;
      });

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Transaction sent: $hash')),
      );
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Transaction failed: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Send Transaction')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              controller: recipientController,
              decoration: InputDecoration(
                labelText: 'Recipient Address',
                hintText: '0x...',
              ),
            ),
            SizedBox(height: 16),
            TextField(
              controller: amountController,
              decoration: InputDecoration(
                labelText: 'Amount (ETH)',
                hintText: '0.001',
              ),
              keyboardType: TextInputType.numberWithOptions(decimal: true),
            ),
            SizedBox(height: 24),
            ElevatedButton(
              onPressed: isLoading ? null : sendTransaction,
              child: isLoading
                  ? CircularProgressIndicator()
                  : Text('Send Transaction'),
            ),
            if (txHash != null) ...[
              SizedBox(height: 24),
              Text('Transaction Hash:'),
              SelectableText(
                txHash!,
                style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
              ),
            ],
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    recipientController.dispose();
    amountController.dispose();
    super.dispose();
  }
}

signTransaction

Sign a transaction without sending it.
Future<String> signTransaction(BaseWallet wallet, EthereumTransaction transaction)

Parameters

  • wallet (BaseWallet) - The wallet to sign with
  • transaction (EthereumTransaction) - Transaction to sign

Returns

  • String - Signed transaction

Example

try {
  final signedTx = await sdk.evm.signTransaction(wallet, transaction);
  print('Signed transaction: $signedTx');
} catch (e) {
  print('Failed to sign: $e');
}

writeContract

Write to a smart contract (call a contract function).
Future<String> writeContract(BaseWallet wallet, WriteContractInput input)

Parameters

  • wallet (BaseWallet) - The wallet to call from
  • input (WriteContractInput) - Contract call parameters

Returns

  • String - Transaction hash

Example

import 'package:web3dart/web3dart.dart';

final sdk = DynamicSDK.instance;

try {
  // ERC20 transfer example
  final input = WriteContractInput(
    address: tokenContractAddress,
    functionName: 'transfer',
    args: [recipientAddress, '1000000000000000000'], // 1 token with 18 decimals
    abi: [
      {
        'inputs': [
          {'name': 'recipient', 'type': 'address'},
          {'name': 'amount', 'type': 'uint256'},
        ],
        'name': 'transfer',
        'outputs': [
          {'name': '', 'type': 'bool'},
        ],
        'stateMutability': 'nonpayable',
        'type': 'function',
      },
    ],
  );

  final txHash = await sdk.evm.writeContract(wallet, input);
  print('Contract write successful: $txHash');
} catch (e) {
  print('Contract write failed: $e');
}

Complete ERC20 Token Transfer Example

class TokenTransferScreen extends StatefulWidget {
  final BaseWallet wallet;

  TokenTransferScreen({required this.wallet});

  @override
  _TokenTransferScreenState createState() => _TokenTransferScreenState();
}

class _TokenTransferScreenState extends State<TokenTransferScreen> {
  final sdk = DynamicSDK.instance;
  final contractController = TextEditingController();
  final recipientController = TextEditingController();
  final amountController = TextEditingController();
  bool isLoading = false;
  String? txHash;

  // Standard ERC20 ABI (transfer function)
  static const erc20TransferAbi = [
    {
      'inputs': [
        {'name': 'recipient', 'type': 'address'},
        {'name': 'amount', 'type': 'uint256'},
      ],
      'name': 'transfer',
      'outputs': [
        {'name': '', 'type': 'bool'},
      ],
      'stateMutability': 'nonpayable',
      'type': 'function',
    },
  ];

  Future<void> transferTokens() async {
    setState(() => isLoading = true);

    try {
      // Convert amount to smallest unit (assuming 18 decimals)
      final amount = double.parse(amountController.text);
      final rawAmount = (amount * 1e18).toStringAsFixed(0);

      final input = WriteContractInput(
        address: contractController.text,
        functionName: 'transfer',
        args: [recipientController.text, rawAmount],
        abi: erc20TransferAbi,
      );

      final hash = await sdk.evm.writeContract(widget.wallet, input);

      setState(() {
        txHash = hash;
        isLoading = false;
      });

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Transfer successful: $hash')),
      );
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Transfer failed: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Transfer ERC20 Tokens')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              controller: contractController,
              decoration: InputDecoration(
                labelText: 'Token Contract Address',
                hintText: '0x...',
              ),
            ),
            SizedBox(height: 16),
            TextField(
              controller: recipientController,
              decoration: InputDecoration(
                labelText: 'Recipient Address',
                hintText: '0x...',
              ),
            ),
            SizedBox(height: 16),
            TextField(
              controller: amountController,
              decoration: InputDecoration(
                labelText: 'Amount',
                hintText: '1.0',
              ),
              keyboardType: TextInputType.numberWithOptions(decimal: true),
            ),
            SizedBox(height: 24),
            ElevatedButton(
              onPressed: isLoading ? null : transferTokens,
              child: isLoading
                  ? CircularProgressIndicator()
                  : Text('Transfer Tokens'),
            ),
            if (txHash != null) ...[
              SizedBox(height: 24),
              Text('Transaction Hash:'),
              SelectableText(
                txHash!,
                style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
              ),
            ],
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    contractController.dispose();
    recipientController.dispose();
    amountController.dispose();
    super.dispose();
  }
}

Solana Operations (sdk.solana)

createConnection

Create a connection to a Solana network.
SolanaConnection createConnection()

Returns

  • SolanaConnection - Connection to the Solana network

Example

final sdk = DynamicSDK.instance;
final connection = sdk.solana.createConnection();

getLatestBlockhash

Get the latest blockhash for transaction creation.
Future<BlockhashResult> getLatestBlockhash()

Returns

  • BlockhashResult - Contains the blockhash and last valid block height

Example

final connection = sdk.solana.createConnection();

try {
  final blockhashResult = await connection.getLatestBlockhash();
  final blockhash = blockhashResult.blockhash;
  print('Latest blockhash: $blockhash');
} catch (e) {
  print('Failed to get blockhash: $e');
}

createSigner

Create a signer for signing Solana transactions.
SolanaSigner createSigner(BaseWallet wallet)

Parameters

  • wallet (BaseWallet) - The Solana wallet

Returns

  • SolanaSigner - Signer for the wallet

Example

final sdk = DynamicSDK.instance;
final signer = sdk.solana.createSigner(wallet);

signMessage

Sign a message with a Solana wallet.
Future<String> signMessage(String message)

Parameters

  • message (String) - The message to sign

Returns

  • String - The signature

Example

final signer = sdk.solana.createSigner(wallet);

try {
  final signature = await signer.signMessage('Hello from Dynamic SDK!');
  print('Signature: $signature');
} catch (e) {
  print('Failed to sign message: $e');
}

signEncodedTransaction

Sign a Solana transaction (base64 encoded).
Future<String> signEncodedTransaction(String base64Transaction)

Parameters

  • base64Transaction (String) - Base64 encoded transaction

Returns

  • String - Signed transaction (base64 encoded)

Example

final signer = sdk.solana.createSigner(wallet);

try {
  final signedTx = await signer.signEncodedTransaction(base64Transaction);
  print('Signed transaction: $signedTx');
} catch (e) {
  print('Failed to sign transaction: $e');
}

signAndSendEncodedTransaction

Sign and send a Solana transaction.
Future<String> signAndSendEncodedTransaction(String base64Transaction)

Parameters

  • base64Transaction (String) - Base64 encoded transaction

Returns

  • String - Transaction signature

Example

final signer = sdk.solana.createSigner(wallet);

try {
  final signature = await signer.signAndSendEncodedTransaction(base64Transaction);
  print('Transaction sent: $signature');
} catch (e) {
  print('Transaction failed: $e');
}

Complete Solana Transfer Example

import 'package:solana/solana.dart';

class SolanaTransferScreen extends StatefulWidget {
  final BaseWallet wallet;

  SolanaTransferScreen({required this.wallet});

  @override
  _SolanaTransferScreenState createState() => _SolanaTransferScreenState();
}

class _SolanaTransferScreenState extends State<SolanaTransferScreen> {
  final sdk = DynamicSDK.instance;
  final recipientController = TextEditingController();
  final amountController = TextEditingController();
  bool isLoading = false;
  String? signature;

  Future<void> sendTransaction() async {
    setState(() => isLoading = true);

    try {
      // Create connection and signer
      final connection = sdk.solana.createConnection();
      final signer = sdk.solana.createSigner(widget.wallet);

      // Get latest blockhash
      final blockhashResult = await connection.getLatestBlockhash();

      // Convert SOL to lamports (multiply by 10^9)
      final amount = double.parse(amountController.text);
      final lamports = (amount * 1e9).toInt();

      // Create Solana client for transaction building
      final client = SolanaClient(
        rpcUrl: Uri.parse('https://api.mainnet-beta.solana.com'),
        websocketUrl: Uri.parse('wss://api.mainnet-beta.solana.com'),
      );

      // Create transfer instruction
      final fromPubkey = Ed25519HDPublicKey.fromBase58(widget.wallet.address);
      final toPubkey = Ed25519HDPublicKey.fromBase58(recipientController.text);

      final instruction = SystemInstruction.transfer(
        fundingAccount: fromPubkey,
        recipientAccount: toPubkey,
        lamports: lamports,
      );

      // Build transaction
      final message = Message(
        instructions: [instruction],
      );

      final compiledMessage = message.compile(
        recentBlockhash: blockhashResult.blockhash,
        feePayer: fromPubkey,
      );

      final transaction = SignedTx(
        compiledMessage: compiledMessage,
        signatures: [],
      );

      // Encode transaction to base64
      final base64Tx = base64Encode(transaction.encode());

      // Sign and send using Dynamic SDK
      final sig = await signer.signAndSendEncodedTransaction(base64Tx);

      setState(() {
        signature = sig;
        isLoading = false;
      });

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Transaction sent: $sig')),
      );
    } catch (e) {
      setState(() => isLoading = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Transaction failed: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Send Solana')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              controller: recipientController,
              decoration: InputDecoration(
                labelText: 'Recipient Address',
                hintText: 'Solana address...',
              ),
            ),
            SizedBox(height: 16),
            TextField(
              controller: amountController,
              decoration: InputDecoration(
                labelText: 'Amount (SOL)',
                hintText: '0.001',
              ),
              keyboardType: TextInputType.numberWithOptions(decimal: true),
            ),
            SizedBox(height: 24),
            ElevatedButton(
              onPressed: isLoading ? null : sendTransaction,
              child: isLoading
                  ? CircularProgressIndicator()
                  : Text('Send Transaction'),
            ),
            if (signature != null) ...[
              SizedBox(height: 24),
              Text('Transaction Signature:'),
              SelectableText(
                signature!,
                style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
              ),
            ],
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    recipientController.dispose();
    amountController.dispose();
    super.dispose();
  }
}

Network Management (sdk.networks)

Available Networks

Get lists of available EVM and Solana networks.
List<GenericNetwork> get evm
List<GenericNetwork> get solana

Example

final sdk = DynamicSDK.instance;

// Get all EVM networks
final evmNetworks = sdk.networks.evm;
for (final network in evmNetworks) {
  print('EVM Network: ${network.name}, Chain ID: ${network.chainId}');
}

// Get all Solana networks
final solanaNetworks = sdk.networks.solana;
for (final network in solanaNetworks) {
  print('Solana Network: ${network.name}, Network ID: ${network.networkId}');
}

// Find a specific network
final polygon = evmNetworks.firstWhere((n) => n.chainId == 137);
print('Found Polygon: ${polygon.name}');

Network Selector Widget

class NetworkSelector extends StatelessWidget {
  final String chainType; // 'EVM' or 'SOL'
  final Function(GenericNetwork) onNetworkSelected;
  final sdk = DynamicSDK.instance;

  NetworkSelector({
    required this.chainType,
    required this.onNetworkSelected,
  });

  @override
  Widget build(BuildContext context) {
    final networks = chainType == 'EVM'
        ? sdk.networks.evm
        : sdk.networks.solana;

    return DropdownButton<GenericNetwork>(
      hint: Text('Select Network'),
      items: networks.map((network) {
        return DropdownMenuItem(
          value: network,
          child: Text(network.name),
        );
      }).toList(),
      onChanged: (network) {
        if (network != null) {
          onNetworkSelected(network);
        }
      },
    );
  }
}

Data Types

EthereumTransaction

class EthereumTransaction {
  final String to;                 // Recipient address
  final String value;              // Amount in Wei (as String)
  final int gasLimit;              // Gas limit
  final int? maxFeePerGas;         // Max fee per gas (optional)
  final int? maxPriorityFeePerGas; // Priority fee (optional)
  final String? data;              // Contract data (optional)
}

WriteContractInput

class WriteContractInput {
  final String address;                      // Contract address
  final String functionName;                 // Function to call
  final List<dynamic> args;                  // Function arguments
  final List<Map<String, dynamic>> abi;      // Contract ABI
}

GasPrice

class GasPrice {
  final int maxFeePerGas;
  final int maxPriorityFeePerGas;
}

GenericNetwork

class GenericNetwork {
  final String name;
  final int? chainId;      // For EVM networks
  final String? networkId; // For Solana networks
}

BlockhashResult

class BlockhashResult {
  final String blockhash;
  final int lastValidBlockHeight;
}

Using web3dart for Advanced EVM Operations

The Dynamic SDK provides integration classes for using the web3dart package with Dynamic wallets.

DynamicRpcService

Extends RpcService from web3dart to make RPC calls through the Dynamic SDK.
class DynamicRpcService extends RpcService {
  final int chainId;
  final dynamic requestChannel;

  DynamicRpcService({
    required this.chainId,
    required this.requestChannel,
  });

  Future<RPCResponse> call(String function, [List? params]);
}

Example

import 'package:web3dart/web3dart.dart';

final rpcService = DynamicRpcService(
  chainId: 1,
  requestChannel: wallet.requestChannel,
);

final response = await rpcService.call('eth_blockNumber');

DynamicCredential

Implements CredentialsWithKnownAddress and CustomTransactionSender for signing messages and transactions with Dynamic wallets.
class DynamicCredential extends CredentialsWithKnownAddress
    implements CustomTransactionSender {
  final dynamic requestChannel;
  final String address;

  DynamicCredential({
    required this.requestChannel,
    required String address,
  });

  Future<String> signMessage({required Uint8List payload});
  Future<String> sendTransaction(Transaction transaction);
}

Example

import 'package:web3dart/web3dart.dart';

final credential = DynamicCredential(
  requestChannel: wallet.requestChannel,
  address: wallet.address,
);

// Sign a message
final signature = await credential.signMessage(
  payload: Uint8List.fromList(utf8.encode('Hello')),
);

// Send a transaction
final transaction = Transaction(
  to: EthereumAddress.fromHex('0x...'),
  value: EtherAmount.fromInt(EtherUnit.ether, 1),
);
final txHash = await credential.sendTransaction(transaction);

Advanced Token Operations

For more complex EVM operations, you can use the web3dart package alongside the Dynamic SDK:
import 'package:web3dart/web3dart.dart';
import 'package:http/http.dart' as http;

class AdvancedEvmOperations {
  final sdk = DynamicSDK.instance;

  Future<EtherAmount> getTokenBalance({
    required String tokenAddress,
    required String walletAddress,
    required String rpcUrl,
  }) async {
    final client = Web3Client(rpcUrl, http.Client());

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

    final balanceFunction = contract.function('balanceOf');
    final balance = await client.call(
      contract: contract,
      function: balanceFunction,
      params: [EthereumAddress.fromHex(walletAddress)],
    );

    return EtherAmount.fromBigInt(EtherUnit.wei, balance.first);
  }
}

Using solana package for Advanced Solana Operations

For complex Solana operations, use the solana package with Dynamic SDK:
import 'package:solana/solana.dart';

class AdvancedSolanaOperations {
  final sdk = DynamicSDK.instance;

  Future<double> getTokenBalance({
    required String tokenMintAddress,
    required String walletAddress,
  }) async {
    final client = SolanaClient(
      rpcUrl: Uri.parse('https://api.mainnet-beta.solana.com'),
      websocketUrl: Uri.parse('wss://api.mainnet-beta.solana.com'),
    );

    final pubkey = Ed25519HDPublicKey.fromBase58(walletAddress);
    final balance = await client.rpcClient.getTokenAccountBalance(
      pubkey.toBase58(),
    );

    return balance.value.uiAmount ?? 0.0;
  }
}