Webux Lab

By Studio Webux

Prototype to setup cardano with Flutter

TG
Tommy Gingras Studio Webux 2025-01-18

Flutter (Dart) with Cardano Blockchain

This is only a MVP and the goal is to learn Dart.

Goals

Todos

Requirements

The “Backend”

Built with deno and cardano serialization lib.

Here are the snippets I used to test:

Create a transaction

You need the list of UTXOs, in this case I had only one. You can get it from multiple places, I used eternl and copied the utxo details manually.

Easiest way to retrieve the string[] of utxos, ìn your browser console, do the following:

const w = await cardano.eternl.enable()
await w.getUtxos()
import { TxBuilder } from "jsr:@studiowebux/cardano";

const tx_builder = new TxBuilder()
  .with_receiver_address(
    "addr_test1qrdt5dsgs4mcy8ynpnu9a8xr5pfknx0ad3c7y36afa6t0hwr24y3xm20jlxu4t8yhue2tpqr587np3f94hdhvlw9ecmqjvt49r",
  )
  .with_sender_address(
    "addr_test1qra369fzgacfz9edsnel4kcpx8r9d7dqc8rfhvqmpajzn6pu8sm3t2e5gfnfsn7328k0rtde0yytk7gnjaau45z2cl6q0pxm0c",
  )
  // support an array of utxos as well.
  .with_utxos([
    {
      output_index: 0,
      address:
        "addr_test1qra369fzgacfz9edsnel4kcpx8r9d7dqc8rfhvqmpajzn6pu8sm3t2e5gfnfsn7328k0rtde0yytk7gnjaau45z2cl6q0pxm0c",
      amount: [{ quantity: "10000000", unit: "lovelace" }],
      tx_hash:
        "c00cc77e0d51c301b9f77a7a57cdc43551c39c1b3351b54b11666bcdefd40196",
    },
  ])
  .with_ada_to_send(5_000_000)
  .build();
const tip_slot = 1_000_000_000_000;
tx_builder
  .add_tx_metadata(["Sent using webux cardano tx builder"])
  .parse_utxos()
  .set_ttl(tip_slot)
  .add_output() // Send 5 ADA to receiver address
  .add_inputs()
  .build_body_and_hash()
  .add_signers()
  .build_tx()
  .assemble_tx();

console.log(tx_builder.get_hash()?.to_hex());
// you are gonna need this tx hex for the next step.
console.log(tx_builder.get_unsigned_tx()?.to_hex());

Prepare the TX to be sent to the Flutter app

There is no API or anything, this is only a learning exercice.

So you are gonna need to do the following:

import { TransactionWitnessSet, FixedTransaction } from "@studiowebux/cardano";

const tx = FixedTransaction.from_hex(
  "84a500d9010281825820c00cc77e0d51c301b9f77a7a57cdc43551c39c1b3351b54b11666bcdefd4019600018282583900daba36088577821c930cf85e9cc3a0536999fd6c71e2475d4f74b7ddc35549136d4f97cdcaace4bf32a58403a1fd30c525addb767dc5ce361a004c4b4082583900fb1d1522477091172d84f3fadb0131c656f9a0c1c69bb01b0f6429e83c3c3715ab344266984fd151ecf1adb97908bb7913977bcad04ac7f41a0049aa1f021a0002a121031b000000e8d4a5100007582097b9aca0f75f27cae8f41c8806f8f7ec5ad13e5f68735725a57d5ad2b206b1e6a0f5a11902a2a1636d736781782353656e74207573696e672077656275782063617264616e6f207478206275696c646572",
);

// This is the body you need in the dart program.
console.log("body", tx.body().to_hex());

The Fronted “Dart”

import 'package:bip32_ed25519/cardano.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:cbor/cbor.dart';
import 'package:hex/hex.dart';

Future<void> main() async {
  // // 24 words mnemonic (use 128 to get 12 words and 256 for 24 words.)
  // // https://pub.dev/documentation/bip39/latest/
  // Doing so will override the last one everytime...
  // var mnemonic = bip39.generateMnemonic(strength: 256);

  const mnemonic =
      'PASTE ONE OF THE MNEMONIC GENERATED WITH THE CLI TOOL';

  final entropy = bip39.mnemonicToEntropy(mnemonic);
  final icarusKeyTree = CardanoIcarusKey.seed(entropy);

  // https://cips.cardano.org/cip/CIP-1852
  String wallet0 = "m/1852'/1815'/0'/0/0";
  String stakePath = "m/1852'/1815'/0'/2/0";
  String privateKeyPath = "m/1852'/1815'/0'";

  final sk = icarusKeyTree.pathToKey(privateKeyPath).derive(0).derive(0)
      as Bip32SigningKey;
  final pk = icarusKeyTree.pathToKey(wallet0).publicKey as Bip32Key;
  final paymentPart = Hash.blake2b(pk.rawKey.asTypedList, digestSize: 28);

  final spk = icarusKeyTree.pathToKey(stakePath).publicKey as Bip32Key;
  final stakePart = Hash.blake2b(spk.rawKey.asTypedList, digestSize: 28);

  // https://cardano.stackexchange.com/a/7059
  // https://github.com/cardano-foundation/CIPs/blob/master/CIP-0019/README.md
  final addrTestnet = ByteList([0x00] + paymentPart + stakePart)
      .encode(Bech32Encoder(hrp: 'addr_test'));

  final addrMainnet = ByteList([0x01] + paymentPart + stakePart)
      .encode(Bech32Encoder(hrp: 'addr'));

  // https://cardano.stackexchange.com/a/4108
  // https://github.com/IntersectMBO/cardano-ledger/blob/f2a783cf00911b7492e81dd6c7fb8a963f9ce8fe/eras/shelley/test-suite/cddl-files/shelley.cddl
  final stakeTest =
      ByteList([0xE0] + stakePart).encode(Bech32Encoder(hrp: 'stake_test'));
  final stakeMainnet =
      ByteList([0xE1] + stakePart).encode(Bech32Encoder(hrp: 'stake'));

  print('Address Testnet: $addrTestnet');
  print('Address Mainnet: $addrMainnet');
  print('Stake test: $stakeTest');
  print('Stake mainnet: $stakeMainnet');

  print("pk Hex: ${HEX.encode(pk.rawKey)}");
  print("sk Hex: ${HEX.encode(sk.rawKey)}");

  print("keyhash: ${HEX.encode(paymentPart)}");

  // To get the hash we need only the body (do not keep the aux and witnesses)
  final hash = Hash.blake2b(
      HEX.decode(
          "PASTE_THE_BODY_HEX_FROM_DENO_HERE_ULTIMATELY_THIS_WILL_BE_FETCHED"),
      digestSize: 32);

  print("tx hash: ${HEX.encode(hash)}");

  final signed = sk.sign(hash);
  print("Signature: ${HEX.encode(signed.prefix)}");

  final pkEd = pk.rawKey.encode(Bech32Encoder(hrp: 'ed25519_pk'));
  print(pkEd);

  final witnessSet = CborMap(
    {
      CborSmallInt(0): CborList(
        CborList([
          CborList(
            [CborBytes(pk.rawKey), CborBytes(signed.prefix)],
          ),
        ]),
        tags: [258],
      ),
    },
  );

  print(HEX.encode(cbor.encode(witnessSet)));
  // Paste this output in the next step
  // The goal will be to send this tx back to the backend to then submit it onto the cardano network.
}

Finalize Transaction and Submit

// replace with the a100.. printed from the flutter app.
tx.add_vkey_witness(
  TransactionWitnessSet.from_hex(
    "a10081825820e482b66b52779cd4641129cff01c718f97aab61521f167307a2327b703f939d25840c96a86e7f0ae6494c7a8c47a179b548a7f5b4932e2b13088f6da1e199517ea1c672ccb57763ad53cbe75814f1b87ff33aa7985514ef7f3c889a01665f1e2c109",
  )
    .vkeys()
    ?.get(0)!,
);

console.log("TX Hash", tx.transaction_hash().to_hex());
// Submit this tx to the network.
console.log("TX hex", tx.to_hex());

You can use the browser console like that:

await w.submitTx("your-signed-tx-hex-from-above")

Or with Eternl Wallet, click “Send”, then “Options”, then “import transaction” and paste the CBOR hex.

Conclusion

I have learnt a lot of concepts for cardano !

But you should use an existing and supported library to do all of that.

References

My open source projects made with Deno


Search