Skip to content

Commit

Permalink
refactor: vtt_create_bloc
Browse files Browse the repository at this point in the history
separate the logic for building transactions apart from the UI BLOC actions.
  • Loading branch information
parodyBit committed Sep 8, 2023
1 parent a2bd911 commit 46509ac
Show file tree
Hide file tree
Showing 10 changed files with 798 additions and 642 deletions.
467 changes: 467 additions & 0 deletions lib/bloc/transactions/transaction_builder.dart

Large diffs are not rendered by default.

656 changes: 83 additions & 573 deletions lib/bloc/transactions/value_transfer/vtt_create/vtt_create_bloc.dart

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ enum VTTCreateStatus {
sending,
accepted,
finished,
recipientSet,
inputSet,
exception,
}

Expand All @@ -22,12 +20,14 @@ class VTTCreateState extends Equatable {
final VTTCreateStatus vttCreateStatus;
final VTTransaction vtTransaction;
final String? message;

VTTCreateState copyWith({
List<Input>? inputs,
List<ValueTransferOutput>? outputs,
List<KeyedSignature>? signatures,
VTTCreateStatus? status,
String? message,
VTTransaction? vtTransaction,
}) {
return VTTCreateState(
vtTransaction: VTTransaction(
Expand All @@ -42,6 +42,43 @@ class VTTCreateState extends Equatable {
);
}

static VTTCreateState initial(state) =>
state.copyWith(status: VTTCreateStatus.initial);

static VTTCreateState building(
VTTCreateState state,
VttBuilder builder,
) {
return state.copyWith(
inputs: builder.inputs,
outputs: builder.outputs,
status: VTTCreateStatus.building,
);
}

static VTTCreateState busy(VTTCreateState state) =>
state.copyWith(status: VTTCreateStatus.busy);

static VTTCreateState signing(VTTCreateState state) =>
state.copyWith(status: VTTCreateStatus.signing);

static VTTCreateState sending(VTTCreateState state) =>
state.copyWith(status: VTTCreateStatus.sending);

static VTTCreateState accepted(VTTCreateState state) =>
state.copyWith(status: VTTCreateStatus.accepted);

static VTTCreateState finished(
VTTCreateState state, VTTransaction vtTransaction) =>
state.copyWith(
vtTransaction: vtTransaction,
status: VTTCreateStatus.finished,
message: null,
);

static VTTCreateState exception(VTTCreateState state, String error) =>
state.copyWith(status: VTTCreateStatus.exception, message: error);

@override
List<Object> get props => [vtTransaction, vttCreateStatus];
}
4 changes: 2 additions & 2 deletions lib/screens/send_transaction/send_vtt_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ class CreateVttScreenState extends State<CreateVttScreen>
try {
setState(() => {
currentTxOutput = vttBloc.state.vtTransaction.body.outputs.first,
savedFeeAmount = vttBloc.feeNanoWit.toString(),
savedFeeType = vttBloc.feeType,
savedFeeAmount = vttBloc.vttBuilder.feeNanoWit.toString(),
savedFeeType = vttBloc.vttBuilder.feeType,
});
} catch (err) {
// There is no saved transaction details
Expand Down
6 changes: 6 additions & 0 deletions lib/util/storage/database/account.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:core';

import 'package:flutter/foundation.dart';
import 'package:my_wit_wallet/shared/api_database.dart';
import 'package:my_wit_wallet/util/storage/database/transaction_adapter.dart';
import 'package:my_wit_wallet/util/storage/database/wallet.dart';
import 'package:my_wit_wallet/util/utxo_list_to_string.dart';
Expand Down Expand Up @@ -170,6 +172,10 @@ class Account extends _Account {
address == other.address;
}

static Account? fromDatabase(ApiDatabase database, String address) {
return database.walletStorage.currentWallet.accountByAddress(address);
}

@override
int get hashCode => hash4(walletName.hashCode, address.hashCode,
vttHashes.hashCode, utxos.hashCode);
Expand Down
131 changes: 117 additions & 14 deletions lib/util/storage/database/wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:witnet/explorer.dart';
import 'package:witnet/utils.dart';
import 'package:witnet/witnet.dart';

import '../../../bloc/crypto/api_crypto.dart';
import 'account.dart';
import 'balance_info.dart';

Expand Down Expand Up @@ -74,6 +75,7 @@ class Wallet {
Map<int, Account> externalAccounts = {};
Map<int, Account> internalAccounts = {};
Account? masterAccount;
Account? changeAccount;

Map<String, Account> accountMap(KeyType keyType) {
Map<String, Account> _accounts = {};
Expand Down Expand Up @@ -309,20 +311,8 @@ class Wallet {
}
}

BalanceInfo balanceNanoWit() {
List<Utxo> _utxos = [];

internalAccounts.forEach((address, account) {
_utxos.addAll(account.utxos);
});
externalAccounts.forEach((address, account) {
_utxos.addAll(account.utxos);
});
if (masterAccount != null) {
_utxos.addAll(masterAccount!.utxos);
}
return BalanceInfo.fromUtxoList(_utxos);
}
BalanceInfo balanceNanoWit() =>
BalanceInfo.fromUtxoList(utxoMap().keys.toList());

Future<Account> generateKey({
required int index,
Expand Down Expand Up @@ -600,6 +590,119 @@ class Wallet {
}
}

Map<Utxo, String> utxoMap([bool includeTimeLocked = true]) {
Map<Utxo, String> _utxoMap = {};

void updateMapByAccount(Account account) {
account.utxos.forEach((utxo) {
if (includeTimeLocked) {
_utxoMap[utxo] = account.address;
} else {
if (!utxoLocked(utxo)) {
_utxoMap[utxo] = account.address;
}
}
});
}

internalAccounts.forEach((address, account) {
updateMapByAccount(account);
});
externalAccounts.forEach((address, account) {
updateMapByAccount(account);
});
if (masterAccount != null) {
updateMapByAccount(masterAccount!);
}

return _utxoMap;
}

bool utxoLocked(Utxo utxo) {
if (utxo.timelock > 0) {
int _ts = utxo.timelock * 1000;
DateTime _timelock = DateTime.fromMillisecondsSinceEpoch(_ts);
int currentTimestamp = DateTime.now().millisecondsSinceEpoch;
if (_timelock.millisecondsSinceEpoch > currentTimestamp) return true;
return false;
}
return false;
}

Account? utxoOwner(Utxo utxo) {
return null;
}

List<InputUtxo> buildInputUtxoList(List<Utxo> utxos) {
List<InputUtxo> _inputs = [];

/// loop through utxos
for (int i = 0; i < utxos.length; i++) {
Utxo currentUtxo = utxos.elementAt(i);

/// loop though every external account
externalAccounts.forEach((index, account) {
if (account.utxos.contains(currentUtxo)) {
_inputs.add(InputUtxo(
address: account.address,
input: currentUtxo.toInput(),
value: currentUtxo.value));
}
});

/// loop though every internal account
internalAccounts.forEach((index, account) {
if (account.utxos.contains(currentUtxo)) {
_inputs.add(InputUtxo(
address: account.address,
input: currentUtxo.toInput(),
value: currentUtxo.value));
}
});

if (walletType == WalletType.single && masterAccount != null) {
_inputs.add(InputUtxo(
address: masterAccount!.address,
input: currentUtxo.toInput(),
value: currentUtxo.value));
}
}

return _inputs;
}

Future<Account> getChangeAccount() async {
/// get the internal account that will be used for any change
bool changeAccountSet = false;

if (walletType == WalletType.hd) {
for (int i = 0; i < internalAccounts.keys.length; i++) {
if (!changeAccountSet) {
Account account = internalAccounts[i]!;
if (account.vttHashes.isEmpty) {
changeAccount = account;
changeAccountSet = true;
}
}
}

/// did we run out of change addresses?
if (!changeAccountSet) {
ApiCrypto apiCrypto = Locator.instance<ApiCrypto>();
changeAccount = await apiCrypto.generateAccount(
this,
KeyType.internal,
internalAccounts.keys.length,
);
}
} else {
/// master node
changeAccount = masterAccount!;
}

return changeAccount!;
}

void printDebug() {
print('Wallet');
print(' ID: $id');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
Expand All @@ -13,6 +15,29 @@ class QrScanner extends StatelessWidget {
required this.onChanged,
}) : super(key: key);

static Color iconColor(bool isScanQrFocused, ThemeData theme) =>
isScanQrFocused
? theme.textSelectionTheme.cursorColor!
: theme.inputDecorationTheme.enabledBorder!.borderSide.color;

static Icon icon = Icon(FontAwesomeIcons.qrcode);

static Widget? iconButton({
FocusNode? focusNode,
void Function()? onPressed,
required bool isScanQrFocused,
required ThemeData theme,
}) {
return !Platform.isWindows && !Platform.isLinux
? IconButton(
focusNode: focusNode,
splashRadius: 1,
icon: QrScanner.icon,
onPressed: () => onPressed,
color: QrScanner.iconColor(isScanQrFocused, theme))
: null;
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down
Loading

0 comments on commit 46509ac

Please sign in to comment.