Introduction

This guide demonstrates how to integrate Para with Ethereum Virtual Machine (EVM) compatible blockchains in your Flutter applications. Para acts as a secure signer for your transactions, while Web3Dart handles the blockchain interactions.

Para’s approach to EVM transaction signing is designed for flexibility and security:

  • Para signs the raw transaction bytes without modifying the transaction data
  • Your application maintains full control over transaction construction
  • Para never has access to your application’s blockchain RPC endpoints

Prerequisites

Before you begin, ensure you have:

  1. Para SDK set up in your Flutter project (see the Setup Guide)
  2. A user authenticated with Para
  3. Web3Dart package installed in your project
flutter pub add web3dart:'^2.7.3'

Use version ^2.7.3 of Web3Dart to ensure compatibility with Para’s EVM signer.

Setting Up the Para EVM Signer

The Para SDK provides an EVM signer that implements interfaces compatible with Web3Dart. First, let’s create a signer and connect it to Web3Dart:

import 'package:para/para.dart';
import 'package:web3dart/web3dart.dart';
import 'package:http/http.dart';

Future<void> setupEvmSigner() async {
  // Initialize Para SDK (if not already done)
  final para = Para(
    environment: Environment.beta,
    apiKey: 'YOUR_API_KEY',
  );
  
  // Get the user's EVM wallet
  final wallets = await para.getWallets();
  final evmWallet = wallets.values
      .firstWhere((wallet) => wallet.type == WalletType.evm);
  
  // Create the Para EVM signer
  final signer = await para.getEvmSigner(evmWallet);
  
  // Initialize Web3Dart client with your RPC endpoint
  final client = Web3Client(
    'https://rpc.ankr.com/eth_sepolia', // Example RPC URL (Sepolia testnet)
    Client(),
  );
  
  // The signer and client are now ready for transaction creation and signing
}

Signing Transactions

Para acts as the underlying signer, taking the serialized version of the transaction data and signing the raw bytes. Para never modifies the transaction data, which means your application is responsible for properly constructing transactions.

EIP-1559 Transaction

EIP-1559 transactions are the recommended format for most modern EVM chains, offering more predictable gas fees:

Future<String> sendEip1559Transaction(
  Web3Client client, 
  ParaEvmSigner signer,
  EthereumAddress recipientAddress,
  BigInt amount,
) async {
  // Prepare transaction parameters
  final credentials = signer.credentials;
  final fromAddress = await credentials.extractAddress();
  
  // Get the latest gas parameters from the network
  final gasPrice = await client.getGasPrice();
  final chainId = await client.getChainId();
  final nonce = await client.getTransactionCount(fromAddress);
  
  // Create the transaction
  final transaction = Transaction(
    from: fromAddress,
    to: recipientAddress,
    value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
    nonce: nonce,
    maxGas: 21000, // Standard gas limit for ETH transfers
    maxFeePerGas: gasPrice,
    maxPriorityFeePerGas: EtherAmount.fromBigInt(EtherUnit.gwei, BigInt.from(1)),
  );
  
  // Sign and send the transaction
  final txHash = await client.sendTransaction(
    credentials,
    transaction,
    chainId: chainId.toInt(),
  );
  
  return txHash;
}

Interacting with Smart Contracts

Para can also sign transactions that interact with smart contracts:

Future<String> callSmartContract(
  Web3Client client, 
  ParaEvmSigner signer,
  String contractAddress,
  String contractAbi,
  String functionName,
  List<dynamic> parameters,
) async {
  // Prepare transaction parameters
  final credentials = signer.credentials;
  final fromAddress = await credentials.extractAddress();
  
  // Parse the contract ABI and create a contract instance
  final contract = DeployedContract(
    ContractAbi.fromJson(contractAbi, 'Contract'),
    EthereumAddress.fromHex(contractAddress),
  );
  
  // Get the function from the contract
  final function = contract.function(functionName);
  
  // Estimate gas needed for this transaction
  final gasEstimate = await client.estimateGas(
    sender: fromAddress,
    to: contract.address,
    data: function.encodeCall(parameters),
  );
  
  // Get the latest gas parameters and nonce
  final gasPrice = await client.getGasPrice();
  final chainId = await client.getChainId();
  final nonce = await client.getTransactionCount(fromAddress);
  
  // Create and send the transaction
  final txHash = await client.sendTransaction(
    credentials,
    Transaction(
      from: fromAddress,
      to: contract.address,
      data: function.encodeCall(parameters),
      maxGas: (gasEstimate * BigInt.from(1.5)).toInt(), // Add buffer to gas estimate
      maxFeePerGas: gasPrice,
      maxPriorityFeePerGas: EtherAmount.fromBigInt(EtherUnit.gwei, BigInt.from(1)),
      nonce: nonce,
    ),
    chainId: chainId.toInt(),
  );
  
  return txHash;
}

Error Handling

When working with Para and EVM transactions, you might encounter several types of errors:

  1. Para Signing Errors: Occur when Para cannot sign the transaction (e.g., user cancels or verification fails)
  2. Transaction Formation Errors: Occur when the transaction is improperly formed
  3. RPC Errors: Occur when the blockchain node rejects the transaction

Best Practices

When using Para with EVM chains:

  1. Always verify transaction data before signing: Para signs whatever you provide, so ensure transaction data is correct
  2. Keep RPC endpoints secure: Never expose your RPC endpoints in client-side code
  3. Estimate gas properly: Always estimate gas and add a buffer to avoid transaction failures
  4. Handle nonce management: Track nonces carefully, especially for high-frequency transaction applications
  5. Test on testnets first: Always test your integration on testnets before moving to mainnet

Multi-Chain Support

Para’s EVM signer works with any EVM-compatible chain. To support multiple chains, simply change the RPC endpoint in your Web3Client:

// Ethereum Mainnet
final ethMainnetClient = Web3Client(
  'https://rpc.ankr.com/eth',
  Client(),
);

// Polygon Mainnet
final polygonClient = Web3Client(
  'https://rpc.ankr.com/polygon',
  Client(),
);

// Arbitrum
final arbitrumClient = Web3Client(
  'https://rpc.ankr.com/arbitrum',
  Client(),
);

Example

For an example on using Para with EVM in Flutter, check out this evm_sign_example.dart in our examples repository:

Additional Resources

For more information on Web3Dart usage, refer to the Web3Dart documentation: