What changed, and why it matters
This commit refines how the Stack Wallet mobile app handles Solana (SPL) custom tokens. It separates default tokens from user-added custom tokens, passes the wallet ID when adding a custom token, and adds unique keys to token list items. There is no clear security vulnerability in the diff itself; it appears to be a feature/bug-fix improvement for token management.
No immediate security action required. Review the custom-token addition flow end-to-end to ensure user-supplied token addresses are validated before balance queries or transactions, but that is outside the scope of this diff.
Security signals we found
No input validation changes affecting security
No cryptographic operations modified
No network or IPC changes
No privilege or authorization changes
Data-flow change: walletId passed to custom token view
Data-flow change: default and custom SPL token mints stored separately
Evidence from the diff
The change updates edit_wallet_tokens_view.dart to distinguish default SPL token mints from custom ones, storing each in separate database fields rather than mixing them. It also passes widget.walletId as an argument to AddCustomSolanaTokenView, and updates route_generator.dart to accept and forward that wallet ID. Additionally, add_token_list.dart now assigns a Flutter Key based on token address to list elements. No cryptographic, input-validation, or authorization flaws are visible in the diff.
Changed components
lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dartlib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dartlib/route_generator.dartInspect captured patch +41 / −21
diff --git a/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart b/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart
index 6197a33..2d4f869 100644
--- a/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart
+++ b/lib/pages/add_wallet_views/add_token_view/edit_wallet_tokens_view.dart
@@ -118,27 +118,26 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
// Get WalletInfo and update Solana token mint addresses.
final walletInfo = wallet.info;
- // Combine default tokens with custom tokens.
- final allSelectedTokens = selectedTokens.toSet();
- // Add any existing custom tokens that should be preserved.
- allSelectedTokens.addAll(walletInfo.solanaCustomTokenMintAddresses);
+ // Separate selected tokens into default and custom.
+ final defaultTokenMints = DefaultSplTokens.list.map((e) => e.address).toSet();
+ final selectedDefaultTokens = selectedTokens.where(
+ (mint) => defaultTokenMints.contains(mint),
+ ).toSet();
+ final selectedCustomTokens = selectedTokens.where(
+ (mint) => !defaultTokenMints.contains(mint),
+ ).toSet();
+ // Update default token mint addresses.
await walletInfo.updateSolanaTokenMintAddresses(
- newMintAddresses: selectedTokens.toSet(),
+ newMintAddresses: selectedDefaultTokens,
isar: MainDB.instance.isar,
);
- // Update custom tokens if any.
- final customTokens = allSelectedTokens.where(
- (mint) => !selectedTokens.contains(mint),
- ).toSet();
-
- if (customTokens.isNotEmpty) {
- await walletInfo.updateSolanaCustomTokenMintAddresses(
- newMintAddresses: customTokens,
- isar: MainDB.instance.isar,
- );
- }
+ // Update custom token mint addresses.
+ await walletInfo.updateSolanaCustomTokenMintAddresses(
+ newMintAddresses: selectedCustomTokens,
+ isar: MainDB.instance.isar,
+ );
// Log selected tokens and verify ownership.
debugPrint('===== SOLANA TOKEN OWNERSHIP CHECK =====');
@@ -304,7 +303,10 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
} else {
final result = await Navigator.of(
context,
- ).pushNamed(AddCustomSolanaTokenView.routeName);
+ ).pushNamed(
+ AddCustomSolanaTokenView.routeName,
+ arguments: widget.walletId,
+ );
token = result as SplToken?;
}
@@ -347,9 +349,25 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
// Load appropriate tokens based on wallet type.
if (wallet is SolanaWallet) {
- // Load Solana tokens (SPL tokens).
- final splTokens = DefaultSplTokens.list;
- tokenEntities.addAll(splTokens.map((e) => AddTokenListElementData(e)));
+ // Load both default and custom Solana tokens.
+ final defaultSplTokens = DefaultSplTokens.list;
+ tokenEntities.addAll(defaultSplTokens.map((e) => AddTokenListElementData(e)));
+
+ // Load custom tokens from database
+ final customSplTokens = MainDB.instance.getSplTokens().findAllSync();
+
+ // Deduplicate: only add custom tokens that aren't already in defaults.
+ final seenAddresses = <String>{
+ ...defaultSplTokens.map((e) => e.address),
+ ...tokenEntities.map((e) => e.token.address),
+ };
+
+ for (final token in customSplTokens) {
+ if (!seenAddresses.contains(token.address)) {
+ tokenEntities.add(AddTokenListElementData(token));
+ seenAddresses.add(token.address);
+ }
+ }
} else {
// Load Ethereum tokens (default behavior for Ethereum wallets).
final contracts = MainDB.instance
diff --git a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart
index 23bfb36..b479a62 100644
--- a/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart
+++ b/lib/pages/add_wallet_views/add_token_view/sub_widgets/add_token_list.dart
@@ -46,6 +46,7 @@ class AddTokenList extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: AddTokenListElement(
+ key: Key(items[index].token.address),
data: items[index],
),
),
diff --git a/lib/route_generator.dart b/lib/route_generator.dart
index 2fbbe06..295035b 100644
--- a/lib/route_generator.dart
+++ b/lib/route_generator.dart
@@ -398,9 +398,10 @@ class RouteGenerator {
);
case AddCustomSolanaTokenView.routeName:
+ final walletId = args is String ? args : null;
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
- builder: (_) => const AddCustomSolanaTokenView(),
+ builder: (_) => AddCustomSolanaTokenView(walletId: walletId),
settings: RouteSettings(name: settings.name),
);
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.