Skip to content

8 · React to events

Some events answer a request, and ask hands those to you. Others report something that happened on its own: a balance changed, an invoice was paid, a block confirmed a transaction. You follow those with coordinator.on<E>(). In this last step you listen for them, mine the payment, and watch both wallets settle.

Add this right after initializing, before step 3, so it hears everything:

bin/first_payment.dart (after step 2)
// Step 8: listen for what happens without being asked.
final subscriptions = [
coordinator.on<BalanceUpdatedEvent>().listen((e) =>
print(' [${e.walletId}] balance now ${e.totalBalance} sats')),
coordinator.on<TransactionConfirmedEvent>().listen((e) =>
print(' [${e.walletId}] ${e.txid} confirmed at ${e.blockHeight}')),
coordinator.on<InvoicePaidEvent>().listen((e) =>
print(' [${e.walletId}] invoice ${e.invoiceId} paid: '
'${e.amountReceived} sats in ${e.txid}')),
];

on<E>({walletId}) gives you one kind of event, for one wallet if you name it. BalanceUpdatedEvent is announced whenever a balance changes, so you don’t need to poll GetBalanceQuery.

Mine the payment and wait for both confirmations

Section titled “Mine the payment and wait for both confirmations”
bin/first_payment.dart (step 8)
// Step 8: mine the payment. Each wallet confirms it on its own once the
// proof arrives and matches the header it synced.
await printBalance(coordinator, 'alice'); // inputs held: 0 until it settles
await waitForArcade(payment.txid, {'SEEN_ON_NETWORK', 'SEEN_MULTIPLE_NODES'});
final confirmations = [
for (final walletId in ['alice', 'bob'])
coordinator
.on<TransactionConfirmedEvent>(walletId: walletId)
.firstWhere((e) => e.txid == payment.txid)
.timeout(const Duration(minutes: 3)),
];
await mineBlock();
await Future.wait(confirmations);
await printBalance(coordinator, 'alice');
await printBalance(coordinator, 'bob');
for (final s in subscriptions) {
await s.cancel();
}

You start waiting for the confirmations before mining, so one that arrives quickly isn’t missed. Each wallet confirms the payment independently: ARC reports it MINED with its merkle path, and the wallet checks that path against the header it synced. On localnet this took about a minute.

libspiffy is up; headers at 0
Created alice (root address mgK27BXo3QodQsiRa8gBLF46jYnsY78Yvm)
Created bob (root address mxSu1TZsACrkLcoUDBVjwqDj7qHdYwM2nQ)
Alice receives at mu343b2opCEdXpDRT4wXjnC8qHfee5SqkB
Faucet sent 1000000 sats in a80c33b869c4ef6d0f33698bc48a0c3eb0bd9ed6d9d84786a59ac5c996e8a77c
Mined at height 486
[alice] balance now 1000000 sats
alice: 1000000 sats (confirmed 1000000, unconfirmed 0)
[alice] a80c33b869c4ef6d0f33698bc48a0c3eb0bd9ed6d9d84786a59ac5c996e8a77c confirmed at 486
Bob invoices 100000 sats to mvd5qb56orNeWYdutrXpub9YR1yAJbrUKr
[alice] balance now 0 sats
Alice built c5013d51885cc8e9115b2bdd1865c35866d77e165c6cc25dd564d13f0fa489f5: 533 BEEF bytes, 1 ancestors
[alice] balance now 0 sats
[bob] balance now 0 sats
[bob] balance now 100000 sats
Bob: valid=true broadcasted=true networkStatus=SEEN_ON_NETWORK
[bob] invoice d2fb82ed-424b-4477-9a65-fc8e9394947a paid: 100000 sats in c5013d51885cc8e9115b2bdd1865c35866d77e165c6cc25dd564d13f0fa489f5
alice: 0 sats (confirmed 0, unconfirmed 0)
[alice] balance now 0 sats
[alice] balance now 899977 sats
[bob] c5013d51885cc8e9115b2bdd1865c35866d77e165c6cc25dd564d13f0fa489f5 confirmed at 487
[bob] balance now 100000 sats
[alice] c5013d51885cc8e9115b2bdd1865c35866d77e165c6cc25dd564d13f0fa489f5 confirmed at 487
alice: 899977 sats (confirmed 899977, unconfirmed 0)
[alice] balance now 899977 sats
bob: 100000 sats (confirmed 100000, unconfirmed 0)

Alice paid 100,000 sats and a 23-sat fee, and kept 899,977 in change. Bob has 100,000, confirmed against his own headers. Neither wallet trusted the other or a server for any of it.