diff --git a/assets/images/dcr_icon.png b/assets/images/dcr_icon.png new file mode 100644 index 000000000..609873611 Binary files /dev/null and b/assets/images/dcr_icon.png differ diff --git a/assets/images/husd_icon.png b/assets/images/husd_icon.png new file mode 100644 index 000000000..071027099 Binary files /dev/null and b/assets/images/husd_icon.png differ diff --git a/assets/images/kmd_icon.png b/assets/images/kmd_icon.png new file mode 100644 index 000000000..dc07f5802 Binary files /dev/null and b/assets/images/kmd_icon.png differ diff --git a/assets/images/mana_icon.png b/assets/images/mana_icon.png new file mode 100644 index 000000000..ad9d7b1b1 Binary files /dev/null and b/assets/images/mana_icon.png differ diff --git a/assets/images/matic_icon.png b/assets/images/matic_icon.png new file mode 100644 index 000000000..7f5493343 Binary files /dev/null and b/assets/images/matic_icon.png differ diff --git a/assets/images/mkr_icon.png b/assets/images/mkr_icon.png new file mode 100644 index 000000000..66c06446d Binary files /dev/null and b/assets/images/mkr_icon.png differ diff --git a/assets/images/near_icon.png b/assets/images/near_icon.png new file mode 100644 index 000000000..667ea45c3 Binary files /dev/null and b/assets/images/near_icon.png differ diff --git a/assets/images/oxt_icon.png b/assets/images/oxt_icon.png new file mode 100644 index 000000000..43228e189 Binary files /dev/null and b/assets/images/oxt_icon.png differ diff --git a/assets/images/paxg_icon.png b/assets/images/paxg_icon.png new file mode 100644 index 000000000..17f093303 Binary files /dev/null and b/assets/images/paxg_icon.png differ diff --git a/assets/images/pivx_icon.png b/assets/images/pivx_icon.png new file mode 100644 index 000000000..0b3b80eb1 Binary files /dev/null and b/assets/images/pivx_icon.png differ diff --git a/assets/images/rune_icon.png b/assets/images/rune_icon.png new file mode 100644 index 000000000..96d8bf40c Binary files /dev/null and b/assets/images/rune_icon.png differ diff --git a/assets/images/rvn_icon.png b/assets/images/rvn_icon.png new file mode 100644 index 000000000..24c5c1d41 Binary files /dev/null and b/assets/images/rvn_icon.png differ diff --git a/assets/images/scrt_icon.png b/assets/images/scrt_icon.png new file mode 100644 index 000000000..ce38dcc58 Binary files /dev/null and b/assets/images/scrt_icon.png differ diff --git a/assets/images/stx_icon.png b/assets/images/stx_icon.png new file mode 100644 index 000000000..76571a7c8 Binary files /dev/null and b/assets/images/stx_icon.png differ diff --git a/assets/images/uni_icon.png b/assets/images/uni_icon.png new file mode 100644 index 000000000..01ffcf726 Binary files /dev/null and b/assets/images/uni_icon.png differ diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index ffe378069..7abed8c07 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -495,7 +495,7 @@ abstract class ElectrumWalletBase extends WalletBase with Serializable { CryptoCurrency.zec, CryptoCurrency.zen, CryptoCurrency.xvg, + CryptoCurrency.usdcpoly, + CryptoCurrency.dcr, + CryptoCurrency.husd, + CryptoCurrency.kmd, + CryptoCurrency.mana, + CryptoCurrency.maticpoly, + CryptoCurrency.matic, + CryptoCurrency.mkr, + CryptoCurrency.near, + CryptoCurrency.oxt, + CryptoCurrency.paxg, + CryptoCurrency.pivx, + CryptoCurrency.rune, + CryptoCurrency.rvn, + CryptoCurrency.scrt, + CryptoCurrency.uni, + CryptoCurrency.stx, ]; static const xmr = CryptoCurrency(title: 'XMR', iconPath: 'assets/images/monero_icon.png', name: 'Monero', raw: 0); @@ -102,6 +119,26 @@ class CryptoCurrency extends EnumerableItem with Serializable { static const zen = CryptoCurrency(title: 'ZEN', iconPath: 'assets/images/zen_icon.png', raw: 44); static const xvg = CryptoCurrency(title: 'XVG', name: 'Verge', iconPath: 'assets/images/xvg_icon.png', raw: 45); + static const usdcpoly = CryptoCurrency(title: 'USDC', iconPath: 'assets/images/usdc_icon.png', tag: 'POLY', raw: 46); + static const dcr = CryptoCurrency(title: 'DCR', iconPath: 'assets/images/dcr_icon.png', raw: 47); + static const husd = CryptoCurrency(title: 'HUSD', iconPath: 'assets/images/husd_icon.png', tag: 'ETH', raw: 48); + static const kmd = CryptoCurrency(title: 'KMD', iconPath: 'assets/images/kmd_icon.png', raw: 49); + static const mana = CryptoCurrency(title: 'MANA', iconPath: 'assets/images/mana_icon.png', tag: 'ETH', raw: 50); + static const maticpoly = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'POLY', raw: 51); + static const matic = CryptoCurrency(title: 'MATIC', iconPath: 'assets/images/matic_icon.png', tag: 'ETH', raw: 52); + static const mkr = CryptoCurrency(title: 'MKR', iconPath: 'assets/images/mkr_icon.png', tag: 'ETH', raw: 53); + static const near = CryptoCurrency(title: 'NEAR', iconPath: 'assets/images/near_icon.png', raw: 54); + static const oxt = CryptoCurrency(title: 'OXT', iconPath: 'assets/images/oxt_icon.png', tag: 'ETH', raw: 55); + static const paxg = CryptoCurrency(title: 'PAXG', iconPath: 'assets/images/paxg_icon.png', tag: 'ETH', raw: 56); + static const pivx = CryptoCurrency(title: 'PIVX', iconPath: 'assets/images/pivx_icon.png', raw: 57); + static const rune = CryptoCurrency(title: 'RUNE', iconPath: 'assets/images/rune_icon.png', raw: 58); + static const rvn = CryptoCurrency(title: 'RVN', iconPath: 'assets/images/rvn_icon.png', raw: 59); + static const scrt = CryptoCurrency(title: 'SCRT', iconPath: 'assets/images/scrt_icon.png', raw: 60); + static const uni = CryptoCurrency(title: 'UNI', iconPath: 'assets/images/uni_icon.png', tag: 'ETH', raw: 61); + static const stx = CryptoCurrency(title: 'STX', iconPath: 'assets/images/stx_icon.png', raw: 62); + + + static CryptoCurrency deserialize({required int raw}) { switch (raw) { case 0: @@ -196,6 +233,40 @@ class CryptoCurrency extends EnumerableItem with Serializable { return CryptoCurrency.zen; case 45: return CryptoCurrency.xvg; + case 46: + return CryptoCurrency.usdcpoly; + case 47: + return CryptoCurrency.dcr; + case 48: + return CryptoCurrency.husd; + case 49: + return CryptoCurrency.kmd; + case 50: + return CryptoCurrency.mana; + case 51: + return CryptoCurrency.maticpoly; + case 52: + return CryptoCurrency.matic; + case 53: + return CryptoCurrency.mkr; + case 54: + return CryptoCurrency.near; + case 55: + return CryptoCurrency.oxt; + case 56: + return CryptoCurrency.paxg; + case 57: + return CryptoCurrency.pivx; + case 58: + return CryptoCurrency.rune; + case 59: + return CryptoCurrency.rvn; + case 60: + return CryptoCurrency.scrt; + case 61: + return CryptoCurrency.uni; + case 62: + return CryptoCurrency.stx; default: throw Exception('Unexpected token: $raw for CryptoCurrency deserialize'); } @@ -295,6 +366,40 @@ class CryptoCurrency extends EnumerableItem with Serializable { return CryptoCurrency.zen; case 'xvg': return CryptoCurrency.xvg; + case 'usdcpoly': + return CryptoCurrency.usdcpoly; + case 'dcr': + return CryptoCurrency.dcr; + case 'husd': + return CryptoCurrency.husd; + case 'kmd': + return CryptoCurrency.kmd; + case 'mana': + return CryptoCurrency.mana; + case 'maticpoly': + return CryptoCurrency.maticpoly; + case 'matic': + return CryptoCurrency.matic; + case 'mkr': + return CryptoCurrency.mkr; + case 'near': + return CryptoCurrency.near; + case 'oxt': + return CryptoCurrency.oxt; + case 'paxg': + return CryptoCurrency.paxg; + case 'pivx': + return CryptoCurrency.pivx; + case 'rune': + return CryptoCurrency.rune; + case 'rvn': + return CryptoCurrency.rvn; + case 'scrt': + return CryptoCurrency.scrt; + case 'uni': + return CryptoCurrency.uni; + case 'stx': + return CryptoCurrency.stx; default: throw Exception('Unexpected token: $raw for CryptoCurrency fromString'); } diff --git a/cw_core/lib/monero_transaction_priority.dart b/cw_core/lib/monero_transaction_priority.dart index 1aca5dd8c..cca887398 100644 --- a/cw_core/lib/monero_transaction_priority.dart +++ b/cw_core/lib/monero_transaction_priority.dart @@ -9,13 +9,13 @@ class MoneroTransactionPriority extends TransactionPriority { static const all = [ MoneroTransactionPriority.slow, - MoneroTransactionPriority.regular, + MoneroTransactionPriority.automatic, MoneroTransactionPriority.medium, MoneroTransactionPriority.fast, MoneroTransactionPriority.fastest ]; static const slow = MoneroTransactionPriority(title: 'Slow', raw: 0); - static const regular = MoneroTransactionPriority(title: 'Regular', raw: 1); + static const automatic = MoneroTransactionPriority(title: 'Automatic', raw: 1); static const medium = MoneroTransactionPriority(title: 'Medium', raw: 2); static const fast = MoneroTransactionPriority(title: 'Fast', raw: 3); static const fastest = MoneroTransactionPriority(title: 'Fastest', raw: 4); @@ -29,7 +29,7 @@ class MoneroTransactionPriority extends TransactionPriority { case WalletType.bitcoin: return [ MoneroTransactionPriority.slow, - MoneroTransactionPriority.regular, + MoneroTransactionPriority.automatic, MoneroTransactionPriority.fast ]; default: @@ -42,7 +42,7 @@ class MoneroTransactionPriority extends TransactionPriority { case 0: return slow; case 1: - return regular; + return automatic; case 2: return medium; case 3: @@ -59,8 +59,8 @@ class MoneroTransactionPriority extends TransactionPriority { switch (this) { case MoneroTransactionPriority.slow: return 'Slow'; // S.current.transaction_priority_slow; - case MoneroTransactionPriority.regular: - return 'Regular'; // S.current.transaction_priority_regular; + case MoneroTransactionPriority.automatic: + return 'Automatic'; // S.current.transaction_priority_regular; case MoneroTransactionPriority.medium: return 'Medium'; // S.current.transaction_priority_medium; case MoneroTransactionPriority.fast: diff --git a/cw_core/lib/unspent_coins_info.dart b/cw_core/lib/unspent_coins_info.dart index 3efbe26c5..420517939 100644 --- a/cw_core/lib/unspent_coins_info.dart +++ b/cw_core/lib/unspent_coins_info.dart @@ -9,7 +9,7 @@ class UnspentCoinsInfo extends HiveObject { required this.hash, required this.isFrozen, required this.isSending, - required this.note}); + required this.noteRaw}); static const typeId = 9; static const boxName = 'Unspent'; @@ -28,5 +28,9 @@ class UnspentCoinsInfo extends HiveObject { bool isSending; @HiveField(4) - String note; + String? noteRaw; + + String get note => noteRaw ?? ''; + + set note(String value) => noteRaw = value; } \ No newline at end of file diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 130b8ff5f..66ed1e932 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -64,15 +64,15 @@ class WalletInfo extends HiveObject { Map? addresses; @HiveField(11) - String yatEid; + String? yatEid; @HiveField(12) - String yatLastUsedAddressRaw; + String? yatLastUsedAddressRaw; @HiveField(13) bool? showIntroCakePayCard; - String get yatLastUsedAddress => yatLastUsedAddressRaw; + String get yatLastUsedAddress => yatLastUsedAddressRaw ?? ''; set yatLastUsedAddress(String address) { yatLastUsedAddressRaw = address; diff --git a/cw_haven/lib/haven_wallet.dart b/cw_haven/lib/haven_wallet.dart index 14ff2f88e..4b4748c1a 100644 --- a/cw_haven/lib/haven_wallet.dart +++ b/cw_haven/lib/haven_wallet.dart @@ -189,7 +189,9 @@ abstract class HavenWalletBase extends WalletBase ''; - // FIX-ME: AmountConverter @override - String get amountFormatted => ''; - // AmountConverter.amountIntToString( - // cryptoCurrency, pendingTransactionDescription.amount); + String get amountFormatted => AmountConverter.amountIntToString( + cryptoCurrency, pendingTransactionDescription.amount); - // FIX-ME: AmountConverter @override - String get feeFormatted => ''; - // AmountConverter.amountIntToString( - // cryptoCurrency, pendingTransactionDescription.fee); + String get feeFormatted => AmountConverter.amountIntToString( + cryptoCurrency, pendingTransactionDescription.fee); @override Future commit() async { diff --git a/cw_monero/lib/api/transaction_history.dart b/cw_monero/lib/api/transaction_history.dart index e9e161f42..0fc507500 100644 --- a/cw_monero/lib/api/transaction_history.dart +++ b/cw_monero/lib/api/transaction_history.dart @@ -182,7 +182,7 @@ void commitTransaction({required Pointer transactionPoint PendingTransactionDescription _createTransactionSync(Map args) { final address = args['address'] as String; final paymentId = args['paymentId'] as String; - final amount = args['amount'] as String; + final amount = args['amount'] as String?; final priorityRaw = args['priorityRaw'] as int; final accountIndex = args['accountIndex'] as int; diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 86a0358f0..0cbd69951 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -244,7 +244,7 @@ abstract class MoneroWalletBase extends WalletBase pendingTransactionDescription.txKey; - // FIX-ME: AmountConverter @override - String get amountFormatted => ''; - // AmountConverter.amountIntToString( - // CryptoCurrency.xmr, pendingTransactionDescription.amount); + String get amountFormatted => AmountConverter.amountIntToString( + CryptoCurrency.xmr, pendingTransactionDescription.amount); - // FIX-ME: AmountConverter @override - String get feeFormatted => ''; - // AmountConverter.amountIntToString( - // CryptoCurrency.xmr, pendingTransactionDescription.fee); + String get feeFormatted => AmountConverter.amountIntToString( + CryptoCurrency.xmr, pendingTransactionDescription.fee); @override Future commit() async { diff --git a/ios/Podfile.lock b/ios/Podfile.lock index ec8e4f2e3..f8f120bd6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -119,7 +119,7 @@ PODS: - SDWebImage (5.9.1): - SDWebImage/Core (= 5.9.1) - SDWebImage/Core (5.9.1) - - share (0.0.1): + - share_plus (0.0.1): - Flutter - shared_preferences_ios (0.0.1): - Flutter @@ -153,7 +153,7 @@ DEPENDENCIES: - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - platform_device_id (from `.symlinks/plugins/platform_device_id/ios`) - - share (from `.symlinks/plugins/share/ios`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - UnstoppableDomainsResolution (~> 4.0.0) @@ -206,8 +206,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/permission_handler_apple/ios" platform_device_id: :path: ".symlinks/plugins/platform_device_id/ios" - share: - :path: ".symlinks/plugins/share/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" shared_preferences_ios: :path: ".symlinks/plugins/shared_preferences_ios/ios" uni_links: @@ -241,7 +241,7 @@ SPEC CHECKSUMS: platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SDWebImage: a990c053fff71e388a10f3357edb0be17929c9c5 - share: 0b2c3e82132f5888bccca3351c504d0003b3b410 + share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad SwiftProtobuf: c3c12645230d9b09c72267e0de89468c5543bd86 SwiftyGif: e466e86c660d343357ab944a819a101c4127cb40 diff --git a/lib/anypay/any_pay_payment_instruction.dart b/lib/anypay/any_pay_payment_instruction.dart index 6477e6649..5181cad75 100644 --- a/lib/anypay/any_pay_payment_instruction.dart +++ b/lib/anypay/any_pay_payment_instruction.dart @@ -17,8 +17,8 @@ class AnyPayPaymentInstruction { return AnyPayPaymentInstruction( type: obj['type'] as String, requiredFeeRate: obj['requiredFeeRate'] as int, - txKey: obj['tx_key'] as bool, - txHash: obj['tx_hash'] as bool, + txKey: obj['tx_key'] as bool? ?? false, + txHash: obj['tx_hash'] as bool? ?? false, outputs: outputs); } diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 9d5484b79..ff6fd7531 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -112,6 +112,10 @@ class CWBitcoin extends Bitcoin { int formatterStringDoubleToBitcoinAmount(String amount) => stringDoubleToBitcoinAmount(amount); + @override + String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate) + => (priority as BitcoinTransactionPriority).labelWithRate(rate); + @override List getUnspents(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index e3c4ed334..9d7b0e3b5 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -17,39 +17,25 @@ class AddressValidator extends TextValidator { case CryptoCurrency.ada: return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$' '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$'; - case CryptoCurrency.ape: - return '0x[0-9a-zA-Z]'; - case CryptoCurrency.avaxc: - return '0x[0-9a-zA-Z]'; - case CryptoCurrency.bch: - return '[0-9a-zA-Z]'; - case CryptoCurrency.bnb: - return '[0-9a-zA-Z]'; case CryptoCurrency.btc: return '^1[0-9a-zA-Z]{32}\$|^1[0-9a-zA-Z]{33}\$|^3[0-9a-zA-Z]{32}\$' '|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{39}\$|^bc1[0-9a-zA-Z]{59}\$'; - case CryptoCurrency.dai: - return '[0-9a-zA-Z]'; - case CryptoCurrency.dash: - return '[0-9a-zA-Z]'; - case CryptoCurrency.eos: - return '[0-9a-zA-Z]'; - case CryptoCurrency.eth: - return '0x[0-9a-zA-Z]'; - case CryptoCurrency.ltc: - return '[0-9a-zA-Z]'; case CryptoCurrency.nano: return '[0-9a-zA-Z_]'; - case CryptoCurrency.trx: - return '[0-9a-zA-Z]'; case CryptoCurrency.usdc: + case CryptoCurrency.usdcpoly: + case CryptoCurrency.husd: + case CryptoCurrency.ape: + case CryptoCurrency.avaxc: + case CryptoCurrency.eth: + case CryptoCurrency.mana: + case CryptoCurrency.matic: + case CryptoCurrency.maticpoly: + case CryptoCurrency.mkr: + case CryptoCurrency.oxt: + case CryptoCurrency.paxg: + case CryptoCurrency.uni: return '0x[0-9a-zA-Z]'; - case CryptoCurrency.usdt: - return '[0-9a-zA-Z]'; - case CryptoCurrency.usdterc20: - return '[0-9a-zA-Z]'; - case CryptoCurrency.xlm: - return '[0-9a-zA-Z]'; case CryptoCurrency.xrp: return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$'; case CryptoCurrency.xhv: @@ -67,6 +53,16 @@ class AddressValidator extends TextValidator { case CryptoCurrency.xnok: case CryptoCurrency.xnzd: case CryptoCurrency.xusd: + case CryptoCurrency.usdt: + case CryptoCurrency.usdterc20: + case CryptoCurrency.xlm: + case CryptoCurrency.trx: + case CryptoCurrency.dai: + case CryptoCurrency.dash: + case CryptoCurrency.eos: + case CryptoCurrency.ltc: + case CryptoCurrency.bch: + case CryptoCurrency.bnb: return '[0-9a-zA-Z]'; case CryptoCurrency.hbar: return '[0-9a-zA-Z.]'; @@ -74,6 +70,22 @@ class AddressValidator extends TextValidator { return '^zs[0-9a-zA-Z]{75}'; case CryptoCurrency.zec: return '^t1[0-9a-zA-Z]{33}\$|^t3[0-9a-zA-Z]{33}\$'; + case CryptoCurrency.dcr: + return 'D[ksecS]([0-9a-zA-Z])+'; + case CryptoCurrency.rvn: + return '[Rr]([1-9a-km-zA-HJ-NP-Z]){33}'; + case CryptoCurrency.near: + return '[0-9a-f]{64}'; + case CryptoCurrency.rune: + return 'thor1[0-9a-z]{38}'; + case CryptoCurrency.scrt: + return 'secret1[0-9a-z]{38}'; + case CryptoCurrency.stx: + return 'S[MP][0-9a-zA-Z]+'; + case CryptoCurrency.kmd: + return 'R[0-9a-zA-Z]{33}'; + case CryptoCurrency.pivx: + return 'D([1-9a-km-zA-HJ-NP-Z]){33}'; default: return '[0-9a-zA-Z]'; } @@ -160,6 +172,30 @@ class AddressValidator extends TextValidator { return null; case CryptoCurrency.zec: return null; + case CryptoCurrency.kmd: + case CryptoCurrency.pivx: + case CryptoCurrency.rvn: + return [34]; + case CryptoCurrency.dcr: + return [35]; + case CryptoCurrency.stx: + return [40, 41, 42]; + case CryptoCurrency.usdcpoly: + case CryptoCurrency.husd: + case CryptoCurrency.mana: + case CryptoCurrency.matic: + case CryptoCurrency.maticpoly: + case CryptoCurrency.mkr: + case CryptoCurrency.oxt: + case CryptoCurrency.paxg: + case CryptoCurrency.uni: + return [42]; + case CryptoCurrency.rune: + return [43]; + case CryptoCurrency.scrt: + return [45]; + case CryptoCurrency.near: + return [64]; default: return []; } diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 9b2b2c7d4..999b67120 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -16,6 +16,7 @@ import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/wallet_types.g.dart'; +import 'package:cake_backup/backup.dart' as cake_backup; class BackupService { BackupService(this._flutterSecureStorage, this._walletInfoSource, @@ -23,9 +24,10 @@ class BackupService { : _cipher = Cryptography.instance.chacha20Poly1305Aead(), _correctWallets = []; - static const currentVersion = _v1; + static const currentVersion = _v2; static const _v1 = 1; + static const _v2 = 2; final Cipher _cipher; final FlutterSecureStorage _flutterSecureStorage; @@ -37,13 +39,16 @@ class BackupService { Future importBackup(Uint8List data, String password, {String nonce = secrets.backupSalt}) async { final version = getVersion(data); - final backupBytes = data.toList()..removeAt(0); - final backupData = Uint8List.fromList(backupBytes); switch (version) { case _v1: + final backupBytes = data.toList()..removeAt(0); + final backupData = Uint8List.fromList(backupBytes); await _importBackupV1(backupData, password, nonce: nonce); break; + case _v2: + await _importBackupV2(data, password); + break; default: break; } @@ -54,20 +59,26 @@ class BackupService { switch (version) { case _v1: return await _exportBackupV1(password, nonce: nonce); + case _v2: + return await _exportBackupV2(password); default: throw Exception('Incorrect version: $version for exportBackup'); } } + @Deprecated('Use v2 instead') Future _exportBackupV1(String password, - {String nonce = secrets.backupSalt}) async { + {String nonce = secrets.backupSalt}) async + => throw Exception('Deprecated. Export for backups v1 is deprecated. Please use export v2.'); + + Future _exportBackupV2(String password) async { final zipEncoder = ZipFileEncoder(); final appDir = await getApplicationDocumentsDirectory(); final now = DateTime.now(); final tmpDir = Directory('${appDir.path}/~_BACKUP_TMP'); final archivePath = '${tmpDir.path}/backup_${now.toString()}.zip'; final fileEntities = appDir.listSync(recursive: false); - final keychainDump = await _exportKeychainDump(password, nonce: nonce); + final keychainDump = await _exportKeychainDumpV2(password); final preferencesDump = await _exportPreferencesJSON(); final preferencesDumpFile = File('${tmpDir.path}/~_preferences_dump_TMP'); final keychainDumpFile = File('${tmpDir.path}/~_keychain_dump_TMP'); @@ -98,15 +109,13 @@ class BackupService { final content = File(archivePath).readAsBytesSync(); tmpDir.deleteSync(recursive: true); - final encryptedData = await _encrypt(content, password, nonce); - - return setVersion(encryptedData, currentVersion); + return await _encryptV2(content, password); } Future _importBackupV1(Uint8List data, String password, {required String nonce}) async { final appDir = await getApplicationDocumentsDirectory(); - final decryptedData = await _decrypt(data, password, nonce); + final decryptedData = await _decryptV1(data, password, nonce); final zip = ZipDecoder().decodeBytes(decryptedData); zip.files.forEach((file) { @@ -123,7 +132,30 @@ class BackupService { }); await _verifyWallets(); - await _importKeychainDump(password, nonce: nonce); + await _importKeychainDumpV1(password, nonce: nonce); + await _importPreferencesDump(); + } + + Future _importBackupV2(Uint8List data, String password) async { + final appDir = await getApplicationDocumentsDirectory(); + final decryptedData = await _decryptV2(data, password); + final zip = ZipDecoder().decodeBytes(decryptedData); + + zip.files.forEach((file) { + final filename = file.name; + + if (file.isFile) { + final content = file.content as List; + File('${appDir.path}/' + filename) + ..createSync(recursive: true) + ..writeAsBytesSync(content); + } else { + Directory('${appDir.path}/' + filename)..create(recursive: true); + } + }); + + await _verifyWallets(); + await _importKeychainDumpV2(password); await _importPreferencesDump(); } @@ -258,12 +290,12 @@ class BackupService { await preferencesFile.delete(); } - Future _importKeychainDump(String password, + Future _importKeychainDumpV1(String password, {required String nonce, String keychainSalt = secrets.backupKeychainSalt}) async { final appDir = await getApplicationDocumentsDirectory(); final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); - final decryptedKeychainDumpFileData = await _decrypt( + final decryptedKeychainDumpFileData = await _decryptV1( keychainDumpFile.readAsBytesSync(), '$keychainSalt$password', nonce); final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData)) as Map; @@ -288,6 +320,35 @@ class BackupService { keychainDumpFile.deleteSync(); } + Future _importKeychainDumpV2(String password, + {String keychainSalt = secrets.backupKeychainSalt}) async { + final appDir = await getApplicationDocumentsDirectory(); + final keychainDumpFile = File('${appDir.path}/~_keychain_dump'); + final decryptedKeychainDumpFileData = await _decryptV2( + keychainDumpFile.readAsBytesSync(), '$keychainSalt$password'); + final keychainJSON = json.decode(utf8.decode(decryptedKeychainDumpFileData)) + as Map; + final keychainWalletsInfo = keychainJSON['wallets'] as List; + final decodedPin = keychainJSON['pin'] as String; + final pinCodeKey = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); + final backupPasswordKey = + generateStoreKeyFor(key: SecretStoreKey.backupPassword); + final backupPassword = keychainJSON[backupPasswordKey] as String; + + await _flutterSecureStorage.write( + key: backupPasswordKey, value: backupPassword); + + keychainWalletsInfo.forEach((dynamic rawInfo) async { + final info = rawInfo as Map; + await importWalletKeychainInfo(info); + }); + + await _flutterSecureStorage.write( + key: pinCodeKey, value: encodedPinCode(pin: decodedPin)); + + keychainDumpFile.deleteSync(); + } + Future importWalletKeychainInfo(Map info) async { final name = info['name'] as String; final password = info['password'] as String; @@ -295,9 +356,14 @@ class BackupService { await _keyService.saveWalletPassword(walletName: name, password: password); } - Future _exportKeychainDump(String password, + @Deprecated('Use v2 instead') + Future _exportKeychainDumpV1(String password, {required String nonce, - String keychainSalt = secrets.backupKeychainSalt}) async { + String keychainSalt = secrets.backupKeychainSalt}) async + => throw Exception('Deprecated'); + + Future _exportKeychainDumpV2(String password, + {String keychainSalt = secrets.backupKeychainSalt}) async { final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); final encodedPin = await _flutterSecureStorage.read(key: key); final decodedPin = decodedPinCode(pin: encodedPin!); @@ -319,49 +385,48 @@ class BackupService { 'wallets': wallets, backupPasswordKey: backupPassword })); - final encrypted = await _encrypt( - Uint8List.fromList(data), '$keychainSalt$password', nonce); + final encrypted = await _encryptV2( + Uint8List.fromList(data), '$keychainSalt$password'); return encrypted; } Future _exportPreferencesJSON() async { - // FIX-ME: Force unwrap final preferences = { PreferencesKey.currentWalletName: - _sharedPreferences.getString(PreferencesKey.currentWalletName)!, + _sharedPreferences.getString(PreferencesKey.currentWalletName), PreferencesKey.currentNodeIdKey: - _sharedPreferences.getInt(PreferencesKey.currentNodeIdKey)!, + _sharedPreferences.getInt(PreferencesKey.currentNodeIdKey), PreferencesKey.currentBalanceDisplayModeKey: _sharedPreferences - .getInt(PreferencesKey.currentBalanceDisplayModeKey)!, + .getInt(PreferencesKey.currentBalanceDisplayModeKey), PreferencesKey.currentWalletType: - _sharedPreferences.getInt(PreferencesKey.currentWalletType)!, + _sharedPreferences.getInt(PreferencesKey.currentWalletType), PreferencesKey.currentFiatCurrencyKey: - _sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!, + _sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey), PreferencesKey.shouldSaveRecipientAddressKey: _sharedPreferences - .getBool(PreferencesKey.shouldSaveRecipientAddressKey)!, + .getBool(PreferencesKey.shouldSaveRecipientAddressKey), PreferencesKey.isDarkThemeLegacy: - _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy)!, + _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy), PreferencesKey.currentPinLength: - _sharedPreferences.getInt(PreferencesKey.currentPinLength)!, + _sharedPreferences.getInt(PreferencesKey.currentPinLength), PreferencesKey.currentTransactionPriorityKeyLegacy: _sharedPreferences - .getInt(PreferencesKey.currentTransactionPriorityKeyLegacy)!, + .getInt(PreferencesKey.currentTransactionPriorityKeyLegacy), PreferencesKey.allowBiometricalAuthenticationKey: _sharedPreferences - .getBool(PreferencesKey.allowBiometricalAuthenticationKey)!, + .getBool(PreferencesKey.allowBiometricalAuthenticationKey), PreferencesKey.currentBitcoinElectrumSererIdKey: _sharedPreferences - .getInt(PreferencesKey.currentBitcoinElectrumSererIdKey)!, + .getInt(PreferencesKey.currentBitcoinElectrumSererIdKey), PreferencesKey.currentLanguageCode: - _sharedPreferences.getString(PreferencesKey.currentLanguageCode)!, + _sharedPreferences.getString(PreferencesKey.currentLanguageCode), PreferencesKey.displayActionListModeKey: - _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey)!, + _sharedPreferences.getInt(PreferencesKey.displayActionListModeKey), PreferencesKey.currentTheme: - _sharedPreferences.getInt(PreferencesKey.currentTheme)!, + _sharedPreferences.getInt(PreferencesKey.currentTheme), PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences - .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion)!, + .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion), PreferencesKey.bitcoinTransactionPriority: - _sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority)!, + _sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority), PreferencesKey.moneroTransactionPriority: - _sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!, + _sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority), }; return json.encode(preferences); @@ -374,16 +439,12 @@ class BackupService { return Uint8List.fromList(bytes); } - Future _encrypt( - Uint8List data, String secretKeySource, String nonceBase64) async { - final secretKeyHash = await Cryptography.instance.sha256().hash(utf8.encode(secretKeySource)); - final secretKey = SecretKey(secretKeyHash.bytes); - final nonce = base64.decode(nonceBase64).toList(); - final box = await _cipher.encrypt(data.toList(), secretKey: secretKey, nonce: nonce); - return Uint8List.fromList(box.cipherText); - } + @Deprecated('Use v2 instead') + Future _encryptV1( + Uint8List data, String secretKeySource, String nonceBase64) async + => throw Exception('Deprecated'); - Future _decrypt( + Future _decryptV1( Uint8List data, String secretKeySource, String nonceBase64, {int macLength = 16}) async { final secretKeyHash = await Cryptography.instance.sha256().hash(utf8.encode(secretKeySource)); final secretKey = SecretKey(secretKeyHash.bytes); @@ -395,4 +456,12 @@ class BackupService { final plainData = await _cipher.decrypt(box, secretKey: secretKey); return Uint8List.fromList(plainData); } + + Future _encryptV2( + Uint8List data, String passphrase) async + => cake_backup.encrypt(passphrase, data, version: _v2); + + Future _decryptV2( + Uint8List data, String passphrase) async + => cake_backup.decrypt(passphrase, data); } diff --git a/lib/di.dart b/lib/di.dart index 5a2eb7285..7ce2ff6d9 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/entities/wake_lock.dart'; import 'package:cake_wallet/ionia/ionia_anypay.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; +import 'package:cake_wallet/src/screens/buy/onramper_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; @@ -419,16 +420,16 @@ Future setup( getIt.get()));*/ getIt.registerFactoryParam( - (AccountListItem account, _) => MoneroAccountEditOrCreateViewModel( + AccountListItem?, void>( + (AccountListItem? account, _) => MoneroAccountEditOrCreateViewModel( monero!.getAccountList(getIt.get().wallet!), haven?.getAccountList(getIt.get().wallet!), wallet: getIt.get().wallet!, accountListItem: account)); - getIt.registerFactoryParam( - (AccountListItem account, _) => MoneroAccountEditOrCreatePage( + (AccountListItem? account, _) => MoneroAccountEditOrCreatePage( moneroAccountCreationViewModel: getIt.get(param1: account))); @@ -482,6 +483,10 @@ Future setup( getIt.registerFactory( () => NodeCreateOrEditPage(getIt.get())); + getIt.registerFactory(() => OnRamperPage( + settingsStore: getIt.get().settingsStore, + wallet: getIt.get().wallet!)); + getIt.registerFactory(() => ExchangeViewModel( getIt.get().wallet!, _tradesSource, @@ -489,6 +494,7 @@ Future setup( getIt.get(), getIt.get().settingsStore, getIt.get(), + getIt.get(), )); getIt.registerFactory(() => ExchangeTradeViewModel( @@ -587,6 +593,9 @@ Future setup( getIt.registerFactory(() => BackupPage(getIt.get())); + getIt.registerFactory( + () => EditBackupPasswordViewModel(getIt.get(), getIt.get())); + getIt.registerFactory( () => EditBackupPasswordPage(getIt.get())); diff --git a/lib/entities/qr_scanner.dart b/lib/entities/qr_scanner.dart index 2a8e875ed..8c23f6ea1 100644 --- a/lib/entities/qr_scanner.dart +++ b/lib/entities/qr_scanner.dart @@ -1,17 +1,15 @@ -// import 'package:barcode_scan/barcode_scan.dart'; +import 'package:barcode_scan2/barcode_scan2.dart'; var isQrScannerShown = false; Future presentQRScanner() async { isQrScannerShown = true; - // FIX-ME: BarcodeScanner - throw Exception('Unimplemented'); - // try { - // final result = await BarcodeScanner.scan(); - // isQrScannerShown = false; - // return result.rawContent; - // } catch (e) { - // isQrScannerShown = false; - // rethrow; - // } + try { + final result = await BarcodeScanner.scan(); + isQrScannerShown = false; + return result.rawContent; + } catch (e) { + isQrScannerShown = false; + rethrow; + } } diff --git a/lib/entities/template.dart b/lib/entities/template.dart index e05c423fd..c26e3a501 100644 --- a/lib/entities/template.dart +++ b/lib/entities/template.dart @@ -5,36 +5,50 @@ part 'template.g.dart'; @HiveType(typeId: Template.typeId) class Template extends HiveObject { Template({ - required this.name, - required this.isCurrencySelected, - required this.address, - required this.cryptoCurrency, - required this.amount, - required this.fiatCurrency, - required this.amountFiat}); + required this.nameRaw, + required this.isCurrencySelectedRaw, + required this.addressRaw, + required this.cryptoCurrencyRaw, + required this.amountRaw, + required this.fiatCurrencyRaw, + required this.amountFiatRaw}); static const typeId = 6; static const boxName = 'Template'; @HiveField(0) - String name; + String? nameRaw; @HiveField(1) - String address; + String? addressRaw; @HiveField(2) - String cryptoCurrency; + String? cryptoCurrencyRaw; @HiveField(3) - String amount; + String? amountRaw; @HiveField(4) - String fiatCurrency; + String? fiatCurrencyRaw; @HiveField(5) - bool isCurrencySelected; + bool? isCurrencySelectedRaw; @HiveField(6) - String amountFiat; + String? amountFiatRaw; + + bool get isCurrencySelected => isCurrencySelectedRaw ?? false; + + String get fiatCurrency => fiatCurrencyRaw ?? ''; + + String get amountFiat => amountFiatRaw ?? ''; + + String get name => nameRaw ?? ''; + + String get address => addressRaw ?? ''; + + String get cryptoCurrency => cryptoCurrencyRaw ?? ''; + + String get amount => amountRaw ?? ''; } diff --git a/lib/exchange/exchange_template.dart b/lib/exchange/exchange_template.dart index 93d8b851e..26e1fa761 100644 --- a/lib/exchange/exchange_template.dart +++ b/lib/exchange/exchange_template.dart @@ -5,32 +5,44 @@ part 'exchange_template.g.dart'; @HiveType(typeId: ExchangeTemplate.typeId) class ExchangeTemplate extends HiveObject { ExchangeTemplate({ - required this.amount, - required this.depositCurrency, - required this.receiveCurrency, - required this.provider, - required this.depositAddress, - required this.receiveAddress + required this.amountRaw, + required this.depositCurrencyRaw, + required this.receiveCurrencyRaw, + required this.providerRaw, + required this.depositAddressRaw, + required this.receiveAddressRaw }); static const typeId = 7; static const boxName = 'ExchangeTemplate'; @HiveField(0) - String amount; + String? amountRaw; @HiveField(1) - String depositCurrency; + String? depositCurrencyRaw; @HiveField(2) - String receiveCurrency; + String? receiveCurrencyRaw; @HiveField(3) - String provider; + String? providerRaw; @HiveField(4) - String depositAddress; + String? depositAddressRaw; @HiveField(5) - String receiveAddress; + String? receiveAddressRaw; + + String get amount => amountRaw ?? ''; + + String get depositCurrency => depositCurrencyRaw ?? ''; + + String get receiveCurrency => receiveCurrencyRaw ?? ''; + + String get provider => providerRaw ?? ''; + + String get depositAddress => depositAddressRaw ?? ''; + + String get receiveAddress => receiveAddressRaw ?? ''; } \ No newline at end of file diff --git a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart index c90b89391..1df4fef87 100644 --- a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart +++ b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'package:cake_wallet/core/amount_converter.dart'; +import 'package:cw_core/amount_converter.dart'; import 'package:hive/hive.dart'; import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; import 'package:flutter/foundation.dart'; diff --git a/lib/exchange/sideshift/sideshift_exchange_provider.dart b/lib/exchange/sideshift/sideshift_exchange_provider.dart index ff489aa83..a732f7ef2 100644 --- a/lib/exchange/sideshift/sideshift_exchange_provider.dart +++ b/lib/exchange/sideshift/sideshift_exchange_provider.dart @@ -16,16 +16,7 @@ import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; class SideShiftExchangeProvider extends ExchangeProvider { - SideShiftExchangeProvider() - : super( - pairList: CryptoCurrency.all - .where((i) => i != CryptoCurrency.xhv) - .map((i) => CryptoCurrency.all - .where((i) => i != CryptoCurrency.xhv) - .map((k) => ExchangePair(from: i, to: k, reverse: true)) - .where((c) => c != null)) - .expand((i) => i) - .toList()); + SideShiftExchangeProvider() : super(pairList: _supportedPairs()); static const affiliateId = secrets.sideShiftAffiliateId; static const apiBaseUrl = 'https://sideshift.ai/api'; @@ -34,6 +25,35 @@ class SideShiftExchangeProvider extends ExchangeProvider { static const quotePath = '/v1/quotes'; static const permissionPath = '/v1/permissions'; + static const List _notSupported = [ + CryptoCurrency.xhv, + CryptoCurrency.dcr, + CryptoCurrency.husd, + CryptoCurrency.kmd, + CryptoCurrency.mkr, + CryptoCurrency.near, + CryptoCurrency.oxt, + CryptoCurrency.paxg, + CryptoCurrency.pivx, + CryptoCurrency.rune, + CryptoCurrency.rvn, + CryptoCurrency.scrt, + CryptoCurrency.stx, + ]; + + static List _supportedPairs() { + final supportedCurrencies = CryptoCurrency.all + .where((element) => !_notSupported.contains(element)) + .toList(); + + return supportedCurrencies + .map((i) => supportedCurrencies + .map((k) => ExchangePair(from: i, to: k, reverse: true)) + .where((c) => c != null)) + .expand((i) => i) + .toList(); + } + @override ExchangeProviderDescription get description => ExchangeProviderDescription.sideShift; @@ -270,6 +290,14 @@ class SideShiftExchangeProvider extends ExchangeProvider { return currency.tag!.toLowerCase(); case CryptoCurrency.usdterc20: return 'usdtErc20'; + case CryptoCurrency.usdttrc20: + return 'usdtTrc20'; + case CryptoCurrency.usdcpoly: + return 'usdcpolygon'; + case CryptoCurrency.usdcsol: + return 'usdcsol'; + case CryptoCurrency.maticpoly: + return 'polygon'; default: return currency.title.toLowerCase(); } diff --git a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart index b7e6d9bf1..c6e1baeea 100644 --- a/lib/exchange/simpleswap/simpleswap_exchange_provider.dart +++ b/lib/exchange/simpleswap/simpleswap_exchange_provider.dart @@ -17,8 +17,11 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { SimpleSwapExchangeProvider() : super( pairList: CryptoCurrency.all - .map((i) => - CryptoCurrency.all.map((k) => ExchangePair(from: i, to: k, reverse: true)).where((c) => c != null)) + .where((i) => i != CryptoCurrency.zaddr) + .map((i) => CryptoCurrency.all + .where((i) => i != CryptoCurrency.zaddr) + .map((k) => ExchangePair(from: i, to: k, reverse: true)) + .where((c) => c != null)) .expand((i) => i) .toList()); @@ -32,7 +35,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { @override ExchangeProviderDescription get description => ExchangeProviderDescription.simpleSwap; - + @override Future calculateAmount( {required CryptoCurrency from, @@ -56,9 +59,8 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { final uri = Uri.https(apiAuthority, getEstimatePath, params); final response = await get(uri); - if (response.body == null) return 0.00; + if (response.body == null || response.body == "null") return 0.00; final data = json.decode(response.body) as String; - return double.parse(data); } catch (_) { return 0.00; @@ -76,7 +78,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { @override Future createTrade({required TradeRequest request, required bool isFixedRateMode}) async { final _request = request as SimpleSwapRequest; - final headers = { + final headers = { 'Content-Type': 'application/json'}; final params = { 'api_key': apiKey, @@ -108,7 +110,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { final id = responseJSON['id'] as String; final inputAddress = responseJSON['address_from'] as String; final settleAddress = responseJSON['user_refund_address'] as String; - + final extraId = responseJSON['extra_id_from'] as String?; return Trade( id: id, provider: description, @@ -116,6 +118,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { to: _request.to, inputAddress: inputAddress, refundAddress: settleAddress, + extraId: extraId, state: TradeState.created, amount: _request.amount, createdAt: DateTime.now(), @@ -151,8 +154,8 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { } final responseJSON = json.decode(response.body) as Map; - final min = responseJSON['min'] != null ? double.tryParse(responseJSON['min'] as String) : null; - final max = responseJSON['max'] != null ? double.parse(responseJSON['max'] as String) : null; + final min = double.tryParse(responseJSON['min'] as String? ?? ''); + final max = double.tryParse(responseJSON['max'] as String? ?? ''); return Limits(min: min, max: max); } @@ -185,6 +188,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { final to = CryptoCurrency.fromString(toCurrency); final inputAddress = responseJSON['address_from'] as String; final expectedSendAmount = responseJSON['expected_amount'].toString(); + final extraId = responseJSON['extra_id_from'] as String?; final status = responseJSON['status'] as String; final state = TradeState.deserialize(raw: status); @@ -192,6 +196,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { id: id, from: from, to: to, + extraId: extraId, provider: description, inputAddress: inputAddress, amount: expectedSendAmount, @@ -217,7 +222,13 @@ class SimpleSwapExchangeProvider extends ExchangeProvider { case CryptoCurrency.bnb: return currency.tag!.toLowerCase(); case CryptoCurrency.usdterc20: - return 'usdterc'; + return 'usdterc20'; + case CryptoCurrency.usdttrc20: + return 'usdttrc20'; + case CryptoCurrency.usdcpoly: + return 'usdcpoly'; + case CryptoCurrency.usdcsol: + return 'usdcspl'; default: return currency.title.toLowerCase(); } diff --git a/lib/haven/cw_haven.dart b/lib/haven/cw_haven.dart index d7aab48d0..6e805b0e3 100644 --- a/lib/haven/cw_haven.dart +++ b/lib/haven/cw_haven.dart @@ -166,7 +166,7 @@ class CWHaven extends Haven { @override TransactionPriority getDefaultTransactionPriority() { - return MoneroTransactionPriority.slow; + return MoneroTransactionPriority.automatic; } @override diff --git a/lib/main.dart b/lib/main.dart index aa09b9a15..3cd5679b3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -130,7 +130,7 @@ Future main() async { secureStorage: secureStorage, initialMigrationVersion: 17); runApp(App()); - } catch (e) { + } catch (e, stacktrace) { runApp(MaterialApp( debugShowCheckedModeBanner: true, home: Scaffold( @@ -138,7 +138,7 @@ Future main() async { margin: EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20), child: Text( - 'Error:\n${e.toString()}', + 'Error:\n${e.toString()}\nStacktrace: $stacktrace', style: TextStyle(fontSize: 22), ))))); } diff --git a/lib/monero/cw_monero.dart b/lib/monero/cw_monero.dart index 51ddf2688..9b10a12c9 100644 --- a/lib/monero/cw_monero.dart +++ b/lib/monero/cw_monero.dart @@ -167,7 +167,7 @@ class CWMonero extends Monero { @override TransactionPriority getDefaultTransactionPriority() { - return MoneroTransactionPriority.slow; + return MoneroTransactionPriority.automatic; } @override diff --git a/lib/router.dart b/lib/router.dart index 5f4618656..b255ce8b9 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; +import 'package:cake_wallet/src/screens/buy/onramper_page.dart'; import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart'; @@ -293,7 +294,7 @@ Route createRoute(RouteSettings settings) { case Routes.accountCreation: return CupertinoPageRoute( builder: (_) => getIt.get( - param1: settings.arguments as AccountListItem)); + param1: settings.arguments as AccountListItem?)); case Routes.addressBook: return MaterialPageRoute( @@ -471,6 +472,9 @@ Route createRoute(RouteSettings settings) { param1: paymentInfo, param2: commitedInfo)); + case Routes.onramperPage: + return CupertinoPageRoute(builder: (_) => getIt.get()); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 7431d71df..3a781ac3a 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -76,4 +76,5 @@ class Routes { static const ioniaPaymentStatusPage = '/ionia_payment_status_page'; static const ioniaMoreOptionsPage = '/ionia_more_options_page'; static const ioniaCustomRedeemPage = '/ionia_custom_redeem_page'; + static const onramperPage = '/onramper'; } diff --git a/lib/src/screens/auth/auth_page.dart b/lib/src/screens/auth/auth_page.dart index b508d1123..0b3b8511a 100644 --- a/lib/src/screens/auth/auth_page.dart +++ b/lib/src/screens/auth/auth_page.dart @@ -1,5 +1,5 @@ +import 'package:another_flushbar/flushbar.dart'; import 'package:cake_wallet/utils/show_bar.dart'; -// import 'package:flushbar/flushbar.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -31,9 +31,8 @@ class AuthPageState extends State { final _backArrowImageDarkTheme = Image.asset('assets/images/close_button.png'); ReactionDisposer? _reaction; - // FIX-ME: replace Flushbar - // Flushbar? _authBar; - // Flushbar? _progressBar; + Flushbar? _authBar; + Flushbar? _progressBar; @override void initState() { @@ -48,39 +47,34 @@ class AuthPageState extends State { if (state is IsExecutingState) { WidgetsBinding.instance.addPostFrameCallback((_) { - // FIX-ME: Changes related to flutter upgreade. - // Could be incorrect value for duration of auth bar - // _authBar = - // createBar(S.of(context).authentication, duration: Duration()) - // ..show(context); + // null duration to make it indefinite until its disposed + _authBar = + createBar(S.of(context).authentication, duration: null) + ..show(context); }); } if (state is FailureState) { print('X'); print(state.error); - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) async { _pinCodeKey.currentState?.clear(); - // _authBar?.dismiss(); + dismissFlushBar(_authBar); showBar( context, S.of(context).failed_authentication(state.error)); - if (widget.onAuthenticationFinished != null) { - widget.onAuthenticationFinished(false, this); - } + widget.onAuthenticationFinished(false, this); }); } if (state is AuthenticationBanned) { - WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) async { _pinCodeKey.currentState?.clear(); - // _authBar?.dismiss(); + dismissFlushBar(_authBar); showBar( context, S.of(context).failed_authentication(state.error)); - if (widget.onAuthenticationFinished != null) { - widget.onAuthenticationFinished(false, this); - } + widget.onAuthenticationFinished(false, this); }); } }); @@ -102,26 +96,32 @@ class AuthPageState extends State { } void changeProcessText(String text) { - // _authBar?.dismiss(); - // FIX-ME: Changes related to flutter upgreade. - // Could be incorrect value for duration of auth bar - // _progressBar = createBar(text, duration: Duration()) - // ..show(_key.currentContext); + dismissFlushBar(_authBar); + _progressBar = createBar(text, duration: null) + ..show(_key.currentContext!); } void hideProgressText() { - // _progressBar?.dismiss(); - // _progressBar = null; + dismissFlushBar(_progressBar); + _progressBar = null; } - void close() { + Future close({String? route}) async { if (_key.currentContext == null) { throw Exception('Key context is null. Should be not happened'); } - // _authBar?.dismiss(); - // _progressBar?.dismiss(); - Navigator.of(_key.currentContext!).pop(); + WidgetsBinding.instance.addPostFrameCallback((_) { + dismissFlushBar(_authBar); + dismissFlushBar(_progressBar); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (route != null) { + Navigator.of(_key.currentContext!).pushReplacementNamed(route); + } else { + Navigator.of(_key.currentContext!).pop(); + } + }); + }); } @override @@ -135,14 +135,9 @@ class AuthPageState extends State { child: SizedBox( height: 37, width: 37, - child: ButtonTheme( - minWidth: double.minPositive, - child: TextButton( - //highlightColor: Colors.transparent, - //splashColor: Colors.transparent, - //padding: EdgeInsets.all(0), - onPressed: () => Navigator.of(context).pop(), - child: _backArrowImageDarkTheme), + child: InkWell( + onTap: () => Navigator.of(context).pop(), + child: _backArrowImageDarkTheme, ), )) : Container(), @@ -152,4 +147,10 @@ class AuthPageState extends State { body: PinCode((pin, _) => widget.authViewModel.auth(password: pin), (_) => null, widget.authViewModel.pinLength, false, _pinCodeKey)); } + + void dismissFlushBar(Flushbar? bar) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + await bar?.dismiss(); + }); + } } diff --git a/lib/src/screens/backup/backup_page.dart b/lib/src/screens/backup/backup_page.dart index f55388943..a055066c0 100644 --- a/lib/src/screens/backup/backup_page.dart +++ b/lib/src/screens/backup/backup_page.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'package:cake_wallet/palette.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -// import 'package:esys_flutter_share/esys_flutter_share.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:cross_file/cross_file.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -103,12 +103,14 @@ class BackupPage extends BasePage { Navigator.of(dialogContext).pop(); final backup = await backupViewModelBase.exportBackup(); + if (backup == null) { + return; + } + if (Platform.isAndroid) { - onExportAndroid(context, backup!); + onExportAndroid(context, backup); } else { - // FIX-ME: Share esys_flutter_share.dart - // await Share.file(S.of(context).backup_file, backup.name, - // backup.content, 'application/*'); + await share(backup); } }, actionLeftButton: () => Navigator.of(dialogContext).pop()); @@ -136,12 +138,20 @@ class BackupPage extends BasePage { backup.name, backup.content); Navigator.of(dialogContext).pop(); }, - actionLeftButton: () { + actionLeftButton: () async { Navigator.of(dialogContext).pop(); - // FIX-ME: Share esys_flutter_share.dart - // Share.file(S.of(context).backup_file, backup.name, - // backup.content, 'application/*'); + await share(backup); }); }); } + + Future share(BackupExportFile backup) async { + const mimeType = 'application/*'; + final path = await backupViewModelBase.saveBackupFileLocally(backup); + await Share.shareXFiles([XFile( + path, + name: backup.name, + mimeType: mimeType)]); + await backupViewModelBase.removeBackupFileLocally(backup); + } } diff --git a/lib/src/screens/buy/onramper_page.dart b/lib/src/screens/buy/onramper_page.dart new file mode 100644 index 000000000..b766fd388 --- /dev/null +++ b/lib/src/screens/buy/onramper_page.dart @@ -0,0 +1,86 @@ +import 'dart:io'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:flutter/material.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; + +class OnRamperPage extends BasePage { + OnRamperPage({ + required this.settingsStore, + required this.wallet}); + + final SettingsStore settingsStore; + final WalletBase wallet; + + @override + String get title => S.current.buy; + + @override + Widget body(BuildContext context) { + final darkMode = Theme.of(context).brightness == Brightness.dark; + return OnRamperPageBody( + settingsStore: settingsStore, + wallet: wallet, + darkMode: darkMode, + backgroundColor: darkMode + ? backgroundDarkColor + : backgroundLightColor, + supportSell: false, + supportSwap: false); + } +} + +class OnRamperPageBody extends StatefulWidget { + OnRamperPageBody({ + required this.settingsStore, + required this.wallet, + required this.darkMode, + required this.supportSell, + required this.supportSwap, + required this.backgroundColor}); + + static const baseUrl = 'widget.onramper.com'; + final SettingsStore settingsStore; + final WalletBase wallet; + final Color backgroundColor; + final bool darkMode; + final bool supportSell; + final bool supportSwap; + + Uri get uri + => Uri.https( + baseUrl, + '', + { + 'apiKey': secrets.onramperApiKey, + 'defaultCrypto': wallet.currency.title, + 'defaultFiat': settingsStore.fiatCurrency.title, + 'wallets': '${wallet.currency.title}:${wallet.walletAddresses.address}', + 'darkMode': darkMode.toString(), + 'supportSell': supportSell.toString(), + 'supportSwap': supportSwap.toString()}); + + @override + OnRamperPageBodyState createState() => OnRamperPageBodyState(); +} + +class OnRamperPageBodyState extends State { + OnRamperPageBodyState(); + + @override + void initState() { + super.initState(); + if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + } + + @override + Widget build(BuildContext context) { + return WebView( + initialUrl: widget.uri.toString(), + backgroundColor: widget.backgroundColor, + javascriptMode: JavascriptMode.unrestricted); + } +} diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index 2b9bf94d2..00a64896f 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -80,7 +80,7 @@ class PreOrderPage extends BasePage { return KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1! + keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1! .backgroundColor!, nextFocus: false, actions: [ @@ -102,10 +102,9 @@ class PreOrderPage extends BasePage { bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), gradient: LinearGradient(colors: [ - Theme.of(context).primaryTextTheme!.subtitle1!.color!, + Theme.of(context).primaryTextTheme.subtitle1!.color!, Theme.of(context) - .primaryTextTheme! - .subtitle1! + .primaryTextTheme.subtitle1! .decorationColor!, ], begin: Alignment.topLeft, end: Alignment.bottomRight), ), @@ -161,11 +160,11 @@ class PreOrderPage extends BasePage { ), ), hintText: '0.00', - borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.decorationColor!, + borderColor: Theme.of(context).primaryTextTheme.bodyText1!.decorationColor!, borderWidth: 0.5, textStyle: TextStyle(fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white), placeholderTextStyle: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline5!.decorationColor!, + color: Theme.of(context).primaryTextTheme.headline5!.decorationColor!, fontWeight: FontWeight.w500, fontSize: 36, ), @@ -180,7 +179,7 @@ class PreOrderPage extends BasePage { S.of(context).buy_with + ':', textAlign: TextAlign.center, style: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline6!.color!, + color: Theme.of(context).primaryTextTheme.headline6!.color!, fontSize: 18, fontWeight: FontWeight.bold ), @@ -197,11 +196,11 @@ class PreOrderPage extends BasePage { double achAmount; int minAmount; - if (snapshot.hasData) { + if (snapshot.hasData && snapshot.data != null) { sourceAmount = snapshot.data!.sourceAmount; destAmount = snapshot.data!.destAmount; minAmount = snapshot.data!.minAmount; - achAmount = snapshot.data!.achSourceAmount!; + achAmount = snapshot.data!.achSourceAmount ?? 0; } else { sourceAmount = 0.0; destAmount = 0.0; @@ -215,7 +214,7 @@ class PreOrderPage extends BasePage { child: Observer(builder: (_) { return BuyListItem( selectedProvider: - buyViewModel.selectedProvider!, + buyViewModel.selectedProvider, provider: item.provider, sourceAmount: sourceAmount, sourceCurrency: buyViewModel.fiatCurrency, @@ -247,7 +246,7 @@ class PreOrderPage extends BasePage { ? S.of(context).buy : S.of(context).buy_with + ' ${buyViewModel.selectedProvider!.description.title}', - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, + color: Theme.of(context).accentTextTheme.bodyText1!.color!, textColor: Colors.white, isLoading: buyViewModel.isRunning, isDisabled: (buyViewModel.selectedProvider == null) || diff --git a/lib/src/screens/buy/widgets/buy_list_item.dart b/lib/src/screens/buy/widgets/buy_list_item.dart index 8dc12ac58..fc8fea81d 100644 --- a/lib/src/screens/buy/widgets/buy_list_item.dart +++ b/lib/src/screens/buy/widgets/buy_list_item.dart @@ -18,7 +18,7 @@ class BuyListItem extends StatelessWidget { this.onTap }); - final BuyProvider selectedProvider; + final BuyProvider? selectedProvider; final BuyProvider provider; final double sourceAmount; final FiatCurrency sourceCurrency; diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index 501d3038c..7912472d8 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/entities/contact_base.dart'; +import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; @@ -34,12 +35,12 @@ class ContactListPage extends BasePage { height: 32.0, decoration: BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context).accentTextTheme!.caption!.color!), + color: Theme.of(context).accentTextTheme.caption!.color!), child: Stack( alignment: Alignment.center, children: [ Icon(Icons.add, - color: Theme.of(context).primaryTextTheme!.headline6!.color!, + color: Theme.of(context).primaryTextTheme.headline6!.color!, size: 22.0), ButtonTheme( minWidth: 32.0, @@ -66,9 +67,9 @@ class ContactListPage extends BasePage { return CollapsibleSectionList( context: context, sectionCount: 2, - themeColor: Theme.of(context).primaryTextTheme!.headline6!.color!, + themeColor: Theme.of(context).primaryTextTheme.headline6!.color!, dividerThemeColor: - Theme.of(context).primaryTextTheme!.caption!.decorationColor!, + Theme.of(context).primaryTextTheme.caption!.decorationColor!, sectionTitleBuilder: (_, int sectionIndex) { var title = 'Contacts'; @@ -91,37 +92,13 @@ class ContactListPage extends BasePage { final contact = contactListViewModel.contacts[index]; final content = generateRaw(context, contact); - // FIX-ME: Slidable - return content; - // return !isEditable - // ? content - // : Slidable( - // key: Key('${contact.key}'), - // actionPane: SlidableDrawerActionPane(), - // child: content, - // secondaryActions: [ - // IconSlideAction( - // caption: S.of(context).edit, - // color: Colors.blue, - // icon: Icons.edit, - // onTap: () async => await Navigator.of(context) - // .pushNamed(Routes.addressBookAddContact, - // arguments: contact), - // ), - // IconSlideAction( - // caption: S.of(context).delete, - // color: Colors.red, - // icon: CupertinoIcons.delete, - // onTap: () async { - // final isDelete = - // await showAlertDialog(context) ?? false; - - // if (isDelete) { - // await contactListViewModel.delete(contact); - // } - // }, - // ), - // ]); + return !isEditable + ? content + : Slidable( + key: Key('${contact.key}'), + endActionPane: _actionPane(context, contact), + child: content, + ); }, ); }, @@ -141,7 +118,7 @@ class ContactListPage extends BasePage { final isCopied = await showNameAndAddressDialog( context, contact.name, contact.address); - if (isCopied != null && isCopied) { + if (isCopied) { await Clipboard.setData(ClipboardData(text: contact.address)); await showBar(context, S.of(context).copied_to_clipboard); } @@ -165,7 +142,7 @@ class ContactListPage extends BasePage { style: TextStyle( fontSize: 14, fontWeight: FontWeight.normal, - color: Theme.of(context).primaryTextTheme!.headline6!.color!), + color: Theme.of(context).primaryTextTheme.headline6!.color!), ), ) ) @@ -266,4 +243,34 @@ class ContactListPage extends BasePage { actionLeftButton: () => Navigator.of(context).pop(false)); }) ?? false; } + + ActionPane _actionPane(BuildContext context, ContactRecord contact) => ActionPane( + motion: const ScrollMotion(), + extentRatio: 0.4, + children: [ + SlidableAction( + onPressed: (_) async => await Navigator.of(context) + .pushNamed(Routes.addressBookAddContact, + arguments: contact), + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + icon: Icons.edit, + label: S.of(context).edit, + ), + SlidableAction( + onPressed: (_) async { + final isDelete = + await showAlertDialog(context); + + if (isDelete) { + await contactListViewModel.delete(contact); + } + }, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: CupertinoIcons.delete, + label: S.of(context).delete, + ), + ], + ); } diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index c893a7df8..f3d5a21fb 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -271,10 +271,10 @@ class DashboardPage extends BasePage { switch (walletType) { case WalletType.bitcoin: - Navigator.of(context).pushNamed(Routes.preOrder); + Navigator.of(context).pushNamed(Routes.onramperPage); break; case WalletType.litecoin: - Navigator.of(context).pushNamed(Routes.preOrder); + Navigator.of(context).pushNamed(Routes.onramperPage); break; default: await showPopUp( diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index b7d12a190..68e7cc76d 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -42,8 +42,7 @@ class WalletMenu { Navigator.of(context).pushNamed(Routes.auth, arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { if (isAuthenticatedSuccessfully) { - auth.close(); - Navigator.of(auth.context).pushNamed(Routes.showKeys); + auth.close(route: Routes.showKeys); } }); }), @@ -52,6 +51,21 @@ class WalletMenu { image: Image.asset('assets/images/open_book_menu.png', height: 16, width: 16), handler: () => Navigator.of(context).pushNamed(Routes.addressBook)), + WalletMenuItem( + title: S.current.backup, + image: Image.asset('assets/images/restore_wallet.png', + height: 16, + width: 16, + color: Palette.darkBlue), + handler: () { + Navigator.of(context).pushNamed( + Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) { + if (isAuthenticatedSuccessfully) { + auth.close(route:Routes.backup); + } + }); + }), WalletMenuItem( title: S.current.settings_title, image: Image.asset('assets/images/settings_menu.png', diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/widgets/address_page.dart index 2c7d36437..9a06dc39d 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/widgets/address_page.dart @@ -15,6 +15,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; +import 'package:share_plus/share_plus.dart'; class AddressPage extends BasePage { AddressPage({ @@ -85,6 +86,26 @@ class AddressPage extends BasePage { ], begin: Alignment.topRight, end: Alignment.bottomLeft)), child: scaffold); + @override + Widget? trailing(BuildContext context) { + final shareImage = + Image.asset('assets/images/share.png', + color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!); + + return !addressListViewModel.hasAddressList ? Material( + color: Colors.transparent, + child: IconButton( + padding: EdgeInsets.zero, + constraints: BoxConstraints(), + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + iconSize: 25, + onPressed: () => Share.share(addressListViewModel.address.address), + icon: shareImage, + ), + ) : null; + } + @override Widget body(BuildContext context) { autorun((_) async { diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 09791ca83..744848156 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -111,6 +111,10 @@ class ExchangePage extends BasePage { WidgetsBinding.instance .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel)); + if (exchangeViewModel.isLowFee) { + _showFeeAlert(context); + } + return KeyboardActions( disableScroll: true, config: KeyboardActionsConfig( @@ -179,6 +183,7 @@ class ExchangePage extends BasePage { padding: EdgeInsets.fromLTRB(24, 100, 24, 32), child: Observer( builder: (_) => ExchangeCard( + onDispose: disposeBestRateSync, hasAllAmount: exchangeViewModel.hasAllAmount, allAmount: exchangeViewModel.hasAllAmount ? () => exchangeViewModel @@ -265,6 +270,7 @@ class ExchangePage extends BasePage { EdgeInsets.only(top: 29, left: 24, right: 24), child: Observer( builder: (_) => ExchangeCard( + onDispose: disposeBestRateSync, amountFocusNode: _receiveAmountFocus, addressFocusNode: _receiveAddressFocus, key: receiveKey, @@ -743,13 +749,13 @@ class ExchangePage extends BasePage { if (_receiveAmountFocus.hasFocus) { exchangeViewModel.isFixedRateMode = true; } - exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text); + // exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text); }); _depositAmountFocus.addListener(() { exchangeViewModel.isFixedRateMode = false; - exchangeViewModel.changeDepositAmount( - amount: depositAmountController.text); + // exchangeViewModel.changeDepositAmount( + // amount: depositAmountController.text); }); _isReactionsSet = true; @@ -791,4 +797,24 @@ class ExchangePage extends BasePage { final address = await extractAddressFromParsed(context, parsedAddress); return address; } + + void _showFeeAlert(BuildContext context) async { + await Future.delayed(Duration(seconds: 1)); + final confirmed = await showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).low_fee, + alertContent: S.of(context).low_fee_alert, + leftButtonText: S.of(context).ignor, + rightButtonText: S.of(context).use_suggested, + actionLeftButton: () => Navigator.of(context).pop(false), + actionRightButton: () => Navigator.of(context).pop(true)); + }) ?? false; + if (confirmed) { + exchangeViewModel.setDefaultTransactionPriority(); + } + } + + void disposeBestRateSync() => exchangeViewModel.bestRateSync?.cancel(); } diff --git a/lib/src/screens/exchange/widgets/currency_picker.dart b/lib/src/screens/exchange/widgets/currency_picker.dart index cd223a146..9f8b9e493 100644 --- a/lib/src/screens/exchange/widgets/currency_picker.dart +++ b/lib/src/screens/exchange/widgets/currency_picker.dart @@ -6,6 +6,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; +import 'package:cake_wallet/palette.dart'; import 'currency_picker_widget.dart'; class CurrencyPicker extends StatefulWidget { @@ -104,7 +105,7 @@ class CurrencyPickerState extends State { Padding( padding: const EdgeInsets.all(16), child: TextFormField( - style: TextStyle(color: Theme.of(context).primaryTextTheme!.headline6!.color!), + style: TextStyle(color: Palette.darkBlueCraiola), decoration: InputDecoration( hintText: widget.hintText, prefixIcon: Image.asset("assets/images/search_icon.png"), diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 04c69040b..98843b1fb 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -37,7 +37,8 @@ class ExchangeCard extends StatefulWidget { this.addressFocusNode, this.allAmount, this.onPushPasteButton, - this.onPushAddressBookButton}) + this.onPushAddressBookButton, + this.onDispose}) : super(key: key); final List currencies; @@ -63,6 +64,7 @@ class ExchangeCard extends StatefulWidget { final VoidCallback? allAmount; final void Function(BuildContext context)? onPushPasteButton; final void Function(BuildContext context)? onPushAddressBookButton; + final Function()? onDispose; @override ExchangeCardState createState() => ExchangeCardState(); @@ -106,6 +108,13 @@ class ExchangeCardState extends State { super.initState(); } + @override + void dispose() { + widget.onDispose?.call(); + + super.dispose(); + } + void changeLimits({String? min, String? max}) { setState(() { _min = min; diff --git a/lib/src/screens/exchange/widgets/present_provider_picker.dart b/lib/src/screens/exchange/widgets/present_provider_picker.dart index 15e5bb6fd..9300aaf89 100644 --- a/lib/src/screens/exchange/widgets/present_provider_picker.dart +++ b/lib/src/screens/exchange/widgets/present_provider_picker.dart @@ -20,9 +20,12 @@ class PresentProviderPicker extends StatelessWidget { return TextButton( onPressed: () => _presentProviderPicker(context), - // FIX-ME: Style - //highlightColor: Colors.transparent, - //splashColor: Colors.transparent, + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + splashFactory: NoSplash.splashFactory, + foregroundColor: MaterialStateProperty.all(Colors.transparent), + overlayColor: MaterialStateProperty.all(Colors.transparent), + ), child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -46,7 +49,7 @@ class PresentProviderPicker extends StatelessWidget { style: TextStyle( fontSize: 10.0, fontWeight: FontWeight.w500, - color: Theme.of(context).textTheme!.headline5!.color!))) + color: Theme.of(context).textTheme.headline5!.color!))) ], ), SizedBox(width: 5), diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart index 23da7320a..41179f34c 100644 --- a/lib/src/screens/new_wallet/new_wallet_type_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart @@ -1,15 +1,6 @@ -import 'package:cake_wallet/core/execution_state.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cake_wallet/utils/show_bar.dart'; -import 'package:cake_wallet/view_model/wallet_new_vm.dart'; -// import 'package:flushbar/flushbar.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; @@ -67,8 +58,6 @@ class WalletTypeFormState extends State { WalletType? selected; List types; - // FIX-ME: Replace Flushbar - // Flushbar? _progressBar; @override void initState() { @@ -97,7 +86,7 @@ class WalletTypeFormState extends State { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Theme.of(context).primaryTextTheme!.headline6!.color!), + color: Theme.of(context).primaryTextTheme.headline6!.color!), ), ), ...types.map((type) => Padding( @@ -114,7 +103,7 @@ class WalletTypeFormState extends State { bottomSection: PrimaryButton( onPressed: () => onTypeSelected(), text: S.of(context).seed_language_next, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, + color: Theme.of(context).accentTextTheme.bodyText1!.color!, textColor: Colors.white, isDisabled: selected == null, ), diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart index 0eb79bada..556a0fd06 100644 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ b/lib/src/screens/nodes/nodes_list_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cw_core/node.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -26,7 +27,7 @@ class NodeListPage extends BasePage { height: 32, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(16)), - color: Theme.of(context).accentTextTheme!.caption!.color!), + color: Theme.of(context).accentTextTheme.caption!.color!), child: ButtonTheme( minWidth: double.minPositive, child: TextButton( @@ -85,7 +86,7 @@ class NodeListPage extends BasePage { final node = nodeListViewModel.nodes[index]; final isSelected = - node.keyIndex == nodeListViewModel.currentNode?.keyIndex; + node.keyIndex == nodeListViewModel.currentNode.keyIndex; final nodeListRow = NodeListRow( title: node.uriRaw, isSelected: isSelected, @@ -114,45 +115,52 @@ class NodeListPage extends BasePage { }); }); }); - // FIX-ME: Slidable - // final dismissibleRow = Slidable( - // key: Key('${node.keyIndex}'), - // actionPane: SlidableDrawerActionPane(), - // child: nodeListRow, - // secondaryActions: [ - // IconSlideAction( - // caption: S.of(context).delete, - // color: Colors.red, - // icon: CupertinoIcons.delete, - // onTap: () async { - // final confirmed = await showPopUp( - // context: context, - // builder: (BuildContext context) { - // return AlertWithTwoActions( - // alertTitle: S.of(context).remove_node, - // alertContent: - // S.of(context).remove_node_message, - // rightButtonText: S.of(context).remove, - // leftButtonText: S.of(context).cancel, - // actionRightButton: () => - // Navigator.pop(context, true), - // actionLeftButton: () => - // Navigator.pop(context, false)); - // }) ?? - // false; - // if (confirmed) { - // await nodeListViewModel.delete(node); - // } - // }, - // ), - // ]); + final dismissibleRow = Slidable( + key: Key('${node.keyIndex}'), + startActionPane: _actionPane(context, node), + endActionPane: _actionPane(context, node), + child: nodeListRow, + ); - return nodeListRow; - // return isSelected ? nodeListRow : dismissibleRow; + return isSelected ? nodeListRow : dismissibleRow; }); }, ), ); } + + ActionPane _actionPane(BuildContext context, Node node) => ActionPane( + motion: const ScrollMotion(), + extentRatio: 0.3, + children: [ + SlidableAction( + onPressed: (context) async { + final confirmed = await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).remove_node, + alertContent: + S.of(context).remove_node_message, + rightButtonText: S.of(context).remove, + leftButtonText: S.of(context).cancel, + actionRightButton: () => + Navigator.pop(context, true), + actionLeftButton: () => + Navigator.pop(context, false)); + }) ?? + false; + + if (confirmed) { + await nodeListViewModel.delete(node); + } + }, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: CupertinoIcons.delete, + label: S.of(context).delete, + ), + ], + ); } diff --git a/lib/src/screens/pin_code/pin_code_widget.dart b/lib/src/screens/pin_code/pin_code_widget.dart index dd763696b..a647f3d95 100644 --- a/lib/src/screens/pin_code/pin_code_widget.dart +++ b/lib/src/screens/pin_code/pin_code_widget.dart @@ -1,7 +1,6 @@ import 'package:cake_wallet/utils/show_bar.dart'; -// import 'package:flushbar/flushbar.dart'; +import 'package:another_flushbar/flushbar.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; class PinCodeWidget extends StatefulWidget { @@ -40,8 +39,7 @@ class PinCodeState extends State { String pin; String title; double _aspectRatio; - // FIX-ME: Replace Flushbar - // Flushbar? _progressBar; + Flushbar? _progressBar; int currentPinLength() => pin.length; @@ -91,19 +89,18 @@ class PinCodeState extends State { void changeProcessText(String text) { hideProgressText(); - // FIX-ME: Empty Duration, - // _progressBar = createBar(text, duration: Duration()) - // ..show(_key.currentContext); + _progressBar = createBar(text, duration: null) + ..show(_key.currentContext!); } void close() { - // _progressBar?.dismiss(); + _progressBar?.dismiss(); Navigator.of(_key.currentContext!).pop(); } void hideProgressText() { - // _progressBar?.dismiss(); - // _progressBar = null; + _progressBar?.dismiss(); + _progressBar = null; } @override diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index d8d4f7edb..6f6b4e31c 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -6,7 +6,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -// import 'package:esys_flutter_share/esys_flutter_share.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/di.dart'; @@ -92,23 +92,17 @@ class ReceivePage extends BasePage { Image.asset('assets/images/share.png', color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!); - return SizedBox( - height: 20.0, - width: 20.0, - child: ButtonTheme( - minWidth: double.minPositive, - child: TextButton( - // FIX-ME: Style - //highlightColor: Colors.transparent, - //splashColor: Colors.transparent, - //padding: EdgeInsets.all(0), - onPressed: () { - // FIX-ME: Share esys_flutter_share.dart - // Share.text(S.current.share_address, - // addressListViewModel.address.address, 'text/plain') - }, - child: shareImage), - ), + return Material( + color: Colors.transparent, + child: IconButton( + padding: EdgeInsets.zero, + constraints: BoxConstraints(), + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + iconSize: 25, + onPressed: () => Share.share(addressListViewModel.address.address), + icon: shareImage + ) ); } diff --git a/lib/src/screens/receive/widgets/address_cell.dart b/lib/src/screens/receive/widgets/address_cell.dart index c5e12a496..137db108b 100644 --- a/lib/src/screens/receive/widgets/address_cell.dart +++ b/lib/src/screens/receive/widgets/address_cell.dart @@ -57,6 +57,7 @@ class AddressCell extends StatelessWidget { final Widget cell = InkWell( onTap: () => onTap?.call(address), child: Container( + width: double.infinity, color: backgroundColor, padding: EdgeInsets.only(left: 24, right: 24, top: 28, bottom: 28), child: Text( @@ -69,20 +70,25 @@ class AddressCell extends StatelessWidget { ), ), )); - // FIX-ME: Slidable - return cell; - // return Container( - // color: backgroundColor, - // child: Slidable( - // key: Key(address), - // actionPane: SlidableDrawerActionPane(), - // child: cell, - // secondaryActions: [ - // IconSlideAction( - // caption: S.of(context).edit, - // color: Colors.blue, - // icon: Icons.edit, - // onTap: () => onEdit?.call()) - // ])); + return Slidable( + key: Key(address), + startActionPane: _actionPane(context), + endActionPane: _actionPane(context), + child: cell, + ); } + + ActionPane _actionPane(BuildContext context) => ActionPane( + motion: const ScrollMotion(), + extentRatio: 0.3, + children: [ + SlidableAction( + onPressed: (_) => onEdit?.call(), + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + icon: Icons.edit, + label: S.of(context).edit, + ), + ], + ); } diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index f5e206837..8de1526e8 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -6,7 +6,7 @@ import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -// import 'package:esys_flutter_share/esys_flutter_share.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; @@ -159,13 +159,8 @@ class WalletSeedPage extends BasePage { child: Container( padding: EdgeInsets.only(right: 8.0), child: PrimaryButton( - onPressed: () { - // FIX-ME: Share esys_flutter_share - // Share.text( - // S.of(context).seed_share, - // walletSeedViewModel.seed, - // 'text/plain') - }, + onPressed: () => + Share.share(walletSeedViewModel.seed), text: S.of(context).save, color: Colors.green, textColor: Colors.white), diff --git a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart index e91072ba0..51fcc16d5 100644 --- a/lib/src/screens/settings/widgets/settings_link_provider_cell.dart +++ b/lib/src/screens/settings/widgets/settings_link_provider_cell.dart @@ -20,7 +20,7 @@ class SettingsLinkProviderCell extends StandardListRow { @override Widget? buildLeading(BuildContext context) => - icon != null ? Image.asset(icon!, color: iconColor, height: 30, width: 30) : null; + icon != null ? Image.asset(icon!, color: iconColor, height: 24, width: 24) : null; @override Widget buildTrailing(BuildContext context) => Text(linkTitle, diff --git a/lib/src/screens/wallet_keys/wallet_keys_page.dart b/lib/src/screens/wallet_keys/wallet_keys_page.dart index 72da07171..942809ac8 100644 --- a/lib/src/screens/wallet_keys/wallet_keys_page.dart +++ b/lib/src/screens/wallet_keys/wallet_keys_page.dart @@ -1,3 +1,4 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -18,7 +19,36 @@ class WalletKeysPage extends BasePage { @override Widget body(BuildContext context) { - return Container( + return Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Theme.of(context).accentTextTheme!.caption!.color!, + ), + child: Center( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: AutoSizeText( + S.of(context).do_not_share_warning_text.toUpperCase(), + textAlign: TextAlign.center, + maxLines: 4, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.red)), + ), + ), + ), + ), + ), + Expanded( + flex: 7, + child: Container( padding: EdgeInsets.only(top: 20.0, bottom: 20.0), child: Observer( builder: (_) { @@ -48,6 +78,6 @@ class WalletKeysPage extends BasePage { ); }); }, - )); + )))]); } } diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index d08126407..c1a7ea953 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -3,7 +3,7 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; -// import 'package:flushbar/flushbar.dart'; +import 'package:another_flushbar/flushbar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -49,7 +49,7 @@ class WalletListBodyState extends State { Image.asset('assets/images/haven_logo.png', height: 24, width: 24); final scrollController = ScrollController(); final double tileHeight = 60; - // Flushbar? _progressBar; + Flushbar? _progressBar; @override Widget build(BuildContext context) { @@ -156,22 +156,14 @@ class WalletListBodyState extends State { ), )); - // FIX-ME: Slidable for current - return row; - // return wallet.isCurrent - // ? row - // : Slidable( - // key: Key('${wallet.key}'), - // actionPane: SlidableDrawerActionPane(), - // child: row, - // secondaryActions: [ - // IconSlideAction( - // caption: S.of(context).delete, - // color: Colors.red, - // icon: CupertinoIcons.delete, - // onTap: () async => _removeWallet(wallet), - // ) - // ]); + return wallet.isCurrent + ? row + : Slidable( + key: Key('${wallet.key}'), + startActionPane: _actionPane(wallet), + endActionPane: _actionPane(wallet), + child: row, + ); }), ), ), @@ -240,7 +232,9 @@ class WalletListBodyState extends State { await widget.walletListViewModel.loadWallet(wallet); auth.hideProgressText(); auth.close(); - Navigator.of(context).pop(); + WidgetsBinding.instance.addPostFrameCallback((_) { + Navigator.of(context).pop(); + }); } catch (e) { auth.changeProcessText(S .of(context) @@ -256,14 +250,34 @@ class WalletListBodyState extends State { return; } - try { - auth.changeProcessText( - S.of(context).wallet_list_removing_wallet(wallet.name)); - await widget.walletListViewModel.remove(wallet); - } catch (e) { - auth.changeProcessText(S - .of(context) - .wallet_list_failed_to_remove(wallet.name, e.toString())); + bool confirmed = false; + + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).delete_wallet, + alertContent: S.of(context).delete_wallet_confirm_message(wallet.name), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).delete, + actionLeftButton: () => Navigator.of(context).pop(), + actionRightButton: () { + confirmed = true; + Navigator.of(context).pop(); + }, + ); + }); + + if (confirmed) { + try { + auth.changeProcessText( + S.of(context).wallet_list_removing_wallet(wallet.name)); + await widget.walletListViewModel.remove(wallet); + } catch (e) { + auth.changeProcessText(S + .of(context) + .wallet_list_failed_to_remove(wallet.name, e.toString())); + } } auth.close(); @@ -271,12 +285,25 @@ class WalletListBodyState extends State { } void changeProcessText(String text) { - // FIX-ME: Duration - // _progressBar = createBar(text, duration: Duration())..show(context); + _progressBar = createBar(text, duration: null)..show(context); } void hideProgressText() { - // _progressBar?.dismiss(); - // _progressBar = null; + _progressBar?.dismiss(); + _progressBar = null; } + + ActionPane _actionPane(WalletListItem wallet) => ActionPane( + motion: const ScrollMotion(), + extentRatio: 0.3, + children: [ + SlidableAction( + onPressed: (_) => _removeWallet(wallet), + backgroundColor: Colors.red, + foregroundColor: Colors.white, + icon: CupertinoIcons.delete, + label: S.of(context).delete, + ), + ], + ); } diff --git a/lib/store/templates/exchange_template_store.dart b/lib/store/templates/exchange_template_store.dart index 9e61dee4c..a63c2f56e 100644 --- a/lib/store/templates/exchange_template_store.dart +++ b/lib/store/templates/exchange_template_store.dart @@ -24,7 +24,7 @@ abstract class ExchangeTemplateBase with Store { templates.replaceRange(0, templates.length, templateSource.values.toList()); @action - Future addTemplate({ + Future addTemplate({ required String amount, required String depositCurrency, required String receiveCurrency, @@ -32,15 +32,15 @@ abstract class ExchangeTemplateBase with Store { required String depositAddress, required String receiveAddress}) async { final template = ExchangeTemplate( - amount: amount, - depositCurrency: depositCurrency, - receiveCurrency: receiveCurrency, - provider: provider, - depositAddress: depositAddress, - receiveAddress: receiveAddress); + amountRaw: amount, + depositCurrencyRaw: depositCurrency, + receiveCurrencyRaw: receiveCurrency, + providerRaw: provider, + depositAddressRaw: depositAddress, + receiveAddressRaw: receiveAddress); await templateSource.add(template); } @action - Future remove({required ExchangeTemplate template}) async => await template.delete(); + Future remove({required ExchangeTemplate template}) async => await template.delete(); } \ No newline at end of file diff --git a/lib/store/templates/send_template_store.dart b/lib/store/templates/send_template_store.dart index b1a5ce44b..d5181c0ce 100644 --- a/lib/store/templates/send_template_store.dart +++ b/lib/store/templates/send_template_store.dart @@ -23,7 +23,7 @@ abstract class SendTemplateBase with Store { templates.replaceRange(0, templates.length, templateSource.values.toList()); @action - Future addTemplate({ + Future addTemplate({ required String name, required bool isCurrencySelected, required String address, @@ -32,16 +32,16 @@ abstract class SendTemplateBase with Store { required String amount, required String amountFiat}) async { final template = Template( - name: name, - isCurrencySelected: isCurrencySelected, - address: address, - cryptoCurrency: cryptoCurrency, - fiatCurrency: fiatCurrency, - amount: amount, - amountFiat: amountFiat); + nameRaw: name, + isCurrencySelectedRaw: isCurrencySelected, + addressRaw: address, + cryptoCurrencyRaw: cryptoCurrency, + fiatCurrencyRaw: fiatCurrency, + amountRaw: amount, + amountFiatRaw: amountFiat); await templateSource.add(template); } @action - Future remove({required Template template}) async => await template.delete(); + Future remove({required Template template}) async => await template.delete(); } \ No newline at end of file diff --git a/lib/utils/show_bar.dart b/lib/utils/show_bar.dart index 451887970..1480b58ac 100644 --- a/lib/utils/show_bar.dart +++ b/lib/utils/show_bar.dart @@ -1,58 +1,71 @@ -// import 'package:flushbar/flushbar.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:another_flushbar/flushbar.dart'; import 'package:flutter/material.dart'; Future showBar(BuildContext context, String messageText, {bool isDark = false, - Duration duration = const Duration(seconds: 1), - bool isDismissible = true, - String? titleText}) async { - // FIX-ME: Unimplemented Flushbar - // final bar = Flushbar( - // boxShadows: [ - // BoxShadow( - // color: Colors.black.withOpacity(0.09), - // blurRadius: 8, - // offset: Offset(0, 2)) - // ], - // backgroundColor: isDark ? Colors.black : Colors.white, - // borderRadius: 35, - // margin: EdgeInsets.all(50), - // titleText: titleText != null - // ? Text(titleText, - // textAlign: TextAlign.center, - // style: TextStyle(color: isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold, fontSize: 24.0)) - // : null, - // messageText: Text(messageText, - // textAlign: TextAlign.center, - // style: TextStyle(color: isDark ? Colors.white : Colors.black, fontSize: 16)), - // duration: duration, - // isDismissible: isDismissible, - // flushbarPosition: FlushbarPosition.TOP, - // flushbarStyle: FlushbarStyle.FLOATING); + Duration? duration = const Duration(seconds: 1), // pass explicitly by null to make the duration indefinite + bool isDismissible = true, + String? titleText}) async { + final bar = Flushbar( + boxShadows: [ + BoxShadow( + color: Colors.black.withOpacity(0.09), + blurRadius: 8, + offset: Offset(0, 2), + ) + ], + backgroundColor: isDark ? Colors.black : Colors.white, + borderRadius: BorderRadius.circular(35), + margin: EdgeInsets.all(50), + titleText: titleText != null + ? Text( + titleText, + textAlign: TextAlign.center, + style: TextStyle( + color: isDark ? Colors.white : Colors.black, + fontWeight: FontWeight.bold, + fontSize: 24.0, + ), + ) + : null, + messageText: Text( + messageText, + textAlign: TextAlign.center, + style: TextStyle( + color: isDark ? Colors.white : Colors.black, + fontSize: 16, + ), + ), + duration: duration, + isDismissible: isDismissible, + flushbarPosition: FlushbarPosition.TOP, + flushbarStyle: FlushbarStyle.FLOATING, + ); - // return bar.show(context); - return null; + return bar.show(context); } -// FIX-ME: Unimplemented Flushbar -// Flushbar createBar(String text, - // {bool isDark = false, Duration duration = const Duration(seconds: 1), bool isDismissible = true}) { - // return Flushbar( - // boxShadows: [ - // BoxShadow( - // color: Colors.black.withOpacity(0.09), - // blurRadius: 8, - // offset: Offset(0, 2)) - // ], - // backgroundColor: isDark ? Colors.black : Colors.white, - // borderRadius: 35, - // margin: EdgeInsets.all(50), - // messageText: Text(text, - // textAlign: TextAlign.center, - // style: TextStyle(color: isDark ? Colors.white : Colors.black)), - // duration: duration, - // isDismissible: isDismissible, - // flushbarPosition: FlushbarPosition.TOP, - // flushbarStyle: FlushbarStyle.FLOATING); -// } +Flushbar createBar(String text, + {bool isDark = false, + Duration? duration = const Duration(seconds: 1), // pass explicitly by null to make the duration indefinite + bool isDismissible = true}) { + return Flushbar( + boxShadows: [ + BoxShadow( + color: Colors.black.withOpacity(0.09), + blurRadius: 8, + offset: Offset(0, 2), + ) + ], + backgroundColor: isDark ? Colors.black : Colors.white, + borderRadius: BorderRadius.circular(35), + margin: EdgeInsets.all(50), + messageText: Text(text, + textAlign: TextAlign.center, + style: TextStyle(color: isDark ? Colors.white : Colors.black)), + duration: duration, + isDismissible: isDismissible, + flushbarPosition: FlushbarPosition.TOP, + flushbarStyle: FlushbarStyle.FLOATING, + ); +} diff --git a/lib/view_model/auth_view_model.dart b/lib/view_model/auth_view_model.dart index 0003b0275..29ef46b47 100644 --- a/lib/view_model/auth_view_model.dart +++ b/lib/view_model/auth_view_model.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/view_model/auth_state.dart'; @@ -55,8 +56,10 @@ abstract class AuthViewModelBase with Store { final isSuccessfulAuthenticated = await _authService.authenticate(password); if (isSuccessfulAuthenticated) { - state = ExecutedSuccessfullyState(); - _failureCounter = 0; + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + state = ExecutedSuccessfullyState(); + _failureCounter = 0; + }); } else { _failureCounter += 1; diff --git a/lib/view_model/backup_view_model.dart b/lib/view_model/backup_view_model.dart index 2d75e2000..8cc651b17 100644 --- a/lib/view_model/backup_view_model.dart +++ b/lib/view_model/backup_view_model.dart @@ -8,6 +8,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:mobx/mobx.dart'; import 'package:intl/intl.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:path_provider/path_provider.dart'; part 'backup_view_model.g.dart'; @@ -71,6 +72,21 @@ abstract class BackupViewModelBase with Store { } } + Future saveBackupFileLocally(BackupExportFile backup) async { + final appDir = await getApplicationDocumentsDirectory(); + final path = '${appDir.path}/${backup.name}'; + final backupFile = File(path); + await backupFile.writeAsBytes(backup.content); + return path; + } + + Future removeBackupFileLocally(BackupExportFile backup) async { + final appDir = await getApplicationDocumentsDirectory(); + final path = '${appDir.path}/${backup.name}'; + final backupFile = File(path); + await backupFile.delete(); + } + @action void showMasterPassword() => isBackupPasswordVisible = true; diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index 2baa1447b..080caa3ad 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -6,7 +6,6 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/buy/buy_item.dart'; -import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/store/dashboard/orders_store.dart'; @@ -103,7 +102,7 @@ abstract class BuyViewModelBase with Store { print(e.toString()); } - if (isMoonPayEnabled ?? false) { + if (isMoonPayEnabled) { _providerList.add(MoonPayBuyProvider(wallet: wallet)); } diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 7a9ded7ac..9ff690b70 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:collection'; import 'dart:convert'; @@ -5,6 +6,10 @@ import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; +import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; +import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cw_core/monero_transaction_priority.dart'; +import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -39,7 +44,7 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; abstract class ExchangeViewModelBase with Store { ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore, - this.tradesStore, this._settingsStore, this.sharedPreferences) + this.tradesStore, this._settingsStore, this.sharedPreferences, this._settingsViewModel) : _cryptoNumberFormat = NumberFormat(), isReverse = false, isFixedRateMode = false, @@ -59,12 +64,12 @@ abstract class ExchangeViewModelBase with Store { receiveCurrency = wallet.currency, depositCurrency = wallet.currency, providerList = [ChangeNowExchangeProvider(), SideShiftExchangeProvider(), SimpleSwapExchangeProvider()], - selectedProviders = ObservableList(), - currentTradeAvailableProviders = SplayTreeMap() { + selectedProviders = ObservableList() { const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; const excludeReceiveCurrencies = [CryptoCurrency.xlm, CryptoCurrency.xrp, CryptoCurrency.bnb, CryptoCurrency.btt, CryptoCurrency.nano]; _initialPairBasedOnWallet(); + final Map exchangeProvidersSelection = json .decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map; @@ -76,6 +81,11 @@ abstract class ExchangeViewModelBase with Store { : (exchangeProvidersSelection[element.title] as bool)) .toList()); + _setAvailableProviders(); + _calculateBestRate(); + + bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate()); + isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); depositAmount = ''; @@ -119,8 +129,15 @@ abstract class ExchangeViewModelBase with Store { /// Maps in dart are not sorted by default /// SplayTreeMap is a map sorted by keys /// will use it to sort available providers - /// depending on the amount they yield for the current trade - SplayTreeMap currentTradeAvailableProviders; + /// based on the rate they yield for the current trade + /// + /// + /// initialize with descending comparator + /// since we want largest rate first + final SplayTreeMap _sortedAvailableProviders = + SplayTreeMap((double a, double b) => b.compareTo(a)); + + final List _tradeAvailableProviders = []; @observable ObservableList selectedProviders; @@ -179,6 +196,20 @@ abstract class ExchangeViewModelBase with Store { bool get isMoneroWallet => wallet.type == WalletType.monero; + bool get isLowFee { + switch (wallet.type) { + case WalletType.monero: + case WalletType.haven: + return _settingsViewModel.transactionPriority == MoneroTransactionPriority.slow; + case WalletType.bitcoin: + return _settingsViewModel.transactionPriority == BitcoinTransactionPriority.slow; + case WalletType.litecoin: + return _settingsViewModel.transactionPriority == LitecoinTransactionPriority.slow; + default: + return false; + } + } + List receiveCurrencies; List depositCurrencies; @@ -191,6 +222,12 @@ abstract class ExchangeViewModelBase with Store { final SettingsStore _settingsStore; + final SettingsViewModel _settingsViewModel; + + double _bestRate = 0.0; + + late Timer bestRateSync; + @action void changeDepositCurrency({required CryptoCurrency currency}) { depositCurrency = currency; @@ -210,68 +247,11 @@ abstract class ExchangeViewModelBase with Store { } @action - void changeReceiveAmount({required String amount}) { + Future changeReceiveAmount({required String amount}) async { receiveAmount = amount; isReverse = true; - if (amount == null || amount.isEmpty) { - depositAmount = ''; - receiveAmount = ''; - return; - } - - final _enteredAmount = double.parse(amount.replaceAll(',', '.')) ?? 0; - - currentTradeAvailableProviders.clear(); - for (var provider in selectedProviders) { - /// if this provider is not valid for the current pair, skip it - if (!providersForCurrentPair().contains(provider)) { - continue; - } - provider - .calculateAmount( - from: receiveCurrency, - to: depositCurrency, - amount: _enteredAmount, - isFixedRateMode: isFixedRateMode, - isReceiveAmount: true) - .then((amount) { - - final from = isFixedRateMode - ? receiveCurrency - : depositCurrency; - final to = isFixedRateMode - ? depositCurrency - : receiveCurrency; - - provider.fetchLimits( - from: from, - to: to, - isFixedRateMode: isFixedRateMode, - ).then((limits) { - /// if the entered amount doesn't exceed the limits of this provider - if ((limits?.max ?? double.maxFinite) >= _enteredAmount - && (limits?.min ?? 0) <= _enteredAmount) { - /// add this provider as its valid for this trade - /// will be sorted ascending already since - /// we seek the least deposit amount - currentTradeAvailableProviders[amount] = provider; - } - return amount; - }).then((amount) => depositAmount = _cryptoNumberFormat - .format(amount) - .toString() - .replaceAll(RegExp('\\,'), '')); - }); - } - } - - @action - void changeDepositAmount({required String amount}) { - depositAmount = amount; - isReverse = false; - - if (amount == null || amount.isEmpty) { + if (amount.isEmpty) { depositAmount = ''; receiveAmount = ''; return; @@ -279,93 +259,115 @@ abstract class ExchangeViewModelBase with Store { final _enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0; - currentTradeAvailableProviders.clear(); - for (var provider in selectedProviders) { - /// if this provider is not valid for the current pair, skip it - if (!providersForCurrentPair().contains(provider)) { - continue; + if (_bestRate == 0) { + depositAmount = S.current.fetching; + + await _calculateBestRate(); + } + + depositAmount = _cryptoNumberFormat + .format(_enteredAmount / _bestRate) + .toString() + .replaceAll(RegExp('\\,'), ''); + } + + @action + Future changeDepositAmount({required String amount}) async { + depositAmount = amount; + isReverse = false; + + if (amount.isEmpty) { + depositAmount = ''; + receiveAmount = ''; + return; + } + + final _enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0; + + /// in case the best rate was not calculated yet + if (_bestRate == 0) { + receiveAmount = S.current.fetching; + + await _calculateBestRate(); + } + + receiveAmount = _cryptoNumberFormat + .format(_bestRate * _enteredAmount) + .toString() + .replaceAll(RegExp('\\,'), ''); + } + + Future _calculateBestRate() async { + final result = await Future.wait( + _tradeAvailableProviders + .map((element) => element.calculateAmount( + from: depositCurrency, + to: receiveCurrency, + amount: 1, + isFixedRateMode: isFixedRateMode, + isReceiveAmount: false)) + ); + + _sortedAvailableProviders.clear(); + + for (int i=0;i= _enteredAmount - && (limits?.min ?? 0) <= _enteredAmount) { - /// add this provider as its valid for this trade - /// subtract from maxFinite so the provider - /// with the largest amount would be sorted ascending - currentTradeAvailableProviders[double.maxFinite - amount] = provider; - } - return amount; - }).then((amount) => receiveAmount = - receiveAmount = _cryptoNumberFormat - .format(amount) - .toString() - .replaceAll(RegExp('\\,'), '')); - }); + } + if (_sortedAvailableProviders.isNotEmpty) { + _bestRate = _sortedAvailableProviders.keys.first; } } @action - Future loadLimits() async { + Future loadLimits() async { if (selectedProviders.isEmpty) { return; } limitsState = LimitsIsLoading(); - try { - final from = isFixedRateMode + final from = isFixedRateMode ? receiveCurrency : depositCurrency; - final to = isFixedRateMode + final to = isFixedRateMode ? depositCurrency : receiveCurrency; - limits = await selectedProviders.first.fetchLimits( - from: from, - to: to, - isFixedRateMode: isFixedRateMode); + double lowestMin = double.maxFinite; + double? highestMax = 0.0; - /// if the first provider limits is bounded then check with other providers - /// for the highest maximum limit - if (limits.max != null) { - for (int i = 1;i < selectedProviders.length;i++) { - final Limits tempLimits = await selectedProviders[i].fetchLimits( - from: from, - to: to, - isFixedRateMode: isFixedRateMode); - - /// set the limits with the maximum provider limit - /// if there is a provider with null max then it's the maximum limit - if ((tempLimits.max ?? double.maxFinite) > limits.max!) { - limits = tempLimits; - } - } + for (var provider in selectedProviders) { + /// if this provider is not valid for the current pair, skip it + if (!providersForCurrentPair().contains(provider)) { + continue; } + try { + final tempLimits = await provider.fetchLimits( + from: from, + to: to, + isFixedRateMode: isFixedRateMode); + + if (tempLimits.min != null && tempLimits.min! < lowestMin) { + lowestMin = tempLimits.min!; + } + if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) { + highestMax = tempLimits.max; + } + } catch (e) { + continue; + } + } + + if (lowestMin < double.maxFinite) { + limits = Limits(min: lowestMin, max: highestMax); + limitsState = LimitsLoadedSuccessfully(limits: limits); - } catch (e) { - limitsState = LimitsLoadedFailure(error: e.toString()); + } else { + limitsState = LimitsLoadedFailure(error: 'Limits loading failed'); } } @@ -374,7 +376,7 @@ abstract class ExchangeViewModelBase with Store { TradeRequest? request; String amount = ''; - for (var provider in currentTradeAvailableProviders.values) { + for (var provider in _sortedAvailableProviders.values) { if (!(await provider.checkIsAvailable())) { continue; } @@ -383,7 +385,7 @@ abstract class ExchangeViewModelBase with Store { request = SideShiftRequest( depositMethod: depositCurrency, settleMethod: receiveCurrency, - depositAmount: depositAmount?.replaceAll(',', '.') ?? '', + depositAmount: depositAmount.replaceAll(',', '.'), settleAddress: receiveAddress, refundAddress: depositAddress, ); @@ -394,7 +396,7 @@ abstract class ExchangeViewModelBase with Store { request = SimpleSwapRequest( from: depositCurrency, to: receiveCurrency, - amount: depositAmount?.replaceAll(',', '.') ?? '', + amount: depositAmount.replaceAll(',', '.'), address: receiveAddress, refundAddress: depositAddress, ); @@ -405,8 +407,8 @@ abstract class ExchangeViewModelBase with Store { request = XMRTOTradeRequest( from: depositCurrency, to: receiveCurrency, - amount: depositAmount?.replaceAll(',', '.') ?? '', - receiveAmount: receiveAmount?.replaceAll(',', '.') ?? '', + amount: depositAmount.replaceAll(',', '.'), + receiveAmount: receiveAmount.replaceAll(',', '.'), address: receiveAddress, refundAddress: depositAddress, isBTCRequest: isReceiveAmountEntered); @@ -417,8 +419,8 @@ abstract class ExchangeViewModelBase with Store { request = ChangeNowRequest( from: depositCurrency, to: receiveCurrency, - fromAmount: depositAmount?.replaceAll(',', '.') ?? '', - toAmount: receiveAmount?.replaceAll(',', '.') ?? '', + fromAmount: depositAmount.replaceAll(',', '.'), + toAmount: receiveAmount.replaceAll(',', '.'), refundAddress: depositAddress, address: receiveAddress, isReverse: isReverse); @@ -429,7 +431,7 @@ abstract class ExchangeViewModelBase with Store { request = MorphTokenRequest( from: depositCurrency, to: receiveCurrency, - amount: depositAmount?.replaceAll(',', '.') ?? '', + amount: depositAmount.replaceAll(',', '.'), refundAddress: depositAddress, address: receiveAddress); amount = depositAmount; @@ -437,7 +439,7 @@ abstract class ExchangeViewModelBase with Store { amount = amount.replaceAll(',', '.'); - if (limitsState is LimitsLoadedSuccessfully && amount != null) { + if (limitsState is LimitsLoadedSuccessfully) { if (double.parse(amount) < limits.min!) { continue; } else if (limits.max != null && double.parse(amount) > limits.max!) { @@ -527,7 +529,7 @@ abstract class ExchangeViewModelBase with Store { final providers = providerList .where((provider) => provider.pairList .where((pair) => - pair.from == (from ?? depositCurrency) && pair.to == (to ?? receiveCurrency)) + pair.from == from && pair.to == to) .isNotEmpty) .toList(); @@ -537,6 +539,10 @@ abstract class ExchangeViewModelBase with Store { void _onPairChange() { depositAmount = ''; receiveAmount = ''; + loadLimits(); + _setAvailableProviders(); + _bestRate = 0; + _calculateBestRate(); } void _initialPairBasedOnWallet() { @@ -579,11 +585,15 @@ abstract class ExchangeViewModelBase with Store { @action void addExchangeProvider(ExchangeProvider provider) { selectedProviders.add(provider); + if (providersForCurrentPair().contains(provider)) { + _tradeAvailableProviders.add(provider); + } } @action void removeExchangeProvider(ExchangeProvider provider) { selectedProviders.remove(provider); + _tradeAvailableProviders.remove(provider); } @action @@ -593,13 +603,14 @@ abstract class ExchangeViewModelBase with Store { isFixedRateMode = false; _defineIsReceiveAmountEditable(); loadLimits(); + _bestRate = 0; + _calculateBestRate(); final Map exchangeProvidersSelection = json .decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") as Map; - exchangeProvidersSelection.updateAll((key, dynamic value) => false); - for (var provider in selectedProviders) { - exchangeProvidersSelection[provider.title] = true; + for (var provider in providerList) { + exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider); } sharedPreferences.setString( @@ -612,4 +623,30 @@ abstract class ExchangeViewModelBase with Store { final providersForPair = providersForCurrentPair(); return selectedProviders.any((element) => element.isAvailable && providersForPair.contains(element)); } + + void _setAvailableProviders() { + _tradeAvailableProviders.clear(); + + _tradeAvailableProviders.addAll( + selectedProviders + .where((provider) => providersForCurrentPair().contains(provider))); + } + + @action + void setDefaultTransactionPriority() { + switch (wallet.type) { + case WalletType.monero: + case WalletType.haven: + _settingsStore.priority[wallet.type] = MoneroTransactionPriority.automatic; + break; + case WalletType.bitcoin: + _settingsStore.priority[wallet.type] = BitcoinTransactionPriority.medium; + break; + case WalletType.litecoin: + _settingsStore.priority[wallet.type] = LitecoinTransactionPriority.medium; + break; + default: + break; + } + } } diff --git a/lib/view_model/restore_from_backup_view_model.dart b/lib/view_model/restore_from_backup_view_model.dart index 41bbd5b0a..d3542a668 100644 --- a/lib/view_model/restore_from_backup_view_model.dart +++ b/lib/view_model/restore_from_backup_view_model.dart @@ -64,7 +64,7 @@ abstract class RestoreFromBackupViewModelBase with Store { } catch (e) { var msg = e.toString(); - if (msg == 'Message authentication code (MAC) is invalid') { + if (msg.toLowerCase().contains("message authentication code (mac)")) { msg = 'Incorrect backup password'; } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index b6bac59e0..cdef6faba 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,7 +1,6 @@ import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; -import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/view_model/send/output.dart'; import 'package:cake_wallet/view_model/send/send_template_view_model.dart'; @@ -276,7 +275,7 @@ abstract class SendViewModelBase with Store { if (isElectrumWallet) { final rate = bitcoin!.getFeeRate(wallet, _priority); - return '${(priority as BitcoinTransactionPriority).labelWithRate(rate)}'; + return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate); } return priority.toString(); diff --git a/lib/view_model/settings/settings_view_model.dart b/lib/view_model/settings/settings_view_model.dart index c3ca2e3b9..96b2e7bdd 100644 --- a/lib/view_model/settings/settings_view_model.dart +++ b/lib/view_model/settings/settings_view_model.dart @@ -1,7 +1,6 @@ import 'package:cake_wallet/entities/language_service.dart'; import 'package:cake_wallet/store/yat/yat_store.dart'; import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:flutter/cupertino.dart'; import 'package:mobx/mobx.dart'; import 'package:package_info/package_info.dart'; @@ -137,7 +136,7 @@ abstract class SettingsViewModelBase with Store { if (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) { final rate = bitcoin!.getFeeRate(wallet, _priority); - return '${(priority as BitcoinTransactionPriority).labelWithRate(rate)}'; + return bitcoin!.bitcoinTransactionPriorityWithLabel(_priority, rate); } return priority.toString(); diff --git a/lib/view_model/support_view_model.dart b/lib/view_model/support_view_model.dart index 83c65bb53..5fa81b288 100644 --- a/lib/view_model/support_view_model.dart +++ b/lib/view_model/support_view_model.dart @@ -53,6 +53,16 @@ abstract class SupportViewModelBase with Store { icon: 'assets/images/change_now.png', linkTitle: 'support@changenow.io', link: 'mailto:support@changenow.io'), + LinkListItem( + title: 'SideShift', + icon: 'assets/images/sideshift.png', + linkTitle: S.current.help, + link: 'https://help.sideshift.ai/en/'), + LinkListItem( + title: 'SimpleSwap', + icon: 'assets/images/simpleSwap.png', + linkTitle: 'support@simpleswap.io', + link: 'mailto:support@simpleswap.io'), if (!isMoneroOnly) ... [ LinkListItem( title: 'Wyre', diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 3513cc4ad..248a06de0 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -25,9 +25,7 @@ dependencies: mobx: ^2.0.7+4 flutter_mobx: ^2.0.6+1 flutter_slidable: ^2.0.0 - share: ^2.0.1 - # share_plus: ^4.0.10 - # esys_flutter_share: ^1.0.2 + share_plus: ^4.0.10 # date_range_picker: ^1.0.6 #https://api.flutter.dev/flutter/material/showDateRangePicker.html dio: ^4.0.6 @@ -54,8 +52,7 @@ dependencies: connectivity: ^3.0.3 # connectivity_plus: ^2.3.5 keyboard_actions: ^4.0.1 - flushbar: ^1.10.4 - # check flushbar for replace + another_flushbar: ^1.12.29 archive: ^3.3.0 cryptography: ^2.0.5 file_picker: ^4.6.1 @@ -64,6 +61,11 @@ dependencies: permission_handler: ^10.0.0 device_display_brightness: ^0.0.6 platform_device_id: ^1.0.1 + cake_backup: + git: + url: https://github.com/cake-tech/cake_backup.git + ref: main + version: 1.0.0 dev_dependencies: flutter_test: diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index fa04a6a8e..6692acb61 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Wähle aus verfügbaren Optionen:", "custom_redeem_amount": "Benutzerdefinierter Einlösungsbetrag", "add_custom_redemption": "Benutzerdefinierte Einlösung hinzufügen", - "remaining": "Rest" + "remaining": "Rest", + "delete_wallet": "Geldbörse löschen", + "delete_wallet_confirm_message" : "Sind Sie sicher, dass Sie das ${wallet_name} Wallet löschen möchten?", + "low_fee": "Niedrige Gebühr", + "low_fee_alert": "Sie verwenden derzeit eine niedrige Netzwerkgebührenpriorität. Dies kann zu langen Wartezeiten, unterschiedlichen Kursen oder stornierten Trades führen. Wir empfehlen, für ein besseres Erlebnis eine höhere Gebühr festzulegen.", + "ignor": "Ignorieren", + "use_suggested": "Vorgeschlagen verwenden", + "do_not_share_warning_text" : "Teilen Sie diese nicht mit anderen, einschließlich des Supports.\n\nSie werden Ihr Geld stehlen!", + "help": "hilfe" } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 4a46d36c8..bcf13c129 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Choose from the available options:", "custom_redeem_amount": "Custom Redeem Amount", "add_custom_redemption": "Add Custom Redemption", - "remaining": "remaining" + "remaining": "remaining", + "delete_wallet": "Delete wallet", + "delete_wallet_confirm_message" : "Are you sure that you want to delete ${wallet_name} wallet?", + "low_fee": "Low fee", + "low_fee_alert": "You currently are using a low network fee priority. This could cause long waits, different rates, or canceled trades. We recommend setting a higher fee for a better experience.", + "ignor": "Ignore", + "use_suggested": "Use Suggested", + "do_not_share_warning_text" : "Do not share these with anyone else, including support.\n\nThey will steal your money!", + "help": "help" } diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 756ac3e11..db3a69e17 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Elija entre las opciones disponibles:", "custom_redeem_amount": "Cantidad de canje personalizada", "add_custom_redemption": "Agregar redención personalizada", - "remaining": "restante" + "remaining": "restante", + "delete_wallet": "Eliminar billetera", + "delete_wallet_confirm_message" : "¿Está seguro de que desea eliminar la billetera ${wallet_name}?", + "low_fee": "Tarifa baja", + "low_fee_alert": "Actualmente está utilizando una prioridad de tarifa de red baja. Esto podría causar largas esperas, tarifas diferentes o transacciones canceladas. Recomendamos establecer una tarifa más alta para una mejor experiencia.", + "ignor": "Pasar por alto", + "use_suggested": "Usar sugerido", + "do_not_share_warning_text" : "No comparta estos con nadie más, incluido el soporte.\n\n¡Te robarán tu dinero!", + "help": "ayuda" } diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 04462943e..da68d18aa 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -642,5 +642,13 @@ "choose_from_available_options": "Choisissez parmi les options disponibles :", "custom_redeem_amount": "Montant d'échange personnalisé", "add_custom_redemption": "Ajouter un remboursement personnalisé", - "remaining": "restant" + "remaining": "restant", + "delete_wallet": "Supprimer le portefeuille", + "delete_wallet_confirm_message" : "Êtes-vous sûr de vouloir supprimer le portefeuille ${wallet_name}?", + "low_fee": "Frais modiques", + "low_fee_alert": "Vous utilisez actuellement une priorité de frais de réseau peu élevés. Cela pourrait entraîner de longues attentes, des taux différents ou des transactions annulées. Nous vous recommandons de fixer des frais plus élevés pour une meilleure expérience.", + "ignor": "Ignorer", + "use_suggested": "Utilisation suggérée", + "do_not_share_warning_text" : "Ne les partagez avec personne d'autre, y compris avec l'assistance.\n\nIls vont voler votre argent!", + "help": "aider" } diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index f8bad76e2..128b533a3 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "उपलब्ध विकल्पों में से चुनें:", "custom_redeem_amount": "कस्टम रिडीम राशि", "add_custom_redemption": "कस्टम रिडेम्पशन जोड़ें", - "remaining": "शेष" + "remaining": "शेष", + "delete_wallet": "वॉलेट हटाएं", + "delete_wallet_confirm_message" : "क्या आप वाकई ${wallet_name} वॉलेट हटाना चाहते हैं?", + "low_fee": "कम शुल्क", + "low_fee_alert": "आप वर्तमान में कम नेटवर्क शुल्क प्राथमिकता का उपयोग कर रहे हैं। यह लंबे इंतजार, अलग-अलग दरों या रद्द किए गए ट्रेडों का कारण बन सकता है। हम बेहतर अनुभव के लिए अधिक शुल्क निर्धारित करने की सलाह देते हैं।", + "ignor": "नज़रअंदाज़ करना", + "use_suggested": "सुझाए गए का प्रयोग करें", + "do_not_share_warning_text" : "इन्हें समर्थन सहित किसी और के साथ साझा न करें।\n\nवे आपका पैसा चुरा लेंगे!", + "help": "मदद करना" } diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 0e27e3a2c..6e15984ac 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Odaberite neku od dostupnih opcija:", "custom_redeem_amount": "Prilagođeni iznos otkupa", "add_custom_redemption": "Dodaj prilagođeni otkup", - "remaining": "preostalo" + "remaining": "preostalo", + "delete_wallet": "Izbriši novčanik", + "delete_wallet_confirm_message" : "Jeste li sigurni da želite izbrisati ${wallet_name} novčanik?", + "low_fee": "Niska naknada", + "low_fee_alert": "Trenutačno koristite niski prioritet mrežne naknade. To bi moglo uzrokovati duga čekanja, različite tečajeve ili otkazane trgovine. Preporučujemo postavljanje veće naknade za bolje iskustvo.", + "ignor": "Zanemariti", + "use_suggested": "Koristite predloženo", + "do_not_share_warning_text" : "Nemojte ih dijeliti ni s kim, uključujući podršku.\n\nUkrast će vam novac!", + "help": "pomozite" } diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index ea9bbd5be..49f962a9d 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Scegli tra le opzioni disponibili:", "custom_redeem_amount": "Importo di riscatto personalizzato", "add_custom_redemption": "Aggiungi riscatto personalizzato", - "remaining": "rimanente" + "remaining": "rimanente", + "delete_wallet": "Elimina portafoglio", + "delete_wallet_confirm_message" : "Sei sicuro di voler eliminare il portafoglio ${wallet_name}?", + "low_fee": "Tariffa bassa", + "low_fee_alert": "Attualmente stai utilizzando una priorità a tariffa di rete bassa. Ciò potrebbe causare lunghe attese, tariffe diverse o operazioni annullate. Ti consigliamo di impostare una tariffa più alta per un'esperienza migliore.", + "ignor": "Ignorare", + "use_suggested": "Usa suggerito", + "do_not_share_warning_text" : "Non condividerli con nessun altro, incluso il supporto.\n\nTi ruberanno i soldi!", + "help": "aiuto" } diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 5ae3a5a1a..249e5f038 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "利用可能なオプションから選択してください:", "custom_redeem_amount": "カスタム交換金額", "add_custom_redemption": "カスタム引き換えを追加", - "remaining": "残り" + "remaining": "残り", + "delete_wallet": "ウォレットを削除", + "delete_wallet_confirm_message" : "${wallet_name} ウォレットを削除してもよろしいですか?", + "low_fee": "低料金", + "low_fee_alert": "現在、低ネットワーク料金優先度を使用しています。これにより、長い待ち時間、異なるレート、またはキャンセルされた取引が発生する可能性があります。より良い体験のために、より高い料金を設定することをお勧めします。", + "ignor": "無視", + "use_suggested": "推奨を使用", + "do_not_share_warning_text" : "サポートを含め、これらを他の誰とも共有しないでください。\n\n彼らはあなたのお金を盗みます!", + "help": "ヘルプ" } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index f934784ba..fabc69ed6 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "사용 가능한 옵션에서 선택:", "custom_redeem_amount": "사용자 지정 상환 금액", "add_custom_redemption": "사용자 지정 상환 추가", - "remaining": "남은" + "remaining": "남은", + "delete_wallet": "지갑 삭제", + "delete_wallet_confirm_message" : "${wallet_name} 지갑을 삭제하시겠습니까?", + "low_fee": "낮은 수수료", + "low_fee_alert": "현재 낮은 네트워크 요금 우선 순위를 사용하고 있습니다. 이로 인해 긴 대기 시간, 다른 요금 또는 취소된 거래가 발생할 수 있습니다. 더 나은 경험을 위해 더 높은 요금을 설정하는 것이 좋습니다.", + "ignor": "무시하다", + "use_suggested": "추천 사용", + "do_not_share_warning_text" : "지원을 포함하여 다른 사람과 이러한 정보를 공유하지 마십시오.\n\n그들은 당신의 돈을 훔칠 것입니다!", + "help": "돕다" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 428da3d76..bbbeafb16 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Kies uit de beschikbare opties:", "custom_redeem_amount": "Aangepast inwisselbedrag", "add_custom_redemption": "Voeg aangepaste inwisseling toe", - "remaining": "resterende" + "remaining": "resterende", + "delete_wallet": "Portemonnee verwijderen", + "delete_wallet_confirm_message" : "Weet u zeker dat u de portemonnee van ${wallet_name} wilt verwijderen?", + "low_fee": "Lage vergoeding", + "low_fee_alert": "U gebruikt momenteel een lage prioriteit voor netwerkkosten. Dit kan lange wachttijden, andere tarieven of geannuleerde transacties veroorzaken. We raden aan een hogere vergoeding in te stellen voor een betere ervaring.", + "ignor": "Negeren", + "use_suggested": "Gebruik aanbevolen", + "do_not_share_warning_text" : "Deel deze met niemand anders, ook niet met support.\n\nZe zullen je geld stelen!", + "help": "helpen" } diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index fda0ea721..aba1075f2 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Wybierz z dostępnych opcji:", "custom_redeem_amount": "Niestandardowa kwota wykorzystania", "add_custom_redemption": "Dodaj niestandardowe wykorzystanie", - "remaining": "pozostałe" + "remaining": "pozostałe", + "delete_wallet": "Usuń portfel", + "delete_wallet_confirm_message" : "Czy na pewno chcesz usunąć portfel ${wallet_name}?", + "low_fee": "Niska opłata", + "low_fee_alert": "Obecnie korzystasz z niskiego priorytetu opłaty sieciowej. Może to spowodować długie oczekiwanie, różne stawki lub anulowane transakcje. Zalecamy ustawienie wyższej opłaty, aby zapewnić lepsze wrażenia.", + "ignor": "Ignorować", + "use_suggested": "Użyj sugerowane", + "do_not_share_warning_text" : "Nie udostępniaj ich nikomu innemu, w tym wsparcia.\n\nUkradną twoje pieniądze!", + "help": "pomoc" } diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 6a55cf534..69b24c910 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -643,5 +643,13 @@ "choose_from_available_options": "Escolha entre as opções disponíveis:", "custom_redeem_amount": "Valor de resgate personalizado", "add_custom_redemption": "Adicionar resgate personalizado", - "remaining": "restante" + "remaining": "restante", + "delete_wallet": "Excluir carteira", + "delete_wallet_confirm_message" : "Tem certeza de que deseja excluir a carteira ${wallet_name}?", + "low_fee": "Taxa baixa", + "low_fee_alert": "No momento, você está usando uma prioridade de taxa de rede baixa. Isso pode causar longas esperas, taxas diferentes ou negociações canceladas. Recomendamos definir uma taxa mais alta para uma melhor experiência.", + "ignor": "Ignorar", + "use_suggested": "Uso sugerido", + "do_not_share_warning_text" : "Não os compartilhe com mais ninguém, incluindo suporte.\n\nEles vão roubar seu dinheiro!", + "help": "ajuda" } diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index ebc4a615c..33ec2f971 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -644,5 +644,13 @@ "choose_from_available_options": "Выберите из доступных вариантов:", "custom_redeem_amount": "Пользовательская сумма погашения", "add_custom_redemption": "Добавить пользовательское погашение", - "remaining": "осталось" + "remaining": "осталось", + "delete_wallet": "Удалить кошелек", + "delete_wallet_confirm_message" : "Вы уверены, что хотите удалить кошелек ${wallet_name}?", + "low_fee": "Низкая комиссия", + "low_fee_alert": "В настоящее время вы используете низкий приоритет платы за сеть. Это может привести к длительному ожиданию, изменению ставок или отмене сделок. Мы рекомендуем установить более высокую плату для лучшего опыта.", + "ignor": "Игнорировать", + "use_suggested": "Использовать предложенный", + "do_not_share_warning_text" : "Не делитесь ими с кем-либо еще, в том числе со службой поддержки.\n\nОни украдут ваши деньги!", + "help": "помощь" } diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index efbd5a3b8..7d05c7f81 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -643,5 +643,13 @@ "choose_from_available_options": "Виберіть із доступних варіантів:", "custom_redeem_amount": "Власна сума викупу", "add_custom_redemption": "Додати спеціальне погашення", - "remaining": "залишилося" + "remaining": "залишилося", + "delete_wallet": "Видалити гаманець", + "delete_wallet_confirm_message" : "Ви впевнені, що хочете видалити гаманець ${wallet_name}?", + "low_fee": "Низька плата", + "low_fee_alert": "Зараз ви використовуєте низький пріоритет плати за мережу. Це може спричинити тривале очікування, інший курс або скасування угод. Ми рекомендуємо встановити вищу плату для кращого досвіду.", + "ignor": "Ігнорувати", + "use_suggested": "Використати запропоноване", + "do_not_share_warning_text" : "Не повідомляйте їх нікому, включно зі службою підтримки.\n\nВони вкрадуть ваші гроші!", + "help": "допомога" } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 83990cf2e..ab96bb9ec 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -642,5 +642,13 @@ "choose_from_available_options": "从可用选项中选择:", "custom_redeem_amount": "自定义兑换金额", "add_custom_redemption": "添加自定义兑换", - "remaining": "剩余" + "remaining": "剩余", + "delete_wallet": "删除钱包", + "delete_wallet_confirm_message" : "您确定要删除 ${wallet_name} 钱包吗?", + "low_fee": "费用低", + "low_fee_alert": "您当前正在使用低网络费用优先级。这可能会导致长时间等待、不同的费率或取消交易。我们建议设置更高的费用以获得更好的体验。", + "ignor": "忽视", + "use_suggested": "使用建议", + "do_not_share_warning_text" : "不要與其他任何人分享這些內容,包括支持。\n\n他們會偷你的錢!", + "help": "帮助" } diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh old mode 100755 new mode 100644 index 311913329..48fd9c196 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -14,14 +14,14 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.1.1" -MONERO_COM_BUILD_NUMBER=20 +MONERO_COM_VERSION="1.1.2" +MONERO_COM_BUILD_NUMBER=21 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.4.7" -CAKEWALLET_BUILD_NUMBER=119 +CAKEWALLET_VERSION="4.4.8" +CAKEWALLET_BUILD_NUMBER=122 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh old mode 100755 new mode 100644 index 236482b05..d20e921a0 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.1.1" -MONERO_COM_BUILD_NUMBER=22 +MONERO_COM_VERSION="1.1.2" +MONERO_COM_BUILD_NUMBER=23 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.4.7" -CAKEWALLET_BUILD_NUMBER=118 +CAKEWALLET_VERSION="4.4.8" +CAKEWALLET_BUILD_NUMBER=121 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/tool/configure.dart b/tool/configure.dart index 3b2414192..ca8d3c9fb 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -86,6 +86,7 @@ abstract class Bitcoin { String formatterBitcoinAmountToString({required int amount}); double formatterBitcoinAmountToDouble({required int amount}); int formatterStringDoubleToBitcoinAmount(String amount); + String bitcoinTransactionPriorityWithLabel(TransactionPriority priority, int rate); List getUnspents(Object wallet); void updateUnspents(Object wallet); diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 9bf360017..4502bca74 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -25,7 +25,8 @@ class SecretKey { SecretKey('moonPaySecretKey', () => ''), SecretKey('sideShiftAffiliateId', () => ''), SecretKey('sideShiftApiKey', () => ''), - SecretKey('simpleSwapApiKey', () => '') + SecretKey('simpleSwapApiKey', () => ''), + SecretKey('onramperApiKey', () => ''), ]; final String name;