Cw 1379 add monero support to trezor rebase dev (#3273)
* refactor: migrate hardware wallet resources to `new-ui`, add Trezor model support, and streamline hardware wallet handling across services * refactor: migrate hardware wallet resources to `new-ui`, add Trezor model support, and streamline hardware wallet handling across services * fix: correctly check for Ledger hardware wallet type in `isHardwareWallet` logic * refactor: unify `isConnected` method for hardware wallets, add Trezor-specific support, and update Monero sync logic * refactor: add Trezor transaction signing support, streamline hardware wallet handling, and update Monero dependency * refactor: improve Trezor transaction handling, adjust hardware wallet state logic, and update Monero dependency references * fix: non-hww tx commit() in xmr fix: prod monero_c fix: keyImage import workaround for Trezor/Cupcake chore: add kotlin's .salive to .gitignore * fix: universal_ble builds on iOS chore: remove prints fix: proper CI prebuilt for linux * new trezor connection ui * fix page title * typo * fix page title anim * refactor: enhance Trezor pairing flow, update state management, and simplify UI logic in hardware wallet integration * fix: correct widget indentation in Trezor pairing failure state UI * fix: extract Trezor pairing failure state UI to `_errorBox` widget and update Trezor dependency reference * refactor: simplify hardware wallet pairing error UI, optimize `_errorBox` layout, and streamline widget logic * Update cw_monero/lib/pending_monero_transaction.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Update lib/buy/robinhood/robinhood_buy_provider.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * feat: add USB intent filters, device filter for hardware wallets, and update Trezor dependency reference * Update cw_monero/pubspec.yaml [skip ci] * Update cw_wownero/pubspec.yaml [skip ci] * Update cw_zano/pubspec.yaml [skip ci] * fix: use CryptoCurrency.title instead of amount-refactor-only .symbol getter new_wallet_type_page used curr.symbol, which only exists on the amount-refactor branch (String get symbol => title). On dev the equivalent is curr.title. Adapts the Trezor feature to dev's API without pulling in amount-refactor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix wallet list screen not scrollable minor fixes --------- Co-authored-by: Konstantin Ullrich <konstantinullrich12@gmail.com> Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net> Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com> Co-authored-by: Konstantin Ullrich <konstantin@cakewallet.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@@ -118,7 +118,7 @@ jobs:
|
||||
pushd scripts/monero_c/release
|
||||
mkdir -p $(git describe --tags)
|
||||
pushd $(git describe --tags)
|
||||
wget https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
|
||||
wget https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC2/release-bundle.zip
|
||||
unzip release-bundle.zip
|
||||
rm release-bundle.zip
|
||||
popd
|
||||
|
||||
@@ -225,3 +225,5 @@ scripts/zcash_lib
|
||||
scripts/android/yttrium
|
||||
scripts/bitbox_flutter
|
||||
scripts/reown_flutter
|
||||
|
||||
*.salive
|
||||
@@ -60,7 +60,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId appProperties['id']
|
||||
minSdkVersion 24
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 36
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
||||
@@ -128,6 +128,14 @@
|
||||
<data android:scheme="nano-gpt" />
|
||||
</intent-filter>
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||
android:resource="@xml/device_filter" />
|
||||
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
|
||||
</activity>
|
||||
<meta-data
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<!-- BitBox02 Vendor ID: 0x03eb, Product ID: 0x2403 -->
|
||||
<usb-device vendor-id="1003" product-id="9219"/>
|
||||
|
||||
<!-- Trezor Devices -->
|
||||
<usb-device vendor-id="21324" product-id="1"/>
|
||||
<usb-device vendor-id="4617" product-id="21440"/>
|
||||
<usb-device vendor-id="4617" product-id="21441"/>
|
||||
</resources>
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.5 2C17.8807 2 19 3.11929 19 4.5V5C19.2761 5 19.5 5.22386 19.5 5.5V8C19.5 8.24171 19.3286 8.44371 19.1006 8.49023L19 8.5V19.5C19 20.8807 17.8807 22 16.5 22H7.5C6.11929 22 5 20.8807 5 19.5V4.5C5 3.11929 6.11929 2 7.5 2H16.5ZM14.5 19C13.9477 19 13.5 19.4477 13.5 20C13.5 20.5523 13.9477 21 14.5 21H16.5C17.0523 21 17.5 20.5523 17.5 20C17.5 19.4477 17.0523 19 16.5 19H14.5ZM7.5 3.5C6.94772 3.5 6.5 3.94772 6.5 4.5V16.5C6.5 17.0523 6.94772 17.5 7.5 17.5H16.5C17.0523 17.5 17.5 17.0523 17.5 16.5V4.5C17.5 3.94772 17.0523 3.5 16.5 3.5H7.5Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 665 B |
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32">
|
||||
<path fill="#000" fill-rule="evenodd" d="M7 6a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4v12.191a9 9 0 0 1-1.204 4.498l-3.353 5.81A3 3 0 0 1 17.845 30h-3.69a3 3 0 0 1-2.598-1.5l-3.353-5.811A9 9 0 0 1 7 18.19zm4-2a2 2 0 0 0-2 2v12.191a7 7 0 0 0 .937 3.498l3.352 5.81a1 1 0 0 0 .866.501h3.69a1 1 0 0 0 .866-.5l3.352-5.81a7 7 0 0 0 .937-3.5V6a2 2 0 0 0-2-2z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 447 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -29,7 +29,7 @@ enum DeviceConnectionType {
|
||||
break;
|
||||
case HardwareWalletType.trezor:
|
||||
isSupported = [
|
||||
// WalletType.monero,
|
||||
WalletType.monero,
|
||||
WalletType.bitcoin,
|
||||
WalletType.litecoin,
|
||||
WalletType.ethereum,
|
||||
|
||||
@@ -4,7 +4,8 @@ import 'dart:typed_data';
|
||||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
|
||||
abstract class HardwareWalletService {
|
||||
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5});
|
||||
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) =>
|
||||
throw UnimplementedError();
|
||||
|
||||
Future<Uint8List> signTransaction({required String transaction}) => throw UnimplementedError();
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ enum HardwareWalletType {
|
||||
coldcard,
|
||||
seedsigner,
|
||||
keystone,
|
||||
trezor,
|
||||
trezor;
|
||||
}
|
||||
|
||||
enum WalletInfoAddressType {
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -118,3 +119,10 @@ void _logLedgerCommand(Uint8List command, [bool isResponse = true]) {
|
||||
"> ${_ledgerMoneroCommands[command[1]]} ${toHexString(command.sublist(2))}");
|
||||
}
|
||||
}
|
||||
|
||||
class MoneroLedgerService extends HardwareWalletService {
|
||||
final LedgerConnection connection;
|
||||
|
||||
MoneroLedgerService(this.connection);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import 'package:cw_monero/monero_unspent.dart';
|
||||
import 'package:cw_monero/monero_wallet_addresses.dart';
|
||||
import 'package:cw_monero/monero_wallet_service.dart';
|
||||
import 'package:cw_monero/pending_monero_transaction.dart';
|
||||
import 'package:cw_monero/trezor.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||
@@ -385,8 +386,42 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
currentWallet!.hasUnknownKeyImages();
|
||||
}
|
||||
|
||||
MoneroTrezorService? trezorService;
|
||||
Future<void> syncTrezor() async {
|
||||
if (trezorService == null) throw Exception("Trezor not connected");
|
||||
|
||||
final ptr = Pointer<Void>.fromAddress(currentWallet!.ffiAddress());
|
||||
final tdis = monero.Wallet_exportTrezorTdis(ptr);
|
||||
|
||||
final response = await Trezor(trezorService!).keyImageSync(tdis);
|
||||
|
||||
final success = monero.Wallet_importTrezorEncryptedKeyImagesJson(ptr, response);
|
||||
|
||||
if (!success) throw Exception(monero.Wallet_errorString(ptr));
|
||||
}
|
||||
|
||||
Future<String> signTrezorTransaction(String json) async {
|
||||
if (trezorService == null) throw Exception("Trezor not connected");
|
||||
return Trezor(trezorService!).signTransaction(json);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
if (hardwareWalletType == HardwareWalletType.trezor) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
try {
|
||||
return await _createTransaction(credentials);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
await save();
|
||||
await Future.delayed(Duration(seconds: i));
|
||||
}
|
||||
}
|
||||
return await _createTransaction(credentials);
|
||||
}
|
||||
|
||||
Future<PendingTransaction> _createTransaction(Object credentials) async {
|
||||
final _credentials = credentials as MoneroTransactionCreationCredentials;
|
||||
final inputs = <String>[];
|
||||
final outputs = _credentials.outputs;
|
||||
|
||||
@@ -3,8 +3,8 @@ import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/get_height_by_date.dart';
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/monero_wallet_utils.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
@@ -20,8 +20,8 @@ import 'package:cw_monero/api/wallet_manager.dart';
|
||||
import 'package:cw_monero/bip39_seed.dart';
|
||||
import 'package:cw_monero/ledger.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:cw_monero/trezor.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||
import 'package:monero/monero.dart' as monero;
|
||||
import 'package:polyseed/polyseed.dart';
|
||||
|
||||
@@ -46,11 +46,11 @@ class MoneroNewWalletCredentials extends WalletCredentials {
|
||||
class MoneroRestoreWalletFromHardwareCredentials extends WalletCredentials {
|
||||
MoneroRestoreWalletFromHardwareCredentials(
|
||||
{required String name,
|
||||
required this.ledgerConnection,
|
||||
required this.hardwareWalletService,
|
||||
int height = 0,
|
||||
String? password})
|
||||
: super(name: name, password: password, height: height);
|
||||
LedgerConnection ledgerConnection;
|
||||
HardwareWalletService hardwareWalletService;
|
||||
}
|
||||
|
||||
class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||
@@ -193,7 +193,7 @@ class MoneroWalletService extends WalletService<
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
password: password);
|
||||
|
||||
if (wallet.isHardwareWallet) {
|
||||
if (wallet.hardwareWalletType == HardwareWalletType.ledger) {
|
||||
wallet.setLedgerConnection(gLedger!);
|
||||
gLedger = null;
|
||||
}
|
||||
@@ -301,15 +301,29 @@ class MoneroWalletService extends WalletService<
|
||||
try {
|
||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||
final password = credentials.password;
|
||||
final height = credentials.height;
|
||||
|
||||
enableLedgerExchange(credentials.ledgerConnection);
|
||||
if (credentials.hardwareWalletService case MoneroLedgerService service) {
|
||||
enableLedgerExchange(service.connection);
|
||||
|
||||
await monero_wallet_manager.restoreWalletFromHardwareWallet(
|
||||
await monero_wallet_manager.restoreWalletFromHardwareWallet(
|
||||
path: path,
|
||||
password: password!,
|
||||
restoreHeight: height!,
|
||||
deviceName: 'Ledger');
|
||||
restoreHeight: credentials.height!,
|
||||
deviceName: 'Ledger',
|
||||
);
|
||||
} else if (credentials.hardwareWalletService case MoneroTrezorService service) {
|
||||
final watchCredentials = await Trezor(service).getWatchCredentials();
|
||||
|
||||
monero_wallet_manager.restoreWalletFromKeys(
|
||||
path: path,
|
||||
password: credentials.password!,
|
||||
language: "English",
|
||||
restoreHeight: credentials.height!,
|
||||
address: watchCredentials.address,
|
||||
viewKey: watchCredentials.watchKey,
|
||||
spendKey: "",
|
||||
);
|
||||
}
|
||||
|
||||
final wallet = MoneroWallet(
|
||||
walletInfo: credentials.walletInfo!,
|
||||
@@ -567,7 +581,7 @@ class MoneroWalletService extends WalletService<
|
||||
if (walletInfo == null) {
|
||||
return false;
|
||||
}
|
||||
return walletInfo.isHardwareWallet;
|
||||
return walletInfo.hardwareWalletType == HardwareWalletType.ledger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,4 +599,4 @@ Future<void> closeWalletAwaitIfShould(int wmaddr, int waddr) async {
|
||||
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_monero/api/account_list.dart';
|
||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/transaction_history.dart'
|
||||
@@ -10,6 +13,7 @@ import 'package:cw_core/amount_converter.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/wallet.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:monero/monero.dart' as monero;
|
||||
|
||||
class DoubleSpendException implements Exception {
|
||||
DoubleSpendException();
|
||||
@@ -43,22 +47,39 @@ class PendingMoneroTransaction with PendingTransaction {
|
||||
CryptoCurrency.xmr, pendingTransactionDescription.fee);
|
||||
|
||||
@override
|
||||
bool shouldCommitUR() => isViewOnly;
|
||||
bool shouldCommitUR() => isViewOnly && wallet.hardwareWalletType != HardwareWalletType.trezor;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
try {
|
||||
await monero_transaction_history.commitTransactionFromPointerAddress(
|
||||
address: pendingTransactionDescription.pointerAddress,
|
||||
useUR: false);
|
||||
} catch (e) {
|
||||
final message = e.toString();
|
||||
if (wallet.hardwareWalletType == HardwareWalletType.trezor) {
|
||||
final ptr = Pointer<Void>.fromAddress(pendingTransactionDescription.pointerAddress);
|
||||
final ret = await monero.PendingTransaction_commitTrezor(ptr, 0);
|
||||
|
||||
if (message.contains('Reason: double spend')) {
|
||||
throw DoubleSpendException();
|
||||
final json = await wallet.signTrezorTransaction(ret);
|
||||
|
||||
final wptr = Pointer<Void>.fromAddress(currentWallet!.ffiAddress());
|
||||
|
||||
final suc = await monero.Wallet_submitTransactionHex(wptr, json);
|
||||
|
||||
if (!suc) {
|
||||
final err = monero.UnsignedTransaction_errorString(ptr);
|
||||
printV(err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await monero_transaction_history.commitTransactionFromPointerAddress(
|
||||
address: pendingTransactionDescription.pointerAddress,
|
||||
useUR: false);
|
||||
} catch (e) {
|
||||
final message = e.toString();
|
||||
|
||||
rethrow;
|
||||
if (message.contains('Reason: double spend')) {
|
||||
throw DoubleSpendException();
|
||||
}
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
storeSync(force: true);
|
||||
unawaited(() async {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:trezor_flutter/trezor_flutter.dart';
|
||||
|
||||
class MoneroTrezorService extends HardwareWalletService {
|
||||
final TrezorClient client;
|
||||
|
||||
MoneroTrezorService(this.client);
|
||||
}
|
||||
|
||||
class MoneroTrezorWatchCredentials {
|
||||
final String watchKey;
|
||||
final String address;
|
||||
|
||||
const MoneroTrezorWatchCredentials(this.watchKey, this.address);
|
||||
}
|
||||
|
||||
class Trezor {
|
||||
final MoneroTrezorService service;
|
||||
|
||||
Trezor(this.service);
|
||||
|
||||
Future<MoneroTrezorWatchCredentials> getWatchCredentials() async {
|
||||
final credentials = await TrezorMonero(service.client).getWatchCredentials();
|
||||
|
||||
return MoneroTrezorWatchCredentials(credentials.$1, credentials.$2);
|
||||
}
|
||||
|
||||
Future<String> keyImageSync(String tdis) async {
|
||||
final tdisMap = jsonDecode(tdis) as Map<String, dynamic>;
|
||||
final tdisList = tdisMap["tdis"] as List<dynamic>;
|
||||
|
||||
final txIds = <MoneroKeyImageTxData>[];
|
||||
for (final tdi in tdisList) {
|
||||
txIds.add(MoneroKeyImageTxData(
|
||||
outKey: tdi["out_key"],
|
||||
txPubKey: tdi["tx_pub_key"],
|
||||
internalOutputIndex: tdi["internal_output_index"],
|
||||
subAddrMajor: tdi["sub_addr_major"],
|
||||
subAddrMinor: tdi["sub_addr_minor"],
|
||||
additionalTxPubKeys: (tdi["additional_tx_pub_keys"] as List?)?.map((e) => e as String).toList() ?? []
|
||||
));
|
||||
}
|
||||
final keyImages = await TrezorMonero(service.client).syncKeyImages(txIds);
|
||||
|
||||
return jsonEncode(keyImages.toMap());
|
||||
}
|
||||
|
||||
Future<String> signTransaction(String json) async {
|
||||
return TrezorMonero(service.client).signTransaction(jsonDecode(json));
|
||||
}
|
||||
}
|
||||
@@ -296,6 +296,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
fast_base58:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fast_base58
|
||||
sha256: "611f65633b734f27a850b51371b3eba993a5165650e12e8e7b02959f3768ba06"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -342,10 +350,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_web_bluetooth
|
||||
sha256: "1363831def5eed1e1064d1eca04e8ccb35446e8f758579c3c519e156b77926da"
|
||||
sha256: ad26a1b3fef95b86ea5f63793b9a0cdc1a33490f35d754e4e711046cae3ebbf8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -494,18 +502,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ledger_flutter_plus
|
||||
sha256: "1c03f3c4a9754b5f0170a9eb9552ec54fa86e985f8ee71a255ee2c5629b53d31"
|
||||
sha256: "59c5a6062505c721c2ca909fa085c52cffe93de995174b8418b54a6acf52eb70"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
version: "1.5.6"
|
||||
ledger_usb_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ledger_usb_plus
|
||||
sha256: "21cc5d976cf7edb3518bd2a0c4164139cbb0817d2e4f2054707fc4edfdf9ce87"
|
||||
sha256: "911df2d1d55cdae612ff2c9db140cf71a9b28a67ba16adfdc4983cd9f02c531e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.0.8"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -582,9 +590,9 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
resolved-ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
url: "http://github.com/mrcyjanek/monero_c.git"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
mutex:
|
||||
@@ -724,6 +732,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -962,6 +978,24 @@ packages:
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
trezor_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: trezor-flutter
|
||||
ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
|
||||
resolved-ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
|
||||
url: "https://github.com/cake-tech/trezor-flutter.git"
|
||||
source: git
|
||||
version: "0.1.0"
|
||||
trezor_usb_transport:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: trezor_usb_transport
|
||||
ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
|
||||
resolved-ref: "4bf54339a1caefd4c53a4a8873a9686ec13a9107"
|
||||
url: "https://github.com/cake-tech/trezor-flutter.git"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
tuple:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -982,10 +1016,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: universal_ble
|
||||
sha256: "1fad089150a29db82b3b7d60327e18c5ad6b3a5bb509defc1c690b0a76b9c098"
|
||||
sha256: "8325ca9f5cce2fba2b4efef74a4fcfe7144f303b409d6fc6ed16197b00a1a241"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.0"
|
||||
version: "1.2.0"
|
||||
universal_platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -25,11 +25,16 @@ dependencies:
|
||||
path: ../cw_core
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
url: https://github.com/mrcyjanek/monero_c.git
|
||||
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
ledger_flutter_plus: ^1.4.1
|
||||
trezor_flutter:
|
||||
git:
|
||||
url: https://github.com/cake-tech/trezor-flutter.git
|
||||
ref: 4bf54339a1caefd4c53a4a8873a9686ec13a9107
|
||||
path: trezor-flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -502,9 +502,9 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
resolved-ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
url: "http://github.com/mrcyjanek/monero_c.git"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
mutex:
|
||||
|
||||
@@ -23,8 +23,8 @@ dependencies:
|
||||
path: ../cw_core
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2 # monero_c hash
|
||||
url: https://github.com/mrcyjanek/monero_c.git
|
||||
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
path: impls/monero.dart
|
||||
mutex: ^3.1.0
|
||||
|
||||
|
||||
@@ -507,9 +507,9 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "impls/monero.dart"
|
||||
ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
resolved-ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
url: "https://github.com/mrcyjanek/monero_c"
|
||||
ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
resolved-ref: "3bfb3856a838f2bf6b729501837bb0295dedf25d"
|
||||
url: "http://github.com/mrcyjanek/monero_c.git"
|
||||
source: git
|
||||
version: "0.0.0"
|
||||
nested:
|
||||
|
||||
@@ -24,8 +24,8 @@ dependencies:
|
||||
fluttertoast: ^8.2.12
|
||||
monero:
|
||||
git:
|
||||
url: https://github.com/mrcyjanek/monero_c
|
||||
ref: bc8d1a0b75b97156d71579581b4cdfe58c777ed2 # monero_c hash
|
||||
url: https://github.com/mrcyjanek/monero_c.git
|
||||
ref: 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
path: impls/monero.dart
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -231,12 +231,9 @@ class CommonTestFlows {
|
||||
|
||||
await _newWalletTypePageRobot.findParticularWalletTypeInScrollableList(type);
|
||||
|
||||
// Tapping the wallet type navigates directly (no separate Next button).
|
||||
_tester.printToConsole('Tapping wallet type: ${type.name}');
|
||||
await _newWalletTypePageRobot.selectWalletType(type);
|
||||
await _tester.pumpAndSettle(Duration(milliseconds: 500));
|
||||
|
||||
_tester.printToConsole('Pressing next button');
|
||||
await _newWalletTypePageRobot.onNextButtonPressed();
|
||||
|
||||
await _tester.pumpAndSettle(Duration(milliseconds: 2000));
|
||||
|
||||
|
||||
@@ -77,11 +77,9 @@ class NewWalletTypePageRobot {
|
||||
tester.printToConsole('Wallet type ${type.name} is now visible and tappable');
|
||||
}
|
||||
|
||||
// Tapping a wallet type now navigates directly (the page no longer has a
|
||||
// separate "Next" button — selection happens on tap).
|
||||
Future<void> selectWalletType(WalletType type) async {
|
||||
await commonTestCases.tapItemByKey('new_wallet_type_${type.name}_button_key');
|
||||
}
|
||||
|
||||
Future<void> onNextButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('new_wallet_type_next_button_key');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@ SPEC CHECKSUMS:
|
||||
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
|
||||
warp_api: 997d8fb3e777669bc6260d0c12e56cd40b9db1d1
|
||||
YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966
|
||||
|
||||
PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a
|
||||
|
||||
@@ -336,7 +336,7 @@ class DFXBuyProvider extends BuyProvider {
|
||||
required String cryptoCurrencyAddress,
|
||||
String? countryCode}) async {
|
||||
if (wallet.isHardwareWallet) {
|
||||
if (!hardwareWalletVM!.isConnected) {
|
||||
if (!hardwareWalletVM!.isConnected(wallet.walletInfo.type)) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: wallet.walletInfo.type,
|
||||
|
||||
@@ -173,7 +173,7 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||
required String cryptoCurrencyAddress,
|
||||
String? countryCode}) async {
|
||||
if (wallet.isHardwareWallet) {
|
||||
if (!hardwareWalletVM!.isConnected) {
|
||||
if (!hardwareWalletVM!.isConnected(wallet.walletInfo.type)) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: wallet.walletInfo.type,
|
||||
|
||||
@@ -240,7 +240,7 @@ import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/bitbox_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/trezor_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/trezor_connect_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||
@@ -414,7 +414,7 @@ Future<void> setup({
|
||||
switch(type) {
|
||||
case HardwareWalletType.bitbox: return getIt<BitboxViewModel>();
|
||||
case HardwareWalletType.ledger: return getIt<LedgerViewModel>();
|
||||
case HardwareWalletType.trezor: return getIt<TrezorViewModel>();
|
||||
case HardwareWalletType.trezor: return getIt<TrezorConnectViewModel>();
|
||||
case HardwareWalletType.cupcake:
|
||||
case HardwareWalletType.coldcard:
|
||||
case HardwareWalletType.seedsigner:
|
||||
@@ -429,7 +429,7 @@ Future<void> setup({
|
||||
|
||||
getIt.registerLazySingleton(() => TrezorConnect("cakewallet://trezor_connect",
|
||||
appName: "Cake Wallet"));
|
||||
getIt.registerLazySingleton(() => TrezorViewModel(getIt<TrezorConnect>()));
|
||||
getIt.registerLazySingleton(() => TrezorConnectViewModel(getIt<TrezorConnect>()));
|
||||
|
||||
|
||||
getIt.registerFactory<KeyService>(() => KeyService(getIt.get<SecureStorage>()));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:bitbox_flutter/usb/bitbox_device.dart' as bitbox;
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
|
||||
import 'package:trezor_flutter/trezor_flutter.dart' as trezor;
|
||||
|
||||
abstract class HardwareWalletDevice {
|
||||
String get name;
|
||||
@@ -41,6 +42,21 @@ class BitboxHardwareWalletDevice extends HardwareWalletDevice {
|
||||
HardwareWalletConnectionType get connectionType => HardwareWalletConnectionType.usb;
|
||||
}
|
||||
|
||||
class TrezorHardwareWalletDevice extends HardwareWalletDevice {
|
||||
final trezor.TrezorDevice device;
|
||||
|
||||
TrezorHardwareWalletDevice(this.device);
|
||||
|
||||
@override
|
||||
String get name => device.name;
|
||||
|
||||
@override
|
||||
HardwareWalletDeviceType get type => device.deviceInfo.toGeneric();
|
||||
|
||||
@override
|
||||
HardwareWalletConnectionType get connectionType => device.connectionType.toGeneric();
|
||||
}
|
||||
|
||||
enum HardwareWalletDeviceType {
|
||||
ledgerBlue,
|
||||
ledgerNanoS,
|
||||
@@ -50,7 +66,12 @@ enum HardwareWalletDeviceType {
|
||||
ledgerStax,
|
||||
ledgerFlex,
|
||||
BitBox02,
|
||||
BitBox02Nova;
|
||||
BitBox02Nova,
|
||||
trezorModelOne,
|
||||
trezorModelT,
|
||||
trezorSafe3,
|
||||
trezorSafe5,
|
||||
trezorSafe7;
|
||||
}
|
||||
|
||||
enum HardwareWalletConnectionType {
|
||||
@@ -60,7 +81,7 @@ enum HardwareWalletConnectionType {
|
||||
qr;
|
||||
}
|
||||
|
||||
extension ToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
|
||||
extension LedgerToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
|
||||
HardwareWalletDeviceType toGeneric() {
|
||||
switch (this) {
|
||||
case ledger.LedgerDeviceType.blue:
|
||||
@@ -81,7 +102,24 @@ extension ToGenericHardwareWalletDeviceType on ledger.LedgerDeviceType {
|
||||
}
|
||||
}
|
||||
|
||||
extension ToGenericHardwareWalletConnectionType on ledger.ConnectionType {
|
||||
extension TrezorToGenericHardwareWalletDeviceType on trezor.TrezorDeviceType {
|
||||
HardwareWalletDeviceType toGeneric() {
|
||||
switch (this) {
|
||||
case trezor.TrezorDeviceType.modelOne:
|
||||
return HardwareWalletDeviceType.trezorModelOne;
|
||||
case trezor.TrezorDeviceType.modelT:
|
||||
return HardwareWalletDeviceType.trezorModelT;
|
||||
case trezor.TrezorDeviceType.safe3:
|
||||
return HardwareWalletDeviceType.trezorSafe3;
|
||||
case trezor.TrezorDeviceType.safe5:
|
||||
return HardwareWalletDeviceType.trezorSafe5;
|
||||
case trezor.TrezorDeviceType.safe7:
|
||||
return HardwareWalletDeviceType.trezorSafe7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension LedgerToGenericHardwareWalletConnectionType on ledger.ConnectionType {
|
||||
HardwareWalletConnectionType toGeneric() {
|
||||
switch (this) {
|
||||
case ledger.ConnectionType.usb:
|
||||
@@ -91,3 +129,14 @@ extension ToGenericHardwareWalletConnectionType on ledger.ConnectionType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension TrezorToGenericHardwareWalletConnectionType on trezor.ConnectionType {
|
||||
HardwareWalletConnectionType toGeneric() {
|
||||
switch (this) {
|
||||
case trezor.ConnectionType.usb:
|
||||
return HardwareWalletConnectionType.usb;
|
||||
case trezor.ConnectionType.ble:
|
||||
return HardwareWalletConnectionType.ble;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,13 +236,14 @@ class CWMonero extends Monero {
|
||||
required String name,
|
||||
required String password,
|
||||
required int height,
|
||||
required ledger.LedgerConnection ledgerConnection,
|
||||
required HardwareWalletService hardwareWalletService,
|
||||
}) =>
|
||||
MoneroRestoreWalletFromHardwareCredentials(
|
||||
name: name,
|
||||
password: password,
|
||||
height: height,
|
||||
ledgerConnection: ledgerConnection);
|
||||
name: name,
|
||||
password: password,
|
||||
height: height,
|
||||
hardwareWalletService: hardwareWalletService,
|
||||
);
|
||||
|
||||
@override
|
||||
WalletCredentials createMoneroRestoreWalletFromSeedCredentials(
|
||||
@@ -416,9 +417,7 @@ class CWMonero extends Monero {
|
||||
}
|
||||
|
||||
@override
|
||||
void monerocCheck() {
|
||||
checkIfMoneroCIsFine();
|
||||
}
|
||||
void monerocCheck() => checkIfMoneroCIsFine();
|
||||
|
||||
@override
|
||||
Future<void> setLedgerConnection(Object wallet, ledger.LedgerConnection connection) async {
|
||||
@@ -427,29 +426,41 @@ class CWMonero extends Monero {
|
||||
}
|
||||
|
||||
@override
|
||||
void resetLedgerConnection() {
|
||||
disableLedgerExchange();
|
||||
}
|
||||
void resetLedgerConnection() => disableLedgerExchange();
|
||||
|
||||
@override
|
||||
void setGlobalLedgerConnection(ledger.LedgerConnection connection) {
|
||||
gLedger = connection;
|
||||
}
|
||||
void setGlobalLedgerConnection(ledger.LedgerConnection connection) => gLedger = connection;
|
||||
|
||||
@override
|
||||
String? getLastLedgerCommand() => latestLedgerCommand;
|
||||
|
||||
bool isViewOnly() {
|
||||
return isViewOnlyBySpendKey(null);
|
||||
@override
|
||||
void setHardwareWalletService(Object wallet, HardwareWalletService service) {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
moneroWallet.trezorService = service as MoneroTrezorService;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, List<int>> debugCallLength() {
|
||||
return monero_wallet_api.debugCallLength();
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> getWalletCacheDebug() {
|
||||
return monero_wallet_api.getWalletCacheDebug();
|
||||
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection) =>
|
||||
MoneroLedgerService(connection);
|
||||
|
||||
@override
|
||||
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorClient client) =>
|
||||
MoneroTrezorService(client);
|
||||
|
||||
@override
|
||||
Future<void> syncTrezor(Object wallet) async {
|
||||
final moneroWallet = wallet as MoneroWallet;
|
||||
|
||||
return moneroWallet.syncTrezor();
|
||||
}
|
||||
|
||||
bool isViewOnly() => isViewOnlyBySpendKey(null);
|
||||
|
||||
@override
|
||||
Map<String, List<int>> debugCallLength() => monero_wallet_api.debugCallLength();
|
||||
|
||||
@override
|
||||
Map<String, dynamic> getWalletCacheDebug() => monero_wallet_api.getWalletCacheDebug();
|
||||
}
|
||||
|
||||
@@ -726,12 +726,13 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
}
|
||||
|
||||
if (widget.sendViewModel.wallet.isHardwareWallet) {
|
||||
if (!widget.sendViewModel.hardwareWalletViewModel!.isConnected) {
|
||||
if (!widget.sendViewModel.hardwareWalletViewModel!
|
||||
.isConnected(widget.sendViewModel.walletType)) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: widget.sendViewModel.walletType,
|
||||
hardwareWalletType: widget.sendViewModel.wallet.walletInfo.hardwareWalletType!,
|
||||
onConnectDevice: (BuildContext context, _) {
|
||||
onConnectDevice: (_, __) {
|
||||
widget.sendViewModel.hardwareWalletViewModel!
|
||||
.initWallet(widget.sendViewModel.wallet);
|
||||
Navigator.of(context).pop();
|
||||
@@ -748,8 +749,12 @@ class _NewSendPageState extends State<NewSendPage> {
|
||||
amount += item.formattedCryptoAmount;
|
||||
}
|
||||
if (monero!.needExportOutputs(widget.sendViewModel.wallet, amount)) {
|
||||
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
|
||||
arguments: monero!.exportOutputsUR(widget.sendViewModel.wallet));
|
||||
if (widget.sendViewModel.wallet.hardwareWalletType == HardwareWalletType.trezor) {
|
||||
await monero!.syncTrezor(widget.sendViewModel.wallet);
|
||||
} else {
|
||||
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
|
||||
arguments: monero!.exportOutputsUR(widget.sendViewModel.wallet));
|
||||
}
|
||||
await Future.delayed(Duration(seconds: 1)); // wait for monero to refresh the state
|
||||
}
|
||||
if (monero!.needExportOutputs(widget.sendViewModel.wallet, amount)) {
|
||||
|
||||
@@ -86,11 +86,11 @@ class WalletInfoBar extends StatelessWidget {
|
||||
case null:
|
||||
return null;
|
||||
case HardwareWalletType.bitbox:
|
||||
return "assets/images/hardware_wallet/device_bitbox.svg";
|
||||
return "assets/new-ui/hardware_wallets/device_bitbox.svg";
|
||||
case HardwareWalletType.ledger:
|
||||
return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
|
||||
case HardwareWalletType.trezor:
|
||||
return "assets/images/hardware_wallet/device_trezor_safe_5.svg";
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_safe_5.svg";
|
||||
case HardwareWalletType.cupcake:
|
||||
return "assets/images/cupcake.svg";
|
||||
case HardwareWalletType.coldcard:
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class DigitInputController {
|
||||
String _text = "";
|
||||
|
||||
String get text => _text;
|
||||
|
||||
set text(String newText) {
|
||||
_text = newText;
|
||||
for (final listener in _listeners) {
|
||||
listener.call();
|
||||
}
|
||||
}
|
||||
|
||||
final List<void Function()> _listeners = [];
|
||||
|
||||
void addListener(void Function() listener) => _listeners.add(listener);
|
||||
}
|
||||
|
||||
class DigitInputPill extends StatelessWidget {
|
||||
const DigitInputPill({super.key, this.digit, required this.highlighted});
|
||||
|
||||
final String? digit;
|
||||
final bool highlighted;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 51,
|
||||
height: 84,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border:
|
||||
highlighted ? Border.all(color: Theme.of(context).colorScheme.primary, width: 2) : null,
|
||||
),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text(digit ?? " ", style: TextStyle(fontSize: 40)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DigitInput extends StatefulWidget {
|
||||
const DigitInput({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.desiredLength,
|
||||
this.breakAt = 3,
|
||||
});
|
||||
|
||||
final DigitInputController controller;
|
||||
final int desiredLength;
|
||||
final int breakAt;
|
||||
|
||||
@override
|
||||
State<DigitInput> createState() => _DigitInputState();
|
||||
}
|
||||
|
||||
class _DigitInputState extends State<DigitInput> implements TextInputClient {
|
||||
TextInputConnection? _textInputConnection;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_openVirtualKeyboard();
|
||||
}
|
||||
|
||||
void _openVirtualKeyboard() {
|
||||
if (_textInputConnection == null || !_textInputConnection!.attached) {
|
||||
_textInputConnection = TextInput.attach(
|
||||
this,
|
||||
const TextInputConfiguration(
|
||||
inputType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: false,
|
||||
),
|
||||
inputAction: TextInputAction.none,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_textInputConnection!.setEditingState(
|
||||
TextEditingValue(
|
||||
text: widget.controller.text,
|
||||
selection: TextSelection.collapsed(offset: widget.controller.text.length),
|
||||
),
|
||||
);
|
||||
_textInputConnection!.show();
|
||||
}
|
||||
|
||||
void _closeVirtualKeyboard() => _textInputConnection?.close();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_closeVirtualKeyboard();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: _openVirtualKeyboard,
|
||||
child: SizedBox(
|
||||
height: 84,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => DigitInputPill(
|
||||
digit: widget.controller.text.length > index ? widget.controller.text[index] : null,
|
||||
highlighted: widget.controller.text.length == index,
|
||||
),
|
||||
separatorBuilder: (context, index) =>
|
||||
SizedBox(width: (index + 1) % widget.breakAt == 0 ? 16 : 4),
|
||||
itemCount: widget.desiredLength,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateEditingValue(TextEditingValue value) {
|
||||
final RegExp numberRegExp = RegExp(r'^\d{0,6}$');
|
||||
|
||||
if (numberRegExp.hasMatch(value.text)) {
|
||||
setState(() => widget.controller.text = value.text);
|
||||
} else {
|
||||
// flutter keeps the text editing state regardless of whether you actually used it or not.
|
||||
// so, if validation fails, we gotta set it back to what it was explicitly
|
||||
_textInputConnection?.setEditingState(
|
||||
TextEditingValue(
|
||||
text: widget.controller.text,
|
||||
selection: TextSelection.collapsed(offset: widget.controller.text.length),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void performAction(TextInputAction action) {}
|
||||
|
||||
@override
|
||||
TextEditingValue? get currentTextEditingValue => TextEditingValue(text: widget.controller.text);
|
||||
|
||||
@override
|
||||
void connectionClosed() => _textInputConnection = null;
|
||||
|
||||
@override
|
||||
AutofillScope? get currentAutofillScope => null;
|
||||
|
||||
@override
|
||||
void performPrivateCommand(String action, Map<String, dynamic> data) {}
|
||||
|
||||
@override
|
||||
void showAutocorrectionPromptRect(int start, int end) {}
|
||||
|
||||
@override
|
||||
void updateFloatingCursor(RawFloatingCursorPoint point) {}
|
||||
|
||||
@override
|
||||
void insertTextPlaceholder(Size size) {}
|
||||
|
||||
@override
|
||||
void removeTextPlaceholder() {}
|
||||
|
||||
@override
|
||||
void showToolbar() {}
|
||||
|
||||
@override
|
||||
void performSelector(String selectorName) {}
|
||||
|
||||
@override
|
||||
void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) {}
|
||||
|
||||
@override
|
||||
void insertContent(KeyboardInsertedContent content) {}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HardwareWalletProceedOnDeviceMessage extends StatelessWidget {
|
||||
const HardwareWalletProceedOnDeviceMessage({super.key, required this.hardwareWalletType});
|
||||
|
||||
final HardwareWalletType hardwareWalletType;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(spacing: 8, children: [
|
||||
if (hardwareWalletIcon != null)
|
||||
CakeImageWidget(
|
||||
imageUrl: hardwareWalletIcon!,
|
||||
width: 36,
|
||||
height: 36,
|
||||
colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
Text(
|
||||
S.of(context).proceed_on_device,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
)
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String? get hardwareWalletIcon {
|
||||
switch (hardwareWalletType) {
|
||||
case HardwareWalletType.bitbox:
|
||||
return "assets/new-ui/hardware_wallets/device_bitbox.svg";
|
||||
case HardwareWalletType.ledger:
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
|
||||
case HardwareWalletType.trezor:
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_safe_7.svg";
|
||||
case HardwareWalletType.cupcake:
|
||||
return "assets/images/cupcake.svg";
|
||||
case HardwareWalletType.coldcard:
|
||||
case HardwareWalletType.seedsigner:
|
||||
case HardwareWalletType.keystone:
|
||||
return "assets/images/hardware_wallet/device_qr.svg";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/digit_input.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/send_page/directional_switcher.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/trezor_connect_view_model.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class HardwareWalletProceedOnDeviceSheet extends StatefulWidget {
|
||||
const HardwareWalletProceedOnDeviceSheet({
|
||||
super.key,
|
||||
required this.hardwareWalletType,
|
||||
required this.trezorConnectVM,
|
||||
required this.onRetry,
|
||||
});
|
||||
|
||||
final HardwareWalletType hardwareWalletType;
|
||||
final TrezorConnectViewModelBase trezorConnectVM;
|
||||
final void Function() onRetry;
|
||||
|
||||
@override
|
||||
State<HardwareWalletProceedOnDeviceSheet> createState() =>
|
||||
_HardwareWalletProceedOnDeviceSheetState();
|
||||
}
|
||||
|
||||
class _HardwareWalletProceedOnDeviceSheetState extends State<HardwareWalletProceedOnDeviceSheet> {
|
||||
TrezorParingState _paringState = TrezorParingState.initial;
|
||||
|
||||
final DigitInputController _controller = DigitInputController();
|
||||
|
||||
static const pinOpenDuration = Duration(milliseconds: 300);
|
||||
|
||||
// you should probably check if there can be other pin lengths
|
||||
static const pinLength = 6;
|
||||
|
||||
ReactionDisposer? paringStateReaction;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller.addListener(() => setState(() {}));
|
||||
|
||||
_paringState = widget.trezorConnectVM.paringState;
|
||||
paringStateReaction = reaction((_) => widget.trezorConnectVM.paringState, (paringState) {
|
||||
if (paringState is SuccessTrezorParingState) {
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
return;
|
||||
}
|
||||
setState(() => _paringState = paringState);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
paringStateReaction?.reaction.dispose();
|
||||
}
|
||||
|
||||
bool get _isAwaitingPin => _paringState == TrezorParingState.enterPin;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: "",
|
||||
leadingWidget: AnimatedSwitcher(
|
||||
layoutBuilder: (currentChild, previousChildren) {
|
||||
return Stack(
|
||||
alignment: Alignment.centerLeft,
|
||||
children: <Widget>[
|
||||
...previousChildren,
|
||||
if (currentChild != null) currentChild,
|
||||
],
|
||||
);
|
||||
},
|
||||
duration: pinOpenDuration,
|
||||
child: Text(
|
||||
key: ValueKey(pageTitle),
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
pageTitle,
|
||||
),
|
||||
),
|
||||
trailingIcon: Icon(
|
||||
Icons.close,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
onTrailingPressed: () => showPopUp(
|
||||
context: context,
|
||||
builder: (context) => AlertWithTwoActions(
|
||||
alertTitle: S.of(context).are_you_sure_exit,
|
||||
alertContent: S.of(context).hww_exit_desc,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).yes_exit,
|
||||
rightAlertButtonStyle: AlertButtonStyle.error(context),
|
||||
actionLeftButton: Navigator.of(context).pop,
|
||||
actionRightButton: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: DirectionalAnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 400),
|
||||
child: content,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 24, right: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(),
|
||||
AnimatedOpacity(
|
||||
curve: Curves.easeOutQuad,
|
||||
opacity: hasFullPin ? 1 : 0,
|
||||
duration: pinOpenDuration,
|
||||
child: ModernButton(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
iconColor: Theme.of(context).colorScheme.onPrimary,
|
||||
size: 36,
|
||||
icon: Icon(Icons.arrow_forward),
|
||||
onPressed: () =>
|
||||
widget.trezorConnectVM.setParingPin(_controller.text.trim()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget get content {
|
||||
if (_paringState is InitialTrezorParingState || _paringState is EnterPinTrezorParingState) {
|
||||
return _enterPinCode();
|
||||
}
|
||||
|
||||
if (_paringState is VerifyingPinTrezorParingState) return _verifyingCode();
|
||||
|
||||
if (_paringState is FailTrezorParingState)
|
||||
return _errorBox((_paringState as FailTrezorParingState).message);
|
||||
|
||||
return SizedBox.shrink(key: ValueKey(3));
|
||||
}
|
||||
|
||||
void retry() {
|
||||
_controller.text = "";
|
||||
widget.onRetry();
|
||||
}
|
||||
|
||||
String get pageTitle {
|
||||
if (_paringState is InitialTrezorParingState) return S.of(context).device_confirmation;
|
||||
if (_isAwaitingPin) return S.of(context).pairing_code;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool get hasFullPin => _controller.text.length == pinLength && _isAwaitingPin;
|
||||
|
||||
String? get hardwareWalletIcon {
|
||||
switch (widget.hardwareWalletType) {
|
||||
case HardwareWalletType.bitbox:
|
||||
return "assets/new-ui/hardware_wallets/device_bitbox.svg";
|
||||
case HardwareWalletType.ledger:
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
|
||||
case HardwareWalletType.trezor:
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_safe_7.svg";
|
||||
case HardwareWalletType.cupcake:
|
||||
return "assets/images/cupcake.svg";
|
||||
case HardwareWalletType.coldcard:
|
||||
case HardwareWalletType.seedsigner:
|
||||
case HardwareWalletType.keystone:
|
||||
return "assets/images/hardware_wallet/device_qr.svg";
|
||||
}
|
||||
}
|
||||
|
||||
Widget _verifyingCode() => Container(
|
||||
key: ValueKey(1),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const CupertinoActivityIndicator(radius: 36),
|
||||
const SizedBox(),
|
||||
Text(
|
||||
"${S.of(context).verifying_code}...",
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
||||
),
|
||||
Text(
|
||||
S.of(context).this_can_take_few_seconds,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _errorBox(String errorText) => Container(
|
||||
key: ValueKey(2),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 12,
|
||||
children: [
|
||||
const Spacer(),
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
size: 80,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
Text(
|
||||
S.of(context).pairing_error,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
errorText,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Spacer(),
|
||||
NewPrimaryButton(
|
||||
onPressed: retry,
|
||||
text: S.of(context).try_again,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _enterPinCode() => Column(
|
||||
key: ValueKey(0),
|
||||
spacing: 12,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AnimatedContainer(
|
||||
duration: pinOpenDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: _isAwaitingPin ? 40 : 100,
|
||||
height: _isAwaitingPin ? 40 : 100,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: hardwareWalletIcon,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedSwitcher(
|
||||
duration: pinOpenDuration,
|
||||
switchInCurve: Curves.easeInCubic,
|
||||
switchOutCurve: Curves.easeOutCubic,
|
||||
child: _isAwaitingPin
|
||||
? Column(
|
||||
spacing: 24,
|
||||
key: ValueKey(1),
|
||||
children: [
|
||||
Text(
|
||||
textAlign: TextAlign.center,
|
||||
"${S.of(context).pairing_code_desc_1}\n${S.of(context).pairing_code_desc_2}",
|
||||
),
|
||||
DigitInput(
|
||||
controller: _controller,
|
||||
desiredLength: pinLength,
|
||||
)
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
key: ValueKey(0),
|
||||
spacing: 12,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).proceed_on_device,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Text(
|
||||
S.of(context).proceed_on_device_description,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class HardwareWalletTrezorParingSheet extends StatefulWidget {
|
||||
const HardwareWalletTrezorParingSheet({super.key});
|
||||
|
||||
@override
|
||||
State<HardwareWalletTrezorParingSheet> createState() => _HardwareWalletTrezorParingSheetState();
|
||||
}
|
||||
|
||||
class _HardwareWalletTrezorParingSheetState extends State<HardwareWalletTrezorParingSheet> {
|
||||
final TextEditingController _paringCodeController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: SafeArea(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(18)),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: "Paring code",
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
onTrailingPressed: () {},
|
||||
leadingIcon: Icon(Icons.close),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(18.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 12,
|
||||
children: [
|
||||
SizedBox(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 75,
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
bottomLeft: Radius.circular(16),
|
||||
),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: TextField(
|
||||
textAlign: TextAlign.left,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
controller: _paringCodeController,
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(signed: false, decimal: false),
|
||||
decoration: InputDecoration(
|
||||
hint: Text(
|
||||
"000 000",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
border: InputBorder.none,
|
||||
filled: true,
|
||||
fillColor: Colors.transparent,
|
||||
),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(),
|
||||
NewPrimaryButton(
|
||||
text: S.of(context).continue_text,
|
||||
onPressed: () => Navigator.of(context).pop(_paringCodeController.text),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/confirm_swiper.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/hardware_wallet/proceed_on_device_message.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:vector_graphics/vector_graphics.dart';
|
||||
|
||||
class SendConfirmBottomWidget extends StatelessWidget {
|
||||
const SendConfirmBottomWidget({super.key, required this.sendViewModel});
|
||||
@@ -61,7 +59,7 @@ class SendConfirmBottomWidget extends StatelessWidget {
|
||||
text: "${S.of(context).signing_transaction}...",
|
||||
);
|
||||
case IsAwaitingDeviceResponseState:
|
||||
return HardwareWalletConfirmationMessage(
|
||||
return HardwareWalletProceedOnDeviceMessage(
|
||||
hardwareWalletType: sendViewModel.wallet.hardwareWalletType!);
|
||||
case TransactionCommitting:
|
||||
return LoadingBottomWidget(
|
||||
@@ -173,58 +171,3 @@ class TransactionErrorActions extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HardwareWalletConfirmationMessage extends StatelessWidget {
|
||||
const HardwareWalletConfirmationMessage({super.key, required this.hardwareWalletType});
|
||||
|
||||
final HardwareWalletType hardwareWalletType;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(spacing: 8, children: [
|
||||
if (hardwareWalletIcon != null)
|
||||
CakeImageWidget(imageUrl:
|
||||
hardwareWalletIcon!,
|
||||
width: 36,
|
||||
height: 36,
|
||||
colorFilter: ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
Text(
|
||||
S.of(context).proceed_on_device,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Theme.of(context).colorScheme.onSurface),
|
||||
)
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String? get hardwareWalletIcon {
|
||||
switch (hardwareWalletType) {
|
||||
case HardwareWalletType.bitbox:
|
||||
return "assets/images/hardware_wallet/device_bitbox.svg";
|
||||
case HardwareWalletType.ledger:
|
||||
return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
|
||||
case HardwareWalletType.trezor:
|
||||
return "assets/images/hardware_wallet/device_trezor_safe_5.svg";
|
||||
case HardwareWalletType.cupcake:
|
||||
return "assets/images/cupcake.svg";
|
||||
case HardwareWalletType.coldcard:
|
||||
case HardwareWalletType.seedsigner:
|
||||
case HardwareWalletType.keystone:
|
||||
return "assets/images/hardware_wallet/device_qr.svg";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class _SwapConfirmSheetState extends State<SwapConfirmSheet> {
|
||||
final sendVM = widget.exchangeTradeViewModel.sendViewModel;
|
||||
|
||||
if (sendVM.wallet.isHardwareWallet) {
|
||||
if (!sendVM.hardwareWalletViewModel!.isConnected) {
|
||||
if (!sendVM.hardwareWalletViewModel!.isConnected(sendVM.walletType)) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: sendVM.walletType,
|
||||
|
||||
@@ -371,7 +371,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
(_) => getIt.get<NewWalletTypePage>(
|
||||
param1: NewWalletTypeArguments(
|
||||
onTypeSelected: (BuildContext context, WalletType type) {
|
||||
if (hardwareWalletType == HardwareWalletType.trezor) {
|
||||
if (hardwareWalletType == HardwareWalletType.trezor && type != WalletType.monero) {
|
||||
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
|
||||
arguments: [type, hardwareWalletType]);
|
||||
return;
|
||||
|
||||
@@ -3,10 +3,13 @@ import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/connect_device/widgets/device_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_steps_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
@@ -91,6 +94,8 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
var bleDevices = <HardwareWalletDevice>[];
|
||||
var usbDevices = <HardwareWalletDevice>[];
|
||||
|
||||
List<HardwareWalletDevice> get allDevices => [...bleDevices, ...usbDevices];
|
||||
|
||||
late Timer? _usbRefreshTimer = null;
|
||||
late Timer? _bleRefreshTimer = null;
|
||||
late Timer? _bleStateTimer = null;
|
||||
@@ -158,24 +163,38 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
|
||||
Future<void> _connectToDevice(HardwareWalletDevice device) async {
|
||||
final isConnected = await widget.hardwareWalletVM.connectDevice(device, widget.walletType);
|
||||
if (isConnected) widget.onConnectDevice(context, widget.hardwareWalletVM);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (isConnected) widget.onConnectDevice(navigatorKey.currentContext!, widget.hardwareWalletVM);
|
||||
});
|
||||
}
|
||||
|
||||
String _getDeviceTileLeading(HardwareWalletDeviceType deviceType) {
|
||||
switch (deviceType) {
|
||||
case HardwareWalletDeviceType.ledgerNanoX:
|
||||
return 'assets/images/hardware_wallet/ledger_nano_x.png';
|
||||
return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
|
||||
case HardwareWalletDeviceType.ledgerNanoGen5:
|
||||
return 'assets/images/hardware_wallet/device_ledger_nano_gen_5.svg';
|
||||
return 'assets/new-ui/hardware_wallets/device_ledger_nano_gen_5.svg';
|
||||
case HardwareWalletDeviceType.ledgerStax:
|
||||
return 'assets/images/hardware_wallet/ledger_stax.png';
|
||||
return 'assets/new-ui/hardware_wallets/ledger_stax.svg';
|
||||
case HardwareWalletDeviceType.ledgerFlex:
|
||||
return 'assets/images/hardware_wallet/ledger_flex.png';
|
||||
return 'assets/new-ui/hardware_wallets/ledger_flex.svg';
|
||||
case HardwareWalletDeviceType.BitBox02:
|
||||
return 'assets/images/hardware_wallet/device_bitbox.svg';
|
||||
case HardwareWalletDeviceType.BitBox02Nova:
|
||||
return 'assets/new-ui/hardware_wallets/device_bitbox.svg';
|
||||
case HardwareWalletDeviceType.trezorModelOne:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_model_one.svg';
|
||||
case HardwareWalletDeviceType.trezorModelT:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_model_t.svg';
|
||||
case HardwareWalletDeviceType.trezorSafe3:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_safe_3.svg';
|
||||
case HardwareWalletDeviceType.trezorSafe5:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_safe_5.svg';
|
||||
case HardwareWalletDeviceType.trezorSafe7:
|
||||
return 'assets/new-ui/hardware_wallets/device_trezor_safe_7.svg';
|
||||
|
||||
default:
|
||||
return 'assets/images/hardware_wallet/ledger_nano_x.png';
|
||||
return 'assets/new-ui/hardware_wallets/ledger_nano_x.svg';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,25 +216,15 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
spacing: 24,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Text(
|
||||
description,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/hardware_wallet.svg",
|
||||
),
|
||||
Offstage(
|
||||
offstage: !longWait,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Text(
|
||||
S.of(context).if_you_dont_see_your_device,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Observer(
|
||||
builder: (_) => Offstage(
|
||||
@@ -231,56 +240,69 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (bleDevices.length > 0) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
S.of(context).bluetooth,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
...bleDevices
|
||||
.map(
|
||||
(device) => Padding(
|
||||
padding: EdgeInsets.only(bottom: 20),
|
||||
child: DeviceTile(
|
||||
onPressed: () => _connectToDevice(device),
|
||||
title: device.name,
|
||||
leading: _getDeviceTileLeading(device.type),
|
||||
connectionType: device.connectionType,
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18)),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final item = allDevices[index];
|
||||
return GestureDetector(
|
||||
onTap: () => _connectToDevice(item),
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: _getDeviceTileLeading(item.type),
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn),
|
||||
),
|
||||
Text(item.name)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
if (item.connectionType ==
|
||||
HardwareWalletConnectionType.ble)
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/bluetooth.svg",
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
BlendMode.srcIn),
|
||||
),
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/arrow_forward.svg",
|
||||
height: 16,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
],
|
||||
if (usbDevices.length > 0) ...[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
S.of(context).usb,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
...usbDevices
|
||||
.map(
|
||||
(device) => Padding(
|
||||
padding: EdgeInsets.only(bottom: 20),
|
||||
child: DeviceTile(
|
||||
onPressed: () => _connectToDevice(device),
|
||||
title: device.name,
|
||||
leading: _getDeviceTileLeading(device.type),
|
||||
connectionType: device.connectionType,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
],
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh),
|
||||
itemCount: allDevices.length),
|
||||
),
|
||||
if (widget.allowChangeWallet) ...[
|
||||
PrimaryButton(
|
||||
text: S.of(context).wallets,
|
||||
@@ -293,10 +315,10 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
),
|
||||
),
|
||||
),
|
||||
PrimaryButton(
|
||||
NewPrimaryButton(
|
||||
text: S.of(context).how_to_connect,
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
textColor: Theme.of(context).colorScheme.primary,
|
||||
onPressed: () => _onHowToConnect(context),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
@@ -43,14 +44,12 @@ class DeviceTile extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
if (leading != null && !leading!.endsWith(".svg"))
|
||||
Image.asset(
|
||||
leading!,
|
||||
height: 30,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
if (leading != null && leading!.endsWith(".svg"))
|
||||
SvgPicture.asset(leading!, height: 30),
|
||||
CakeImageWidget(
|
||||
height: 30,
|
||||
imageUrl: leading,
|
||||
colorFilter:
|
||||
ColorFilter.mode(Theme.of(context).colorScheme.onSurface, BlendMode.srcIn),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 16),
|
||||
|
||||
@@ -34,11 +34,11 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
case null:
|
||||
return null;
|
||||
case HardwareWalletType.bitbox:
|
||||
return "assets/images/hardware_wallet/device_bitbox.svg";
|
||||
return "assets/new-ui/hardware_wallets/device_bitbox.svg";
|
||||
case HardwareWalletType.ledger:
|
||||
return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
|
||||
return "assets/new-ui/hardware_wallets/device_ledger_nano_x.svg";
|
||||
case HardwareWalletType.trezor:
|
||||
return "assets/images/hardware_wallet/device_trezor_safe_5.svg";
|
||||
return "assets/new-ui/hardware_wallets/device_trezor_safe_5.svg";
|
||||
case HardwareWalletType.cupcake:
|
||||
return "assets/images/cupcake.svg";
|
||||
case HardwareWalletType.coldcard:
|
||||
|
||||
@@ -213,7 +213,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||
final sendVM = widget.exchangeTradeViewModel.sendViewModel;
|
||||
|
||||
if (sendVM.wallet.isHardwareWallet) {
|
||||
if (!sendVM.hardwareWalletViewModel!.isConnected) {
|
||||
if (!sendVM.hardwareWalletViewModel!.isConnected(sendVM.walletType)) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: sendVM.walletType,
|
||||
|
||||
@@ -160,7 +160,7 @@ class DEuroSavingsPage extends BasePage {
|
||||
|
||||
Future<void> _requireHardwareWallet(BuildContext context) async {
|
||||
if (_dEuroViewModel.wallet.isHardwareWallet) {
|
||||
if (!_dEuroViewModel.hardwareWalletViewModel!.isConnected) {
|
||||
if (!_dEuroViewModel.hardwareWalletViewModel!.isConnected(_dEuroViewModel.wallet.type)) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: _dEuroViewModel.wallet.type,
|
||||
|
||||
@@ -6,14 +6,8 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/reactions/wallet_utils.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||
import 'package:cake_wallet/src/screens/setup_2fa/widgets/popup_cancellable_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/search_bar_widget.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/wallet_types.g.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
@@ -21,10 +15,38 @@ import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NewWalletTypePageHeader extends StatelessWidget {
|
||||
const NewWalletTypePageHeader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => const SizedBox(
|
||||
width: 138,
|
||||
height: 75,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: 0,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/hardware_wallet.svg",
|
||||
width: 75,
|
||||
height: 75,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 63,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/cake_coin.svg",
|
||||
width: 75,
|
||||
height: 75,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class NewWalletTypePage extends BasePage {
|
||||
NewWalletTypePage({
|
||||
required this.newWalletTypeArguments,
|
||||
});
|
||||
NewWalletTypePage({required this.newWalletTypeArguments});
|
||||
|
||||
final NewWalletTypeArguments newWalletTypeArguments;
|
||||
|
||||
@@ -38,23 +60,21 @@ class NewWalletTypePage extends BasePage {
|
||||
|
||||
@override
|
||||
Function(BuildContext)? get pushToNextWidget => (context) {
|
||||
FocusScopeNode currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus) {
|
||||
currentFocus.focusedChild?.unfocus();
|
||||
}
|
||||
};
|
||||
final currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus) currentFocus.focusedChild?.unfocus();
|
||||
};
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletTypeForm(
|
||||
walletImage: currentTheme.isDark ? walletTypeImage : walletTypeLightImage,
|
||||
isCreate: newWalletTypeArguments.isCreate,
|
||||
onTypeSelected: newWalletTypeArguments.onTypeSelected,
|
||||
hardwareWalletType: newWalletTypeArguments.hardwareWalletType,
|
||||
);
|
||||
walletImage: currentTheme.isDark ? walletTypeImage : walletTypeLightImage,
|
||||
isCreate: newWalletTypeArguments.isCreate,
|
||||
onTypeSelected: newWalletTypeArguments.onTypeSelected,
|
||||
hardwareWalletType: newWalletTypeArguments.hardwareWalletType,
|
||||
);
|
||||
}
|
||||
|
||||
class WalletTypeForm extends StatefulWidget {
|
||||
WalletTypeForm({
|
||||
const WalletTypeForm({
|
||||
required this.walletImage,
|
||||
required this.isCreate,
|
||||
this.onTypeSelected,
|
||||
@@ -77,9 +97,6 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
|
||||
static const aspectRatioImage = 1.22;
|
||||
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
WalletType? selected;
|
||||
List<WalletType> types;
|
||||
List<WalletType> filteredTypes = [];
|
||||
|
||||
@@ -87,21 +104,12 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
void initState() {
|
||||
types = filteredTypes = availableWalletTypes
|
||||
.where((element) =>
|
||||
!widget.isHardwareWallet ||
|
||||
DeviceConnectionType.supportedConnectionTypes(
|
||||
element, widget.hardwareWalletType!, Platform.isIOS)
|
||||
.isNotEmpty)
|
||||
!widget.isHardwareWallet ||
|
||||
DeviceConnectionType.supportedConnectionTypes(
|
||||
element, widget.hardwareWalletType!, Platform.isIOS)
|
||||
.isNotEmpty)
|
||||
.toList();
|
||||
super.initState();
|
||||
|
||||
searchController.addListener(() {
|
||||
setState(() {
|
||||
filteredTypes = List.from(types.where((type) => walletTypeToDisplayName(type)
|
||||
.toLowerCase()
|
||||
.contains(searchController.text.toLowerCase())));
|
||||
return;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -110,96 +118,102 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
child: Column(
|
||||
spacing: 24,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 48),
|
||||
child: Text(
|
||||
S.of(context).choose_wallet_currency,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const NewWalletTypePageHeader(),
|
||||
Text(
|
||||
S.of(context).choose_wallet_currency,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 12),
|
||||
child: SearchBarWidget(searchController: searchController),
|
||||
),
|
||||
Expanded(
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
scrollableKey: ValueKey('new_wallet_type_scrollable_key'),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
...filteredTypes.map(
|
||||
(type) => Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: SelectButton(
|
||||
key: ValueKey('new_wallet_type_${type.name}_button_key'),
|
||||
image: CakeImageWidget(
|
||||
imageUrl: getCryptoCurrencyIconForWalletListItem(type),
|
||||
height: 24,
|
||||
width: 24,
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
child: ListView.separated(
|
||||
key: const ValueKey('new_wallet_type_scrollable_key'),
|
||||
itemCount: filteredTypes.length,
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
final item = filteredTypes[index];
|
||||
final curr = getCryptoCurrencyForWalletListItem(item);
|
||||
|
||||
return GestureDetector(
|
||||
key: ValueKey('new_wallet_type_${item.name}_button_key'),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
onTypeSelected(item);
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
height: 24,
|
||||
width: 24,
|
||||
imageUrl: curr.iconPath,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(curr.fullName ?? curr.name),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
curr.title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/arrow_forward.svg",
|
||||
height: 16,
|
||||
colorFilter: ColorFilter.mode(
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
text: walletTypeToDisplayName(type),
|
||||
showTrailingIcon: false,
|
||||
height: 54,
|
||||
isSelected: selected == type,
|
||||
onTap: () => setState(() => selected = type),
|
||||
deviceConnectionTypes: widget.isHardwareWallet
|
||||
? DeviceConnectionType.supportedConnectionTypes(
|
||||
type, widget.hardwareWalletType!, Platform.isIOS)
|
||||
: [],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: PrimaryButton(
|
||||
key: ValueKey('new_wallet_type_next_button_key'),
|
||||
onPressed: () => onTypeSelected(),
|
||||
text: S.of(context).seed_language_next,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: selected == null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onTypeSelected() async {
|
||||
if (selected == null) throw Exception('Wallet Type is not selected yet.');
|
||||
|
||||
if (selected == WalletType.haven && widget.isCreate) {
|
||||
return await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => PopUpCancellableAlertDialog(
|
||||
contentText: S.of(context).pause_wallet_creation,
|
||||
actionButtonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onTypeSelected(WalletType selected) async {
|
||||
// If it's a restore flow, trigger the external callback
|
||||
// If it's not a BIP39 Wallet or if there are no other wallets, route to the newWallet page
|
||||
// Any other scenario, route to pre-existing seed page
|
||||
final walletList = await WalletInfo.getAll();
|
||||
if (!widget.isCreate) {
|
||||
widget.onTypeSelected!(context, selected!);
|
||||
} else if (!isBIP39Wallet(selected!) || walletList.isEmpty) {
|
||||
widget.onTypeSelected!(context, selected);
|
||||
} else if (!isBIP39Wallet(selected) || walletList.isEmpty) {
|
||||
Navigator.of(context).pushNamed(
|
||||
Routes.newWallet,
|
||||
arguments: NewWalletArguments(type: selected!),
|
||||
arguments: NewWalletArguments(type: selected),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(Routes.walletGroupDescription, arguments: selected!);
|
||||
Navigator.of(context).pushNamed(Routes.walletGroupDescription, arguments: selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ class SendPage extends BasePage {
|
||||
}
|
||||
|
||||
if (sendViewModel.wallet.isHardwareWallet) {
|
||||
if (!sendViewModel.hardwareWalletViewModel!.isConnected) {
|
||||
if (!sendViewModel.hardwareWalletViewModel!.isConnected(sendViewModel.walletType)) {
|
||||
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||
arguments: ConnectDevicePageParams(
|
||||
walletType: sendViewModel.walletType,
|
||||
|
||||
@@ -78,7 +78,7 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
|
||||
bool _isConnecting = false;
|
||||
|
||||
@override
|
||||
bool get isConnected => false;
|
||||
bool isConnected(WalletType type) => false;
|
||||
|
||||
@override
|
||||
HardwareWalletService getHardwareWalletService(WalletType type) {
|
||||
|
||||
@@ -6,10 +6,11 @@ import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
abstract class HardwareWalletViewModel {
|
||||
HardwareWalletType get hardwareWalletType;
|
||||
bool get isConnected;
|
||||
bool get isBleEnabled;
|
||||
bool get hasBluetooth;
|
||||
|
||||
bool isConnected(WalletType type);
|
||||
|
||||
Future<void> updateBleState();
|
||||
|
||||
Stream<HardwareWalletDevice> scanForBleDevices();
|
||||
|
||||
@@ -114,7 +114,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
if (_isConnecting) return false;
|
||||
_isConnecting = true;
|
||||
_connectingWalletType = type;
|
||||
if (isConnected) {
|
||||
if (isConnected(type)) {
|
||||
try {
|
||||
await _connection!.disconnect().catchError((_) {});
|
||||
} catch (_) {}
|
||||
@@ -126,7 +126,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
|
||||
if (_connectionChangeSubscription == null) {
|
||||
_connectionChangeSubscription = ledger
|
||||
.deviceStateChanges
|
||||
.deviceStateChanges(device.device.id)
|
||||
.listen(_connectionChangeListener);
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isConnected => _connection != null && !(_connection!.isDisconnected);
|
||||
bool isConnected(WalletType type) => _connection != null && !(_connection!.isDisconnected);
|
||||
|
||||
sdk.LedgerConnection get connection => _connection!;
|
||||
|
||||
@@ -196,6 +196,8 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
@override
|
||||
HardwareWalletService getHardwareWalletService(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return monero!.getLedgerHardwareWalletService(connection);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.getLedgerHardwareWalletService(connection, true);
|
||||
case WalletType.litecoin:
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/main.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/hardware_wallet/proceed_on_device_sheet.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:trezor_connect/trezor_connect.dart' as connect_sdk;
|
||||
import 'package:trezor_flutter/trezor_flutter.dart' as sdk;
|
||||
|
||||
part 'trezor_connect_view_model.g.dart';
|
||||
|
||||
class TrezorConnectViewModel = TrezorConnectViewModelBase with _$TrezorConnectViewModel;
|
||||
|
||||
abstract class TrezorConnectViewModelBase extends HardwareWalletViewModel with Store {
|
||||
final connect_sdk.TrezorConnect trezorConnect;
|
||||
|
||||
late final sdk.TrezorInterface trezorBLE;
|
||||
late final sdk.TrezorInterface trezorUSB;
|
||||
|
||||
sdk.ThpState _state = sdk.ThpState();
|
||||
|
||||
TrezorConnectViewModelBase(this.trezorConnect) {
|
||||
if (_doesSupportHardwareWallets) {
|
||||
reaction((_) => isBleEnabled, (_) {
|
||||
if (isBleEnabled) _initBLE();
|
||||
});
|
||||
updateBleState();
|
||||
|
||||
if (!Platform.isIOS) {
|
||||
trezorUSB = sdk.TrezorInterface.usb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get _doesSupportHardwareWallets {
|
||||
if (isMoneroOnly) {
|
||||
return DeviceConnectionType.supportedConnectionTypes(
|
||||
WalletType.monero, HardwareWalletType.trezor, Platform.isIOS)
|
||||
.isNotEmpty;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _bleIsInitialized = false;
|
||||
|
||||
Future<void> _initBLE() async {
|
||||
if (isBleEnabled && !_bleIsInitialized) {
|
||||
trezorBLE = sdk.TrezorInterface.ble(
|
||||
onPermissionRequest: (_) async {
|
||||
if (Platform.isMacOS) return true;
|
||||
|
||||
Map<Permission, PermissionStatus> statuses = await [
|
||||
Permission.bluetoothScan,
|
||||
Permission.bluetoothConnect,
|
||||
Permission.bluetoothAdvertise,
|
||||
].request();
|
||||
|
||||
return statuses.values.where((status) => status.isDenied).isEmpty;
|
||||
},
|
||||
bleOptions: sdk.BluetoothOptions(maxScanDuration: Duration(minutes: 5)),
|
||||
);
|
||||
_bleIsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => true;
|
||||
|
||||
@override
|
||||
Future<void> updateBleState() async {
|
||||
final bleState = await sdk.UniversalBle.getBluetoothAvailabilityState();
|
||||
|
||||
final newState = bleState == sdk.AvailabilityState.poweredOn;
|
||||
|
||||
if (newState != isBleEnabled) isBleEnabled = newState;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<HardwareWalletDevice> scanForBleDevices() =>
|
||||
trezorBLE.scan().map((d) => TrezorHardwareWalletDevice(d));
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() => trezorUSB.devices
|
||||
.then((devices) => devices.map((d) => TrezorHardwareWalletDevice(d)).toList());
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {
|
||||
if (_bleIsInitialized) await trezorBLE.stopScanning();
|
||||
if (!Platform.isIOS) await trezorUSB.stopScanning();
|
||||
}
|
||||
|
||||
bool _isConnecting = false;
|
||||
sdk.TrezorClient? _client;
|
||||
|
||||
Completer<String?>? _pinCompleter;
|
||||
|
||||
@observable
|
||||
TrezorParingState paringState = TrezorParingState.initial;
|
||||
|
||||
void setParingPin(String pin) => _pinCompleter?.complete(pin);
|
||||
|
||||
@override
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type,
|
||||
[bool isRetry = false]) async {
|
||||
if (!(device is TrezorHardwareWalletDevice)) return false;
|
||||
if (_isConnecting) return false;
|
||||
_isConnecting = true;
|
||||
paringState = TrezorParingState.initial;
|
||||
|
||||
try {
|
||||
final trezorInterface =
|
||||
device.connectionType == HardwareWalletConnectionType.ble ? trezorBLE : trezorUSB;
|
||||
final connection = await trezorInterface.connect(device.device);
|
||||
|
||||
if (!isRetry) {
|
||||
showModalBottomSheet(
|
||||
context: navigatorKey.currentContext!,
|
||||
isScrollControlled: true,
|
||||
isDismissible: false,
|
||||
enableDrag: false,
|
||||
useSafeArea: true,
|
||||
builder: (context) =>
|
||||
HardwareWalletProceedOnDeviceSheet(
|
||||
hardwareWalletType: hardwareWalletType,
|
||||
trezorConnectVM: this,
|
||||
onRetry: () => connectDevice(device, type, true)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> onPinCode() async {
|
||||
_pinCompleter = Completer<String?>();
|
||||
paringState = TrezorParingState.enterPin;
|
||||
|
||||
final res = await _pinCompleter!.future;
|
||||
paringState = TrezorParingState.verifyingPin;
|
||||
if (res == null) throw Exception();
|
||||
return res;
|
||||
}
|
||||
|
||||
final deviceInfo = await _deviceName;
|
||||
|
||||
_client = sdk.TrezorClient.getClientForConnection(
|
||||
connection,
|
||||
_state,
|
||||
"Cake Wallet",
|
||||
deviceInfo,
|
||||
onPinCode,
|
||||
);
|
||||
|
||||
await _client!.createChannel();
|
||||
|
||||
paringState = TrezorParingState.success;
|
||||
return true;
|
||||
} catch (e) {
|
||||
await _client?.connection.disconnect();
|
||||
_client = null;
|
||||
_state = sdk.ThpState();
|
||||
// rethrow;
|
||||
paringState = TrezorParingState.fail(e.toString());
|
||||
printV(e);
|
||||
return false;
|
||||
} finally {
|
||||
_isConnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> get _deviceName async {
|
||||
final deviceInfo = DeviceInfoPlugin();
|
||||
if (Platform.isAndroid) return (await deviceInfo.androidInfo).model;
|
||||
if (Platform.isIOS) return (await deviceInfo.iosInfo).name;
|
||||
return "Computer";
|
||||
}
|
||||
|
||||
@override
|
||||
bool isConnected(WalletType type) => type == WalletType.monero
|
||||
? _client != null && _client?.connection.isDisconnected == false
|
||||
: true;
|
||||
|
||||
@override
|
||||
HardwareWalletService getHardwareWalletService(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return monero!.getTrezorHardwareWalletService(_client!);
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, true);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, false);
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.getTrezorHardwareWalletService(trezorConnect);
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initWallet(WalletBase wallet) async {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
return monero!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type} for trezor');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class TrezorParingState {
|
||||
static TrezorParingState initial = InitialTrezorParingState();
|
||||
static TrezorParingState enterPin = EnterPinTrezorParingState();
|
||||
static TrezorParingState verifyingPin = VerifyingPinTrezorParingState();
|
||||
static TrezorParingState success = SuccessTrezorParingState();
|
||||
static TrezorParingState fail(String message) => FailTrezorParingState(message);
|
||||
}
|
||||
|
||||
class InitialTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class EnterPinTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class VerifyingPinTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class SuccessTrezorParingState extends TrezorParingState {}
|
||||
|
||||
class FailTrezorParingState extends TrezorParingState {
|
||||
final String message;
|
||||
|
||||
FailTrezorParingState(this.message);
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
|
||||
import 'package:cake_wallet/evm/evm.dart';
|
||||
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:trezor_connect/trezor_connect.dart' as sdk;
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'trezor_view_model.g.dart';
|
||||
|
||||
class TrezorViewModel = TrezorViewModelBase with _$TrezorViewModel;
|
||||
|
||||
abstract class TrezorViewModelBase extends HardwareWalletViewModel with Store {
|
||||
final sdk.TrezorConnect trezorConnect;
|
||||
|
||||
TrezorViewModelBase(this.trezorConnect);
|
||||
|
||||
@override
|
||||
HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
|
||||
|
||||
@override
|
||||
@observable
|
||||
bool isBleEnabled = false;
|
||||
|
||||
@override
|
||||
bool get hasBluetooth => false;
|
||||
|
||||
@override
|
||||
Future<void> updateBleState() async {}
|
||||
|
||||
@override
|
||||
Stream<HardwareWalletDevice> scanForBleDevices() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<List<HardwareWalletDevice>> getAllUsbDevices() async => [];
|
||||
|
||||
@override
|
||||
Future<void> stopScanning() async {}
|
||||
|
||||
@override
|
||||
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async => true;
|
||||
|
||||
@override
|
||||
bool get isConnected => true;
|
||||
|
||||
@override
|
||||
HardwareWalletService getHardwareWalletService(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, true);
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, false);
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.getTrezorHardwareWalletService(trezorConnect);
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initWallet(WalletBase wallet) async {
|
||||
switch (wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
return bitcoin!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
case WalletType.ethereum:
|
||||
case WalletType.polygon:
|
||||
return evm!.setHardwareWalletService(wallet, await getHardwareWalletService(wallet.type));
|
||||
default:
|
||||
throw Exception('Unexpected wallet type: ${wallet.type} for trezor');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,7 +619,6 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
if (!(state is IsExecutingState)) state = IsExecutingState();
|
||||
|
||||
if (wallet.isHardwareWallet) {
|
||||
state = IsAwaitingDeviceResponseState();
|
||||
if (walletType == WalletType.monero) {
|
||||
_ledgerTxStateTimer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
if (monero!.getLastLedgerCommand() == "INS_CLSAG") {
|
||||
@@ -627,6 +626,8 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
state = IsDeviceSigningResponseState();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
state = IsAwaitingDeviceResponseState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,8 +971,11 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||
throw Exception("Pending transaction doesn't exist. It should not be happened.");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
state = TransactionCommitting();
|
||||
state = wallet.isHardwareWallet && walletType == WalletType.monero
|
||||
? IsAwaitingDeviceResponseState()
|
||||
: TransactionCommitting();
|
||||
|
||||
if (ocpRequest != null) {
|
||||
await _handleOcpRequest();
|
||||
|
||||
@@ -89,7 +89,7 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
|
||||
|
||||
credentials = monero!.createMoneroRestoreWalletFromHardwareCredentials(
|
||||
name: name,
|
||||
ledgerConnection: (hardwareWalletVM as LedgerViewModel).connection,
|
||||
hardwareWalletService: hardwareWalletVM.getHardwareWalletService(type),
|
||||
password: password,
|
||||
height: _options['height'] as int? ?? 0,
|
||||
);
|
||||
|
||||
@@ -125,7 +125,7 @@ dependencies:
|
||||
ledger_flutter_plus:
|
||||
git:
|
||||
url: https://github.com/vespr-wallet/ledger-flutter-plus
|
||||
ref: 60817d4b20144f9da9029f5034790272795b9d38
|
||||
ref: 0ccfbe26298e8974dfd4e891f34719c8348562f9
|
||||
trezor_connect:
|
||||
git:
|
||||
url: https://github.com/cake-tech/trezor_connect
|
||||
@@ -203,7 +203,7 @@ dependency_overrides:
|
||||
ledger_flutter_plus:
|
||||
git:
|
||||
url: https://github.com/cake-tech/ledger-flutter-plus
|
||||
ref: 5237e5b3d5f07696eefd0aaee9c32478ed399d20
|
||||
ref: 7bcb00380783f8611f68f5cd63fec96261fea2c2
|
||||
ledger_usb_plus:
|
||||
git:
|
||||
url: https://github.com/konstantinullrich/ledger-usb-plus
|
||||
@@ -227,6 +227,10 @@ dependency_overrides:
|
||||
decimal: ^2.3.3
|
||||
flutter_rust_bridge: 2.11.1
|
||||
flutter_secure_storage_windows: 3.1.2
|
||||
universal_ble:
|
||||
git:
|
||||
url: https://github.com/cake-tech/universal_ble.git
|
||||
ref: f7044558ad111a156987614971e08c583c80175c
|
||||
|
||||
flutter_icons:
|
||||
image_path: "assets/images/app_logo.png"
|
||||
@@ -290,6 +294,7 @@ flutter:
|
||||
- assets/new-ui/card_icons/outline_icons/
|
||||
- assets/new-ui/card_icons/symbol_icons/
|
||||
- assets/new-ui/crypto_full_icons/
|
||||
- assets/new-ui/hardware_wallets/
|
||||
- assets/new-ui/node_speed_badges/
|
||||
|
||||
fonts:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.2495 21.6537V13.8037L6.39945 18.6537L5.3457 17.5999L10.9457 11.9999L5.3457 6.39994L6.39945 5.34619L11.2495 10.1962V2.34619H11.9995L17.3532 7.69994L13.0532 11.9999L17.3532 16.2999L11.9995 21.6537H11.2495ZM12.7495 10.1962L15.2457 7.69994L12.7495 5.23444V10.1962ZM12.7495 18.7654L15.2457 16.2999L12.7495 13.8037V18.7654Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 454 B |
@@ -0,0 +1,60 @@
|
||||
<svg width="75" height="75" viewBox="0 0 75 75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M37.5 1C57.6584 1 74 17.3416 74 37.5C74 57.6584 57.6584 74 37.5 74C17.3416 74 1 57.6584 1 37.5C1 17.3416 17.3416 1 37.5 1Z" fill="url(#paint0_linear_19960_1035188)"/>
|
||||
<path d="M37.5 1C57.6584 1 74 17.3416 74 37.5C74 57.6584 57.6584 74 37.5 74C17.3416 74 1 57.6584 1 37.5C1 17.3416 17.3416 1 37.5 1Z" fill="url(#paint1_linear_19960_1035188)" fill-opacity="0.25"/>
|
||||
<path d="M63.1728 37.4961C63.1728 23.3155 51.6777 11.8195 37.4971 11.8193C23.3163 11.8193 11.8203 23.3154 11.8203 37.4961C11.8205 51.6767 23.3164 63.1719 37.4971 63.1719V66.1719L36.7568 66.1631C21.5073 65.7768 9.21662 53.4858 8.83008 38.2363L8.82031 37.4961C8.82031 21.6585 21.6595 8.81934 37.4971 8.81934L38.2373 8.8291C53.7326 9.22188 66.1728 21.9061 66.1728 37.4961L66.1641 38.2363C65.7713 53.7316 53.0869 66.1717 37.4971 66.1719V63.1719C51.6776 63.1717 63.1727 51.6766 63.1728 37.4961Z" fill="#D58308" fill-opacity="0.2"/>
|
||||
<path d="M65.1728 37.4961C65.1728 22.2109 52.7822 9.81949 37.4971 9.81934C22.2118 9.81934 9.82031 22.2108 9.82031 37.4961C9.82047 52.7812 22.2119 65.1719 37.4971 65.1719V66.1719L36.7568 66.1631C21.5073 65.7768 9.21662 53.4858 8.83008 38.2363L8.82031 37.4961C8.82031 21.6585 21.6595 8.81934 37.4971 8.81934L38.2373 8.8291C53.7326 9.22188 66.1728 21.9061 66.1728 37.4961L66.1641 38.2363C65.7713 53.7316 53.0869 66.1717 37.4971 66.1719V65.1719C52.7821 65.1717 65.1727 52.7812 65.1728 37.4961Z" fill="url(#paint2_linear_19960_1035188)" fill-opacity="0.5"/>
|
||||
<path d="M48.2033 35.5459C47.8236 35.9243 47.5076 36.3135 47.2941 36.8839C47.0805 37.4542 46.9748 38.0753 46.8228 39.0575C46.6049 40.4558 46.1799 41.0057 45.8435 41.3528C45.1564 42.0612 44.1803 42.0213 42.9907 41.7863C42.2433 41.6385 42.0491 41.8164 41.1604 42.5841L36.4752 47.2223L36.4731 54.2637C36.4731 54.3898 36.6263 54.4535 36.7158 54.364L50.5534 40.5539C50.74 40.3674 50.8457 40.1151 50.8457 39.851L50.8629 32.8721L48.2033 35.5448V35.5459Z" fill="url(#paint3_linear_19960_1035188)"/>
|
||||
<path d="M48.2033 35.5459C47.8236 35.9243 47.5076 36.3135 47.2941 36.8839C47.0805 37.4542 46.9748 38.0753 46.8228 39.0575C46.6049 40.4558 46.1799 41.0057 45.8435 41.3528C45.1564 42.0612 44.1803 42.0213 42.9907 41.7863C42.2433 41.6385 42.0491 41.8164 41.1604 42.5841L36.4752 47.2223L36.4731 54.2637C36.4731 54.3898 36.6263 54.4535 36.7158 54.364L50.5534 40.5539C50.74 40.3674 50.8457 40.1151 50.8457 39.851L50.8629 32.8721L48.2033 35.5448V35.5459Z" fill="url(#paint4_linear_19960_1035188)" fill-opacity="0.2"/>
|
||||
<path d="M42.6209 39.9512C43.5225 39.9771 44.1513 40.6294 44.8934 40.1614C45.6354 39.6935 45.6149 39.1728 45.7583 38.3124C45.9028 37.451 46.1887 36.4494 46.4432 35.8456C46.6988 35.2408 47.2694 34.4677 47.8496 33.9039L50.8404 30.7934L50.8598 27.6592C50.8598 27.4673 50.629 27.3703 50.4931 27.5061L36.8917 41.1027C36.6242 41.37 36.4743 41.7323 36.4743 42.1097V45.1953L41.0689 40.5852C41.0689 40.5852 41.7181 39.9264 42.6198 39.9523L42.6209 39.9512Z" fill="url(#paint5_linear_19960_1035188)"/>
|
||||
<path d="M42.6209 39.9512C43.5225 39.9771 44.1513 40.6294 44.8934 40.1614C45.6354 39.6935 45.6149 39.1728 45.7583 38.3124C45.9028 37.451 46.1887 36.4494 46.4432 35.8456C46.6988 35.2408 47.2694 34.4677 47.8496 33.9039L50.8404 30.7934L50.8598 27.6592C50.8598 27.4673 50.629 27.3703 50.4931 27.5061L36.8917 41.1027C36.6242 41.37 36.4743 41.7323 36.4743 42.1097V45.1953L41.0689 40.5852C41.0689 40.5852 41.7181 39.9264 42.6198 39.9523L42.6209 39.9512Z" fill="url(#paint6_linear_19960_1035188)" fill-opacity="0.2"/>
|
||||
<path d="M50.8641 25.0892L36.4743 39.5116V36.4324C36.4743 36.0551 36.6242 35.6928 36.8917 35.4255L50.4964 21.8257C50.6312 21.6909 50.8631 21.7858 50.8631 21.9777V25.0892H50.8641Z" fill="url(#paint7_linear_19960_1035188)"/>
|
||||
<path d="M50.8641 25.0892L36.4743 39.5116V36.4324C36.4743 36.0551 36.6242 35.6928 36.8917 35.4255L50.4964 21.8257C50.6312 21.6909 50.8631 21.7858 50.8631 21.9777V25.0892H50.8641Z" fill="url(#paint8_linear_19960_1035188)" fill-opacity="0.2"/>
|
||||
<path d="M49.7562 19.6088L25.3952 24.9143C24.5227 25.1041 23.9004 25.8761 23.9004 26.7688V44.5508C23.9004 44.8149 24.005 45.0682 24.1927 45.2558L34.4431 55.5026C34.6523 55.7118 35.0104 55.564 35.0104 55.2676V36.2101C35.0115 35.5816 35.2617 34.9778 35.7061 34.5336L50.0625 20.1683C50.2966 19.9344 50.0808 19.5387 49.7573 19.6088H49.7562Z" fill="url(#paint9_linear_19960_1035188)"/>
|
||||
<path d="M49.7562 19.6088L25.3952 24.9143C24.5227 25.1041 23.9004 25.8761 23.9004 26.7688V44.5508C23.9004 44.8149 24.005 45.0682 24.1927 45.2558L34.4431 55.5026C34.6523 55.7118 35.0104 55.564 35.0104 55.2676V36.2101C35.0115 35.5816 35.2617 34.9778 35.7061 34.5336L50.0625 20.1683C50.2966 19.9344 50.0808 19.5387 49.7573 19.6088H49.7562Z" fill="url(#paint10_linear_19960_1035188)" fill-opacity="0.2"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_19960_1035188" x1="37.5" y1="0" x2="37.5" y2="75" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFCC35"/>
|
||||
<stop offset="1" stop-color="#FFAE00"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_19960_1035188" x1="37.5" y1="0" x2="37.5" y2="75" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="0.25" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_19960_1035188" x1="37.4968" y1="8.81934" x2="37.4968" y2="66.1723" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#935800"/>
|
||||
<stop offset="1" stop-color="#D58308" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D08C00"/>
|
||||
<stop offset="0.5" stop-color="#E19803"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5A3B00"/>
|
||||
<stop offset="0.25" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D08C00"/>
|
||||
<stop offset="0.5" stop-color="#E19803"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5A3B00"/>
|
||||
<stop offset="0.25" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D08C00"/>
|
||||
<stop offset="0.5" stop-color="#E19803"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5A3B00"/>
|
||||
<stop offset="0.25" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D08C00"/>
|
||||
<stop offset="0.5" stop-color="#E19803"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_19960_1035188" x1="37.3823" y1="19.6006" x2="37.3823" y2="55.6006" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#5A3B00"/>
|
||||
<stop offset="0.25" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,191 @@
|
||||
<svg width="75" height="75" viewBox="0 0 75 75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_19960_1035187)">
|
||||
<g clip-path="url(#clip1_19960_1035187)">
|
||||
<path d="M26.5632 1.37614C27.7392 0.200141 29.6352 0.200141 30.8112 1.37614L71.1192 41.6841C76.9752 47.5401 76.9752 57.0441 71.1192 62.9001C65.2632 68.7561 55.7592 68.7561 49.9032 62.9001L9.58317 22.5921C8.40717 21.4161 8.40717 19.5201 9.58317 18.3441L26.5632 1.37614Z" fill="url(#paint0_linear_19960_1035187)"/>
|
||||
<path d="M26.5632 1.37614C27.7392 0.200141 29.6352 0.200141 30.8112 1.37614L71.1192 41.6841C76.9752 47.5401 76.9752 57.0441 71.1192 62.9001C65.2632 68.7561 55.7592 68.7561 49.9032 62.9001L9.58317 22.5921C8.40717 21.4161 8.40717 19.5201 9.58317 18.3441L26.5632 1.37614Z" fill="url(#paint1_linear_19960_1035187)"/>
|
||||
<path d="M26.5632 1.37614C27.7392 0.200141 29.6352 0.200141 30.8112 1.37614L71.1192 41.6841C76.9752 47.5401 76.9752 57.0441 71.1192 62.9001C65.2632 68.7561 55.7592 68.7561 49.9032 62.9001L9.58317 22.5921C8.40717 21.4161 8.40717 19.5201 9.58317 18.3441L26.5632 1.37614Z" fill="url(#paint2_linear_19960_1035187)"/>
|
||||
<path d="M26.5632 1.37614C27.7392 0.200141 29.6352 0.200141 30.8112 1.37614L71.1192 41.6841C76.9752 47.5401 76.9752 57.0441 71.1192 62.9001C65.2632 68.7561 55.7592 68.7561 49.9032 62.9001L9.58317 22.5921C8.40717 21.4161 8.40717 19.5201 9.58317 18.3441L26.5632 1.37614Z" fill="url(#paint3_linear_19960_1035187)" fill-opacity="0.5"/>
|
||||
<path d="M26.5632 1.37614C27.7392 0.200141 29.6352 0.200141 30.8112 1.37614L71.1192 41.6841C76.9752 47.5401 76.9752 57.0441 71.1192 62.9001C65.2632 68.7561 55.7592 68.7561 49.9032 62.9001L9.58317 22.5921C8.40717 21.4161 8.40717 19.5201 9.58317 18.3441L26.5632 1.37614Z" fill="url(#paint4_linear_19960_1035187)" fill-opacity="0.3"/>
|
||||
<path d="M26.5632 1.37614C27.7392 0.200141 29.6352 0.200141 30.8112 1.37614L71.1192 41.6841C76.9752 47.5401 76.9752 57.0441 71.1192 62.9001C65.2632 68.7561 55.7592 68.7561 49.9032 62.9001L9.58317 22.5921C8.40717 21.4161 8.40717 19.5201 9.58317 18.3441L26.5632 1.37614Z" fill="url(#paint5_linear_19960_1035187)" fill-opacity="0.4"/>
|
||||
<rect x="44.2656" y="24.5062" width="32.76" height="16.44" rx="1.68" transform="rotate(45 44.2656 24.5062)" fill="#09479E"/>
|
||||
<rect x="44.2656" y="24.5062" width="32.76" height="16.44" rx="1.68" transform="rotate(45 44.2656 24.5062)" fill="url(#paint6_linear_19960_1035187)"/>
|
||||
<rect x="44.2656" y="24.5062" width="32.76" height="16.44" rx="1.68" transform="rotate(45 44.2656 24.5062)" stroke="url(#paint7_linear_19960_1035187)" stroke-width="0.72"/>
|
||||
<mask id="mask0_19960_1035187" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="32" y="24" width="36" height="35">
|
||||
<rect x="44.2656" y="24.5062" width="32.76" height="16.44" rx="1.68" transform="rotate(45 44.2656 24.5062)" fill="#09479E" stroke="url(#paint8_linear_19960_1035187)" stroke-width="0.72"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_19960_1035187)">
|
||||
<rect x="58.5298" y="28.9963" width="9.6" height="29.0002" transform="rotate(75 58.5298 28.9963)" fill="url(#paint9_linear_19960_1035187)" fill-opacity="0.2" stroke="url(#paint10_linear_19960_1035187)" stroke-width="0.12"/>
|
||||
</g>
|
||||
<rect x="44.2656" y="24.5062" width="32.76" height="16.44" rx="1.68" transform="rotate(45 44.2656 24.5062)" stroke="url(#paint11_linear_19960_1035187)" stroke-width="0.72"/>
|
||||
<path d="M26.9023 1.71582C27.8909 0.727272 29.4831 0.727273 30.4717 1.71582L70.7793 42.0234C76.4478 47.692 76.4478 56.892 70.7793 62.5605C65.1107 68.2289 55.9107 68.229 50.2422 62.5605L9.92285 22.2529C8.99611 21.3262 8.93817 19.869 9.74902 18.876L9.92285 18.6836L26.9023 1.71582Z" stroke="url(#paint12_linear_19960_1035187)" stroke-width="0.96"/>
|
||||
<path d="M26.9023 1.71582C27.8909 0.727272 29.4831 0.727273 30.4717 1.71582L70.7793 42.0234C76.4478 47.692 76.4478 56.892 70.7793 62.5605C65.1107 68.2289 55.9107 68.229 50.2422 62.5605L9.92285 22.2529C8.99611 21.3262 8.93817 19.869 9.74902 18.876L9.92285 18.6836L26.9023 1.71582Z" stroke="url(#paint13_linear_19960_1035187)" stroke-opacity="0.5" stroke-width="0.96"/>
|
||||
<circle cx="28.7422" cy="20.5524" r="8.04" transform="rotate(45 28.7422 20.5524)" stroke="url(#paint14_linear_19960_1035187)" stroke-width="1.08"/>
|
||||
<g filter="url(#filter0_d_19960_1035187)">
|
||||
<circle cx="28.7422" cy="20.5524" r="7.26" transform="rotate(45 28.7422 20.5524)" stroke="url(#paint15_linear_19960_1035187)" stroke-width="0.48" shape-rendering="crispEdges"/>
|
||||
</g>
|
||||
<path d="M0.5 40.292C0.5 38.636 1.844 37.292 3.5 37.292H60.5C68.78 37.292 75.5 44.012 75.5 52.292C75.5 60.572 68.78 67.292 60.5 67.292H3.5C1.844 67.292 0.5 65.948 0.5 64.292V40.292Z" fill="url(#paint16_linear_19960_1035187)"/>
|
||||
<path d="M0.5 40.292C0.5 38.636 1.844 37.292 3.5 37.292H60.5C68.78 37.292 75.5 44.012 75.5 52.292C75.5 60.572 68.78 67.292 60.5 67.292H3.5C1.844 67.292 0.5 65.948 0.5 64.292V40.292Z" fill="url(#paint17_linear_19960_1035187)"/>
|
||||
<path d="M0.5 40.292C0.5 38.636 1.844 37.292 3.5 37.292H60.5C68.78 37.292 75.5 44.012 75.5 52.292C75.5 60.572 68.78 67.292 60.5 67.292H3.5C1.844 67.292 0.5 65.948 0.5 64.292V40.292Z" fill="url(#paint18_linear_19960_1035187)" fill-opacity="0.5"/>
|
||||
<path d="M0.5 40.292C0.5 38.636 1.844 37.292 3.5 37.292H60.5C68.78 37.292 75.5 44.012 75.5 52.292C75.5 60.572 68.78 67.292 60.5 67.292H3.5C1.844 67.292 0.5 65.948 0.5 64.292V40.292Z" fill="url(#paint19_linear_19960_1035187)" fill-opacity="0.3"/>
|
||||
<mask id="mask1_19960_1035187" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="37" width="76" height="31">
|
||||
<path d="M0.5 40.292C0.5 38.636 1.844 37.292 3.5 37.292H60.5C68.78 37.292 75.5 44.012 75.5 52.292C75.5 60.572 68.78 67.292 60.5 67.292H3.5C1.844 67.292 0.5 65.948 0.5 64.292V40.292Z" fill="url(#paint20_linear_19960_1035187)"/>
|
||||
<path d="M0.5 40.292C0.5 38.636 1.844 37.292 3.5 37.292H60.5C68.78 37.292 75.5 44.012 75.5 52.292C75.5 60.572 68.78 67.292 60.5 67.292H3.5C1.844 67.292 0.5 65.948 0.5 64.292V40.292Z" fill="url(#paint21_linear_19960_1035187)"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_19960_1035187)">
|
||||
<rect x="20.6016" y="23.8003" width="21.4265" height="49.5049" transform="rotate(20.9508 20.6016 23.8003)" fill="url(#paint22_linear_19960_1035187)" fill-opacity="0.3"/>
|
||||
</g>
|
||||
<path d="M3.5 37.6523H60.5C68.5812 37.6523 75.1396 44.2108 75.1396 52.292C75.1396 60.3732 68.5812 66.9316 60.5 66.9316H3.5C2.04282 66.9316 0.860355 65.7492 0.860352 64.292V40.292C0.860352 38.8348 2.04282 37.6523 3.5 37.6523Z" stroke="url(#paint23_linear_19960_1035187)" stroke-width="0.72"/>
|
||||
<path d="M3.5 37.6523H60.5C68.5812 37.6523 75.1396 44.2108 75.1396 52.292C75.1396 60.3732 68.5812 66.9316 60.5 66.9316H3.5C2.04282 66.9316 0.860355 65.7492 0.860352 64.292V40.292C0.860352 38.8348 2.04282 37.6523 3.5 37.6523Z" stroke="url(#paint24_linear_19960_1035187)" stroke-opacity="0.3" stroke-width="0.72"/>
|
||||
<circle cx="60.5605" cy="52.2798" r="8.04" stroke="url(#paint25_linear_19960_1035187)" stroke-width="1.08"/>
|
||||
<g filter="url(#filter1_d_19960_1035187)">
|
||||
<circle cx="60.5605" cy="52.2798" r="7.5" fill="url(#paint26_linear_19960_1035187)" fill-opacity="0.5" shape-rendering="crispEdges"/>
|
||||
<circle cx="60.5605" cy="52.2798" r="7.5" fill="url(#paint27_linear_19960_1035187)" fill-opacity="0.2" shape-rendering="crispEdges"/>
|
||||
<circle cx="60.5605" cy="52.2798" r="7.25955" stroke="url(#paint28_linear_19960_1035187)" stroke-width="0.480902" shape-rendering="crispEdges"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_19960_1035187" x="19.1589" y="12.6356" width="22.5" height="22.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="1.66667" dy="3.33333"/>
|
||||
<feGaussianBlur stdDeviation="1.875"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0353643 0 0 0 0 0.224702 0 0 0 0 0.490385 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_19960_1035187"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_19960_1035187" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_19960_1035187" x="50.9772" y="44.3631" width="22.5" height="22.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="1.66667" dy="3.33333"/>
|
||||
<feGaussianBlur stdDeviation="1.875"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.34956 0 0 0 0 0.30834 0 0 0 0 0.442308 0 0 0 0.5 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_19960_1035187"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_19960_1035187" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_19960_1035187" x1="18.3192" y1="5.66012" x2="48.4992" y2="40.8201" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EFEFF8"/>
|
||||
<stop offset="1" stop-color="#C6C6E6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_19960_1035187" x1="34.1959" y1="0.49414" x2="67.9747" y2="49.3316" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FE6412"/>
|
||||
<stop offset="1" stop-color="#EB4600"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_19960_1035187" x1="19.3392" y1="0.500117" x2="61.0992" y2="50.2401" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4EBEFF"/>
|
||||
<stop offset="1" stop-color="#2982FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_19960_1035187" x1="46.7493" y1="-9.91652" x2="52.9993" y2="15.0835" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_19960_1035187" x1="33.8325" y1="17.1668" x2="55.4992" y2="49.6668" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#000938" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#000938"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_19960_1035187" x1="30.4991" y1="20.3751" x2="12.4991" y2="47.7501" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.176297" stop-color="#EFEFF8" stop-opacity="0"/>
|
||||
<stop offset="0.571544" stop-color="#EFEFF8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_19960_1035187" x1="61.0056" y1="23.9971" x2="61.0056" y2="41.1571" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#09479E"/>
|
||||
<stop offset="1" stop-color="#001D67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_19960_1035187" x1="49.3656" y1="23.2171" x2="64.2035" y2="38.4576" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#002A66"/>
|
||||
<stop offset="1" stop-color="#002A66" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_19960_1035187" x1="49.3656" y1="23.2171" x2="64.2035" y2="38.4576" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#002A66"/>
|
||||
<stop offset="1" stop-color="#002A66" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_19960_1035187" x1="58.5723" y1="43.4829" x2="68.2923" y2="43.4829" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#DBEDFF"/>
|
||||
<stop offset="1" stop-color="#DBEDFF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_19960_1035187" x1="60.0529" y1="27.5992" x2="68.6402" y2="29.1082" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#002A66"/>
|
||||
<stop offset="1" stop-color="#002A66" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_19960_1035187" x1="49.3656" y1="23.2171" x2="64.2035" y2="38.4576" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#002A66"/>
|
||||
<stop offset="1" stop-color="#002A66" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_19960_1035187" x1="42.1062" y1="0.494141" x2="34.0992" y2="38.6001" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.5"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint13_linear_19960_1035187" x1="32.6592" y1="8.54012" x2="21.6192" y2="36.3201" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#195BA1" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#195BA1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint14_linear_19960_1035187" x1="21.2422" y1="14.3964" x2="36.646" y2="16.1247" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#2D72BB" stop-opacity="0.2"/>
|
||||
<stop offset="1" stop-color="#185AA0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint15_linear_19960_1035187" x1="21.9572" y1="17.7316" x2="35.7882" y2="23.3319" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.3"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint16_linear_19960_1035187" x1="29.12" y1="37.292" x2="32.8635" y2="62.2832" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EB4600"/>
|
||||
<stop offset="1" stop-color="#FE6412"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint17_linear_19960_1035187" x1="0.5" y1="39.98" x2="72.8" y2="60.26" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EFEFF8"/>
|
||||
<stop offset="1" stop-color="#C6C6E6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint18_linear_19960_1035187" x1="38" y1="43" x2="35.5" y2="75.9167" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7D6493" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#7D6493"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint19_linear_19960_1035187" x1="61.3333" y1="28.4167" x2="47.5833" y2="58" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#46B0FF"/>
|
||||
<stop offset="1" stop-color="#46B0FF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint20_linear_19960_1035187" x1="29.12" y1="37.292" x2="32.8635" y2="62.2832" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EB4600"/>
|
||||
<stop offset="1" stop-color="#FE6412"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint21_linear_19960_1035187" x1="0.5" y1="39.98" x2="72.8" y2="60.26" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EFEFF8"/>
|
||||
<stop offset="1" stop-color="#C6C6E6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint22_linear_19960_1035187" x1="30.974" y1="33.6949" x2="32.8091" y2="66.4546" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint23_linear_19960_1035187" x1="38" y1="37.292" x2="38" y2="67.292" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint24_linear_19960_1035187" x1="38" y1="37.292" x2="38" y2="67.292" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#312938" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#312938"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint25_linear_19960_1035187" x1="53.0605" y1="46.1238" x2="68.4643" y2="47.8521" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#6E6EA8"/>
|
||||
<stop offset="1" stop-color="#312938"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint26_linear_19960_1035187" x1="56.3337" y1="45.9165" x2="63.0003" y2="57.5831" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint27_linear_19960_1035187" x1="60.5605" y1="44.7798" x2="65.5005" y2="56.3331" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#564A84" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#564A84"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint28_linear_19960_1035187" x1="53.7756" y1="49.459" x2="67.6066" y2="55.0593" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_19960_1035187">
|
||||
<rect width="75" height="75" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_19960_1035187">
|
||||
<rect width="75" height="66.792" fill="white" transform="translate(0.5 0.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -1,3 +1,3 @@
|
||||
<svg width="97" height="97" viewBox="0 0 97 97" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M67.0791 26C72.5581 26 77 30.4419 77 35.9209V39H90V58H77V61.0791C77 66.5581 72.5581 71 67.0791 71H15.9209L15.4102 70.9873C10.1684 70.7216 6 66.3869 6 61.0791V35.9209C6 30.6131 10.1684 26.2784 15.4102 26.0127L15.9209 26H67.0791ZM15.9209 33C14.3582 33 13.082 34.2273 13.0039 35.7705L13 35.9209V61.0791C13 62.6921 14.3079 64 15.9209 64H67.0791C68.6921 64 70 62.6921 70 61.0791V35.9209C70 34.3079 68.6921 33 67.0791 33H15.9209Z" fill="#D7E2F7"/>
|
||||
<path d="M67.0791 26C72.5581 26 77 30.4419 77 35.9209V39H90V58H77V61.0791C77 66.5581 72.5581 71 67.0791 71H15.9209L15.4102 70.9873C10.1684 70.7216 6 66.3869 6 61.0791V35.9209C6 30.6131 10.1684 26.2784 15.4102 26.0127L15.9209 26H67.0791ZM15.9209 33C14.3582 33 13.082 34.2273 13.0039 35.7705L13 35.9209V61.0791C13 62.6921 14.3079 64 15.9209 64H67.0791C68.6921 64 70 62.6921 70 61.0791V35.9209C70 34.3079 68.6921 33 67.0791 33H15.9209Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 552 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.7886 19.8887C16.3406 19.8889 16.7884 20.3366 16.7886 20.8887C16.7886 20.9262 16.7858 20.9636 16.7817 21H16.7886V23H6.45459V21H6.46143C6.4574 20.9636 6.45459 20.9262 6.45459 20.8887C6.45481 20.3366 6.90254 19.8888 7.45459 19.8887H15.7886ZM18.3989 5.33301C18.3987 4.0445 17.3545 3 16.0659 3H7.17725C5.88869 3 4.84346 4.0445 4.84326 5.33301V23H2.84326V5.33301C2.84346 2.93992 4.78411 1 7.17725 1H16.0659C18.459 1 20.3987 2.93992 20.3989 5.33301V5.46582C20.968 5.57023 21.3989 6.06778 21.3989 6.66699V9.55566C21.3989 10.1548 20.9679 10.6514 20.3989 10.7559V23H18.3989V5.33301Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 705 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.3503 0C19.0071 0 20.3503 1.34315 20.3503 3V3.6C20.6816 3.6 20.9503 3.86863 20.9503 4.2V7.2C20.9503 7.49005 20.7446 7.73245 20.471 7.78828L20.3503 7.8V21C20.3503 22.6568 19.0071 24 17.3503 24H6.55029C4.89344 24 3.55029 22.6568 3.55029 21V3C3.55029 1.34315 4.89344 0 6.55029 0H17.3503ZM14.9503 20.4C14.2875 20.4 13.7503 20.9372 13.7503 21.6C13.7503 22.2628 14.2875 22.8 14.9503 22.8H17.3503C18.0131 22.8 18.5503 22.2628 18.5503 21.6C18.5503 20.9372 18.0131 20.4 17.3503 20.4H14.9503ZM6.55029 1.8C5.88756 1.8 5.35029 2.33726 5.35029 3V17.4C5.35029 18.0628 5.88756 18.6 6.55029 18.6H17.3503C18.0131 18.6 18.5503 18.0628 18.5503 17.4V3C18.5503 2.33726 18.0131 1.8 17.3503 1.8H6.55029Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 812 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="22" height="21" viewBox="0 0 22 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 20.4286H17.4952C19.9571 20.4286 22 18.3857 22 15.8976C22 14.7452 21.581 13.6452 20.8214 12.8595L15.1643 7.1762L15.8714 6.46906L14.0119 4.60954L13.3048 5.31667L11.2095 3.24762L11.9429 2.51429L10.0833 0.654762L9.34999 1.3881L7.9881 0L1.72857 6.25954L6.8881 11.3929H0V20.4286ZM1.96429 18.4643V13.3571H17.4952C18.8833 13.3571 20.0357 14.5095 20.0357 15.8976C20.0357 17.3119 18.8833 18.4643 17.4952 18.4643H1.96429ZM4.50476 6.25954L7.9881 2.77619L16.5786 11.3929H15.7929H9.66428L4.50476 6.25954ZM15.9762 15.8976C15.9762 16.6309 16.5786 17.2857 17.3643 17.2857C18.1238 17.2857 18.7262 16.6309 18.7262 15.8976C18.7262 15.1381 18.1238 14.5357 17.3643 14.5357C16.5786 14.5357 15.9762 15.1381 15.9762 15.8976Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 831 B |
@@ -1,3 +1,3 @@
|
||||
<svg width="97" height="97" viewBox="0 0 97 97" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.76001 86.33H72.556C81.674 86.33 89.24 78.764 89.24 69.549C89.24 65.281 87.688 61.207 84.875 58.297L37.345 10.67L14.162 33.8531L33.271 52.865H7.76001V86.33ZM15.035 79.055V60.14H72.556C77.697 60.14 81.965 64.408 81.965 69.549C81.965 74.787 77.697 79.055 72.556 79.055H15.035ZM24.444 33.8531L37.345 20.952L69.161 52.865H66.251H43.553L24.444 33.8531ZM32.592 34.1441C32.592 36.8601 34.823 39.2851 37.733 39.2851C40.546 39.2851 42.777 36.8601 42.777 34.1441C42.777 31.3311 40.546 29.1001 37.733 29.1001C34.823 29.1001 32.592 31.3311 32.592 34.1441ZM66.93 69.549C66.93 72.265 69.161 74.69 72.071 74.69C74.884 74.69 77.115 72.265 77.115 69.549C77.115 66.736 74.884 64.505 72.071 64.505C69.161 64.505 66.93 66.736 66.93 69.549Z" fill="#D7E2F7"/>
|
||||
<path d="M7.76001 86.33H72.556C81.674 86.33 89.24 78.764 89.24 69.549C89.24 65.281 87.688 61.207 84.875 58.297L37.345 10.67L14.162 33.8531L33.271 52.865H7.76001V86.33ZM15.035 79.055V60.14H72.556C77.697 60.14 81.965 64.408 81.965 69.549C81.965 74.787 77.697 79.055 72.556 79.055H15.035ZM24.444 33.8531L37.345 20.952L69.161 52.865H66.251H43.553L24.444 33.8531ZM32.592 34.1441C32.592 36.8601 34.823 39.2851 37.733 39.2851C40.546 39.2851 42.777 36.8601 42.777 34.1441C42.777 31.3311 40.546 29.1001 37.733 29.1001C34.823 29.1001 32.592 31.3311 32.592 34.1441ZM66.93 69.549C66.93 72.265 69.161 74.69 72.071 74.69C74.884 74.69 77.115 72.265 77.115 69.549C77.115 66.736 74.884 64.505 72.071 64.505C69.161 64.505 66.93 66.736 66.93 69.549Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 850 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.2876 4.09623C17.2876 3.31191 16.6517 2.67602 15.8674 2.67602H5.4143V21.324H15.8674C16.6517 21.324 17.2876 20.6881 17.2876 19.9038V4.09623ZM18.9636 5.51864C19.5292 5.54359 19.9803 6.00899 19.9803 6.58049V9.51574C19.9803 10.0872 19.5292 10.5515 18.9636 10.5765V19.9038C18.9636 21.6139 17.5775 23 15.8674 23H5.14085C4.36614 23 3.73828 22.372 3.73828 21.5974V2.40257C3.73828 1.6279 4.36618 1 5.14085 1H15.8674C17.5775 1 18.9636 2.38619 18.9636 4.09623V5.51864Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 589 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.4873 0.655234C10.4323 -0.218431 13.5677 -0.218392 16.5127 0.655234C17.1731 0.851269 17.7531 1.25547 18.165 1.80758C18.577 2.35968 18.7989 3.03083 18.7988 3.71969V14.8994C18.7986 15.7851 18.6448 16.6641 18.3438 17.497L16.8057 21.7548C16.5693 22.4091 16.1376 22.9755 15.5693 23.3769C15.001 23.7783 14.3227 23.9961 13.627 24H10.3721C9.67638 23.9959 8.99793 23.7783 8.42969 23.3769C7.86158 22.9756 7.43065 22.4091 7.19434 21.7548L5.65625 17.497C5.35543 16.6639 5.20116 15.7851 5.20117 14.8994V3.71969C5.20109 3.03082 5.42396 2.35968 5.83594 1.80758C6.2479 1.25564 6.82704 0.851231 7.4873 0.655234ZM16.0283 2.28512C13.3994 1.50548 10.6006 1.50544 7.97168 2.28512C7.33598 2.47294 6.90137 3.05679 6.90137 3.71969V12.1015H10.3008C10.526 12.1016 10.7421 12.1913 10.9014 12.3505C11.0606 12.5099 11.1503 12.7259 11.1504 12.9511C11.1504 13.1765 11.0607 13.3933 10.9014 13.5527C10.7421 13.7119 10.526 13.8006 10.3008 13.8007H6.90137V14.8994C6.9013 15.5883 7.02094 16.2719 7.25488 16.9199L8.79297 21.1777C8.9117 21.5061 9.12832 21.7905 9.41406 21.9912C9.69995 22.1919 10.0413 22.2996 10.3906 22.2998H13.6094C13.9587 22.2996 14.3001 22.1919 14.5859 21.9912C14.8717 21.7904 15.0883 21.5061 15.207 21.1777L16.7461 16.9199C16.98 16.2719 17.0997 15.5882 17.0996 14.8994V13.8007H13.7002C13.4748 13.8007 13.258 13.712 13.0986 13.5527C12.9393 13.3933 12.8496 13.1765 12.8496 12.9511C12.8497 12.7259 12.9394 12.5099 13.0986 12.3505C13.258 12.1912 13.4748 12.1015 13.7002 12.1015H17.0996V3.71969C17.0995 3.39721 16.9947 3.08255 16.8018 2.82418C16.6088 2.56599 16.3374 2.37669 16.0283 2.28512ZM14.125 5.30269C14.4629 5.30278 14.7874 5.43681 15.0264 5.67574C15.2654 5.91481 15.3994 6.23999 15.3994 6.57809V9.12691C15.3994 9.46486 15.2652 9.78924 15.0264 10.0283C14.7874 10.2673 14.463 10.4022 14.125 10.4023H9.875C9.53702 10.4022 9.21263 10.2673 8.97363 10.0283C8.73476 9.78924 8.60059 9.46486 8.60059 9.12691V6.57809C8.60059 6.23999 8.73456 5.91481 8.97363 5.67574C9.21262 5.43681 9.53707 5.30278 9.875 5.30269H14.125ZM10.2998 8.70211H13.7002V7.00289H10.2998V8.70211Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.2856 0C17.195 0 18.0675 0.360924 18.7104 1.00391C19.3534 1.64688 19.7144 2.51943 19.7144 3.42871V12.7871C19.7143 14.8347 19.2982 16.8608 18.4917 18.7422C18.0912 19.6743 17.5819 20.5558 16.9741 21.3682L15.771 22.9717C15.5315 23.2909 15.2207 23.55 14.8638 23.7285C14.5068 23.9069 14.1134 24 13.7144 24H10.2856C9.88644 24 9.49231 23.907 9.13525 23.7285C8.77834 23.55 8.46747 23.2909 8.22803 22.9717L7.02588 21.3682C6.41841 20.5557 5.90858 19.6743 5.5083 18.7422C4.70075 16.8608 4.285 14.8345 4.28564 12.7871V3.42871C4.28564 2.5194 4.64657 1.64689 5.28955 1.00391C5.93253 0.360924 6.80504 0 7.71436 0H16.2856ZM7.71436 1.71387C7.2597 1.71387 6.82393 1.89531 6.50244 2.2168C6.18095 2.53829 5.99951 2.97405 5.99951 3.42871V12.7871C5.99955 14.6023 6.36877 16.3986 7.0835 18.0664C7.42973 18.8737 7.87078 19.6371 8.39697 20.3398L9.6001 21.9424C9.67986 22.0487 9.78298 22.1358 9.90186 22.1953C10.0209 22.2548 10.1526 22.2861 10.2856 22.2861H13.7144C13.8472 22.2861 13.9783 22.2547 14.0972 22.1953C14.2162 22.1358 14.3201 22.0488 14.3999 21.9424L15.6021 20.3398C16.1283 19.637 16.5702 18.8738 16.9165 18.0664C17.6322 16.3984 18.0005 14.6022 17.9995 12.7871V3.42871C17.9995 2.97409 17.819 2.53828 17.4976 2.2168C17.1761 1.89531 16.7403 1.71387 16.2856 1.71387H7.71436ZM14.9995 5.14258C15.3404 5.14258 15.6676 5.27852 15.9087 5.51953C16.1498 5.76065 16.2856 6.08772 16.2856 6.42871V12.4287C16.2856 12.7697 16.1498 13.0968 15.9087 13.3379C15.6676 13.5788 15.3404 13.7139 14.9995 13.7139H8.99951C8.65884 13.7137 8.33228 13.5787 8.09131 13.3379C7.85022 13.0968 7.71439 12.7697 7.71436 12.4287V6.42871C7.71436 6.08772 7.85019 5.76065 8.09131 5.51953C8.33229 5.27864 8.65878 5.14271 8.99951 5.14258H14.9995ZM9.42822 12H14.5718V6.85742H9.42822V12Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.1431 0C17.825 7.38484e-05 18.4793 0.270759 18.9614 0.75293C19.4436 1.23513 19.7143 1.88939 19.7144 2.57129V13.2578C19.7143 14.3894 19.4651 15.5076 18.9849 16.5322L16.1772 22.5205C15.9697 22.9628 15.6404 23.3366 15.228 23.5986C14.8157 23.8605 14.3376 24.0001 13.8491 24H10.1509C9.66231 24.0001 9.18339 23.8606 8.771 23.5986C8.35878 23.3366 8.0292 22.9627 7.82178 22.5205L5.01514 16.5322C4.53484 15.5076 4.28567 14.3894 4.28564 13.2578V2.57129C4.28568 1.88935 4.55637 1.23513 5.03857 0.75293C5.52078 0.270726 6.175 3.69506e-05 6.85693 0H17.1431ZM5.99951 12V13.2578C5.99957 14.1379 6.19335 15.0078 6.56689 15.8047L9.37451 21.792C9.44362 21.9396 9.55337 22.0649 9.69092 22.1523C9.82836 22.2397 9.98804 22.2861 10.1509 22.2861H13.8491C14.012 22.2861 14.1717 22.2397 14.3091 22.1523C14.4466 22.0649 14.5564 21.9396 14.6255 21.792L17.4321 15.8047C17.8058 15.0077 17.9994 14.138 17.9995 13.2578V12H5.99951ZM15.4282 13.7139C15.6556 13.7139 15.8739 13.8051 16.0347 13.9658C16.1952 14.1265 16.2856 14.3441 16.2856 14.5713C16.2856 14.7986 16.1954 15.017 16.0347 15.1777C15.8739 15.3384 15.6555 15.4287 15.4282 15.4287H8.57178C8.3445 15.4287 8.12607 15.3384 7.96533 15.1777C7.80459 15.017 7.71436 14.7986 7.71436 14.5713C7.71439 14.3441 7.80477 14.1265 7.96533 13.9658C8.12608 13.8051 8.34445 13.7139 8.57178 13.7139H15.4282ZM6.85693 1.71387C6.62966 1.7139 6.41218 1.80511 6.25146 1.96582C6.09075 2.12653 5.99955 2.34401 5.99951 2.57129V10.2861H17.9995V2.57129C17.9995 2.34404 17.9092 2.12653 17.7485 1.96582C17.5879 1.80514 17.3703 1.71394 17.1431 1.71387H6.85693Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.28564 3.42857C4.28564 2.51926 4.64687 1.64719 5.28985 1.00421C5.93283 0.361224 6.8049 0 7.71422 0H16.2856C17.195 0 18.067 0.361224 18.71 1.00421C19.353 1.64719 19.7142 2.51926 19.7142 3.42857V13.878C19.7144 15.2314 19.3585 16.5611 18.6822 17.7334L15.8082 22.7134C15.5826 23.1045 15.2581 23.4293 14.8672 23.6551C14.4763 23.881 14.0328 23.9999 13.5814 24H10.4185C9.96713 24 9.52372 23.8812 9.13282 23.6555C8.74193 23.4298 8.41733 23.1052 8.19164 22.7143L5.31765 17.7334C4.64125 16.5608 4.28533 15.2309 4.28564 13.8771V3.42857ZM7.71422 1.71429C7.25956 1.71429 6.82352 1.8949 6.50203 2.21639C6.18054 2.53788 5.99993 2.97391 5.99993 3.42857V13.878C6.00002 14.9306 6.277 15.9646 6.80307 16.8763L9.67622 21.8563C9.75134 21.9867 9.85949 22.0951 9.98979 22.1705C10.1201 22.2459 10.268 22.2856 10.4185 22.2857H13.5814C13.7318 22.2857 13.8796 22.2461 14.0099 22.1709C14.1402 22.0956 14.2484 21.9874 14.3236 21.8571L17.1968 16.8771C17.7232 15.965 18.0001 14.9303 17.9999 13.8771V3.42857C17.9999 2.97391 17.8193 2.53788 17.4978 2.21639C17.1763 1.8949 16.7403 1.71429 16.2856 1.71429H7.71422Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.8571 0C17.5391 0 18.1932 0.270918 18.6754 0.753154C19.1577 1.23539 19.4286 1.88944 19.4286 2.57143V14.208C19.4287 15.3244 19.1866 16.4275 18.7189 17.4411L16.3806 22.506C16.1748 22.9519 15.8457 23.3296 15.432 23.5943C15.0184 23.8591 14.5377 23.9998 14.0466 24H9.382C8.89091 23.9998 8.41014 23.8591 7.99653 23.5943C7.58291 23.3296 7.25377 22.9519 7.048 22.506L4.70971 17.4403C4.24235 16.4274 4.00021 15.3252 4 14.2097V2.57143C4 1.88944 4.27092 1.23539 4.75315 0.753154C5.23539 0.270918 5.88944 0 6.57143 0H16.8571ZM8.60457 21.7877C8.67312 21.9363 8.78275 22.0621 8.92051 22.1503C9.05826 22.2386 9.2184 22.2856 9.382 22.2857H14.0466C14.2102 22.2856 14.3703 22.2386 14.5081 22.1503C14.6458 22.0621 14.7555 21.9363 14.824 21.7877L16.1766 18.8571H7.252L8.60457 21.7877ZM6.57143 1.71429C6.3441 1.71429 6.12608 1.80459 5.96534 1.96534C5.80459 2.12608 5.71429 2.3441 5.71429 2.57143V14.208C5.71429 15.0763 5.90286 15.9343 6.26714 16.7229L6.46086 17.1429H16.8571C16.8931 17.1429 16.9289 17.1451 16.9643 17.1497L17.1614 16.7229C17.5257 15.9343 17.7143 15.0771 17.7143 14.208V2.57143C17.7143 2.3441 17.624 2.12608 17.4632 1.96534C17.3025 1.80459 17.0845 1.71429 16.8571 1.71429H6.57143Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -74,6 +74,7 @@
|
||||
"approve_tokens": "Approve tokens",
|
||||
"apps": "Apps",
|
||||
"arbiscan_history": "ArbiScan History",
|
||||
"are_you_sure_exit": "Are you sure you want to exit?",
|
||||
"arrive_in_this_address": "${currency} ${tag}will arrive in this address",
|
||||
"ascending": "Ascending",
|
||||
"ask_each_time": "Ask each time",
|
||||
@@ -330,6 +331,7 @@
|
||||
"deuro_savings_welcome_description": "dEURO is a fully decentralized, collateralized stablecoin pegged to the Euro — secure, versatile, and censorship-resistant without having to rely on oracles. 1 dEURO = 1 EURO.",
|
||||
"deuro_tooltip_no_eth": "In order to enable dEURO, you must have ETH in your wallet.",
|
||||
"deuro_tx_commited_content": "It might take a couple of seconds for the transaction to confirm and be reflected on screen",
|
||||
"device_confirmation": "Device Confirmation",
|
||||
"device_is_signing": "Device is signing",
|
||||
"dfx_option_description": "Buy crypto with EUR & CHF. For retail and corporate customers in Europe",
|
||||
"did_you_back_up_seeds": "Did you back up all your seeds?",
|
||||
@@ -529,6 +531,7 @@
|
||||
"how_to_connect": "How to Connect",
|
||||
"how_to_use": "How to use",
|
||||
"how_to_use_card": "How to use this card",
|
||||
"hww_exit_desc": "This will ed the ongoing pairing process with your hardware device",
|
||||
"i_understand": "I understand",
|
||||
"icon_style": "Icon style",
|
||||
"id": "ID: ",
|
||||
@@ -722,6 +725,10 @@
|
||||
"overshot": "Overshot",
|
||||
"overwrite_amount": "Overwrite amount",
|
||||
"pairingInvalidEvent": "Pairing Invalid Event",
|
||||
"pairing_code": "Pairing Code",
|
||||
"pairing_code_desc_1": "Your device will show a pairing code.",
|
||||
"pairing_code_desc_2": "Input here to continue",
|
||||
"pairing_error": "Pairing Error",
|
||||
"passphrase": "Passphrase (Optional)",
|
||||
"passphrase_view_keys": "Passphrase",
|
||||
"passphrases_doesnt_match": "Passphrases do not match, please try again",
|
||||
@@ -1138,6 +1145,7 @@
|
||||
"testnet_coins_no_value": "Testnet coins have no value",
|
||||
"third_intro_content": "Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!",
|
||||
"third_intro_title": "Yat plays nicely with others",
|
||||
"this_can_take_few_seconds": "This can take a few seconds",
|
||||
"this_pair_is_not_supported_warning": "This pair is not supported with the currently selected exchange(s). Please select another exchange.",
|
||||
"thorchain_contract_address_not_supported": "THORChain does not support sending to a contract address",
|
||||
"thorchain_taproot_address_not_supported": "The THORChain provider does not support Taproot addresses. Please change the address or select a different provider.",
|
||||
@@ -1287,6 +1295,7 @@
|
||||
"value_type": "Value Type",
|
||||
"variable_pair_not_supported": "This variable pair is not supported with the selected exchanges",
|
||||
"verification": "Verification",
|
||||
"verifying_code": "Verifying Code",
|
||||
"verify_message": "Verify Message",
|
||||
"verify_seed": "Verify Seed",
|
||||
"verify_with_2fa": "Verify with Cake 2FA",
|
||||
@@ -1394,6 +1403,7 @@
|
||||
"yat_popup_title": "Your wallet address can be emojified.",
|
||||
"yes": "Yes",
|
||||
"yesterday": "Yesterday",
|
||||
"yes_exit": "Yes. Exit",
|
||||
"you_now_have_debit_card": "You now have a debit card",
|
||||
"you_pay": "You Pay",
|
||||
"you_will_get": "Convert to",
|
||||
@@ -1408,4 +1418,4 @@
|
||||
"zcash_card_missing_funds": "Missing funds?",
|
||||
"zcash_card_scan": "Scan",
|
||||
"zcash_card_warning": "Do not close the app until the procedure completes, if you do so this process will need to restart from scratch."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ else
|
||||
fi
|
||||
|
||||
# NOTE: Make sure to update monero_c prebuilds link in workflow files
|
||||
# https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC1/release-bundle.zip
|
||||
# https://github.com/MrCyjaneK/monero_c/releases/download/v0.18.4.6-RC2/release-bundle.zip
|
||||
git fetch -a
|
||||
git checkout bc8d1a0b75b97156d71579581b4cdfe58c777ed2
|
||||
git checkout 3bfb3856a838f2bf6b729501837bb0295dedf25d
|
||||
git reset --hard
|
||||
git submodule update --init --force --recursive
|
||||
|
||||
|
||||
@@ -334,8 +334,10 @@ import 'package:cake_wallet/view_model/send/output.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
|
||||
import 'package:trezor_flutter/trezor_flutter.dart' as trezor;
|
||||
import 'package:polyseed/polyseed.dart';""";
|
||||
const moneroCWHeaders = """
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/account.dart' as monero_account;
|
||||
import 'package:cw_core/get_height_by_date.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
@@ -345,6 +347,7 @@ import 'package:cw_monero/api/wallet.dart' as monero_wallet_api;
|
||||
import 'package:cw_monero/ledger.dart';
|
||||
import 'package:cw_monero/monero_unspent.dart';
|
||||
import 'package:cw_monero/api/account_list.dart';
|
||||
import 'package:cw_monero/trezor.dart';
|
||||
import 'package:cw_monero/monero_wallet_service.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:cw_monero/monero_transaction_info.dart';
|
||||
@@ -463,7 +466,7 @@ abstract class Monero {
|
||||
HardwareWalletType? hardwareWalletType,
|
||||
required int height});
|
||||
WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required String mnemonic});
|
||||
WalletCredentials createMoneroRestoreWalletFromHardwareCredentials({required String name, required String password, required int height, required ledger.LedgerConnection ledgerConnection});
|
||||
WalletCredentials createMoneroRestoreWalletFromHardwareCredentials({required String name, required String password, required int height, required HardwareWalletService hardwareWalletService});
|
||||
WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required int seedType, required String? passphrase, String? password, String? mnemonic});
|
||||
Map<String, String> getKeys(Object wallet);
|
||||
int? getRestoreHeight(Object wallet);
|
||||
@@ -484,6 +487,10 @@ WalletCredentials createMoneroNewWalletCredentials({required String name, requir
|
||||
void resetLedgerConnection();
|
||||
void setGlobalLedgerConnection(ledger.LedgerConnection connection);
|
||||
String? getLastLedgerCommand();
|
||||
void setHardwareWalletService(Object wallet, HardwareWalletService service);
|
||||
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
|
||||
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorClient client);
|
||||
Future<void> syncTrezor(Object wallet);
|
||||
Map<String, List<int>> debugCallLength();
|
||||
Map<String, dynamic> getWalletCacheDebug();
|
||||
}
|
||||
|
||||