What changed, and why it matters
This commit fixes a race condition in the Solana token transaction list widget. Previously, the widget tried to set up a database query and stream listener during initialization, before the wallet data was guaranteed to be ready. This could lead to crashes or a broken transaction list. The fix delays query setup until the wallet is available, and safely handles cases where the listener hasn't been created yet.
No immediate security action required. This is a stability and correctness fix. Reviewers may want to verify that `_initializeQuery()` is idempotent and that no other widgets share the same pattern.
Security signals we found
Race condition in UI state initialization
Potential null/late-initialization crash in transaction list widget
Stream subscription lifecycle management improved
Evidence from the diff
The patch changes _subscription and _query from late final non-nullable fields to nullable fields. It moves query initialization out of initState() into a new _initializeQuery() method, which is called during build() once the wallet is available. It also adds a null-aware ?.cancel() in dispose() and shows a loading indicator if the query isn’t initialized. This prevents LateInitializationError and avoids subscribing to an invalid or incomplete query state.
Changed components
lib/pages/token_view/sub_widgets/token_transaction_list_widget_sol.dartInspect captured patch +29 / −6
diff --git a/lib/pages/token_view/sub_widgets/token_transaction_list_widget_sol.dart b/lib/pages/token_view/sub_widgets/token_transaction_list_widget_sol.dart
index d3640e8..ffbfb2e 100644
--- a/lib/pages/token_view/sub_widgets/token_transaction_list_widget_sol.dart
+++ b/lib/pages/token_view/sub_widgets/token_transaction_list_widget_sol.dart
@@ -45,8 +45,8 @@ class _SolanaTransactionsListState extends ConsumerState<SolanaTokenTransactions
bool _hasLoaded = false;
List<TransactionV2> _transactions = [];
- late final StreamSubscription<List<TransactionV2>> _subscription;
- late final Query<TransactionV2> _query;
+ StreamSubscription<List<TransactionV2>>? _subscription;
+ Query<TransactionV2>? _query;
BorderRadius get _borderRadiusFirst {
return BorderRadius.only(
@@ -77,6 +77,14 @@ class _SolanaTransactionsListState extends ConsumerState<SolanaTokenTransactions
.getWallet(widget.walletId)
.cryptoCurrency
.minConfirms;
+ super.initState();
+ }
+
+ /// Initialize the query and subscription when the wallet becomes available.
+ void _initializeQuery() {
+ if (_query != null) {
+ return; // Already initialized.
+ }
// Get transaction filter from Solana token wallet if available.
final solanaTokenWallet = ref.read(pCurrentSolanaTokenWallet);
@@ -102,7 +110,7 @@ class _SolanaTransactionsListState extends ConsumerState<SolanaTokenTransactions
],
);
- _subscription = _query.watch().listen((event) {
+ _subscription = _query!.watch().listen((event) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
@@ -111,12 +119,11 @@ class _SolanaTransactionsListState extends ConsumerState<SolanaTokenTransactions
}
});
});
- super.initState();
}
@override
void dispose() {
- _subscription.cancel();
+ _subscription?.cancel();
super.dispose();
}
@@ -125,8 +132,24 @@ class _SolanaTransactionsListState extends ConsumerState<SolanaTokenTransactions
final wallet =
ref.watch(pWallets.select((value) => value.getWallet(widget.walletId)));
+ // Ensure query is initialized when wallet becomes available.
+ _initializeQuery();
+
+ // If query hasn't been initialized yet, show loading.
+ if (_query == null) {
+ return Center(
+ child: Container(
+ color: Theme.of(context).extension<StackColors>()!.background,
+ child: const LoadingIndicator(
+ width: 100,
+ height: 100,
+ ),
+ ),
+ );
+ }
+
return FutureBuilder(
- future: _query.findAll(),
+ future: _query!.findAll(),
builder: (fbContext, AsyncSnapshot<List<TransactionV2>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
Why this scored 23/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.