Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
00a8191
fix possible nano send amount bug
julian-CStack Aug 14, 2026
0c74bbd
firo fix segwit address validation
julian-CStack Aug 14, 2026
8163840
use payInAmount if available, not as a fallback
julian-CStack Aug 14, 2026
5e2eeba
better handling of localized group and decimal separators in amounts
julian-CStack Aug 14, 2026
2bba546
fix desktop token amount parsing
julian-CStack Aug 14, 2026
5c54df9
fix mobile SPL token amount parsing
julian-CStack Aug 14, 2026
467f610
clean up isar wallet data delete
julian-CStack Aug 14, 2026
0e56467
fix mwc first block
julian-CStack Aug 14, 2026
9c37a8f
fix frost prev keys issue
julian-CStack Aug 14, 2026
11c5bdf
fix test with platform specific check
julian-CStack Aug 14, 2026
f372d52
ensure marked used UTXOs do not get included in frost txn
julian-CStack Aug 14, 2026
c6f639c
fix checkTrades early return
julian-CStack Aug 14, 2026
3b57511
fix exchange refund gating
julian-CStack Aug 14, 2026
9cd17db
fix mweb fee provider
julian-CStack Aug 14, 2026
598898a
clean up epicbox/http/https check
julian-CStack Aug 14, 2026
5348187
fix missing xelis stagenet case
julian-CStack Aug 14, 2026
97ddbfb
return false on ping timeout
julian-CStack Aug 14, 2026
e1354a8
fix sorting to match comment
julian-CStack Aug 14, 2026
75d0e18
fix duplicate trade details dialog appearing on desktop
julian-CStack Aug 14, 2026
ea722de
update mocks
julian-CStack Aug 14, 2026
c12e533
add paynym notif tx input check
julian-CStack Aug 14, 2026
a4ea73b
fix dash dust limit
julian-CStack Aug 14, 2026
696a54e
ppc default fee rate update
julian-CStack Aug 14, 2026
68b206b
fix double decoding which can cause weird issues
julian-CStack Aug 14, 2026
a6531a9
fix address copy button value
julian-CStack Aug 14, 2026
e0dbdb8
handle zero restore chain height
julian-CStack Aug 14, 2026
f32a921
tighten up amount parsing
julian-CStack Aug 15, 2026
f5bfe88
tighten up payment uri parsing
julian-CStack Aug 15, 2026
f918252
fix swap rate sorting
julian-CStack Aug 15, 2026
9685de5
fix btc frost price
julian-CStack Aug 15, 2026
f8c2bb1
fix node backup restores
julian-CStack Aug 15, 2026
1c85b97
ensure frost wallet data is fully deleted on wallet delete
julian-CStack Aug 16, 2026
d84aba4
clean up node uri handling
julian-CStack Aug 16, 2026
24d5800
fix nanswap networks
julian-CStack Aug 16, 2026
6876df3
add extra check to swap pay in amount parsing
julian-CStack Aug 16, 2026
0fde946
add extra check to nano interface account info parsing
julian-CStack Aug 16, 2026
0144f91
add extra check to handle amount formatter invalid selection
julian-CStack Aug 16, 2026
6541cdc
fix autobackup location
julian-CStack Aug 16, 2026
898c56d
tighten up DPS persistence
julian-CStack Aug 16, 2026
78eced1
handle edgecase where estimate could be stale in the 1.5s window
julian-CStack Aug 17, 2026
f232514
formatting
julian-CStack Aug 17, 2026
9bb5aca
tighten localized number string parsing
julian-CStack Aug 17, 2026
c31d828
fix payment uri amount parsing
julian-CStack Aug 17, 2026
09ac90f
memo/extraid swap support
julian-CStack Aug 18, 2026
a8ca183
fix custom fee ui state drift on mobile
julian-CStack Aug 18, 2026
d00afa7
refactor electrumx based coin transaction building and fee calculation
julian-CStack Aug 19, 2026
143ae53
handle mweb 6 required min confirms
julian-CStack Aug 19, 2026
2466857
some dependency clean up
julian-CStack Aug 19, 2026
f7a7a60
fix wifi check (due to connectivity_plus package updated)
julian-CStack Aug 19, 2026
000c3fe
another set of dep upgrades for SPM as well as android/gradle stuff
julian-CStack Aug 19, 2026
dafffe8
replace deprecated share call
julian-CStack Aug 22, 2026
0f6c54f
update NSPhotoLibraryUsageDescription string
julian-CStack Aug 24, 2026
e5c4f64
Merge remote-tracking branch 'origin/staging' into julian/various-fixes
julian-CStack Aug 26, 2026
3621c59
Trim pasted text and pass it through the field's input formatters so
julian-CStack Aug 26, 2026
865fd82
Standardize localized input:
julian-CStack Aug 26, 2026
83f71a5
- Treat maxFeePerGas as the total protocol cap for presets and custom…
julian-CStack Aug 26, 2026
0360a3c
update and use adaptive text field for eth fee form
julian-CStack Aug 26, 2026
30ddf06
use shared editable amount policy for buy, cakepay, masternode reward…
julian-CStack Aug 26, 2026
62d0508
- editable locale-aware text for mobile and desktop send flows
julian-CStack Aug 26, 2026
a3bd6d9
keep debounced send and receive text through swaps, pair changes, foc…
julian-CStack Aug 26, 2026
c0f7e2f
serialize external amounts with locale-independent decimals and use s…
julian-CStack Aug 26, 2026
d8efa9a
fix(search): respect locale and coin precision
julian-CStack Aug 26, 2026
fee5f39
clean up unused amount parsing stuff
julian-CStack Aug 26, 2026
1711bc1
don't reset memo field every address change
julian-CStack Aug 26, 2026
c4f0cb3
reset desktop fee rate type state provider
julian-CStack Aug 26, 2026
d202038
fix autoformat on save mess
julian-CStack Aug 26, 2026
5dd9fdb
remove unused parsing functions
julian-CStack Aug 26, 2026
6b853a9
clean up replaced eth transactions
julian-CStack Aug 27, 2026
c38227f
replace error-prone refresh mutex/lock with wallet state coordination…
julian-CStack Aug 27, 2026
35d7595
mweb custom fee fix
julian-CStack Aug 27, 2026
25c93da
autoformat to satisfy ci
julian-CStack Aug 27, 2026
2854809
fixup! replace error-prone refresh mutex/lock with wallet state coord…
sneurlax Aug 28, 2026
cbd7a3f
build(flutter_mweb): install native assets in linux and windows bundles
sneurlax Aug 28, 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
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,14 @@ RUN mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools" \
&& sdkmanager \
"platform-tools" \
"build-tools;35.0.0" \
"build-tools;36.0.0" \
"build-tools;37.0.0" \
"platforms;android-32" \
"platforms;android-33" \
"platforms;android-34" \
"platforms;android-35" \
"platforms;android-36" \
"platforms;android-37" \
"ndk;28.0.13004108" \
"ndk;28.2.13676358" \
"cmake;3.22.1" \
Expand Down Expand Up @@ -148,11 +151,14 @@ RUN mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools" \
&& sdkmanager \
"platform-tools" \
"build-tools;35.0.0" \
"build-tools;36.0.0" \
"build-tools;37.0.0" \
"platforms;android-32" \
"platforms;android-33" \
"platforms;android-34" \
"platforms;android-35" \
"platforms;android-36" \
"platforms;android-37" \
"ndk;28.0.13004108" \
"ndk;28.2.13676358" \
"cmake;3.22.1" \
Expand Down
5 changes: 4 additions & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
# Jetifier removed in AGP 9; all deps are AndroidX already.
# Preserve Flutter's legacy Kotlin and Android DSL compatibility on AGP 9.
android.newDsl=false
android.builtInKotlin=false
2 changes: 1 addition & 1 deletion android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip
4 changes: 2 additions & 2 deletions android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.11.1' apply false
id "org.jetbrains.kotlin.android" version "2.2.20" apply false
id "com.android.application" version '9.1.1' apply false
id "org.jetbrains.kotlin.android" version "2.3.20" apply false
}

include ":app"
66 changes: 7 additions & 59 deletions lib/db/isar/main_db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import 'dart:io';

import 'package:decimal/decimal.dart';
import 'package:isar_community/isar.dart';
import 'package:tuple/tuple.dart';

Expand Down Expand Up @@ -349,6 +348,7 @@ class MainDB {
blockTime: utxo.blockTime,
blockHeight: utxo.blockHeight,
blockHash: utxo.blockHash,
otherData: utxo.otherData,
// passing null keeps the stored value
isBlocked: applyAutoBlock ? true : null,
blockedReason: applyAutoBlock ? utxo.blockedReason : null,
Expand Down Expand Up @@ -463,52 +463,14 @@ class MainDB {
//
Future<void> deleteWalletBlockchainData(String walletId) async {
await isar.writeTxn(() async {
final transactionCount = await getTransactions(walletId).count();
final transactionCountV2 = await isar.transactionV2s
.where()
.walletIdEqualTo(walletId)
.count();
final addressCount = await getAddresses(walletId).count();
final utxoCount = await getUTXOs(walletId).count();

const paginateLimit = 100;

// transactions
for (int i = 0; i < transactionCount; i += paginateLimit) {
final txnIds = await getTransactions(
walletId,
).offset(i).limit(paginateLimit).idProperty().findAll();
await isar.transactions.deleteAll(txnIds);
}

await getTransactions(walletId).deleteAll();
// transactions V2
for (int i = 0; i < transactionCountV2; i += paginateLimit) {
final txnIds = await isar.transactionV2s
.where()
.walletIdEqualTo(walletId)
.offset(i)
.limit(paginateLimit)
.idProperty()
.findAll();
await isar.transactionV2s.deleteAll(txnIds);
}

await isar.transactionV2s.where().walletIdEqualTo(walletId).deleteAll();
// addresses
for (int i = 0; i < addressCount; i += paginateLimit) {
final addressIds = await getAddresses(
walletId,
).offset(i).limit(paginateLimit).idProperty().findAll();
await isar.addresses.deleteAll(addressIds);
}

await getAddresses(walletId).deleteAll();
// utxos
for (int i = 0; i < utxoCount; i += paginateLimit) {
final utxoIds = await getUTXOs(
walletId,
).offset(i).limit(paginateLimit).idProperty().findAll();
await isar.utxos.deleteAll(utxoIds);
}

await getUTXOs(walletId).deleteAll();
// spark coins
await isar.sparkCoins
.where()
Expand All @@ -518,28 +480,14 @@ class MainDB {
}

Future<void> deleteAddressLabels(String walletId) async {
final addressLabelCount = await getAddressLabels(walletId).count();
await isar.writeTxn(() async {
const paginateLimit = 50;
for (int i = 0; i < addressLabelCount; i += paginateLimit) {
final labelIds = await getAddressLabels(
walletId,
).offset(i).limit(paginateLimit).idProperty().findAll();
await isar.addressLabels.deleteAll(labelIds);
}
await getAddressLabels(walletId).deleteAll();
});
}

Future<void> deleteTransactionNotes(String walletId) async {
final noteCount = await getTransactionNotes(walletId).count();
await isar.writeTxn(() async {
const paginateLimit = 50;
for (int i = 0; i < noteCount; i += paginateLimit) {
final labelIds = await getTransactionNotes(
walletId,
).offset(i).limit(paginateLimit).idProperty().findAll();
await isar.transactionNotes.deleteAll(labelIds);
}
await getTransactionNotes(walletId).deleteAll();
});
}

Expand Down
136 changes: 64 additions & 72 deletions lib/db/queries/queries.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@ extension MainDBQueries on MainDB {
required CCFilter filter,
required CCSortDescriptor sort,
required String searchTerm,
required String locale,
required CryptoCurrency cryptoCurrency,
}) {
var preSort = getUTXOs(walletId).filter().group((q) {
final qq = q.group(
(q) => q.usedIsNull().or().usedEqualTo(false),
);
final qq = q.group((q) => q.usedIsNull().or().usedEqualTo(false));
switch (filter) {
case CCFilter.frozen:
return qq.and().isBlockedEqualTo(true);
Expand All @@ -59,39 +58,36 @@ extension MainDBQueries on MainDB {
});

if (searchTerm.isNotEmpty) {
preSort = preSort.and().group(
(q) {
var qq = q.addressContains(searchTerm, caseSensitive: false);

qq = qq.or().nameContains(searchTerm, caseSensitive: false);
qq = qq.or().group(
(q) => q
.isBlockedEqualTo(true)
.and()
.blockedReasonContains(searchTerm, caseSensitive: false),
);

qq = qq.or().txidContains(searchTerm, caseSensitive: false);
qq = qq.or().blockHashContains(searchTerm, caseSensitive: false);

final maybeDecimal = Decimal.tryParse(searchTerm);
if (maybeDecimal != null) {
qq = qq.or().valueEqualTo(
Amount.fromDecimal(
maybeDecimal,
fractionDigits: cryptoCurrency.fractionDigits,
).raw.toInt(),
);
}

final maybeInt = int.tryParse(searchTerm);
if (maybeInt != null) {
qq = qq.or().valueEqualTo(maybeInt);
}

return qq;
},
);
preSort = preSort.and().group((q) {
var qq = q.addressContains(searchTerm, caseSensitive: false);

qq = qq.or().nameContains(searchTerm, caseSensitive: false);
qq = qq.or().group(
(q) => q
.isBlockedEqualTo(true)
.and()
.blockedReasonContains(searchTerm, caseSensitive: false),
);

qq = qq.or().txidContains(searchTerm, caseSensitive: false);
qq = qq.or().blockHashContains(searchTerm, caseSensitive: false);

final maybeAmount = Amount.tryParseEditableAmount(
searchTerm,
locale: locale,
fractionDigits: cryptoCurrency.fractionDigits,
);
if (maybeAmount != null) {
qq = qq.or().valueEqualTo(maybeAmount.raw.toInt());
}

final maybeInt = int.tryParse(searchTerm);
if (maybeInt != null) {
qq = qq.or().valueEqualTo(maybeInt);
}

return qq;
});
}

final List<Id> ids;
Expand All @@ -114,12 +110,11 @@ extension MainDBQueries on MainDB {
required CCFilter filter,
required CCSortDescriptor sort,
required String searchTerm,
required String locale,
required CryptoCurrency cryptoCurrency,
}) {
var preSort = getUTXOs(walletId).filter().group((q) {
final qq = q.group(
(q) => q.usedIsNull().or().usedEqualTo(false),
);
final qq = q.group((q) => q.usedIsNull().or().usedEqualTo(false));
switch (filter) {
case CCFilter.frozen:
return qq.and().isBlockedEqualTo(true);
Expand All @@ -131,39 +126,36 @@ extension MainDBQueries on MainDB {
});

if (searchTerm.isNotEmpty) {
preSort = preSort.and().group(
(q) {
var qq = q.addressContains(searchTerm, caseSensitive: false);

qq = qq.or().nameContains(searchTerm, caseSensitive: false);
qq = qq.or().group(
(q) => q
.isBlockedEqualTo(true)
.and()
.blockedReasonContains(searchTerm, caseSensitive: false),
);

qq = qq.or().txidContains(searchTerm, caseSensitive: false);
qq = qq.or().blockHashContains(searchTerm, caseSensitive: false);

final maybeDecimal = Decimal.tryParse(searchTerm);
if (maybeDecimal != null) {
qq = qq.or().valueEqualTo(
Amount.fromDecimal(
maybeDecimal,
fractionDigits: cryptoCurrency.fractionDigits,
).raw.toInt(),
);
}

final maybeInt = int.tryParse(searchTerm);
if (maybeInt != null) {
qq = qq.or().valueEqualTo(maybeInt);
}

return qq;
},
);
preSort = preSort.and().group((q) {
var qq = q.addressContains(searchTerm, caseSensitive: false);

qq = qq.or().nameContains(searchTerm, caseSensitive: false);
qq = qq.or().group(
(q) => q
.isBlockedEqualTo(true)
.and()
.blockedReasonContains(searchTerm, caseSensitive: false),
);

qq = qq.or().txidContains(searchTerm, caseSensitive: false);
qq = qq.or().blockHashContains(searchTerm, caseSensitive: false);

final maybeAmount = Amount.tryParseEditableAmount(
searchTerm,
locale: locale,
fractionDigits: cryptoCurrency.fractionDigits,
);
if (maybeAmount != null) {
qq = qq.or().valueEqualTo(maybeAmount.raw.toInt());
}

final maybeInt = int.tryParse(searchTerm);
if (maybeInt != null) {
qq = qq.or().valueEqualTo(maybeInt);
}

return qq;
});
}

final List<UTXO> utxos;
Expand Down
4 changes: 3 additions & 1 deletion lib/db/special_migrations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ abstract class CampfireMigration {
final myHive = HiveImpl();
myHive.init(appDirectory.path);
_wallets = await myHive.openBox<dynamic>('wallets');
_secureStore = const FlutterSecureStorage();
_secureStore = const FlutterSecureStorage(
aOptions: AndroidOptions(resetOnError: false, migrateWithBackup: true),
);
} else {
await setDidRun();
}
Expand Down
16 changes: 11 additions & 5 deletions lib/electrumx_rpc/electrumx_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,9 @@ class ElectrumXClient {

Future<bool> _allow() async {
if (_prefs.wifiOnly) {
return (await Connectivity().checkConnectivity()) ==
ConnectivityResult.wifi;
return (await Connectivity().checkConnectivity()).contains(
ConnectivityResult.wifi,
);
}
return true;
}
Expand Down Expand Up @@ -519,7 +520,11 @@ class ElectrumXClient {
/// Ping the server to ensure it is responding
///
/// Returns true if ping succeeded
Future<bool> ping({String? requestID, int retryCount = 1}) async {
Future<bool> ping({
String? requestID,
int retryCount = 1,
Duration timeout = const Duration(seconds: 30),
}) async {
try {
// This doesn't work because electrum_adapter only returns the result:
// (which is always `null`).
Expand All @@ -535,14 +540,15 @@ class ElectrumXClient {
return await request(
requestID: requestID,
command: 'server.ping',
requestTimeout: const Duration(seconds: 30),
requestTimeout: timeout,
retries: retryCount,
).timeout(
const Duration(seconds: 30),
timeout,
onTimeout: () {
Logging.instance.d(
"ElectrumxClient.ping timed out with retryCount=$retryCount, host=$_host",
);
return false;
},
)
as bool;
Expand Down
7 changes: 6 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,12 @@ void main(List<String> args) async {
await DbVersionMigrator().migrate(
dbVersion,
secureStore: const SecureStorageWrapper(
store: FlutterSecureStorage(),
store: FlutterSecureStorage(
aOptions: AndroidOptions(
resetOnError: false,
migrateWithBackup: true,
),
),
isDesktop: false,
),
);
Expand Down
Loading
Loading