feat(spl): fetch custom token metadata from db and update bal on open
What changed, and why it matters
This commit adds a fallback so that when a Solana token isn't on the built-in default list, the app looks it up in the user's own saved custom-token database. It also refreshes the token balance when the wallet view opens. There is no clear security bug in the change itself; it is a feature/fix for missing custom-token metadata and stale balances.
No immediate security action required. As a defensive measure, reviewers should verify that getSplTokenSync treats the mint argument as a lookup key only (not executable), that the returned token metadata is validated before use, and that updateBalance() does not trigger unintended network or signing operations.
Security signals we found
New database lookup path using user-controlled token mint identifier
Balance refresh triggered on wallet open
No explicit input sanitization shown in diff for db lookup
Evidence from the diff
The patch modifies SolTokenView and DesktopSolTokenView. _initializeSolanaTokenWallet() now first searches DefaultSplTokens.list, and if the mint is not found, calls db.getSplTokenSync(widget.tokenMint) via mainDBProvider. It also calls solanaTokenWallet.updateBalance() after setting the provider state. No input validation, SQL injection, or network handling changes are visible in the diff. The database lookup uses the token mint as a key, which is expected to be a public identifier.
Changed components
lib/pages/token_view/sol_token_view.dartlib/pages_desktop_specific/my_stack_view/wallet_view/desktop_sol_token_view.dartSolana SPL token wallet initializationCustom SPL token database lookupInspect captured patch +46 / −15
diff --git a/lib/pages/token_view/sol_token_view.dart b/lib/pages/token_view/sol_token_view.dart
index d206d23..7efe2dc 100644
--- a/lib/pages/token_view/sol_token_view.dart
+++ b/lib/pages/token_view/sol_token_view.dart
@@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
+import '../../providers/db/main_db_provider.dart';
import '../../providers/providers.dart';
import '../../services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import '../../themes/stack_colors.dart';
@@ -62,6 +63,7 @@ class _SolTokenViewState extends ConsumerState<SolTokenView> {
: WalletSyncStatus.synced;
// Initialize the Solana token wallet provider with mock data.
+ //
// This sets up the pCurrentSolanaTokenWallet provider so that
// SolanaTokenSummary can access the token wallet information.
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -74,29 +76,40 @@ class _SolTokenViewState extends ConsumerState<SolTokenView> {
}
/// Initialize the Solana token wallet for this token view.
- ///
- /// Creates a SolanaTokenWallet with token data from DefaultSplTokens
- /// and sets it as the current token wallet in the provider so that UI widgets can access it.
- ///
- /// If the token is not found in DefaultSplTokens, sets the token wallet to null
+ ///
+ /// Creates a SolanaTokenWallet with token data from DefaultSplTokens or the database.
+ /// First looks in DefaultSplTokens, then checks the database for custom tokens.
+ /// Sets it as the current token wallet in the provider so that UI widgets can access it.
+ ///
+ /// If the token is not found anywhere, sets the token wallet to null
/// so the UI can display an error message.
- ///
- /// TODO: Implement token data lookup for tokens not on the default list.
void _initializeSolanaTokenWallet() {
dynamic tokenInfo;
+
+ // First try to find in default tokens.
try {
tokenInfo = DefaultSplTokens.list.firstWhere(
(token) => token.address == widget.tokenMint,
);
} catch (e) {
- // Token not found in DefaultSplTokens.
+ // Token not found in DefaultSplTokens, try database for custom tokens.
tokenInfo = null;
}
+ // If not found in defaults, try database for custom tokens.
+ if (tokenInfo == null) {
+ try {
+ final db = ref.read(mainDBProvider);
+ tokenInfo = db.getSplTokenSync(widget.tokenMint);
+ } catch (e) {
+ tokenInfo = null;
+ }
+ }
+
if (tokenInfo == null) {
ref.read(solanaTokenServiceStateProvider.state).state = null;
debugPrint(
- 'ERROR: Token not found in DefaultSplTokens: ${widget.tokenMint}',
+ 'ERROR: Token not found in DefaultSplTokens or database: ${widget.tokenMint}',
);
return;
}
@@ -121,6 +134,9 @@ class _SolTokenViewState extends ConsumerState<SolTokenView> {
);
ref.read(solanaTokenServiceStateProvider.state).state = solanaTokenWallet;
+
+ // Fetch the token balance when the wallet is opened.
+ solanaTokenWallet.updateBalance();
}
@override
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_sol_token_view.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_sol_token_view.dart
index 65aa72b..f17c37a 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_sol_token_view.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/desktop_sol_token_view.dart
@@ -14,6 +14,7 @@ import 'package:flutter_svg/svg.dart';
import '../../../pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import '../../../pages/token_view/sub_widgets/token_transaction_list_widget_sol.dart';
+import '../../../providers/db/main_db_provider.dart';
import '../../../providers/providers.dart';
import '../../../services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import '../../../themes/stack_colors.dart';
@@ -75,27 +76,38 @@ class _DesktopTokenViewState extends ConsumerState<DesktopSolTokenView> {
/// Initialize the Solana token wallet.
///
- /// Creates a SolanaTokenWallet with token data from DefaultSplTokens
- /// and sets it as the current token wallet in the provider so that UI widgets can access it.
+ /// Creates a SolanaTokenWallet with token data from DefaultSplTokens or the database.
+ /// First looks in DefaultSplTokens, then checks the database for custom tokens.
+ /// Sets it as the current token wallet in the provider so that UI widgets can access it.
///
- /// If the token is not found in DefaultSplTokens, sets the token wallet to null
+ /// If the token is not found anywhere, sets the token wallet to null
/// so the UI can display an error message.
void _initializeSolanaTokenWallet() {
- // Look up the actual token from DefaultSplTokens.
+ // First try to find in default tokens
dynamic tokenInfo;
try {
tokenInfo = DefaultSplTokens.list.firstWhere(
(token) => token.address == widget.tokenMint,
);
} catch (e) {
- // Token not found in DefaultSplTokens.
+ // Token not found in DefaultSplTokens, try database for custom tokens
tokenInfo = null;
}
+ // If not found in defaults, try database for custom tokens
+ if (tokenInfo == null) {
+ try {
+ final db = ref.read(mainDBProvider);
+ tokenInfo = db.getSplTokenSync(widget.tokenMint);
+ } catch (e) {
+ tokenInfo = null;
+ }
+ }
+
if (tokenInfo == null) {
ref.read(solanaTokenServiceStateProvider.state).state = null;
debugPrint(
- 'ERROR: Token not found in DefaultSplTokens: ${widget.tokenMint}',
+ 'ERROR: Token not found in DefaultSplTokens or database: ${widget.tokenMint}',
);
return;
}
@@ -120,6 +132,9 @@ class _DesktopTokenViewState extends ConsumerState<DesktopSolTokenView> {
);
ref.read(solanaTokenServiceStateProvider.state).state = solanaTokenWallet;
+
+ // Fetch the token balance when the wallet is opened
+ solanaTokenWallet.updateBalance();
}
@override
Why this scored 19/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.