Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persistence - Wallet Addresses Storage #41

Draft
wants to merge 24 commits into
base: add_arrr
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c89d825
create WalletAddress
naezith Jun 27, 2023
b7d13c1
create AccountAddressesApiInterface
naezith Jun 27, 2023
4ee8952
create AccountAddressesApiHive
naezith Jun 27, 2023
0b25c30
validation and cleanup for AccountAddressesApiHive
naezith Jun 27, 2023
3cf3d9e
error handling for AccountAddressesApiHive
naezith Jun 27, 2023
01f5cab
field params instead of WalletAddress obj
naezith Jun 27, 2023
90b1541
updateOrCreate for AccountAddressesApiHive
naezith Jun 27, 2023
14748a4
factory for AccountAddressesApiHive
naezith Jun 27, 2023
cb92f78
HiveException for AccountAddressesApiHive
naezith Jun 27, 2023
562b899
FutureOr for AccountAddressesApiHive init
naezith Jun 27, 2023
dc8fa90
store original exception in HiveException
naezith Jun 27, 2023
448162a
remove comment
naezith Jun 27, 2023
16bb0ca
call Hive.registerAdapter only once
naezith Jun 28, 2023
8d7d6ee
add trailing commas
naezith Jun 28, 2023
b0b6fa7
added @required and named params
naezith Jun 28, 2023
f5809ab
implement AccountAddressesRepository
naezith Jun 28, 2023
1b3066b
atomic AccountAddressesRepository
naezith Jun 29, 2023
010fcc5
cancel AddressRepository subscription earlier
naezith Jun 29, 2023
3ea1e55
remove custom HiveException
naezith Jul 3, 2023
fac2231
Add a real-time stream for active wallet changes
CharlVS Jul 3, 2023
e1e8ba2
Add stream for wallet snapshot changes
CharlVS Jul 3, 2023
7197b76
Add wallet address serialisation metods
CharlVS Jul 4, 2023
8631c9b
Save wallet changes in DB class methods
CharlVS Jul 4, 2023
9d3548e
Add realtime address list helper method for bloc
CharlVS Jul 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:komodo_dex/packages/z_coin_activation/bloc/z_coin_activation_blo
import 'package:komodo_dex/packages/z_coin_activation/bloc/z_coin_activation_state.dart';
import 'package:komodo_dex/packages/z_coin_activation/bloc/z_coin_notifications.dart';
import 'package:komodo_dex/packages/z_coin_activation/widgets/z_coin_status_list_tile.dart';
import 'package:komodo_dex/services/db/persistence_manager.dart';
import '../app_config/app_config.dart';
import '../blocs/authenticate_bloc.dart';
import '../blocs/coins_bloc.dart';
Expand Down Expand Up @@ -71,6 +72,9 @@ Future<void> startApp() async {
await ZCoinProgressNotifications.initNotifications();
try {
mmSe.metrics();

await PersistenceManager.init();

startup.start();

return runApp(
Expand Down
6 changes: 6 additions & 0 deletions lib/model/wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ class Wallet {
'id': id ?? '',
'name': name ?? '',
};

static bool areWalletsEqual(Wallet wallet1, Wallet wallet2) {
assert(wallet1 != null && wallet2 != null, "Can't compare null wallets");

return wallet1.id == wallet2.id && wallet1.name == wallet2.name;
}
}
214 changes: 214 additions & 0 deletions lib/packages/account_addresses/api/account_addresses_api_hive.dart
naezith marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import 'dart:async';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:komodo_dex/packages/account_addresses/api/account_addresses_api_interface.dart';
import 'package:komodo_dex/packages/account_addresses/models/wallet_address.dart';
import 'package:meta/meta.dart';

class AccountAddressesApiHive implements AccountAddressesApiInterface {
final Box<WalletAddress> _box;

static bool _isAdapterRegistered = false;

AccountAddressesApiHive._(this._box);

static FutureOr<AccountAddressesApiHive> initialize() async {
naezith marked this conversation as resolved.
Show resolved Hide resolved
if (!_isAdapterRegistered) {
Hive.registerAdapter(WalletAddressAdapter());
_isAdapterRegistered = true;
}

Box<WalletAddress> box;
try {
box = await Hive.openBox<WalletAddress>('account_addresses');
} catch (e) {
throw Exception('Failed to open Hive box: $e');
}
return AccountAddressesApiHive._(box);
}

String _getKey(String walletId, String address) {
return '${walletId}_$address';
}

@override
Future<void> create({
@required String walletId,
@required String address,
@required String ticker,
@required double availableBalance,
@required String accountId,
}) async {
_validateFields(walletId, address, availableBalance);
try {
await _box.put(
_getKey(walletId, address),
WalletAddress(
walletId: walletId,
address: address,
ticker: ticker,
availableBalance: availableBalance,
accountId: accountId,
),
);
} catch (e) {
throw Exception('Failed to create WalletAddress: $e');
}
}

@override
Future<void> updateOrCreate({
@required String walletId,
@required String address,
@required String ticker,
@required double availableBalance,
@required String accountId,
}) async {
final key = _getKey(walletId, address);
if (_box.containsKey(key)) {
await update(
walletId: walletId,
address: address,
ticker: ticker,
availableBalance: availableBalance,
accountId: accountId,
);
} else {
await create(
walletId: walletId,
address: address,
ticker: ticker,
availableBalance: availableBalance,
accountId: accountId,
);
}
}

@override
Future<void> update({
@required String walletId,
@required String address,
String ticker,
double availableBalance,
String accountId,
}) async {
_validateFields(walletId, address, availableBalance);
final key = _getKey(walletId, address);
final existingWalletAddress = _box.get(key);

if (existingWalletAddress == null) {
throw Exception('WalletAddress not found');
}

final updatedWalletAddress = WalletAddress(
walletId: walletId,
address: address,
ticker: ticker ?? existingWalletAddress.ticker,
availableBalance:
availableBalance ?? existingWalletAddress.availableBalance,
accountId: accountId ?? existingWalletAddress.accountId,
);

try {
await _box.put(key, updatedWalletAddress);
} catch (e) {
throw Exception('Failed to update WalletAddress: $e');
}
}

@override
Future<void> deleteOne({
@required String walletId,
@required String address,
}) async {
try {
await _box.delete(_getKey(walletId, address));
} catch (e) {
throw Exception('Failed to delete WalletAddress: $e');
}
}

@override
Future<void> deleteAll({@required String walletId}) async {
final keys = _box.keys.where((key) => key.startsWith(walletId));
try {
await _box.deleteAll(keys);
} catch (e) {
throw Exception('Failed to delete WalletAddresses: $e');
}
}

@override
Future<WalletAddress> readOne({
@required String walletId,
@required String address,
}) async {
try {
return _box.get(_getKey(walletId, address));
} catch (e) {
throw Exception('Failed to read WalletAddress: $e');
}
}

@override
Future<List<WalletAddress>> readAll({@required String walletId}) async {
try {
return _box.values
.where((walletAddress) => walletAddress.walletId == walletId)
.toList();
} catch (e) {
throw Exception('Failed to read WalletAddresses: $e');
}
}

@override
Stream<WalletAddress> watchAll({@required String walletId}) {
return _box
.watch()
.where(
(event) =>
event.key.startsWith(walletId) && event.value is WalletAddress,
)
.map((event) => event.value as WalletAddress);
}

@override
Stream<List<WalletAddress>> watchAllList({@required String walletId}) async* {
List<WalletAddress> addresses = await readAll(walletId: walletId);
yield addresses;

yield* watchAll(walletId: walletId).map<List<WalletAddress>>(
(walletAddress) => addresses = addresses
.map(
(address) => address.address == walletAddress.address
? walletAddress
: address,
)
.toList(),
);
}

Future<void> close() async {
try {
await _box.close();
} catch (e) {
throw Exception('Failed to close Hive box: $e');
}
}

void _validateFields(
String walletId,
String address,
double availableBalance,
) {
if (walletId == null || walletId.isEmpty) {
throw ArgumentError('Wallet ID must not be empty');
}
if (address == null || address.isEmpty) {
throw ArgumentError('Address must not be empty');
}
if (availableBalance == null || availableBalance < 0) {
throw ArgumentError('Available balance must be non-negative');
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:meta/meta.dart';
import 'package:komodo_dex/packages/account_addresses/models/wallet_address.dart';

abstract class AccountAddressesApiInterface {
Future<void> create({
@required String walletId,
@required String address,
@required String ticker,
@required double availableBalance,
@required String accountId,
});

Future<void> update({
@required String walletId,
@required String address,
String ticker,
double availableBalance,
String accountId,
});

Future<void> updateOrCreate({
@required String walletId,
@required String address,
@required String ticker,
@required double availableBalance,
@required String accountId,
});

Future<void> deleteOne({
@required String walletId,
@required String address,
});

Future<void> deleteAll({
@required String walletId,
});

Future<WalletAddress> readOne({
@required String walletId,
@required String address,
});

Future<List<WalletAddress>> readAll({
@required String walletId,
});

Stream<WalletAddress> watchAll({
@required String walletId,
});

Stream<List<WalletAddress>> watchAllList({
@required String walletId,
});
}
43 changes: 43 additions & 0 deletions lib/packages/account_addresses/models/wallet_address.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:meta/meta.dart';
import 'package:hive/hive.dart';

part 'wallet_address.g.dart';

@HiveType(typeId: 0)
class WalletAddress {
@HiveField(0)
final String walletId;

@HiveField(1)
final String address;

@HiveField(2)
final String ticker;

@HiveField(3)
final double availableBalance;

@HiveField(4)
final String accountId;

WalletAddress({
@required this.walletId,
@required this.address,
@required this.ticker,
@required this.availableBalance,
@required this.accountId,
});

Map<String, dynamic> toJson() => {
'walletId': walletId,
'address': address,
'ticker': ticker,
'availableBalance': availableBalance,
'accountId': accountId,
};

@override
String toString() {
return 'WalletAddress{walletId: $walletId, address: $address, ticker: $ticker, availableBalance: $availableBalance, accountId: $accountId}';
}
}
26 changes: 26 additions & 0 deletions lib/packages/account_addresses/models/wallet_address.g.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
part of 'wallet_address.dart';

class WalletAddressAdapter extends TypeAdapter<WalletAddress> {
@override
final typeId = 0;

@override
WalletAddress read(BinaryReader reader) {
return WalletAddress(
walletId: reader.read(),
address: reader.read(),
ticker: reader.read(),
availableBalance: reader.read(),
accountId: reader.read(),
);
}

@override
void write(BinaryWriter writer, WalletAddress obj) {
writer.write(obj.walletId);
writer.write(obj.address);
writer.write(obj.ticker);
writer.write(obj.availableBalance);
writer.write(obj.accountId);
}
}
Loading