Skip to content

Commit 6e34517

Browse files
committed
feat(spl): add sol tokens to add wallet list
1 parent 44fed9a commit 6e34517

File tree

6 files changed

+414
-3
lines changed

6 files changed

+414
-3
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* This file is part of Stack Wallet.
3+
*
4+
* Copyright (c) 2023 Cypher Stack
5+
* All Rights Reserved.
6+
* The code is distributed under GPLv3 license, see LICENSE file for details.
7+
*
8+
*/
9+
10+
import '../add_wallet_list_entity.dart';
11+
import '../../isar/models/solana/spl_token.dart';
12+
import '../../../wallets/crypto_currency/crypto_currency.dart';
13+
14+
class SolTokenEntity extends AddWalletListEntity {
15+
SolTokenEntity(this.token);
16+
17+
final SplToken token;
18+
19+
@override
20+
CryptoCurrency get cryptoCurrency => Solana(CryptoCurrencyNetwork.main);
21+
22+
@override
23+
String get name => token.name;
24+
25+
@override
26+
String get ticker => token.symbol;
27+
28+
@override
29+
List<Object?> get props =>
30+
[cryptoCurrency.identifier, name, ticker, token.address];
31+
}

lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@ import '../../../db/isar/main_db.dart';
2020
import '../../../models/add_wallet_list_entity/add_wallet_list_entity.dart';
2121
import '../../../models/add_wallet_list_entity/sub_classes/coin_entity.dart';
2222
import '../../../models/add_wallet_list_entity/sub_classes/eth_token_entity.dart';
23+
import '../../../models/add_wallet_list_entity/sub_classes/sol_token_entity.dart';
2324
import '../../../models/isar/models/ethereum/eth_contract.dart';
25+
import '../../../models/isar/models/solana/spl_token.dart';
2426
import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
2527
import '../../../providers/providers.dart';
2628
import '../../../themes/stack_colors.dart';
2729
import '../../../utilities/assets.dart';
2830
import '../../../utilities/constants.dart';
2931
import '../../../utilities/default_eth_tokens.dart';
32+
import '../../../utilities/default_spl_tokens.dart';
3033
import '../../../utilities/text_styles.dart';
3134
import '../../../utilities/util.dart';
3235
import '../../../wallets/crypto_currency/crypto_currency.dart';
@@ -41,6 +44,7 @@ import '../../../widgets/rounded_white_container.dart';
4144
import '../../../widgets/stack_text_field.dart';
4245
import '../../../widgets/textfield_icon_button.dart';
4346
import '../add_token_view/add_custom_token_view.dart';
47+
import '../add_token_view/add_custom_solana_token_view.dart';
4448
import '../add_token_view/sub_widgets/add_custom_token_selector.dart';
4549
import 'sub_widgets/add_wallet_text.dart';
4650
import 'sub_widgets/expanding_sub_list_item.dart';
@@ -68,6 +72,7 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
6872
final List<AddWalletListEntity> coinEntities = [];
6973
final List<AddWalletListEntity> coinTestnetEntities = [];
7074
final List<EthTokenEntity> tokenEntities = [];
75+
final List<SolTokenEntity> solTokenEntities = [];
7176

7277
final bool isDesktop = Util.isDesktop;
7378

@@ -84,6 +89,8 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
8489
e.name.toLowerCase().contains(lowercaseTerm) ||
8590
e.cryptoCurrency.identifier.toLowerCase().contains(lowercaseTerm) ||
8691
(e is EthTokenEntity &&
92+
e.token.address.toLowerCase().contains(lowercaseTerm)) ||
93+
(e is SolTokenEntity &&
8794
e.token.address.toLowerCase().contains(lowercaseTerm)),
8895
);
8996
}
@@ -125,6 +132,38 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
125132
}
126133
}
127134

135+
Future<void> _addSolToken() async {
136+
SplToken? token;
137+
if (isDesktop) {
138+
token = await showDialog(
139+
context: context,
140+
builder:
141+
(context) => const DesktopDialog(
142+
maxWidth: 580,
143+
maxHeight: 500,
144+
child: AddCustomSolanaTokenView(),
145+
),
146+
);
147+
} else {
148+
token = await Navigator.of(
149+
context,
150+
).pushNamed(AddCustomSolanaTokenView.routeName);
151+
}
152+
153+
if (token != null) {
154+
await MainDB.instance.putSplToken(token);
155+
if (mounted) {
156+
setState(() {
157+
if (solTokenEntities
158+
.where((e) => e.token.address == token!.address)
159+
.isEmpty) {
160+
solTokenEntities.add(SolTokenEntity(token!));
161+
}
162+
});
163+
}
164+
}
165+
}
166+
128167
@override
129168
void initState() {
130169
_searchFieldController = TextEditingController();
@@ -153,6 +192,20 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
153192
tokenEntities.addAll(contracts.map((e) => EthTokenEntity(e)));
154193
}
155194

195+
if (AppConfig.coins.whereType<Solana>().isNotEmpty) {
196+
// Add default tokens.
197+
final defaultTokenAddresses = DefaultSplTokens.list.map((e) => e.address).toSet();
198+
solTokenEntities.addAll(DefaultSplTokens.list.map((e) => SolTokenEntity(e)));
199+
200+
// Add custom tokens from database.
201+
final allDatabaseTokens = MainDB.instance.getSplTokens().findAllSync();
202+
for (final token in allDatabaseTokens) {
203+
if (!defaultTokenAddresses.contains(token.address)) {
204+
solTokenEntities.add(SolTokenEntity(token));
205+
}
206+
}
207+
}
208+
156209
WidgetsBinding.instance.addPostFrameCallback((_) {
157210
if (mounted) {
158211
ref.refresh(addWalletSelectedEntityStateProvider);
@@ -296,14 +349,24 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
296349
),
297350
if (tokenEntities.isNotEmpty)
298351
ExpandingSubListItem(
299-
title: "Tokens",
352+
title: "Ethereum tokens",
300353
entities: filter(_searchTerm, tokenEntities),
301354
initialState: ExpandableState.expanded,
302355
animationDurationMultiplier: 0.5,
303356
trailing: AddCustomTokenSelector(
304357
addFunction: _addToken,
305358
),
306359
),
360+
if (solTokenEntities.isNotEmpty)
361+
ExpandingSubListItem(
362+
title: "Solana tokens",
363+
entities: filter(_searchTerm, solTokenEntities),
364+
initialState: ExpandableState.expanded,
365+
animationDurationMultiplier: 0.5,
366+
trailing: AddCustomTokenSelector(
367+
addFunction: _addSolToken,
368+
),
369+
),
307370
],
308371
),
309372
),
@@ -427,10 +490,16 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
427490
),
428491
if (tokenEntities.isNotEmpty)
429492
ExpandingSubListItem(
430-
title: "Tokens",
493+
title: "Ethereum tokens",
431494
entities: filter(_searchTerm, tokenEntities),
432495
initialState: ExpandableState.expanded,
433496
),
497+
if (solTokenEntities.isNotEmpty)
498+
ExpandingSubListItem(
499+
title: "Solana tokens",
500+
entities: filter(_searchTerm, solTokenEntities),
501+
initialState: ExpandableState.expanded,
502+
),
434503
],
435504
),
436505
),

lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'package:isar_community/isar.dart';
1717

1818
import '../../../../models/add_wallet_list_entity/add_wallet_list_entity.dart';
1919
import '../../../../models/add_wallet_list_entity/sub_classes/eth_token_entity.dart';
20+
import '../../../../models/add_wallet_list_entity/sub_classes/sol_token_entity.dart';
2021
import '../../../../models/isar/exchange_cache/currency.dart';
2122
import '../../../../providers/providers.dart';
2223
import '../../../../services/exchange/change_now/change_now_exchange.dart';
@@ -27,6 +28,7 @@ import '../../../../utilities/assets.dart';
2728
import '../../../../utilities/constants.dart';
2829
import '../../../../utilities/text_styles.dart';
2930
import '../../../../utilities/util.dart';
31+
import '../../../../widgets/app_icon.dart';
3032

3133
class CoinSelectItem extends ConsumerStatefulWidget {
3234
const CoinSelectItem({super.key, required this.entity});
@@ -69,6 +71,39 @@ class _CoinSelectItemState extends ConsumerState<CoinSelectItem> {
6971
});
7072
}
7173
});
74+
} else if (widget.entity is SolTokenEntity) {
75+
final solToken = (widget.entity as SolTokenEntity).token;
76+
77+
ExchangeDataLoadingService.instance.isar.then((isar) async {
78+
final currency =
79+
await isar.currencies
80+
.where()
81+
.exchangeNameEqualTo(ChangeNowExchange.exchangeName)
82+
.filter()
83+
.tokenContractEqualTo(
84+
solToken.address,
85+
caseSensitive: false,
86+
)
87+
.and()
88+
.imageIsNotEmpty()
89+
.findFirst();
90+
91+
if (mounted) {
92+
WidgetsBinding.instance.addPostFrameCallback((_) {
93+
if (mounted) {
94+
setState(() {
95+
// Use exchange cache image if available, otherwise use logoUri if it's a PNG.
96+
String? fallbackUri;
97+
if (solToken.logoUri != null &&
98+
solToken.logoUri!.endsWith('.png')) {
99+
fallbackUri = solToken.logoUri;
100+
}
101+
tokenImageUri = currency?.image ?? fallbackUri;
102+
});
103+
}
104+
});
105+
}
106+
});
72107
}
73108
}
74109

@@ -108,7 +143,12 @@ class _CoinSelectItemState extends ConsumerState<CoinSelectItem> {
108143
child: Row(
109144
children: [
110145
tokenImageUri != null
111-
? SvgPicture.network(tokenImageUri!, width: 26, height: 26)
146+
? SvgPicture.network(
147+
tokenImageUri!,
148+
width: 26,
149+
height: 26,
150+
placeholderBuilder: (_) => AppIcon(width: 26, height: 26),
151+
)
112152
: SvgPicture.file(
113153
File(
114154
ref.watch(coinIconProvider(widget.entity.cryptoCurrency)),

lib/pages/add_wallet_views/add_wallet_view/sub_widgets/next_button.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
import 'package:flutter/material.dart';
1212
import 'package:flutter_riverpod/flutter_riverpod.dart';
1313
import '../../../../models/add_wallet_list_entity/sub_classes/eth_token_entity.dart';
14+
import '../../../../models/add_wallet_list_entity/sub_classes/sol_token_entity.dart';
1415
import '../../create_or_restore_wallet_view/create_or_restore_wallet_view.dart';
1516
import '../../select_wallet_for_token_view.dart';
17+
import '../../select_wallet_for_sol_token_view.dart';
1618
import '../../../../providers/providers.dart';
1719
import '../../../../themes/stack_colors.dart';
1820
import '../../../../utilities/text_styles.dart';
@@ -42,6 +44,11 @@ class AddWalletNextButton extends ConsumerWidget {
4244
SelectWalletForTokenView.routeName,
4345
arguments: selectedCoin,
4446
);
47+
} else if (selectedCoin is SolTokenEntity) {
48+
Navigator.of(context).pushNamed(
49+
SelectWalletForSolTokenView.routeName,
50+
arguments: selectedCoin,
51+
);
4552
} else {
4653
Navigator.of(context).pushNamed(
4754
CreateOrRestoreWalletView.routeName,

0 commit comments

Comments
 (0)