In today’s digital landscape, integrating blockchain technology into mobile applications is becoming increasingly essential. This article dives into building a multi-chain wallet using Flutter and Dart, enabling support for Bitcoin, Ethereum, and Tron. We’ll walk through generating wallets from a single mnemonic phrase, deriving addresses across chains, and signing transactions—particularly focusing on Ethereum and Bitcoin due to library limitations in Dart for Tron.
Whether you're building a decentralized finance (DeFi) app or a crypto wallet, understanding how different blockchains handle key derivation and transaction structures is crucial. Let’s explore the core concepts with practical code examples.
Understanding Multi-Chain Wallets
A multi-chain wallet allows users to manage assets across various blockchains—Bitcoin, Ethereum, Tron, and more—using a single interface. The foundation of such wallets lies in HD Wallets (Hierarchical Deterministic Wallets), which use a mnemonic seed to generate multiple private-public key pairs deterministically.
This approach ensures that users only need to remember one 12- or 24-word recovery phrase, from which all their wallet addresses across different chains can be derived—thanks to the BIP-44 standard.
Core Keywords:
- multi-chain wallet
- HD Wallet
- BIP-44
- mnemonic phrase
- transaction signing
- Flutter Web3
- Dart blockchain
- crypto wallet development
Generating a Multi-Chain Wallet in Flutter
To create a wallet that supports Bitcoin, Ethereum, and Tron, we use the following Dart packages:
bip39
– for generating and validating mnemonic phrasesflutter_bitcoin
– for Bitcoin key derivation and address generationweb3dart
– for Ethereum interactionswallet
– for Tron address creation
Each blockchain uses a specific Coin Type under BIP-44:
Chain | Coin Type |
---|---|
Bitcoin | 0 |
Ethereum | 60 |
Tron | 195 |
Using these values, we derive keys via the path: m/44'/{coin_type}'/0'/0/0
.
import 'package:bip39/bip39.dart' as bip39;
import 'package:flutter_bitcoin/flutter_bitcoin.dart';
import 'package:web3dart/web3dart.dart';
import 'package:wallet/wallet.dart' as wallet;
final mnemonic = bip39.generateMnemonic(strength: 128);
final seed = bip39.mnemonicToSeed(mnemonic);
final hdWallet = HDWallet.fromSeed(seed);
// Derive wallets
final btcWallet = hdWallet.derivePath("m/44'/0'/0'/0/0");
final ethWallet = hdWallet.derivePath("m/44'/60'/0'/0/0");
final tronWallet = hdWallet.derivePath("m/44'/195'/0'/0/0");
👉 Discover how to securely generate and store mnemonic phrases in your app
Now that we have the HD wallets, let’s convert private keys into usable addresses.
Converting Keys to Blockchain Addresses
Each chain encodes addresses differently based on cryptographic hashing and encoding rules.
Bitcoin Address Generation
The flutter_bitcoin
package simplifies this:
final btcAddress = btcWallet.address; // Automatically formatted in Base58Check
Bitcoin uses SHA-256 + RIPEMD-160 hashing, followed by Base58Check encoding. Legacy addresses start with 1
or 3
, while newer ones (SegWit) begin with bc1
.
Ethereum Address Generation
Using web3dart
, extract the address from the private key:
final ethPrivateKey = EthPrivateKey.fromHex(ethWallet.privKey!);
final ethAddress = ethPrivateKey.address.hex;
Ethereum takes the last 20 bytes of the Keccak-256 hash of the public key, prefixed with 0x
.
Tron Address Generation
Tron uses SHA-256 + RIPEMD-160 like Bitcoin but applies Base58Check encoding:
final tronPrivateKey = wallet.PrivateKey(BigInt.parse(tronWallet.privKey!, radix: 16));
final tronPublicKey = wallet.tron.createPublicKey(tronPrivateKey);
final tronAddress = wallet.tron.createAddress(tronPublicKey); // Starts with 'T'
This distinction makes it easy to identify chain types just by looking at the address prefix.
Signing Transactions Across Chains
With addresses and private keys ready, we can now sign transactions.
Ethereum: Account-Based Model
Ethereum uses an account-based model, where each address has a balance and nonce. To send ETH:
- Connect to an Ethereum node (e.g., Alchemy or Infura).
- Build a transaction with
from
,to
,value
, andchainId
. - Sign and broadcast.
const alchemyApiKey = 'your_api_key';
final client = Web3Client('https://eth-sepolia.g.alchemy.com/v2/$alchemyApiKey', Client());
Future<String> signTransaction({
required EthPrivateKey privateKey,
required Transaction transaction,
}) async {
return await client.signTransaction(privateKey, transaction, chainId: 11155111);
}
// Example transaction
final tx = Transaction(
from: ethPrivateKey.address,
to: EthereumAddress.fromHex("0xE2Dc3214f7096a94077E71A3E218243E289F1067"),
value: EtherAmount.fromBase10String(EtherUnit.gwei, "10000"),
);
final rawTx = await signTransaction(privateKey: ethPrivateKey, transaction: tx);
final txHash = await client.sendRawTransaction(Uint8List.fromList(HEX.decode(rawTx)));
print("Transaction Hash: $txHash");
👉 Learn how to integrate secure transaction signing in your Flutter app
Bitcoin: UTXO Model
Unlike Ethereum, Bitcoin uses the UTXO (Unspent Transaction Output) model. A user’s balance isn’t stored directly—it’s the sum of unspent outputs from prior transactions.
When sending BTC:
- You select one or more UTXOs as inputs.
- Define outputs: recipient amount + change back to yourself.
- Pay a miner fee based on transaction size (in bytes) × satoshis per byte.
Example:
String signBitcoinTransaction(HDWallet wallet) {
final txb = TransactionBuilder();
txb.setVersion(1);
// Add input: previous output (txid + vout)
txb.addInput('61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', 0);
// Output: send 12,000 satoshis to recipient
txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000);
// Sign input with private key
txb.sign(vin: 0, keyPair: ECPair.fromWIF(wallet.wif!));
return txb.build().toHex();
}
⚠️ Note: To get real UTXOs, query a blockchain API like Blockchair or QuickNode.
Frequently Asked Questions (FAQ)
Q: Can one mnemonic work for all blockchains?
Yes! Thanks to BIP-44, a single mnemonic phrase can generate compatible wallets across Bitcoin, Ethereum, Tron, and other BIP-44-compliant chains by changing the derivation path.
Q: Why do Tron addresses start with 'T'?
Tron uses Base58Check encoding with a version byte that results in addresses starting with T
. It's similar to Bitcoin but designed specifically for Tron’s network identification.
Q: What is the difference between account-based and UTXO models?
In account-based systems (like Ethereum), balances are tracked per address. In UTXO-based systems (like Bitcoin), ownership is proven by controlling unspent outputs from past transactions.
Q: How do I test my multi-chain wallet without real funds?
Use testnet versions:
- Ethereum Sepolia
- Bitcoin Testnet
- Tron Shasta
Obtain test tokens from respective faucets to simulate real transactions safely.
Q: Is it safe to generate private keys in a mobile app?
Only if done offline and never transmitted. Always ensure mnemonic phrases are stored securely (e.g., device keychain), and avoid logging sensitive data.
Q: Can I add Solana or Polygon support later?
Absolutely. By extending the HD derivation path with their BIP-44 coin types (e.g., Solana = 501), you can support nearly any blockchain.
Final Thoughts
You now have the foundational knowledge to build a secure, multi-chain crypto wallet in Flutter. From generating a mnemonic to deriving addresses and signing transactions on Ethereum and Bitcoin, this setup provides a strong base for further enhancements—like token transfers (ERC-20, TRC-20), gas optimization (EIP-1559), and UI integration.
While Tron transaction signing lacks robust Dart libraries today, you can bridge this gap using REST APIs or native modules. As Web3 adoption grows, so will tooling support in the Flutter ecosystem.
👉 Start building your own Web3 wallet with powerful developer tools
By mastering these patterns, you're not just coding an app—you're empowering users with control over their digital assets across the decentralized web.