Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6ff31f3
fix(screenshot-sensitivity): notify listeners in postFrameCallback
takenagain Sep 24, 2025
212ad6d
chore(sdk): switch to zhtlc branch
takenagain Sep 25, 2025
23ded71
feat(zhtlc): add configuration and param download dialogs
takenagain Sep 26, 2025
349baca
refactor: generate localisations, and fix theming on popup dialog
takenagain Sep 26, 2025
d33742e
refactor: use repositoryprovider and adjust status bar width
takenagain Sep 26, 2025
79ee69d
Merge remote-tracking branch 'origin/dev' into feat/zhtlc-asset-support
takenagain Sep 26, 2025
521ba35
perf(coins-manager): add input debouncer and cache heavy operations
takenagain Sep 26, 2025
9c68937
fix(activation): swap page activation, improve list and disposal mana…
takenagain Sep 27, 2025
16c5e5a
fix(zhtlc): update activation warning, add padding, and stealth text
takenagain Sep 27, 2025
b351e11
feat(orderbook): migrate to v2 orderbook RPC via SDK
takenagain Sep 28, 2025
3a6d3db
perf(dex): remove enabled assets rpc call from taker validator
takenagain Sep 28, 2025
2f0a129
style(zhtlc): improve dialog layout, add transaction note, fix hidden…
takenagain Sep 28, 2025
a264209
fix(market-metrics): filter out inactive coins before starting calcs
takenagain Sep 29, 2025
ccfae68
fix(zhtlc): workaround "Task is finished" error with retry on activation
takenagain Sep 29, 2025
6968034
feat(zhtlc): add activation status to banner
takenagain Sep 30, 2025
0c34a9b
fix(withdraw): add recipient address and memo (if not empty)
takenagain Sep 30, 2025
0e0fd64
fix(zhtlc): filter out active assets before attempting zhtlc activation
takenagain Sep 30, 2025
b85c889
fix(wallet): add resilience to delisted coins for wallet coin metadata
takenagain Sep 30, 2025
a130697
fix(version-info): ensure that apiversion and commit has persist
takenagain Sep 30, 2025
3537231
perf(market-data): add update backoff strategy and guard against fetc…
takenagain Oct 1, 2025
3b4319b
Merge branch 'dev' into feat/zhtlc-asset-support
takenagain Oct 1, 2025
a82e022
perf(dex): reduce overhead in coin selection and filtering
takenagain Oct 1, 2025
ccfae6a
fix(dex): only update sell amount on order selection if no value present
takenagain Oct 1, 2025
fd73814
Merge branch 'dev' into feat/zhtlc-asset-support
takenagain Oct 1, 2025
1e58d91
fix(zhtlc): track ongoing activations to prevent duplicate requests
takenagain Oct 1, 2025
066dd1d
fix(taker-validator): revert to getting active assets from API
takenagain Oct 2, 2025
99602ee
Merge remote-tracking branch 'origin/dev' into feat/zhtlc-asset-support
takenagain Oct 2, 2025
4416b1c
chore(sdk): switch back to dev branch (sdk branch merged)
takenagain Oct 2, 2025
c6e7621
Merge remote-tracking branch 'origin/dev' into feat/zhtlc-asset-support
takenagain Oct 2, 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
29 changes: 27 additions & 2 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,6 @@
"backupSeedPhrase": "Backup seed phrase",
"seedOr": "OR",
"seedDownload": "Download seed phrase",
"seedSaveAndRemember": "Save and remember",
"seedIntroWarning": "This phrase is the main access to your\nassets, save and never share this phrase",
"seedSettings": "Seed phrase",
"errorDescription": "Error description",
Expand Down Expand Up @@ -460,6 +459,7 @@
"withdrawAmountTooLowError": "{} {} too low, you need > {} {} to send",
"withdrawNoSuchCoinError": "Invalid selection, {} does not exist",
"withdrawPreview": "Preview Withdrawal",
"withdrawPreviewZhtlcNote": "ZHTLC transactions can take a while to generate.\nPlease stay on this page until the preview is ready, otherwise you will need to start over.",
"withdrawPreviewError": "Error occurred while fetching withdrawal preview",
"txHistoryFetchError": "Error fetching tx history from the endpoint. Unsupported type: {}",
"txHistoryNoTransactions": "Transactions are not available",
Expand Down Expand Up @@ -510,6 +510,7 @@
"userActionRequired": "User action required",
"unknown": "Unknown",
"unableToActiveCoin": "Unable to activate {}",
"coinIsNotActive": "{} is not active",
"feedback": "Feedback",
"feedbackViewTitle": "Send us your feedback",
"feedbackPageDescription": "Help us improve by sharing your suggestions, reporting bugs, or giving general feedback.",
Expand Down Expand Up @@ -765,5 +766,29 @@
"fetchingPrivateKeysTitle": "Fetching Private Keys...",
"fetchingPrivateKeysMessage": "Please wait while we securely fetch your private keys...",
"pubkeyType": "Type",
"securitySettings": "Security Settings"
"securitySettings": "Security Settings",
"zhtlcConfigureTitle": "Configure {}",
"zhtlcZcashParamsPathLabel": "Zcash parameters path",
"zhtlcPathAutomaticallyDetected": "Path automatically detected",
"zhtlcSaplingParamsFolder": "Folder containing sapling params",
"zhtlcBlocksPerIterationLabel": "Blocks per iteration",
"zhtlcScanIntervalLabel": "Scan interval (ms)",
"zhtlcStartSyncFromLabel": "Start sync from:",
"zhtlcEarliestSaplingOption": "Earliest (sapling)",
"zhtlcBlockHeightOption": "Block height",
"zhtlcShieldedAddress": "Shielded",
"zhtlcDateTimeOption": "Date & Time",
"zhtlcSelectDateTimeLabel": "Select date & time",
"zhtlcZcashParamsRequired": "Zcash params path is required",
"zhtlcInvalidBlockHeight": "Enter a valid block height",
"zhtlcSelectDateTimeRequired": "Please select a date and time",
"zhtlcDownloadingZcashParams": "Downloading Zcash Parameters",
"zhtlcPreparingDownload": "Preparing download...",
"zhtlcErrorSettingUpZcash": "Error setting up Zcash parameters: {}",
"zhtlcDateSyncHint": "Selecting a date further in the past can significantly increase the activation time. \nActivation can take a little while the first time to download block cache data.\n\nTransactions and balance prior to the sync date may be missing.\nOften this can be restored by sending in and out new transactions",
"zhtlcActivating": {
"one": "Activating ZHTLC coin. Please do not close the app or tab until complete.",
"other": "Activating ZHTLC coins. Please do not close the app or tab until complete."
},
"zhtlcActivationWarning": "This may take from a few minutes to a few hours, depending on your sync params and how long since your last sync."
}
7 changes: 0 additions & 7 deletions lib/app_config/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,8 @@ const Set<String> excludedAssetList = {
'FENIX',
'AWR',
'BOT',
// Pirate activation params are not yet implemented, so we need to
// exclude it from the list of coins for now.
'ARRR',
'ZOMBIE',
'SMTF-v2',
'SFUSD',
'VOTE2023',
'RICK',
'MORTY',

// NFT v2 coins: https://github.com/KomodoPlatform/coins/pull/1061 will be
// used in the background, so users do not need to see them.
Expand Down
2 changes: 1 addition & 1 deletion lib/bloc/app_bloc_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class AppBlocRoot extends StatelessWidget {
dexRepository: dexRepository,
),
),
RepositoryProvider(create: (_) => OrderbookBloc(api: mm2Api)),
RepositoryProvider(create: (_) => OrderbookBloc(sdk: komodoDefiSdk)),
RepositoryProvider(create: (_) => myOrdersService),
RepositoryProvider(
create: (_) => KmdRewardsBloc(coinsRepository, mm2Api),
Expand Down
21 changes: 17 additions & 4 deletions lib/bloc/assets_overview/bloc/asset_overview_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:web_dex/bloc/cex_market_data/profit_loss/models/fiat_value.dart'
import 'package:web_dex/bloc/cex_market_data/profit_loss/models/profit_loss.dart';
import 'package:web_dex/bloc/cex_market_data/profit_loss/profit_loss_repository.dart';
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';

part 'asset_overview_event.dart';
Expand Down Expand Up @@ -93,9 +94,21 @@ class AssetOverviewBloc extends Bloc<AssetOverviewEvent, AssetOverviewState> {
return;
}

await _sdk.waitForEnabledCoinsToPassThreshold(event.coins);
final supportedCoins = await event.coins.filterSupportedCoins();
if (supportedCoins.isEmpty) {
_log.warning('No supported coins to load portfolio overview for');
return;
}

await _sdk.waitForEnabledCoinsToPassThreshold(supportedCoins);

final activeCoins = await supportedCoins.removeInactiveCoins(_sdk);
if (activeCoins.isEmpty) {
_log.warning('No active coins to load portfolio overview for');
return;
}

final profitLossesFutures = event.coins.map((coin) async {
final profitLossesFutures = activeCoins.map((coin) async {
// Catch errors that occur for single coins and exclude them from the
// total so that transaction fetching errors for a single coin do not
// affect the total investment calculation.
Expand All @@ -114,7 +127,7 @@ class AssetOverviewBloc extends Bloc<AssetOverviewEvent, AssetOverviewState> {
final profitLosses = await Future.wait(profitLossesFutures);

final totalInvestment = await _investmentRepository
.calculateTotalInvestment(event.walletId, event.coins);
.calculateTotalInvestment(event.walletId, activeCoins);

final profitAmount = profitLosses.fold(0.0, (sum, item) {
return sum + (item.lastOrNull?.profitLoss ?? 0.0);
Expand All @@ -130,7 +143,7 @@ class AssetOverviewBloc extends Bloc<AssetOverviewEvent, AssetOverviewState> {

emit(
PortfolioAssetsOverviewLoadSuccess(
selectedAssetIds: event.coins.map((coin) => coin.id.id).toList(),
selectedAssetIds: activeCoins.map((coin) => coin.id.id).toList(),
assetPortionPercentages: assetPortionPercentages,
totalInvestment: totalInvestment,
totalValue: FiatValue.usd(profitAmount),
Expand Down
1 change: 0 additions & 1 deletion lib/bloc/bridge_form/bridge_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ class BridgeRepository {
Future<CoinsByTicker> getAvailableTickers() async {
List<Coin> coins = _coinsRepository.getKnownCoins();
coins = removeWalletOnly(coins);
coins = removeSuspended(coins, await _kdfSdk.auth.isSignedIn());

final CoinsByTicker coinsByTicker = convertToCoinsByTicker(coins);
final CoinsByTicker multiProtocolCoins =
Expand Down
2 changes: 1 addition & 1 deletion lib/bloc/bridge_form/bridge_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ class BridgeValidator {
}

DexFormError _coinNotActiveError(String abbr) {
return DexFormError(error: '$abbr is not active.');
return DexFormError(error: LocaleKeys.coinIsNotActive.tr(args: [abbr]));
}

DexFormError _selectSourceProtocolError() =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import 'dart:math' as math;

/// A strategy for implementing exponential backoff with paired intervals.
/// The pattern is: 1min, 1min, 2min, 2min, 4min, 4min, 8min, 8min, etc.
/// This reduces API calls while still providing reasonable update frequency.
class UpdateFrequencyBackoffStrategy {
UpdateFrequencyBackoffStrategy({
this.baseInterval = const Duration(minutes: 1),
this.maxInterval = const Duration(hours: 1),
});

/// The base interval for the first attempts (default: 2 minutes)
final Duration baseInterval;

/// The maximum interval to backoff to (default: 1 hour)
final Duration maxInterval;

int _attemptCount = 0;

/// Reset the backoff strategy to start from the beginning
void reset() {
_attemptCount = 0;
}

/// Get the current attempt count
int get attemptCount => _attemptCount;

/// Get the next interval duration and increment the attempt count
Duration getNextInterval() {
final interval = getCurrentInterval();
_attemptCount++;
return interval;
}

/// Get the current interval duration without incrementing the attempt count
Duration getCurrentInterval() {
// Calculate which "pair" we're in (0, 1, 2, 3, ...)
// Each pair has 2 attempts with the same interval
final pairIndex = _attemptCount ~/ 2;

// Calculate the multiplier: 2^pairIndex
final multiplier = math.pow(2, pairIndex).toInt();

// Calculate the interval
final intervalMs = baseInterval.inMilliseconds * multiplier;

// Cap at maximum interval
final cappedIntervalMs = math.min(intervalMs, maxInterval.inMilliseconds);

return Duration(milliseconds: cappedIntervalMs);
}

/// Check if we should update the cache on the current attempt
/// Returns true for cache update attempts, false for cache-only reads
bool shouldUpdateCache() {
// Update cache on every attempt for now, but this could be modified
// to only update on certain intervals if needed
return true;
}

/// Get a preview of the next N intervals without affecting the state
List<Duration> previewNextIntervals(int count) {
final currentAttempt = _attemptCount;
final intervals = <Duration>[];

for (int i = 0; i < count; i++) {
final pairIndex = (currentAttempt + i) ~/ 2;
final multiplier = math.pow(2, pairIndex).toInt();
final intervalMs = baseInterval.inMilliseconds * multiplier;
final cappedIntervalMs = math.min(intervalMs, maxInterval.inMilliseconds);
intervals.add(Duration(milliseconds: cappedIntervalMs));
}

return intervals;
}
}
Loading
Loading