mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 11:59:30 +00:00
WIP: Add wallet tokens
This commit is contained in:
parent
706cbbfa39
commit
4efd432de6
15 changed files with 1874 additions and 1673 deletions
|
@ -26,18 +26,22 @@ import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
|
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
|
||||||
import 'package:stackwallet/utilities/eth_commons.dart';
|
import 'package:stackwallet/utilities/eth_commons.dart';
|
||||||
|
|
||||||
|
import 'package:stackwallet/services/coins/manager.dart';
|
||||||
|
|
||||||
class MyTokensView extends ConsumerStatefulWidget {
|
class MyTokensView extends ConsumerStatefulWidget {
|
||||||
const MyTokensView({
|
const MyTokensView({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
required this.managerProvider,
|
||||||
|
required this.walletId,
|
||||||
required this.walletAddress,
|
required this.walletAddress,
|
||||||
required this.tokens,
|
required this.tokens,
|
||||||
required this.walletName,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
static const String routeName = "/myTokens";
|
static const String routeName = "/myTokens";
|
||||||
|
final ChangeNotifierProvider<Manager> managerProvider;
|
||||||
|
final String walletId;
|
||||||
final String walletAddress;
|
final String walletAddress;
|
||||||
final List<dynamic> tokens;
|
final List<dynamic> tokens;
|
||||||
final String walletName;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<MyTokensView> createState() => _TokenDetailsViewState();
|
ConsumerState<MyTokensView> createState() => _TokenDetailsViewState();
|
||||||
|
@ -45,12 +49,13 @@ class MyTokensView extends ConsumerStatefulWidget {
|
||||||
|
|
||||||
class _TokenDetailsViewState extends ConsumerState<MyTokensView> {
|
class _TokenDetailsViewState extends ConsumerState<MyTokensView> {
|
||||||
late final String walletAddress;
|
late final String walletAddress;
|
||||||
|
// late final String walletName;
|
||||||
late final TextEditingController _searchController;
|
late final TextEditingController _searchController;
|
||||||
final searchFieldFocusNode = FocusNode();
|
final searchFieldFocusNode = FocusNode();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
walletAddress = widget.walletAddress;
|
// walletAddress = widget.walletAddress;
|
||||||
_searchController = TextEditingController();
|
_searchController = TextEditingController();
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
@ -101,7 +106,7 @@ class _TokenDetailsViewState extends ConsumerState<MyTokensView> {
|
||||||
width: 12,
|
width: 12,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"${widget.walletName} Tokens",
|
"${ref.read(widget.managerProvider).walletName} Tokens",
|
||||||
style: STextStyles.desktopH3(context),
|
style: STextStyles.desktopH3(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -123,7 +128,7 @@ class _TokenDetailsViewState extends ConsumerState<MyTokensView> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
"${widget.walletName} Tokens",
|
"${ref.read(widget.managerProvider).walletName} Tokens",
|
||||||
style: STextStyles.navBarTitle(context),
|
style: STextStyles.navBarTitle(context),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
|
@ -261,7 +266,10 @@ class _TokenDetailsViewState extends ConsumerState<MyTokensView> {
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: MyTokensList(
|
child: MyTokensList(
|
||||||
tokens: widget.tokens, walletAddress: walletAddress),
|
managerProvider: widget.managerProvider,
|
||||||
|
walletId: widget.walletId,
|
||||||
|
tokens: widget.tokens,
|
||||||
|
walletAddress: widget.walletAddress),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||||
|
import 'package:stackwallet/providers/global/tokens_provider.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
@ -13,14 +14,22 @@ import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||||
|
|
||||||
|
import 'package:stackwallet/services/coins/manager.dart';
|
||||||
|
|
||||||
class MyTokenSelectItem extends ConsumerWidget {
|
class MyTokenSelectItem extends ConsumerWidget {
|
||||||
const MyTokenSelectItem(
|
const MyTokenSelectItem(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
|
required this.managerProvider,
|
||||||
|
required this.walletId,
|
||||||
required this.walletAddress,
|
required this.walletAddress,
|
||||||
required this.tokenData,
|
required this.tokenData,
|
||||||
required})
|
required})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
|
final ChangeNotifierProvider<Manager> managerProvider;
|
||||||
|
final String walletId;
|
||||||
final String walletAddress;
|
final String walletAddress;
|
||||||
final Map<String, String> tokenData;
|
final Map<String, String> tokenData;
|
||||||
|
|
||||||
|
@ -42,9 +51,38 @@ class MyTokenSelectItem extends ConsumerWidget {
|
||||||
BorderRadius.circular(Constants.size.circularBorderRadius),
|
BorderRadius.circular(Constants.size.circularBorderRadius),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
// ref
|
||||||
|
// .read(walletsChangeNotifierProvider)
|
||||||
|
// .getManagerProvider(walletId)
|
||||||
|
|
||||||
|
// final walletId = ref
|
||||||
|
// .read(managerProvider)
|
||||||
|
// .walletName;
|
||||||
|
// final manager = ref
|
||||||
|
// .read(walletsChangeNotifierProvider)
|
||||||
|
// .getManagerProvider(walletId)
|
||||||
|
|
||||||
|
// arguments: Tuple2(walletId, managerProvider, walletAddress,
|
||||||
|
// tokenData["contractAddress"])
|
||||||
|
|
||||||
|
// arguments: Tuple2(
|
||||||
|
// walletId,
|
||||||
|
// ref
|
||||||
|
// .read(tokensChangeNotifierProvider)
|
||||||
|
// .getManagerProvider(walletId)
|
||||||
|
|
||||||
|
// arguments: Tuple2(
|
||||||
|
// walletId,
|
||||||
|
// ref
|
||||||
|
// .read(tokensChangeNotifierProvider)
|
||||||
|
// .getManagerProvider(walletId)
|
||||||
|
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
TokenView.routeName,
|
TokenView.routeName,
|
||||||
arguments: Tuple2(walletAddress, tokenData["contractAddress"]),
|
arguments: Tuple2(
|
||||||
|
walletId,
|
||||||
|
ref.read(tokensChangeNotifierProvider).getManagerProvider(
|
||||||
|
tokenData["contractAddress"] as String)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/pages/token_view/sub_widgets/my_token_select_item.dart';
|
import 'package:stackwallet/pages/token_view/sub_widgets/my_token_select_item.dart';
|
||||||
|
import 'package:stackwallet/services/coins/manager.dart';
|
||||||
|
|
||||||
class MyTokensList extends StatelessWidget {
|
class MyTokensList extends StatelessWidget {
|
||||||
const MyTokensList({
|
const MyTokensList({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
required this.managerProvider,
|
||||||
|
required this.walletId,
|
||||||
required this.tokens,
|
required this.tokens,
|
||||||
required this.walletAddress,
|
required this.walletAddress,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final ChangeNotifierProvider<Manager> managerProvider;
|
||||||
|
final String walletId;
|
||||||
final List<dynamic> tokens;
|
final List<dynamic> tokens;
|
||||||
final String walletAddress;
|
final String walletAddress;
|
||||||
|
|
||||||
|
@ -23,6 +28,8 @@ class MyTokensList extends StatelessWidget {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
child: MyTokenSelectItem(
|
child: MyTokenSelectItem(
|
||||||
|
managerProvider: managerProvider,
|
||||||
|
walletId: walletId,
|
||||||
walletAddress: walletAddress,
|
walletAddress: walletAddress,
|
||||||
tokenData: tokens[index] as Map<String, String>,
|
tokenData: tokens[index] as Map<String, String>,
|
||||||
),
|
),
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -783,18 +783,17 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
.read(managerProvider)
|
.read(managerProvider)
|
||||||
.currentReceivingAddress;
|
.currentReceivingAddress;
|
||||||
|
|
||||||
final walletName = ref
|
// String walletTokens = await
|
||||||
.read(managerProvider)
|
|
||||||
.walletName;
|
|
||||||
|
|
||||||
List<dynamic> tokens =
|
List<dynamic> tokens =
|
||||||
await getWalletTokens(
|
await getWalletTokens(await ref
|
||||||
walletAddress);
|
.read(managerProvider)
|
||||||
|
.currentReceivingAddress);
|
||||||
|
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
MyTokensView.routeName,
|
MyTokensView.routeName,
|
||||||
arguments: Tuple3(walletAddress,
|
arguments: Tuple4(managerProvider,
|
||||||
tokens, walletName),
|
walletId, walletAddress, tokens),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
23
lib/providers/global/tokens_provider.dart
Normal file
23
lib/providers/global/tokens_provider.dart
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/providers/global/node_service_provider.dart';
|
||||||
|
import 'package:stackwallet/providers/global/tokens_service_provider.dart';
|
||||||
|
import 'package:stackwallet/providers/global/wallets_service_provider.dart';
|
||||||
|
import 'package:stackwallet/services/tokens.dart';
|
||||||
|
import 'package:stackwallet/services/wallets.dart';
|
||||||
|
|
||||||
|
int _count = 0;
|
||||||
|
|
||||||
|
final tokensChangeNotifierProvider = ChangeNotifierProvider<Tokens>((ref) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
_count++;
|
||||||
|
debugPrint("tokensChangeNotifierProvider instantiation count: $_count");
|
||||||
|
}
|
||||||
|
|
||||||
|
final tokensService = ref.read(tokensServiceChangeNotifierProvider);
|
||||||
|
// final nodeService = ref.read(nodeServiceChangeNotifierProvider);
|
||||||
|
|
||||||
|
final tokens = Tokens.sharedInstance;
|
||||||
|
tokens.tokensService = tokensService;
|
||||||
|
return tokens;
|
||||||
|
});
|
20
lib/providers/global/tokens_service_provider.dart
Normal file
20
lib/providers/global/tokens_service_provider.dart
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||||
|
import 'package:stackwallet/services/tokens_service.dart';
|
||||||
|
import 'package:stackwallet/services/wallets_service.dart';
|
||||||
|
|
||||||
|
int _count = 0;
|
||||||
|
|
||||||
|
final tokensServiceChangeNotifierProvider =
|
||||||
|
ChangeNotifierProvider<TokensService>((ref) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
_count++;
|
||||||
|
debugPrint(
|
||||||
|
"tokensServiceChangeNotifierProvider instantiation count: $_count");
|
||||||
|
}
|
||||||
|
|
||||||
|
return TokensService(
|
||||||
|
secureStorageInterface: ref.read(secureStoreProvider),
|
||||||
|
);
|
||||||
|
});
|
|
@ -117,6 +117,7 @@ import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/syncin
|
||||||
import 'package:stackwallet/services/coins/manager.dart';
|
import 'package:stackwallet/services/coins/manager.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||||
|
import 'package:stackwallet/services/tokens/token_manager.dart';
|
||||||
import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart';
|
import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart';
|
||||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
@ -1295,13 +1296,15 @@ class RouteGenerator {
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
case MyTokensView.routeName:
|
case MyTokensView.routeName:
|
||||||
if (args is Tuple3<String, List<dynamic>, String>) {
|
if (args is Tuple4<ChangeNotifierProvider<Manager>, String, String,
|
||||||
|
List<dynamic>>) {
|
||||||
return getRoute(
|
return getRoute(
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
builder: (_) => MyTokensView(
|
builder: (_) => MyTokensView(
|
||||||
walletAddress: args.item1,
|
managerProvider: args.item1,
|
||||||
tokens: args.item2,
|
walletId: args.item2,
|
||||||
walletName: args.item3),
|
walletAddress: args.item3,
|
||||||
|
tokens: args.item4),
|
||||||
settings: RouteSettings(
|
settings: RouteSettings(
|
||||||
name: settings.name,
|
name: settings.name,
|
||||||
),
|
),
|
||||||
|
@ -1309,13 +1312,29 @@ class RouteGenerator {
|
||||||
}
|
}
|
||||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||||
|
|
||||||
|
// case WalletView.routeName:
|
||||||
|
// if (args is Tuple2<String, ChangeNotifierProvider<Manager>>) {
|
||||||
|
// return getRoute(
|
||||||
|
// shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
|
// builder: (_) => WalletView(
|
||||||
|
// walletId: args.item1,
|
||||||
|
// managerProvider: args.item2,
|
||||||
|
// ),
|
||||||
|
// settings: RouteSettings(
|
||||||
|
// name: settings.name,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
case TokenView.routeName:
|
case TokenView.routeName:
|
||||||
if (args is Tuple2<String, String>) {
|
if (args is Tuple2<String, ChangeNotifierProvider<TokenManager>>) {
|
||||||
return getRoute(
|
return getRoute(
|
||||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||||
builder: (_) => TokenView(
|
builder: (_) => TokenView(
|
||||||
walletAddress: args.item1,
|
contractAddress: args.item1,
|
||||||
contractAddress: args.item2,
|
managerProvider: args.item2,
|
||||||
|
// walletAddress: args.item3,
|
||||||
|
// contractAddress: args.item4,
|
||||||
),
|
),
|
||||||
settings: RouteSettings(
|
settings: RouteSettings(
|
||||||
name: settings.name,
|
name: settings.name,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -84,9 +84,19 @@ class GasTracker {
|
||||||
class EthereumWallet extends CoinServiceAPI {
|
class EthereumWallet extends CoinServiceAPI {
|
||||||
NodeModel? _ethNode;
|
NodeModel? _ethNode;
|
||||||
final _gasLimit = 21000;
|
final _gasLimit = 21000;
|
||||||
final _blockExplorer = "https://blockscout.com/eth/mainnet/api?";
|
// final _blockExplorer = "https://blockscout.com/eth/mainnet/api?";
|
||||||
|
final _blockExplorer = "https://api.etherscan.io/api?";
|
||||||
final _gasTrackerUrl = "https://beaconcha.in/api/v1/execution/gasnow";
|
final _gasTrackerUrl = "https://beaconcha.in/api/v1/execution/gasnow";
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get walletId => _walletId;
|
||||||
|
late String _walletId;
|
||||||
|
|
||||||
|
late String _walletName;
|
||||||
|
late Coin _coin;
|
||||||
|
Timer? timer;
|
||||||
|
Timer? _networkAliveTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set isFavorite(bool markFavorite) {
|
set isFavorite(bool markFavorite) {
|
||||||
DB.instance.put<dynamic>(
|
DB.instance.put<dynamic>(
|
||||||
|
@ -140,7 +150,6 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
_walletId = walletId;
|
_walletId = walletId;
|
||||||
_walletName = walletName;
|
_walletName = walletName;
|
||||||
_coin = coin;
|
_coin = coin;
|
||||||
|
|
||||||
_priceAPI = priceAPI ?? PriceAPI(Client());
|
_priceAPI = priceAPI ?? PriceAPI(Client());
|
||||||
_secureStore = secureStore;
|
_secureStore = secureStore;
|
||||||
}
|
}
|
||||||
|
@ -167,11 +176,6 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get walletName => _walletName;
|
String get walletName => _walletName;
|
||||||
late String _walletName;
|
|
||||||
|
|
||||||
late Coin _coin;
|
|
||||||
Timer? timer;
|
|
||||||
Timer? _networkAliveTimer;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<String>> get allOwnAddresses =>
|
Future<List<String>> get allOwnAddresses =>
|
||||||
|
@ -339,7 +343,9 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
await DB.instance
|
await DB.instance
|
||||||
.put<dynamic>(boxName: walletId, key: "id", value: _walletId);
|
.put<dynamic>(boxName: walletId, key: "id", value: _walletId);
|
||||||
await DB.instance.put<dynamic>(
|
await DB.instance.put<dynamic>(
|
||||||
boxName: walletId, key: 'receivingAddresses', value: ["0"]);
|
boxName: walletId,
|
||||||
|
key: 'receivingAddresses',
|
||||||
|
value: [_credentials.address.toString()]);
|
||||||
await DB.instance
|
await DB.instance
|
||||||
.put<dynamic>(boxName: walletId, key: "receivingIndex", value: 0);
|
.put<dynamic>(boxName: walletId, key: "receivingIndex", value: 0);
|
||||||
await DB.instance
|
await DB.instance
|
||||||
|
@ -480,6 +486,46 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
|
|
||||||
_credentials = EthPrivateKey.fromHex(StringToHex.toHexString(mnemonic));
|
_credentials = EthPrivateKey.fromHex(StringToHex.toHexString(mnemonic));
|
||||||
|
|
||||||
|
print(_credentials.address);
|
||||||
|
//Get ERC-20 transactions for wallet (So we can get the and save wallet's ERC-20 TOKENS
|
||||||
|
AddressTransaction tokenTransactions = await fetchAddressTransactions(
|
||||||
|
_credentials.address.toString(), "tokentx");
|
||||||
|
var tokenMap = {};
|
||||||
|
List<Map<dynamic, dynamic>> tokensList = [];
|
||||||
|
if (tokenTransactions.message == "OK") {
|
||||||
|
final allTxs = tokenTransactions.result;
|
||||||
|
print("RESULT IS $allTxs");
|
||||||
|
allTxs.forEach((element) {
|
||||||
|
String key = element["tokenSymbol"] as String;
|
||||||
|
tokenMap[key] = {};
|
||||||
|
tokenMap[key]["balance"] = 0;
|
||||||
|
|
||||||
|
if (tokenMap.containsKey(key)) {
|
||||||
|
tokenMap[key]["contractAddress"] = element["contractAddress"];
|
||||||
|
tokenMap[key]["decimals"] = element["tokenDecimal"];
|
||||||
|
tokenMap[key]["name"] = element["tokenName"];
|
||||||
|
tokenMap[key]["symbol"] = element["tokenSymbol"];
|
||||||
|
if (element["to"] == _credentials.address.toString()) {
|
||||||
|
tokenMap[key]["balance"] += int.parse(element["value"] as String);
|
||||||
|
} else {
|
||||||
|
tokenMap[key]["balance"] -= int.parse(element["value"] as String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tokenMap.forEach((key, value) {
|
||||||
|
//Create New token
|
||||||
|
|
||||||
|
tokensList.add(value as Map<dynamic, dynamic>);
|
||||||
|
});
|
||||||
|
|
||||||
|
await _secureStore.write(
|
||||||
|
key: '${_walletId}_tokens', value: tokensList.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
print("THIS WALLET TOKENS IS $tokenMap");
|
||||||
|
print("ALL TOKENS LIST IS $tokensList");
|
||||||
|
|
||||||
await DB.instance
|
await DB.instance
|
||||||
.put<dynamic>(boxName: walletId, key: "id", value: _walletId);
|
.put<dynamic>(boxName: walletId, key: "id", value: _walletId);
|
||||||
await DB.instance
|
await DB.instance
|
||||||
|
@ -528,8 +574,8 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
}
|
}
|
||||||
if (!needsRefresh) {
|
if (!needsRefresh) {
|
||||||
var allOwnAddresses = await _fetchAllOwnAddresses();
|
var allOwnAddresses = await _fetchAllOwnAddresses();
|
||||||
AddressTransaction addressTransactions =
|
AddressTransaction addressTransactions = await fetchAddressTransactions(
|
||||||
await fetchAddressTransactions(allOwnAddresses.elementAt(0));
|
allOwnAddresses.elementAt(0), "txlist");
|
||||||
final txData = await transactionData;
|
final txData = await transactionData;
|
||||||
if (addressTransactions.message == "OK") {
|
if (addressTransactions.message == "OK") {
|
||||||
final allTxs = addressTransactions.result;
|
final allTxs = addressTransactions.result;
|
||||||
|
@ -854,9 +900,10 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
return isValidEthereumAddress(address);
|
return isValidEthereumAddress(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<AddressTransaction> fetchAddressTransactions(String address) async {
|
Future<AddressTransaction> fetchAddressTransactions(
|
||||||
|
String address, String action) async {
|
||||||
final response = await get(Uri.parse(
|
final response = await get(Uri.parse(
|
||||||
"${_blockExplorer}module=account&action=txlist&address=$address"));
|
"${_blockExplorer}module=account&action=$action&address=$address&apikey=EG6J7RJIQVSTP2BS59D3TY2G55YHS5F2HP"));
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return AddressTransaction.fromJson(
|
return AddressTransaction.fromJson(
|
||||||
|
@ -881,7 +928,8 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero;
|
Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero;
|
||||||
final List<Map<String, dynamic>> midSortedArray = [];
|
final List<Map<String, dynamic>> midSortedArray = [];
|
||||||
|
|
||||||
AddressTransaction txs = await fetchAddressTransactions(thisAddress);
|
AddressTransaction txs =
|
||||||
|
await fetchAddressTransactions(thisAddress, "txlist");
|
||||||
|
|
||||||
if (txs.message == "OK") {
|
if (txs.message == "OK") {
|
||||||
final allTxs = txs.result;
|
final allTxs = txs.result;
|
||||||
|
@ -991,13 +1039,11 @@ class EthereumWallet extends CoinServiceAPI {
|
||||||
return txModel;
|
return txModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
String get walletId => _walletId;
|
|
||||||
late String _walletId;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set walletName(String newName) => _walletName = newName;
|
set walletName(String newName) => _walletName = newName;
|
||||||
|
|
||||||
|
// Future<String>
|
||||||
|
|
||||||
void stopNetworkAlivePinging() {
|
void stopNetworkAlivePinging() {
|
||||||
_networkAliveTimer?.cancel();
|
_networkAliveTimer?.cancel();
|
||||||
_networkAliveTimer = null;
|
_networkAliveTimer = null;
|
||||||
|
|
374
lib/services/tokens.dart
Normal file
374
lib/services/tokens.dart
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:stackwallet/hive/db.dart';
|
||||||
|
import 'package:stackwallet/models/node_model.dart';
|
||||||
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||||
|
import 'package:stackwallet/services/coins/manager.dart';
|
||||||
|
import 'package:stackwallet/services/node_service.dart';
|
||||||
|
import 'package:stackwallet/services/tokens/token_manager.dart';
|
||||||
|
import 'package:stackwallet/services/tokens_service.dart';
|
||||||
|
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||||
|
import 'package:stackwallet/services/wallets_service.dart';
|
||||||
|
import 'package:stackwallet/utilities/default_nodes.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/sync_type_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/listenable_list.dart';
|
||||||
|
import 'package:stackwallet/utilities/listenable_map.dart';
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
// final ListenableList<ChangeNotifierProvider<Manager>> _nonFavorites =
|
||||||
|
// ListenableList();
|
||||||
|
// ListenableList<ChangeNotifierProvider<Manager>> get nonFavorites =>
|
||||||
|
// _nonFavorites;
|
||||||
|
//
|
||||||
|
// final ListenableList<ChangeNotifierProvider<Manager>> _favorites =
|
||||||
|
// ListenableList();
|
||||||
|
// ListenableList<ChangeNotifierProvider<Manager>> get favorites => _favorites;
|
||||||
|
|
||||||
|
class Tokens extends ChangeNotifier {
|
||||||
|
Tokens._private();
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
debugPrint("Tokens dispose was called!!");
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Tokens _sharedInstance = Tokens._private();
|
||||||
|
static Tokens get sharedInstance => _sharedInstance;
|
||||||
|
|
||||||
|
late TokensService tokensService;
|
||||||
|
// late NodeService nodeService;
|
||||||
|
|
||||||
|
// mirrored maps for access to reading managers without using riverpod ref
|
||||||
|
static final ListenableMap<String, ChangeNotifierProvider<TokenManager>>
|
||||||
|
_managerProviderMap = ListenableMap();
|
||||||
|
static final ListenableMap<String, TokenManager> _managerMap =
|
||||||
|
ListenableMap();
|
||||||
|
|
||||||
|
// bool get hasWallets => _managerProviderMap.isNotEmpty;
|
||||||
|
|
||||||
|
List<ChangeNotifierProvider<TokenManager>> get managerProviders =>
|
||||||
|
_managerProviderMap.values.toList(growable: false);
|
||||||
|
List<TokenManager> get managers => _managerMap.values.toList(growable: false);
|
||||||
|
|
||||||
|
// List<String> getWalletIdsFor({required Coin coin}) {
|
||||||
|
// final List<String> result = [];
|
||||||
|
// for (final manager in _managerMap.values) {
|
||||||
|
// if (manager.coin == coin) {
|
||||||
|
// result.add(manager.walletId);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Map<Coin, List<ChangeNotifierProvider<Manager>>> getManagerProvidersByCoin() {
|
||||||
|
// print("DOES THIS GET HERE?????");
|
||||||
|
// Map<Coin, List<ChangeNotifierProvider<Manager>>> result = {};
|
||||||
|
// for (final manager in _managerMap.values) {
|
||||||
|
// if (result[manager.coin] == null) {
|
||||||
|
// result[manager.coin] = [];
|
||||||
|
// }
|
||||||
|
// result[manager.coin]!.add(_managerProviderMap[manager.walletId]
|
||||||
|
// as ChangeNotifierProvider<Manager>);
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// List<ChangeNotifierProvider<Manager>> getManagerProvidersForCoin(Coin coin) {
|
||||||
|
// List<ChangeNotifierProvider<Manager>> result = [];
|
||||||
|
// for (final manager in _managerMap.values) {
|
||||||
|
// if (manager.coin == coin) {
|
||||||
|
// result.add(_managerProviderMap[manager.walletId]
|
||||||
|
// as ChangeNotifierProvider<Manager>);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
ChangeNotifierProvider<TokenManager> getManagerProvider(
|
||||||
|
String contractAddress) {
|
||||||
|
print("WALLET ID HERE IS ${_managerProviderMap.length}");
|
||||||
|
return _managerProviderMap[contractAddress]
|
||||||
|
as ChangeNotifierProvider<TokenManager>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenManager getManager(String contractAddress) {
|
||||||
|
return _managerMap[contractAddress] as TokenManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToken(
|
||||||
|
{required String contractAddress, required TokenManager manager}) {
|
||||||
|
_managerMap.add(contractAddress, manager, true);
|
||||||
|
_managerProviderMap.add(contractAddress,
|
||||||
|
ChangeNotifierProvider<TokenManager>((_) => manager), true);
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// void removeWallet({required String walletId}) {
|
||||||
|
// if (_managerProviderMap[walletId] == null) {
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "Wallets.removeWallet($walletId) failed. ManagerProvider with $walletId not found!",
|
||||||
|
// level: LogLevel.Warning);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final provider = _managerProviderMap[walletId]!;
|
||||||
|
//
|
||||||
|
// // in both non and favorites for removal
|
||||||
|
// _favorites.remove(provider, true);
|
||||||
|
// _nonFavorites.remove(provider, true);
|
||||||
|
//
|
||||||
|
// _managerProviderMap.remove(walletId, true);
|
||||||
|
// _managerMap.remove(walletId, true)!.exitCurrentWallet();
|
||||||
|
//
|
||||||
|
// notifyListeners();
|
||||||
|
// }
|
||||||
|
|
||||||
|
static bool hasLoaded = false;
|
||||||
|
|
||||||
|
Future<void> _initLinearly(
|
||||||
|
List<Tuple2<TokenManager, bool>> tuples,
|
||||||
|
) async {
|
||||||
|
for (final tuple in tuples) {
|
||||||
|
await tuple.item1.initializeExisting();
|
||||||
|
if (tuple.item2 && !tuple.item1.shouldAutoSync) {
|
||||||
|
tuple.item1.shouldAutoSync = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _count = 0;
|
||||||
|
Future<void> load(Prefs prefs) async {
|
||||||
|
debugPrint("++++++++++++++ Tokens().load() called: ${++_count} times");
|
||||||
|
if (hasLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hasLoaded = true;
|
||||||
|
|
||||||
|
// clear out any wallet hive boxes where the wallet was deleted in previous app run
|
||||||
|
// for (final walletId in DB.instance
|
||||||
|
// .values<String>(boxName: DB.boxNameWalletsToDeleteOnStart)) {
|
||||||
|
// await DB.instance.deleteBoxFromDisk(boxName: walletId);
|
||||||
|
// }
|
||||||
|
// // clear list
|
||||||
|
// await DB.instance
|
||||||
|
// .deleteAll<String>(boxName: DB.boxNameWalletsToDeleteOnStart);
|
||||||
|
//
|
||||||
|
// final map = await walletsService.walletNames;
|
||||||
|
|
||||||
|
// List<Future<dynamic>> walletInitFutures = [];
|
||||||
|
// List<Tuple2<Manager, bool>> walletsToInitLinearly = [];
|
||||||
|
|
||||||
|
// final favIdList = await walletsService.getFavoriteWalletIds();
|
||||||
|
|
||||||
|
// List<String> walletIdsToEnableAutoSync = [];
|
||||||
|
// bool shouldAutoSyncAll = false;
|
||||||
|
// switch (prefs.syncType) {
|
||||||
|
// case SyncingType.currentWalletOnly:
|
||||||
|
// // do nothing as this will be set when going into a wallet from the main screen
|
||||||
|
// break;
|
||||||
|
// case SyncingType.selectedWalletsAtStartup:
|
||||||
|
// walletIdsToEnableAutoSync.addAll(prefs.walletIdsSyncOnStartup);
|
||||||
|
// break;
|
||||||
|
// case SyncingType.allWalletsOnStartup:
|
||||||
|
// shouldAutoSyncAll = true;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for (final entry in map.entries) {
|
||||||
|
// try {
|
||||||
|
// final walletId = entry.value.walletId;
|
||||||
|
//
|
||||||
|
// late final bool isVerified;
|
||||||
|
// try {
|
||||||
|
// isVerified =
|
||||||
|
// await walletsService.isMnemonicVerified(walletId: walletId);
|
||||||
|
// } catch (e, s) {
|
||||||
|
// Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||||
|
// isVerified = false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "LOADING WALLET: ${entry.value.toString()} IS VERIFIED: $isVerified",
|
||||||
|
// level: LogLevel.Info);
|
||||||
|
// if (isVerified) {
|
||||||
|
// if (_managerMap[walletId] == null &&
|
||||||
|
// _managerProviderMap[walletId] == null) {
|
||||||
|
// final coin = entry.value.coin;
|
||||||
|
// NodeModel node = nodeService.getPrimaryNodeFor(coin: coin) ??
|
||||||
|
// DefaultNodes.getNodeFor(coin);
|
||||||
|
// // ElectrumXNode? node = await nodeService.getCurrentNode(coin: coin);
|
||||||
|
//
|
||||||
|
// // folowing shouldn't be needed as the defaults get saved on init
|
||||||
|
// // if (node == null) {
|
||||||
|
// // node = DefaultNodes.getNodeFor(coin);
|
||||||
|
// //
|
||||||
|
// // // save default node
|
||||||
|
// // nodeService.add(node, false);
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// final txTracker =
|
||||||
|
// TransactionNotificationTracker(walletId: walletId);
|
||||||
|
//
|
||||||
|
// final failovers = nodeService.failoverNodesFor(coin: coin);
|
||||||
|
//
|
||||||
|
// // load wallet
|
||||||
|
// final wallet = CoinServiceAPI.from(
|
||||||
|
// coin,
|
||||||
|
// walletId,
|
||||||
|
// entry.value.name,
|
||||||
|
// nodeService.secureStorageInterface,
|
||||||
|
// node,
|
||||||
|
// txTracker,
|
||||||
|
// prefs,
|
||||||
|
// failovers,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// final manager = Manager(wallet);
|
||||||
|
//
|
||||||
|
// final shouldSetAutoSync = shouldAutoSyncAll ||
|
||||||
|
// walletIdsToEnableAutoSync.contains(manager.walletId);
|
||||||
|
//
|
||||||
|
// if (manager.coin == Coin.monero || manager.coin == Coin.wownero) {
|
||||||
|
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
||||||
|
// } else {
|
||||||
|
// walletInitFutures.add(manager.initializeExisting().then((value) {
|
||||||
|
// if (shouldSetAutoSync) {
|
||||||
|
// manager.shouldAutoSync = true;
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _managerMap.add(walletId, manager, false);
|
||||||
|
//
|
||||||
|
// final managerProvider =
|
||||||
|
// ChangeNotifierProvider<Manager>((_) => manager);
|
||||||
|
// _managerProviderMap.add(walletId, managerProvider, false);
|
||||||
|
//
|
||||||
|
// final favIndex = favIdList.indexOf(walletId);
|
||||||
|
//
|
||||||
|
// if (favIndex == -1) {
|
||||||
|
// _nonFavorites.add(managerProvider, true);
|
||||||
|
// } else {
|
||||||
|
// // it is a favorite
|
||||||
|
// if (favIndex >= _favorites.length) {
|
||||||
|
// _favorites.add(managerProvider, true);
|
||||||
|
// } else {
|
||||||
|
// _favorites.insert(favIndex, managerProvider, true);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // wallet creation was not completed by user so we remove it completely
|
||||||
|
// await walletsService.deleteWallet(entry.value.name, false);
|
||||||
|
// }
|
||||||
|
// } catch (e, s) {
|
||||||
|
// Logging.instance.log("$e $s", level: LogLevel.Fatal);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (walletInitFutures.isNotEmpty && walletsToInitLinearly.isNotEmpty) {
|
||||||
|
// await Future.wait([
|
||||||
|
// _initLinearly(walletsToInitLinearly),
|
||||||
|
// ...walletInitFutures,
|
||||||
|
// ]);
|
||||||
|
// notifyListeners();
|
||||||
|
// } else if (walletInitFutures.isNotEmpty) {
|
||||||
|
// await Future.wait(walletInitFutures);
|
||||||
|
// notifyListeners();
|
||||||
|
// } else if (walletsToInitLinearly.isNotEmpty) {
|
||||||
|
// await _initLinearly(walletsToInitLinearly);
|
||||||
|
// notifyListeners();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<void> loadAfterStackRestore(
|
||||||
|
// Prefs prefs, List<Manager> managers) async {
|
||||||
|
// List<Future<dynamic>> walletInitFutures = [];
|
||||||
|
// List<Tuple2<Manager, bool>> walletsToInitLinearly = [];
|
||||||
|
//
|
||||||
|
// final favIdList = await walletsService.getFavoriteWalletIds();
|
||||||
|
//
|
||||||
|
// List<String> walletIdsToEnableAutoSync = [];
|
||||||
|
// bool shouldAutoSyncAll = false;
|
||||||
|
// switch (prefs.syncType) {
|
||||||
|
// case SyncingType.currentWalletOnly:
|
||||||
|
// // do nothing as this will be set when going into a wallet from the main screen
|
||||||
|
// break;
|
||||||
|
// case SyncingType.selectedWalletsAtStartup:
|
||||||
|
// walletIdsToEnableAutoSync.addAll(prefs.walletIdsSyncOnStartup);
|
||||||
|
// break;
|
||||||
|
// case SyncingType.allWalletsOnStartup:
|
||||||
|
// shouldAutoSyncAll = true;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// for (final manager in managers) {
|
||||||
|
// final walletId = manager.walletId;
|
||||||
|
//
|
||||||
|
// final isVerified =
|
||||||
|
// await walletsService.isMnemonicVerified(walletId: walletId);
|
||||||
|
// debugPrint(
|
||||||
|
// "LOADING RESTORED WALLET: ${manager.walletName} ${manager.walletId} IS VERIFIED: $isVerified");
|
||||||
|
//
|
||||||
|
// if (isVerified) {
|
||||||
|
// if (_managerMap[walletId] == null &&
|
||||||
|
// _managerProviderMap[walletId] == null) {
|
||||||
|
// final shouldSetAutoSync = shouldAutoSyncAll ||
|
||||||
|
// walletIdsToEnableAutoSync.contains(manager.walletId);
|
||||||
|
//
|
||||||
|
// if (manager.coin == Coin.monero || manager.coin == Coin.wownero) {
|
||||||
|
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
||||||
|
// } else {
|
||||||
|
// walletInitFutures.add(manager.initializeExisting().then((value) {
|
||||||
|
// if (shouldSetAutoSync) {
|
||||||
|
// manager.shouldAutoSync = true;
|
||||||
|
// }
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _managerMap.add(walletId, manager, false);
|
||||||
|
//
|
||||||
|
// final managerProvider =
|
||||||
|
// ChangeNotifierProvider<Manager>((_) => manager);
|
||||||
|
// _managerProviderMap.add(walletId, managerProvider, false);
|
||||||
|
//
|
||||||
|
// final favIndex = favIdList.indexOf(walletId);
|
||||||
|
//
|
||||||
|
// if (favIndex == -1) {
|
||||||
|
// _nonFavorites.add(managerProvider, true);
|
||||||
|
// } else {
|
||||||
|
// // it is a favorite
|
||||||
|
// if (favIndex >= _favorites.length) {
|
||||||
|
// _favorites.add(managerProvider, true);
|
||||||
|
// } else {
|
||||||
|
// _favorites.insert(favIndex, managerProvider, true);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // wallet creation was not completed by user so we remove it completely
|
||||||
|
// await walletsService.deleteWallet(manager.walletName, false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (walletInitFutures.isNotEmpty && walletsToInitLinearly.isNotEmpty) {
|
||||||
|
// await Future.wait([
|
||||||
|
// _initLinearly(walletsToInitLinearly),
|
||||||
|
// ...walletInitFutures,
|
||||||
|
// ]);
|
||||||
|
// notifyListeners();
|
||||||
|
// } else if (walletInitFutures.isNotEmpty) {
|
||||||
|
// await Future.wait(walletInitFutures);
|
||||||
|
// notifyListeners();
|
||||||
|
// } else if (walletsToInitLinearly.isNotEmpty) {
|
||||||
|
// await _initLinearly(walletsToInitLinearly);
|
||||||
|
// notifyListeners();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
127
lib/services/tokens/ethereum/ethereum_token.dart
Normal file
127
lib/services/tokens/ethereum/ethereum_token.dart
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
|
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||||
|
import 'package:stackwallet/models/paymint/transactions_model.dart';
|
||||||
|
import 'package:stackwallet/services/tokens/token_service.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
|
||||||
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
|
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||||
|
|
||||||
|
class EthereumToken extends TokenServiceAPI {
|
||||||
|
@override
|
||||||
|
late bool shouldAutoSync;
|
||||||
|
late String _walletId;
|
||||||
|
late String _contractAddress;
|
||||||
|
late SecureStorageInterface _secureStore;
|
||||||
|
late final TransactionNotificationTracker txTracker;
|
||||||
|
|
||||||
|
EthereumToken({
|
||||||
|
required String contractAddress,
|
||||||
|
required String walletId,
|
||||||
|
required SecureStorageInterface secureStore,
|
||||||
|
required TransactionNotificationTracker tracker,
|
||||||
|
}) {
|
||||||
|
txTracker = tracker;
|
||||||
|
_walletId = walletId;
|
||||||
|
_contractAddress = contractAddress;
|
||||||
|
_secureStore = secureStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement allOwnAddresses
|
||||||
|
Future<List<String>> get allOwnAddresses => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement availableBalance
|
||||||
|
Future<Decimal> get availableBalance => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement balanceMinusMaxFee
|
||||||
|
Future<Decimal> get balanceMinusMaxFee => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement coin
|
||||||
|
Coin get coin => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> confirmSend({required Map<String, dynamic> txData}) {
|
||||||
|
// TODO: implement confirmSend
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement currentReceivingAddress
|
||||||
|
Future<String> get currentReceivingAddress => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) {
|
||||||
|
// TODO: implement estimateFeeFor
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement fees
|
||||||
|
Future<FeeObject> get fees => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> initializeExisting() {
|
||||||
|
// TODO: implement initializeExisting
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> initializeNew() async {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement isConnected
|
||||||
|
bool get isConnected => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement isRefreshing
|
||||||
|
bool get isRefreshing => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement maxFee
|
||||||
|
Future<int> get maxFee => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement pendingBalance
|
||||||
|
Future<Decimal> get pendingBalance => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, dynamic>> prepareSend(
|
||||||
|
{required String address,
|
||||||
|
required int satoshiAmount,
|
||||||
|
Map<String, dynamic>? args}) {
|
||||||
|
// TODO: implement prepareSend
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> refresh() {
|
||||||
|
// TODO: implement refresh
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement totalBalance
|
||||||
|
Future<Decimal> get totalBalance => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
// TODO: implement transactionData
|
||||||
|
Future<TransactionData> get transactionData => throw UnimplementedError();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) {
|
||||||
|
// TODO: implement updateSentCachedTxData
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool validateAddress(String address) {
|
||||||
|
// TODO: implement validateAddress
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
130
lib/services/tokens/token_manager.dart
Normal file
130
lib/services/tokens/token_manager.dart
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
|
import 'package:event_bus/event_bus.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stackwallet/models/models.dart';
|
||||||
|
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||||
|
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||||
|
import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart';
|
||||||
|
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||||
|
import 'package:stackwallet/services/tokens/token_service.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
|
||||||
|
class TokenManager with ChangeNotifier {
|
||||||
|
final TokenServiceAPI _currentToken;
|
||||||
|
StreamSubscription<dynamic>? _backgroundRefreshListener;
|
||||||
|
|
||||||
|
/// optional eventbus parameter for testing only
|
||||||
|
TokenManager(this._currentToken, [EventBus? globalEventBusForTesting]) {
|
||||||
|
final bus = globalEventBusForTesting ?? GlobalEventBus.instance;
|
||||||
|
_backgroundRefreshListener = bus.on<UpdatedInBackgroundEvent>().listen(
|
||||||
|
(event) async {
|
||||||
|
// if (event.walletId == walletId) {
|
||||||
|
// notifyListeners();
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "UpdatedInBackgroundEvent activated notifyListeners() in Manager instance $hashCode $walletName with: ${event.message}",
|
||||||
|
// level: LogLevel.Info);
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenServiceAPI get token => _currentToken;
|
||||||
|
|
||||||
|
bool get hasBackgroundRefreshListener => _backgroundRefreshListener != null;
|
||||||
|
|
||||||
|
bool get isRefreshing => _currentToken.isRefreshing;
|
||||||
|
|
||||||
|
bool get shouldAutoSync => _currentToken.shouldAutoSync;
|
||||||
|
set shouldAutoSync(bool shouldAutoSync) =>
|
||||||
|
_currentToken.shouldAutoSync = shouldAutoSync;
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> prepareSend({
|
||||||
|
required String address,
|
||||||
|
required int satoshiAmount,
|
||||||
|
Map<String, dynamic>? args,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final txInfo = await _currentToken.prepareSend(
|
||||||
|
address: address,
|
||||||
|
satoshiAmount: satoshiAmount,
|
||||||
|
args: args,
|
||||||
|
);
|
||||||
|
// notifyListeners();
|
||||||
|
return txInfo;
|
||||||
|
} catch (e) {
|
||||||
|
// rethrow to pass error in alert
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
|
||||||
|
try {
|
||||||
|
final txid = await _currentToken.confirmSend(txData: txData);
|
||||||
|
|
||||||
|
txData["txid"] = txid;
|
||||||
|
await _currentToken.updateSentCachedTxData(txData);
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
return txid;
|
||||||
|
} catch (e) {
|
||||||
|
// rethrow to pass error in alert
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FeeObject> get fees => _currentToken.fees;
|
||||||
|
Future<int> get maxFee => _currentToken.maxFee;
|
||||||
|
|
||||||
|
Future<String> get currentReceivingAddress =>
|
||||||
|
_currentToken.currentReceivingAddress;
|
||||||
|
// Future<String> get currentLegacyReceivingAddress =>
|
||||||
|
// _currentWallet.currentLegacyReceivingAddress;
|
||||||
|
|
||||||
|
Future<Decimal> get availableBalance async {
|
||||||
|
_cachedAvailableBalance = await _currentToken.availableBalance;
|
||||||
|
return _cachedAvailableBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Decimal _cachedAvailableBalance = Decimal.zero;
|
||||||
|
Decimal get cachedAvailableBalance => _cachedAvailableBalance;
|
||||||
|
|
||||||
|
Future<Decimal> get pendingBalance => _currentToken.pendingBalance;
|
||||||
|
Future<Decimal> get balanceMinusMaxFee => _currentToken.balanceMinusMaxFee;
|
||||||
|
|
||||||
|
Future<Decimal> get totalBalance async {
|
||||||
|
_cachedTotalBalance = await _currentToken.totalBalance;
|
||||||
|
return _cachedTotalBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Decimal _cachedTotalBalance = Decimal.zero;
|
||||||
|
Decimal get cachedTotalBalance => _cachedTotalBalance;
|
||||||
|
|
||||||
|
Future<List<String>> get allOwnAddresses => _currentToken.allOwnAddresses;
|
||||||
|
|
||||||
|
Future<TransactionData> get transactionData => _currentToken.transactionData;
|
||||||
|
|
||||||
|
Future<void> refresh() async {
|
||||||
|
await _currentToken.refresh();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validateAddress(String address) =>
|
||||||
|
_currentToken.validateAddress(address);
|
||||||
|
|
||||||
|
Future<void> initializeNew() => _currentToken.initializeNew();
|
||||||
|
Future<void> initializeExisting() => _currentToken.initializeExisting();
|
||||||
|
|
||||||
|
Future<bool> isOwnAddress(String address) async {
|
||||||
|
final allOwnAddresses = await this.allOwnAddresses;
|
||||||
|
return allOwnAddresses.contains(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isConnected => _currentToken.isConnected;
|
||||||
|
|
||||||
|
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
|
||||||
|
return _currentToken.estimateFeeFor(satoshiAmount, feeRate);
|
||||||
|
}
|
||||||
|
}
|
73
lib/services/tokens/token_service.dart
Normal file
73
lib/services/tokens/token_service.dart
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import 'package:decimal/decimal.dart';
|
||||||
|
import 'package:stackwallet/models/models.dart';
|
||||||
|
import 'package:stackwallet/services/tokens/ethereum/ethereum_token.dart';
|
||||||
|
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
|
import 'package:stackwallet/utilities/prefs.dart';
|
||||||
|
|
||||||
|
abstract class TokenServiceAPI {
|
||||||
|
TokenServiceAPI();
|
||||||
|
|
||||||
|
factory TokenServiceAPI.from(
|
||||||
|
String contractAddress,
|
||||||
|
String walletId,
|
||||||
|
SecureStorageInterface secureStorageInterface,
|
||||||
|
TransactionNotificationTracker tracker,
|
||||||
|
Prefs prefs,
|
||||||
|
) {
|
||||||
|
return EthereumToken(
|
||||||
|
contractAddress: contractAddress,
|
||||||
|
walletId: walletId,
|
||||||
|
secureStore: secureStorageInterface,
|
||||||
|
tracker: tracker,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Coin get coin;
|
||||||
|
bool get isRefreshing;
|
||||||
|
bool get shouldAutoSync;
|
||||||
|
set shouldAutoSync(bool shouldAutoSync);
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> prepareSend({
|
||||||
|
required String address,
|
||||||
|
required int satoshiAmount,
|
||||||
|
Map<String, dynamic>? args,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<String> confirmSend({required Map<String, dynamic> txData});
|
||||||
|
|
||||||
|
Future<FeeObject> get fees;
|
||||||
|
Future<int> get maxFee;
|
||||||
|
|
||||||
|
Future<String> get currentReceivingAddress;
|
||||||
|
// Future<String> get currentLegacyReceivingAddress;
|
||||||
|
|
||||||
|
Future<Decimal> get availableBalance;
|
||||||
|
Future<Decimal> get pendingBalance;
|
||||||
|
Future<Decimal> get totalBalance;
|
||||||
|
Future<Decimal> get balanceMinusMaxFee;
|
||||||
|
|
||||||
|
Future<List<String>> get allOwnAddresses;
|
||||||
|
|
||||||
|
Future<TransactionData> get transactionData;
|
||||||
|
|
||||||
|
Future<void> refresh();
|
||||||
|
|
||||||
|
// String get walletName;
|
||||||
|
// String get walletId;
|
||||||
|
|
||||||
|
bool validateAddress(String address);
|
||||||
|
|
||||||
|
Future<void> initializeNew();
|
||||||
|
Future<void> initializeExisting();
|
||||||
|
|
||||||
|
// void Function(bool isActive)? onIsActiveWalletChanged;
|
||||||
|
|
||||||
|
bool get isConnected;
|
||||||
|
|
||||||
|
Future<int> estimateFeeFor(int satoshiAmount, int feeRate);
|
||||||
|
|
||||||
|
// used for electrumx coins
|
||||||
|
Future<void> updateSentCachedTxData(Map<String, dynamic> txData);
|
||||||
|
}
|
432
lib/services/tokens_service.dart
Normal file
432
lib/services/tokens_service.dart
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_libmonero/monero/monero.dart';
|
||||||
|
import 'package:flutter_libmonero/wownero/wownero.dart';
|
||||||
|
import 'package:stackwallet/hive/db.dart';
|
||||||
|
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
|
||||||
|
import 'package:stackwallet/services/notifications_service.dart';
|
||||||
|
import 'package:stackwallet/services/trade_sent_from_stack_service.dart';
|
||||||
|
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class TokenInfo {
|
||||||
|
final Coin coin;
|
||||||
|
final String walletId;
|
||||||
|
final String contractAddress;
|
||||||
|
|
||||||
|
const TokenInfo(
|
||||||
|
{required this.coin,
|
||||||
|
required this.walletId,
|
||||||
|
required this.contractAddress});
|
||||||
|
|
||||||
|
factory TokenInfo.fromJson(Map<String, dynamic> jsonObject) {
|
||||||
|
return TokenInfo(
|
||||||
|
coin: Coin.values.byName(jsonObject["coin"] as String),
|
||||||
|
walletId: jsonObject["id"] as String,
|
||||||
|
contractAddress: jsonObject["contractAddress"] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> toMap() {
|
||||||
|
return {
|
||||||
|
"contractAddress": contractAddress,
|
||||||
|
"walletId": walletId,
|
||||||
|
"coin": coin.name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
String toJsonString() {
|
||||||
|
return jsonEncode(toMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return "TokenInfo: ${toJsonString()}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TokensService extends ChangeNotifier {
|
||||||
|
late final SecureStorageInterface _secureStore;
|
||||||
|
|
||||||
|
// Future<Map<String, TokenInfo>>? _walletNames;
|
||||||
|
// Future<Map<String, TokenInfo>> get walletNames =>
|
||||||
|
// _walletNames ??= _fetchWalletNames();
|
||||||
|
|
||||||
|
TokensService({
|
||||||
|
required SecureStorageInterface secureStorageInterface,
|
||||||
|
}) {
|
||||||
|
_secureStore = secureStorageInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<Coin> getWalletCryptoCurrency({required String walletName}) async {
|
||||||
|
// final id = await getWalletId(walletName);
|
||||||
|
// final currency = DB.instance.get<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData, key: "${id}_cryptoCurrency");
|
||||||
|
// return Coin.values.byName(currency as String);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<bool> renameWallet({
|
||||||
|
// required String from,
|
||||||
|
// required String to,
|
||||||
|
// required bool shouldNotifyListeners,
|
||||||
|
// }) async {
|
||||||
|
// if (from == to) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final walletInfo = DB.instance
|
||||||
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map;
|
||||||
|
//
|
||||||
|
// final info = walletInfo.values.firstWhere(
|
||||||
|
// (element) => element['name'] == from,
|
||||||
|
// orElse: () => <String, String>{}) as Map;
|
||||||
|
//
|
||||||
|
// if (info.isEmpty) {
|
||||||
|
// // tried to rename a non existing wallet
|
||||||
|
// Logging.instance
|
||||||
|
// .log("Tried to rename a non existing wallet!", level: LogLevel.Error);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (from != to &&
|
||||||
|
// (walletInfo.values.firstWhere((element) => element['name'] == to,
|
||||||
|
// orElse: () => <String, String>{}) as Map)
|
||||||
|
// .isNotEmpty) {
|
||||||
|
// // name already exists
|
||||||
|
// Logging.instance.log("wallet with name \"$to\" already exists!",
|
||||||
|
// level: LogLevel.Error);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// info["name"] = to;
|
||||||
|
// walletInfo[info['id']] = info;
|
||||||
|
//
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: walletInfo);
|
||||||
|
// await refreshWallets(shouldNotifyListeners);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<Map<String, WalletInfo>> _fetchWalletNames() async {
|
||||||
|
// final names = DB.instance
|
||||||
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
||||||
|
// if (names == null) {
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "Fetched wallet 'names' returned null. Setting initializing 'names'",
|
||||||
|
// level: LogLevel.Info);
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: 'names',
|
||||||
|
// value: <String, dynamic>{});
|
||||||
|
// return {};
|
||||||
|
// }
|
||||||
|
// Logging.instance.log("Fetched wallet names: $names", level: LogLevel.Info);
|
||||||
|
// final mapped = Map<String, dynamic>.from(names);
|
||||||
|
// mapped.removeWhere((name, dyn) {
|
||||||
|
// final jsonObject = Map<String, dynamic>.from(dyn as Map);
|
||||||
|
// try {
|
||||||
|
// Coin.values.byName(jsonObject["coin"] as String);
|
||||||
|
// return false;
|
||||||
|
// } catch (e, s) {
|
||||||
|
// Logging.instance.log("Error, ${jsonObject["coin"]} does not exist",
|
||||||
|
// level: LogLevel.Error);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// return mapped.map((name, dyn) => MapEntry(
|
||||||
|
// name, WalletInfo.fromJson(Map<String, dynamic>.from(dyn as Map))));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<void> addExistingStackWallet({
|
||||||
|
// required String name,
|
||||||
|
// required String walletId,
|
||||||
|
// required Coin coin,
|
||||||
|
// required bool shouldNotifyListeners,
|
||||||
|
// }) async {
|
||||||
|
// final _names = DB.instance
|
||||||
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
||||||
|
//
|
||||||
|
// Map<String, dynamic> names;
|
||||||
|
// if (_names == null) {
|
||||||
|
// names = {};
|
||||||
|
// } else {
|
||||||
|
// names = Map<String, dynamic>.from(_names);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (names.keys.contains(walletId)) {
|
||||||
|
// throw Exception("Wallet with walletId \"$walletId\" already exists!");
|
||||||
|
// }
|
||||||
|
// if (names.values.where((element) => element['name'] == name).isNotEmpty) {
|
||||||
|
// throw Exception("Wallet with name \"$name\" already exists!");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// names[walletId] = {
|
||||||
|
// "id": walletId,
|
||||||
|
// "coin": coin.name,
|
||||||
|
// "name": name,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${walletId}_cryptoCurrency",
|
||||||
|
// value: coin.name);
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${walletId}_mnemonicHasBeenVerified",
|
||||||
|
// value: false);
|
||||||
|
// await DB.instance.addWalletBox(walletId: walletId);
|
||||||
|
// await refreshWallets(shouldNotifyListeners);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// returns the new walletId if successful, otherwise null
|
||||||
|
// Future<String?> addNewWallet({
|
||||||
|
// required String name,
|
||||||
|
// required Coin coin,
|
||||||
|
// required bool shouldNotifyListeners,
|
||||||
|
// }) async {
|
||||||
|
// final _names = DB.instance
|
||||||
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
||||||
|
//
|
||||||
|
// Map<String, dynamic> names;
|
||||||
|
// if (_names == null) {
|
||||||
|
// names = {};
|
||||||
|
// } else {
|
||||||
|
// names = Map<String, dynamic>.from(_names);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Prevent overwriting or storing empty names
|
||||||
|
// if (name.isEmpty ||
|
||||||
|
// names.values.where((element) => element['name'] == name).isNotEmpty) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final id = const Uuid().v1();
|
||||||
|
// names[id] = {
|
||||||
|
// "id": id,
|
||||||
|
// "coin": coin.name,
|
||||||
|
// "name": name,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${id}_cryptoCurrency",
|
||||||
|
// value: coin.name);
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${id}_mnemonicHasBeenVerified",
|
||||||
|
// value: false);
|
||||||
|
// await DB.instance.addWalletBox(walletId: id);
|
||||||
|
// await refreshWallets(shouldNotifyListeners);
|
||||||
|
// return id;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<List<String>> getFavoriteWalletIds() async {
|
||||||
|
// return DB.instance
|
||||||
|
// .values<String>(boxName: DB.boxNameFavoriteWallets)
|
||||||
|
// .toList();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Future<void> saveFavoriteWalletIds(List<String> walletIds) async {
|
||||||
|
// await DB.instance.deleteAll<String>(boxName: DB.boxNameFavoriteWallets);
|
||||||
|
// await DB.instance
|
||||||
|
// .addAll(boxName: DB.boxNameFavoriteWallets, values: walletIds);
|
||||||
|
// debugPrint("saveFavoriteWalletIds list: $walletIds");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> addFavorite(String walletId) async {
|
||||||
|
// final list = await getFavoriteWalletIds();
|
||||||
|
// if (!list.contains(walletId)) {
|
||||||
|
// list.add(walletId);
|
||||||
|
// }
|
||||||
|
// await saveFavoriteWalletIds(list);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> removeFavorite(String walletId) async {
|
||||||
|
// final list = await getFavoriteWalletIds();
|
||||||
|
// list.remove(walletId);
|
||||||
|
// await saveFavoriteWalletIds(list);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> moveFavorite({
|
||||||
|
// required int fromIndex,
|
||||||
|
// required int toIndex,
|
||||||
|
// }) async {
|
||||||
|
// final list = await getFavoriteWalletIds();
|
||||||
|
// if (fromIndex < toIndex) {
|
||||||
|
// toIndex -= 1;
|
||||||
|
// }
|
||||||
|
// final walletId = list.removeAt(fromIndex);
|
||||||
|
// list.insert(toIndex, walletId);
|
||||||
|
// await saveFavoriteWalletIds(list);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<bool> checkForDuplicate(String name) async {
|
||||||
|
// final names = DB.instance
|
||||||
|
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
|
||||||
|
// if (names == null) return false;
|
||||||
|
//
|
||||||
|
// return names.values.where((element) => element['name'] == name).isNotEmpty;
|
||||||
|
// }
|
||||||
|
|
||||||
|
Future<String?> getWalletId(String walletName) async {
|
||||||
|
final names = DB.instance
|
||||||
|
.get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map;
|
||||||
|
final shells =
|
||||||
|
names.values.where((element) => element['name'] == walletName);
|
||||||
|
if (shells.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return shells.first["id"] as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<bool> isMnemonicVerified({required String walletId}) async {
|
||||||
|
// final isVerified = DB.instance.get<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${walletId}_mnemonicHasBeenVerified") as bool?;
|
||||||
|
//
|
||||||
|
// if (isVerified == null) {
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "isMnemonicVerified(walletId: $walletId) returned null which should never happen!",
|
||||||
|
// level: LogLevel.Error,
|
||||||
|
// );
|
||||||
|
// throw Exception(
|
||||||
|
// "isMnemonicVerified(walletId: $walletId) returned null which should never happen!");
|
||||||
|
// } else {
|
||||||
|
// return isVerified;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> setMnemonicVerified({required String walletId}) async {
|
||||||
|
// final isVerified = DB.instance.get<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${walletId}_mnemonicHasBeenVerified") as bool?;
|
||||||
|
//
|
||||||
|
// if (isVerified == null) {
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "setMnemonicVerified(walletId: $walletId) tried running on non existent wallet!",
|
||||||
|
// level: LogLevel.Error,
|
||||||
|
// );
|
||||||
|
// throw Exception(
|
||||||
|
// "setMnemonicVerified(walletId: $walletId) tried running on non existent wallet!");
|
||||||
|
// } else if (isVerified) {
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "setMnemonicVerified(walletId: $walletId) tried running on already verified wallet!",
|
||||||
|
// level: LogLevel.Error,
|
||||||
|
// );
|
||||||
|
// throw Exception(
|
||||||
|
// "setMnemonicVerified(walletId: $walletId) tried running on already verified wallet!");
|
||||||
|
// } else {
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${walletId}_mnemonicHasBeenVerified",
|
||||||
|
// value: true);
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "setMnemonicVerified(walletId: $walletId) successful",
|
||||||
|
// level: LogLevel.Error,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // pin + mnemonic as well as anything else in secureStore
|
||||||
|
// Future<int> deleteWallet(String name, bool shouldNotifyListeners) async {
|
||||||
|
// final names = DB.instance.get<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData, key: 'names') as Map? ??
|
||||||
|
// {};
|
||||||
|
//
|
||||||
|
// final walletId = await getWalletId(name);
|
||||||
|
// if (walletId == null) {
|
||||||
|
// return 3;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "deleteWallet called with name=$name and id=$walletId",
|
||||||
|
// level: LogLevel.Warning,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// final shell = names.remove(walletId);
|
||||||
|
//
|
||||||
|
// if (shell == null) {
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO delete derivations!!!
|
||||||
|
// await _secureStore.delete(key: "${walletId}_pin");
|
||||||
|
// await _secureStore.delete(key: "${walletId}_mnemonic");
|
||||||
|
//
|
||||||
|
// await DB.instance.delete<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData, key: "${walletId}_cryptoCurrency");
|
||||||
|
// await DB.instance.delete<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData,
|
||||||
|
// key: "${walletId}_mnemonicHasBeenVerified");
|
||||||
|
// if (coinFromPrettyName(shell['coin'] as String) == Coin.wownero) {
|
||||||
|
// final wowService =
|
||||||
|
// wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
|
||||||
|
// await wowService.remove(walletId);
|
||||||
|
// Logging.instance
|
||||||
|
// .log("monero wallet: $walletId deleted", level: LogLevel.Info);
|
||||||
|
// } else if (coinFromPrettyName(shell['coin'] as String) == Coin.monero) {
|
||||||
|
// final xmrService =
|
||||||
|
// monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
|
||||||
|
// await xmrService.remove(walletId);
|
||||||
|
// Logging.instance
|
||||||
|
// .log("monero wallet: $walletId deleted", level: LogLevel.Info);
|
||||||
|
// } else if (coinFromPrettyName(shell['coin'] as String) == Coin.epicCash) {
|
||||||
|
// final deleteResult =
|
||||||
|
// await deleteEpicWallet(walletId: walletId, secureStore: _secureStore);
|
||||||
|
// Logging.instance.log(
|
||||||
|
// "epic wallet: $walletId deleted with result: $deleteResult",
|
||||||
|
// level: LogLevel.Info);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // box data may currently still be read/written to if wallet was refreshing
|
||||||
|
// // when delete was requested so instead of deleting now we mark the wallet
|
||||||
|
// // as needs delete by adding it's id to a list which gets checked on app start
|
||||||
|
// await DB.instance.add<String>(
|
||||||
|
// boxName: DB.boxNameWalletsToDeleteOnStart, value: walletId);
|
||||||
|
//
|
||||||
|
// final lookupService = TradeSentFromStackService();
|
||||||
|
// for (final lookup in lookupService.all) {
|
||||||
|
// if (lookup.walletIds.contains(walletId)) {
|
||||||
|
// // update lookup data to reflect deleted wallet
|
||||||
|
// await lookupService.save(
|
||||||
|
// tradeWalletLookup: lookup.copyWith(
|
||||||
|
// walletIds: lookup.walletIds.where((id) => id != walletId).toList(),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // delete notifications tied to deleted wallet
|
||||||
|
// for (final notification in NotificationsService.instance.notifications) {
|
||||||
|
// if (notification.walletId == walletId) {
|
||||||
|
// await NotificationsService.instance.delete(notification, false);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (names.isEmpty) {
|
||||||
|
// await DB.instance.deleteAll<dynamic>(boxName: DB.boxNameAllWalletsData);
|
||||||
|
// _walletNames = Future(() => {});
|
||||||
|
// notifyListeners();
|
||||||
|
// return 2; // error code no wallets on device
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// await DB.instance.put<dynamic>(
|
||||||
|
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
|
||||||
|
// await refreshWallets(shouldNotifyListeners);
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> refreshWallets(bool shouldNotifyListeners) async {
|
||||||
|
// final newNames = await _fetchWalletNames();
|
||||||
|
// _walletNames = Future(() => newNames);
|
||||||
|
// if (shouldNotifyListeners) notifyListeners();
|
||||||
|
// }
|
||||||
|
}
|
Loading…
Reference in a new issue