Flutter and Cardano
Reviewed the code and improved everything. Built a class to simplified the integration
My goal is simple, I have a deno backend that creates the Transaction, the mobile app has its own wallet and is allowed to sign the transactions. Then it has to send back the signature and the backend handle the rest.
Dependencies
# Cardano
bip32_ed25519: ^0.6.2
bip39: ^1.0.6
hex: ^0.2.0
cbor: ^6.3.3
google_fonts: ^6.2.1
Flutter theme
Probably broken tho…
final lightThemeBW = ThemeData(
fontFamily: GoogleFonts.robotoMono().fontFamily,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor: Color(0xFFEBEBEB),
backgroundColor: Color(0xFF111111),
),
),
colorScheme: ColorScheme(
brightness: Brightness.light,
primary: Color(0xFF212121),
onPrimary: Color(0xFFEBEBEB),
primaryContainer: Color(0xFF333333),
onPrimaryContainer: Color(0xFFEBEBEB),
secondary: Color(0xFFFFBF00),
onSecondary: Color(0xFF333333),
secondaryContainer: Color(0xFFEBEBEB),
onSecondaryContainer: Color(0xFF212121),
error: Color(0xFF93000A),
onError: Color(0xFF690005),
errorContainer: Color(0xFF93000A),
onErrorContainer: Color(0xFFFFDAD6),
surface: Color(0xFF333333),
onSurface: Color(0xFF000000),
surfaceContainerHighest: Color(0xFF212121),
onSurfaceVariant: Color(0xFFFFFFFF),
outline: Color(0xFF111111),
shadow: Color(0xFF000000),
inverseSurface: Color(0xFF333333),
onInverseSurface: Color(0xFFFFFFFF),
inversePrimary: Color(0xFFEBEBEB),
),
useMaterial3: false,
);
Cardano Dart Class
You can use this class in your project, it allows to setup a wallet and sign transactions. You are gonna need a backend to create the transaction, fetch the tx body serialized in cbor format. Then once the transaction is signed, you are gonna need an endpoint to rebuild the transaction and submit it on-chain (see part 1 for Deno examples)
Part 1: https://webuxlab.com/en/tutorials/flutter-cardano
import 'package:bip32_ed25519/cardano.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:cbor/cbor.dart';
import 'package:hex/hex.dart';
class Cardano {
String mnemonic = "";
String entropy = "";
late CardanoIcarusKey wallet;
// Paths
String index = "0"; // wallet index
String wallet0 = "m/1852'/1815'/0'/0/"; // + index
String stakePath = "m/1852'/1815'/0'/2/"; // + index
String privateKeyPath = "m/1852'/1815'/0'";
// Wallet info
late Bip32SigningKey sk;
late Bip32Key pk;
late Uint8List paymentPart;
late Bip32Key spk;
late Uint8List stakePart;
String get addressTestnet => ByteList([0x00] + paymentPart + stakePart)
.encode(Bech32Encoder(hrp: 'addr_test'));
String get addressMainnet => ByteList([0x01] + paymentPart + stakePart)
.encode(Bech32Encoder(hrp: 'addr'));
String get stakeAddressTestnet =>
ByteList([0xE0] + stakePart).encode(Bech32Encoder(hrp: 'stake_test'));
String get stakeAddressMainnet =>
ByteList([0xE1] + stakePart).encode(Bech32Encoder(hrp: 'stake'));
String get pkHex => HEX.encode(pk.rawKey);
String get pkEd => pk.rawKey.encode(Bech32Encoder(hrp: 'ed25519_pk'));
String get skHex => HEX.encode(sk.rawKey);
String get keyhash => HEX.encode(paymentPart);
void createOrRestoreWallet(String? index, String? mnemonic) {
if (mnemonic == null || mnemonic.isEmpty) {
print("Creating new wallet.");
this.mnemonic = bip39.generateMnemonic(strength: 256);
} else {
print("Restoring wallet from seedphrases.");
this.mnemonic = mnemonic;
}
if (index != null && index.isNotEmpty) {
print("Wallet Index: $index");
this.index = index;
}
entropy = bip39.mnemonicToEntropy(mnemonic);
wallet = CardanoIcarusKey.seed(entropy);
sk =
wallet.pathToKey(privateKeyPath).derive(0).derive(0) as Bip32SigningKey;
pk = wallet.pathToKey(wallet0 + this.index).publicKey as Bip32Key;
paymentPart = Hash.blake2b(pk.rawKey.asTypedList, digestSize: 28);
spk = wallet.pathToKey(stakePath + this.index).publicKey as Bip32Key;
stakePart = Hash.blake2b(spk.rawKey.asTypedList, digestSize: 28);
}
String signTransaction(String txBodyHex) {
final hash = Hash.blake2b(HEX.decode(txBodyHex), digestSize: 32);
final txHash = HEX.encode(hash);
print("TX Hash: $txHash");
final signature = sk.sign(hash);
// Limited Implementation
// Only vkeys are implementeds
final witnessSet = CborMap(
{
CborSmallInt(0): CborList(
CborList([
CborList(
[CborBytes(pk.rawKey), CborBytes(signature.prefix)],
),
]),
tags: [258],
),
},
);
final cborHex = HEX.encode(cbor.encode(witnessSet));
print("Witness set: $cborHex");
return cborHex;
}
}
Cardano Flutter Page
import 'package:flutter/material.dart';
import 'package:my_app/data/cardano.dart';
class CardanoPage extends StatefulWidget {
const CardanoPage({super.key});
@override
State<StatefulWidget> createState() => _CardanoPageState();
}
class _CardanoPageState extends State<CardanoPage> {
Cardano wallet = Cardano();
TextEditingController txInputController = TextEditingController();
@override
initState() {
setState(() {
wallet.createOrRestoreWallet("0",
"ADD YOUR 24 WORDS MNEMONIC");
});
super.initState();
}
void signTx() {
final cborHex = wallet.signTransaction(txInputController.text);
print(cborHex);
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: SizedBox(
height: 200,
child: Column(
children: [
SelectableText(cborHex),
],
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
// wallet info
Padding(
padding: const EdgeInsets.all(25.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(wallet.addressMainnet),
SizedBox(height: 8),
Text(wallet.stakeAddressMainnet),
SizedBox(height: 8),
Text(wallet.addressTestnet),
SizedBox(height: 8),
Text(wallet.stakeAddressTestnet),
SizedBox(height: 8),
Text(wallet.keyhash),
],
),
),
// sign tx
Padding(
padding: const EdgeInsets.all(25.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: txInputController,
maxLines: 4, //or null
decoration: InputDecoration.collapsed(
hintText: "Enter Transaction cbor hex",
border: OutlineInputBorder(),
),
),
ElevatedButton(
onPressed: signTx,
child: Text("Sign Transaction"),
),
],
),
),
],
),
),
);
}
}
Screenshots
Conclusion
Flutter and Dart are pretty awesome tech !