diff --git a/lib/data/models/multi_wallet_transaction_cubit.dart b/lib/data/models/multi_wallet_transaction_cubit.dart
index 02b5d06fdc66e3a7a91fb4720d7097771382c871..6618e3f959b46de39414772f30f43bb14f718809 100644
--- a/lib/data/models/multi_wallet_transaction_cubit.dart
+++ b/lib/data/models/multi_wallet_transaction_cubit.dart
@@ -12,6 +12,8 @@ import '../../shared_prefs_helper.dart';
 import '../../ui/logger.dart';
 import '../../ui/notification_controller.dart';
 import '../../ui/pay_helper.dart';
+import '../../ui/ui_helpers.dart';
+import '../../ui/widgets/connectivity_widget_wrapper_wrapper.dart';
 import 'app_cubit.dart';
 import 'contact.dart';
 import 'multi_wallet_transaction_state.dart';
@@ -22,6 +24,7 @@ import 'transaction.dart';
 import 'transaction_state.dart';
 import 'transaction_type.dart';
 import 'transactions_bloc.dart';
+import 'utxo_cubit.dart';
 
 class MultiWalletTransactionCubit
     extends HydratedCubit<MultiWalletTransactionState> {
@@ -51,7 +54,8 @@ class MultiWalletTransactionCubit
     _emitState(key, newState);
   }
 
-  void _emitState(String key, TransactionState newState) {
+  void _emitState(String keyRaw, TransactionState newState) {
+    final String key = extractPublicKey(keyRaw);
     final Map<String, TransactionState> newStates =
         Map<String, TransactionState>.of(state.map)..[key] = newState;
     emit(MultiWalletTransactionState(newStates));
@@ -62,7 +66,8 @@ class MultiWalletTransactionCubit
     return key;
   }
 
-  TransactionState _getStateOfWallet(String key) {
+  TransactionState _getStateOfWallet(String keyRaw) {
+    final String key = extractPublicKey(keyRaw);
     final TransactionState currentState =
         state.map[key] ?? TransactionState.emptyState;
     return currentState;
@@ -85,7 +90,7 @@ class MultiWalletTransactionCubit
     final List<Transaction> newPendingTransactions = <Transaction>[];
     for (final Transaction t in currentState.pendingTransactions) {
       if (tx.from == t.from &&
-          tx.to == t.to &&
+          tx.recipients == t.recipients &&
           tx.amount == t.amount &&
           tx.comment == t.comment) {
         newPendingTransactions
@@ -118,10 +123,12 @@ class MultiWalletTransactionCubit
 
   DateTime get lastChecked => currentWalletState().lastChecked;
 
-  String _getTxKey(Transaction t) => '${t.to.pubKey}-${t.comment}-${t.amount}';
+  String _getTxKey(Transaction t) => t.isToMultiple
+      ? '${t.recipients.map((Contact c) => c.pubKey).join('-')}-${t.comment}-${t.amount}'
+      : '${t.to.pubKey}-${t.comment}-${t.amount}';
 
   Future<List<Transaction>> fetchTransactions(
-      NodeListCubit cubit, AppCubit appCubit,
+      NodeListCubit cubit, UtxoCubit utxoCubit, AppCubit appCubit,
       {int retries = 5, int? pageSize, String? cursor, String? pubKey}) async {
     pubKey = _defKey(pubKey);
     final TransactionState currentState = _getStateOfWallet(pubKey);
@@ -145,7 +152,7 @@ class MultiWalletTransactionCubit
 
       final Map<String, dynamic> txData = txDataResult.item1!;
       final TransactionState newParsedState =
-          await transactionsGvaParser(txData, currentState);
+          await transactionsGvaParser(txData, currentState, pubKey);
 
       if (newParsedState.balance < 0) {
         logger('Warning: Negative balance in node ${txDataResult.item2}');
@@ -175,6 +182,8 @@ class MultiWalletTransactionCubit
       logger(
           'Last sent notification: ${currentModifiedState.latestSentNotification.toIso8601String()})}');
 
+      logger(
+          '>>>>>>>>>>>>>>>>>>> Transactions: ${currentModifiedState.transactions.length}, wallets ${state.map.length}');
       for (final Transaction tx in currentModifiedState.transactions.reversed) {
         bool stateModified = false;
 
@@ -197,13 +206,13 @@ class MultiWalletTransactionCubit
         if (tx.type == TransactionType.sent &&
             currentModifiedState.latestSentNotification.isBefore(tx.time)) {
           // Future
-          final Contact to = tx.to;
           NotificationController.notifyTransaction(
               tx.time.millisecondsSinceEpoch.toString(),
               amount: -tx.amount,
               currentUd: appCubit.currentUd,
               comment: tx.comment,
-              to: to.title,
+              to: humanizeContacts(
+                  fromAddress: tx.from.pubKey, contacts: tx.recipients),
               isG1: isG1);
           currentModifiedState =
               currentModifiedState.copyWith(latestSentNotification: tx.time);
@@ -352,4 +361,35 @@ class MultiWalletTransactionCubit
     }
     return newState;
   }
+
+  Future<void> clearState() async {
+    final Set<String> keys = <String>{};
+    for (final String keyRaw in state.map.keys) {
+      final String key = extractPublicKey(keyRaw);
+      keys.add(key);
+    }
+
+    // remove old key:hash keys in state that wre duplicates
+    final MultiWalletTransactionState newState = state.copyWith();
+    final Set<String> mapKeys = Set<String>.from(state.map.keys);
+
+    for (final String key in mapKeys) {
+      if (!keys.contains(key)) {
+        newState.map.remove(key);
+      }
+    }
+
+    emit(newState);
+
+    // Clear tx if is connected and refresh
+    final bool isConnected = await ConnectivityWidgetWrapperWrapper.isConnected;
+    if (isConnected) {
+      for (final String key in state.map.keys) {
+        final TransactionState currentState = _getStateOfWallet(key);
+        final TransactionState newState =
+            currentState.copyWith(transactions: <Transaction>[]);
+        _emitState(key, newState);
+      }
+    }
+  }
 }
diff --git a/lib/data/models/transaction.dart b/lib/data/models/transaction.dart
index bf03832af9068bc6053f26401caf4ef78d5a0537..7431add88033c2aa0d46d317897becb0679c8178 100644
--- a/lib/data/models/transaction.dart
+++ b/lib/data/models/transaction.dart
@@ -17,19 +17,27 @@ class Transaction extends Equatable {
       required this.comment,
       required this.time,
       required this.from,
-      required this.to,
-      this.debugInfo});
+      @Deprecated('Use recipients instead') required this.to,
+      // Old tx does not store outputs so let it be null
+      List<Contact>? recipients,
+      List<double>? recipientsAmounts,
+      this.debugInfo})
+      : recipients = recipients ?? const <Contact>[],
+        recipientsAmounts = recipientsAmounts ?? const <double>[];
 
   factory Transaction.fromJson(Map<String, dynamic> json) =>
       _$TransactionFromJson(json);
 
   final TransactionType type;
   final Contact from;
+  @Deprecated('Use recipients instead')
   final Contact to;
   final double amount;
   final String comment;
   final DateTime time;
   final String? debugInfo;
+  final List<Contact> recipients;
+  final List<double> recipientsAmounts;
 
   bool get isFailed => type == TransactionType.failed;
 
@@ -41,12 +49,23 @@ class Transaction extends Equatable {
   bool get isIncoming =>
       type == TransactionType.receiving || type == TransactionType.received;
 
+  bool get isToMultiple => recipients.length > 2;
+
   Map<String, dynamic> toJson() => _$TransactionToJson(this);
 
   String toStringSmall(String pubKey) =>
       "Transaction { type: ${type.name}, from: ${from.toStringSmall(pubKey)}, to: ${to.toStringSmall(pubKey)}, amount: $amount, comment: $comment, time: ${humanizeTime(time, 'en')}, debugInfo: '$debugInfo' }";
 
   @override
-  List<Object?> get props =>
-      <dynamic>[type, from, to, amount, comment, time, debugInfo];
+  List<Object?> get props => <dynamic>[
+        type,
+        from,
+        to,
+        amount,
+        comment,
+        time,
+        debugInfo,
+        recipients,
+        recipientsAmounts
+      ];
 }
diff --git a/lib/data/models/transaction.g.dart b/lib/data/models/transaction.g.dart
index b721984bab7b327a763e5402896308bd3ac27967..303d116937cfb41b4f4a4ceaff7a2d02162e3a5e 100644
--- a/lib/data/models/transaction.g.dart
+++ b/lib/data/models/transaction.g.dart
@@ -19,6 +19,10 @@ abstract class _$TransactionCWProxy {
 
   Transaction to(Contact to);
 
+  Transaction recipients(List<Contact>? recipients);
+
+  Transaction recipientsAmounts(List<double>? recipientsAmounts);
+
   Transaction debugInfo(String? debugInfo);
 
   /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `Transaction(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
@@ -34,6 +38,8 @@ abstract class _$TransactionCWProxy {
     DateTime? time,
     Contact? from,
     Contact? to,
+    List<Contact>? recipients,
+    List<double>? recipientsAmounts,
     String? debugInfo,
   });
 }
@@ -62,6 +68,14 @@ class _$TransactionCWProxyImpl implements _$TransactionCWProxy {
   @override
   Transaction to(Contact to) => this(to: to);
 
+  @override
+  Transaction recipients(List<Contact>? recipients) =>
+      this(recipients: recipients);
+
+  @override
+  Transaction recipientsAmounts(List<double>? recipientsAmounts) =>
+      this(recipientsAmounts: recipientsAmounts);
+
   @override
   Transaction debugInfo(String? debugInfo) => this(debugInfo: debugInfo);
 
@@ -80,6 +94,8 @@ class _$TransactionCWProxyImpl implements _$TransactionCWProxy {
     Object? time = const $CopyWithPlaceholder(),
     Object? from = const $CopyWithPlaceholder(),
     Object? to = const $CopyWithPlaceholder(),
+    Object? recipients = const $CopyWithPlaceholder(),
+    Object? recipientsAmounts = const $CopyWithPlaceholder(),
     Object? debugInfo = const $CopyWithPlaceholder(),
   }) {
     return Transaction(
@@ -107,6 +123,14 @@ class _$TransactionCWProxyImpl implements _$TransactionCWProxy {
           ? _value.to
           // ignore: cast_nullable_to_non_nullable
           : to as Contact,
+      recipients: recipients == const $CopyWithPlaceholder()
+          ? _value.recipients
+          // ignore: cast_nullable_to_non_nullable
+          : recipients as List<Contact>?,
+      recipientsAmounts: recipientsAmounts == const $CopyWithPlaceholder()
+          ? _value.recipientsAmounts
+          // ignore: cast_nullable_to_non_nullable
+          : recipientsAmounts as List<double>?,
       debugInfo: debugInfo == const $CopyWithPlaceholder()
           ? _value.debugInfo
           // ignore: cast_nullable_to_non_nullable
@@ -132,6 +156,12 @@ Transaction _$TransactionFromJson(Map<String, dynamic> json) => Transaction(
       time: DateTime.parse(json['time'] as String),
       from: Contact.fromJson(json['from'] as Map<String, dynamic>),
       to: Contact.fromJson(json['to'] as Map<String, dynamic>),
+      recipients: (json['recipients'] as List<dynamic>?)
+          ?.map((e) => Contact.fromJson(e as Map<String, dynamic>))
+          .toList(),
+      recipientsAmounts: (json['recipientsAmounts'] as List<dynamic>?)
+          ?.map((e) => (e as num).toDouble())
+          .toList(),
       debugInfo: json['debugInfo'] as String?,
     );
 
@@ -144,6 +174,8 @@ Map<String, dynamic> _$TransactionToJson(Transaction instance) =>
       'comment': instance.comment,
       'time': instance.time.toIso8601String(),
       'debugInfo': instance.debugInfo,
+      'recipients': instance.recipients,
+      'recipientsAmounts': instance.recipientsAmounts,
     };
 
 const _$TransactionTypeEnumMap = {
diff --git a/lib/data/models/transaction_cubit_remove.dart b/lib/data/models/transaction_cubit_remove.dart
index c8b142feaffe781abfa9595278878158bec69ddd..bb0cc995d9fed6db162578cda7588365f6a29ecf 100644
--- a/lib/data/models/transaction_cubit_remove.dart
+++ b/lib/data/models/transaction_cubit_remove.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: deprecated_member_use_from_same_package
+
 import 'dart:collection';
 
 import 'package:flutter/foundation.dart';
@@ -74,7 +76,8 @@ class TransactionCubitRemove extends HydratedCubit<TransactionState> {
       }
 
       final Map<String, dynamic> txData = txDataResult.item1!;
-      TransactionState newState = await transactionsGvaParser(txData, state);
+      TransactionState newState =
+          await transactionsGvaParser(txData, state, myPubKey);
 
       if (newState.balance < 0) {
         logger('Warning: Negative balance in node ${txDataResult.item2}');
diff --git a/lib/data/models/transactions_bloc.dart b/lib/data/models/transactions_bloc.dart
index db268e2652a04c36a4454b578b3687878de5e399..30912f434ed60a0ac6d046328c5e89cbfb607c0f 100644
--- a/lib/data/models/transactions_bloc.dart
+++ b/lib/data/models/transactions_bloc.dart
@@ -10,6 +10,7 @@ import 'app_cubit.dart';
 import 'multi_wallet_transaction_cubit.dart';
 import 'node_list_cubit.dart';
 import 'transaction.dart';
+import 'utxo_cubit.dart';
 
 part 'transactions_state.dart';
 
@@ -29,6 +30,7 @@ class TransactionsBloc {
   late AppCubit appCubit;
   late NodeListCubit nodeListCubit;
   late MultiWalletTransactionCubit transCubit;
+  late UtxoCubit utxoCubit;
 
   static const int _pageSize = 20;
 
@@ -71,10 +73,11 @@ class TransactionsBloc {
   }
 
   void init(MultiWalletTransactionCubit transCubit, NodeListCubit nodeListCubit,
-      AppCubit appCubit) {
+      AppCubit appCubit, UtxoCubit utxoCubit) {
     this.appCubit = appCubit;
     this.transCubit = transCubit;
     this.nodeListCubit = nodeListCubit;
+    this.utxoCubit = utxoCubit;
   }
 
   Stream<TransactionsState> _fetchTransactionsList(String? pageKey) async* {
@@ -98,8 +101,8 @@ class TransactionsBloc {
           itemList: transCubit.transactions,
         );
       } else {
-        final List<Transaction> fetchedItems =
-            await transCubit.fetchTransactions(nodeListCubit, appCubit,
+        final List<Transaction> fetchedItems = await transCubit
+            .fetchTransactions(nodeListCubit, utxoCubit, appCubit,
                 cursor: pageKey, pageSize: _pageSize);
 
         final bool isLastPage = fetchedItems.length < _pageSize;
diff --git a/lib/data/models/utxo_cubit.dart b/lib/data/models/utxo_cubit.dart
index 8db41be917097b49ad8900b78adb78c08433a111..e6a32725ece363bed80df88dd72a5213fca1669b 100644
--- a/lib/data/models/utxo_cubit.dart
+++ b/lib/data/models/utxo_cubit.dart
@@ -3,6 +3,7 @@ import 'package:hydrated_bloc/hydrated_bloc.dart';
 import 'package:tuple/tuple.dart';
 
 import '../../g1/api.dart';
+import '../../ui/logger.dart';
 import 'node.dart';
 import 'utxo.dart';
 import 'utxo_state.dart';
@@ -15,7 +16,11 @@ class UtxoCubit extends HydratedCubit<UtxoState> {
 
   @override
   UtxoState? fromJson(Map<String, dynamic> json) {
-    return UtxoLoaded.fromJson(json);
+    try {
+      return UtxoLoaded.fromJson(json);
+    } catch (e) {
+      return UtxoInitial();
+    }
   }
 
   @override
@@ -34,6 +39,8 @@ class UtxoCubit extends HydratedCubit<UtxoState> {
                 pubKeyRaw: myPubKey,
                 cursor:
                     state is UtxoLoaded ? (state as UtxoLoaded).cursor : null);
+        loggerDev('utxoDataResult: $utxoDataResult');
+
         if (utxoDataResult.item1 != null) {
           final List<Utxo> utxos = <Utxo>[];
           double total = state is UtxoLoaded ? (state as UtxoLoaded).total : 0;
@@ -57,7 +64,7 @@ class UtxoCubit extends HydratedCubit<UtxoState> {
     }
   }
 
-  List<Utxo> consume(double amount) {
+  List<Utxo>? consume(double amount) {
     if (state is UtxoLoaded) {
       final UtxoLoaded currentState = state as UtxoLoaded;
 
@@ -76,8 +83,9 @@ class UtxoCubit extends HydratedCubit<UtxoState> {
       }
 
       if (total < amount) {
-        emit(UtxosError('Insufficient UTXOs to cover the requested amount'));
-        return <Utxo>[];
+        const String error = 'Insufficient UTXOs to cover the requested amount';
+        emit(UtxosError(error));
+        throw Exception(error);
       }
 
       final List<Utxo> updatedUtxos = currentState.utxos
@@ -87,8 +95,9 @@ class UtxoCubit extends HydratedCubit<UtxoState> {
       emit(currentState.copyWith(utxos: updatedUtxos));
       return selectedUtxos;
     } else {
-      emit(UtxosError('Wrong utxo state'));
-      return <Utxo>[];
+      const String error = 'Wrong utxo state';
+      emit(UtxosError(error));
+      throw Exception(error);
     }
   }
 
diff --git a/lib/g1/api.dart b/lib/g1/api.dart
index 86ab470fe7294f88a8c11ebd59644121d80ca778..38b4e0f176df5b93ce3b3f44b223a95304afe894 100644
--- a/lib/g1/api.dart
+++ b/lib/g1/api.dart
@@ -1,5 +1,6 @@
 import 'dart:convert';
 import 'dart:io';
+import 'dart:math';
 
 import 'package:crypto/crypto.dart';
 import 'package:durt/durt.dart';
@@ -15,6 +16,7 @@ import '../data/models/contact.dart';
 import '../data/models/node.dart';
 import '../data/models/node_manager.dart';
 import '../data/models/node_type.dart';
+import '../data/models/utxo.dart';
 import '../shared_prefs_helper.dart';
 import '../ui/logger.dart';
 import '../ui/ui_helpers.dart';
@@ -31,7 +33,8 @@ final String currency = currencyDotEnv.isEmpty ? 'g1' : currencyDotEnv;
 
 Future<String> getTxHistory(String publicKey) async {
   final Response response =
-      await requestWithRetry(NodeType.duniter, '/tx/history/$publicKey');
+      (await requestWithRetry(NodeType.duniter, '/tx/history/$publicKey'))
+          .item2;
   if (response.statusCode == 200) {
     return response.body;
   } else {
@@ -40,9 +43,10 @@ Future<String> getTxHistory(String publicKey) async {
 }
 
 Future<Response> getPeers() async {
-  final Response response = await requestWithRetry(
-      NodeType.duniter, '/network/peers',
-      dontRecord: true);
+  final Response response = (await requestWithRetry(
+          NodeType.duniter, '/network/peers',
+          dontRecord: true))
+      .item2;
   if (response.statusCode == 200) {
     return response;
   } else {
@@ -60,7 +64,7 @@ Future<Response> searchCPlusUser(String initialSearchTerm) async {
       '/user/profile/_search?q=title:$searchTermLower OR issuer:$searchTerm OR title:$searchTermCapitalized OR title:$searchTerm';
 
   final Response response =
-      await requestCPlusWithRetry(query, retryWith404: false);
+      (await requestCPlusWithRetry(query, retryWith404: false)).item2;
   return response;
 }
 
@@ -68,9 +72,10 @@ Future<Contact> getProfile(String pubKeyRaw,
     [bool onlyCPlusProfile = false]) async {
   final String pubKey = extractPublicKey(pubKeyRaw);
   try {
-    final Response cPlusResponse = await requestCPlusWithRetry(
-        '/user/profile/$pubKey',
-        retryWith404: false);
+    final Response cPlusResponse = (await requestCPlusWithRetry(
+            '/user/profile/$pubKey',
+            retryWith404: false))
+        .item2;
     final Map<String, dynamic> result =
         const JsonDecoder().convert(cPlusResponse.body) as Map<String, dynamic>;
     if (result['found'] == false) {
@@ -108,9 +113,10 @@ Not found sample:
  */
 Future<List<Contact>> searchWot(String initialSearchTerm) async {
   final String searchTerm = normalizeQuery(initialSearchTerm);
-  final Response response = await requestDuniterWithRetry(
-      '/wot/lookup/$searchTerm',
-      retryWith404: false);
+  final Response response = (await requestDuniterWithRetry(
+          '/wot/lookup/$searchTerm',
+          retryWith404: false))
+      .item2;
   // Will be better to analyze the 404 response (to detect faulty node)
   final List<Contact> contacts = <Contact>[];
   if (response.statusCode == HttpStatus.ok) {
@@ -136,9 +142,10 @@ Future<List<Contact>> searchWot(String initialSearchTerm) async {
 }
 
 Future<Contact> getWot(Contact contact) async {
-  final Response response = await requestDuniterWithRetry(
-      '/wot/lookup/${contact.pubKey}',
-      retryWith404: false);
+  final Response response = (await requestDuniterWithRetry(
+          '/wot/lookup/${contact.pubKey}',
+          retryWith404: false))
+      .item2;
   // Will be better to analyze the 404 response (to detect faulty node)
   if (response.statusCode == HttpStatus.ok) {
     final Map<String, dynamic> data =
@@ -159,7 +166,7 @@ Future<Contact> getWot(Contact contact) async {
 @Deprecated('use getProfile')
 Future<String> _getDataImageFromKey(String publicKey) async {
   final Response response =
-      await requestCPlusWithRetry('/user/profile/$publicKey');
+      (await requestCPlusWithRetry('/user/profile/$publicKey')).item2;
   if (response.statusCode == HttpStatus.ok) {
     final Map<String, dynamic> data =
         json.decode(response.body) as Map<String, dynamic>;
@@ -214,12 +221,13 @@ Future<void> _fetchDuniterNodes({bool force = false}) async {
   const NodeType type = NodeType.duniter;
   NodeManager().loading = true;
   final bool forceOrFewNodes =
-      force || NodeManager().nodesWorking(type) < NodeManager.maxNodes;
+      force || (NodeManager().nodesWorking(type) < NodeManager.maxNodes);
   if (forceOrFewNodes) {
+    defaultDuniterNodes.shuffle();
     NodeManager().updateNodes(type, defaultDuniterNodes);
   }
   logger(
-      'Fetching ${type.name} nodes, we have ${NodeManager().nodesWorking(type)}');
+      'Fetching (forced: $force) ${type.name} nodes, we have ${NodeManager().nodesWorking(type)}');
   final List<Node> nodes = await _fetchDuniterNodesFromPeers(type);
   NodeManager().updateNodes(type, nodes);
   NodeManager().loading = false;
@@ -462,22 +470,22 @@ Future<NodeCheck> _pingNode(String node, NodeType type) async {
   }
 }
 
-Future<http.Response> requestWithRetry(NodeType type, String path,
+Future<Tuple2<Node, http.Response>> requestWithRetry(NodeType type, String path,
     {bool dontRecord = false, bool retryWith404 = true}) async {
   return _requestWithRetry(type, path, dontRecord, retryWith404);
 }
 
-Future<http.Response> requestDuniterWithRetry(String path,
+Future<Tuple2<Node, http.Response>> requestDuniterWithRetry(String path,
     {bool retryWith404 = true}) async {
   return _requestWithRetry(NodeType.duniter, path, true, retryWith404);
 }
 
-Future<http.Response> requestCPlusWithRetry(String path,
+Future<Tuple2<Node, http.Response>> requestCPlusWithRetry(String path,
     {bool retryWith404 = true}) async {
   return _requestWithRetry(NodeType.cesiumPlus, path, true, retryWith404);
 }
 
-Future<http.Response> requestGvaWithRetry(String path,
+Future<Tuple2<Node, http.Response>> requestGvaWithRetry(String path,
     {bool retryWith404 = true,
     HttpType httpType = HttpType.get,
     Map<String, String>? headers,
@@ -489,7 +497,7 @@ Future<http.Response> requestGvaWithRetry(String path,
 
 enum HttpType { get, post, delete }
 
-Future<http.Response> _requestWithRetry(
+Future<Tuple2<Node, http.Response>> _requestWithRetry(
     NodeType type, String path, bool dontRecord, bool retryWith404,
     {HttpType httpType = HttpType.get,
     Map<String, String>? headers,
@@ -528,7 +536,7 @@ Future<http.Response> _requestWithRetry(
           if (!dontRecord) {
             NodeManager().updateNode(type, node.copyWith(latency: newLatency));
           }
-          return response;
+          return Tuple2<Node, Response>(node, response);
         } else if (response.statusCode == 404) {
           logger('404 on fetch $url');
           if (retryWith404) {
@@ -540,7 +548,7 @@ Future<http.Response> _requestWithRetry(
             if (!kReleaseMode) {
               logger('Returning not 200 or 400 response');
             }
-            return response;
+            return Tuple2<Node, Response>(node, response);
           }
         } else {
           /* await Sentry.captureMessage(
@@ -564,31 +572,44 @@ Future<http.Response> _requestWithRetry(
       'Cannot make the request to any of the ${nodes.length} nodes');
 }
 
-Future<PayResult> pay(
-    {required String to, required double amount, String? comment}) async {
+Future<PayResult> payWithGVA(
+    {required List<String> to, required double amount, String? comment}) async {
   try {
-    final SelectedGvaNode selected = getGvaNode();
+    final Tuple2<String, Node> selected = getGvaNode();
 
-    final String nodeUrl = selected.url;
+    final String nodeUrl = selected.item1;
     try {
       final Gva gva = Gva(node: nodeUrl);
       final CesiumWallet wallet = await SharedPreferencesHelper().getWallet();
       logger(
           'Trying $nodeUrl to send $amount to $to with comment ${comment ?? ''}');
-
-      final String response = await gva.pay(
-          recipient: extractPublicKey(to),
-          amount: amount,
-          comment: comment ?? '',
-          cesiumSeed: wallet.seed,
-          useMempool: true,
-          raiseException: true);
+      String response;
+      if (to.length == 1) {
+        response = await gva.pay(
+            recipient: extractPublicKey(to[0]),
+            amount: amount,
+            comment: comment ?? '',
+            cesiumSeed: wallet.seed,
+            useMempool: true,
+            raiseException: true);
+      } else {
+        response = await gva.complexPay(
+            recipients: to
+                .map((String recipient) => extractPublicKey(recipient))
+                .toList(),
+            amounts: List<double>.filled(to.length, amount),
+            totalAmount: amount * to.length,
+            comment: comment ?? '',
+            cesiumSeed: wallet.seed,
+            useMempool: true,
+            raiseException: true);
+      }
       logger('GVA replied with "$response"');
-      return PayResult(message: response, node: selected);
+      return PayResult(message: response, node: selected.item2);
     } on GraphQLException catch (e) {
       final List<String> eCause = e.cause.split('message: ');
       return PayResult(
-          node: selected,
+          node: selected.item2,
           message: eCause.isNotEmpty
               ? eCause[eCause.length > 1 ? 1 : 0].split(',')[0]
               : 'Transaction failed for unknown reason');
@@ -597,14 +618,15 @@ Future<PayResult> pay(
       logger(e);
       logger(stacktrace);
       return PayResult(
-          node: selected, message: "Something didn't work as expected ($e)");
+          node: selected.item2,
+          message: "Something didn't work as expected ($e)");
     }
   } catch (e) {
     return PayResult(message: "Something didn't work as expected ($e)");
   }
 }
 
-SelectedGvaNode getGvaNode() {
+Tuple2<String, Node> getGvaNode() {
   final List<Node> nodes = _getBestGvaNodes();
   if (nodes.isNotEmpty) {
     final Node? currentGvaNode = NodeManager().getCurrentGvaNode();
@@ -612,24 +634,17 @@ SelectedGvaNode getGvaNode() {
     if (currentGvaNode == null) {
       NodeManager().setCurrentGvaNode(node);
     }
-    return SelectedGvaNode(url: proxyfyNode(node.url), node: node);
+    return Tuple2<String, Node>(proxyfyNode(node.url), node);
   } else {
     throw Exception(
         'Sorry: I cannot find a working node to send the transaction');
   }
 }
 
-class SelectedGvaNode {
-  SelectedGvaNode({required this.url, required this.node});
-
-  final String url;
-  final Node node;
-}
-
 class PayResult {
   PayResult({required this.message, this.node});
 
-  final SelectedGvaNode? node;
+  final Node? node;
   final String message;
 }
 
@@ -647,33 +662,25 @@ Future<Tuple2<Map<String, dynamic>?, Node>> gvaHistoryAndBalance(
   logger('Get tx history (page size: $pageSize: cursor $cursor)');
   final String pubKey = extractPublicKey(pubKeyRaw);
   return gvaFunctionWrapper<Map<String, dynamic>>(
-      pubKey, (Gva gva) => gva.history(pubKey, pageSize, cursor));
+      (Gva gva) => gva.history(pubKey, pageSize, cursor));
 }
 
 Future<Tuple2<double?, Node>> gvaBalance(String pubKey) async {
-  return gvaFunctionWrapper<double>(
-      extractPublicKey(pubKey), (Gva gva) => gva.balance(pubKey));
+  return gvaFunctionWrapper<double>((Gva gva) => gva.balance(pubKey));
 }
 
 Future<Tuple2<String?, Node>> gvaNick(String pubKey) async {
   return gvaFunctionWrapper<String>(
-      pubKey, (Gva gva) => gva.getUsername(extractPublicKey(pubKey)));
+      (Gva gva) => gva.getUsername(extractPublicKey(pubKey)));
 }
 
-Future<Tuple2<Map<String, dynamic>?, Node>> gvaFetchUtxosOfScript(
-    {required String pubKeyRaw,
-    int pageSize = 100,
-    String? cursor,
-    int? amount}) {
-  final String pubKey = extractPublicKey(pubKeyRaw);
+Future<Tuple2<Map<String, dynamic>?, Node>> getCurrentBlockGVA() async {
   return gvaFunctionWrapper<Map<String, dynamic>>(
-      pubKey,
-      (Gva gva) => gva.fetchUtxosOfScript(
-          script: pubKey, pageSize: pageSize, amount: amount, cursor: cursor));
+      (Gva gva) => gva.getCurrentBlockExtended());
 }
 
 Future<Tuple2<T?, Node>> gvaFunctionWrapper<T>(
-    String pubKey, Future<T?> Function(Gva) specificFunction) async {
+    Future<T?> Function(Gva) specificFunction) async {
   final List<Node> nodes = _getBestGvaNodes();
 
   // Try first the current GVA node
@@ -763,21 +770,22 @@ Future<void> createOrUpdateCesiumPlusUser(String name) async {
     'tags': <String>[],
   };
 
-  signAndHash(userProfile, wallet);
+  hashAndSign(userProfile, wallet);
 
   // Convert the user profile data into a JSON string again, now including hash and signature
   final String userProfileJsonWithHashAndSignature = jsonEncode(userProfile);
 
   if (userName != null) {
     logger('User exists, update the user profile');
-    final http.Response updateResponse = await _requestWithRetry(
-        NodeType.cesiumPlus,
-        '/user/profile/$pubKey/_update?pubkey=$pubKey',
-        false,
-        true,
-        httpType: HttpType.post,
-        headers: _defCPlusHeaders(),
-        body: userProfileJsonWithHashAndSignature);
+    final http.Response updateResponse = (await _requestWithRetry(
+            NodeType.cesiumPlus,
+            '/user/profile/$pubKey/_update?pubkey=$pubKey',
+            false,
+            true,
+            httpType: HttpType.post,
+            headers: _defCPlusHeaders(),
+            body: userProfileJsonWithHashAndSignature))
+        .item2;
     if (updateResponse.statusCode == 200) {
       logger('User profile updated successfully.');
     } else {
@@ -787,11 +795,12 @@ Future<void> createOrUpdateCesiumPlusUser(String name) async {
     }
   } else if (userName == null) {
     logger('User does not exist, create a new user profile');
-    final http.Response createResponse = await _requestWithRetry(
-        NodeType.cesiumPlus, '/user/profile', false, false,
-        httpType: HttpType.post,
-        headers: _defCPlusHeaders(),
-        body: userProfileJsonWithHashAndSignature);
+    final http.Response createResponse = (await _requestWithRetry(
+            NodeType.cesiumPlus, '/user/profile', false, false,
+            httpType: HttpType.post,
+            headers: _defCPlusHeaders(),
+            body: userProfileJsonWithHashAndSignature))
+        .item2;
 
     if (createResponse.statusCode == 200) {
       logger('User profile created successfully.');
@@ -809,12 +818,12 @@ Map<String, String> _defCPlusHeaders() {
   };
 }
 
-void signAndHash(Map<String, dynamic> userProfile, CesiumWallet wallet) {
-  final String userProfileJson = jsonEncode(userProfile);
-  final String hash = calculateHash(userProfileJson);
+void hashAndSign(Map<String, dynamic> data, CesiumWallet wallet) {
+  final String dataJson = jsonEncode(data);
+  final String hash = calculateHash(dataJson);
   final String signature = wallet.sign(hash);
-  userProfile['hash'] = hash;
-  userProfile['signature'] = signature;
+  data['hash'] = hash;
+  data['signature'] = signature;
 }
 
 Future<String?> getCesiumPlusUser(String pubKey) async {
@@ -835,13 +844,14 @@ Future<bool> deleteCesiumPlusUser() async {
         1000, // current time in seconds
   };
 
-  signAndHash(userProfile, wallet);
+  hashAndSign(userProfile, wallet);
 
-  final http.Response delResponse = await _requestWithRetry(
-      NodeType.cesiumPlus, '/history/delete', false, false,
-      httpType: HttpType.post,
-      headers: _defCPlusHeaders(),
-      body: jsonEncode(userProfile));
+  final http.Response delResponse = (await _requestWithRetry(
+          NodeType.cesiumPlus, '/history/delete', false, false,
+          httpType: HttpType.post,
+          headers: _defCPlusHeaders(),
+          body: jsonEncode(userProfile)))
+      .item2;
   return delResponse.statusCode == 200;
 }
 
@@ -850,3 +860,136 @@ String calculateHash(String input) {
   final Digest digest = sha256.convert(bytes);
   return digest.toString().toUpperCase();
 }
+
+Future<Tuple2<Map<String, dynamic>?, Node>> gvaFetchUtxosOfScript(
+    {required String pubKeyRaw,
+    int pageSize = 100,
+    String? cursor,
+    int? amount}) {
+  final String pubKey = extractPublicKey(pubKeyRaw);
+  return gvaFunctionWrapper<Map<String, dynamic>>((Gva gva) =>
+      gva.fetchUtxosOfScript(
+          script: pubKey, pageSize: pageSize, amount: amount, cursor: cursor));
+}
+
+Future<PayResult> payWithBMA({
+  required CesiumWallet wallet,
+  required List<Utxo> utxos,
+  required String destPub,
+  required double amount,
+  required String blockNumber,
+  required String blockHash,
+  String? comment,
+}) async {
+  try {
+    final String issuer = wallet.pubkey;
+    // Change back address == issuer
+    final String restPub = issuer;
+
+    final List<List<Utxo>> utxoSlices = sliceUtxos(utxos);
+    Response? finalResponse;
+    Node? node;
+    for (final List<Utxo> utxoSlice in utxoSlices) {
+      final Map<String, Object> transaction = <String, Object>{
+        'Version': 10,
+        'Currency': currency,
+        'Blockstamp': '$blockNumber-$blockHash',
+        'Locktime': 0,
+        'Issuers': <String>[issuer],
+        'Comment': comment ?? ''
+      };
+
+      // Inputs
+      final List<String> inputs = <String>[];
+      for (final Utxo utxo in utxoSlice) {
+        // if D (DU) : AMOUNT:BASE:D:PUBLIC_KEY:BLOCK_ID
+        // if T (TX) : AMOUNT:BASE:T:T_HASH:T_INDEX
+        inputs.add(
+            '${utxo.amount}:${utxo.base}:T:${utxo.txHash}:${utxo.outputIndex}');
+      }
+      transaction['Inputs'] = inputs;
+      // Unlocks
+      final List<String> unlocks = <String>[];
+      for (int i = 0; i < utxos.length; i++) {
+        // INPUT_INDEX:UNLOCK_CONDITION
+        unlocks.add('$i:SIG(0)');
+      }
+      transaction['Unlocks'] = unlocks;
+
+      final List<String> outputs = <String>[];
+
+      // AMOUNT:BASE:CONDITIONS
+      double rest = amount;
+      final int maxBase =
+          utxos.fold(0, (int prev, Utxo utxo) => max(prev, utxo.base));
+      final double inputsAmount =
+          utxos.fold(0, (double prev, Utxo utxo) => prev + utxo.amount);
+      int outputBase = maxBase;
+      int outputOffset = 0;
+      final List<Map<String, dynamic>> newSources = <Map<String, dynamic>>[];
+
+      if (destPub != issuer) {
+        while (rest > 0) {
+          double outputAmount = truncBase(rest, outputBase);
+          rest -= outputAmount;
+          if (outputAmount > 0) {
+            outputAmount = outputBase == 0
+                ? outputAmount
+                : outputAmount / pow(10, outputBase);
+            outputs.add('$outputAmount:$outputBase:SIG($destPub)');
+            outputOffset++;
+          }
+          outputBase--;
+        }
+        rest = inputsAmount - amount;
+        outputBase = maxBase;
+      }
+
+      while (rest > 0) {
+        double outputAmount = truncBase(rest, outputBase);
+        rest -= outputAmount;
+        if (outputAmount > 0) {
+          outputAmount = outputBase == 0
+              ? outputAmount
+              : outputAmount / pow(10, outputBase);
+          outputs.add('$outputAmount:$outputBase:SIG($restPub)');
+          if (issuer == restPub) {
+            newSources.add(<String, dynamic>{
+              'type': 'T',
+              'noffset': outputOffset,
+              'amount': outputAmount,
+              'base': outputBase,
+              'conditions': 'SIG($restPub)',
+              'consumed': false,
+            });
+          }
+          outputOffset++;
+        }
+        outputBase--;
+      }
+      transaction['Outputs'] = outputs;
+
+      hashAndSign(transaction, wallet);
+      final String transactionJson = jsonEncode(transaction);
+
+      // final List<int> bytes = utf8.encode(transactionJson);
+      logger(transactionJson);
+
+      final Tuple2<Node, http.Response> response = await _requestWithRetry(
+          NodeType.duniter, '/tx/processTesting', false, false,
+          httpType: HttpType.post,
+          // headers: ??
+          body: transactionJson);
+      finalResponse = response.item2;
+      node = response.item1;
+      if (response.item2.statusCode != 200) {
+        return PayResult(
+            node: response.item1,
+            message: "Something didn't work as expected ($e)");
+      }
+    }
+    return PayResult(message: finalResponse!.body, node: node);
+  } catch (e) {
+    return PayResult(message: "Something didn't work as expected ($e)");
+  }
+}
diff --git a/lib/g1/transaction_parser.dart b/lib/g1/transaction_parser.dart
index 48dbbaaf80ac3a62e2fe3182978d7da21a959c59..baddc7a992cd7bb10431f13585fb7bcb3b98ba06 100644
--- a/lib/g1/transaction_parser.dart
+++ b/lib/g1/transaction_parser.dart
@@ -4,7 +4,7 @@ import '../data/models/contact.dart';
 import '../data/models/transaction.dart';
 import '../data/models/transaction_state.dart';
 import '../data/models/transaction_type.dart';
-import '../ui/contacts_cache.dart';
+import 'g1_helper.dart';
 
 final RegExp exp = RegExp(r'\((.*?)\)');
 
@@ -42,8 +42,8 @@ Future<TransactionState> transactionParser(
       logger('Timestamp: $timestamp');
       logger('Fecha: $txDate');
     } */
-    final Contact fromC = await ContactsCache().getContact(address2!);
-    final Contact toC = await ContactsCache().getContact(address1!);
+    final Contact fromC = Contact(pubKey: address2!);
+    final Contact toC = Contact(pubKey: address1!);
 
     tx.insert(
         0,
@@ -62,8 +62,9 @@ Future<TransactionState> transactionParser(
       lastChecked: DateTime.now());
 }
 
-Future<TransactionState> transactionsGvaParser(
-    Map<String, dynamic> txData, TransactionState state) async {
+Future<TransactionState> transactionsGvaParser(Map<String, dynamic> txData,
+    TransactionState state, String myPubKeyRaw) async {
+  final String myPubKey = extractPublicKey(myPubKeyRaw);
   // Balance
   final dynamic rawBalance = txData['balance'];
   final double amount = rawBalance != null
@@ -88,21 +89,29 @@ Future<TransactionState> transactionsGvaParser(
   final List<Transaction> txs = <Transaction>[];
   for (final dynamic edgeRaw in edges) {
     final Transaction tx =
-        await _transactionGvaParser(edgeRaw as Map<String, dynamic>);
-    txs.add(tx);
+        await _transactionGvaParser(edgeRaw as Map<String, dynamic>, myPubKey);
+    if (tx.from.pubKey == myPubKey &&
+        tx.to.pubKey == myPubKey &&
+        tx.recipients.length == 1) {
+      // This is a return cash back to me
+      continue;
+    } else {
+      txs.add(tx);
+    }
   }
   final List<dynamic> receiving = txsHistoryMp['receiving'] as List<dynamic>;
   final List<dynamic> sending = txsHistoryMp['sending'] as List<dynamic>;
   for (final dynamic receiveRaw in receiving) {
-    final Transaction tx = await _txGvaParse(
-        receiveRaw as Map<String, dynamic>, TransactionType.receiving);
+    final Transaction tx = await _txGvaParse(receiveRaw as Map<String, dynamic>,
+        myPubKey, TransactionType.receiving);
     txs.insert(0, tx);
   }
   for (final dynamic sendingRaw in sending) {
     final Transaction tx = await _txGvaParse(
-        sendingRaw as Map<String, dynamic>, TransactionType.sending);
+        sendingRaw as Map<String, dynamic>, myPubKey, TransactionType.sending);
     txs.insert(0, tx);
   }
+
   return state.copyWith(
       transactions: txs,
       balance: amount,
@@ -111,7 +120,8 @@ Future<TransactionState> transactionsGvaParser(
       endCursor: pageInfo['endCursor'] as String?);
 }
 
-Future<Transaction> _transactionGvaParser(Map<String, dynamic> edge) {
+Future<Transaction> _transactionGvaParser(
+    Map<String, dynamic> edge, String myPubKey) {
   final Map<String, dynamic> parsedTxData = edge;
   // Direction
   final String direction = parsedTxData['direction'] as String;
@@ -119,34 +129,67 @@ Future<Transaction> _transactionGvaParser(Map<String, dynamic> edge) {
       direction == 'SENT' ? TransactionType.sent : TransactionType.received;
 
   final Map<String, dynamic> tx = parsedTxData['node'] as Map<String, dynamic>;
-  return _txGvaParse(tx, type);
+  return _txGvaParse(tx, myPubKey, type);
 }
 
 Future<Transaction> _txGvaParse(
-    Map<String, dynamic> tx, TransactionType type) async {
+    Map<String, dynamic> tx, String myPubKey, TransactionType type) async {
   final List<dynamic> issuers = tx['issuers'] as List<dynamic>;
   final List<dynamic> outputs = tx['outputs'] as List<dynamic>;
   final String from = issuers[0] as String;
-  final String? to = exp.firstMatch(outputs[0] as String)!.group(1);
+
+  final List<Contact> recipients = <Contact>[];
+  final List<double> recipientsAmounts = <double>[];
+  double amount = 0.0;
+  Contact? toC;
+  final bool isSent =
+      type == TransactionType.sent || type == TransactionType.sending;
+  for (final dynamic output in outputs) {
+    // Extract the recipient from each output
+    final String outputS = output as String;
+    final String? recipient = exp.firstMatch(outputS)!.group(1);
+    final Contact recipientContact = Contact(pubKey: recipient!);
+    recipients.add(recipientContact);
+
+    final double outputAmount = double.parse(outputS.split(':')[0]);
+    recipientsAmounts.add(amount);
+    if (isSent) {
+      if (recipient != myPubKey) {
+        // Is not the return cash back to me
+        amount += outputAmount;
+      }
+    } else {
+      if (recipient == myPubKey) {
+        amount = outputAmount;
+        toC = recipientContact;
+      }
+    }
+  }
+
+  if (isSent) {
+    // this only works in the case of a single recipient
+    toC = recipients.first;
+  }
 
   // Time
   final dynamic writtenTime = tx['writtenTime'];
   final DateTime time = writtenTime == null
       ? DateTime.now()
       : DateTime.fromMillisecondsSinceEpoch((writtenTime as int) * 1000);
-  // Amount
-  double amount = double.parse((outputs.first as String).split(':')[0]);
-  if (type == TransactionType.sent || type == TransactionType.sending) {
+
+  if (isSent) {
     amount = -amount;
   }
   // Comment
   final String comment = tx['comment'] as String;
-  final Contact fromC = await ContactsCache().getContact(from);
-  final Contact toC = await ContactsCache().getContact(to!);
+  final Contact fromC = Contact(pubKey: from);
+
   return Transaction(
     type: type,
     from: fromC,
-    to: toC,
+    to: toC!,
+    recipients: recipients,
+    recipientsAmounts: recipientsAmounts,
     amount: amount,
     comment: comment,
     time: time,
diff --git a/lib/ui/pay_helper.dart b/lib/ui/pay_helper.dart
index d59a0b1188bb102bb472159a6404163e61119ea1..8a779d70b971503058b73c99db8042ea6ae9731a 100644
--- a/lib/ui/pay_helper.dart
+++ b/lib/ui/pay_helper.dart
@@ -1,8 +1,10 @@
 import 'dart:async';
 
+import 'package:durt/durt.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:tuple/tuple.dart';
 
 import '../../../data/models/contact.dart';
 import '../../../data/models/node_type.dart';
@@ -17,6 +19,8 @@ import '../data/models/multi_wallet_transaction_cubit.dart';
 import '../data/models/node.dart';
 import '../data/models/node_manager.dart';
 import '../data/models/payment_state.dart';
+import '../data/models/utxo.dart';
+import '../data/models/utxo_cubit.dart';
 import '../g1/currency.dart';
 import '../g1/g1_helper.dart';
 import 'contacts_cache.dart';
@@ -27,15 +31,16 @@ import 'widgets/fifth_screen/import_dialog.dart';
 
 Future<bool> payWithRetry(
     {required BuildContext context,
-    required Contact to,
+    required List<Contact> recipients,
     required double amount,
     required String comment,
     bool isRetry = false,
-    bool isMultiPayment = false,
     required bool isG1,
-    required double currentUd}) async {
+    required double currentUd,
+    bool useBMA = false}) async {
   assert(amount > 0);
   bool hasPass = false;
+  final bool isToMultiple = recipients.length > 1;
   if (!SharedPreferencesHelper().isG1nkgoCard() &&
       !SharedPreferencesHelper().hasVolatile()) {
     hasPass = await showImportCesiumWalletDialog(
@@ -54,25 +59,26 @@ Future<bool> payWithRetry(
       final AppCubit appCubit = context.read<AppCubit>();
       paymentCubit.sending();
       final String fromPubKey = SharedPreferencesHelper().getPubKey();
-      final String contactPubKey = to.pubKey;
-      bool? confirmed;
-      if (!isMultiPayment) {
-        confirmed = await _confirmSend(context, amount.toString(),
-            humanizeContact(fromPubKey, to, true), isRetry, appCubit.currency);
-      } else {
-        confirmed = true;
-      }
+
+      final bool? confirmed = await _confirmSend(context, amount.toString(),
+          fromPubKey, recipients, isRetry, appCubit.currency, isToMultiple);
       final Contact fromContact = await ContactsCache().getContact(fromPubKey);
+      final CesiumWallet wallet = await SharedPreferencesHelper().getWallet();
+      if (!context.mounted) {
+        return false;
+      }
+      final UtxoCubit utxoCubit = context.read<UtxoCubit>();
       final double convertedAmount = toG1(amount, isG1, currentUd);
-
       if (confirmed == null || !confirmed) {
         paymentCubit.sentFailed();
       } else {
         final Transaction tx = Transaction(
             type: TransactionType.pending,
             from: fromContact,
-            to: to,
-            amount: -toCG1(convertedAmount).toDouble(),
+            to: recipients[0],
+            recipients: recipients,
+            recipientsAmounts: List<double>.filled(recipients.length, amount),
+            amount: -toCG1(convertedAmount).toDouble() * recipients.length,
             comment: comment,
             time: DateTime.now());
         final bool isConnected =
@@ -83,35 +89,54 @@ Future<bool> payWithRetry(
           if (!context.mounted) {
             return true;
           }
-          if (!isMultiPayment) {
-            showAlertDialog(context, tr('payment_waiting_internet_title'),
-                tr('payment_waiting_internet_desc_beta'));
-          }
+          showAlertDialog(context, tr('payment_waiting_internet_title'),
+              tr('payment_waiting_internet_desc_beta'));
           final Transaction pending =
               tx.copyWith(type: TransactionType.waitingNetwork);
           txCubit.addPendingTransaction(pending);
-          if (!isMultiPayment) {
-            context.read<BottomNavCubit>().updateIndex(3);
-          }
+          context.read<BottomNavCubit>().updateIndex(3);
           return true;
         } else {
-          final PayResult result = await pay(
-              to: contactPubKey, comment: comment, amount: convertedAmount);
+          // PAY!
+          PayResult result;
+          if (!useBMA) {
+            result = await payWithGVA(
+                to: recipients.map((Contact c) => c.pubKey).toList(),
+                comment: comment,
+                amount: convertedAmount);
+          } else {
+            await utxoCubit.fetchUtxos(fromPubKey);
+            final List<Utxo>? utxos = utxoCubit.consume(convertedAmount);
+            final Tuple2<Map<String, dynamic>?, Node> currentBlock =
+                await getCurrentBlockGVA();
+
+            if (currentBlock != null && utxos != null) {
+              result = await payWithBMA(
+                  destPub: recipients[0].pubKey,
+                  blockHash: '${currentBlock.item1!['hash']}',
+                  blockNumber: '${currentBlock.item1!['number']}',
+                  comment: comment,
+                  wallet: wallet,
+                  utxos: utxos,
+                  amount: convertedAmount);
+            } else {
+              final Node triedNode = currentBlock.item2;
+              result = PayResult(
+                  message: 'Error retrieving payment data', node: triedNode);
+            }
+          }
 
           final Transaction pending = tx.copyWith(
               debugInfo:
-                  'Node used: ${result.node != null ? result.node!.url : 'unknown'}');
+                  'Node used: ${result != null && result.node != null ? result.node!.url : 'unknown'}');
           if (result.message == 'success') {
             paymentCubit.sent();
             // ignore: use_build_context_synchronously
             if (!context.mounted) {
               return true;
             }
-            if (!isMultiPayment) {
-              showAlertDialog(context, tr('payment_successful'),
-                  tr('payment_successful_desc'));
-            }
-
+            showAlertDialog(context, tr('payment_successful'),
+                tr('payment_successful_desc'));
             if (!isRetry) {
               // Add here the transaction to the pending list (so we can check it the tx is confirmed)
               txCubit.addPendingTransaction(pending);
@@ -136,9 +161,8 @@ Future<bool> payWithRetry(
                         // We try to translate the error, like "insufficient balance"
                         'error': tr(result.message)
                       }),
-                isMultiPayment: isMultiPayment,
                 increaseErrors: failedWithBalance,
-                node: result.node!.node);
+                node: result.node);
             if (!isRetry) {
               txCubit.insertPendingTransaction(
                   pending.copyWith(type: TransactionType.failed));
@@ -158,7 +182,6 @@ Future<bool> payWithRetry(
       showPayError(
           context: context,
           desc: tr('payment_error_no_pass'),
-          isMultiPayment: isMultiPayment,
           increaseErrors: false);
     }
     return false;
@@ -175,22 +198,35 @@ bool weHaveBalance(BuildContext context, double amount) {
 double getBalance(BuildContext context) =>
     context.read<MultiWalletTransactionCubit>().balance;
 
-Future<bool?> _confirmSend(BuildContext context, String amount, String to,
-    bool isRetry, Currency currency) async {
+Future<bool?> _confirmSend(
+    BuildContext context,
+    String amount,
+    String fromPubKey,
+    List<Contact> recipients,
+    bool isRetry,
+    Currency currency,
+    bool isPayToMultiple) async {
   return showDialog<bool>(
     context: context,
     builder: (BuildContext context) {
       return AlertDialog(
         title: Text(tr('please_confirm_sent')),
-        content: Text(tr(
-            isRetry
-                ? 'please_confirm_retry_sent_desc'
-                : 'please_confirm_sent_desc',
-            namedArgs: <String, String>{
-              'amount': amount,
-              'to': to,
-              'currency': currency.name()
-            })),
+        content: isPayToMultiple
+            ? Text(tr('please_confirm_sent_multi_desc',
+                namedArgs: <String, String>{
+                    'amount': amount,
+                    'currency': currency.name(),
+                    'people': recipients.length.toString()
+                  }))
+            : Text(tr(
+                isRetry
+                    ? 'please_confirm_retry_sent_desc'
+                    : 'please_confirm_sent_desc',
+                namedArgs: <String, String>{
+                    'amount': amount,
+                    'to': humanizeContact(fromPubKey, recipients[0], true),
+                    'currency': currency.name()
+                  })),
         actions: <Widget>[
           TextButton(
             onPressed: () => Navigator.of(context).pop(false),
@@ -209,12 +245,9 @@ Future<bool?> _confirmSend(BuildContext context, String amount, String to,
 void showPayError(
     {required BuildContext context,
     required String desc,
-    required bool isMultiPayment,
     required bool increaseErrors,
     Node? node}) {
-  if (!isMultiPayment) {
-    showAlertDialog(context, tr('payment_error'), desc);
-  }
+  showAlertDialog(context, tr('payment_error'), desc);
   context.read<PaymentCubit>().sentFailed();
   if (node != null && increaseErrors) {
     NodeManager().increaseNodeErrors(NodeType.gva, node);
diff --git a/lib/ui/widgets/first_screen/pay_form.dart b/lib/ui/widgets/first_screen/pay_form.dart
index fcd753e8d30eca27175d90a936c792ef029b82af..ea6a289f36e222d476797c0a761c15d71fa184d8 100644
--- a/lib/ui/widgets/first_screen/pay_form.dart
+++ b/lib/ui/widgets/first_screen/pay_form.dart
@@ -5,8 +5,6 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 import '../../../data/models/app_cubit.dart';
-import '../../../data/models/bottom_nav_cubit.dart';
-import '../../../data/models/contact.dart';
 import '../../../data/models/multi_wallet_transaction_cubit.dart';
 import '../../../data/models/payment_cubit.dart';
 import '../../../data/models/payment_state.dart';
@@ -152,93 +150,24 @@ class _PayFormState extends State<PayForm> {
     if (notCanBeSent ||
         nullAmount ||
         notValidComment ||
-        notBalance(context, state, currency, currentUd)) {
+        notBalance(
+            context, state, currency, currentUd, state.contacts.length)) {
       return null;
-    } else if (state.isMultiple()) {
-      // Multiple payments
-      return () async {
-        final bool? confirmed = await showDialog<bool>(
-          context: context,
-          builder: (BuildContext context) {
-            return AlertDialog(
-              title: Text(tr('please_confirm_sent')),
-              content: Text(tr('please_confirm_sent_multi_desc',
-                  namedArgs: <String, String>{
-                    'amount': state.amount.toString(),
-                    'currency': currency.name()
-                  })),
-              actions: <Widget>[
-                TextButton(
-                  child: Text(tr('cancel')),
-                  onPressed: () => Navigator.of(context).pop(false),
-                ),
-                TextButton(
-                  child: Text(tr('accept')),
-                  onPressed: () => Navigator.of(context).pop(true),
-                ),
-              ],
-            );
-          },
-        );
-
-        if (confirmed != null && confirmed) {
-          int validPayments = 0;
-          int invalidPayments = 0;
-
-          paymentResultsStreamController.stream.listen((String paymentResult) {
-            if (Navigator.canPop(context)) {
-              Navigator.pop(context);
-            }
-            showAlertDialog(context, tr('multi_pay_results'), paymentResult);
-          });
-          for (final Contact contact in state.contacts) {
-            if (!mounted) {
-              return;
-            }
-            final bool result = await payWithRetry(
-              context: context,
-              to: contact,
-              amount: state.amount!,
-              isG1: isG1,
-              currentUd: currentUd,
-              isMultiPayment: true,
-              comment: state.comment,
-            );
-            if (result) {
-              validPayments++;
-            } else {
-              invalidPayments++;
-            }
-            paymentResultsStreamController.add(tr('multi_pay_results_desc',
-                namedArgs: <String, String>{
-                  'success': validPayments.toString(),
-                  'fail': invalidPayments.toString()
-                }));
-            // await Future<void>.delayed(const Duration(milliseconds: 200));
-          }
-          if (!mounted) {
-            return;
-          }
-          context.read<BottomNavCubit>().updateIndex(3);
-        }
-      };
-    } else {
-      // Single payment
+    } else
       return () async {
         await payWithRetry(
             context: context,
-            to: state.contacts[0],
+            recipients: state.contacts,
             amount: state.amount!,
             isG1: isG1,
             currentUd: currentUd,
             comment: state.comment);
       };
-    }
   }
 
   bool notBalance(BuildContext context, PaymentState state, Currency currency,
-          double currentUd) =>
-      !_weHaveBalance(context, state.amount!, currency, currentUd);
+          double currentUd, int recipients) =>
+      !_weHaveBalance(context, state.amount!, currency, currentUd, recipients);
 
   bool _commentValidate() {
     final String currentComment = _commentController.value.text;
@@ -253,11 +182,11 @@ class _PayFormState extends State<PayForm> {
   }
 
   bool _weHaveBalance(BuildContext context, double amount, Currency currency,
-      double currentUd) {
+      double currentUd, int recipients) {
     final double balance =
         convertAmount(currency == Currency.G1, getBalance(context), currentUd);
-    logger('We have $balance G1, need $amount');
-    final bool weHave = balance >= amount;
+    logger('We have $balance G1, need ${amount * recipients}');
+    final bool weHave = balance >= amount * recipients;
 
     if (!weHave) {
       _feedbackNotifier.value = tr('insufficient balance');
diff --git a/lib/ui/widgets/fourth_screen/transaction_item.dart b/lib/ui/widgets/fourth_screen/transaction_item.dart
index 64eb5a7d54a0fac633c2ef20c20efa4b25c82587..277b0ebb9c4a278ce50251493cc0bd712d4e6b25 100644
--- a/lib/ui/widgets/fourth_screen/transaction_item.dart
+++ b/lib/ui/widgets/fourth_screen/transaction_item.dart
@@ -222,8 +222,11 @@ class TransactionListItem extends StatelessWidget {
                                         'from':
                                             '${humanizeContact(myPubKey, transaction.from)} 🫴 ',
                                         'to': transaction.isToMultiple
-                                            ? humanizeContacts(myPubKey,
-                                                transaction.recipients)
+                                            ? humanizeContacts(
+                                                fromAddress:
+                                                    transaction.from.pubKey,
+                                                contacts:
+                                                    transaction.recipients)
                                             : humanizeContact(
                                                 myPubKey, transaction.to)
                                       }),
diff --git a/lib/ui/widgets/fourth_screen/transaction_page.dart b/lib/ui/widgets/fourth_screen/transaction_page.dart
index 8f0c9499d2d6f970077f3b0488fcbd2461c3adb3..09ddc6e5993d70052dcadf6d8fe944a7a24730e3 100644
--- a/lib/ui/widgets/fourth_screen/transaction_page.dart
+++ b/lib/ui/widgets/fourth_screen/transaction_page.dart
@@ -16,6 +16,7 @@ import '../../../data/models/node_list_cubit.dart';
 import '../../../data/models/theme_cubit.dart';
 import '../../../data/models/transaction.dart';
 import '../../../data/models/transactions_bloc.dart';
+import '../../../data/models/utxo_cubit.dart';
 import '../../../g1/currency.dart';
 import '../../../shared_prefs_helper.dart';
 import '../../logger.dart';
@@ -43,6 +44,7 @@ class _TransactionsAndBalanceWidgetState
   late AppCubit appCubit;
   late NodeListCubit nodeListCubit;
   late MultiWalletTransactionCubit transCubit;
+  late UtxoCubit utxoCubit;
 
   final PagingController<String?, Transaction> _pagingController =
       PagingController<String?, Transaction>(firstPageKey: null);
@@ -62,7 +64,8 @@ class _TransactionsAndBalanceWidgetState
     appCubit = context.read<AppCubit>();
     transCubit = context.read<MultiWalletTransactionCubit>();
     nodeListCubit = context.read<NodeListCubit>();
-    _bloc.init(transCubit, nodeListCubit, appCubit);
+    utxoCubit = context.read<UtxoCubit>();
+    _bloc.init(transCubit, nodeListCubit, appCubit, utxoCubit);
     _pagingController.addPageRequestListener((String? cursor) {
       _bloc.onPageRequestSink.add(cursor);
     });
@@ -114,7 +117,7 @@ class _TransactionsAndBalanceWidgetState
         _refresh();
       } catch (e) {
         logger('Failed via _refresh, lets try a basic fetchTransactions');
-        transCubit.fetchTransactions(nodeListCubit, appCubit);
+        transCubit.fetchTransactions(nodeListCubit, utxoCubit, appCubit);
       }
     });
     tutorial = FourthTutorial(context);
diff --git a/pubspec.lock b/pubspec.lock
index 68f1f8e65b1d09eb8e62b8cf718aff30a23e50dd..ab3cc751b6c6b331d4a39aab99bfdb9ab343ff74 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -149,10 +149,10 @@ packages:
     dependency: "direct dev"
     description:
       name: build_runner
-      sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b"
+      sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.6"
+    version: "2.4.7"
   build_runner_core:
     dependency: transitive
     description:
@@ -173,10 +173,10 @@ packages:
     dependency: transitive
     description:
       name: built_value
-      sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e"
+      sha256: "69acb7007eb2a31dc901512bfe0f7b767168be34cb734835d54c070bfa74c1b2"
       url: "https://pub.dev"
     source: hosted
-    version: "8.7.0"
+    version: "8.8.0"
   characters:
     dependency: transitive
     description:
@@ -229,10 +229,10 @@ packages:
     dependency: transitive
     description:
       name: code_builder
-      sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677"
+      sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f
       url: "https://pub.dev"
     source: hosted
-    version: "4.7.0"
+    version: "4.8.0"
   collection:
     dependency: "direct main"
     description:
@@ -245,10 +245,10 @@ packages:
     dependency: transitive
     description:
       name: connectivity_plus
-      sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f
+      sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0"
       url: "https://pub.dev"
     source: hosted
-    version: "5.0.1"
+    version: "5.0.2"
   connectivity_plus_platform_interface:
     dependency: transitive
     description:
@@ -301,10 +301,10 @@ packages:
     dependency: transitive
     description:
       name: cross_file
-      sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c"
+      sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5"
       url: "https://pub.dev"
     source: hosted
-    version: "0.3.3+6"
+    version: "0.3.3+7"
   crypto:
     dependency: "direct main"
     description:
@@ -325,26 +325,26 @@ packages:
     dependency: transitive
     description:
       name: dart_style
-      sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334
+      sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.3"
+    version: "2.3.4"
   dbus:
     dependency: transitive
     description:
       name: dbus
-      sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263"
+      sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
       url: "https://pub.dev"
     source: hosted
-    version: "0.7.8"
+    version: "0.7.10"
   device_info_plus:
     dependency: transitive
     description:
       name: device_info_plus
-      sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419"
+      sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6"
       url: "https://pub.dev"
     source: hosted
-    version: "9.1.0"
+    version: "9.1.1"
   device_info_plus_platform_interface:
     dependency: transitive
     description:
@@ -357,10 +357,10 @@ packages:
     dependency: transitive
     description:
       name: dio
-      sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7"
+      sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3"
       url: "https://pub.dev"
     source: hosted
-    version: "5.3.3"
+    version: "5.4.0"
   dom_tools:
     dependency: transitive
     description:
@@ -380,10 +380,9 @@ packages:
   durt:
     dependency: "direct main"
     description:
-      name: durt
-      sha256: "97007458aa5ba95c78465af8489e1330d0f7d48c74e10699f6283ce4cf8a5410"
-      url: "https://pub.dev"
-    source: hosted
+      path: "../durt"
+      relative: true
+    source: path
     version: "0.1.7"
   easy_debounce:
     dependency: "direct main"
@@ -914,10 +913,10 @@ packages:
     dependency: "direct main"
     description:
       name: l10n_esperanto
-      sha256: "05a7aa7e8026c63aa25e476f556e3098e43bf1ab1e6987730d3bd301680df52b"
+      sha256: d3b969643aa91ed8111bf04c4114d23891fff3ee5fbc90179e66576481b24267
       url: "https://pub.dev"
     source: hosted
-    version: "2.0.6"
+    version: "2.0.7"
   lehttp_overrides:
     dependency: "direct main"
     description:
@@ -1146,10 +1145,10 @@ packages:
     dependency: transitive
     description:
       name: plugin_platform_interface
-      sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
+      sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.6"
+    version: "2.1.7"
   pointycastle:
     dependency: transitive
     description:
@@ -1258,10 +1257,10 @@ packages:
     dependency: transitive
     description:
       name: sentry
-      sha256: "9cfd325611ab54b57d5e26957466823f05bea9d6cfcc8d48f11817b8bcedf0d1"
+      sha256: e7ded42974bac5f69e4ca4ddc57d30499dd79381838f24b7e8fd9aa4139e7b79
       url: "https://pub.dev"
     source: hosted
-    version: "7.12.0"
+    version: "7.13.2"
   sentry_dart_plugin:
     dependency: "direct main"
     description:
@@ -1274,18 +1273,18 @@ packages:
     dependency: "direct main"
     description:
       name: sentry_flutter
-      sha256: "0cd7d622cb63c94fd1b2f87ab508e158b950bd281e2a80f327ebf73bb217eaf3"
+      sha256: d6f55ec7a1f681784165021f749007712a72ff57eadf91e963331b6ae326f089
       url: "https://pub.dev"
     source: hosted
-    version: "7.12.0"
+    version: "7.13.2"
   sentry_logging:
     dependency: "direct main"
     description:
       name: sentry_logging
-      sha256: e71c78e3dd01d67455f4ee489778a358a44016ec06358943fbf30e7901eec7e8
+      sha256: "157b30f8ad6b360333e651366022003399c337c2bf04d822711ae78f80576216"
       url: "https://pub.dev"
     source: hosted
-    version: "7.12.0"
+    version: "7.13.2"
   share_plus:
     dependency: "direct main"
     description:
@@ -1591,10 +1590,10 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_ios
-      sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a"
+      sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3
       url: "https://pub.dev"
     source: hosted
-    version: "6.2.0"
+    version: "6.2.1"
   url_launcher_linux:
     dependency: transitive
     description:
@@ -1679,10 +1678,10 @@ packages:
     dependency: "direct main"
     description:
       name: vibration
-      sha256: "63d4f6b03e38d106599da18e786d5edcd02354433a4ed478fccbbcfc347193ab"
+      sha256: "778ace40e84852e6cf6017cdbaf6790a837d73ff3dd50b27da9ac232a19de8fc"
       url: "https://pub.dev"
     source: hosted
-    version: "1.8.3"
+    version: "1.8.4"
   watcher:
     dependency: transitive
     description:
@@ -1727,10 +1726,10 @@ packages:
     dependency: transitive
     description:
       name: win32
-      sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3"
+      sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
       url: "https://pub.dev"
     source: hosted
-    version: "5.0.9"
+    version: "5.1.1"
   win32_registry:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index b6f54d620624022d4dede0d5ae0c48ad1f09818a..64a976c1b012805301c68216d730853d7a092257 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -39,9 +39,9 @@ dependencies:
   qr_flutter: ^4.0.0
   introduction_screen: ^3.1.6
   responsive_framework: ^0.2.0
-  durt: ^0.1.7
-  #durt:
-   # path: ../durt
+  #durt: ^0.1.7
+  durt:
+    path: ../durt
    # git:
    #   url: https://git.duniter.org/clients/durt
   flutter_neumorphic: