Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bc54930
fix: clamp sync transaction parse batch size to prevent zero-size bat…
vilenarios Jul 3, 2026
abbc68d
fix: make wallet balance fetch best-effort during login
vilenarios Jul 3, 2026
c749c4d
perf: bound concurrent drive syncs to 5 at a time
vilenarios Jul 3, 2026
4fbdcfe
perf: reuse a single MetadataCache instance across sync batches
vilenarios Jul 3, 2026
8206d3b
fix: collect successfully imported files in bulk import result
vilenarios Jul 3, 2026
49a1da9
perf: fetch pin transaction info in batches of 100 instead of 5
vilenarios Jul 3, 2026
8218128
nit: remove unnecessary dart:async import in worker pool test
vilenarios Jul 3, 2026
a66fb87
chore: pin equatable below 2.1.0 in ardrive_ui to fix package analyze
vilenarios Jul 5, 2026
cd8ba4a
chore: point graphql at turbo-gateway.com directly, goldsky as fallback
vilenarios Jul 9, 2026
84048cf
chore: pin equatable below 2.1.0 in ardrive_ui to fix package analyze
vilenarios Jul 5, 2026
31718fb
perf: remove ArNS record fetching from the sync pipeline
vilenarios Jul 9, 2026
00634f6
perf: persist the GAR gateway list and refresh only on demand
vilenarios Jul 9, 2026
7300fef
chore: pin equatable below 2.1.0 in ardrive_ui to fix package analyze
vilenarios Jul 5, 2026
fe3d465
test: await FutureOr getString in gateway fallback tests
vilenarios Jul 9, 2026
878c2f3
Merge remote-tracking branch 'origin/fix/gql-turbo-gateway' into fix/…
vilenarios Jul 9, 2026
0215ebc
Merge remote-tracking branch 'origin/perf/minimize-solana-rpc' into f…
vilenarios Jul 9, 2026
dadcfdd
fix: time out gateway refresh instead of hanging on a stalled RPC
vilenarios Jul 9, 2026
67e12aa
perf: paginate drive history at 1000 per page with endpoint-sticky fa…
vilenarios Jul 10, 2026
8d68b87
fix: catch pagination-phase errors with await-for instead of yield*
vilenarios Jul 10, 2026
5b0389f
fix: harden sync pagination and address adversarial review findings
vilenarios Jul 10, 2026
6b714e4
nit: const GraphQLResponse in strategy test
vilenarios Jul 10, 2026
f689a21
test: no-version config migration also preserves custom gateways
vilenarios Jul 10, 2026
8b8f137
perf: skip verification pages for tiny tails, raise drive concurrency…
vilenarios Jul 10, 2026
9511d13
perf: default drive-history page size back to 100
vilenarios Jul 15, 2026
5e45fad
fix: restore full retry budget at safe page size, sync-scoped owner memo
vilenarios Jul 15, 2026
5f6a3d1
revert: remove the drive-history pagination experiment
vilenarios Jul 15, 2026
c2fa83e
perf: raise default concurrent drive syncs to 50
vilenarios Jul 15, 2026
dca6be8
Merge remote-tracking branch 'origin/dev' into fix/sync-quick-wins
Jul 29, 2026
916216e
perf: size the tx-parse batch by actual concurrency, not total drives…
Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions assets/config/dev.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"configVersion": 2,
"defaultArweaveGatewayUrl": "https://ardrive.net",
"configVersion": 3,
"defaultArweaveGatewayUrl": "https://turbo-gateway.com",
"defaultArweaveGatewayForDataRequest": {
"label": "Turbo Gateway",
"url": "https://turbo-gateway.com"
Expand All @@ -12,5 +12,7 @@
"allowedDataItemSizeForTurbo": 100000,
"stripePublishableKey": "pk_test_51JUAtwC8apPOWkDLh2FPZkQkiKZEkTo6wqgLCtQoClL6S4l2jlbbc5MgOdwOUdU9Tn93NNvqAGbu115lkJChMikG00XUfTmo2z",
"uploadThumbnails": true,
"autoSync": false
"autoSync": false,
"maxConcurrentDriveSyncs": 5,
"driveHistoryGqlPageSize": 1000
}
8 changes: 5 additions & 3 deletions assets/config/prod.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"configVersion": 2,
"defaultArweaveGatewayUrl": "https://ardrive.net",
"configVersion": 3,
"defaultArweaveGatewayUrl": "https://turbo-gateway.com",
"defaultArweaveGatewayForDataRequest": {
"label": "Turbo Gateway",
"url": "https://turbo-gateway.com"
Expand All @@ -12,5 +12,7 @@
"allowedDataItemSizeForTurbo": 100000,
"stripePublishableKey": "pk_live_51JUAtwC8apPOWkDLMQqNF9sPpfneNSPnwX8YZ8y1FNDl6v94hZIwzgFSYl27bWE4Oos8CLquunUswKrKcaDhDO6m002Yj9AeKj",
"uploadThumbnails": true,
"autoSync": false
"autoSync": false,
"maxConcurrentDriveSyncs": 5,
"driveHistoryGqlPageSize": 1000
}
8 changes: 5 additions & 3 deletions assets/config/staging.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"configVersion": 2,
"defaultArweaveGatewayUrl": "https://ardrive.net",
"configVersion": 3,
"defaultArweaveGatewayUrl": "https://turbo-gateway.com",
"defaultArweaveGatewayForDataRequest": {
"label": "Turbo Gateway",
"url": "https://turbo-gateway.com"
Expand All @@ -12,5 +12,7 @@
"allowedDataItemSizeForTurbo": 100000,
"stripePublishableKey": "pk_live_51JUAtwC8apPOWkDLMQqNF9sPpfneNSPnwX8YZ8y1FNDl6v94hZIwzgFSYl27bWE4Oos8CLquunUswKrKcaDhDO6m002Yj9AeKj",
"uploadThumbnails": true,
"autoSync": false
"autoSync": false,
"maxConcurrentDriveSyncs": 5,
"driveHistoryGqlPageSize": 1000
}
12 changes: 11 additions & 1 deletion lib/authentication/ardrive_auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,17 @@ class ArDriveAuthImpl implements ArDriveAuth {

@override
Future<void> refreshBalance() async {
_updateBalance();
// Await the fetch so callers (e.g. ProfileCubit.refreshBalance, which
// re-emits currentUser right after) observe the refreshed value instead
// of the stale one.
try {
final balance = await _userRepository.getBalance(currentUser.wallet);
_currentUser = _currentUser!.copyWith(walletBalance: balance);
_userStreamController.add(_currentUser);
} catch (e) {
logger.e('Error refreshing wallet balance', e);
// Keep the previous value on error.
}
}
}

Expand Down
22 changes: 20 additions & 2 deletions lib/core/arfs/use_cases/bulk_import_files.dart
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,22 @@ class BulkImportFiles {
? 1
: 5,
taskQueue: fileEntries,
onWorkerError: (e) {
logger.e('Bulk import worker error', e, StackTrace.current);
onWorkerError: (file) {
// WorkerPool passes the failed TASK (the FileEntity), not the
// exception. Record it so BulkImportResult stays truthful:
// imported + failures should account for every file.
logger.e(
'Bulk import worker failed for file: ${file.name}', null,
StackTrace.current);
final manifestFile =
file.dataTxId != null ? fileDataTxIdToFile[file.dataTxId] : null;
final path = manifestFile?.path ?? file.name ?? 'unknown';
failures.add(FileImportFailure(
path: path,
dataTxId: file.dataTxId ?? '',
error: 'Import failed during metadata upload',
));
onFileFailure?.call(path);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
},
execute: (file) async {
if (_isCancelled) {
Expand All @@ -442,6 +456,10 @@ class BulkImportFiles {
originalOwnerAddress: originalOwnerAddress,
);

// WorkerPool discards execute's return value, so successful
// imports must be collected here for BulkImportResult.
importedFiles.add(fileEntry);

onFileUploadSuccess?.call(file.name!);

return fileEntry;
Expand Down
14 changes: 13 additions & 1 deletion lib/gar/domain/repositories/gar_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:collection/collection.dart';

abstract class GarRepository {
Future<List<Gateway>> getGateways();
Future<List<Gateway>> refreshGateways();
List<Gateway> searchGateways(String query);
Future<Gateway> getSelectedGateway();
Future<void> updateGateway(Gateway gateway);
Expand Down Expand Up @@ -35,10 +36,21 @@ class GarRepositoryImpl implements GarRepository {

final List<Gateway> _gateways = [];

/// Serves the persisted/session gateway list; the network is hit at most
/// once ever (on the very first use). Use [refreshGateways] for an
/// explicit, user-initiated re-fetch.
@override
Future<List<Gateway>> getGateways() async {
_gateways.clear();
_gateways.addAll(await arioSDK.getGateways());
_gateways.addAll(await arweave.gatewayFallback.getGatewaysCached());

return _gateways;
}

@override
Future<List<Gateway>> refreshGateways() async {
_gateways.clear();
_gateways.addAll(await arweave.gatewayFallback.refreshGateways());

return _gateways;
}
Expand Down
19 changes: 19 additions & 0 deletions lib/gar/presentation/bloc/gar_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ class GarBloc extends Bloc<GarEvent, GarState> {
}
});

on<RefreshGateways>((event, emit) async {
try {
emit(LoadingGateways());

final gateways = await garRepository.refreshGateways();
final currentGateway = await garRepository.getSelectedGateway();

emit(
GatewaysLoaded(
gateways: gateways,
currentGateway: currentGateway,
),
);
} catch (e) {
logger.e('Failed to refresh gateways from AR.IO', e);
emit(const GatewaysError());
}
});

on<SelectGateway>((event, emit) async {
emit(VerifyingGateway());

Expand Down
3 changes: 3 additions & 0 deletions lib/gar/presentation/bloc/gar_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ abstract class GarEvent extends Equatable {

final class GetGateways extends GarEvent {}

/// User-initiated refresh of the gateway list from the network.
final class RefreshGateways extends GarEvent {}

final class SelectGateway extends GarEvent {
final Gateway gateway;

Expand Down
6 changes: 6 additions & 0 deletions lib/gar/presentation/widgets/gar_modal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ class _ArIOGatewaySelectorModalContentState extends State<_ArIOGatewaySelectorMo
),
),
actions: [
ModalAction(
action: () {
garBloc.add(RefreshGateways());
},
title: 'Refresh list',
),
ModalAction(
action: () {
Navigator.of(context).pop();
Expand Down
1 change: 0 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,6 @@ class AppState extends State<App> {
configService: configService,
arioSDK: ArioSDKFactory().create(),
),
arnsRepository: _.read<ARNSRepository>(),
userPreferencesRepository: _.read<UserPreferencesRepository>(),
),
),
Expand Down
32 changes: 28 additions & 4 deletions lib/services/arweave/arweave_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ class ArweaveService {
/// Data requests (GET /tx/{id}/data, wallet balance, etc.) continue using
/// the configured arweaveGatewayForDataRequest.
void updateGraphQLEndpoint(String gatewayUrl) {
// The old endpoint's capabilities no longer apply.
_gqlOwnersPreferringFallback.clear();
final previousClient = _gql;
final graphqlUrl = _graphqlUrlFromGateway(gatewayUrl);
_gql = ArtemisClient(graphqlUrl);
Expand Down Expand Up @@ -152,6 +154,18 @@ class ArweaveService {
/// Cache for drive signatures (immutable on-chain, never change).
final Map<String, DriveSignatureEntity?> _cachedDriveSignatures = {};

/// Single [MetadataCache] instance reused across all sync batches.
/// Previously a new cache was rebuilt from SharedPreferences for every
/// parsed batch of every drive during sync.
MetadataCache? _metadataCache;

Future<MetadataCache> _getMetadataCache() async {
_metadataCache ??= await MetadataCache.fromCacheStore(
await newSharedPreferencesCacheStore(),
);
return _metadataCache!;
}

/// Clears the cached result of [getUniqueUserDriveEntityTxs] and entity data.
/// Call after creating/updating a drive or after a full sync completes.
void clearUserDriveTxsCache() {
Expand Down Expand Up @@ -341,6 +355,12 @@ class ArweaveService {
return (activeDriveIds: activeDriveIds, isComplete: isComplete);
}

/// Owners whose drive-history queries the primary GraphQL endpoint could
/// not serve this session (e.g. indexer scan limits for very large
/// owners). Shared across strategy instances so the probing cost is paid
/// once per owner, not once per drive.
final Set<String> _gqlOwnersPreferringFallback = <String>{};

Stream<List<DriveEntityHistoryTransactionModel>>
getSegmentedTransactionsFromDrive(
String driveId, {
Expand All @@ -352,6 +372,8 @@ class ArweaveService {
strategy ??=
GetSegmentedTransactionFromDriveWithoutEntityTypeFilterStrategy(
graphQLRetry,
pageSize: _configService.config.driveHistoryGqlPageSize.clamp(1, 1000),
ownersPreferringFallback: _gqlOwnersPreferringFallback,
);

logger.d(
Expand Down Expand Up @@ -545,9 +567,7 @@ class ArweaveService {
);
}

final metadataCache = await MetadataCache.fromCacheStore(
await newSharedPreferencesCacheStore(),
);
final metadataCache = await _getMetadataCache();

final blockHistory = <BlockEntities>[];

Expand Down Expand Up @@ -1735,9 +1755,13 @@ class ArweaveService {

/// Fetches transaction info for multiple transactions in batches.
/// Returns a stream of transaction info batches.
///
/// The batch size matches the query's `first: 100` page size (the same
/// pattern as [getTransactionConfirmations] and [getLicenseAssertions]),
/// so each batch resolves in a single request.
Stream<Map<String, TxInfo>> getInfoOfTxsToBePinned(
List<String> transactionIds, {
int batchSize = 5,
int batchSize = 100,
}) async* {
for (var i = 0; i < transactionIds.length; i += batchSize) {
final end = (i + batchSize < transactionIds.length)
Expand Down
Loading
Loading