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:
- Para SDK set up in your Flutter project (see the Setup Guide)
- A user authenticated with Para
- 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:
- Para Signing Errors: Occur when Para cannot sign the transaction (e.g., user cancels or verification fails)
- Transaction Formation Errors: Occur when the transaction is improperly formed
- RPC Errors: Occur when the blockchain node rejects the transaction
Best Practices
When using Para with EVM chains:
- Always verify transaction data before signing: Para signs whatever you provide, so ensure transaction data is correct
- Keep RPC endpoints secure: Never expose your RPC endpoints in client-side code
- Estimate gas properly: Always estimate gas and add a buffer to avoid transaction failures
- Handle nonce management: Track nonces carefully, especially for high-frequency transaction applications
- 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: