Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
701d3cc
feat(pubkeys): prefer cached pubkeys before RPC across app; add post-…
CharlVS Oct 26, 2025
9bdee62
chore(format): run dart format on pubkey cache call-sites and taker d…
CharlVS Oct 26, 2025
c9c3434
Merge branch 'dev' into perf/3238-reduce-rpc-spam
CharlVS Oct 26, 2025
e5e0d86
fix: fix analysis type warning
CharlVS Oct 26, 2025
f0a6b0d
chore(sdk): bump submodule to include local-auth test fix
CharlVS Oct 26, 2025
00d19ab
Merge branch 'dev' into perf/3238-reduce-rpc-spam
CharlVS Oct 27, 2025
ae8eb92
perf(rpc): reduce MM2 RPC spam via SDK cache and UI debouncing
CharlVS Oct 27, 2025
6e83be9
chore: update SDK submodule with streaming type safety improvements
CharlVS Oct 27, 2025
d8eef4f
chore: update SDK submodule with event streaming manager
CharlVS Oct 27, 2025
49d1756
chore: roll SDK
CharlVS Oct 27, 2025
e472aad
chore: update sdk submodule with review feedback fixes
CharlVS Oct 27, 2025
8c6fb84
chore: update SDK submodule with shutdown event streaming optimization
CharlVS Oct 27, 2025
909bf11
chore: update sdk submodule with new wallet optimization
CharlVS Oct 27, 2025
051ac42
chore: update sdk submodule with imported wallet fix
CharlVS Oct 27, 2025
4bf37d5
Merge branch 'dev' of https://github.com/KomodoPlatform/komodo-wallet…
CharlVS Oct 27, 2025
6047895
chore: roll SDK
CharlVS Oct 27, 2025
fbd4db4
(TEMP): Comment out breaking changes from merge
CharlVS Oct 27, 2025
6a2237b
chore: roll SDK
CharlVS Oct 27, 2025
fb5738f
chore: roll SDK
CharlVS Oct 28, 2025
3d12c67
chore: roll SDK with activation cache fix
CharlVS Oct 28, 2025
2ecaf56
chore: roll SDK with asset ID streaming fix
CharlVS Oct 28, 2025
812f939
chore: roll SDK
CharlVS Oct 28, 2025
5e35d03
Merge branch 'dev' into perf/3238-reduce-rpc-spam
CharlVS Oct 28, 2025
d920efe
Update lib/bloc/fiat/fiat_onramp_form/fiat_form_bloc.dart
CharlVS Oct 28, 2025
2c8c4c4
fix(coins): add check for already-activated assets before activation
CharlVS Oct 28, 2025
491e9f9
chore(sdk): bump submodule to include stream type normalization and f…
CharlVS Oct 28, 2025
0494442
Merge branch 'dev' of https://github.com/KomodoPlatform/komodo-wallet…
CharlVS Oct 28, 2025
bb099db
fix: unnecessary activation cache invalidation
CharlVS Oct 28, 2025
9900372
fix: show unconfirmed transactions first in list
CharlVS Oct 28, 2025
0c2eef2
chore: roll SDK for streaming fix
CharlVS Oct 28, 2025
24276e8
chore: roll SDk
CharlVS Oct 28, 2025
6614fad
chore: roll SDK
CharlVS Oct 28, 2025
dce1709
fix: transaction history list sorting
CharlVS Oct 28, 2025
cf5c124
feat(wallet): realtime balance updates via CoinsBloc\n\n- Add balance…
CharlVS Oct 28, 2025
43a02dd
fix(wallet): preserve coin activation state on realtime balance updat…
CharlVS Oct 28, 2025
bb03bdc
Merge branch 'dev' into perf/3238-reduce-rpc-spam
CharlVS Oct 28, 2025
3f7703d
Merge branch 'dev' into perf/3238-reduce-rpc-spam
CharlVS Oct 29, 2025
00ab6c7
chore: roll sdk
CharlVS Oct 29, 2025
27b7db2
fix(wallet): isolate TransactionHistoryBloc per-coin to stop cross-as…
CharlVS Oct 29, 2025
1312486
Merge branch 'dev' into perf/3238-reduce-rpc-spam
ca333 Oct 29, 2025
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
6 changes: 3 additions & 3 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@
"$(PROJECT_DIR)",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = com.komodoplatform.atomicdex;
PRODUCT_BUNDLE_IDENTIFIER = com.komodo.wallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
Expand Down Expand Up @@ -549,7 +549,7 @@
"$(PROJECT_DIR)",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = com.komodoplatform.atomicdex;
PRODUCT_BUNDLE_IDENTIFIER = com.komodo.wallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
Expand Down Expand Up @@ -584,7 +584,7 @@
"$(PROJECT_DIR)",
);
MACOSX_DEPLOYMENT_TARGET = 15.0;
PRODUCT_BUNDLE_IDENTIFIER = com.komodoplatform.atomicdex;
PRODUCT_BUNDLE_IDENTIFIER = com.komodo.wallet;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
Expand Down
5 changes: 0 additions & 5 deletions lib/bloc/app_bloc_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import 'package:web_dex/bloc/system_health/system_health_bloc.dart';
import 'package:web_dex/bloc/taker_form/taker_bloc.dart';
import 'package:web_dex/bloc/trading_status/trading_status_bloc.dart';
import 'package:web_dex/bloc/trading_status/trading_status_service.dart';
import 'package:web_dex/bloc/transaction_history/transaction_history_bloc.dart';
import 'package:web_dex/bloc/transaction_history/transaction_history_repo.dart';
import 'package:web_dex/bloc/version_info/version_info_bloc.dart';
import 'package:web_dex/blocs/kmd_rewards_bloc.dart';
Expand Down Expand Up @@ -211,10 +210,6 @@ class AppBlocRoot extends StatelessWidget {
sdk: komodoDefiSdk,
),
),
BlocProvider<TransactionHistoryBloc>(
create: (BuildContext ctx) =>
TransactionHistoryBloc(sdk: komodoDefiSdk),
),
BlocProvider<SettingsBloc>(
create: (context) =>
SettingsBloc(storedPrefs, SettingsRepository()),
Expand Down
6 changes: 5 additions & 1 deletion lib/bloc/assets_overview/bloc/asset_overview_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:web_dex/bloc/cex_market_data/profit_loss/profit_loss_repository.
import 'package:web_dex/bloc/cex_market_data/sdk_auth_activation_extension.dart';
import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart';
import 'package:web_dex/model/coin.dart';
import 'package:web_dex/shared/constants.dart';

part 'asset_overview_event.dart';
part 'asset_overview_state.dart';
Expand Down Expand Up @@ -100,7 +101,10 @@ class AssetOverviewBloc extends Bloc<AssetOverviewEvent, AssetOverviewState> {
return;
}

await _sdk.waitForEnabledCoinsToPassThreshold(supportedCoins);
await _sdk.waitForEnabledCoinsToPassThreshold(
supportedCoins,
delay: kActivationPollingInterval,
);

final activeCoins = await supportedCoins.removeInactiveCoins(_sdk);
if (activeCoins.isEmpty) {
Expand Down
117 changes: 61 additions & 56 deletions lib/bloc/bridge_form/bridge_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ class BridgeValidator {
required CoinsRepo coinsRepository,
required DexRepository dexRepository,
required KomodoDefiSdk sdk,
}) : _bloc = bloc,
_coinsRepo = coinsRepository,
_dexRepo = dexRepository,
_sdk = sdk,
_add = bloc.add;
}) : _bloc = bloc,
_coinsRepo = coinsRepository,
_dexRepo = dexRepository,
_sdk = sdk,
_add = bloc.add;

final BridgeBloc _bloc;
final CoinsRepo _coinsRepo;
Expand Down Expand Up @@ -71,32 +71,32 @@ class BridgeValidator {
}

DexFormError? _parsePreimageError(
DataFromService<TradePreimage, BaseError> preimageData) {
DataFromService<TradePreimage, BaseError> preimageData,
) {
final BaseError? error = preimageData.error;

if (error is TradePreimageNotSufficientBalanceError) {
return _insufficientBalanceError(
Rational.parse(error.required), error.coin);
Rational.parse(error.required),
error.coin,
);
} else if (error is TradePreimageNotSufficientBaseCoinBalanceError) {
return _insufficientBalanceError(
Rational.parse(error.required), error.coin);
} else if (error is TradePreimageTransportError) {
return DexFormError(
error: LocaleKeys.notEnoughBalanceForGasError.tr(),
Rational.parse(error.required),
error.coin,
);
} else if (error is TradePreimageTransportError) {
return DexFormError(error: LocaleKeys.notEnoughBalanceForGasError.tr());
} else if (error is TradePreimageVolumeTooLowError) {
return DexFormError(
error: LocaleKeys.lowTradeVolumeError
.tr(args: [formatAmt(double.parse(error.threshold)), error.coin]),
error: LocaleKeys.lowTradeVolumeError.tr(
args: [formatAmt(double.parse(error.threshold)), error.coin],
),
);
} else if (error != null) {
return DexFormError(
error: error.message,
);
return DexFormError(error: error.message);
} else if (preimageData.data == null) {
return DexFormError(
error: LocaleKeys.somethingWrong.tr(),
);
return DexFormError(error: LocaleKeys.somethingWrong.tr());
}

return null;
Expand Down Expand Up @@ -128,10 +128,15 @@ class BridgeValidator {
_state.sellAmount,
);
} catch (e, s) {
log(e.toString(),
trace: s, path: 'bridge_validator::_getPreimageData', isError: true);
log(
e.toString(),
trace: s,
path: 'bridge_validator::_getPreimageData',
isError: true,
);
return DataFromService(
error: TextError(error: 'Failed to request trade preimage'));
error: TextError(error: 'Failed to request trade preimage'),
);
}
}

Expand Down Expand Up @@ -187,17 +192,17 @@ class BridgeValidator {
if (availableBalance < maxOrderVolume && sellAmount > availableBalance) {
final Rational minAmount = maxRational([
_state.minSellAmount ?? Rational.zero,
_state.bestOrder!.minVolume
_state.bestOrder!.minVolume,
])!;

if (availableBalance < minAmount) {
_add(BridgeSetError(
_insufficientBalanceError(minAmount, _state.sellCoin!.abbr),
));
_add(
BridgeSetError(
_insufficientBalanceError(minAmount, _state.sellCoin!.abbr),
),
);
} else {
_add(BridgeSetError(
_setMaxError(availableBalance),
));
_add(BridgeSetError(_setMaxError(availableBalance)));
}

return false;
Expand All @@ -218,9 +223,11 @@ class BridgeValidator {
if (sellAmount < minAmount) {
final Rational available = _state.maxSellAmount ?? Rational.zero;
if (available < minAmount) {
_add(BridgeSetError(
_insufficientBalanceError(minAmount, _state.sellCoin!.abbr),
));
_add(
BridgeSetError(
_insufficientBalanceError(minAmount, _state.sellCoin!.abbr),
),
);
} else {
_add(BridgeSetError(_setMinError(minAmount)));
}
Expand All @@ -233,22 +240,17 @@ class BridgeValidator {

Future<bool> _validateCoinAndParent(String abbr) async {
final coin = _sdk.getSdkAsset(abbr);
final enabledAssets = await _sdk.assets.getActivatedAssets();
final isAssetEnabled = enabledAssets.contains(coin);
final activatedAssetIds = await _coinsRepo.getActivatedAssetIds();
final parentId = coin.id.parentId;
final parent = _sdk.assets.available[parentId];

if (!isAssetEnabled) {
if (!activatedAssetIds.contains(coin.id)) {
_add(BridgeSetError(_coinNotActiveError(coin.id.id)));
return false;
}

if (parent != null) {
final isParentEnabled = enabledAssets.contains(parent);
if (!isParentEnabled) {
_add(BridgeSetError(_coinNotActiveError(parent.id.id)));
return false;
}
if (parentId != null && !activatedAssetIds.contains(parentId)) {
_add(BridgeSetError(_coinNotActiveError(parentId.id)));
return false;
}

return true;
Expand All @@ -262,7 +264,8 @@ class BridgeValidator {

final selectedOrderAddress = selectedOrder.address;
final asset = _sdk.getSdkAsset(selectedOrder.coin);
final ownPubkeys = await _sdk.pubkeys.getPubkeys(asset);
final cached = _sdk.pubkeys.lastKnown(asset.id);
final ownPubkeys = cached ?? await _sdk.pubkeys.getPubkeys(asset);
final ownAddresses = ownPubkeys.keys
.where((pubkeyInfo) => pubkeyInfo.isActiveForSwap)
.map((e) => e.address)
Expand Down Expand Up @@ -304,8 +307,9 @@ class BridgeValidator {

DexFormError _setOrderMaxError(Rational maxAmount) {
return DexFormError(
error: LocaleKeys.dexMaxOrderVolume
.tr(args: [formatDexAmt(maxAmount), _state.sellCoin!.abbr]),
error: LocaleKeys.dexMaxOrderVolume.tr(
args: [formatDexAmt(maxAmount), _state.sellCoin!.abbr],
),
type: DexFormErrorType.largerMaxSellVolume,
action: DexFormErrorAction(
text: LocaleKeys.setMax.tr(),
Expand All @@ -318,8 +322,9 @@ class BridgeValidator {

DexFormError _insufficientBalanceError(Rational required, String abbr) {
return DexFormError(
error: LocaleKeys.dexBalanceNotSufficientError
.tr(args: [abbr, formatDexAmt(required), abbr]),
error: LocaleKeys.dexBalanceNotSufficientError.tr(
args: [abbr, formatDexAmt(required), abbr],
),
);
}

Expand All @@ -341,20 +346,20 @@ class BridgeValidator {
DexFormError _setMinError(Rational minAmount) {
return DexFormError(
type: DexFormErrorType.lessMinVolume,
error: LocaleKeys.dexMinSellAmountError
.tr(args: [formatDexAmt(minAmount), _state.sellCoin!.abbr]),
error: LocaleKeys.dexMinSellAmountError.tr(
args: [formatDexAmt(minAmount), _state.sellCoin!.abbr],
),
action: DexFormErrorAction(
text: LocaleKeys.setMin.tr(),
callback: () async {
_add(BridgeSetSellAmount(minAmount));
}),
text: LocaleKeys.setMin.tr(),
callback: () async {
_add(BridgeSetSellAmount(minAmount));
},
),
);
}

DexFormError _tradingWithSelfError() {
return DexFormError(
error: LocaleKeys.dexTradingWithSelfError.tr(),
);
return DexFormError(error: LocaleKeys.dexTradingWithSelfError.tr());
}

bool get _isSellCoinSelected => _state.sellCoin != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart';
import 'package:web_dex/mm2/mm2_api/rpc/base.dart';
import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/constants.dart';

part 'portfolio_growth_event.dart';
part 'portfolio_growth_state.dart';
Expand Down Expand Up @@ -160,7 +161,10 @@ class PortfolioGrowthBloc
// In case most coins are activating on wallet startup, wait for at least
// 50% of the coins to be enabled before attempting to load the uncached
// chart.
await _sdk.waitForEnabledCoinsToPassThreshold(filteredEventCoins);
await _sdk.waitForEnabledCoinsToPassThreshold(
filteredEventCoins,
delay: kActivationPollingInterval,
);

// Only remove inactivate/activating coins after an attempt to load the
// cached chart, as the cached chart may contain inactive coins.
Expand Down
6 changes: 5 additions & 1 deletion lib/bloc/cex_market_data/profit_loss/profit_loss_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart';
import 'package:web_dex/mm2/mm2_api/rpc/base.dart';
import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/constants.dart';

part 'profit_loss_event.dart';
part 'profit_loss_state.dart';
Expand Down Expand Up @@ -80,7 +81,10 @@ class ProfitLossBloc extends Bloc<ProfitLossEvent, ProfitLossState> {

// Fetch the un-cached version of the chart to update the cache.
if (supportedCoins.isNotEmpty) {
await _sdk.waitForEnabledCoinsToPassThreshold(supportedCoins);
await _sdk.waitForEnabledCoinsToPassThreshold(
supportedCoins,
delay: kActivationPollingInterval,
);
}
final activeCoins = await supportedCoins.removeInactiveCoins(_sdk);
if (activeCoins.isNotEmpty) {
Expand Down
9 changes: 6 additions & 3 deletions lib/bloc/cex_market_data/sdk_auth_activation_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:logging/logging.dart';
import 'package:web_dex/model/coin.dart';
import 'package:web_dex/shared/constants.dart';

extension SdkAuthActivationExtension on KomodoDefiSdk {
/// Waits for the enabled coins to pass the provided threshold of the provided
Expand All @@ -14,7 +15,7 @@ extension SdkAuthActivationExtension on KomodoDefiSdk {
List<Coin> walletCoins, {
double threshold = 0.5,
Duration timeout = const Duration(seconds: 30),
Duration delay = const Duration(milliseconds: 500),
Duration delay = kActivationPollingInterval,
}) async {
if (timeout <= Duration.zero) {
throw ArgumentError.value(timeout, 'timeout', 'is negative');
Expand All @@ -27,8 +28,10 @@ extension SdkAuthActivationExtension on KomodoDefiSdk {
final walletCoinIds = walletCoins.map((e) => e.id).toSet();
final stopwatch = Stopwatch()..start();
while (true) {
final isAboveThreshold =
await _areEnabledCoinsAboveThreshold(walletCoinIds, threshold);
final isAboveThreshold = await _areEnabledCoinsAboveThreshold(
walletCoinIds,
threshold,
);
if (isAboveThreshold) {
log.fine(
'Enabled coins have passed the threshold in '
Expand Down
4 changes: 3 additions & 1 deletion lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {

try {
final asset = getSdkAsset(sdk, assetId);
final addresses = (await asset.getPubkeys(sdk)).keys;
// Prefer cached pubkeys to avoid unnecessary RPC delay
final cached = sdk.pubkeys.lastKnown(asset.id);
final addresses = (cached ?? await asset.getPubkeys(sdk)).keys;

final reasons = await asset.getCantCreateNewAddressReasons(sdk);

Expand Down
Loading
Loading