Day 12 - Web3 and App: Building a Multi-Chain Wallet and Sending Transactions

·

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:


Generating a Multi-Chain Wallet in Flutter

To create a wallet that supports Bitcoin, Ethereum, and Tron, we use the following Dart packages:

Each blockchain uses a specific Coin Type under BIP-44:

ChainCoin Type
Bitcoin0
Ethereum60
Tron195

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:

  1. Connect to an Ethereum node (e.g., Alchemy or Infura).
  2. Build a transaction with from, to, value, and chainId.
  3. 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:

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:

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.