mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
Cw 604 integrate bitcoin ledger (#1407)
* CW-503 Prepare Ledger integration * CW-503 Revert Tor ignore * CW-503 Add Connect Device Page * CW-503 Add createWalletFromDevice for monero * CW-503 Add Connect Device Page * CW-503 Add Connect Device Page * CW-503 Add Debug Options * CW-503 Add proper hardware wallet selection screen * CW-503 Minor design changes on connect_device_page * CW-503 Add Create Wallet from Ledger * CW-503 Spent Ledger ETH Funds * CW-503 Minor fixes * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix minor conflicts * CW-503 Improve Ledger BLE Communication * CW-503 Improve Ledger BLE Communication * CW-503 Rollback Monero Test code * CW-503 Fix Execution failed for task :app:checkReleaseDuplicateClasses * CW-503 Better Error-Exceptions * CW-503 Add SetPinScreen before restore from hardware-wallet * CW-503 override web3dart to use cake's git hosted version * CW-503 Implement ledger sign messages * CW-503 Implement ledger sign messages and send erc20 tokens * CW-503 Fix merge conflicts * CW-503 Fix merge conflicts * CW-503 Use dep override for ledger_flutter * CW-503 Ledger ERC20 finalisation * CW-503 More graceful error handling * CW-503 Even more graceful error handling & remove debug code * CW-503 Minor Changes for Vik * CW-503 Fix USB connection * CW-503 Maybe this overrides web3dart finally * Indicate Loading on the first 5 Wallet Accounts * Prepare Ledger Bitcoin * Fix conflicts with main * Add Bluetooth permission to iOS [skip ci] * add privacyinfo for ios [skip ci] * update PrivacyInfo.xcprivacy [scip ci] * ios shit [skip ci] * Improve bitcoin xpub * Resolve open Todos regarding iOS Support * Minor debug * Remove erc20 dependency to have more granular control over the tx UX * Create Bitcoin Wallets using xpub * Create Bitcoin Wallets using xpub * Better error handling * Improve Ledger account handling * Add Bitcoin Support for Ledger * Add Bitcoin Support for Ledger * Add Bitcoin Support for Ledger * Implement requested Changes * Implement requested Changes * Minor fix * Implement requested Changes * Implement requested Changes * Add comment to remind me * Enable RBF for Bitcoin Ledger * Fix merge conflicts * Update wallet_info.dart * Cw 503 ledger support (#1310) * CW-503 Prepare Ledger integration * CW-503 Revert Tor ignore * CW-503 Add Connect Device Page * CW-503 Add createWalletFromDevice for monero * CW-503 Add Connect Device Page * CW-503 Add Connect Device Page * CW-503 Add Debug Options * CW-503 Add proper hardware wallet selection screen * CW-503 Minor design changes on connect_device_page * CW-503 Add Create Wallet from Ledger * CW-503 Spent Ledger ETH Funds * CW-503 Minor fixes * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix Merge conflicts * CW-503 Fix minor conflicts * CW-503 Improve Ledger BLE Communication * CW-503 Improve Ledger BLE Communication * CW-503 Rollback Monero Test code * CW-503 Fix Execution failed for task :app:checkReleaseDuplicateClasses * CW-503 Better Error-Exceptions * CW-503 Add SetPinScreen before restore from hardware-wallet * CW-503 override web3dart to use cake's git hosted version * CW-503 Implement ledger sign messages * CW-503 Implement ledger sign messages and send erc20 tokens * CW-503 Fix merge conflicts * CW-503 Fix merge conflicts * CW-503 Use dep override for ledger_flutter * CW-503 Ledger ERC20 finalisation * CW-503 More graceful error handling * CW-503 Even more graceful error handling & remove debug code * CW-503 Minor Changes for Vik * CW-503 Fix USB connection * CW-503 Maybe this overrides web3dart finally * Indicate Loading on the first 5 Wallet Accounts * Fix conflicts with main * Add Bluetooth permission to iOS [skip ci] * add privacyinfo for ios [skip ci] * update PrivacyInfo.xcprivacy [scip ci] * ios shit [skip ci] * Resolve open Todos regarding iOS Support * Remove erc20 dependency to have more granular control over the tx UX * Better error handling * Improve Ledger account handling * Implement requested Changes * Implement requested Changes * Implement requested Changes * Implement requested Changes * Fix merge conflicts * Update wallet_info.dart --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Fix merge conflicts * Fix merge conflicts * Minor Fix to derivations * Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci] * Update cw_bitcoin/lib/bitcoin_wallet.dart [skip ci] * Update cw_bitcoin/lib/electrum_wallet.dart [skip ci] * Fix backward compatibility issues * Merge Tron * Fix Tron with HW changes * fix ble on iOS fix tron address validation --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
043d7d7c8b
commit
5eabdcdca1
118 changed files with 3363 additions and 579 deletions
|
@ -9,6 +9,26 @@
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
|
||||||
|
<!--bibo01 : hardware option-->
|
||||||
|
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||||
|
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
|
||||||
|
|
||||||
|
<!-- required for API 18 - 30 -->
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.BLUETOOTH"
|
||||||
|
android:maxSdkVersion="30" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||||
|
android:maxSdkVersion="30" />
|
||||||
|
|
||||||
|
<!-- API 31+ -->
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.BLUETOOTH_SCAN"
|
||||||
|
android:usesPermissionFlags="neverForLocation" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Application"
|
android:name=".Application"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.7.10'
|
ext.kotlin_version = '1.8.21'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|
BIN
assets/images/bluetooth.png
Normal file
BIN
assets/images/bluetooth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/images/ledger_nano.png
Normal file
BIN
assets/images/ledger_nano.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/usb.png
Normal file
BIN
assets/images/usb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
43
cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart
Normal file
43
cw_bitcoin/lib/bitcoin_hardware_wallet_service.dart
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart';
|
||||||
|
import 'package:cw_bitcoin/utils.dart';
|
||||||
|
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||||
|
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
|
||||||
|
class BitcoinHardwareWalletService {
|
||||||
|
BitcoinHardwareWalletService(this.ledger, this.device);
|
||||||
|
|
||||||
|
final Ledger ledger;
|
||||||
|
final LedgerDevice device;
|
||||||
|
|
||||||
|
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
|
||||||
|
final bitcoinLedgerApp = BitcoinLedgerApp(ledger);
|
||||||
|
|
||||||
|
final masterFp = await bitcoinLedgerApp.getMasterFingerprint(device);
|
||||||
|
print(masterFp);
|
||||||
|
|
||||||
|
final accounts = <HardwareAccountData>[];
|
||||||
|
final indexRange = List.generate(limit, (i) => i + index);
|
||||||
|
|
||||||
|
for (final i in indexRange) {
|
||||||
|
final derivationPath = "m/84'/0'/$i'";
|
||||||
|
final xpub = await bitcoinLedgerApp.getXPubKey(device, derivationPath: derivationPath);
|
||||||
|
HDWallet hd = HDWallet.fromBase58(xpub).derive(0);
|
||||||
|
|
||||||
|
final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
|
||||||
|
|
||||||
|
accounts.add(HardwareAccountData(
|
||||||
|
address: address,
|
||||||
|
accountIndex: i,
|
||||||
|
derivationPath: derivationPath,
|
||||||
|
masterFingerprint: masterFp,
|
||||||
|
xpub: xpub,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,13 @@
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
|
import 'package:cw_bitcoin/psbt_transaction_builder.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:ledger_bitcoin/ledger_bitcoin.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
|
@ -20,11 +25,12 @@ class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
||||||
|
|
||||||
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
BitcoinWalletBase({
|
BitcoinWalletBase({
|
||||||
required String mnemonic,
|
|
||||||
required String password,
|
required String password,
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required Uint8List seedBytes,
|
Uint8List? seedBytes,
|
||||||
|
String? mnemonic,
|
||||||
|
String? xpub,
|
||||||
String? addressPageType,
|
String? addressPageType,
|
||||||
BasedUtxoNetwork? networkParam,
|
BasedUtxoNetwork? networkParam,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
|
@ -35,6 +41,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
}) : super(
|
}) : super(
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
|
xpub: xpub,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
|
@ -49,8 +56,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
currency: CryptoCurrency.btc) {
|
currency: CryptoCurrency.btc) {
|
||||||
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
||||||
// the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here)
|
// the sideHd derivation path = m/84'/0'/0'/1 (account 1, index unspecified here)
|
||||||
String derivationPath = walletInfo.derivationInfo!.derivationPath!;
|
// String derivationPath = walletInfo.derivationInfo!.derivationPath!;
|
||||||
String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
|
// String sideDerivationPath = derivationPath.substring(0, derivationPath.length - 1) + "1";
|
||||||
|
// final hd = bitcoin.HDWallet.fromSeed(seedBytes, network: networkType);
|
||||||
walletAddresses = BitcoinWalletAddresses(
|
walletAddresses = BitcoinWalletAddresses(
|
||||||
walletInfo,
|
walletInfo,
|
||||||
electrumClient: electrumClient,
|
electrumClient: electrumClient,
|
||||||
|
@ -58,7 +66,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath(sideDerivationPath),
|
sideHd: accountHD.derive(1),
|
||||||
network: networkParam ?? network,
|
network: networkParam ?? network,
|
||||||
);
|
);
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
|
@ -129,23 +137,26 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? "m/0'/0";
|
walletInfo.derivationInfo!.derivationPath = snp.derivationPath ?? "m/0'/0";
|
||||||
walletInfo.derivationInfo!.derivationType = snp.derivationType ?? DerivationType.electrum;
|
walletInfo.derivationInfo!.derivationType = snp.derivationType ?? DerivationType.electrum;
|
||||||
|
|
||||||
late Uint8List seedBytes;
|
Uint8List? seedBytes = null;
|
||||||
|
|
||||||
|
if (snp.mnemonic != null) {
|
||||||
switch (walletInfo.derivationInfo!.derivationType) {
|
switch (walletInfo.derivationInfo!.derivationType) {
|
||||||
case DerivationType.electrum:
|
case DerivationType.electrum:
|
||||||
seedBytes = await mnemonicToSeedBytes(snp.mnemonic);
|
seedBytes = await mnemonicToSeedBytes(snp.mnemonic!);
|
||||||
break;
|
break;
|
||||||
case DerivationType.bip39:
|
case DerivationType.bip39:
|
||||||
default:
|
default:
|
||||||
seedBytes = await bip39.mnemonicToSeed(
|
seedBytes = await bip39.mnemonicToSeed(
|
||||||
snp.mnemonic,
|
snp.mnemonic!,
|
||||||
passphrase: snp.passphrase ?? '',
|
passphrase: snp.passphrase ?? '',
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return BitcoinWallet(
|
return BitcoinWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic,
|
||||||
|
xpub: snp.xpub,
|
||||||
password: password,
|
password: password,
|
||||||
passphrase: snp.passphrase,
|
passphrase: snp.passphrase,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -159,4 +170,49 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
networkParam: network,
|
networkParam: network,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ledger? _ledger;
|
||||||
|
LedgerDevice? _ledgerDevice;
|
||||||
|
BitcoinLedgerApp? _bitcoinLedgerApp;
|
||||||
|
|
||||||
|
void setLedger(Ledger setLedger, LedgerDevice setLedgerDevice) {
|
||||||
|
_ledger = setLedger;
|
||||||
|
_ledgerDevice = setLedgerDevice;
|
||||||
|
_bitcoinLedgerApp = BitcoinLedgerApp(_ledger!, derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<BtcTransaction> buildHardwareWalletTransaction({
|
||||||
|
required List<BitcoinBaseOutput> outputs,
|
||||||
|
required BigInt fee,
|
||||||
|
required BasedUtxoNetwork network,
|
||||||
|
required List<UtxoWithAddress> utxos,
|
||||||
|
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||||
|
String? memo,
|
||||||
|
bool enableRBF = false,
|
||||||
|
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
|
||||||
|
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
|
||||||
|
}) async {
|
||||||
|
final masterFingerprint = await _bitcoinLedgerApp!.getMasterFingerprint(_ledgerDevice!);
|
||||||
|
|
||||||
|
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
||||||
|
for (final utxo in utxos) {
|
||||||
|
final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
|
||||||
|
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||||
|
|
||||||
|
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
||||||
|
utxo: utxo.utxo,
|
||||||
|
rawTx: rawTx,
|
||||||
|
ownerDetails: utxo.ownerDetails,
|
||||||
|
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
|
||||||
|
ownerMasterFingerprint: masterFingerprint,
|
||||||
|
ownerPublicKey: publicKeyAndDerivationPath.publicKey,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
final psbt = PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF);
|
||||||
|
|
||||||
|
final rawHex = await _bitcoinLedgerApp!.signPsbt(_ledgerDevice!, psbt: psbt.psbt);
|
||||||
|
return BtcTransaction.fromRaw(hex.encode(rawHex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||||
import 'package:cw_core/wallet_credentials.dart';
|
import 'package:cw_core/wallet_credentials.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
|
||||||
|
@ -36,9 +37,22 @@ class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||||
BitcoinRestoreWalletFromWIFCredentials(
|
BitcoinRestoreWalletFromWIFCredentials({
|
||||||
{required String name, required String password, required this.wif, WalletInfo? walletInfo})
|
required String name,
|
||||||
: super(name: name, password: password, walletInfo: walletInfo);
|
required String password,
|
||||||
|
required this.wif,
|
||||||
|
WalletInfo? walletInfo,
|
||||||
|
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||||
|
|
||||||
final String wif;
|
final String wif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BitcoinRestoreWalletFromHardware extends WalletCredentials {
|
||||||
|
BitcoinRestoreWalletFromHardware({
|
||||||
|
required String name,
|
||||||
|
required this.hwAccountData,
|
||||||
|
WalletInfo? walletInfo,
|
||||||
|
}) : super(name: name, walletInfo: walletInfo);
|
||||||
|
|
||||||
|
final HardwareAccountData hwAccountData;
|
||||||
|
}
|
||||||
|
|
|
@ -14,8 +14,11 @@ import 'package:hive/hive.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
|
||||||
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
class BitcoinWalletService extends WalletService<
|
||||||
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
BitcoinNewWalletCredentials,
|
||||||
|
BitcoinRestoreWalletFromSeedCredentials,
|
||||||
|
BitcoinRestoreWalletFromWIFCredentials,
|
||||||
|
BitcoinRestoreWalletFromHardware> {
|
||||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -99,6 +102,25 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<BitcoinWallet> restoreFromHardwareWallet(BitcoinRestoreWalletFromHardware credentials,
|
||||||
|
{bool? isTestnet}) async {
|
||||||
|
|
||||||
|
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||||
|
credentials.walletInfo?.network = network.value;
|
||||||
|
credentials.walletInfo?.derivationInfo?.derivationPath = credentials.hwAccountData.derivationPath;
|
||||||
|
|
||||||
|
final wallet = await BitcoinWallet(password: credentials.password!,
|
||||||
|
xpub: credentials.hwAccountData.xpub,
|
||||||
|
walletInfo: credentials.walletInfo!,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||||
|
networkParam: network,
|
||||||
|
);
|
||||||
|
await wallet.save();
|
||||||
|
await wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
|
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials,
|
||||||
{bool? isTestnet}) async =>
|
{bool? isTestnet}) async =>
|
||||||
|
|
|
@ -4,7 +4,7 @@ Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
|
||||||
DerivationType.electrum: [
|
DerivationType.electrum: [
|
||||||
DerivationInfo(
|
DerivationInfo(
|
||||||
derivationType: DerivationType.electrum,
|
derivationType: DerivationType.electrum,
|
||||||
derivationPath: "m/0'/0",
|
derivationPath: "m/0'",
|
||||||
description: "Electrum",
|
description: "Electrum",
|
||||||
scriptType: "p2wpkh",
|
scriptType: "p2wpkh",
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,8 +4,8 @@ import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin_base;
|
import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin_base;
|
||||||
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:cw_bitcoin/address_from_output.dart';
|
import 'package:cw_bitcoin/address_from_output.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
|
@ -37,9 +37,9 @@ import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:rxdart/subjects.dart';
|
import 'package:rxdart/subjects.dart';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
part 'electrum_wallet.g.dart';
|
part 'electrum_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -53,17 +53,16 @@ abstract class ElectrumWalletBase
|
||||||
required WalletInfo walletInfo,
|
required WalletInfo walletInfo,
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
required this.networkType,
|
required this.networkType,
|
||||||
required this.mnemonic,
|
String? xpub,
|
||||||
required Uint8List seedBytes,
|
String? mnemonic,
|
||||||
|
Uint8List? seedBytes,
|
||||||
this.passphrase,
|
this.passphrase,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
ElectrumClient? electrumClient,
|
ElectrumClient? electrumClient,
|
||||||
ElectrumBalance? initialBalance,
|
ElectrumBalance? initialBalance,
|
||||||
CryptoCurrency? currency})
|
CryptoCurrency? currency})
|
||||||
: hd = currency == CryptoCurrency.bch
|
: accountHD =
|
||||||
? bitcoinCashHDWallet(seedBytes)
|
getAccountHDWallet(currency, networkType, seedBytes, xpub, walletInfo.derivationInfo),
|
||||||
: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
|
||||||
.derivePath(walletInfo.derivationInfo?.derivationPath ?? "m/0'/0"),
|
|
||||||
syncStatus = NotConnectedSyncStatus(),
|
syncStatus = NotConnectedSyncStatus(),
|
||||||
_password = password,
|
_password = password,
|
||||||
_feeRates = <int>[],
|
_feeRates = <int>[],
|
||||||
|
@ -80,20 +79,44 @@ abstract class ElectrumWalletBase
|
||||||
this.unspentCoinsInfo = unspentCoinsInfo,
|
this.unspentCoinsInfo = unspentCoinsInfo,
|
||||||
this.network = _getNetwork(networkType, currency),
|
this.network = _getNetwork(networkType, currency),
|
||||||
this.isTestnet = networkType == bitcoin.testnet,
|
this.isTestnet = networkType == bitcoin.testnet,
|
||||||
|
this._mnemonic = mnemonic,
|
||||||
super(walletInfo) {
|
super(walletInfo) {
|
||||||
this.electrumClient = electrumClient ?? ElectrumClient();
|
this.electrumClient = electrumClient ?? ElectrumClient();
|
||||||
this.walletInfo = walletInfo;
|
this.walletInfo = walletInfo;
|
||||||
transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password);
|
transactionHistory = ElectrumTransactionHistory(walletInfo: walletInfo, password: password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bitcoin.HDWallet getAccountHDWallet(
|
||||||
|
CryptoCurrency? currency,
|
||||||
|
bitcoin.NetworkType networkType,
|
||||||
|
Uint8List? seedBytes,
|
||||||
|
String? xpub,
|
||||||
|
DerivationInfo? derivationInfo) {
|
||||||
|
if (seedBytes == null && xpub == null) {
|
||||||
|
throw Exception(
|
||||||
|
"To create a Wallet you need either a seed or an xpub. This should not happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seedBytes != null) {
|
||||||
|
return currency == CryptoCurrency.bch
|
||||||
|
? bitcoinCashHDWallet(seedBytes)
|
||||||
|
: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType)
|
||||||
|
.derivePath(_hardenedDerivationPath(derivationInfo?.derivationPath ?? "m/0'"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitcoin.HDWallet.fromBase58(xpub!);
|
||||||
|
}
|
||||||
|
|
||||||
static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
|
static bitcoin.HDWallet bitcoinCashHDWallet(Uint8List seedBytes) =>
|
||||||
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/0");
|
bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'");
|
||||||
|
|
||||||
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
static int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
||||||
inputsCount * 68 + outputsCounts * 34 + 10;
|
inputsCount * 68 + outputsCounts * 34 + 10;
|
||||||
|
|
||||||
final bitcoin.HDWallet hd;
|
final bitcoin.HDWallet accountHD;
|
||||||
final String mnemonic;
|
final String? _mnemonic;
|
||||||
|
|
||||||
|
bitcoin.HDWallet get hd => accountHD.derive(0);
|
||||||
final String? passphrase;
|
final String? passphrase;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -123,10 +146,10 @@ abstract class ElectrumWalletBase
|
||||||
.map((addr) => scriptHash(addr.address, network: network))
|
.map((addr) => scriptHash(addr.address, network: network))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
String get xpub => hd.base58!;
|
String get xpub => accountHD.base58!;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get seed => mnemonic;
|
String? get seed => _mnemonic;
|
||||||
|
|
||||||
bitcoin.NetworkType networkType;
|
bitcoin.NetworkType networkType;
|
||||||
BasedUtxoNetwork network;
|
BasedUtxoNetwork network;
|
||||||
|
@ -203,7 +226,9 @@ abstract class ElectrumWalletBase
|
||||||
int credentialsAmount = 0,
|
int credentialsAmount = 0,
|
||||||
}) async {
|
}) async {
|
||||||
final utxos = <UtxoWithAddress>[];
|
final utxos = <UtxoWithAddress>[];
|
||||||
List<ECPrivate> privateKeys = [];
|
final privateKeys = <ECPrivate>[];
|
||||||
|
final publicKeys = <String, PublicKeyWithDerivationPath>{};
|
||||||
|
|
||||||
int allInputsAmount = 0;
|
int allInputsAmount = 0;
|
||||||
|
|
||||||
bool spendsUnconfirmedTX = false;
|
bool spendsUnconfirmedTX = false;
|
||||||
|
@ -217,12 +242,22 @@ abstract class ElectrumWalletBase
|
||||||
allInputsAmount += utx.value;
|
allInputsAmount += utx.value;
|
||||||
|
|
||||||
final address = addressTypeFromStr(utx.address, network);
|
final address = addressTypeFromStr(utx.address, network);
|
||||||
final privkey = generateECPrivate(
|
final hd =
|
||||||
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
|
||||||
index: utx.bitcoinAddressRecord.index,
|
final derivationPath =
|
||||||
network: network);
|
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
|
||||||
|
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||||
|
"/${utx.bitcoinAddressRecord.index}";
|
||||||
|
final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
|
||||||
|
|
||||||
|
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||||
|
|
||||||
|
if (!walletInfo.isHardwareWallet) {
|
||||||
|
final privkey =
|
||||||
|
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
||||||
|
|
||||||
privateKeys.add(privkey);
|
privateKeys.add(privkey);
|
||||||
|
}
|
||||||
|
|
||||||
utxos.add(
|
utxos.add(
|
||||||
UtxoWithAddress(
|
UtxoWithAddress(
|
||||||
|
@ -233,7 +268,7 @@ abstract class ElectrumWalletBase
|
||||||
scriptType: _getScriptType(address),
|
scriptType: _getScriptType(address),
|
||||||
),
|
),
|
||||||
ownerDetails: UtxoAddressDetails(
|
ownerDetails: UtxoAddressDetails(
|
||||||
publicKey: privkey.getPublic().toHex(),
|
publicKey: pubKeyHex,
|
||||||
address: address,
|
address: address,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -294,6 +329,7 @@ abstract class ElectrumWalletBase
|
||||||
return EstimatedTxResult(
|
return EstimatedTxResult(
|
||||||
utxos: utxos,
|
utxos: utxos,
|
||||||
privateKeys: privateKeys,
|
privateKeys: privateKeys,
|
||||||
|
publicKeys: publicKeys,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
isSendAll: true,
|
isSendAll: true,
|
||||||
|
@ -312,7 +348,9 @@ abstract class ElectrumWalletBase
|
||||||
bool? useUnconfirmed,
|
bool? useUnconfirmed,
|
||||||
}) async {
|
}) async {
|
||||||
final utxos = <UtxoWithAddress>[];
|
final utxos = <UtxoWithAddress>[];
|
||||||
List<ECPrivate> privateKeys = [];
|
final privateKeys = <ECPrivate>[];
|
||||||
|
final publicKeys = <String, PublicKeyWithDerivationPath>{};
|
||||||
|
|
||||||
int allInputsAmount = 0;
|
int allInputsAmount = 0;
|
||||||
bool spendsUnconfirmedTX = false;
|
bool spendsUnconfirmedTX = false;
|
||||||
|
|
||||||
|
@ -332,12 +370,23 @@ abstract class ElectrumWalletBase
|
||||||
leftAmount = leftAmount - utx.value;
|
leftAmount = leftAmount - utx.value;
|
||||||
|
|
||||||
final address = addressTypeFromStr(utx.address, network);
|
final address = addressTypeFromStr(utx.address, network);
|
||||||
final privkey = generateECPrivate(
|
|
||||||
hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
|
final hd =
|
||||||
index: utx.bitcoinAddressRecord.index,
|
utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
|
||||||
network: network);
|
final derivationPath =
|
||||||
|
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
|
||||||
|
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||||
|
"/${utx.bitcoinAddressRecord.index}";
|
||||||
|
final pubKeyHex = hd.derive(utx.bitcoinAddressRecord.index).pubKey!;
|
||||||
|
|
||||||
|
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||||
|
|
||||||
|
if (!walletInfo.isHardwareWallet) {
|
||||||
|
final privkey =
|
||||||
|
generateECPrivate(hd: hd, index: utx.bitcoinAddressRecord.index, network: network);
|
||||||
|
|
||||||
privateKeys.add(privkey);
|
privateKeys.add(privkey);
|
||||||
|
}
|
||||||
|
|
||||||
utxos.add(
|
utxos.add(
|
||||||
UtxoWithAddress(
|
UtxoWithAddress(
|
||||||
|
@ -348,7 +397,7 @@ abstract class ElectrumWalletBase
|
||||||
scriptType: _getScriptType(address),
|
scriptType: _getScriptType(address),
|
||||||
),
|
),
|
||||||
ownerDetails: UtxoAddressDetails(
|
ownerDetails: UtxoAddressDetails(
|
||||||
publicKey: privkey.getPublic().toHex(),
|
publicKey: pubKeyHex,
|
||||||
address: address,
|
address: address,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -490,6 +539,7 @@ abstract class ElectrumWalletBase
|
||||||
return EstimatedTxResult(
|
return EstimatedTxResult(
|
||||||
utxos: utxos,
|
utxos: utxos,
|
||||||
privateKeys: privateKeys,
|
privateKeys: privateKeys,
|
||||||
|
publicKeys: publicKeys,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
hasChange: true,
|
hasChange: true,
|
||||||
|
@ -557,6 +607,35 @@ abstract class ElectrumWalletBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (walletInfo.isHardwareWallet) {
|
||||||
|
final transaction = await buildHardwareWalletTransaction(
|
||||||
|
utxos: estimatedTx.utxos,
|
||||||
|
outputs: outputs,
|
||||||
|
publicKeys: estimatedTx.publicKeys,
|
||||||
|
fee: BigInt.from(estimatedTx.fee),
|
||||||
|
network: network,
|
||||||
|
memo: estimatedTx.memo,
|
||||||
|
outputOrdering: BitcoinOrdering.none,
|
||||||
|
enableRBF: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return PendingBitcoinTransaction(
|
||||||
|
transaction,
|
||||||
|
type,
|
||||||
|
electrumClient: electrumClient,
|
||||||
|
amount: estimatedTx.amount,
|
||||||
|
fee: estimatedTx.fee,
|
||||||
|
feeRate: feeRateInt.toString(),
|
||||||
|
network: network,
|
||||||
|
hasChange: estimatedTx.hasChange,
|
||||||
|
isSendAll: estimatedTx.isSendAll,
|
||||||
|
hasTaprootInputs: false, // ToDo: (Konsti) Support Taproot
|
||||||
|
)..addListener((transaction) async {
|
||||||
|
transactionHistory.addOne(transaction);
|
||||||
|
await updateBalance();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
BasedBitcoinTransacationBuilder txb;
|
BasedBitcoinTransacationBuilder txb;
|
||||||
if (network is BitcoinCashNetwork) {
|
if (network is BitcoinCashNetwork) {
|
||||||
txb = ForkedTransactionBuilder(
|
txb = ForkedTransactionBuilder(
|
||||||
|
@ -618,8 +697,22 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<BtcTransaction> buildHardwareWalletTransaction({
|
||||||
|
required List<BitcoinBaseOutput> outputs,
|
||||||
|
required BigInt fee,
|
||||||
|
required BasedUtxoNetwork network,
|
||||||
|
required List<UtxoWithAddress> utxos,
|
||||||
|
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||||
|
String? memo,
|
||||||
|
bool enableRBF = false,
|
||||||
|
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
|
||||||
|
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
|
||||||
|
}) async =>
|
||||||
|
throw UnimplementedError();
|
||||||
|
|
||||||
String toJSON() => json.encode({
|
String toJSON() => json.encode({
|
||||||
'mnemonic': mnemonic,
|
'mnemonic': _mnemonic,
|
||||||
|
'xpub': xpub,
|
||||||
'passphrase': passphrase ?? '',
|
'passphrase': passphrase ?? '',
|
||||||
'account_index': walletAddresses.currentReceiveAddressIndexByType,
|
'account_index': walletAddresses.currentReceiveAddressIndexByType,
|
||||||
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
||||||
|
@ -1263,7 +1356,7 @@ abstract class ElectrumWalletBase
|
||||||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
|
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address = null}) {
|
Future<String> signMessage(String message, {String? address = null}) async {
|
||||||
final index = address != null
|
final index = address != null
|
||||||
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
|
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
|
||||||
: null;
|
: null;
|
||||||
|
@ -1286,6 +1379,9 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
return BitcoinNetwork.mainnet;
|
return BitcoinNetwork.mainnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String _hardenedDerivationPath(String derivationPath) =>
|
||||||
|
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EstimateTxParams {
|
class EstimateTxParams {
|
||||||
|
@ -1307,6 +1403,7 @@ class EstimatedTxResult {
|
||||||
EstimatedTxResult({
|
EstimatedTxResult({
|
||||||
required this.utxos,
|
required this.utxos,
|
||||||
required this.privateKeys,
|
required this.privateKeys,
|
||||||
|
required this.publicKeys,
|
||||||
required this.fee,
|
required this.fee,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.hasChange,
|
required this.hasChange,
|
||||||
|
@ -1317,6 +1414,7 @@ class EstimatedTxResult {
|
||||||
|
|
||||||
final List<UtxoWithAddress> utxos;
|
final List<UtxoWithAddress> utxos;
|
||||||
final List<ECPrivate> privateKeys;
|
final List<ECPrivate> privateKeys;
|
||||||
|
final Map<String, PublicKeyWithDerivationPath> publicKeys; // PubKey to derivationPath
|
||||||
final int fee;
|
final int fee;
|
||||||
final int amount;
|
final int amount;
|
||||||
final bool hasChange;
|
final bool hasChange;
|
||||||
|
@ -1325,6 +1423,13 @@ class EstimatedTxResult {
|
||||||
final bool spendsUnconfirmedTX;
|
final bool spendsUnconfirmedTX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PublicKeyWithDerivationPath {
|
||||||
|
const PublicKeyWithDerivationPath(this.publicKey, this.derivationPath);
|
||||||
|
|
||||||
|
final String derivationPath;
|
||||||
|
final String publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
|
||||||
if (network is BitcoinCashNetwork) {
|
if (network is BitcoinCashNetwork) {
|
||||||
if (!address.startsWith("bitcoincash:") &&
|
if (!address.startsWith("bitcoincash:") &&
|
||||||
|
|
|
@ -13,6 +13,7 @@ class ElectrumWalletSnapshot {
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.password,
|
required this.password,
|
||||||
required this.mnemonic,
|
required this.mnemonic,
|
||||||
|
required this.xpub,
|
||||||
required this.addresses,
|
required this.addresses,
|
||||||
required this.balance,
|
required this.balance,
|
||||||
required this.regularAddressIndex,
|
required this.regularAddressIndex,
|
||||||
|
@ -28,7 +29,8 @@ class ElectrumWalletSnapshot {
|
||||||
final WalletType type;
|
final WalletType type;
|
||||||
final String? addressPageType;
|
final String? addressPageType;
|
||||||
|
|
||||||
String mnemonic;
|
String? mnemonic;
|
||||||
|
String? xpub;
|
||||||
List<BitcoinAddressRecord> addresses;
|
List<BitcoinAddressRecord> addresses;
|
||||||
ElectrumBalance balance;
|
ElectrumBalance balance;
|
||||||
Map<String, int> regularAddressIndex;
|
Map<String, int> regularAddressIndex;
|
||||||
|
@ -43,7 +45,8 @@ class ElectrumWalletSnapshot {
|
||||||
final jsonSource = await read(path: path, password: password);
|
final jsonSource = await read(path: path, password: password);
|
||||||
final data = json.decode(jsonSource) as Map;
|
final data = json.decode(jsonSource) as Map;
|
||||||
final addressesTmp = data['addresses'] as List? ?? <Object>[];
|
final addressesTmp = data['addresses'] as List? ?? <Object>[];
|
||||||
final mnemonic = data['mnemonic'] as String;
|
final mnemonic = data['mnemonic'] as String?;
|
||||||
|
final xpub = data['xpub'] as String?;
|
||||||
final passphrase = data['passphrase'] as String? ?? '';
|
final passphrase = data['passphrase'] as String? ?? '';
|
||||||
final addresses = addressesTmp
|
final addresses = addressesTmp
|
||||||
.whereType<String>()
|
.whereType<String>()
|
||||||
|
@ -79,6 +82,7 @@ class ElectrumWalletSnapshot {
|
||||||
password: password,
|
password: password,
|
||||||
passphrase: passphrase,
|
passphrase: passphrase,
|
||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
|
xpub: xpub,
|
||||||
addresses: addresses,
|
addresses: addresses,
|
||||||
balance: balance,
|
balance: balance,
|
||||||
regularAddressIndex: regularAddressIndexByType,
|
regularAddressIndex: regularAddressIndexByType,
|
||||||
|
|
|
@ -50,7 +50,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes, network: networkType).derivePath("m/0'/1"),
|
sideHd: accountHD.derive(1),
|
||||||
network: network,
|
network: network,
|
||||||
);
|
);
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
|
@ -106,13 +106,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
final snp =
|
final snp =
|
||||||
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
|
await ElectrumWalletSnapshot.load(name, walletInfo.type, password, LitecoinNetwork.mainnet);
|
||||||
return LitecoinWallet(
|
return LitecoinWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic!,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
initialAddresses: snp.addresses,
|
initialAddresses: snp.addresses,
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await mnemonicToSeedBytes(snp.mnemonic),
|
seedBytes: await mnemonicToSeedBytes(snp.mnemonic!),
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
addressPageType: snp.addressPageType,
|
addressPageType: snp.addressPageType,
|
||||||
|
|
|
@ -16,7 +16,7 @@ import 'package:bip39/bip39.dart' as bip39;
|
||||||
class LitecoinWalletService extends WalletService<
|
class LitecoinWalletService extends WalletService<
|
||||||
BitcoinNewWalletCredentials,
|
BitcoinNewWalletCredentials,
|
||||||
BitcoinRestoreWalletFromSeedCredentials,
|
BitcoinRestoreWalletFromSeedCredentials,
|
||||||
BitcoinRestoreWalletFromWIFCredentials> {
|
BitcoinRestoreWalletFromWIFCredentials,BitcoinNewWalletCredentials> {
|
||||||
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
LitecoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -94,6 +94,11 @@ class LitecoinWalletService extends WalletService<
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LitecoinWallet> restoreFromHardwareWallet(BitcoinNewWalletCredentials credentials) {
|
||||||
|
throw UnimplementedError("Restoring a Litecoin wallet from a hardware wallet is not yet supported!");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LitecoinWallet> restoreFromKeys(
|
Future<LitecoinWallet> restoreFromKeys(
|
||||||
BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async =>
|
BitcoinRestoreWalletFromWIFCredentials credentials, {bool? isTestnet}) async =>
|
||||||
|
|
96
cw_bitcoin/lib/psbt_transaction_builder.dart
Normal file
96
cw_bitcoin/lib/psbt_transaction_builder.dart
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
import 'package:ledger_bitcoin/psbt.dart';
|
||||||
|
|
||||||
|
class PSBTTransactionBuild {
|
||||||
|
final PsbtV2 psbt = PsbtV2();
|
||||||
|
|
||||||
|
PSBTTransactionBuild(
|
||||||
|
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, bool enableRBF = true}) {
|
||||||
|
psbt.setGlobalTxVersion(2);
|
||||||
|
psbt.setGlobalInputCount(inputs.length);
|
||||||
|
psbt.setGlobalOutputCount(outputs.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
|
final input = inputs[i];
|
||||||
|
|
||||||
|
print(input.utxo.isP2tr());
|
||||||
|
print(input.utxo.isSegwit());
|
||||||
|
print(input.utxo.isP2shSegwit());
|
||||||
|
|
||||||
|
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||||
|
psbt.setInputOutputIndex(i, input.utxo.vout);
|
||||||
|
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
|
||||||
|
|
||||||
|
|
||||||
|
if (input.utxo.isSegwit()) {
|
||||||
|
setInputSegwit(i, input);
|
||||||
|
} else if (input.utxo.isP2shSegwit()) {
|
||||||
|
setInputP2shSegwit(i, input);
|
||||||
|
} else if (input.utxo.isP2tr()) {
|
||||||
|
// ToDo: (Konsti) Handle Taproot Inputs
|
||||||
|
} else {
|
||||||
|
setInputP2pkh(i, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < outputs.length; i++) {
|
||||||
|
final output = outputs[i];
|
||||||
|
|
||||||
|
if (output is BitcoinOutput) {
|
||||||
|
psbt.setOutputScript(i, Uint8List.fromList(output.address.toScriptPubKey().toBytes()));
|
||||||
|
psbt.setOutputAmount(i, output.value.toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
||||||
|
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||||
|
psbt.setInputBip32Derivation(
|
||||||
|
i,
|
||||||
|
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||||
|
input.ownerMasterFingerprint,
|
||||||
|
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||||
|
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||||
|
psbt.setInputBip32Derivation(
|
||||||
|
i,
|
||||||
|
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||||
|
input.ownerMasterFingerprint,
|
||||||
|
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||||
|
|
||||||
|
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||||
|
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputP2shSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||||
|
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||||
|
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||||
|
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||||
|
|
||||||
|
psbt.setInputRedeemScript(
|
||||||
|
i, Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||||
|
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||||
|
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PSBTReadyUtxoWithAddress extends UtxoWithAddress {
|
||||||
|
final String rawTx;
|
||||||
|
final String ownerDerivationPath;
|
||||||
|
final Uint8List ownerMasterFingerprint;
|
||||||
|
final String ownerPublicKey;
|
||||||
|
|
||||||
|
PSBTReadyUtxoWithAddress({
|
||||||
|
required super.utxo,
|
||||||
|
required this.rawTx,
|
||||||
|
required super.ownerDetails,
|
||||||
|
required this.ownerDerivationPath,
|
||||||
|
required this.ownerMasterFingerprint,
|
||||||
|
required this.ownerPublicKey,
|
||||||
|
});
|
||||||
|
}
|
|
@ -260,6 +260,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.4"
|
version: "2.2.4"
|
||||||
|
dart_varuint_bitcoin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dart_varuint_bitcoin
|
||||||
|
sha256: "4f0ccc9733fb54148b9d3688eea822b7aaabf5cc00025998f8c09a1d45b31b4b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
encrypt:
|
encrypt:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -313,6 +321,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1+1"
|
version: "2.2.1+1"
|
||||||
|
flutter_reactive_ble:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_reactive_ble
|
||||||
|
sha256: "247e2efa76de203d1ba11335c13754b5b9d0504b5423e5b0c93a600f016b24e0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.3.1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -326,6 +342,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
|
functional_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: functional_data
|
||||||
|
sha256: aefdec4365452283b2a7cf420a3169654d51d3e9553069a22d76680d7a9d7c3d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -422,6 +446,31 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.8.1"
|
version: "4.8.1"
|
||||||
|
ledger_bitcoin:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: HEAD
|
||||||
|
resolved-ref: b6ed573cbeb57d5f0d39dfe4254bf9d15b620ab6
|
||||||
|
url: "https://github.com/cake-tech/ledger-bitcoin.git"
|
||||||
|
source: git
|
||||||
|
version: "0.0.1"
|
||||||
|
ledger_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: ledger_flutter
|
||||||
|
sha256: f1680060ed6ff78f275837e0024ccaf667715a59ba7aa29fa7354bc7752e71c8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
ledger_usb:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ledger_usb
|
||||||
|
sha256: "52c92d03a4cffe06c82921c8e2f79f3cdad6e1cf78e1e9ca35444196ff8f14c2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -582,6 +631,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.1"
|
version: "1.5.1"
|
||||||
|
protobuf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: protobuf
|
||||||
|
sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
provider:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -606,6 +663,22 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.3"
|
version: "1.2.3"
|
||||||
|
reactive_ble_mobile:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: reactive_ble_mobile
|
||||||
|
sha256: "9ec2b4c9c725e439950838d551579750060258fbccd5536d0543b4d07d225798"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.3.1"
|
||||||
|
reactive_ble_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: reactive_ble_platform_interface
|
||||||
|
sha256: "632c92401a2d69c9b94bd48f8fd47488a7013f3d1f9b291884350291a4a81813"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.3.1"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -788,5 +861,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.0.0 <4.0.0"
|
dart: ">=3.0.6 <4.0.0"
|
||||||
flutter: ">=3.10.0"
|
flutter: ">=3.10.0"
|
||||||
|
|
|
@ -35,6 +35,10 @@ dependencies:
|
||||||
url: https://github.com/cake-tech/bitcoin_base.git
|
url: https://github.com/cake-tech/bitcoin_base.git
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
blockchain_utils: ^2.1.1
|
blockchain_utils: ^2.1.1
|
||||||
|
ledger_flutter: ^1.0.1
|
||||||
|
ledger_bitcoin:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/ledger-bitcoin.git
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -51,7 +51,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
initialRegularAddressIndex: initialRegularAddressIndex,
|
initialRegularAddressIndex: initialRegularAddressIndex,
|
||||||
initialChangeAddressIndex: initialChangeAddressIndex,
|
initialChangeAddressIndex: initialChangeAddressIndex,
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"),
|
sideHd: accountHD.derive(1),
|
||||||
network: network,
|
network: network,
|
||||||
initialAddressPageType: addressPageType,
|
initialAddressPageType: addressPageType,
|
||||||
);
|
);
|
||||||
|
@ -93,7 +93,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
final snp = await ElectrumWalletSnapshot.load(
|
final snp = await ElectrumWalletSnapshot.load(
|
||||||
name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
|
name, walletInfo.type, password, BitcoinCashNetwork.mainnet);
|
||||||
return BitcoinCashWallet(
|
return BitcoinCashWallet(
|
||||||
mnemonic: snp.mnemonic,
|
mnemonic: snp.mnemonic!,
|
||||||
password: password,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
unspentCoinsInfo: unspentCoinsInfo,
|
unspentCoinsInfo: unspentCoinsInfo,
|
||||||
|
@ -118,7 +118,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
}
|
}
|
||||||
}).toList(),
|
}).toList(),
|
||||||
initialBalance: snp.balance,
|
initialBalance: snp.balance,
|
||||||
seedBytes: await Mnemonic.toSeed(snp.mnemonic),
|
seedBytes: await Mnemonic.toSeed(snp.mnemonic!),
|
||||||
initialRegularAddressIndex: snp.regularAddressIndex,
|
initialRegularAddressIndex: snp.regularAddressIndex,
|
||||||
initialChangeAddressIndex: snp.changeAddressIndex,
|
initialChangeAddressIndex: snp.changeAddressIndex,
|
||||||
addressPageType: P2pkhAddressType.p2pkh,
|
addressPageType: P2pkhAddressType.p2pkh,
|
||||||
|
@ -166,7 +166,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address = null}) {
|
Future<String> signMessage(String message, {String? address = null}) async {
|
||||||
final index = address != null
|
final index = address != null
|
||||||
? walletAddresses.allAddresses
|
? walletAddresses.allAddresses
|
||||||
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
|
.firstWhere((element) => element.address == AddressUtils.toLegacyAddress(address))
|
||||||
|
|
|
@ -12,7 +12,7 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
|
class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredentials,
|
||||||
BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials> {
|
BitcoinCashRestoreWalletFromSeedCredentials, BitcoinCashRestoreWalletFromWIFCredentials, BitcoinCashNewWalletCredentials> {
|
||||||
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
BitcoinCashWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -93,6 +93,11 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<BitcoinCashWallet> restoreFromHardwareWallet(BitcoinCashNewWalletCredentials credentials) {
|
||||||
|
throw UnimplementedError("Restoring a Bitcoin Cash wallet from a hardware wallet is not yet supported!");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinCashWallet> restoreFromKeys(credentials, {bool? isTestnet}) {
|
Future<BitcoinCashWallet> restoreFromKeys(credentials, {bool? isTestnet}) {
|
||||||
// TODO: implement restoreFromKeys
|
// TODO: implement restoreFromKeys
|
||||||
|
|
28
cw_core/lib/hardware/device_connection_type.dart
Normal file
28
cw_core/lib/hardware/device_connection_type.dart
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
|
enum DeviceConnectionType {
|
||||||
|
usb,
|
||||||
|
ble;
|
||||||
|
|
||||||
|
static List<DeviceConnectionType> supportedConnectionTypes(WalletType walletType,
|
||||||
|
[bool isIOS = false]) {
|
||||||
|
switch (walletType) {
|
||||||
|
case WalletType.bitcoin:
|
||||||
|
case WalletType.ethereum:
|
||||||
|
case WalletType.polygon:
|
||||||
|
if (isIOS) return [DeviceConnectionType.ble];
|
||||||
|
return [DeviceConnectionType.ble, DeviceConnectionType.usb];
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get iconString {
|
||||||
|
switch (this) {
|
||||||
|
case ble:
|
||||||
|
return 'assets/images/bluetooth.png';
|
||||||
|
case usb:
|
||||||
|
return 'assets/images/usb.png';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
cw_core/lib/hardware/device_not_connected_exception.dart
Normal file
7
cw_core/lib/hardware/device_not_connected_exception.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class DeviceNotConnectedException implements Exception {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
DeviceNotConnectedException({
|
||||||
|
this.message = '',
|
||||||
|
});
|
||||||
|
}
|
19
cw_core/lib/hardware/hardware_account_data.dart
Normal file
19
cw_core/lib/hardware/hardware_account_data.dart
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
class HardwareAccountData {
|
||||||
|
HardwareAccountData({
|
||||||
|
required this.address,
|
||||||
|
required this.accountIndex,
|
||||||
|
required this.derivationPath,
|
||||||
|
this.xpub,
|
||||||
|
this.masterFingerprint,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String address;
|
||||||
|
final int accountIndex;
|
||||||
|
final String derivationPath;
|
||||||
|
|
||||||
|
// Bitcoin Specific
|
||||||
|
final Uint8List? masterFingerprint;
|
||||||
|
final String? xpub;
|
||||||
|
}
|
|
@ -17,3 +17,4 @@ const DERIVATION_TYPE_TYPE_ID = 15;
|
||||||
const SPL_TOKEN_TYPE_ID = 16;
|
const SPL_TOKEN_TYPE_ID = 16;
|
||||||
const DERIVATION_INFO_TYPE_ID = 17;
|
const DERIVATION_INFO_TYPE_ID = 17;
|
||||||
const TRON_TOKEN_TYPE_ID = 18;
|
const TRON_TOKEN_TYPE_ID = 18;
|
||||||
|
const HARDWARE_WALLET_TYPE_TYPE_ID = 19;
|
||||||
|
|
|
@ -56,6 +56,8 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||||
|
|
||||||
bool get isEnabledAutoGenerateSubaddress => false;
|
bool get isEnabledAutoGenerateSubaddress => false;
|
||||||
|
|
||||||
|
bool get isHardwareWallet => walletInfo.isHardwareWallet;
|
||||||
|
|
||||||
Future<void> connectToNode({required Node node});
|
Future<void> connectToNode({required Node node});
|
||||||
|
|
||||||
// there is a default definition here because only coins with a pow node (nano based) need to override this
|
// there is a default definition here because only coins with a pow node (nano based) need to override this
|
||||||
|
@ -88,7 +90,7 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||||
|
|
||||||
Future<void> renameWalletFiles(String newWalletName);
|
Future<void> renameWalletFiles(String newWalletName);
|
||||||
|
|
||||||
String signMessage(String message, {String? address = null}) => throw UnimplementedError();
|
Future<String> signMessage(String message, {String? address = null}) => throw UnimplementedError();
|
||||||
|
|
||||||
bool? isTestnet;
|
bool? isTestnet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ abstract class WalletCredentials {
|
||||||
this.password,
|
this.password,
|
||||||
this.passphrase,
|
this.passphrase,
|
||||||
this.derivationInfo,
|
this.derivationInfo,
|
||||||
|
this.hardwareWalletType,
|
||||||
}) {
|
}) {
|
||||||
if (this.walletInfo != null && derivationInfo != null) {
|
if (this.walletInfo != null && derivationInfo != null) {
|
||||||
this.walletInfo!.derivationInfo = derivationInfo;
|
this.walletInfo!.derivationInfo = derivationInfo;
|
||||||
|
@ -22,4 +23,5 @@ abstract class WalletCredentials {
|
||||||
String? passphrase;
|
String? passphrase;
|
||||||
WalletInfo? walletInfo;
|
WalletInfo? walletInfo;
|
||||||
DerivationInfo? derivationInfo;
|
DerivationInfo? derivationInfo;
|
||||||
|
HardwareWalletType? hardwareWalletType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cw_core/address_info.dart';
|
import 'package:cw_core/address_info.dart';
|
||||||
import 'package:cw_core/hive_type_ids.dart';
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
@ -20,6 +21,12 @@ enum DerivationType {
|
||||||
electrum,
|
electrum,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HiveType(typeId: HARDWARE_WALLET_TYPE_TYPE_ID)
|
||||||
|
enum HardwareWalletType {
|
||||||
|
@HiveField(0)
|
||||||
|
ledger,
|
||||||
|
}
|
||||||
|
|
||||||
@HiveType(typeId: DerivationInfo.typeId)
|
@HiveType(typeId: DerivationInfo.typeId)
|
||||||
class DerivationInfo extends HiveObject {
|
class DerivationInfo extends HiveObject {
|
||||||
DerivationInfo({
|
DerivationInfo({
|
||||||
|
@ -40,7 +47,7 @@ class DerivationInfo extends HiveObject {
|
||||||
@HiveField(1, defaultValue: '')
|
@HiveField(1, defaultValue: '')
|
||||||
String balance;
|
String balance;
|
||||||
|
|
||||||
@HiveField(2)
|
@HiveField(2, defaultValue: 0)
|
||||||
int transactionsCount;
|
int transactionsCount;
|
||||||
|
|
||||||
@HiveField(3)
|
@HiveField(3)
|
||||||
|
@ -71,8 +78,9 @@ class WalletInfo extends HiveObject {
|
||||||
this.yatEid,
|
this.yatEid,
|
||||||
this.yatLastUsedAddressRaw,
|
this.yatLastUsedAddressRaw,
|
||||||
this.showIntroCakePayCard,
|
this.showIntroCakePayCard,
|
||||||
this.derivationInfo)
|
this.derivationInfo,
|
||||||
: _yatLastUsedAddressController = StreamController<String>.broadcast();
|
this.hardwareWalletType,
|
||||||
|
): _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||||
|
|
||||||
factory WalletInfo.external({
|
factory WalletInfo.external({
|
||||||
required String id,
|
required String id,
|
||||||
|
@ -88,6 +96,7 @@ class WalletInfo extends HiveObject {
|
||||||
String yatEid = '',
|
String yatEid = '',
|
||||||
String yatLastUsedAddressRaw = '',
|
String yatLastUsedAddressRaw = '',
|
||||||
DerivationInfo? derivationInfo,
|
DerivationInfo? derivationInfo,
|
||||||
|
HardwareWalletType? hardwareWalletType,
|
||||||
}) {
|
}) {
|
||||||
return WalletInfo(
|
return WalletInfo(
|
||||||
id,
|
id,
|
||||||
|
@ -103,6 +112,7 @@ class WalletInfo extends HiveObject {
|
||||||
yatLastUsedAddressRaw,
|
yatLastUsedAddressRaw,
|
||||||
showIntroCakePayCard,
|
showIntroCakePayCard,
|
||||||
derivationInfo,
|
derivationInfo,
|
||||||
|
hardwareWalletType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +181,9 @@ class WalletInfo extends HiveObject {
|
||||||
@HiveField(20)
|
@HiveField(20)
|
||||||
DerivationInfo? derivationInfo;
|
DerivationInfo? derivationInfo;
|
||||||
|
|
||||||
|
@HiveField(21)
|
||||||
|
HardwareWalletType? hardwareWalletType;
|
||||||
|
|
||||||
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
String get yatLastUsedAddress => yatLastUsedAddressRaw ?? '';
|
||||||
|
|
||||||
set yatLastUsedAddress(String address) {
|
set yatLastUsedAddress(String address) {
|
||||||
|
@ -187,6 +200,8 @@ class WalletInfo extends HiveObject {
|
||||||
return showIntroCakePayCard!;
|
return showIntroCakePayCard!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get isHardwareWallet => hardwareWalletType != null;
|
||||||
|
|
||||||
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
|
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||||
|
|
||||||
Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
|
Stream<String> get yatLastUsedAddressStream => _yatLastUsedAddressController.stream;
|
||||||
|
|
|
@ -6,11 +6,13 @@ import 'package:cw_core/wallet_credentials.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
||||||
RFK extends WalletCredentials> {
|
RFK extends WalletCredentials, RFH extends WalletCredentials> {
|
||||||
WalletType getType();
|
WalletType getType();
|
||||||
|
|
||||||
Future<WalletBase> create(N credentials, {bool? isTestnet});
|
Future<WalletBase> create(N credentials, {bool? isTestnet});
|
||||||
|
|
||||||
|
Future<WalletBase> restoreFromHardwareWallet(RFH credentials);
|
||||||
|
|
||||||
Future<WalletBase> restoreFromSeed(RFS credentials, {bool? isTestnet});
|
Future<WalletBase> restoreFromSeed(RFS credentials, {bool? isTestnet});
|
||||||
|
|
||||||
Future<WalletBase> restoreFromKeys(RFK credentials, {bool? isTestnet});
|
Future<WalletBase> restoreFromKeys(RFK credentials, {bool? isTestnet});
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cw_ethereum/ethereum_client.dart';
|
import 'package:cw_ethereum/ethereum_client.dart';
|
||||||
import 'package:cw_ethereum/ethereum_mnemonics_exception.dart';
|
import 'package:cw_ethereum/ethereum_mnemonics_exception.dart';
|
||||||
import 'package:cw_ethereum/ethereum_wallet.dart';
|
import 'package:cw_ethereum/ethereum_wallet.dart';
|
||||||
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
||||||
import 'package:cw_evm/evm_chain_wallet_service.dart';
|
import 'package:cw_evm/evm_chain_wallet_service.dart';
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
|
||||||
|
|
||||||
class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
EthereumWalletService(super.walletInfoSource, {required this.client});
|
EthereumWalletService(super.walletInfoSource, {required this.client});
|
||||||
|
@ -82,6 +83,29 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<EthereumWallet> restoreFromHardwareWallet(
|
||||||
|
EVMChainRestoreWalletFromHardware credentials) async {
|
||||||
|
credentials.walletInfo!.derivationInfo = DerivationInfo(
|
||||||
|
derivationType: DerivationType.bip39,
|
||||||
|
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
|
||||||
|
);
|
||||||
|
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
|
||||||
|
credentials.walletInfo!.address = credentials.hwAccountData.address;
|
||||||
|
|
||||||
|
final wallet = EthereumWallet(
|
||||||
|
walletInfo: credentials.walletInfo!,
|
||||||
|
password: credentials.password!,
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
wallet.addInitialTokens();
|
||||||
|
await wallet.save();
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<EthereumWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
|
Future<EthereumWallet> restoreFromKeys(EVMChainRestoreWalletFromPrivateKey credentials,
|
||||||
{bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
|
|
|
@ -19,6 +19,12 @@ dependencies:
|
||||||
path: ../cw_evm
|
path: ../cw_evm
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
web3dart:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/web3dart.git
|
||||||
|
ref: cake
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
209
cw_evm/lib/contract/erc20.dart
Normal file
209
cw_evm/lib/contract/erc20.dart
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:web3dart/web3dart.dart' as web3;
|
||||||
|
|
||||||
|
final _contractAbi = web3.ContractAbi.fromJson(
|
||||||
|
'[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]',
|
||||||
|
'Erc20');
|
||||||
|
|
||||||
|
/// Interface of the ERC20 standard as defined in the EIP.
|
||||||
|
class ERC20 extends web3.GeneratedContract {
|
||||||
|
/// Constructor.
|
||||||
|
ERC20({
|
||||||
|
required web3.EthereumAddress address,
|
||||||
|
required web3.Web3Client client,
|
||||||
|
int? chainId,
|
||||||
|
}) : super(web3.DeployedContract(_contractAbi, address), client, chainId);
|
||||||
|
|
||||||
|
/// Returns the remaining number of tokens that [spender] will be allowed to spend on behalf of [owner] through [transferFrom]. This is zero by default. This value changes when [approve] or [transferFrom] are called.
|
||||||
|
///
|
||||||
|
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||||
|
/// set, the function will be evaluated in the specified block. By default, the
|
||||||
|
/// latest on-chain block will be used.
|
||||||
|
Future<BigInt> allowance(
|
||||||
|
web3.EthereumAddress owner,
|
||||||
|
web3.EthereumAddress spender, {
|
||||||
|
web3.BlockNum? atBlock,
|
||||||
|
}) async {
|
||||||
|
final function = self.abi.functions[0];
|
||||||
|
assert(checkSignature(function, 'dd62ed3e'));
|
||||||
|
final params = [owner, spender];
|
||||||
|
final response = await read(function, params, atBlock);
|
||||||
|
return (response[0] as BigInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets [amount] as the allowance of [spender] over the caller's tokens. Returns a boolean value indicating whether the operation succeeded. IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 Emits an [Approval] event.
|
||||||
|
///
|
||||||
|
/// The optional [transaction] parameter can be used to override parameters
|
||||||
|
/// like the gas price, nonce and max gas. The `data` and `to` fields will be
|
||||||
|
/// set by the contract.
|
||||||
|
Future<Uint8List> approve(
|
||||||
|
web3.EthereumAddress spender,
|
||||||
|
BigInt amount, {
|
||||||
|
required web3.Credentials credentials,
|
||||||
|
web3.Transaction? transaction,
|
||||||
|
}) async {
|
||||||
|
final function = self.abi.functions[1];
|
||||||
|
assert(checkSignature(function, '095ea7b3'));
|
||||||
|
final params = [spender, amount];
|
||||||
|
return writeRaw(credentials, transaction, function, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of tokens owned by [account].
|
||||||
|
///
|
||||||
|
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||||
|
/// set, the function will be evaluated in the specified block. By default, the
|
||||||
|
/// latest on-chain block will be used.
|
||||||
|
Future<BigInt> balanceOf(
|
||||||
|
web3.EthereumAddress account, {
|
||||||
|
web3.BlockNum? atBlock,
|
||||||
|
}) async {
|
||||||
|
final function = self.abi.functions[2];
|
||||||
|
assert(checkSignature(function, '70a08231'));
|
||||||
|
final params = [account];
|
||||||
|
final response = await read(function, params, atBlock);
|
||||||
|
return (response[0] as BigInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the decimal precision of the token.
|
||||||
|
///
|
||||||
|
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||||
|
/// set, the function will be evaluated in the specified block. By default, the
|
||||||
|
/// latest on-chain block will be used.
|
||||||
|
Future<BigInt> decimals({web3.BlockNum? atBlock}) async {
|
||||||
|
final function = self.abi.functions[3];
|
||||||
|
assert(checkSignature(function, '313ce567'));
|
||||||
|
final params = [];
|
||||||
|
final response = await read(function, params, atBlock);
|
||||||
|
return (response[0] as BigInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the name of the token.
|
||||||
|
///
|
||||||
|
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||||
|
/// set, the function will be evaluated in the specified block. By default, the
|
||||||
|
/// latest on-chain block will be used.
|
||||||
|
Future<String> name({web3.BlockNum? atBlock}) async {
|
||||||
|
final function = self.abi.functions[4];
|
||||||
|
assert(checkSignature(function, '06fdde03'));
|
||||||
|
final params = [];
|
||||||
|
final response = await read(function, params, atBlock);
|
||||||
|
return (response[0] as String);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the symbol of the token.
|
||||||
|
///
|
||||||
|
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||||
|
/// set, the function will be evaluated in the specified block. By default, the
|
||||||
|
/// latest on-chain block will be used.
|
||||||
|
Future<String> symbol({web3.BlockNum? atBlock}) async {
|
||||||
|
final function = self.abi.functions[5];
|
||||||
|
assert(checkSignature(function, '95d89b41'));
|
||||||
|
final params = [];
|
||||||
|
final response = await read(function, params, atBlock);
|
||||||
|
return (response[0] as String);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of tokens in existence.
|
||||||
|
///
|
||||||
|
/// The optional [atBlock] parameter can be used to view historical data. When
|
||||||
|
/// set, the function will be evaluated in the specified block. By default, the
|
||||||
|
/// latest on-chain block will be used.
|
||||||
|
Future<BigInt> totalSupply({web3.BlockNum? atBlock}) async {
|
||||||
|
final function = self.abi.functions[6];
|
||||||
|
assert(checkSignature(function, '18160ddd'));
|
||||||
|
final params = [];
|
||||||
|
final response = await read(function, params, atBlock);
|
||||||
|
return (response[0] as BigInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves [amount] tokens from the caller's account to [recipient]. Returns a boolean value indicating whether the operation succeeded. Emits a [Transfer] event.
|
||||||
|
///
|
||||||
|
/// The optional [transaction] parameter can be used to override parameters
|
||||||
|
/// like the gas price, nonce and max gas. The `data` and `to` fields will be
|
||||||
|
/// set by the contract.
|
||||||
|
Future<Uint8List> transfer(
|
||||||
|
web3.EthereumAddress recipient,
|
||||||
|
BigInt amount, {
|
||||||
|
required web3.Credentials credentials,
|
||||||
|
web3.Transaction? transaction,
|
||||||
|
}) async {
|
||||||
|
final function = self.abi.functions[7];
|
||||||
|
assert(checkSignature(function, 'a9059cbb'));
|
||||||
|
final params = [recipient, amount];
|
||||||
|
return writeRaw(credentials, transaction, function, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves [amount] tokens from [sender] to [recipient] using the allowance mechanism. [amount] is then deducted from the caller's allowance. Returns a boolean value indicating whether the operation succeeded. Emits a [Transfer] event.
|
||||||
|
///
|
||||||
|
/// The optional [transaction] parameter can be used to override parameters
|
||||||
|
/// like the gas price, nonce and max gas. The `data` and `to` fields will be
|
||||||
|
/// set by the contract.
|
||||||
|
Future<Uint8List> transferFrom(web3.EthereumAddress sender,
|
||||||
|
web3.EthereumAddress recipient, BigInt amount,
|
||||||
|
{required web3.Credentials credentials,
|
||||||
|
web3.Transaction? transaction}) async {
|
||||||
|
final function = self.abi.functions[8];
|
||||||
|
assert(checkSignature(function, '23b872dd'));
|
||||||
|
final params = [sender, recipient, amount];
|
||||||
|
return writeRaw(credentials, transaction, function, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a live stream of all Approval events emitted by this contract.
|
||||||
|
Stream<Approval> approvalEvents(
|
||||||
|
{web3.BlockNum? fromBlock, web3.BlockNum? toBlock}) {
|
||||||
|
final event = self.event('Approval');
|
||||||
|
final filter = web3.FilterOptions.events(
|
||||||
|
contract: self, event: event, fromBlock: fromBlock, toBlock: toBlock);
|
||||||
|
return client.events(filter).map((web3.FilterEvent result) {
|
||||||
|
final decoded = event.decodeResults(result.topics!, result.data!);
|
||||||
|
return Approval._(decoded);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a live stream of all Transfer events emitted by this contract.
|
||||||
|
Stream<Transfer> transferEvents(
|
||||||
|
{web3.BlockNum? fromBlock, web3.BlockNum? toBlock}) {
|
||||||
|
final event = self.event('Transfer');
|
||||||
|
final filter = web3.FilterOptions.events(
|
||||||
|
contract: self, event: event, fromBlock: fromBlock, toBlock: toBlock);
|
||||||
|
return client.events(filter).map((web3.FilterEvent result) {
|
||||||
|
final decoded = event.decodeResults(result.topics!, result.data!);
|
||||||
|
return Transfer._(decoded);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emitted when the allowance of a [spender] for an [owner] is set by a call to [ERC20.approve]. [value] is the new allowance.
|
||||||
|
class Approval {
|
||||||
|
Approval._(List<dynamic> response)
|
||||||
|
: owner = (response[0] as web3.EthereumAddress),
|
||||||
|
spender = (response[1] as web3.EthereumAddress),
|
||||||
|
value = (response[2] as BigInt);
|
||||||
|
|
||||||
|
/// The owner address.
|
||||||
|
final web3.EthereumAddress owner;
|
||||||
|
|
||||||
|
/// The spender address.
|
||||||
|
final web3.EthereumAddress spender;
|
||||||
|
|
||||||
|
/// Value.
|
||||||
|
final BigInt value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emitted when [value] tokens are moved from one account ([from]) to another ([to]). Note that [value] may be zero.
|
||||||
|
class Transfer {
|
||||||
|
Transfer._(List<dynamic> response)
|
||||||
|
: from = (response[0] as web3.EthereumAddress),
|
||||||
|
to = (response[1] as web3.EthereumAddress),
|
||||||
|
value = (response[2] as BigInt);
|
||||||
|
|
||||||
|
/// From address.
|
||||||
|
final web3.EthereumAddress from;
|
||||||
|
|
||||||
|
/// To address.
|
||||||
|
final web3.EthereumAddress to;
|
||||||
|
|
||||||
|
/// Value.
|
||||||
|
final BigInt value;
|
||||||
|
}
|
|
@ -2,21 +2,20 @@ import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cw_core/node.dart';
|
|
||||||
import 'package:cw_core/erc20_token.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/erc20_token.dart';
|
||||||
import 'package:cw_evm/evm_erc20_balance.dart';
|
import 'package:cw_core/node.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||||
import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
|
||||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||||
|
import 'package:cw_evm/evm_erc20_balance.dart';
|
||||||
|
import 'package:cw_evm/pending_evm_chain_transaction.dart';
|
||||||
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
import 'package:cw_evm/.secrets.g.dart' as secrets;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:erc20/erc20.dart';
|
|
||||||
import 'package:web3dart/web3dart.dart';
|
|
||||||
import 'package:hex/hex.dart' as hex;
|
import 'package:hex/hex.dart' as hex;
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
|
import 'contract/erc20.dart';
|
||||||
|
|
||||||
abstract class EVMChainClient {
|
abstract class EVMChainClient {
|
||||||
final httpClient = Client();
|
final httpClient = Client();
|
||||||
|
@ -82,7 +81,7 @@ abstract class EVMChainClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PendingEVMChainTransaction> signTransaction({
|
Future<PendingEVMChainTransaction> signTransaction({
|
||||||
required EthPrivateKey privateKey,
|
required Credentials privateKey,
|
||||||
required String toAddress,
|
required String toAddress,
|
||||||
required BigInt amount,
|
required BigInt amount,
|
||||||
required int gas,
|
required int gas,
|
||||||
|
@ -96,8 +95,7 @@ abstract class EVMChainClient {
|
||||||
currency == CryptoCurrency.maticpoly ||
|
currency == CryptoCurrency.maticpoly ||
|
||||||
contractAddress != null);
|
contractAddress != null);
|
||||||
|
|
||||||
bool isEVMCompatibleChain =
|
bool isNativeToken = currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
|
||||||
currency == CryptoCurrency.eth || currency == CryptoCurrency.maticpoly;
|
|
||||||
|
|
||||||
final price = _client!.getGasPrice();
|
final price = _client!.getGasPrice();
|
||||||
|
|
||||||
|
@ -105,17 +103,16 @@ abstract class EVMChainClient {
|
||||||
from: privateKey.address,
|
from: privateKey.address,
|
||||||
to: EthereumAddress.fromHex(toAddress),
|
to: EthereumAddress.fromHex(toAddress),
|
||||||
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
maxPriorityFeePerGas: EtherAmount.fromInt(EtherUnit.gwei, priority.tip),
|
||||||
amount: isEVMCompatibleChain ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
amount: isNativeToken ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
||||||
data: data != null ? hexToBytes(data) : null,
|
data: data != null ? hexToBytes(data) : null,
|
||||||
);
|
);
|
||||||
|
|
||||||
final signedTransaction =
|
Uint8List signedTransaction;
|
||||||
await _client!.signTransaction(privateKey, transaction, chainId: chainId);
|
|
||||||
|
|
||||||
final Function _sendTransaction;
|
final Function _sendTransaction;
|
||||||
|
|
||||||
if (isEVMCompatibleChain) {
|
if (isNativeToken) {
|
||||||
_sendTransaction = () async => await sendTransaction(signedTransaction);
|
signedTransaction = await _client!.signTransaction(privateKey, transaction, chainId: chainId);
|
||||||
} else {
|
} else {
|
||||||
final erc20 = ERC20(
|
final erc20 = ERC20(
|
||||||
client: _client!,
|
client: _client!,
|
||||||
|
@ -123,16 +120,17 @@ abstract class EVMChainClient {
|
||||||
chainId: chainId,
|
chainId: chainId,
|
||||||
);
|
);
|
||||||
|
|
||||||
_sendTransaction = () async {
|
signedTransaction = await erc20.transfer(
|
||||||
await erc20.transfer(
|
|
||||||
EthereumAddress.fromHex(toAddress),
|
EthereumAddress.fromHex(toAddress),
|
||||||
amount,
|
amount,
|
||||||
credentials: privateKey,
|
credentials: privateKey,
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
);
|
);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_sendTransaction = () async => await sendTransaction(signedTransaction);
|
||||||
|
|
||||||
|
|
||||||
return PendingEVMChainTransaction(
|
return PendingEVMChainTransaction(
|
||||||
signedTransaction: signedTransaction,
|
signedTransaction: signedTransaction,
|
||||||
amount: amount.toString(),
|
amount: amount.toString(),
|
||||||
|
@ -158,8 +156,9 @@ abstract class EVMChainClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> sendTransaction(Uint8List signedTransaction) async =>
|
Future<String> sendTransaction(Uint8List signedTransaction) async {
|
||||||
await _client!.sendRawTransaction(prepareSignedTransactionForSending(signedTransaction));
|
return await _client!.sendRawTransaction(prepareSignedTransactionForSending(signedTransaction));
|
||||||
|
}
|
||||||
|
|
||||||
Future getTransactionDetails(String transactionHash) async {
|
Future getTransactionDetails(String transactionHash) async {
|
||||||
// Wait for the transaction receipt to become available
|
// Wait for the transaction receipt to become available
|
||||||
|
|
|
@ -6,6 +6,8 @@ class EVMChainTransactionCreationException implements Exception {
|
||||||
EVMChainTransactionCreationException(CryptoCurrency currency)
|
EVMChainTransactionCreationException(CryptoCurrency currency)
|
||||||
: exceptionMessage = 'Wrong balance. Not enough ${currency.title} on your balance.';
|
: exceptionMessage = 'Wrong balance. Not enough ${currency.title} on your balance.';
|
||||||
|
|
||||||
|
EVMChainTransactionCreationException.fromMessage(this.exceptionMessage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => exceptionMessage;
|
String toString() => exceptionMessage;
|
||||||
}
|
}
|
||||||
|
|
35
cw_evm/lib/evm_chain_hardware_wallet_service.dart
Normal file
35
cw_evm/lib/evm_chain_hardware_wallet_service.dart
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||||
|
import 'package:ledger_ethereum/ledger_ethereum.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
|
||||||
|
class EVMChainHardwareWalletService {
|
||||||
|
EVMChainHardwareWalletService(this.ledger, this.device);
|
||||||
|
|
||||||
|
final Ledger ledger;
|
||||||
|
final LedgerDevice device;
|
||||||
|
|
||||||
|
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
|
||||||
|
final ethereumLedgerApp = EthereumLedgerApp(ledger);
|
||||||
|
|
||||||
|
final version = await ethereumLedgerApp.getVersion(device);
|
||||||
|
|
||||||
|
final accounts = <HardwareAccountData>[];
|
||||||
|
final indexRange = List.generate(limit, (i) => i + index);
|
||||||
|
|
||||||
|
for (final i in indexRange) {
|
||||||
|
final derivationPath = "m/44'/60'/$i'/0/0";
|
||||||
|
final address =
|
||||||
|
await ethereumLedgerApp.getAccounts(device, accountsDerivationPath: derivationPath);
|
||||||
|
|
||||||
|
accounts.add(HardwareAccountData(
|
||||||
|
address: address.first,
|
||||||
|
accountIndex: i,
|
||||||
|
derivationPath: derivationPath,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/output_info.dart';
|
import 'package:cw_core/output_info.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
|
||||||
class EVMChainTransactionCredentials {
|
class EVMChainTransactionCredentials {
|
||||||
EVMChainTransactionCredentials(
|
EVMChainTransactionCredentials(
|
||||||
|
|
|
@ -25,6 +25,7 @@ import 'package:cw_evm/evm_chain_transaction_history.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||||
import 'package:cw_evm/evm_chain_wallet_addresses.dart';
|
import 'package:cw_evm/evm_chain_wallet_addresses.dart';
|
||||||
|
import 'package:cw_evm/evm_ledger_credentials.dart';
|
||||||
import 'package:cw_evm/file.dart';
|
import 'package:cw_evm/file.dart';
|
||||||
import 'package:hex/hex.dart';
|
import 'package:hex/hex.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
@ -83,9 +84,9 @@ abstract class EVMChainWalletBase
|
||||||
|
|
||||||
late final Box<Erc20Token> evmChainErc20TokensBox;
|
late final Box<Erc20Token> evmChainErc20TokensBox;
|
||||||
|
|
||||||
late final EthPrivateKey _evmChainPrivateKey;
|
late final Credentials _evmChainPrivateKey;
|
||||||
|
|
||||||
EthPrivateKey get evmChainPrivateKey => _evmChainPrivateKey;
|
Credentials get evmChainPrivateKey => _evmChainPrivateKey;
|
||||||
|
|
||||||
late EVMChainClient _client;
|
late EVMChainClient _client;
|
||||||
|
|
||||||
|
@ -141,12 +142,18 @@ abstract class EVMChainWalletBase
|
||||||
|
|
||||||
await walletAddresses.init();
|
await walletAddresses.init();
|
||||||
await transactionHistory.init();
|
await transactionHistory.init();
|
||||||
|
|
||||||
|
if (walletInfo.isHardwareWallet) {
|
||||||
|
_evmChainPrivateKey = EvmLedgerCredentials(walletInfo.address);
|
||||||
|
walletAddresses.address = walletInfo.address;
|
||||||
|
} else {
|
||||||
_evmChainPrivateKey = await getPrivateKey(
|
_evmChainPrivateKey = await getPrivateKey(
|
||||||
mnemonic: _mnemonic,
|
mnemonic: _mnemonic,
|
||||||
privateKey: _hexPrivateKey,
|
privateKey: _hexPrivateKey,
|
||||||
password: _password,
|
password: _password,
|
||||||
);
|
);
|
||||||
walletAddresses.address = _evmChainPrivateKey.address.hexEip55;
|
walletAddresses.address = _evmChainPrivateKey.address.hexEip55;
|
||||||
|
}
|
||||||
await save();
|
await save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,6 +290,11 @@ abstract class EVMChainWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transactionCurrency is Erc20Token && isHardwareWallet) {
|
||||||
|
await (_evmChainPrivateKey as EvmLedgerCredentials)
|
||||||
|
.provideERC20Info(transactionCurrency.contractAddress, _client.chainId);
|
||||||
|
}
|
||||||
|
|
||||||
final pendingEVMChainTransaction = await _client.signTransaction(
|
final pendingEVMChainTransaction = await _client.signTransaction(
|
||||||
privateKey: _evmChainPrivateKey,
|
privateKey: _evmChainPrivateKey,
|
||||||
toAddress: _credentials.outputs.first.isParsedAddress
|
toAddress: _credentials.outputs.first.isParsedAddress
|
||||||
|
@ -377,7 +389,9 @@ abstract class EVMChainWalletBase
|
||||||
String? get seed => _mnemonic;
|
String? get seed => _mnemonic;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get privateKey => HEX.encode(_evmChainPrivateKey.privateKey);
|
String? get privateKey => evmChainPrivateKey is EthPrivateKey
|
||||||
|
? HEX.encode((evmChainPrivateKey as EthPrivateKey).privateKey)
|
||||||
|
: null;
|
||||||
|
|
||||||
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
Future<String> makePath() async => pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||||
|
|
||||||
|
@ -535,8 +549,8 @@ abstract class EVMChainWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address}) =>
|
Future<String> signMessage(String message, {String? address}) async =>
|
||||||
bytesToHex(_evmChainPrivateKey.signPersonalMessageToUint8List(ascii.encode(message)));
|
bytesToHex(await _evmChainPrivateKey.signPersonalMessage(ascii.encode(message)));
|
||||||
|
|
||||||
Web3Client? getWeb3Client() => _client.getWeb3Client();
|
Web3Client? getWeb3Client() => _client.getWeb3Client();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||||
import 'package:cw_core/wallet_credentials.dart';
|
import 'package:cw_core/wallet_credentials.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
|
||||||
|
@ -27,3 +28,13 @@ class EVMChainRestoreWalletFromPrivateKey extends WalletCredentials {
|
||||||
|
|
||||||
final String privateKey;
|
final String privateKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EVMChainRestoreWalletFromHardware extends WalletCredentials {
|
||||||
|
EVMChainRestoreWalletFromHardware({
|
||||||
|
required String name,
|
||||||
|
required this.hwAccountData,
|
||||||
|
WalletInfo? walletInfo,
|
||||||
|
}) : super(name: name, walletInfo: walletInfo);
|
||||||
|
|
||||||
|
final HardwareAccountData hwAccountData;
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ import 'package:hive/hive.dart';
|
||||||
abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletService<
|
abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletService<
|
||||||
EVMChainNewWalletCredentials,
|
EVMChainNewWalletCredentials,
|
||||||
EVMChainRestoreWalletFromSeedCredentials,
|
EVMChainRestoreWalletFromSeedCredentials,
|
||||||
EVMChainRestoreWalletFromPrivateKey> {
|
EVMChainRestoreWalletFromPrivateKey,
|
||||||
|
EVMChainRestoreWalletFromHardware> {
|
||||||
EVMChainWalletService(this.walletInfoSource);
|
EVMChainWalletService(this.walletInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -24,6 +25,9 @@ abstract class EVMChainWalletService<T extends EVMChainWallet> extends WalletSer
|
||||||
@override
|
@override
|
||||||
Future<T> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet});
|
Future<T> create(EVMChainNewWalletCredentials credentials, {bool? isTestnet});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T> restoreFromHardwareWallet(EVMChainRestoreWalletFromHardware credentials);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<T> openWallet(String name, String password);
|
Future<T> openWallet(String name, String password);
|
||||||
|
|
||||||
|
|
103
cw_evm/lib/evm_ledger_credentials.dart
Normal file
103
cw_evm/lib/evm_ledger_credentials.dart
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cw_core/hardware/device_not_connected_exception.dart';
|
||||||
|
import 'package:ledger_ethereum/ledger_ethereum.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
import 'package:web3dart/crypto.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
|
class EvmLedgerCredentials extends CredentialsWithKnownAddress {
|
||||||
|
final String _address;
|
||||||
|
|
||||||
|
Ledger? ledger;
|
||||||
|
LedgerDevice? ledgerDevice;
|
||||||
|
EthereumLedgerApp? ethereumLedgerApp;
|
||||||
|
|
||||||
|
EvmLedgerCredentials(this._address);
|
||||||
|
|
||||||
|
@override
|
||||||
|
EthereumAddress get address => EthereumAddress.fromHex(_address);
|
||||||
|
|
||||||
|
void setLedger(Ledger setLedger, [LedgerDevice? setLedgerDevice, String? derivationPath]) {
|
||||||
|
ledger = setLedger;
|
||||||
|
ledgerDevice = setLedgerDevice;
|
||||||
|
ethereumLedgerApp =
|
||||||
|
EthereumLedgerApp(ledger!, derivationPath: derivationPath ?? "m/44'/60'/0'/0/0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
MsgSignature signToEcSignature(Uint8List payload, {int? chainId, bool isEIP1559 = false}) =>
|
||||||
|
throw UnimplementedError("EvmLedgerCredentials.signToEcSignature");
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<MsgSignature> signToSignature(Uint8List payload,
|
||||||
|
{int? chainId, bool isEIP1559 = false}) async {
|
||||||
|
if (ledgerDevice == null && ledger?.devices.isNotEmpty != true) {
|
||||||
|
throw DeviceNotConnectedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final sig = await ethereumLedgerApp!.signTransaction(device, payload);
|
||||||
|
|
||||||
|
final v = sig[0].toInt();
|
||||||
|
final r = bytesToHex(sig.sublist(1, 1 + 32));
|
||||||
|
final s = bytesToHex(sig.sublist(1 + 32, 1 + 32 + 32));
|
||||||
|
|
||||||
|
var truncChainId = chainId ?? 1;
|
||||||
|
while (truncChainId.bitLength > 32) {
|
||||||
|
truncChainId >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
final truncTarget = truncChainId * 2 + 35;
|
||||||
|
|
||||||
|
int parity = v;
|
||||||
|
if (truncTarget & 0xff == v) {
|
||||||
|
parity = 0;
|
||||||
|
} else if ((truncTarget + 1) & 0xff == v) {
|
||||||
|
parity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/ethereumjs/ethereumjs-util/blob/8ffe697fafb33cefc7b7ec01c11e3a7da787fe0e/src/signature.ts#L26
|
||||||
|
int chainIdV;
|
||||||
|
if (isEIP1559) {
|
||||||
|
chainIdV = v;
|
||||||
|
} else {
|
||||||
|
chainIdV = chainId != null ? (parity + (chainId * 2 + 35)) : parity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MsgSignature(BigInt.parse(r, radix: 16), BigInt.parse(s, radix: 16), chainIdV);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> signPersonalMessage(Uint8List payload, {int? chainId}) async {
|
||||||
|
if (isNotConnected) throw DeviceNotConnectedException();
|
||||||
|
|
||||||
|
final sig = await ethereumLedgerApp!.signMessage(device, payload);
|
||||||
|
|
||||||
|
final r = sig.sublist(1, 1 + 32);
|
||||||
|
final s = sig.sublist(1 + 32, 1 + 32 + 32);
|
||||||
|
final v = [sig[0]];
|
||||||
|
|
||||||
|
// https://github.com/ethereumjs/ethereumjs-util/blob/8ffe697fafb33cefc7b7ec01c11e3a7da787fe0e/src/signature.ts#L63
|
||||||
|
return Uint8List.fromList(r + s + v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Uint8List signPersonalMessageToUint8List(Uint8List payload, {int? chainId}) =>
|
||||||
|
throw UnimplementedError("EvmLedgerCredentials.signPersonalMessageToUint8List");
|
||||||
|
|
||||||
|
Future<void> provideERC20Info(String erc20ContractAddress, int chainId) async {
|
||||||
|
if (isNotConnected) throw DeviceNotConnectedException();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ethereumLedgerApp!.getAndProvideERC20TokenInformation(device,
|
||||||
|
erc20ContractAddress: erc20ContractAddress, chainId: chainId);
|
||||||
|
} on LedgerException catch (e) {
|
||||||
|
if (e.errorCode != -28672) rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isNotConnected => (ledgerDevice ?? ledger?.devices.firstOrNull) == null;
|
||||||
|
|
||||||
|
LedgerDevice get device => ledgerDevice ?? ledger!.devices.first;
|
||||||
|
}
|
|
@ -13,7 +13,6 @@ dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
web3dart: ^2.7.1
|
web3dart: ^2.7.1
|
||||||
erc20: ^1.0.1
|
|
||||||
bip39: ^1.0.6
|
bip39: ^1.0.6
|
||||||
bip32: ^2.0.0
|
bip32: ^2.0.0
|
||||||
hex: ^0.2.0
|
hex: ^0.2.0
|
||||||
|
@ -23,6 +22,20 @@ dependencies:
|
||||||
shared_preferences: ^2.0.15
|
shared_preferences: ^2.0.15
|
||||||
cw_core:
|
cw_core:
|
||||||
path: ../cw_core
|
path: ../cw_core
|
||||||
|
ledger_flutter: ^1.0.1
|
||||||
|
ledger_ethereum:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/ledger-ethereum.git
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
web3dart:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/web3dart.git
|
||||||
|
ref: cake
|
||||||
|
ledger_flutter:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/ledger-flutter.git
|
||||||
|
ref: cake
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -56,7 +56,8 @@ class HavenRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||||
class HavenWalletService extends WalletService<
|
class HavenWalletService extends WalletService<
|
||||||
HavenNewWalletCredentials,
|
HavenNewWalletCredentials,
|
||||||
HavenRestoreWalletFromSeedCredentials,
|
HavenRestoreWalletFromSeedCredentials,
|
||||||
HavenRestoreWalletFromKeysCredentials> {
|
HavenRestoreWalletFromKeysCredentials,
|
||||||
|
HavenNewWalletCredentials> {
|
||||||
HavenWalletService(this.walletInfoSource);
|
HavenWalletService(this.walletInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -172,6 +173,11 @@ class HavenWalletService extends WalletService<
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<HavenWallet> restoreFromHardwareWallet(HavenNewWalletCredentials credentials) {
|
||||||
|
throw UnimplementedError("Restoring a Haven wallet from a hardware wallet is not yet supported!");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<HavenWallet> restoreFromKeys(
|
Future<HavenWallet> restoreFromKeys(
|
||||||
HavenRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
HavenRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
||||||
|
|
|
@ -17,6 +17,9 @@ typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, P
|
||||||
typedef restore_wallet_from_spend_key = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
typedef restore_wallet_from_spend_key = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||||
Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
|
||||||
|
|
||||||
|
// typedef restore_wallet_from_device = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||||
|
// Int32, Int64, Pointer<Utf8>);
|
||||||
|
|
||||||
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
|
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
|
||||||
|
|
||||||
typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
|
typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
|
||||||
|
|
|
@ -17,6 +17,9 @@ typedef RestoreWalletFromKeys = int Function(Pointer<Utf8>, Pointer<Utf8>,
|
||||||
typedef RestoreWalletFromSpendKey = int Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
typedef RestoreWalletFromSpendKey = int Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||||
Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
|
Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
|
||||||
|
|
||||||
|
typedef RestoreWalletFromDevice = int Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
|
||||||
|
int, int, Pointer<Utf8>);
|
||||||
|
|
||||||
typedef IsWalletExist = int Function(Pointer<Utf8>);
|
typedef IsWalletExist = int Function(Pointer<Utf8>);
|
||||||
|
|
||||||
typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
|
typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
|
||||||
|
|
|
@ -31,6 +31,11 @@ final restoreWalletFromSpendKeyNative = moneroApi
|
||||||
'restore_wallet_from_spend_key')
|
'restore_wallet_from_spend_key')
|
||||||
.asFunction<RestoreWalletFromSpendKey>();
|
.asFunction<RestoreWalletFromSpendKey>();
|
||||||
|
|
||||||
|
// final restoreWalletFromDeviceNative = moneroApi
|
||||||
|
// .lookup<NativeFunction<restore_wallet_from_device>>(
|
||||||
|
// 'restore_wallet_from_device')
|
||||||
|
// .asFunction<RestoreWalletFromDevice>();
|
||||||
|
|
||||||
final isWalletExistNative = moneroApi
|
final isWalletExistNative = moneroApi
|
||||||
.lookup<NativeFunction<is_wallet_exist>>('is_wallet_exist')
|
.lookup<NativeFunction<is_wallet_exist>>('is_wallet_exist')
|
||||||
.asFunction<IsWalletExist>();
|
.asFunction<IsWalletExist>();
|
||||||
|
@ -185,6 +190,38 @@ void restoreWalletFromSpendKeySync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// void restoreMoneroWalletFromDevice(
|
||||||
|
// {required String path,
|
||||||
|
// required String password,
|
||||||
|
// required String deviceName,
|
||||||
|
// int nettype = 0,
|
||||||
|
// int restoreHeight = 0}) {
|
||||||
|
//
|
||||||
|
// final pathPointer = path.toNativeUtf8();
|
||||||
|
// final passwordPointer = password.toNativeUtf8();
|
||||||
|
// final deviceNamePointer = deviceName.toNativeUtf8();
|
||||||
|
// final errorMessagePointer = ''.toNativeUtf8();
|
||||||
|
//
|
||||||
|
// final isWalletRestored = restoreWalletFromDeviceNative(
|
||||||
|
// pathPointer,
|
||||||
|
// passwordPointer,
|
||||||
|
// deviceNamePointer,
|
||||||
|
// nettype,
|
||||||
|
// restoreHeight,
|
||||||
|
// errorMessagePointer) != 0;
|
||||||
|
//
|
||||||
|
// calloc.free(pathPointer);
|
||||||
|
// calloc.free(passwordPointer);
|
||||||
|
//
|
||||||
|
// storeSync();
|
||||||
|
//
|
||||||
|
// if (!isWalletRestored) {
|
||||||
|
// throw WalletRestoreFromKeysException(
|
||||||
|
// message: convertUTF8ToString(pointer: errorMessagePointer));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
void loadWallet({
|
void loadWallet({
|
||||||
required String path,
|
required String path,
|
||||||
required String password,
|
required String password,
|
||||||
|
|
|
@ -686,7 +686,7 @@ abstract class MoneroWalletBase
|
||||||
void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e;
|
void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address}) {
|
Future<String> signMessage(String message, {String? address}) async {
|
||||||
final useAddress = address ?? "";
|
final useAddress = address ?? "";
|
||||||
return monero_wallet.signMessage(message, address: useAddress);
|
return monero_wallet.signMessage(message, address: useAddress);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||||
MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> {
|
MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials, MoneroNewWalletCredentials> {
|
||||||
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -227,6 +227,11 @@ class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<MoneroWallet> restoreFromHardwareWallet(MoneroNewWalletCredentials credentials) {
|
||||||
|
throw UnimplementedError("Restoring a Monero wallet from a hardware wallet is not yet supported!");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials,
|
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials,
|
||||||
{bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import 'package:nanodart/nanodart.dart';
|
||||||
import 'package:nanoutil/nanoutil.dart';
|
import 'package:nanoutil/nanoutil.dart';
|
||||||
|
|
||||||
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials> {
|
NanoRestoreWalletFromSeedCredentials, NanoRestoreWalletFromKeysCredentials, NanoNewWalletCredentials> {
|
||||||
NanoWalletService(this.walletInfoSource);
|
NanoWalletService(this.walletInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -109,6 +109,11 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
|
||||||
|
throw UnimplementedError("Restoring a Nano wallet from a hardware wallet is not yet supported!");
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
||||||
if (credentials.mnemonic.contains(' ')) {
|
if (credentials.mnemonic.contains(' ')) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
|
||||||
import 'package:cw_evm/evm_chain_wallet_service.dart';
|
import 'package:cw_evm/evm_chain_wallet_service.dart';
|
||||||
|
@ -86,6 +87,29 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<PolygonWallet> restoreFromHardwareWallet(
|
||||||
|
EVMChainRestoreWalletFromHardware credentials) async {
|
||||||
|
credentials.walletInfo!.derivationInfo = DerivationInfo(
|
||||||
|
derivationType: DerivationType.bip39,
|
||||||
|
derivationPath: "m/44'/60'/${credentials.hwAccountData.accountIndex}'/0/0"
|
||||||
|
);
|
||||||
|
credentials.walletInfo!.hardwareWalletType = credentials.hardwareWalletType;
|
||||||
|
credentials.walletInfo!.address = credentials.hwAccountData.address;
|
||||||
|
|
||||||
|
final wallet = PolygonWallet(
|
||||||
|
walletInfo: credentials.walletInfo!,
|
||||||
|
password: credentials.password!,
|
||||||
|
client: client,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
wallet.addInitialTokens();
|
||||||
|
await wallet.save();
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PolygonWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
|
Future<PolygonWallet> restoreFromSeed(EVMChainRestoreWalletFromSeedCredentials credentials,
|
||||||
{bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
|
|
|
@ -23,6 +23,11 @@ dependencies:
|
||||||
bip39: ^1.0.6
|
bip39: ^1.0.6
|
||||||
collection: ^1.17.1
|
collection: ^1.17.1
|
||||||
|
|
||||||
|
dependency_overrides:
|
||||||
|
web3dart:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/web3dart.git
|
||||||
|
ref: cake
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -2,7 +2,10 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:cw_core/balance.dart';
|
||||||
import 'package:cw_core/pathForWallet.dart';
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/transaction_history.dart';
|
||||||
|
import 'package:cw_core/transaction_info.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
@ -13,7 +16,7 @@ import 'package:cw_solana/solana_wallet_creation_credentials.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
||||||
SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey> {
|
SolanaRestoreWalletFromSeedCredentials, SolanaRestoreWalletFromPrivateKey, SolanaNewWalletCredentials> {
|
||||||
SolanaWalletService(this.walletInfoSource);
|
SolanaWalletService(this.walletInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -134,4 +137,10 @@ class SolanaWalletService extends WalletService<SolanaNewWalletCredentials,
|
||||||
|
|
||||||
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>> restoreFromHardwareWallet(SolanaNewWalletCredentials credentials) {
|
||||||
|
// TODO: implement restoreFromHardwareWallet
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -551,7 +551,7 @@ abstract class TronWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address}) =>
|
Future<String> signMessage(String message, {String? address}) async =>
|
||||||
_tronPrivateKey.signPersonalMessage(ascii.encode(message));
|
_tronPrivateKey.signPersonalMessage(ascii.encode(message));
|
||||||
|
|
||||||
String getTronBase58AddressFromHex(String hexAddress) {
|
String getTronBase58AddressFromHex(String hexAddress) {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
|
import 'package:cw_core/balance.dart';
|
||||||
import 'package:cw_core/pathForWallet.dart';
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
|
import 'package:cw_core/transaction_history.dart';
|
||||||
|
import 'package:cw_core/transaction_info.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
@ -13,8 +16,11 @@ import 'package:cw_tron/tron_wallet_creation_credentials.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
class TronWalletService extends WalletService<TronNewWalletCredentials,
|
class TronWalletService extends WalletService<
|
||||||
TronRestoreWalletFromSeedCredentials, TronRestoreWalletFromPrivateKey> {
|
TronNewWalletCredentials,
|
||||||
|
TronRestoreWalletFromSeedCredentials,
|
||||||
|
TronRestoreWalletFromPrivateKey,
|
||||||
|
TronNewWalletCredentials> {
|
||||||
TronWalletService(this.walletInfoSource, {required this.client});
|
TronWalletService(this.walletInfoSource, {required this.client});
|
||||||
|
|
||||||
late TronClient client;
|
late TronClient client;
|
||||||
|
@ -145,4 +151,10 @@ class TronWalletService extends WalletService<TronNewWalletCredentials,
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>> restoreFromHardwareWallet(TronNewWalletCredentials credentials) {
|
||||||
|
// TODO: implement restoreFromHardwareWallet
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,6 +277,7 @@ Now you can run the codebase and successfully create a wallet for type walletX s
|
||||||
- Modify `getCredentialsFromRestoredWallet` method
|
- Modify `getCredentialsFromRestoredWallet` method
|
||||||
- Go to `lib/core/address_validator.dart`
|
- Go to `lib/core/address_validator.dart`
|
||||||
- Modify the `getAddressFromStringPattern` method to add a case for `WalletType.walletx`
|
- Modify the `getAddressFromStringPattern` method to add a case for `WalletType.walletx`
|
||||||
|
- and if it has tokens (ex. erc20, trc20, spl tokens) then add them to the switch case as well
|
||||||
- Add the scheme for walletx for both Android in `AndroidManifestBase.xml` and iOS in `InfoBase.plist`
|
- Add the scheme for walletx for both Android in `AndroidManifestBase.xml` and iOS in `InfoBase.plist`
|
||||||
|
|
||||||
**Transaction History**
|
**Transaction History**
|
||||||
|
@ -291,10 +292,10 @@ Now you can run the codebase and successfully create a wallet for type walletX s
|
||||||
# Points to note when adding the new wallet type
|
# Points to note when adding the new wallet type
|
||||||
|
|
||||||
1. if it has tokens (ex. ERC20, SPL, etc...) make sure to add that to this function `_checkIfCanSend` in `exchange_trade_view_model.dart`
|
1. if it has tokens (ex. ERC20, SPL, etc...) make sure to add that to this function `_checkIfCanSend` in `exchange_trade_view_model.dart`
|
||||||
|
1. if it has tokens (ex. ERC20, SPL, etc...) make sure to add a check for the tags as well in the
|
||||||
2. Check On/Off ramp providers that support the new wallet currency and add them accordingly in `provider_types.dart`
|
2. Check On/Off ramp providers that support the new wallet currency and add them accordingly in `provider_types.dart`
|
||||||
3. Add support for wallet uri scheme to restore from QR for both Android in `AndroidManifestBase.xml` and iOS in `InfoBase.plist`
|
3. Add support for wallet uri scheme to restore from QR for both Android in `AndroidManifestBase.xml` and iOS in `InfoBase.plist`
|
||||||
4. Make sure no imports are using the wallet internal package files directly, instead use the proxy layers that is created in the main lib `lib/cw_ethereum.dart` for example. (i.e try building Monero.com if you get compilation errors, then you probably missed something)
|
4. Make sure no imports are using the wallet internal package files directly, instead use the proxy layers that is created in the main lib `lib/cw_ethereum.dart` for example. (i.e try building Monero.com if you get compilation errors, then you probably missed something)
|
||||||
5.
|
|
||||||
|
|
||||||
|
|
||||||
Copyright (C) 2018-2023 Cake Labs LLC
|
Copyright (C) 2018-2023 Cake Labs LLC
|
||||||
|
|
|
@ -131,7 +131,12 @@ PODS:
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- permission_handler_apple (9.1.1):
|
- permission_handler_apple (9.1.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- Protobuf (3.25.3)
|
||||||
- ReachabilitySwift (5.0.0)
|
- ReachabilitySwift (5.0.0)
|
||||||
|
- reactive_ble_mobile (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- Protobuf (~> 3.5)
|
||||||
|
- SwiftProtobuf (~> 1.0)
|
||||||
- SDWebImage (5.18.11):
|
- SDWebImage (5.18.11):
|
||||||
- SDWebImage/Core (= 5.18.11)
|
- SDWebImage/Core (= 5.18.11)
|
||||||
- SDWebImage/Core (5.18.11)
|
- SDWebImage/Core (5.18.11)
|
||||||
|
@ -179,6 +184,7 @@ DEPENDENCIES:
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
|
- reactive_ble_mobile (from `.symlinks/plugins/reactive_ble_mobile/ios`)
|
||||||
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
|
- sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
|
||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
|
@ -196,6 +202,7 @@ SPEC REPOS:
|
||||||
- DKPhotoGallery
|
- DKPhotoGallery
|
||||||
- MTBBarcodeScanner
|
- MTBBarcodeScanner
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
|
- Protobuf
|
||||||
- ReachabilitySwift
|
- ReachabilitySwift
|
||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SwiftProtobuf
|
- SwiftProtobuf
|
||||||
|
@ -244,6 +251,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
|
reactive_ble_mobile:
|
||||||
|
:path: ".symlinks/plugins/reactive_ble_mobile/ios"
|
||||||
sensitive_clipboard:
|
sensitive_clipboard:
|
||||||
:path: ".symlinks/plugins/sensitive_clipboard/ios"
|
:path: ".symlinks/plugins/sensitive_clipboard/ios"
|
||||||
share_plus:
|
share_plus:
|
||||||
|
@ -286,7 +295,9 @@ SPEC CHECKSUMS:
|
||||||
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
||||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||||
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6
|
||||||
|
Protobuf: 8e9074797a13c484a79959fdb819ef4ae6da7dbe
|
||||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||||
|
reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c
|
||||||
SDWebImage: a3ba0b8faac7228c3c8eadd1a55c9c9fe5e16457
|
SDWebImage: a3ba0b8faac7228c3c8eadd1a55c9c9fe5e16457
|
||||||
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
|
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
|
||||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
9F46EE5E2BC11178009318F5 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||||
AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
AD0937B0140D5A4C24E73BEA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
@ -123,6 +125,7 @@
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||||
97C147021CF9000F007C117D /* Info.plist */,
|
97C147021CF9000F007C117D /* Info.plist */,
|
||||||
|
9F46EE5D2BC11178009318F5 /* PrivacyInfo.xcprivacy */,
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
|
@ -196,6 +199,7 @@
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||||
|
9F46EE5E2BC11178009318F5 /* PrivacyInfo.xcprivacy in Resources */,
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -210,16 +210,6 @@
|
||||||
<string>tron-wallet</string>
|
<string>tron-wallet</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>tron_wallet</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>tron_wallet</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
@ -238,6 +228,10 @@
|
||||||
<string>Enable Face ID for fast and secure access to wallets and private keys</string>
|
<string>Enable Face ID for fast and secure access to wallets and private keys</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<string>We need access to documents folder for getting access to open/save backup file</string>
|
<string>We need access to documents folder for getting access to open/save backup file</string>
|
||||||
|
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||||
|
<string>We need access to Bluetooth in order to connect to your hardware wallet when needed</string>
|
||||||
|
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||||
|
<string>We need access to Bluetooth in order to connect to your hardware wallet when needed</string>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>fetch</string>
|
<string>fetch</string>
|
||||||
|
|
24
ios/Runner/PrivacyInfo.xcprivacy
Normal file
24
ios/Runner/PrivacyInfo.xcprivacy
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<!--
|
||||||
|
PrivacyInfo.xcprivacy
|
||||||
|
|
||||||
|
|
||||||
|
Created by Omar Hatem on 06/04/2024.
|
||||||
|
Copyright (c) 2024 Cake Labs. All rights reserved.
|
||||||
|
-->
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPITypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>NSPrivacyAccessedAPIType</key>
|
||||||
|
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||||
|
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||||
|
<array>
|
||||||
|
<string>CA92.1</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -32,6 +32,14 @@ class CWBitcoin extends Bitcoin {
|
||||||
{required String name, WalletInfo? walletInfo}) =>
|
{required String name, WalletInfo? walletInfo}) =>
|
||||||
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo);
|
BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo);
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials createBitcoinHardwareWalletCredentials(
|
||||||
|
{required String name,
|
||||||
|
required HardwareAccountData accountData,
|
||||||
|
WalletInfo? walletInfo}) =>
|
||||||
|
BitcoinRestoreWalletFromHardware(
|
||||||
|
name: name, hwAccountData: accountData, walletInfo: walletInfo);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
|
TransactionPriority getMediumTransactionPriority() => BitcoinTransactionPriority.medium;
|
||||||
|
|
||||||
|
@ -292,7 +300,7 @@ class CWBitcoin extends Bitcoin {
|
||||||
return [
|
return [
|
||||||
DerivationInfo(
|
DerivationInfo(
|
||||||
derivationType: DerivationType.electrum,
|
derivationType: DerivationType.electrum,
|
||||||
derivationPath: "m/0'/0",
|
derivationPath: "m/0'",
|
||||||
description: "Electrum",
|
description: "Electrum",
|
||||||
scriptType: "p2wpkh",
|
scriptType: "p2wpkh",
|
||||||
)
|
)
|
||||||
|
@ -344,9 +352,6 @@ class CWBitcoin extends Bitcoin {
|
||||||
if (derivationDepth == 3) {
|
if (derivationDepth == 3) {
|
||||||
// we add "/0/0" so that we generate account 0, index 0 and correctly get balance
|
// we add "/0/0" so that we generate account 0, index 0 and correctly get balance
|
||||||
derivationPath += "/0/0";
|
derivationPath += "/0/0";
|
||||||
// we don't support sub-ACCOUNTS in bitcoin like we do monero, and so the path dInfoCopy
|
|
||||||
// expects should be ACCOUNT 0, index unspecified:
|
|
||||||
dInfoCopy.derivationPath = dInfoCopy.derivationPath! + "/0";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// var hd = bip32.BIP32.fromSeed(seedBytes).derivePath(derivationPath);
|
// var hd = bip32.BIP32.fromSeed(seedBytes).derivePath(derivationPath);
|
||||||
|
@ -440,4 +445,21 @@ class CWBitcoin extends Bitcoin {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 1.1).round();
|
return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 1.1).round();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device) {
|
||||||
|
(wallet as BitcoinWallet).setLedger(ledger, device);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM,
|
||||||
|
{int index = 0, int limit = 5}) async {
|
||||||
|
final hardwareWalletService = BitcoinHardwareWalletService(ledgerVM.ledger, ledgerVM.device);
|
||||||
|
try {
|
||||||
|
return hardwareWalletService.getAvailableAccounts(index: index, limit: limit);
|
||||||
|
} on LedgerException catch (err) {
|
||||||
|
print(err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||||
import 'package:cake_wallet/buy/order.dart';
|
import 'package:cake_wallet/buy/order.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@ -7,10 +8,12 @@ abstract class BuyProvider {
|
||||||
BuyProvider({
|
BuyProvider({
|
||||||
required this.wallet,
|
required this.wallet,
|
||||||
required this.isTestEnvironment,
|
required this.isTestEnvironment,
|
||||||
|
required this.ledgerVM,
|
||||||
});
|
});
|
||||||
|
|
||||||
final WalletBase wallet;
|
final WalletBase wallet;
|
||||||
final bool isTestEnvironment;
|
final bool isTestEnvironment;
|
||||||
|
final LedgerViewModel? ledgerVM;
|
||||||
|
|
||||||
String get title;
|
String get title;
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@ import 'dart:convert';
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -13,8 +15,8 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class DFXBuyProvider extends BuyProvider {
|
class DFXBuyProvider extends BuyProvider {
|
||||||
DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
DFXBuyProvider({required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
|
||||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
|
||||||
|
|
||||||
static const _baseUrl = 'api.dfx.swiss';
|
static const _baseUrl = 'api.dfx.swiss';
|
||||||
// static const _signMessagePath = '/v1/auth/signMessage';
|
// static const _signMessagePath = '/v1/auth/signMessage';
|
||||||
|
@ -91,7 +93,7 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Future<String> auth() async {
|
Future<String> auth() async {
|
||||||
final signMessage = getSignature(await getSignMessage());
|
final signMessage = await getSignature(await getSignMessage());
|
||||||
|
|
||||||
final requestBody = jsonEncode({
|
final requestBody = jsonEncode({
|
||||||
'wallet': walletName,
|
'wallet': walletName,
|
||||||
|
@ -118,7 +120,7 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getSignature(String message) {
|
Future<String> getSignature(String message) async {
|
||||||
switch (wallet.type) {
|
switch (wallet.type) {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
case WalletType.polygon:
|
case WalletType.polygon:
|
||||||
|
@ -135,6 +137,20 @@ class DFXBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
||||||
|
if (wallet.isHardwareWallet) {
|
||||||
|
if (!ledgerVM!.isConnected) {
|
||||||
|
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||||
|
arguments: ConnectDevicePageParams(
|
||||||
|
walletType: wallet.walletInfo.type,
|
||||||
|
onConnectDevice: (BuildContext context, LedgerViewModel ledgerVM) {
|
||||||
|
ledgerVM.setLedger(wallet);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
ledgerVM!.setLedger(wallet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final assetOut = this.assetOut;
|
final assetOut = this.assetOut;
|
||||||
final blockchain = this.blockchain;
|
final blockchain = this.blockchain;
|
||||||
|
|
|
@ -14,7 +14,6 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
@ -30,7 +29,7 @@ class MoonPayProvider extends BuyProvider {
|
||||||
}) : baseSellUrl = isTestEnvironment ? _baseSellTestUrl : _baseSellProductUrl,
|
}) : baseSellUrl = isTestEnvironment ? _baseSellTestUrl : _baseSellProductUrl,
|
||||||
baseBuyUrl = isTestEnvironment ? _baseBuyTestUrl : _baseBuyProductUrl,
|
baseBuyUrl = isTestEnvironment ? _baseBuyTestUrl : _baseBuyProductUrl,
|
||||||
this._settingsStore = settingsStore,
|
this._settingsStore = settingsStore,
|
||||||
super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
|
||||||
|
|
||||||
final SettingsStore _settingsStore;
|
final SettingsStore _settingsStore;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
class OnRamperBuyProvider extends BuyProvider {
|
class OnRamperBuyProvider extends BuyProvider {
|
||||||
OnRamperBuyProvider(this._settingsStore,
|
OnRamperBuyProvider(this._settingsStore,
|
||||||
{required WalletBase wallet, bool isTestEnvironment = false})
|
{required WalletBase wallet, bool isTestEnvironment = false})
|
||||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
|
||||||
|
|
||||||
static const _baseUrl = 'buy.onramper.com';
|
static const _baseUrl = 'buy.onramper.com';
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,11 @@ import 'dart:convert';
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -12,8 +15,8 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class RobinhoodBuyProvider extends BuyProvider {
|
class RobinhoodBuyProvider extends BuyProvider {
|
||||||
RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false, LedgerViewModel? ledgerVM})
|
||||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
: super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: ledgerVM);
|
||||||
|
|
||||||
static const _baseUrl = 'applink.robinhood.com';
|
static const _baseUrl = 'applink.robinhood.com';
|
||||||
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
|
||||||
|
@ -34,7 +37,7 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
|
|
||||||
String get _apiSecret => secrets.exchangeHelperApiKey;
|
String get _apiSecret => secrets.exchangeHelperApiKey;
|
||||||
|
|
||||||
String getSignature(String message) {
|
Future<String> getSignature(String message) {
|
||||||
switch (wallet.type) {
|
switch (wallet.type) {
|
||||||
case WalletType.ethereum:
|
case WalletType.ethereum:
|
||||||
case WalletType.polygon:
|
case WalletType.polygon:
|
||||||
|
@ -53,7 +56,7 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
final valid_until = (DateTime.now().millisecondsSinceEpoch / 1000).round() + 10;
|
final valid_until = (DateTime.now().millisecondsSinceEpoch / 1000).round() + 10;
|
||||||
final message = "$_apiSecret:${valid_until}";
|
final message = "$_apiSecret:${valid_until}";
|
||||||
|
|
||||||
final signature = getSignature(message);
|
final signature = await getSignature(message);
|
||||||
|
|
||||||
final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
|
final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
|
||||||
|
|
||||||
|
@ -84,6 +87,20 @@ class RobinhoodBuyProvider extends BuyProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
Future<void> launchProvider(BuildContext context, bool? isBuyAction) async {
|
||||||
|
if (wallet.isHardwareWallet) {
|
||||||
|
if (!ledgerVM!.isConnected) {
|
||||||
|
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||||
|
arguments: ConnectDevicePageParams(
|
||||||
|
walletType: wallet.walletInfo.type,
|
||||||
|
onConnectDevice: (BuildContext context, LedgerViewModel ledgerVM) {
|
||||||
|
ledgerVM.setLedger(wallet);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
ledgerVM!.setLedger(wallet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final uri = await requestProviderUrl();
|
final uri = await requestProviderUrl();
|
||||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
class WyreBuyProvider extends BuyProvider {
|
class WyreBuyProvider extends BuyProvider {
|
||||||
WyreBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
WyreBuyProvider({required WalletBase wallet, bool isTestEnvironment = false})
|
||||||
: baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl,
|
: baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl,
|
||||||
super(wallet: wallet, isTestEnvironment: isTestEnvironment);
|
super(wallet: wallet, isTestEnvironment: isTestEnvironment, ledgerVM: null);
|
||||||
|
|
||||||
static const _baseTestApiUrl = 'https://api.testwyre.com';
|
static const _baseTestApiUrl = 'https://api.testwyre.com';
|
||||||
static const _baseProductApiUrl = 'https://api.sendwyre.com';
|
static const _baseProductApiUrl = 'https://api.sendwyre.com';
|
||||||
|
|
|
@ -295,7 +295,7 @@ class AddressValidator extends TextValidator {
|
||||||
case CryptoCurrency.sol:
|
case CryptoCurrency.sol:
|
||||||
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
|
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
|
||||||
case CryptoCurrency.trx:
|
case CryptoCurrency.trx:
|
||||||
return '^(T|t)[1-9A-HJ-NP-Za-km-z]{33}\$';
|
return '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
|
||||||
default:
|
default:
|
||||||
if (type.tag == CryptoCurrency.eth.title) {
|
if (type.tag == CryptoCurrency.eth.title) {
|
||||||
return '0x[0-9a-zA-Z]{42}';
|
return '0x[0-9a-zA-Z]{42}';
|
||||||
|
@ -306,6 +306,9 @@ class AddressValidator extends TextValidator {
|
||||||
if (type.tag == CryptoCurrency.sol.title) {
|
if (type.tag == CryptoCurrency.sol.title) {
|
||||||
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
|
return '([^0-9a-zA-Z]|^)[1-9A-HJ-NP-Za-km-z]{43,44}([^0-9a-zA-Z]|\$)';
|
||||||
}
|
}
|
||||||
|
if (type.tag == CryptoCurrency.trx.title) {
|
||||||
|
return '(T|t)[1-9A-HJ-NP-Za-km-z]{33}';
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,4 +101,19 @@ class WalletCreationService {
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<WalletBase> restoreFromHardwareWallet(WalletCredentials credentials) async {
|
||||||
|
checkIfExists(credentials.name);
|
||||||
|
final password = generateWalletPassword();
|
||||||
|
credentials.password = password;
|
||||||
|
await keyService.saveWalletPassword(password: password, walletName: credentials.name);
|
||||||
|
final wallet = await _service!.restoreFromHardwareWallet(credentials);
|
||||||
|
|
||||||
|
if (wallet.type == WalletType.monero) {
|
||||||
|
await sharedPreferences.setBool(
|
||||||
|
PreferencesKey.moneroWalletUpdateV1Key(wallet.name), _isNewMoneroWalletPasswordUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
321
lib/di.dart
321
lib/di.dart
|
@ -1,237 +1,240 @@
|
||||||
|
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||||
import 'package:cake_wallet/anonpay/anonpay_api.dart';
|
import 'package:cake_wallet/anonpay/anonpay_api.dart';
|
||||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||||
|
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
||||||
|
import 'package:cake_wallet/anypay/anypay_api.dart';
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
|
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
import 'package:cake_wallet/buy/order.dart';
|
||||||
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
|
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||||
|
import 'package:cake_wallet/core/auth_service.dart';
|
||||||
|
import 'package:cake_wallet/core/backup_service.dart';
|
||||||
|
import 'package:cake_wallet/core/key_service.dart';
|
||||||
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
|
import 'package:cake_wallet/core/wallet_connect/wallet_connect_key_service.dart';
|
||||||
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
|
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
|
||||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
|
||||||
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
|
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
|
||||||
|
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||||
|
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||||
import 'package:cake_wallet/core/yat_service.dart';
|
import 'package:cake_wallet/core/yat_service.dart';
|
||||||
import 'package:cake_wallet/entities/background_tasks.dart';
|
import 'package:cake_wallet/entities/background_tasks.dart';
|
||||||
|
import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||||
|
import 'package:cake_wallet/entities/contact.dart';
|
||||||
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||||
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
|
||||||
import 'package:cake_wallet/tron/tron.dart';
|
import 'package:cake_wallet/tron/tron.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
||||||
import 'package:cw_core/receive_page_option.dart';
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
|
import 'package:cake_wallet/entities/template.dart';
|
||||||
|
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||||
import 'package:cake_wallet/ethereum/ethereum.dart';
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
import 'package:cake_wallet/nano/nano.dart';
|
import 'package:cake_wallet/exchange/exchange_template.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade.dart';
|
||||||
|
import 'package:cake_wallet/haven/haven.dart';
|
||||||
|
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
||||||
import 'package:cake_wallet/ionia/ionia_anypay.dart';
|
import 'package:cake_wallet/ionia/ionia_anypay.dart';
|
||||||
|
import 'package:cake_wallet/ionia/ionia_api.dart';
|
||||||
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
|
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
|
||||||
|
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||||
|
import 'package:cake_wallet/ionia/ionia_service.dart';
|
||||||
import 'package:cake_wallet/ionia/ionia_tip.dart';
|
import 'package:cake_wallet/ionia/ionia_tip.dart';
|
||||||
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
|
import 'package:cake_wallet/nano/nano.dart';
|
||||||
import 'package:cake_wallet/polygon/polygon.dart';
|
import 'package:cake_wallet/polygon/polygon.dart';
|
||||||
|
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/solana/solana.dart';
|
import 'package:cake_wallet/solana/solana.dart';
|
||||||
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
|
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
|
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
|
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart';
|
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
|
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
|
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
|
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/send/send_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
|
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
|
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/support/support_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
|
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
|
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
|
||||||
import 'package:cake_wallet/themes/theme_list.dart';
|
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
|
||||||
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
|
|
||||||
import 'package:cake_wallet/utils/payment_request.dart';
|
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
|
||||||
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart';
|
|
||||||
import 'package:cake_wallet/ionia/ionia_service.dart';
|
|
||||||
import 'package:cake_wallet/ionia/ionia_api.dart';
|
|
||||||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
|
||||||
import 'package:cake_wallet/haven/haven.dart';
|
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
|
||||||
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
|
||||||
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
|
|
||||||
import 'package:cw_core/erc20_token.dart';
|
|
||||||
import 'package:cw_core/nano_account.dart';
|
|
||||||
import 'package:cw_core/unspent_coins_info.dart';
|
|
||||||
import 'package:cake_wallet/core/backup_service.dart';
|
|
||||||
import 'package:cw_core/wallet_service.dart';
|
|
||||||
import 'package:cake_wallet/entities/biometric_auth.dart';
|
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
|
||||||
import 'package:cake_wallet/buy/order.dart';
|
|
||||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
|
||||||
import 'package:cw_core/transaction_info.dart';
|
|
||||||
import 'package:cake_wallet/entities/contact.dart';
|
|
||||||
import 'package:cw_core/node.dart';
|
|
||||||
import 'package:cake_wallet/exchange/trade.dart';
|
|
||||||
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/support/support_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
|
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
|
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
|
||||||
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
|
import 'package:cake_wallet/store/authentication_store.dart';
|
||||||
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||||
|
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
|
||||||
|
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||||
|
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
|
||||||
import 'package:cake_wallet/store/node_list_store.dart';
|
import 'package:cake_wallet/store/node_list_store.dart';
|
||||||
import 'package:cake_wallet/store/secret_store.dart';
|
import 'package:cake_wallet/store/secret_store.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/core/auth_service.dart';
|
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
|
||||||
import 'package:cake_wallet/core/key_service.dart';
|
import 'package:cake_wallet/store/templates/send_template_store.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/send/send_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
|
||||||
import 'package:cake_wallet/store/wallet_list_store.dart';
|
import 'package:cake_wallet/store/wallet_list_store.dart';
|
||||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_list.dart';
|
||||||
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/auth_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/backup_view_model.dart';
|
import 'package:cake_wallet/view_model/backup_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
|
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
|
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
|
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
|
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
|
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_custom_redeem_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/order_details_view_model.dart';
|
import 'package:cake_wallet/view_model/order_details_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/rescan_view_model.dart';
|
import 'package:cake_wallet/view_model/rescan_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
||||||
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
|
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
||||||
|
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
|
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/support_view_model.dart';
|
import 'package:cake_wallet/view_model/support_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
|
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart';
|
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
|
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
|
||||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/auth_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_restore_choose_derivation_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/receive_page_option.dart';
|
||||||
|
import 'package:cw_core/transaction_info.dart';
|
||||||
|
import 'package:cw_core/unspent_coins_info.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
||||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
|
||||||
import 'package:cake_wallet/store/authentication_store.dart';
|
|
||||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
|
||||||
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
|
|
||||||
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
|
|
||||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
|
||||||
import 'package:cake_wallet/store/templates/send_template_store.dart';
|
|
||||||
import 'package:cake_wallet/store/templates/exchange_template_store.dart';
|
|
||||||
import 'package:cake_wallet/entities/template.dart';
|
|
||||||
import 'package:cake_wallet/exchange/exchange_template.dart';
|
|
||||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
|
||||||
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
|
|
||||||
import 'package:cake_wallet/anypay/anypay_api.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
|
|
||||||
import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart';
|
|
||||||
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
|
||||||
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
|
||||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
|
||||||
|
|
||||||
import 'buy/dfx/dfx_buy_provider.dart';
|
import 'buy/dfx/dfx_buy_provider.dart';
|
||||||
import 'core/totp_request_details.dart';
|
import 'core/totp_request_details.dart';
|
||||||
|
@ -308,6 +311,7 @@ Future<void> setup({
|
||||||
getIt.registerFactory<Box<Node>>(() => _powNodeSource, instanceName: Node.boxName + "pow");
|
getIt.registerFactory<Box<Node>>(() => _powNodeSource, instanceName: Node.boxName + "pow");
|
||||||
|
|
||||||
getIt.registerSingleton(AuthenticationStore());
|
getIt.registerSingleton(AuthenticationStore());
|
||||||
|
getIt.registerSingleton(LedgerViewModel());
|
||||||
getIt.registerSingleton<WalletListStore>(WalletListStore());
|
getIt.registerSingleton<WalletListStore>(WalletListStore());
|
||||||
getIt.registerSingleton(NodeListStoreBase.instance);
|
getIt.registerSingleton(NodeListStoreBase.instance);
|
||||||
getIt.registerSingleton<SettingsStore>(settingsStore);
|
getIt.registerSingleton<SettingsStore>(settingsStore);
|
||||||
|
@ -367,6 +371,11 @@ Future<void> setup({
|
||||||
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type);
|
getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<WalletHardwareRestoreViewModel, WalletType, void>((type, _) =>
|
||||||
|
WalletHardwareRestoreViewModel(getIt.get<LedgerViewModel>(), getIt.get<AppStore>(),
|
||||||
|
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||||
|
type: type));
|
||||||
|
|
||||||
getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel(
|
getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel(
|
||||||
appStore: getIt.get<AppStore>(),
|
appStore: getIt.get<AppStore>(),
|
||||||
yatStore: getIt.get<YatStore>(),
|
yatStore: getIt.get<YatStore>(),
|
||||||
|
@ -490,12 +499,8 @@ Future<void> setup({
|
||||||
getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl());
|
getIt.registerLazySingleton<WalletConnectKeyService>(() => KeyServiceImpl());
|
||||||
|
|
||||||
getIt.registerLazySingleton<Web3WalletService>(() {
|
getIt.registerLazySingleton<Web3WalletService>(() {
|
||||||
final Web3WalletService web3WalletService = Web3WalletService(
|
final Web3WalletService web3WalletService = Web3WalletService(getIt.get<BottomSheetService>(),
|
||||||
getIt.get<BottomSheetService>(),
|
getIt.get<WalletConnectKeyService>(), appStore, getIt.get<SharedPreferences>());
|
||||||
getIt.get<WalletConnectKeyService>(),
|
|
||||||
appStore,
|
|
||||||
getIt.get<SharedPreferences>()
|
|
||||||
);
|
|
||||||
web3WalletService.create();
|
web3WalletService.create();
|
||||||
return web3WalletService;
|
return web3WalletService;
|
||||||
});
|
});
|
||||||
|
@ -599,6 +604,7 @@ Future<void> setup({
|
||||||
getIt.get<BalanceViewModel>(),
|
getIt.get<BalanceViewModel>(),
|
||||||
getIt.get<ContactListViewModel>(),
|
getIt.get<ContactListViewModel>(),
|
||||||
_transactionDescriptionBox,
|
_transactionDescriptionBox,
|
||||||
|
getIt.get<LedgerViewModel>(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -804,11 +810,11 @@ Future<void> setup({
|
||||||
editingNode: editingNode,
|
editingNode: editingNode,
|
||||||
isSelected: isSelected));
|
isSelected: isSelected));
|
||||||
|
|
||||||
getIt.registerFactory<RobinhoodBuyProvider>(
|
getIt.registerFactory<RobinhoodBuyProvider>(() => RobinhoodBuyProvider(
|
||||||
() => RobinhoodBuyProvider(wallet: getIt.get<AppStore>().wallet!));
|
wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>()));
|
||||||
|
|
||||||
getIt
|
getIt.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(
|
||||||
.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(wallet: getIt.get<AppStore>().wallet!));
|
wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>()));
|
||||||
|
|
||||||
getIt.registerFactory<MoonPayProvider>(() => MoonPayProvider(
|
getIt.registerFactory<MoonPayProvider>(() => MoonPayProvider(
|
||||||
settingsStore: getIt.get<AppStore>().settingsStore,
|
settingsStore: getIt.get<AppStore>().settingsStore,
|
||||||
|
@ -928,8 +934,17 @@ Future<void> setup({
|
||||||
transactionDetailsViewModel:
|
transactionDetailsViewModel:
|
||||||
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
||||||
|
|
||||||
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType), bool?>(
|
getIt.registerFactoryParam<NewWalletTypePage, void Function(BuildContext, WalletType),
|
||||||
(param1, isCreate) => NewWalletTypePage(onTypeSelected: param1, isCreate: isCreate ?? true));
|
List<bool>?>((param1, additionalParams) {
|
||||||
|
final isCreate = additionalParams?[0] ?? true;
|
||||||
|
final isHardwareWallet = additionalParams?[1] ?? false;
|
||||||
|
|
||||||
|
return NewWalletTypePage(
|
||||||
|
onTypeSelected: param1,
|
||||||
|
isCreate: isCreate,
|
||||||
|
isHardwareWallet: isHardwareWallet,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
getIt.registerFactoryParam<PreSeedPage, int, void>(
|
getIt.registerFactoryParam<PreSeedPage, int, void>(
|
||||||
(seedPhraseLength, _) => PreSeedPage(seedPhraseLength));
|
(seedPhraseLength, _) => PreSeedPage(seedPhraseLength));
|
||||||
|
|
|
@ -30,14 +30,23 @@ class CWEthereum extends Ethereum {
|
||||||
}) =>
|
}) =>
|
||||||
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials createEthereumHardwareWalletCredentials({
|
||||||
|
required String name,
|
||||||
|
required HardwareAccountData hwAccountData,
|
||||||
|
WalletInfo? walletInfo,
|
||||||
|
}) =>
|
||||||
|
EVMChainRestoreWalletFromHardware(
|
||||||
|
name: name, hwAccountData: hwAccountData, walletInfo: walletInfo);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address;
|
String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getPrivateKey(WalletBase wallet) {
|
String getPrivateKey(WalletBase wallet) {
|
||||||
final privateKeyHolder = (wallet as EthereumWallet).evmChainPrivateKey;
|
final privateKeyHolder = (wallet as EthereumWallet).evmChainPrivateKey;
|
||||||
String stringKey = bytesToHex(privateKeyHolder.privateKey);
|
if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
|
||||||
return stringKey;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -159,4 +168,24 @@ class CWEthereum extends Ethereum {
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device) {
|
||||||
|
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedger(
|
||||||
|
ledger,
|
||||||
|
device.connectionType == ConnectionType.usb ? device : null,
|
||||||
|
wallet.walletInfo.derivationInfo?.derivationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM,
|
||||||
|
{int index = 0, int limit = 5}) async {
|
||||||
|
final hardwareWalletService = EVMChainHardwareWalletService(ledgerVM.ledger, ledgerVM.device);
|
||||||
|
try {
|
||||||
|
return await hardwareWalletService.getAvailableAccounts(index: index, limit: limit);
|
||||||
|
} on LedgerException catch (err) {
|
||||||
|
print(err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,10 @@ Future<void> initializeAppConfigs() async {
|
||||||
CakeHive.registerAdapter(DerivationInfoAdapter());
|
CakeHive.registerAdapter(DerivationInfoAdapter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!CakeHive.isAdapterRegistered(HARDWARE_WALLET_TYPE_TYPE_ID)) {
|
||||||
|
CakeHive.registerAdapter(HardwareWalletTypeAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
|
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
|
||||||
CakeHive.registerAdapter(WalletTypeAdapter());
|
CakeHive.registerAdapter(WalletTypeAdapter());
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,23 @@ class CWPolygon extends Polygon {
|
||||||
}) =>
|
}) =>
|
||||||
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
EVMChainRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials createPolygonHardwareWalletCredentials({
|
||||||
|
required String name,
|
||||||
|
required HardwareAccountData hwAccountData,
|
||||||
|
WalletInfo? walletInfo,
|
||||||
|
}) =>
|
||||||
|
EVMChainRestoreWalletFromHardware(
|
||||||
|
name: name, hwAccountData: hwAccountData, walletInfo: walletInfo);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getAddress(WalletBase wallet) => (wallet as PolygonWallet).walletAddresses.address;
|
String getAddress(WalletBase wallet) => (wallet as PolygonWallet).walletAddresses.address;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getPrivateKey(WalletBase wallet) {
|
String getPrivateKey(WalletBase wallet) {
|
||||||
final privateKeyHolder = (wallet as PolygonWallet).evmChainPrivateKey;
|
final privateKeyHolder = (wallet as PolygonWallet).evmChainPrivateKey;
|
||||||
String stringKey = bytesToHex(privateKeyHolder.privateKey);
|
if (privateKeyHolder is EthPrivateKey) return bytesToHex(privateKeyHolder.privateKey);
|
||||||
return stringKey;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -157,4 +166,23 @@ class CWPolygon extends Polygon {
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
String getTokenAddress(CryptoCurrency asset) => (asset as Erc20Token).contractAddress;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setLedger(WalletBase wallet, Ledger ledger, LedgerDevice device) {
|
||||||
|
((wallet as EVMChainWallet).evmChainPrivateKey as EvmLedgerCredentials).setLedger(
|
||||||
|
ledger,
|
||||||
|
device.connectionType == ConnectionType.usb ? device : null,
|
||||||
|
wallet.walletInfo.derivationInfo?.derivationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<HardwareAccountData>> getHardwareWalletAccounts(LedgerViewModel ledgerVM,
|
||||||
|
{int index = 0, int limit = 5}) async {
|
||||||
|
final hardwareWalletService = EVMChainHardwareWalletService(ledgerVM.ledger, ledgerVM.device);
|
||||||
|
try {
|
||||||
|
return await hardwareWalletService.getAvailableAccounts(index: index, limit: limit);
|
||||||
|
} on LedgerException catch (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
198
lib/router.dart
198
lib/router.dart
|
@ -1,29 +1,74 @@
|
||||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||||
|
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
||||||
|
import 'package:cake_wallet/buy/order.dart';
|
||||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||||
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
|
import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart';
|
||||||
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
import 'package:cake_wallet/buy/order.dart';
|
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_nft_response.dart';
|
import 'package:cake_wallet/entities/wallet_nft_response.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
|
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
|
import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
import 'package:cake_wallet/src/screens/buy/webview_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
|
import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
|
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart';
|
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
|
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
|
import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/send/send_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
|
import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart';
|
||||||
|
@ -31,90 +76,49 @@ import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
|
import 'package:cake_wallet/src/screens/settings/tor_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_info_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support/support_page.dart';
|
import 'package:cake_wallet/src/screens/support/support_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
|
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
|
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
|
||||||
import 'package:cake_wallet/utils/payment_request.dart';
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
|
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
|
||||||
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
|
import 'package:cake_wallet/view_model/seed_type_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||||
|
import 'package:cake_wallet/wallet_types.g.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/nano_account.dart';
|
import 'package:cw_core/nano_account.dart';
|
||||||
|
import 'package:cw_core/node.dart';
|
||||||
|
import 'package:cw_core/transaction_info.dart';
|
||||||
import 'package:cw_core/wallet_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/di.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
|
||||||
import 'package:cake_wallet/exchange/trade.dart';
|
|
||||||
import 'package:cw_core/transaction_info.dart';
|
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/send/send_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:cake_wallet/wallet_types.g.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
|
|
||||||
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
|
||||||
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
|
||||||
import 'package:cw_core/node.dart';
|
|
||||||
|
|
||||||
import 'src/screens/dashboard/pages/nft_import_page.dart';
|
import 'src/screens/dashboard/pages/nft_import_page.dart';
|
||||||
|
|
||||||
|
@ -151,7 +155,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
||||||
final seedTypeViewModel = getIt.get<SeedTypeViewModel>();
|
final seedTypeViewModel = getIt.get<SeedTypeViewModel>();
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(builder: (_) => NewWalletPage(walletNewVM, seedTypeViewModel));
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => NewWalletPage(walletNewVM, seedTypeViewModel));
|
||||||
|
|
||||||
|
case Routes.chooseHardwareWalletAccount:
|
||||||
|
final arguments = settings.arguments as List<dynamic>;
|
||||||
|
final type = arguments[0] as WalletType;
|
||||||
|
final walletVM = getIt.get<WalletHardwareRestoreViewModel>(param1: type);
|
||||||
|
|
||||||
|
return CupertinoPageRoute<void>(builder: (_) => SelectHardwareWalletAccountPage(walletVM));
|
||||||
|
|
||||||
case Routes.setupPin:
|
case Routes.setupPin:
|
||||||
Function(PinCodeState<PinCodeWidget>, String)? callback;
|
Function(PinCodeState<PinCodeWidget>, String)? callback;
|
||||||
|
@ -168,7 +180,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
param1: (BuildContext context, WalletType type) =>
|
param1: (BuildContext context, WalletType type) =>
|
||||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||||
param2: false));
|
param2: [false, false]));
|
||||||
|
|
||||||
case Routes.restoreOptions:
|
case Routes.restoreOptions:
|
||||||
final isNewInstall = settings.arguments as bool;
|
final isNewInstall = settings.arguments as bool;
|
||||||
|
@ -199,7 +211,46 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
builder: (_) => getIt.get<NewWalletTypePage>(
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
param1: (BuildContext context, WalletType type) =>
|
param1: (BuildContext context, WalletType type) =>
|
||||||
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
|
||||||
param2: false));
|
param2: [false, false]));
|
||||||
|
}
|
||||||
|
|
||||||
|
case Routes.restoreWalletFromHardwareWallet:
|
||||||
|
final isNewInstall = settings.arguments as bool;
|
||||||
|
|
||||||
|
if (isNewInstall) {
|
||||||
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<SetupPinCodePage>(
|
||||||
|
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
|
||||||
|
Navigator.of(context.context).pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: false),
|
||||||
|
),
|
||||||
|
fullscreenDialog: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isSingleCoin) {
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
builder: (_) => ConnectDevicePage(
|
||||||
|
ConnectDevicePageParams(
|
||||||
|
walletType: availableWalletTypes.first,
|
||||||
|
onConnectDevice: (BuildContext context, _) =>
|
||||||
|
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
|
||||||
|
arguments: [availableWalletTypes.first]),
|
||||||
|
),
|
||||||
|
getIt.get<LedgerViewModel>(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
return CupertinoPageRoute<void>(
|
||||||
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
|
param1: (BuildContext context, WalletType type) {
|
||||||
|
final arguments = ConnectDevicePageParams(
|
||||||
|
walletType: type,
|
||||||
|
onConnectDevice: (BuildContext context, _) =>
|
||||||
|
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
|
||||||
|
arguments: [type]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Navigator.of(context).pushNamed(Routes.connectDevices, arguments: arguments);
|
||||||
|
},
|
||||||
|
param2: [false, true]));
|
||||||
}
|
}
|
||||||
|
|
||||||
case Routes.restoreWalletTypeFromQR:
|
case Routes.restoreWalletTypeFromQR:
|
||||||
|
@ -400,8 +451,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.buySellPage:
|
case Routes.buySellPage:
|
||||||
final args = settings.arguments as bool;
|
final args = settings.arguments as bool;
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<BuySellOptionsPage>(param1: args));
|
||||||
builder: (_) => getIt.get<BuySellOptionsPage>(param1: args));
|
|
||||||
|
|
||||||
case Routes.buyWebView:
|
case Routes.buyWebView:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
|
@ -424,8 +474,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.preSeedPage:
|
case Routes.preSeedPage:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) => getIt.get<PreSeedPage>(
|
builder: (_) => getIt.get<PreSeedPage>(param1: settings.arguments as int));
|
||||||
param1: settings.arguments as int));
|
|
||||||
|
|
||||||
case Routes.backup:
|
case Routes.backup:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
@ -647,6 +696,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
case Routes.torPage:
|
case Routes.torPage:
|
||||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
|
return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
|
||||||
|
|
||||||
|
case Routes.connectDevices:
|
||||||
|
final params = settings.arguments as ConnectDevicePageParams;
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
builder: (_) => ConnectDevicePage(params, getIt.get<LedgerViewModel>()));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) => Scaffold(
|
builder: (_) => Scaffold(
|
||||||
|
|
|
@ -6,9 +6,11 @@ class Routes {
|
||||||
static const seed = '/seed';
|
static const seed = '/seed';
|
||||||
static const restoreOptions = '/restore_options';
|
static const restoreOptions = '/restore_options';
|
||||||
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
|
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
|
||||||
|
static const restoreWalletFromHardwareWallet = '/restore/hardware_wallet';
|
||||||
static const restoreWalletTypeFromQR = '/restore_wallet_from_qr_code';
|
static const restoreWalletTypeFromQR = '/restore_wallet_from_qr_code';
|
||||||
static const restoreWalletChooseDerivation =
|
static const restoreWalletChooseDerivation =
|
||||||
'/restore_wallet_choose_derivation';
|
'/restore_wallet_choose_derivation';
|
||||||
|
static const chooseHardwareWalletAccount = '/restore/hardware_wallet/accounts';
|
||||||
static const dashboard = '/dashboard';
|
static const dashboard = '/dashboard';
|
||||||
static const send = '/send';
|
static const send = '/send';
|
||||||
static const transactionDetails = '/transaction_info';
|
static const transactionDetails = '/transaction_info';
|
||||||
|
@ -107,4 +109,5 @@ class Routes {
|
||||||
static const nftDetailsPage = '/nft_details_page';
|
static const nftDetailsPage = '/nft_details_page';
|
||||||
static const importNFTPage = '/import_nft_page';
|
static const importNFTPage = '/import_nft_page';
|
||||||
static const torPage = '/tor_page';
|
static const torPage = '/tor_page';
|
||||||
|
static const connectDevices = '/device/connect';
|
||||||
}
|
}
|
||||||
|
|
221
lib/src/screens/connect_device/connect_device_page.dart
Normal file
221
lib/src/screens/connect_device/connect_device_page.dart
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/generated/i18n.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/themes/extensions/cake_text_theme.dart';
|
||||||
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
typedef OnConnectDevice = void Function(BuildContext, LedgerViewModel);
|
||||||
|
|
||||||
|
class ConnectDevicePageParams {
|
||||||
|
final WalletType walletType;
|
||||||
|
final OnConnectDevice onConnectDevice;
|
||||||
|
|
||||||
|
ConnectDevicePageParams({required this.walletType, required this.onConnectDevice});
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectDevicePage extends BasePage {
|
||||||
|
final WalletType walletType;
|
||||||
|
final OnConnectDevice onConnectDevice;
|
||||||
|
final LedgerViewModel ledgerVM;
|
||||||
|
|
||||||
|
ConnectDevicePage(ConnectDevicePageParams params, this.ledgerVM)
|
||||||
|
: walletType = params.walletType,
|
||||||
|
onConnectDevice = params.onConnectDevice;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => S.current.restore_title_from_hardware_wallet;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) => ConnectDevicePageBody(walletType, onConnectDevice, ledgerVM);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectDevicePageBody extends StatefulWidget {
|
||||||
|
final WalletType walletType;
|
||||||
|
final OnConnectDevice onConnectDevice;
|
||||||
|
final LedgerViewModel ledgerVM;
|
||||||
|
|
||||||
|
const ConnectDevicePageBody(this.walletType, this.onConnectDevice, this.ledgerVM);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConnectDevicePageBodyState createState() => ConnectDevicePageBodyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||||
|
final imageLedger = 'assets/images/ledger_nano.png';
|
||||||
|
|
||||||
|
final ledger = Ledger(
|
||||||
|
options: LedgerOptions(
|
||||||
|
scanMode: ScanMode.balanced,
|
||||||
|
maxScanDuration: const Duration(minutes: 5),
|
||||||
|
),
|
||||||
|
onPermissionRequest: (_) async {
|
||||||
|
Map<Permission, PermissionStatus> statuses = await [
|
||||||
|
Permission.bluetoothScan,
|
||||||
|
Permission.bluetoothConnect,
|
||||||
|
Permission.bluetoothAdvertise,
|
||||||
|
].request();
|
||||||
|
|
||||||
|
return statuses.values.where((status) => status.isDenied).isEmpty;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
var bleIsEnabled = true;
|
||||||
|
var bleDevices = <LedgerDevice>[];
|
||||||
|
var usbDevices = <LedgerDevice>[];
|
||||||
|
|
||||||
|
late Timer? _usbRefreshTimer = null;
|
||||||
|
late Timer? _bleRefreshTimer = null;
|
||||||
|
late StreamSubscription<LedgerDevice>? _bleRefresh = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
Future.delayed(
|
||||||
|
Duration(seconds: 1),
|
||||||
|
() => _bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device))),
|
||||||
|
);
|
||||||
|
// _bleRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshBleDevices());
|
||||||
|
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
_usbRefreshTimer = Timer.periodic(Duration(seconds: 1), (_) => _refreshUsbDevices());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_bleRefreshTimer?.cancel();
|
||||||
|
_usbRefreshTimer?.cancel();
|
||||||
|
_bleRefresh?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _refreshUsbDevices() async {
|
||||||
|
final dev = await ledger.listUsbDevices();
|
||||||
|
if (usbDevices.length != dev.length) setState(() => usbDevices = dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _refreshBleDevices() async {
|
||||||
|
final isBleEnabled = await Permission.bluetooth.serviceStatus.isEnabled;
|
||||||
|
|
||||||
|
setState(() => bleIsEnabled = isBleEnabled);
|
||||||
|
|
||||||
|
if (isBleEnabled) {
|
||||||
|
_bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device)));
|
||||||
|
_bleRefreshTimer?.cancel();
|
||||||
|
_bleRefreshTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _connectToDevice(LedgerDevice device) async {
|
||||||
|
await widget.ledgerVM.connectLedger(device);
|
||||||
|
widget.onConnectDevice(context, widget.ledgerVM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Container(
|
||||||
|
width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
|
||||||
|
height: double.infinity,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 24),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||||
|
child: Text(
|
||||||
|
Platform.isIOS
|
||||||
|
? S.of(context).connect_your_hardware_wallet_ios
|
||||||
|
: S.of(context).connect_your_hardware_wallet,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (!bleIsEnabled)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
|
||||||
|
child: Text(
|
||||||
|
S.of(context).ledger_please_enable_bluetooth,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
...bleDevices
|
||||||
|
.map(
|
||||||
|
(device) => Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: 20),
|
||||||
|
child: DeviceTile(
|
||||||
|
onPressed: () => _connectToDevice(device),
|
||||||
|
title: device.name,
|
||||||
|
leading: imageLedger,
|
||||||
|
connectionType: device.connectionType,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.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: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
...usbDevices
|
||||||
|
.map(
|
||||||
|
(device) => Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: 20),
|
||||||
|
child: DeviceTile(
|
||||||
|
onPressed: () => _connectToDevice(device),
|
||||||
|
title: device.name,
|
||||||
|
leading: imageLedger,
|
||||||
|
connectionType: device.connectionType,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
212
lib/src/screens/connect_device/debug_device_page.dart
Normal file
212
lib/src/screens/connect_device/debug_device_page.dart
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
// import 'dart:convert';
|
||||||
|
//
|
||||||
|
// 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/primary_button.dart';
|
||||||
|
// import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
// import 'package:convert/convert.dart';
|
||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'package:ledger_bitcoin/ledger_bitcoin.dart';
|
||||||
|
// import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
// import 'package:permission_handler/permission_handler.dart';
|
||||||
|
// import 'package:polyseed/polyseed.dart';
|
||||||
|
//
|
||||||
|
// class DebugDevicePage extends BasePage {
|
||||||
|
// @override
|
||||||
|
// String get title => "Connect Ledger";
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget body(BuildContext context) => DebugDevicePageBody();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// class DebugDevicePageBody extends StatefulWidget {
|
||||||
|
// @override
|
||||||
|
// DebugDevicePageBodyState createState() => DebugDevicePageBodyState();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// class DebugDevicePageBodyState extends State<DebugDevicePageBody> {
|
||||||
|
// final imageLedger = Image.asset(
|
||||||
|
// 'assets/images/ledger_icon_black.png',
|
||||||
|
// width: 40,
|
||||||
|
// );
|
||||||
|
// final ledger = Ledger(
|
||||||
|
// options: LedgerOptions(
|
||||||
|
// scanMode: ScanMode.balanced,
|
||||||
|
// maxScanDuration: const Duration(milliseconds: 5000),
|
||||||
|
// ),
|
||||||
|
// onPermissionRequest: (status) async {
|
||||||
|
// Map<Permission, PermissionStatus> statuses = await [
|
||||||
|
// // Permission.location,
|
||||||
|
// Permission.bluetoothScan,
|
||||||
|
// Permission.bluetoothConnect,
|
||||||
|
// Permission.bluetoothAdvertise,
|
||||||
|
// ].request();
|
||||||
|
//
|
||||||
|
// if (status != BleStatus.ready) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return statuses.values.where((status) => status.isDenied).isEmpty;
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// late BitcoinLedgerApp btc;
|
||||||
|
// var devices = <LedgerDevice>[];
|
||||||
|
// var status = "";
|
||||||
|
// var counter = 0;
|
||||||
|
// LedgerDevice? selectedDevice = null;
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// void initState() {
|
||||||
|
// super.initState();
|
||||||
|
// btc = BitcoinLedgerApp(ledger);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// void dispose() {
|
||||||
|
// super.dispose();
|
||||||
|
// ledger.close(ConnectionType.ble);
|
||||||
|
// ledger.close(ConnectionType.usb);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> reconnectCurrentDevice() async {
|
||||||
|
// // await ledger.disconnect(selectedDevice!);
|
||||||
|
// // await ledger.connect(selectedDevice!);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Future<void> disconnectCurrentDevice() async {
|
||||||
|
// await ledger.disconnect(selectedDevice!);
|
||||||
|
// setState(() => selectedDevice = null);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// final imageLedger = 'assets/images/ledger_nano.png';
|
||||||
|
//
|
||||||
|
// return Center(
|
||||||
|
// child: Container(
|
||||||
|
// width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
|
||||||
|
// height: double.infinity,
|
||||||
|
// padding: EdgeInsets.symmetric(vertical: 24, horizontal: 24),
|
||||||
|
// child: SingleChildScrollView(
|
||||||
|
// child: Column(
|
||||||
|
// children: [
|
||||||
|
// Padding(
|
||||||
|
// padding: EdgeInsets.only(top: 20),
|
||||||
|
// child: Text(status),
|
||||||
|
// ),
|
||||||
|
// if (selectedDevice != null) ...[
|
||||||
|
// DebugButton(
|
||||||
|
// title: "Get Version",
|
||||||
|
// method: "Version",
|
||||||
|
// func: () async => await btc.getVersion(selectedDevice!),
|
||||||
|
// ),
|
||||||
|
// DebugButton(
|
||||||
|
// title: "Get Master Fingerprint",
|
||||||
|
// method: "Master Fingerprint",
|
||||||
|
// func: () async => hex.encode(await btc.getMasterFingerprint(selectedDevice!)),
|
||||||
|
// ),
|
||||||
|
// DebugButton(
|
||||||
|
// title: "Get XPub",
|
||||||
|
// method: "XPub",
|
||||||
|
// func: () async => await btc.getXPubKey(selectedDevice!, derivationPath: "m/84'/0'/$counter'"),
|
||||||
|
// ),
|
||||||
|
// DebugButton(
|
||||||
|
// title: "Get Wallet Address",
|
||||||
|
// method: "Wallet Address",
|
||||||
|
// func: () async {
|
||||||
|
// setState(() => counter++);
|
||||||
|
// final derivationPath = "m/84'/0'/$counter'/0/0";
|
||||||
|
// return await btc.getAccounts(selectedDevice!, accountsDerivationPath: derivationPath);
|
||||||
|
// // return await ethereum!.getHardwareWalletAccounts(selectedDevice!);
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// DebugButton(
|
||||||
|
// title: "Send Money",
|
||||||
|
// method: "Sig",
|
||||||
|
// func: () async {
|
||||||
|
// final psbt = PsbtV2();
|
||||||
|
// final psbtBuf = base64.decode(
|
||||||
|
// "cHNidP8BAgQCAAAAAQQBAQEFAQIAAQ4gTW6k/cwKKu1u7m9oKr5ob7VcAC0IPkfaDitRi/FkD7sBDwQAAAAAARAE/////wEA/ekBAQAAAAABA9AYVQLI722H0osKMa/4dvMucrnKV1Myxtlp0l0BoOBDAQAAAAD/////ku6r2ABaHt9N26f/P4eMljX8t1f4lBcFfEwuNm/uXYoBAAAAAP////+YeAl8arEGKOcyrWJAYwSboyCstkhHN8zn7/vy7pkYTAEAAAAA/////wHlHgAAAAAAABYAFKdq0umSucBGVkl2MpT6Hgo/0a/xAkcwRAIgMkiJmNFbEi2I3CQYOwyV/JepCnFQRvj4xghkySpFcJMCIGAypkkWltfj+ucvqUIu27tusDAIAAB+rBhX/GV7hPlEASEDyLmWyTLjLfC9kn8pnW42jW5N6EJo5fObjWWEyfLDu9UCSDBFAiEAg9crVtwBPF+sWk+Th6pLwzDjJGItwsUCvoBPtmMTEb4CIDGuM7WOguV0TP21oidF3bSUZlEAjUHWfWzxLKw+3LofASEDfN16xKb70UZSeQyX5Tlh8iRq7np5Nlz9GYdcSU50sKwCSDBFAiEAvotOblaEiBptRWkvb6bj2MGyRjTphKLBLiHYmrRMTCgCIEKJH+z65uPSSz1NIb0d/u3bU9l0xcWk0idEsXjB+BIiASEDrAEiEtrSNKxbh6F/KPaCTafF2LVjCzb75WB+x4xSuoQAAAAAAQEf5R4AAAAAAAAWABSnatLpkrnARlZJdjKU+h4KP9Gv8SIGA3xMuxmPsBAm9aMEUBs3N46DB+Kdts3bZR/Wxt+uM0H4GKtN6bpUAACAAAAAgAAAAIAAAAAAAAAAAAABBBTk7bEOxYcdXDi1eeWraYDufm6eJgEDCOgDAAAAAAAAAAEEFDX3g/pnDXIfsRw8shK42NZn+SdpAQMIiBMAAAAAAAAiAgN8TLsZj7AQJvWjBFAbNzeOgwfinbbN22Uf1sbfrjNB+BirTem6VAAAgAAAAIAAAACAAAAAAAAAAAAA"
|
||||||
|
// );
|
||||||
|
// psbt.deserialize(psbtBuf);
|
||||||
|
// final result = await btc.signPsbt(selectedDevice!, psbt: psbt);
|
||||||
|
// return result.toHexString();
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// Padding(
|
||||||
|
// padding: EdgeInsets.only(top: 20),
|
||||||
|
// child: PrimaryButton(
|
||||||
|
// text: "Disconnect",
|
||||||
|
// onPressed: () => disconnectCurrentDevice(),
|
||||||
|
// color: Theme.of(context).primaryColor,
|
||||||
|
// textColor: Colors.white),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// if (selectedDevice == null) ...[
|
||||||
|
// ...devices
|
||||||
|
// .map(
|
||||||
|
// (device) => Padding(
|
||||||
|
// padding: EdgeInsets.only(bottom: 20),
|
||||||
|
// child: DeviceTile(
|
||||||
|
// onPressed: () {
|
||||||
|
// setState(() => selectedDevice = device);
|
||||||
|
// ledger.connect(device);
|
||||||
|
// },
|
||||||
|
// title: device.name,
|
||||||
|
// leading: imageLedger,
|
||||||
|
// connectionType: device.connectionType,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// .toList(),
|
||||||
|
// PrimaryButton(
|
||||||
|
// text: "Refresh BLE",
|
||||||
|
// onPressed: () async {
|
||||||
|
// setState(() => devices = []);
|
||||||
|
// ledger.scan().listen((device) => setState(() {
|
||||||
|
// devices.add(device);
|
||||||
|
// }));
|
||||||
|
// },
|
||||||
|
// color: Theme.of(context).primaryColor,
|
||||||
|
// textColor: Colors.white),
|
||||||
|
// Padding(
|
||||||
|
// padding: EdgeInsets.only(top: 20),
|
||||||
|
// child: PrimaryButton(
|
||||||
|
// text: "Use USB",
|
||||||
|
// onPressed: () async {
|
||||||
|
// final dev = await ledger.listUsbDevices();
|
||||||
|
// setState(() => devices = dev);
|
||||||
|
// },
|
||||||
|
// color: Theme.of(context).primaryColor,
|
||||||
|
// textColor: Colors.white),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// )),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Widget DebugButton(
|
||||||
|
// {required String title, required String method, required Future<dynamic> Function() func}) {
|
||||||
|
// return Padding(
|
||||||
|
// padding: EdgeInsets.only(top: 20),
|
||||||
|
// child: PrimaryButton(
|
||||||
|
// text: title,
|
||||||
|
// onPressed: () async {
|
||||||
|
// try {
|
||||||
|
// setState(() => status = "Sending...");
|
||||||
|
// final acc = await func();
|
||||||
|
// setState(() => status = "$method: $acc");
|
||||||
|
// print("$method: $acc");
|
||||||
|
// } on LedgerException catch (ex) {
|
||||||
|
// setState(() => status = "${ex.errorCode.toRadixString(16)} ${ex.message}");
|
||||||
|
// print("${ex.errorCode.toRadixString(16)} ${ex.message}");
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// color: Theme.of(context).primaryColor,
|
||||||
|
// textColor: Colors.white),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -0,0 +1,256 @@
|
||||||
|
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||||
|
import 'package:cake_wallet/entities/generate_name.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.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/widgets/alert_with_one_action.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||||
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
|
class SelectHardwareWalletAccountPage extends BasePage {
|
||||||
|
SelectHardwareWalletAccountPage(this._walletHardwareRestoreVM);
|
||||||
|
|
||||||
|
final WalletHardwareRestoreViewModel _walletHardwareRestoreVM;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => S.current.restore_title_from_hardware_wallet;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) => SelectHardwareWalletAccountForm(_walletHardwareRestoreVM);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectHardwareWalletAccountForm extends StatefulWidget {
|
||||||
|
SelectHardwareWalletAccountForm(this._walletHardwareRestoreVM);
|
||||||
|
|
||||||
|
final WalletHardwareRestoreViewModel _walletHardwareRestoreVM;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SelectHardwareWalletAccountFormState createState() =>
|
||||||
|
_SelectHardwareWalletAccountFormState(_walletHardwareRestoreVM);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAccountForm> {
|
||||||
|
_SelectHardwareWalletAccountFormState(this._walletHardwareRestoreVM)
|
||||||
|
: _formKey = GlobalKey<FormState>(),
|
||||||
|
_controller = TextEditingController();
|
||||||
|
|
||||||
|
final GlobalKey<FormState> _formKey;
|
||||||
|
final WalletHardwareRestoreViewModel _walletHardwareRestoreVM;
|
||||||
|
final TextEditingController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_setEffects(context);
|
||||||
|
if (_walletHardwareRestoreVM.availableAccounts.length == 0) _loadMoreAccounts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(top: 24),
|
||||||
|
child: ScrollableWithBottomSection(
|
||||||
|
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||||
|
content: Center(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints:
|
||||||
|
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 0),
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
onChanged: (value) => _walletHardwareRestoreVM.name = value,
|
||||||
|
controller: _controller,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor,
|
||||||
|
),
|
||||||
|
hintText: S.of(context).wallet_name,
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
suffixIcon: Semantics(
|
||||||
|
label: S.of(context).generate_name,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final rName = await generateName();
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_controller.text = rName;
|
||||||
|
_walletHardwareRestoreVM.name = rName;
|
||||||
|
_controller.selection = TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: _controller.text.length));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(6.0),
|
||||||
|
color: Theme.of(context).hintColor,
|
||||||
|
),
|
||||||
|
width: 34,
|
||||||
|
height: 34,
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/refresh_icon.png',
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<SendPageTheme>()!
|
||||||
|
.textFieldButtonIconColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: WalletNameValidator(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 20),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
child: Text(
|
||||||
|
"Available accounts",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Observer(
|
||||||
|
builder: (context) => Column(
|
||||||
|
children: _walletHardwareRestoreVM.availableAccounts.map((acc) {
|
||||||
|
final address = acc.address;
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(top: 10),
|
||||||
|
child: SelectButton(
|
||||||
|
image: Image.asset(
|
||||||
|
walletTypeToCryptoCurrency(_walletHardwareRestoreVM.type).iconPath ??
|
||||||
|
'',
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
text:
|
||||||
|
"${address.substring(0, 6)}...${address.substring(address.length - 6)}",
|
||||||
|
showTrailingIcon: false,
|
||||||
|
height: 54,
|
||||||
|
isSelected: _walletHardwareRestoreVM.selectedAccount == acc,
|
||||||
|
onTap: () =>
|
||||||
|
setState(() => _walletHardwareRestoreVM.selectedAccount = acc),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 10),
|
||||||
|
child: Observer(builder: (context) {
|
||||||
|
return LoadingPrimaryButton(
|
||||||
|
onPressed: _loadMoreAccounts,
|
||||||
|
text: S.of(context).load_more,
|
||||||
|
color: Theme.of(context).primaryColor,
|
||||||
|
textColor: Colors.white,
|
||||||
|
isLoading: _walletHardwareRestoreVM.isLoadingMoreAccounts,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomSectionPadding: EdgeInsets.all(24),
|
||||||
|
bottomSection: Observer(
|
||||||
|
builder: (context) {
|
||||||
|
return LoadingPrimaryButton(
|
||||||
|
onPressed: _confirmForm,
|
||||||
|
text: S.of(context).seed_language_next,
|
||||||
|
color: Colors.green,
|
||||||
|
textColor: Colors.white,
|
||||||
|
isDisabled: _walletHardwareRestoreVM.name.isEmpty,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadMoreAccounts() async {
|
||||||
|
_walletHardwareRestoreVM.isLoadingMoreAccounts = true;
|
||||||
|
_walletHardwareRestoreVM.getNextAvailableAccounts(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _confirmForm() async {
|
||||||
|
await _walletHardwareRestoreVM.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _effectsInstalled = false;
|
||||||
|
|
||||||
|
void _setEffects(BuildContext context) {
|
||||||
|
if (_effectsInstalled) return;
|
||||||
|
|
||||||
|
reaction((_) => _walletHardwareRestoreVM.error, (String? error) {
|
||||||
|
|
||||||
|
if (error != null) {
|
||||||
|
|
||||||
|
if (error == S.current.ledger_connection_error)
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).error,
|
||||||
|
alertContent: error,
|
||||||
|
buttonText: S.of(context).ok,
|
||||||
|
buttonAction: () {
|
||||||
|
_walletHardwareRestoreVM.error = null;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_effectsInstalled = true;
|
||||||
|
}
|
||||||
|
}
|
78
lib/src/screens/connect_device/widgets/device_tile.dart
Normal file
78
lib/src/screens/connect_device/widgets/device_tile.dart
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
|
||||||
|
class DeviceTile extends StatelessWidget {
|
||||||
|
const DeviceTile({
|
||||||
|
required this.onPressed,
|
||||||
|
required this.title,
|
||||||
|
this.leading,
|
||||||
|
this.connectionType,
|
||||||
|
});
|
||||||
|
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final String title;
|
||||||
|
final String? leading;
|
||||||
|
final ConnectionType? connectionType;
|
||||||
|
|
||||||
|
String? get connectionTypeIcon {
|
||||||
|
switch (connectionType) {
|
||||||
|
case ConnectionType.ble:
|
||||||
|
return 'assets/images/bluetooth.png';
|
||||||
|
case ConnectionType.usb:
|
||||||
|
return 'assets/images/usb.png';
|
||||||
|
case null:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onPressed,
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: EdgeInsets.all(24),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
|
color: Theme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
if (leading != null)
|
||||||
|
Image.asset(
|
||||||
|
leading!,
|
||||||
|
height: 30,
|
||||||
|
color: Theme.of(context).extension<OptionTileTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(left: 16),
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).extension<OptionTileTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (connectionTypeIcon != null)
|
||||||
|
Center(
|
||||||
|
child: Image.asset(
|
||||||
|
connectionTypeIcon!,
|
||||||
|
height: 25,
|
||||||
|
color: Theme.of(context).extension<OptionTileTheme>()!.titleColor,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ class SideMenuItem extends StatelessWidget {
|
||||||
? Icon(
|
? Icon(
|
||||||
icon,
|
icon,
|
||||||
color: _setColor(context),
|
color: _setColor(context),
|
||||||
|
size: 30,
|
||||||
)
|
)
|
||||||
: Image.asset(
|
: Image.asset(
|
||||||
imagePath ?? '',
|
imagePath ?? '',
|
||||||
|
|
|
@ -82,7 +82,8 @@ class DesktopSidebarWrapper extends BasePage {
|
||||||
width: sideMenuWidth,
|
width: sideMenuWidth,
|
||||||
topItems: [
|
topItems: [
|
||||||
SideMenuItem(
|
SideMenuItem(
|
||||||
imagePath: 'assets/images/wallet_outline.png',
|
// imagePath: 'assets/images/wallet_outline.png',
|
||||||
|
icon: Icons.home,
|
||||||
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard,
|
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
desktopSidebarViewModel.onPageChange(SidebarItem.dashboard);
|
desktopSidebarViewModel.onPageChange(SidebarItem.dashboard);
|
||||||
|
|
|
@ -148,6 +148,17 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
if (dashboardViewModel.wallet.isHardwareWallet)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/ledger_nano.png',
|
||||||
|
width: 24,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<DashboardPageTheme>()!
|
||||||
|
.pageTitleTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (dashboardViewModel
|
if (dashboardViewModel
|
||||||
.balanceViewModel.isHomeScreenSettingsEnabled)
|
.balanceViewModel.isHomeScreenSettingsEnabled)
|
||||||
InkWell(
|
InkWell(
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.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/new_wallet/widgets/select_button.dart';
|
||||||
|
@ -10,14 +12,20 @@ import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/wallet_types.g.dart';
|
import 'package:cake_wallet/wallet_types.g.dart';
|
||||||
|
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class NewWalletTypePage extends BasePage {
|
class NewWalletTypePage extends BasePage {
|
||||||
NewWalletTypePage({required this.onTypeSelected, required this.isCreate});
|
NewWalletTypePage({
|
||||||
|
required this.onTypeSelected,
|
||||||
|
required this.isCreate,
|
||||||
|
required this.isHardwareWallet,
|
||||||
|
});
|
||||||
|
|
||||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||||
final bool isCreate;
|
final bool isCreate;
|
||||||
|
final bool isHardwareWallet;
|
||||||
|
|
||||||
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
||||||
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
|
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
|
||||||
|
@ -31,15 +39,22 @@ class NewWalletTypePage extends BasePage {
|
||||||
onTypeSelected: onTypeSelected,
|
onTypeSelected: onTypeSelected,
|
||||||
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage,
|
walletImage: currentTheme.type == ThemeType.dark ? walletTypeImage : walletTypeLightImage,
|
||||||
isCreate: isCreate,
|
isCreate: isCreate,
|
||||||
|
isHardwareWallet: isHardwareWallet,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletTypeForm extends StatefulWidget {
|
class WalletTypeForm extends StatefulWidget {
|
||||||
WalletTypeForm({required this.onTypeSelected, required this.walletImage, required this.isCreate});
|
WalletTypeForm({
|
||||||
|
required this.onTypeSelected,
|
||||||
|
required this.walletImage,
|
||||||
|
required this.isCreate,
|
||||||
|
required this.isHardwareWallet,
|
||||||
|
});
|
||||||
|
|
||||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||||
final Image walletImage;
|
final Image walletImage;
|
||||||
final bool isCreate;
|
final bool isCreate;
|
||||||
|
final bool isHardwareWallet;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletTypeFormState createState() => WalletTypeFormState();
|
WalletTypeFormState createState() => WalletTypeFormState();
|
||||||
|
@ -58,7 +73,11 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
types = filteredTypes = availableWalletTypes;
|
types = filteredTypes = availableWalletTypes
|
||||||
|
.where((element) =>
|
||||||
|
!widget.isHardwareWallet ||
|
||||||
|
DeviceConnectionType.supportedConnectionTypes(element, Platform.isIOS).isNotEmpty)
|
||||||
|
.toList();
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
searchController.addListener(() {
|
searchController.addListener(() {
|
||||||
|
@ -75,8 +94,7 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Center(
|
return Center(
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints:
|
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||||
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -87,11 +105,12 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
|
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 12),
|
padding: EdgeInsets.fromLTRB(24, 24, 24, 12),
|
||||||
child: SearchBarWidget(searchController: searchController, borderRadius: 24),
|
child: SearchBarWidget(searchController: searchController, borderRadius: 24),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -100,19 +119,26 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
content: Column(
|
content: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
...filteredTypes.map((type) => Padding(
|
...filteredTypes.map(
|
||||||
|
(type) => Padding(
|
||||||
padding: EdgeInsets.only(top: 12),
|
padding: EdgeInsets.only(top: 12),
|
||||||
child: SelectButton(
|
child: SelectButton(
|
||||||
image: Image.asset(
|
image: Image.asset(
|
||||||
walletTypeToCryptoCurrency(type).iconPath ?? '',
|
walletTypeToCryptoCurrency(type).iconPath ?? '',
|
||||||
height: 24,
|
height: 24,
|
||||||
width: 24),
|
width: 24,
|
||||||
|
),
|
||||||
text: walletTypeToDisplayName(type),
|
text: walletTypeToDisplayName(type),
|
||||||
showTrailingIcon: false,
|
showTrailingIcon: false,
|
||||||
height: 54,
|
height: 54,
|
||||||
isSelected: selected == type,
|
isSelected: selected == type,
|
||||||
onTap: () => setState(() => selected = type)),
|
onTap: () => setState(() => selected = type),
|
||||||
))
|
deviceConnectionTypes: widget.isHardwareWallet
|
||||||
|
? DeviceConnectionType.supportedConnectionTypes(type, Platform.isIOS)
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||||
|
@ -126,24 +152,22 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)));
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onTypeSelected() async {
|
Future<void> onTypeSelected() async {
|
||||||
if (selected == null) {
|
if (selected == null) throw Exception('Wallet Type is not selected yet.');
|
||||||
throw Exception('Wallet Type is not selected yet.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selected == WalletType.haven && widget.isCreate) {
|
if (selected == WalletType.haven && widget.isCreate) {
|
||||||
return await showPopUp<void>(
|
return await showPopUp<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) => PopUpCancellableAlertDialog(
|
||||||
return PopUpCancellableAlertDialog(
|
|
||||||
contentText: S.of(context).pause_wallet_creation,
|
contentText: S.of(context).pause_wallet_creation,
|
||||||
actionButtonText: S.of(context).ok,
|
actionButtonText: S.of(context).ok,
|
||||||
buttonAction: () => Navigator.of(context).pop(),
|
buttonAction: () => Navigator.of(context).pop(),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
|
||||||
|
import 'package:cw_core/hardware/device_connection_type.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SelectButton extends StatelessWidget {
|
class SelectButton extends StatelessWidget {
|
||||||
SelectButton({
|
SelectButton({
|
||||||
|
@ -16,6 +17,7 @@ class SelectButton extends StatelessWidget {
|
||||||
this.textColor,
|
this.textColor,
|
||||||
this.arrowColor,
|
this.arrowColor,
|
||||||
this.borderColor,
|
this.borderColor,
|
||||||
|
this.deviceConnectionTypes,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Image? image;
|
final Image? image;
|
||||||
|
@ -24,6 +26,7 @@ class SelectButton extends StatelessWidget {
|
||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
final bool showTrailingIcon;
|
final bool showTrailingIcon;
|
||||||
|
final List<DeviceConnectionType>? deviceConnectionTypes;
|
||||||
final double height;
|
final double height;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
final Color? textColor;
|
final Color? textColor;
|
||||||
|
@ -33,15 +36,26 @@ class SelectButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
|
final backgroundColor = color ?? (isSelected ? Colors.green : Theme.of(context).cardColor);
|
||||||
final effectiveTextColor = textColor ?? (isSelected
|
final effectiveTextColor = textColor ??
|
||||||
|
(isSelected
|
||||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||||
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
|
: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor);
|
||||||
final effectiveArrowColor = arrowColor ?? (isSelected
|
final effectiveArrowColor = arrowColor ??
|
||||||
|
(isSelected
|
||||||
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
? Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor
|
||||||
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
|
: Theme.of(context).extension<FilterTheme>()!.titlesColor);
|
||||||
|
|
||||||
final selectArrowImage = Image.asset('assets/images/select_arrow.png',
|
final trailingIcons = <Image>[];
|
||||||
color: effectiveArrowColor);
|
final selectArrowImage =
|
||||||
|
Image.asset('assets/images/select_arrow.png', color: effectiveArrowColor);
|
||||||
|
|
||||||
|
deviceConnectionTypes?.forEach((element) => trailingIcons.add(Image.asset(
|
||||||
|
element.iconString,
|
||||||
|
color: effectiveArrowColor,
|
||||||
|
height: 24,
|
||||||
|
)));
|
||||||
|
|
||||||
|
if (showTrailingIcon) trailingIcons.add(selectArrowImage);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
@ -54,22 +68,20 @@ class SelectButton extends StatelessWidget {
|
||||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
border: borderColor != null ? Border.all(color: borderColor!) : null,
|
border: borderColor != null ? Border.all(color: borderColor!) : null,
|
||||||
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Row(
|
Expanded(
|
||||||
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
image ?? Offstage(),
|
image ?? Offstage(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: image != null
|
padding: image != null ? EdgeInsets.only(left: 15) : EdgeInsets.only(left: 0),
|
||||||
? EdgeInsets.only(left: 15)
|
|
||||||
: EdgeInsets.only(left: 0),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -81,7 +93,8 @@ class SelectButton extends StatelessWidget {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (showTrailingIcon) selectArrowImage
|
),
|
||||||
|
...trailingIcons
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/src/widgets/option_tile.dart';
|
import 'package:cake_wallet/src/widgets/option_tile.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
|
||||||
|
import 'package:cake_wallet/utils/permission_handler.dart';
|
||||||
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
|
||||||
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
|
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:cake_wallet/utils/permission_handler.dart';
|
|
||||||
|
|
||||||
class RestoreOptionsPage extends BasePage {
|
class RestoreOptionsPage extends BasePage {
|
||||||
RestoreOptionsPage({required this.isNewInstall});
|
RestoreOptionsPage({required this.isNewInstall});
|
||||||
|
@ -22,12 +22,15 @@ class RestoreOptionsPage extends BasePage {
|
||||||
String get title => S.current.restore_restore_wallet;
|
String get title => S.current.restore_restore_wallet;
|
||||||
|
|
||||||
final bool isNewInstall;
|
final bool isNewInstall;
|
||||||
final imageSeedKeys = Image.asset('assets/images/restore_wallet_image.png');
|
|
||||||
final imageBackup = Image.asset('assets/images/backup.png');
|
|
||||||
final qrCode = Image.asset('assets/images/restore_qr.png');
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
|
final imageColor = Theme.of(context).extension<OptionTileTheme>()!.titleColor;
|
||||||
|
final imageLedger = Image.asset('assets/images/ledger_nano.png', width: 40, color: imageColor);
|
||||||
|
final imageSeedKeys = Image.asset('assets/images/restore_wallet_image.png', color: imageColor);
|
||||||
|
final imageBackup = Image.asset('assets/images/backup.png', color: imageColor);
|
||||||
|
final qrCode = Image.asset('assets/images/restore_qr.png', color: imageColor);
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
|
width: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
|
||||||
|
@ -41,7 +44,8 @@ class RestoreOptionsPage extends BasePage {
|
||||||
arguments: isNewInstall),
|
arguments: isNewInstall),
|
||||||
image: imageSeedKeys,
|
image: imageSeedKeys,
|
||||||
title: S.of(context).restore_title_from_seed_keys,
|
title: S.of(context).restore_title_from_seed_keys,
|
||||||
description: S.of(context).restore_description_from_seed_keys),
|
description: S.of(context).restore_description_from_seed_keys,
|
||||||
|
),
|
||||||
if (isNewInstall)
|
if (isNewInstall)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24),
|
||||||
|
@ -49,41 +53,24 @@ class RestoreOptionsPage extends BasePage {
|
||||||
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
|
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
|
||||||
image: imageBackup,
|
image: imageBackup,
|
||||||
title: S.of(context).restore_title_from_backup,
|
title: S.of(context).restore_title_from_backup,
|
||||||
description: S.of(context).restore_description_from_backup),
|
description: S.of(context).restore_description_from_backup,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24),
|
||||||
child: OptionTile(
|
child: OptionTile(
|
||||||
onPressed: () async {
|
onPressed: () => Navigator.pushNamed(
|
||||||
bool isCameraPermissionGranted =
|
context, Routes.restoreWalletFromHardwareWallet,
|
||||||
await PermissionHandler.checkPermission(Permission.camera, context);
|
arguments: isNewInstall),
|
||||||
if (!isCameraPermissionGranted) return;
|
image: imageLedger,
|
||||||
bool isPinSet = false;
|
title: S.of(context).restore_title_from_hardware_wallet,
|
||||||
if (isNewInstall) {
|
description: S.of(context).restore_description_from_hardware_wallet,
|
||||||
await Navigator.pushNamed(context, Routes.setupPin,
|
),
|
||||||
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
|
),
|
||||||
setupPinContext.close();
|
Padding(
|
||||||
isPinSet = true;
|
padding: EdgeInsets.only(top: 24),
|
||||||
});
|
child: OptionTile(
|
||||||
}
|
onPressed: () => _onScanQRCode(context),
|
||||||
if (!isNewInstall || isPinSet) {
|
|
||||||
try {
|
|
||||||
final restoreWallet =
|
|
||||||
await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
|
|
||||||
|
|
||||||
final restoreFromQRViewModel =
|
|
||||||
getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
|
|
||||||
|
|
||||||
await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
|
|
||||||
if (restoreFromQRViewModel.state is FailureState) {
|
|
||||||
_onWalletCreateFailure(context,
|
|
||||||
'Create wallet state: ${(restoreFromQRViewModel.state as FailureState).error}');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
_onWalletCreateFailure(context, e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
image: qrCode,
|
image: qrCode,
|
||||||
title: S.of(context).scan_qr_code,
|
title: S.of(context).scan_qr_code,
|
||||||
description: S.of(context).cold_or_recover_wallet),
|
description: S.of(context).cold_or_recover_wallet),
|
||||||
|
@ -105,4 +92,35 @@ class RestoreOptionsPage extends BasePage {
|
||||||
buttonAction: () => Navigator.of(context).pop());
|
buttonAction: () => Navigator.of(context).pop());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onScanQRCode(BuildContext context) async {
|
||||||
|
final isCameraPermissionGranted =
|
||||||
|
await PermissionHandler.checkPermission(Permission.camera, context);
|
||||||
|
|
||||||
|
if (!isCameraPermissionGranted) return;
|
||||||
|
bool isPinSet = false;
|
||||||
|
if (isNewInstall) {
|
||||||
|
await Navigator.pushNamed(context, Routes.setupPin,
|
||||||
|
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
|
||||||
|
setupPinContext.close();
|
||||||
|
isPinSet = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!isNewInstall || isPinSet) {
|
||||||
|
try {
|
||||||
|
final restoreWallet = await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
|
||||||
|
|
||||||
|
final restoreFromQRViewModel =
|
||||||
|
getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
|
||||||
|
|
||||||
|
await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
|
||||||
|
if (restoreFromQRViewModel.state is FailureState) {
|
||||||
|
_onWalletCreateFailure(context,
|
||||||
|
'Create wallet state: ${(restoreFromQRViewModel.state as FailureState).error}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_onWalletCreateFailure(context, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,40 @@
|
||||||
import 'package:cake_wallet/core/auth_service.dart';
|
import 'package:cake_wallet/core/auth_service.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/entities/template.dart';
|
import 'package:cake_wallet/entities/template.dart';
|
||||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
|
||||||
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
|
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
|
||||||
import 'package:cake_wallet/src/widgets/add_template_button.dart';
|
import 'package:cake_wallet/src/widgets/add_template_button.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
import 'package:cake_wallet/src/widgets/template_tile.dart';
|
import 'package:cake_wallet/src/widgets/template_tile.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart';
|
import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/payment_request.dart';
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
import 'package:cake_wallet/utils/request_review_handler.dart';
|
import 'package:cake_wallet/utils/request_review_handler.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/send/output.dart';
|
import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.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/crypto_currency.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
|
||||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
|
|
||||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
|
||||||
|
|
||||||
class SendPage extends BasePage {
|
class SendPage extends BasePage {
|
||||||
SendPage({
|
SendPage({
|
||||||
|
@ -369,6 +370,21 @@ class SendPage extends BasePage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sendViewModel.wallet.isHardwareWallet) {
|
||||||
|
if (!sendViewModel.ledgerViewModel.isConnected) {
|
||||||
|
await Navigator.of(context).pushNamed(Routes.connectDevices,
|
||||||
|
arguments: ConnectDevicePageParams(
|
||||||
|
walletType: sendViewModel.walletType,
|
||||||
|
onConnectDevice: (BuildContext context, _) {
|
||||||
|
sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final check = sendViewModel.shouldDisplayTotp();
|
final check = sendViewModel.shouldDisplayTotp();
|
||||||
authService.authenticateAction(
|
authService.authenticateAction(
|
||||||
context,
|
context,
|
||||||
|
@ -384,7 +400,8 @@ class SendPage extends BasePage {
|
||||||
color: Theme.of(context).primaryColor,
|
color: Theme.of(context).primaryColor,
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
isLoading: sendViewModel.state is IsExecutingState ||
|
isLoading: sendViewModel.state is IsExecutingState ||
|
||||||
sendViewModel.state is TransactionCommitting,
|
sendViewModel.state is TransactionCommitting ||
|
||||||
|
sendViewModel.state is IsAwaitingDeviceResponseState,
|
||||||
isDisabled: !sendViewModel.isReadyForSend,
|
isDisabled: !sendViewModel.isReadyForSend,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -395,12 +412,20 @@ class SendPage extends BasePage {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BuildContext? dialogContext;
|
||||||
|
|
||||||
void _setEffects(BuildContext context) {
|
void _setEffects(BuildContext context) {
|
||||||
if (_effectsInstalled) {
|
if (_effectsInstalled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reaction((_) => sendViewModel.state, (ExecutionState state) {
|
reaction((_) => sendViewModel.state, (ExecutionState state) {
|
||||||
|
|
||||||
|
if (dialogContext != null && dialogContext?.mounted == true) {
|
||||||
|
Navigator.of(dialogContext!).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (state is FailureState) {
|
if (state is FailureState) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
showPopUp<void>(
|
showPopUp<void>(
|
||||||
|
@ -510,6 +535,21 @@ class SendPage extends BasePage {
|
||||||
sendViewModel.clearOutputs();
|
sendViewModel.clearOutputs();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state is IsAwaitingDeviceResponseState) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
dialogContext = context;
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).proceed_on_device,
|
||||||
|
alertContent: S.of(context).proceed_on_device_description,
|
||||||
|
buttonText: S.of(context).cancel,
|
||||||
|
buttonAction: () => Navigator.of(context).pop());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_effectsInstalled = true;
|
_effectsInstalled = true;
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart';
|
import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
@ -12,10 +16,6 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
|
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
|
||||||
import 'package:cw_core/battery_optimization_native.dart';
|
import 'package:cw_core/battery_optimization_native.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
|
||||||
class ConnectionSyncPage extends BasePage {
|
class ConnectionSyncPage extends BasePage {
|
||||||
|
@ -106,7 +106,8 @@ class ConnectionSyncPage extends BasePage {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (isWalletConnectCompatibleChain(dashboardViewModel.wallet.type)) ...[
|
if (isWalletConnectCompatibleChain(dashboardViewModel.wallet.type) &&
|
||||||
|
!dashboardViewModel.wallet.isHardwareWallet) ...[ // ToDo: Remove this line once WalletConnect is implemented
|
||||||
WalletConnectTile(
|
WalletConnectTile(
|
||||||
onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing),
|
onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing),
|
||||||
),
|
),
|
||||||
|
|
|
@ -509,7 +509,9 @@ abstract class DashboardViewModelBase with Store {
|
||||||
final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
|
||||||
final jsonSource = await read(path: path, password: password);
|
final jsonSource = await read(path: path, password: password);
|
||||||
final data = json.decode(jsonSource) as Map;
|
final data = json.decode(jsonSource) as Map;
|
||||||
final mnemonic = data['mnemonic'] as String;
|
final mnemonic = data['mnemonic'] as String?;
|
||||||
|
|
||||||
|
if (mnemonic == null) continue;
|
||||||
|
|
||||||
final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic));
|
final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic));
|
||||||
final seedSha = bytesToHex(hash.bytes);
|
final seedSha = bytesToHex(hash.bytes);
|
||||||
|
|
66
lib/view_model/hardware_wallet/ledger_view_model.dart
Normal file
66
lib/view_model/hardware_wallet/ledger_view_model.dart
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/polygon/polygon.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
class LedgerViewModel {
|
||||||
|
final Ledger ledger = Ledger(
|
||||||
|
options: LedgerOptions(
|
||||||
|
scanMode: ScanMode.balanced,
|
||||||
|
maxScanDuration: const Duration(minutes: 5),
|
||||||
|
),
|
||||||
|
onPermissionRequest: (_) async {
|
||||||
|
Map<Permission, PermissionStatus> statuses = await [
|
||||||
|
Permission.bluetoothScan,
|
||||||
|
Permission.bluetoothConnect,
|
||||||
|
Permission.bluetoothAdvertise,
|
||||||
|
].request();
|
||||||
|
|
||||||
|
return statuses.values.where((status) => status.isDenied).isEmpty;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<void> connectLedger(LedgerDevice device) async {
|
||||||
|
await ledger.connect(device);
|
||||||
|
|
||||||
|
if (device.connectionType == ConnectionType.usb) _device = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
LedgerDevice? _device;
|
||||||
|
|
||||||
|
bool get isConnected => ledger.devices.isNotEmpty || _device != null;
|
||||||
|
|
||||||
|
LedgerDevice get device => _device ?? ledger.devices.first;
|
||||||
|
|
||||||
|
void setLedger(WalletBase wallet) {
|
||||||
|
switch (wallet.type) {
|
||||||
|
case WalletType.bitcoin:
|
||||||
|
return bitcoin!.setLedger(wallet, ledger, device);
|
||||||
|
case WalletType.ethereum:
|
||||||
|
return ethereum!.setLedger(wallet, ledger, device);
|
||||||
|
case WalletType.polygon:
|
||||||
|
return polygon!.setLedger(wallet, ledger, device);
|
||||||
|
default:
|
||||||
|
throw Exception('Unexpected wallet type: ${wallet.type}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String? interpretErrorCode(String errorCode) {
|
||||||
|
switch (errorCode) {
|
||||||
|
case "6985":
|
||||||
|
return S.current.ledger_error_tx_rejected_by_user;
|
||||||
|
case "5515":
|
||||||
|
return S.current.ledger_error_device_locked;
|
||||||
|
case "6d02": // UNKNOWN_APDU
|
||||||
|
case "6511":
|
||||||
|
case "6e00":
|
||||||
|
return S.current.ledger_error_wrong_app;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,13 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/tron/tron.dart';
|
import 'package:cake_wallet/tron/tron.dart';
|
||||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
import 'package:cw_core/exceptions.dart';
|
import 'package:cw_core/exceptions.dart';
|
||||||
import 'package:cw_core/transaction_priority.dart';
|
import 'package:cw_core/transaction_priority.dart';
|
||||||
import 'package:cake_wallet/view_model/send/output.dart';
|
import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/entities/template.dart';
|
import 'package:cake_wallet/entities/template.dart';
|
||||||
import 'package:cake_wallet/core/address_validator.dart';
|
import 'package:cake_wallet/core/address_validator.dart';
|
||||||
|
@ -63,6 +65,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
this.balanceViewModel,
|
this.balanceViewModel,
|
||||||
this.contactListViewModel,
|
this.contactListViewModel,
|
||||||
this.transactionDescriptionBox,
|
this.transactionDescriptionBox,
|
||||||
|
this.ledgerViewModel,
|
||||||
) : state = InitialExecutionState(),
|
) : state = InitialExecutionState(),
|
||||||
currencies = appStore.wallet!.balance.keys.toList(),
|
currencies = appStore.wallet!.balance.keys.toList(),
|
||||||
selectedCryptoCurrency = appStore.wallet!.currency,
|
selectedCryptoCurrency = appStore.wallet!.currency,
|
||||||
|
@ -266,6 +269,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
final SendTemplateViewModel sendTemplateViewModel;
|
final SendTemplateViewModel sendTemplateViewModel;
|
||||||
final BalanceViewModel balanceViewModel;
|
final BalanceViewModel balanceViewModel;
|
||||||
final ContactListViewModel contactListViewModel;
|
final ContactListViewModel contactListViewModel;
|
||||||
|
final LedgerViewModel ledgerViewModel;
|
||||||
final FiatConversionStore _fiatConversationStore;
|
final FiatConversionStore _fiatConversationStore;
|
||||||
final Box<TransactionDescription> transactionDescriptionBox;
|
final Box<TransactionDescription> transactionDescriptionBox;
|
||||||
|
|
||||||
|
@ -340,7 +344,10 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
try {
|
try {
|
||||||
state = IsExecutingState();
|
state = IsExecutingState();
|
||||||
|
|
||||||
|
if (wallet.isHardwareWallet) state = IsAwaitingDeviceResponseState();
|
||||||
|
|
||||||
pendingTransaction = await wallet.createTransaction(_credentials());
|
pendingTransaction = await wallet.createTransaction(_credentials());
|
||||||
|
|
||||||
if (provider is ThorChainExchangeProvider) {
|
if (provider is ThorChainExchangeProvider) {
|
||||||
final outputCount = pendingTransaction?.outputCount ?? 0;
|
final outputCount = pendingTransaction?.outputCount ?? 0;
|
||||||
if (outputCount > 10) {
|
if (outputCount > 10) {
|
||||||
|
@ -354,8 +361,16 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
state = ExecutedSuccessfullyState();
|
state = ExecutedSuccessfullyState();
|
||||||
return pendingTransaction;
|
return pendingTransaction;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (e is LedgerException) {
|
||||||
|
final errorCode = e.errorCode.toRadixString(16);
|
||||||
|
final fallbackMsg = e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
|
||||||
|
final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg;
|
||||||
|
|
||||||
|
state = FailureState(errorMsg);
|
||||||
|
} else {
|
||||||
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
|
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,15 +405,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
|
||||||
|
|
||||||
String address = outputs.fold('', (acc, value) {
|
String address = outputs.fold('', (acc, value) {
|
||||||
return value.isParsedAddress
|
return value.isParsedAddress
|
||||||
? acc + value.address + '\n' + value.extractedAddress + '\n\n'
|
? '$acc${value.address}\n${value.extractedAddress}\n\n'
|
||||||
: acc + value.address + '\n\n';
|
: '$acc${value.address}\n\n';
|
||||||
});
|
});
|
||||||
|
|
||||||
address = address.trim();
|
address = address.trim();
|
||||||
|
|
||||||
String note = outputs.fold('', (acc, value) {
|
String note = outputs.fold('', (acc, value) => '$acc${value.note}\n');
|
||||||
return acc + value.note + '\n';
|
|
||||||
});
|
|
||||||
|
|
||||||
note = note.trim();
|
note = note.trim();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
|
|
||||||
|
class IsAwaitingDeviceResponseState extends IsExecutingState {}
|
||||||
class TransactionCommitting extends ExecutionState {}
|
class TransactionCommitting extends ExecutionState {}
|
||||||
class TransactionCommitted extends ExecutionState {}
|
class TransactionCommitted extends ExecutionState {}
|
||||||
|
|
|
@ -72,6 +72,7 @@ abstract class WalletCreationVMBase with Store {
|
||||||
address: '',
|
address: '',
|
||||||
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
showIntroCakePayCard: (!walletCreationService.typeExists(type)) && type != WalletType.haven,
|
||||||
derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
|
derivationInfo: credentials.derivationInfo ?? getDefaultDerivation(),
|
||||||
|
hardwareWalletType: credentials.hardwareWalletType,
|
||||||
);
|
);
|
||||||
|
|
||||||
credentials.walletInfo = walletInfo;
|
credentials.walletInfo = walletInfo;
|
||||||
|
@ -99,7 +100,7 @@ abstract class WalletCreationVMBase with Store {
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
return DerivationInfo(
|
return DerivationInfo(
|
||||||
derivationType: DerivationType.electrum,
|
derivationType: DerivationType.electrum,
|
||||||
derivationPath: "m/0'/0",
|
derivationPath: "m/0'",
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
|
110
lib/view_model/wallet_hardware_restore_view_model.dart
Normal file
110
lib/view_model/wallet_hardware_restore_view_model.dart
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
|
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||||
|
import 'package:cake_wallet/ethereum/ethereum.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/polygon/polygon.dart';
|
||||||
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
|
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
|
||||||
|
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/wallet_credentials.dart';
|
||||||
|
import 'package:cw_core/wallet_info.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:ledger_flutter/ledger_flutter.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
|
part 'wallet_hardware_restore_view_model.g.dart';
|
||||||
|
|
||||||
|
class WalletHardwareRestoreViewModel = WalletHardwareRestoreViewModelBase
|
||||||
|
with _$WalletHardwareRestoreViewModel;
|
||||||
|
|
||||||
|
abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
|
final LedgerViewModel ledgerViewModel;
|
||||||
|
|
||||||
|
int _nextIndex = 0;
|
||||||
|
|
||||||
|
WalletHardwareRestoreViewModelBase(this.ledgerViewModel, AppStore appStore,
|
||||||
|
WalletCreationService walletCreationService, Box<WalletInfo> walletInfoSource,
|
||||||
|
{required WalletType type})
|
||||||
|
: super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String name = "";
|
||||||
|
|
||||||
|
@observable
|
||||||
|
HardwareAccountData? selectedAccount = null;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isLoadingMoreAccounts = false;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String? error = null;
|
||||||
|
|
||||||
|
// @observable
|
||||||
|
ObservableList<HardwareAccountData> availableAccounts = ObservableList();
|
||||||
|
|
||||||
|
@action
|
||||||
|
Future<void> getNextAvailableAccounts(int limit) async {
|
||||||
|
try {
|
||||||
|
List<HardwareAccountData> accounts;
|
||||||
|
switch (type) {
|
||||||
|
case WalletType.bitcoin:
|
||||||
|
accounts = await bitcoin!
|
||||||
|
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
|
||||||
|
break;
|
||||||
|
case WalletType.ethereum:
|
||||||
|
accounts = await ethereum!
|
||||||
|
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
|
||||||
|
break;
|
||||||
|
case WalletType.polygon:
|
||||||
|
accounts = await polygon!
|
||||||
|
.getHardwareWalletAccounts(ledgerViewModel, index: _nextIndex, limit: limit);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
availableAccounts.addAll(accounts);
|
||||||
|
_nextIndex += limit;
|
||||||
|
} on LedgerException catch (e) {
|
||||||
|
error = ledgerViewModel.interpretErrorCode(e.errorCode.toRadixString(16));
|
||||||
|
} catch (e) {
|
||||||
|
error = S.current.ledger_connection_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoadingMoreAccounts = false;
|
||||||
|
_nextIndex += limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
WalletCredentials getCredentials(dynamic _options) {
|
||||||
|
WalletCredentials credentials;
|
||||||
|
switch (type) {
|
||||||
|
case WalletType.bitcoin:
|
||||||
|
credentials =
|
||||||
|
bitcoin!.createBitcoinHardwareWalletCredentials(name: name, accountData: selectedAccount!);
|
||||||
|
break;
|
||||||
|
case WalletType.ethereum:
|
||||||
|
credentials =
|
||||||
|
ethereum!.createEthereumHardwareWalletCredentials(name: name, hwAccountData: selectedAccount!);
|
||||||
|
break;
|
||||||
|
case WalletType.polygon:
|
||||||
|
credentials = polygon!.createPolygonHardwareWalletCredentials(name: name, hwAccountData: selectedAccount!);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw Exception('Unexpected type: ${type.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
credentials.hardwareWalletType = HardwareWalletType.ledger;
|
||||||
|
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<WalletBase> process(WalletCredentials credentials) async {
|
||||||
|
walletCreationService.changeWalletType(type: type);
|
||||||
|
return walletCreationService.restoreFromHardwareWallet(credentials);
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,5 +32,22 @@
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.finance</string>
|
<string>public.app-category.finance</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Used for scanning QR code and can be used to capture images for identification purposes by third-party providers.</string>
|
||||||
|
<key>NSDocumentsFolderUsageDescription</key>
|
||||||
|
<string>We need access to documents folder for getting access to open/save backup file</string>
|
||||||
|
<key>NSFaceIDUsageDescription</key>
|
||||||
|
<string>Enable Face ID for fast and secure access to wallets and private keys</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>We need access to documents folder for getting access to open/save backup file</string>
|
||||||
|
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||||
|
<string>We need access to Bluetooth in order to connect to your hardware wallet when needed</string>
|
||||||
|
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||||
|
<string>We need access to Bluetooth in order to connect to your hardware wallet when needed</string>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>fetch</string>
|
||||||
|
<string>processing</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -111,6 +111,7 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base.git
|
url: https://github.com/cake-tech/bitcoin_base.git
|
||||||
ref: cake-update-v2
|
ref: cake-update-v2
|
||||||
|
ledger_flutter: ^1.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -133,6 +134,14 @@ dependency_overrides:
|
||||||
bech32:
|
bech32:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bech32.git
|
url: https://github.com/cake-tech/bech32.git
|
||||||
|
ledger_flutter:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/ledger-flutter.git
|
||||||
|
ref: cake
|
||||||
|
web3dart:
|
||||||
|
git:
|
||||||
|
url: https://github.com/cake-tech/web3dart.git
|
||||||
|
ref: cake
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
image_path: "assets/images/app_logo.png"
|
image_path: "assets/images/app_logo.png"
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "موضوع البيتكوين الخفيفة",
|
"bitcoin_light_theme": "موضوع البيتكوين الخفيفة",
|
||||||
"bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.",
|
"bitcoin_payments_require_1_confirmation": "تتطلب مدفوعات Bitcoin تأكيدًا واحدًا ، والذي قد يستغرق 20 دقيقة أو أكثر. شكرا لصبرك! سيتم إرسال بريد إلكتروني إليك عند تأكيد الدفع.",
|
||||||
"Blocks_remaining": "بلوك متبقي ${status}",
|
"Blocks_remaining": "بلوك متبقي ${status}",
|
||||||
|
"bluetooth": "بلوتوث",
|
||||||
"bright_theme": "مشرق",
|
"bright_theme": "مشرق",
|
||||||
"bump_fee": "رسوم عثرة",
|
"bump_fee": "رسوم عثرة",
|
||||||
"buy": "اشتري",
|
"buy": "اشتري",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "تهانينا!",
|
"congratulations": "تهانينا!",
|
||||||
"connect_an_existing_yat": "توصيل Yat الحالي",
|
"connect_an_existing_yat": "توصيل Yat الحالي",
|
||||||
"connect_yats": "توصيل Yats",
|
"connect_yats": "توصيل Yats",
|
||||||
|
"connect_your_hardware_wallet": "قم بتوصيل محفظة الأجهزة الخاصة بك باستخدام Bluetooth أو USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "قم بتوصيل محفظة الأجهزة الخاصة بك باستخدام Bluetooth",
|
||||||
"connection_sync": "الاتصال والمزامنة",
|
"connection_sync": "الاتصال والمزامنة",
|
||||||
"connectWalletPrompt": "ﺕﻼﻣﺎﻌﻤﻟﺍ ءﺍﺮﺟﻹ WalletConnect ﻊﻣ ﻚﺘﻈﻔﺤﻣ ﻞﻴﺻﻮﺘﺑ ﻢﻗ",
|
"connectWalletPrompt": "ﺕﻼﻣﺎﻌﻤﻟﺍ ءﺍﺮﺟﻹ WalletConnect ﻊﻣ ﻚﺘﻈﻔﺤﻣ ﻞﻴﺻﻮﺘﺑ ﻢﻗ",
|
||||||
"contact": "تواصل",
|
"contact": "تواصل",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "يكون",
|
"is_percentage": "يكون",
|
||||||
"last_30_days": "آخر 30 يومًا",
|
"last_30_days": "آخر 30 يومًا",
|
||||||
"learn_more": "اعرف المزيد",
|
"learn_more": "اعرف المزيد",
|
||||||
|
"ledger_connection_error": "فشل في الاتصال بك دفتر الأستاذ. حاول مرة اخرى.",
|
||||||
|
"ledger_error_device_locked": "تم قفل دفتر الأستاذ",
|
||||||
|
"ledger_error_tx_rejected_by_user": "تم رفض المعاملة على الجهاز",
|
||||||
|
"ledger_error_wrong_app": "يرجى التأكد",
|
||||||
|
"ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك",
|
||||||
"light_theme": "فاتح",
|
"light_theme": "فاتح",
|
||||||
|
"load_more": "تحميل المزيد",
|
||||||
"loading_your_wallet": "يتم تحميل محفظتك",
|
"loading_your_wallet": "يتم تحميل محفظتك",
|
||||||
"login": "تسجيل الدخول",
|
"login": "تسجيل الدخول",
|
||||||
"logout": "تسجيل خروج",
|
"logout": "تسجيل خروج",
|
||||||
|
@ -442,6 +451,8 @@
|
||||||
"privacy_settings": "إعدادات الخصوصية",
|
"privacy_settings": "إعدادات الخصوصية",
|
||||||
"private_key": "مفتاح خاص",
|
"private_key": "مفتاح خاص",
|
||||||
"proceed_after_one_minute": "إذا لم تستمر الشاشة بعد دقيقة واحدة ، فتحقق من بريدك الإلكتروني.",
|
"proceed_after_one_minute": "إذا لم تستمر الشاشة بعد دقيقة واحدة ، فتحقق من بريدك الإلكتروني.",
|
||||||
|
"proceed_on_device": "تابع جهازك",
|
||||||
|
"proceed_on_device_description": "يرجى اتباع الإرشادات المطلوبة على محفظة الأجهزة الخاصة بك",
|
||||||
"profile": "حساب تعريفي",
|
"profile": "حساب تعريفي",
|
||||||
"provider_error": "خطأ ${provider}",
|
"provider_error": "خطأ ${provider}",
|
||||||
"public_key": "مفتاح عمومي",
|
"public_key": "مفتاح عمومي",
|
||||||
|
@ -490,6 +501,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "قم باستعادة محفظتك من كود مكون من 24 كلمة",
|
"restore_bitcoin_description_from_seed": "قم باستعادة محفظتك من كود مكون من 24 كلمة",
|
||||||
"restore_bitcoin_title_from_keys": "استعادة من WIF",
|
"restore_bitcoin_title_from_keys": "استعادة من WIF",
|
||||||
"restore_description_from_backup": "يمكنك استعادة تطبيق Cake Wallet بالكامل من ملف النسخ الاحتياطي",
|
"restore_description_from_backup": "يمكنك استعادة تطبيق Cake Wallet بالكامل من ملف النسخ الاحتياطي",
|
||||||
|
"restore_description_from_hardware_wallet": "استعادة من محفظة أجهزة دفتر الأستاذ",
|
||||||
"restore_description_from_keys": "قم باستعادة محفظتك من ضغطات المفاتيح المولدة المحفوظة من مفاتيحك الخاصة",
|
"restore_description_from_keys": "قم باستعادة محفظتك من ضغطات المفاتيح المولدة المحفوظة من مفاتيحك الخاصة",
|
||||||
"restore_description_from_seed": "قم باستعادة محفظتك من الرمز المكون من 25 كلمة أو 13 كلمة",
|
"restore_description_from_seed": "قم باستعادة محفظتك من الرمز المكون من 25 كلمة أو 13 كلمة",
|
||||||
"restore_description_from_seed_keys": "استرجع محفظتك من السييد / المفاتيح التي قمت بحفظها في مكان آمن",
|
"restore_description_from_seed_keys": "استرجع محفظتك من السييد / المفاتيح التي قمت بحفظها في مكان آمن",
|
||||||
|
@ -502,6 +514,7 @@
|
||||||
"restore_seed_keys_restore": "استعادة السييد / المفاتيح",
|
"restore_seed_keys_restore": "استعادة السييد / المفاتيح",
|
||||||
"restore_spend_key_private": "مفتاح الإنفاق (خاص)",
|
"restore_spend_key_private": "مفتاح الإنفاق (خاص)",
|
||||||
"restore_title_from_backup": "استعادة من النسخة الاحتياطية",
|
"restore_title_from_backup": "استعادة من النسخة الاحتياطية",
|
||||||
|
"restore_title_from_hardware_wallet": "استعادة من محفظة الأجهزة",
|
||||||
"restore_title_from_keys": "استعادة من المفاتيح",
|
"restore_title_from_keys": "استعادة من المفاتيح",
|
||||||
"restore_title_from_seed": "استعادة من السييد",
|
"restore_title_from_seed": "استعادة من السييد",
|
||||||
"restore_title_from_seed_keys": "استعادة من السييد / المفاتيح",
|
"restore_title_from_seed_keys": "استعادة من السييد / المفاتيح",
|
||||||
|
@ -752,6 +765,7 @@
|
||||||
"unsupported_asset": ".ﻡﻮﻋﺪﻣ ﻞﺻﺃ ﻉﻮﻧ ﻦﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻭﺃ ءﺎﺸﻧﺇ ﻰﺟﺮﻳ .ﻞﺻﻷﺍ ﺍﺬﻬﻟ ءﺍﺮﺟﻹﺍ ﺍﺬﻫ ﻢﻋﺪﻧ ﻻ ﻦﺤﻧ",
|
"unsupported_asset": ".ﻡﻮﻋﺪﻣ ﻞﺻﺃ ﻉﻮﻧ ﻦﻣ ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻭﺃ ءﺎﺸﻧﺇ ﻰﺟﺮﻳ .ﻞﺻﻷﺍ ﺍﺬﻬﻟ ءﺍﺮﺟﻹﺍ ﺍﺬﻫ ﻢﻋﺪﻧ ﻻ ﻦﺤﻧ",
|
||||||
"uptime": "مدة التشغيل",
|
"uptime": "مدة التشغيل",
|
||||||
"upto": "حتى ${value}",
|
"upto": "حتى ${value}",
|
||||||
|
"usb": "USB",
|
||||||
"use": "التبديل إلى",
|
"use": "التبديل إلى",
|
||||||
"use_card_info_three": "استخدم البطاقة الرقمية عبر الإنترنت أو مع طرق الدفع غير التلامسية.",
|
"use_card_info_three": "استخدم البطاقة الرقمية عبر الإنترنت أو مع طرق الدفع غير التلامسية.",
|
||||||
"use_card_info_two": "يتم تحويل الأموال إلى الدولار الأمريكي عند الاحتفاظ بها في الحساب المدفوع مسبقًا ، وليس بالعملات الرقمية.",
|
"use_card_info_two": "يتم تحويل الأموال إلى الدولار الأمريكي عند الاحتفاظ بها في الحساب المدفوع مسبقًا ، وليس بالعملات الرقمية.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "Лека биткойн тема",
|
"bitcoin_light_theme": "Лека биткойн тема",
|
||||||
"bitcoin_payments_require_1_confirmation": "Плащанията с Bitcoin изискват потвърждение, което може да отнеме 20 минути или повече. Благодарим за търпението! Ще получите имейл, когато плащането е потвърдено.",
|
"bitcoin_payments_require_1_confirmation": "Плащанията с Bitcoin изискват потвърждение, което може да отнеме 20 минути или повече. Благодарим за търпението! Ще получите имейл, когато плащането е потвърдено.",
|
||||||
"Blocks_remaining": "${status} оставащи блока",
|
"Blocks_remaining": "${status} оставащи блока",
|
||||||
|
"bluetooth": "Bluetooth",
|
||||||
"bright_theme": "Ярко",
|
"bright_theme": "Ярко",
|
||||||
"bump_fee": "Такса за бум",
|
"bump_fee": "Такса за бум",
|
||||||
"buy": "Купуване",
|
"buy": "Купуване",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "Поздравления!",
|
"congratulations": "Поздравления!",
|
||||||
"connect_an_existing_yat": "Добавете съществуващ Yat",
|
"connect_an_existing_yat": "Добавете съществуващ Yat",
|
||||||
"connect_yats": "Добавете Yats",
|
"connect_yats": "Добавете Yats",
|
||||||
|
"connect_your_hardware_wallet": "Свържете хардуерния си портфейл с помощта на Bluetooth или USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "Свържете хардуерния си портфейл с помощта на Bluetooth",
|
||||||
"connection_sync": "Свързване и синхронизиране",
|
"connection_sync": "Свързване и синхронизиране",
|
||||||
"connectWalletPrompt": "Свържете портфейла си с WalletConnect, за да извършвате транзакции",
|
"connectWalletPrompt": "Свържете портфейла си с WalletConnect, за да извършвате транзакции",
|
||||||
"contact": "Контакт",
|
"contact": "Контакт",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "е",
|
"is_percentage": "е",
|
||||||
"last_30_days": "Последните 30 дни",
|
"last_30_days": "Последните 30 дни",
|
||||||
"learn_more": "Научете още",
|
"learn_more": "Научете още",
|
||||||
|
"ledger_connection_error": "Не успя да се свърже с вашата книга. Моля, опитайте отново.",
|
||||||
|
"ledger_error_device_locked": "Главната книга е заключена",
|
||||||
|
"ledger_error_tx_rejected_by_user": "Транзакция, отхвърлена на устройство",
|
||||||
|
"ledger_error_wrong_app": "Моля, уверете се, че сте отворили правилното приложение на вашата книга",
|
||||||
|
"ledger_please_enable_bluetooth": "Моля, активирайте Bluetooth да открие вашата книга",
|
||||||
"light_theme": "Светло",
|
"light_theme": "Светло",
|
||||||
|
"load_more": "Зареди още",
|
||||||
"loading_your_wallet": "Зареждане на портфейл",
|
"loading_your_wallet": "Зареждане на портфейл",
|
||||||
"login": "Влизане",
|
"login": "Влизане",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
|
@ -442,6 +451,8 @@
|
||||||
"privacy_settings": "Настройки за поверителност",
|
"privacy_settings": "Настройки за поверителност",
|
||||||
"private_key": "Таен ключ",
|
"private_key": "Таен ключ",
|
||||||
"proceed_after_one_minute": "Ако процесът продължи повече от 1 минута, проверете своя имейл.",
|
"proceed_after_one_minute": "Ако процесът продължи повече от 1 минута, проверете своя имейл.",
|
||||||
|
"proceed_on_device": "Продължете на вашето устройство",
|
||||||
|
"proceed_on_device_description": "Моля, следвайте инструкциите, подканени на вашия хардуер",
|
||||||
"profile": "Профил",
|
"profile": "Профил",
|
||||||
"provider_error": "Грешка на ${provider} ",
|
"provider_error": "Грешка на ${provider} ",
|
||||||
"public_key": "Публичен ключ",
|
"public_key": "Публичен ключ",
|
||||||
|
@ -490,6 +501,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "Възстановяване на портфейл чрез код от 24 думи",
|
"restore_bitcoin_description_from_seed": "Възстановяване на портфейл чрез код от 24 думи",
|
||||||
"restore_bitcoin_title_from_keys": "Възстановяване от WIF",
|
"restore_bitcoin_title_from_keys": "Възстановяване от WIF",
|
||||||
"restore_description_from_backup": "Можете да възстановите цялото приложение Cake Wallet от своя резервен файл",
|
"restore_description_from_backup": "Можете да възстановите цялото приложение Cake Wallet от своя резервен файл",
|
||||||
|
"restore_description_from_hardware_wallet": "Възстановяване от хардуерния портфейл на главната книга",
|
||||||
"restore_description_from_keys": "Възстановяване на портфейл от генерираните от Вашите тайни ключове клавиши",
|
"restore_description_from_keys": "Възстановяване на портфейл от генерираните от Вашите тайни ключове клавиши",
|
||||||
"restore_description_from_seed": "Възстановяване на портфейл от кода от 13 или 25 думи",
|
"restore_description_from_seed": "Възстановяване на портфейл от кода от 13 или 25 думи",
|
||||||
"restore_description_from_seed_keys": "Възстановете своя портфейл от seed/keys, които сте съхранили на сигурно място",
|
"restore_description_from_seed_keys": "Възстановете своя портфейл от seed/keys, които сте съхранили на сигурно място",
|
||||||
|
@ -502,6 +514,7 @@
|
||||||
"restore_seed_keys_restore": "Възстановяне от Seed/Keys",
|
"restore_seed_keys_restore": "Възстановяне от Seed/Keys",
|
||||||
"restore_spend_key_private": "Spend key (публичен)",
|
"restore_spend_key_private": "Spend key (публичен)",
|
||||||
"restore_title_from_backup": "Възстановяване от резервно копие",
|
"restore_title_from_backup": "Възстановяване от резервно копие",
|
||||||
|
"restore_title_from_hardware_wallet": "Възстановяване от хардуерния портфейл",
|
||||||
"restore_title_from_keys": "Възстановяване от keys",
|
"restore_title_from_keys": "Възстановяване от keys",
|
||||||
"restore_title_from_seed": "Възстановяване от seed",
|
"restore_title_from_seed": "Възстановяване от seed",
|
||||||
"restore_title_from_seed_keys": "Възстановяване от seed/keys",
|
"restore_title_from_seed_keys": "Възстановяване от seed/keys",
|
||||||
|
@ -752,6 +765,7 @@
|
||||||
"unsupported_asset": "Не поддържаме това действие за този актив. Моля, създайте или преминете към портфейл от поддържан тип актив.",
|
"unsupported_asset": "Не поддържаме това действие за този актив. Моля, създайте или преминете към портфейл от поддържан тип актив.",
|
||||||
"uptime": "Време за работа",
|
"uptime": "Време за работа",
|
||||||
"upto": "до ${value}",
|
"upto": "до ${value}",
|
||||||
|
"usb": "USB",
|
||||||
"use": "Смяна на ",
|
"use": "Смяна на ",
|
||||||
"use_card_info_three": "Използвайте дигиталната карта онлайн или чрез безконтактен метод на плащане.",
|
"use_card_info_three": "Използвайте дигиталната карта онлайн или чрез безконтактен метод на плащане.",
|
||||||
"use_card_info_two": "Средствата се обръщат в USD, когато биват запазени в предплатената карта, а не в дигитална валута.",
|
"use_card_info_two": "Средствата се обръщат в USD, когато биват запазени в предплатената карта, а не в дигитална валута.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "Světlé téma bitcoinů",
|
"bitcoin_light_theme": "Světlé téma bitcoinů",
|
||||||
"bitcoin_payments_require_1_confirmation": "U plateb Bitcoinem je vyžadováno alespoň 1 potvrzení, což může trvat 20 minut i déle. Děkujeme za vaši trpělivost! Až bude platba potvrzena, budete informováni e-mailem.",
|
"bitcoin_payments_require_1_confirmation": "U plateb Bitcoinem je vyžadováno alespoň 1 potvrzení, což může trvat 20 minut i déle. Děkujeme za vaši trpělivost! Až bude platba potvrzena, budete informováni e-mailem.",
|
||||||
"Blocks_remaining": "Zbývá ${status} bloků",
|
"Blocks_remaining": "Zbývá ${status} bloků",
|
||||||
|
"bluetooth": "Bluetooth",
|
||||||
"bright_theme": "Jasný",
|
"bright_theme": "Jasný",
|
||||||
"bump_fee": "Bump Fee",
|
"bump_fee": "Bump Fee",
|
||||||
"buy": "Koupit",
|
"buy": "Koupit",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "Gratulujeme!",
|
"congratulations": "Gratulujeme!",
|
||||||
"connect_an_existing_yat": "Připojit existující Yat",
|
"connect_an_existing_yat": "Připojit existující Yat",
|
||||||
"connect_yats": "Připojit Yaty",
|
"connect_yats": "Připojit Yaty",
|
||||||
|
"connect_your_hardware_wallet": "Připojte hardwarovou peněženku pomocí Bluetooth nebo USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "Připojte hardwarovou peněženku pomocí Bluetooth",
|
||||||
"connection_sync": "Připojení a synch.",
|
"connection_sync": "Připojení a synch.",
|
||||||
"connectWalletPrompt": "Propojte svou peněženku s WalletConnect a provádějte transakce",
|
"connectWalletPrompt": "Propojte svou peněženku s WalletConnect a provádějte transakce",
|
||||||
"contact": "Kontakt",
|
"contact": "Kontakt",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "je",
|
"is_percentage": "je",
|
||||||
"last_30_days": "Posledních 30 dnů",
|
"last_30_days": "Posledních 30 dnů",
|
||||||
"learn_more": "Zjistit více",
|
"learn_more": "Zjistit více",
|
||||||
|
"ledger_connection_error": "Nepodařilo se připojit k vaší knize. Prosím zkuste to znovu.",
|
||||||
|
"ledger_error_device_locked": "Kniha je uzamčena",
|
||||||
|
"ledger_error_tx_rejected_by_user": "Transakce zamítnuta na zařízení",
|
||||||
|
"ledger_error_wrong_app": "Ujistěte se, že se na své knize otevřete správnou aplikaci",
|
||||||
|
"ledger_please_enable_bluetooth": "Umožněte prosím Bluetooth detekovat vaši knihu",
|
||||||
"light_theme": "Světlý",
|
"light_theme": "Světlý",
|
||||||
|
"load_more": "Načíst další",
|
||||||
"loading_your_wallet": "Načítám peněženku",
|
"loading_your_wallet": "Načítám peněženku",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"logout": "Odhlásit",
|
"logout": "Odhlásit",
|
||||||
|
@ -442,6 +451,8 @@
|
||||||
"privacy_settings": "Nastavení soukromí",
|
"privacy_settings": "Nastavení soukromí",
|
||||||
"private_key": "Soukromý klíč",
|
"private_key": "Soukromý klíč",
|
||||||
"proceed_after_one_minute": "Pokud proces nepokročí během 1 minuty, zkontrolujte svůj e-mail.",
|
"proceed_after_one_minute": "Pokud proces nepokročí během 1 minuty, zkontrolujte svůj e-mail.",
|
||||||
|
"proceed_on_device": "Pokračujte ve svém zařízení",
|
||||||
|
"proceed_on_device_description": "Postupujte podle pokynů na výzvu na vaší hardwarové peněžence",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"provider_error": "${provider} chyba",
|
"provider_error": "${provider} chyba",
|
||||||
"public_key": "Veřejný klíč",
|
"public_key": "Veřejný klíč",
|
||||||
|
@ -490,6 +501,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "Obnovte svou peněženku pomocí kombinace 24 slov",
|
"restore_bitcoin_description_from_seed": "Obnovte svou peněženku pomocí kombinace 24 slov",
|
||||||
"restore_bitcoin_title_from_keys": "Obnovit z WIF",
|
"restore_bitcoin_title_from_keys": "Obnovit z WIF",
|
||||||
"restore_description_from_backup": "Můžete obnovit celou Cake Wallet aplikaci ze souboru se zálohou",
|
"restore_description_from_backup": "Můžete obnovit celou Cake Wallet aplikaci ze souboru se zálohou",
|
||||||
|
"restore_description_from_hardware_wallet": "Obnovit z peněženky hardwaru knihy",
|
||||||
"restore_description_from_keys": "Obnovte svou peněženku pomocí generovaných stisků kláves uložených z vašich soukromých klíčů",
|
"restore_description_from_keys": "Obnovte svou peněženku pomocí generovaných stisků kláves uložených z vašich soukromých klíčů",
|
||||||
"restore_description_from_seed": "Obnovte svou peněženku pomocí kombinace 25, nebo 13 slov",
|
"restore_description_from_seed": "Obnovte svou peněženku pomocí kombinace 25, nebo 13 slov",
|
||||||
"restore_description_from_seed_keys": "Obnovte svou peněženku ze seedu/klíčů, které jste si uložili na bezpečném místě",
|
"restore_description_from_seed_keys": "Obnovte svou peněženku ze seedu/klíčů, které jste si uložili na bezpečném místě",
|
||||||
|
@ -502,6 +514,7 @@
|
||||||
"restore_seed_keys_restore": "Obnovit ze seedu/klíčů",
|
"restore_seed_keys_restore": "Obnovit ze seedu/klíčů",
|
||||||
"restore_spend_key_private": "Klíč pro platby (soukromý)",
|
"restore_spend_key_private": "Klíč pro platby (soukromý)",
|
||||||
"restore_title_from_backup": "Obnovit ze zálohy",
|
"restore_title_from_backup": "Obnovit ze zálohy",
|
||||||
|
"restore_title_from_hardware_wallet": "Obnovit z hardwarové peněženky",
|
||||||
"restore_title_from_keys": "Obnovit z klíčů",
|
"restore_title_from_keys": "Obnovit z klíčů",
|
||||||
"restore_title_from_seed": "Obnovit ze seedu",
|
"restore_title_from_seed": "Obnovit ze seedu",
|
||||||
"restore_title_from_seed_keys": "Obnovit ze seedu/klíčů",
|
"restore_title_from_seed_keys": "Obnovit ze seedu/klíčů",
|
||||||
|
@ -752,6 +765,7 @@
|
||||||
"unsupported_asset": "Tuto akci u tohoto díla nepodporujeme. Vytvořte nebo přepněte na peněženku podporovaného typu aktiv.",
|
"unsupported_asset": "Tuto akci u tohoto díla nepodporujeme. Vytvořte nebo přepněte na peněženku podporovaného typu aktiv.",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"upto": "až ${value}",
|
"upto": "až ${value}",
|
||||||
|
"usb": "USB",
|
||||||
"use": "Přepnout na ",
|
"use": "Přepnout na ",
|
||||||
"use_card_info_three": "Použijte tuto digitální kartu online nebo bezkontaktními platebními metodami.",
|
"use_card_info_three": "Použijte tuto digitální kartu online nebo bezkontaktními platebními metodami.",
|
||||||
"use_card_info_two": "Prostředky jsou převedeny na USD, když jsou drženy na předplaceném účtu, nikoliv na digitální měnu.",
|
"use_card_info_two": "Prostředky jsou převedeny na USD, když jsou drženy na předplaceném účtu, nikoliv na digitální měnu.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "Bitcoin Light-Thema",
|
"bitcoin_light_theme": "Bitcoin Light-Thema",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.",
|
||||||
"Blocks_remaining": "${status} verbleibende Blöcke",
|
"Blocks_remaining": "${status} verbleibende Blöcke",
|
||||||
|
"bluetooth": "Bluetooth",
|
||||||
"bright_theme": "Strahlend hell",
|
"bright_theme": "Strahlend hell",
|
||||||
"bump_fee": "Beulengebühr",
|
"bump_fee": "Beulengebühr",
|
||||||
"buy": "Kaufen",
|
"buy": "Kaufen",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "Glückwunsch!",
|
"congratulations": "Glückwunsch!",
|
||||||
"connect_an_existing_yat": "Verbinden Sie ein vorhandenes Yat",
|
"connect_an_existing_yat": "Verbinden Sie ein vorhandenes Yat",
|
||||||
"connect_yats": "Yats verbinden",
|
"connect_yats": "Yats verbinden",
|
||||||
|
"connect_your_hardware_wallet": "Verbinden Sie Ihre Hardware-Wallet über Bluetooth oder USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "Verbinden Sie Ihre Hardware-Wallet über Bluetooth",
|
||||||
"connection_sync": "Verbindung und Synchronisierung",
|
"connection_sync": "Verbindung und Synchronisierung",
|
||||||
"connectWalletPrompt": "Verbinden Sie Ihr Wallet mit WalletConnect, um Transaktionen durchzuführen",
|
"connectWalletPrompt": "Verbinden Sie Ihr Wallet mit WalletConnect, um Transaktionen durchzuführen",
|
||||||
"contact": "Kontakt",
|
"contact": "Kontakt",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "ist",
|
"is_percentage": "ist",
|
||||||
"last_30_days": "Letzte 30 Tage",
|
"last_30_days": "Letzte 30 Tage",
|
||||||
"learn_more": "Erfahren Sie mehr",
|
"learn_more": "Erfahren Sie mehr",
|
||||||
|
"ledger_connection_error": "Verbindung zum Ledger gescheitert. Bitte versuche es erneut.",
|
||||||
|
"ledger_error_device_locked": "Der Ledger ist gesperrt",
|
||||||
|
"ledger_error_tx_rejected_by_user": "Transaktion auf dem Gerät abgelehnt",
|
||||||
|
"ledger_error_wrong_app": "Bitte stellen Sie sicher, dass Sie die richtige App auf Ihrem Ledger geöffnet haben",
|
||||||
|
"ledger_please_enable_bluetooth": "Bitte aktivieren Sie Bluetooth um sich mit Ihren Ledger zu verbinden.",
|
||||||
"light_theme": "Hell",
|
"light_theme": "Hell",
|
||||||
|
"load_more": "Mehr laden",
|
||||||
"loading_your_wallet": "Wallet wird geladen",
|
"loading_your_wallet": "Wallet wird geladen",
|
||||||
"login": "Einloggen",
|
"login": "Einloggen",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
|
@ -443,6 +452,8 @@
|
||||||
"privacy_settings": "Datenschutzeinstellungen",
|
"privacy_settings": "Datenschutzeinstellungen",
|
||||||
"private_key": "Privater Schlüssel",
|
"private_key": "Privater Schlüssel",
|
||||||
"proceed_after_one_minute": "Wenn der Bildschirm nach 1 Minute nicht weitergeht, überprüfen Sie bitte Ihre E-Mail.",
|
"proceed_after_one_minute": "Wenn der Bildschirm nach 1 Minute nicht weitergeht, überprüfen Sie bitte Ihre E-Mail.",
|
||||||
|
"proceed_on_device": "Fahren Sie auf Ihrem Gerät fort",
|
||||||
|
"proceed_on_device_description": "Bitte befolgen Sie die Anweisungen, die auf Ihrer Hardware-Wallet angezeigt werden",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"provider_error": "${provider}-Fehler",
|
"provider_error": "${provider}-Fehler",
|
||||||
"public_key": "Öffentlicher Schlüssel",
|
"public_key": "Öffentlicher Schlüssel",
|
||||||
|
@ -491,6 +502,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "Stellen Sie Ihre Wallet aus dem 24-Wort-Kombinationscode wieder her",
|
"restore_bitcoin_description_from_seed": "Stellen Sie Ihre Wallet aus dem 24-Wort-Kombinationscode wieder her",
|
||||||
"restore_bitcoin_title_from_keys": "Aus WIF wiederherstellen",
|
"restore_bitcoin_title_from_keys": "Aus WIF wiederherstellen",
|
||||||
"restore_description_from_backup": "Sie können die gesamte Cake Wallet-App aus Ihrer Sicherungsdatei wiederherstellen",
|
"restore_description_from_backup": "Sie können die gesamte Cake Wallet-App aus Ihrer Sicherungsdatei wiederherstellen",
|
||||||
|
"restore_description_from_hardware_wallet": "Stellen Sie eine Wallet von Ledger wieder her",
|
||||||
"restore_description_from_keys": "Stellen Sie Ihr Wallet aus generierten Tastenanschlägen her, die von Ihren privaten Schlüsseln gespeichert wurden",
|
"restore_description_from_keys": "Stellen Sie Ihr Wallet aus generierten Tastenanschlägen her, die von Ihren privaten Schlüsseln gespeichert wurden",
|
||||||
"restore_description_from_seed": "Stellen Sie Ihre Wallet aus den 25 Wörtern oder dem 13-Wort-Kombinationscode wieder her",
|
"restore_description_from_seed": "Stellen Sie Ihre Wallet aus den 25 Wörtern oder dem 13-Wort-Kombinationscode wieder her",
|
||||||
"restore_description_from_seed_keys": "Stellen Sie Ihr Wallet aus Seed/Schlüsseln wieder her, die Sie sicher aufbewahrt haben",
|
"restore_description_from_seed_keys": "Stellen Sie Ihr Wallet aus Seed/Schlüsseln wieder her, die Sie sicher aufbewahrt haben",
|
||||||
|
@ -503,6 +515,7 @@
|
||||||
"restore_seed_keys_restore": "Seed/Schlüssel wiederherstellen",
|
"restore_seed_keys_restore": "Seed/Schlüssel wiederherstellen",
|
||||||
"restore_spend_key_private": "Spend Key (geheim)",
|
"restore_spend_key_private": "Spend Key (geheim)",
|
||||||
"restore_title_from_backup": "Aus einer Sicherungsdatei wiederherstellen",
|
"restore_title_from_backup": "Aus einer Sicherungsdatei wiederherstellen",
|
||||||
|
"restore_title_from_hardware_wallet": "Von Hardware-Wallet wiederherstellen",
|
||||||
"restore_title_from_keys": "Aus Schlüsseln wiederherstellen",
|
"restore_title_from_keys": "Aus Schlüsseln wiederherstellen",
|
||||||
"restore_title_from_seed": "Aus Seed wiederherstellen",
|
"restore_title_from_seed": "Aus Seed wiederherstellen",
|
||||||
"restore_title_from_seed_keys": "Aus Seed/Schlüssel wiederherstellen",
|
"restore_title_from_seed_keys": "Aus Seed/Schlüssel wiederherstellen",
|
||||||
|
@ -754,6 +767,7 @@
|
||||||
"unsupported_asset": "Wir unterstützen diese Aktion für dieses Asset nicht. Bitte erstellen Sie eine Wallet eines unterstützten Asset-Typs oder wechseln Sie zu einer Wallet.",
|
"unsupported_asset": "Wir unterstützen diese Aktion für dieses Asset nicht. Bitte erstellen Sie eine Wallet eines unterstützten Asset-Typs oder wechseln Sie zu einer Wallet.",
|
||||||
"uptime": "Betriebszeit",
|
"uptime": "Betriebszeit",
|
||||||
"upto": "bis zu ${value}",
|
"upto": "bis zu ${value}",
|
||||||
|
"usb": "USB",
|
||||||
"use": "Wechsel zu ",
|
"use": "Wechsel zu ",
|
||||||
"use_card_info_three": "Verwenden Sie die digitale Karte online oder mit kontaktlosen Zahlungsmethoden.",
|
"use_card_info_three": "Verwenden Sie die digitale Karte online oder mit kontaktlosen Zahlungsmethoden.",
|
||||||
"use_card_info_two": "Guthaben werden auf dem Prepaid-Konto in USD umgerechnet, nicht in digitale Währung.",
|
"use_card_info_two": "Guthaben werden auf dem Prepaid-Konto in USD umgerechnet, nicht in digitale Währung.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "Bitcoin Light Theme",
|
"bitcoin_light_theme": "Bitcoin Light Theme",
|
||||||
"bitcoin_payments_require_1_confirmation": "Bitcoin payments require 1 confirmation, which can take 20 minutes or longer. Thanks for your patience! You will be emailed when the payment is confirmed.",
|
"bitcoin_payments_require_1_confirmation": "Bitcoin payments require 1 confirmation, which can take 20 minutes or longer. Thanks for your patience! You will be emailed when the payment is confirmed.",
|
||||||
"Blocks_remaining": "${status} Blocks Remaining",
|
"Blocks_remaining": "${status} Blocks Remaining",
|
||||||
|
"bluetooth": "Bluetooth",
|
||||||
"bright_theme": "Bright",
|
"bright_theme": "Bright",
|
||||||
"bump_fee": "Bump fee",
|
"bump_fee": "Bump fee",
|
||||||
"buy": "Buy",
|
"buy": "Buy",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "Congratulations!",
|
"congratulations": "Congratulations!",
|
||||||
"connect_an_existing_yat": "Connect an existing Yat",
|
"connect_an_existing_yat": "Connect an existing Yat",
|
||||||
"connect_yats": "Connect Yats",
|
"connect_yats": "Connect Yats",
|
||||||
|
"connect_your_hardware_wallet": "Connect your hardware wallet using Bluetooth or USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "Connect your hardware wallet using Bluetooth",
|
||||||
"connection_sync": "Connection and sync",
|
"connection_sync": "Connection and sync",
|
||||||
"connectWalletPrompt": "Connect your wallet with WalletConnect to make transactions",
|
"connectWalletPrompt": "Connect your wallet with WalletConnect to make transactions",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "is",
|
"is_percentage": "is",
|
||||||
"last_30_days": "Last 30 days",
|
"last_30_days": "Last 30 days",
|
||||||
"learn_more": "Learn More",
|
"learn_more": "Learn More",
|
||||||
|
"ledger_connection_error": "Failed to connect to you Ledger. Please try again.",
|
||||||
|
"ledger_error_device_locked": "The Ledger is locked",
|
||||||
|
"ledger_error_tx_rejected_by_user": "Transaction rejected on device",
|
||||||
|
"ledger_error_wrong_app": "Please make sure you opend the right app on your ledger",
|
||||||
|
"ledger_please_enable_bluetooth": "Please enable Bluetooth to detect your Ledger",
|
||||||
"light_theme": "Light",
|
"light_theme": "Light",
|
||||||
|
"load_more": "Load more",
|
||||||
"loading_your_wallet": "Loading your wallet",
|
"loading_your_wallet": "Loading your wallet",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
|
@ -442,6 +451,8 @@
|
||||||
"privacy_settings": "Privacy settings",
|
"privacy_settings": "Privacy settings",
|
||||||
"private_key": "Private key",
|
"private_key": "Private key",
|
||||||
"proceed_after_one_minute": "If the screen doesn’t proceed after 1 minute, check your email.",
|
"proceed_after_one_minute": "If the screen doesn’t proceed after 1 minute, check your email.",
|
||||||
|
"proceed_on_device": "Proceed on your device",
|
||||||
|
"proceed_on_device_description": "Please follow the instructions prompted on your hardware wallet",
|
||||||
"profile": "Profile",
|
"profile": "Profile",
|
||||||
"provider_error": "${provider} error",
|
"provider_error": "${provider} error",
|
||||||
"public_key": "Public key",
|
"public_key": "Public key",
|
||||||
|
@ -490,6 +501,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "Restore your wallet from 24 word combination code",
|
"restore_bitcoin_description_from_seed": "Restore your wallet from 24 word combination code",
|
||||||
"restore_bitcoin_title_from_keys": "Restore from WIF",
|
"restore_bitcoin_title_from_keys": "Restore from WIF",
|
||||||
"restore_description_from_backup": "You can restore the whole Cake Wallet app from your back-up file",
|
"restore_description_from_backup": "You can restore the whole Cake Wallet app from your back-up file",
|
||||||
|
"restore_description_from_hardware_wallet": "Restore from a Ledger hardware wallet",
|
||||||
"restore_description_from_keys": "Restore your wallet from generated keystrokes saved from your private keys",
|
"restore_description_from_keys": "Restore your wallet from generated keystrokes saved from your private keys",
|
||||||
"restore_description_from_seed": "Restore your wallet from either the 25 word or 13 word combination code",
|
"restore_description_from_seed": "Restore your wallet from either the 25 word or 13 word combination code",
|
||||||
"restore_description_from_seed_keys": "Get back your wallet from seed/keys that you've saved to secure place",
|
"restore_description_from_seed_keys": "Get back your wallet from seed/keys that you've saved to secure place",
|
||||||
|
@ -502,6 +514,7 @@
|
||||||
"restore_seed_keys_restore": "Seed/Keys Restore",
|
"restore_seed_keys_restore": "Seed/Keys Restore",
|
||||||
"restore_spend_key_private": "Spend key (private)",
|
"restore_spend_key_private": "Spend key (private)",
|
||||||
"restore_title_from_backup": "Restore from backup",
|
"restore_title_from_backup": "Restore from backup",
|
||||||
|
"restore_title_from_hardware_wallet": "Restore from hardware wallet",
|
||||||
"restore_title_from_keys": "Restore from keys",
|
"restore_title_from_keys": "Restore from keys",
|
||||||
"restore_title_from_seed": "Restore from seed",
|
"restore_title_from_seed": "Restore from seed",
|
||||||
"restore_title_from_seed_keys": "Restore from seed/keys",
|
"restore_title_from_seed_keys": "Restore from seed/keys",
|
||||||
|
@ -752,6 +765,7 @@
|
||||||
"unsupported_asset": "We don't support this action for this asset. Please create or switch to a wallet of a supported asset type.",
|
"unsupported_asset": "We don't support this action for this asset. Please create or switch to a wallet of a supported asset type.",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"upto": "up to ${value}",
|
"upto": "up to ${value}",
|
||||||
|
"usb": "USB",
|
||||||
"use": "Switch to ",
|
"use": "Switch to ",
|
||||||
"use_card_info_three": "Use the digital card online or with contactless payment methods.",
|
"use_card_info_three": "Use the digital card online or with contactless payment methods.",
|
||||||
"use_card_info_two": "Funds are converted to USD when they're held in the prepaid account, not in digital currencies.",
|
"use_card_info_two": "Funds are converted to USD when they're held in the prepaid account, not in digital currencies.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "Tema de la luz de Bitcoin",
|
"bitcoin_light_theme": "Tema de la luz de Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Los pagos de Bitcoin requieren 1 confirmación, que puede demorar 20 minutos o más. ¡Gracias por su paciencia! Se le enviará un correo electrónico cuando se confirme el pago.",
|
"bitcoin_payments_require_1_confirmation": "Los pagos de Bitcoin requieren 1 confirmación, que puede demorar 20 minutos o más. ¡Gracias por su paciencia! Se le enviará un correo electrónico cuando se confirme el pago.",
|
||||||
"Blocks_remaining": "${status} Bloques restantes",
|
"Blocks_remaining": "${status} Bloques restantes",
|
||||||
|
"bluetooth": "Bluetooth",
|
||||||
"bright_theme": "Brillante",
|
"bright_theme": "Brillante",
|
||||||
"bump_fee": "Tarifa",
|
"bump_fee": "Tarifa",
|
||||||
"buy": "Comprar",
|
"buy": "Comprar",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "Felicidades!",
|
"congratulations": "Felicidades!",
|
||||||
"connect_an_existing_yat": "Conectar un Yat existente",
|
"connect_an_existing_yat": "Conectar un Yat existente",
|
||||||
"connect_yats": "Conectar Yats",
|
"connect_yats": "Conectar Yats",
|
||||||
|
"connect_your_hardware_wallet": "Conecte su billetera de hardware con Bluetooth o USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "Conecte su billetera de hardware con Bluetooth",
|
||||||
"connection_sync": "Conexión y sincronización",
|
"connection_sync": "Conexión y sincronización",
|
||||||
"connectWalletPrompt": "Conecte su billetera con WalletConnect para realizar transacciones",
|
"connectWalletPrompt": "Conecte su billetera con WalletConnect para realizar transacciones",
|
||||||
"contact": "Contacto",
|
"contact": "Contacto",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "es",
|
"is_percentage": "es",
|
||||||
"last_30_days": "Últimos 30 días",
|
"last_30_days": "Últimos 30 días",
|
||||||
"learn_more": "Aprende más",
|
"learn_more": "Aprende más",
|
||||||
|
"ledger_connection_error": "No se pudo conectar con su libro mayor. Inténtalo de nuevo.",
|
||||||
|
"ledger_error_device_locked": "El libro mayor está bloqueado",
|
||||||
|
"ledger_error_tx_rejected_by_user": "Transacción rechazada en el dispositivo",
|
||||||
|
"ledger_error_wrong_app": "Por favor, asegúrese de abrir la aplicación correcta en su libro mayor.",
|
||||||
|
"ledger_please_enable_bluetooth": "Habilite Bluetooth para detectar su libro mayor",
|
||||||
"light_theme": "Ligera",
|
"light_theme": "Ligera",
|
||||||
|
"load_more": "Carga más",
|
||||||
"loading_your_wallet": "Cargando tu billetera",
|
"loading_your_wallet": "Cargando tu billetera",
|
||||||
"login": "Iniciar sesión",
|
"login": "Iniciar sesión",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
|
@ -443,6 +452,8 @@
|
||||||
"privacy_settings": "Configuración de privacidad",
|
"privacy_settings": "Configuración de privacidad",
|
||||||
"private_key": "Clave privada",
|
"private_key": "Clave privada",
|
||||||
"proceed_after_one_minute": "Si la pantalla no continúa después de 1 minuto, revisa tu correo electrónico.",
|
"proceed_after_one_minute": "Si la pantalla no continúa después de 1 minuto, revisa tu correo electrónico.",
|
||||||
|
"proceed_on_device": "Continúe con su dispositivo",
|
||||||
|
"proceed_on_device_description": "Siga las instrucciones solicitadas en su billetera de hardware",
|
||||||
"profile": "Perfil",
|
"profile": "Perfil",
|
||||||
"provider_error": "${provider} error",
|
"provider_error": "${provider} error",
|
||||||
"public_key": "Clave pública",
|
"public_key": "Clave pública",
|
||||||
|
@ -491,6 +502,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "Restaure su billetera a partir del código de combinación de 24 palabras",
|
"restore_bitcoin_description_from_seed": "Restaure su billetera a partir del código de combinación de 24 palabras",
|
||||||
"restore_bitcoin_title_from_keys": "Restaurar desde WIF",
|
"restore_bitcoin_title_from_keys": "Restaurar desde WIF",
|
||||||
"restore_description_from_backup": "Puede restaurar toda la aplicación Cake Wallet desde ysu archivo de respaldo",
|
"restore_description_from_backup": "Puede restaurar toda la aplicación Cake Wallet desde ysu archivo de respaldo",
|
||||||
|
"restore_description_from_hardware_wallet": "Restaurar desde una billetera de hardware Ledger",
|
||||||
"restore_description_from_keys": "Restaure su billetera de las pulsaciones de teclas generadas guardadas de sus claves privadas",
|
"restore_description_from_keys": "Restaure su billetera de las pulsaciones de teclas generadas guardadas de sus claves privadas",
|
||||||
"restore_description_from_seed": "Restaure su billetera desde el código de combinación de 25 palabras i de 13 palabras",
|
"restore_description_from_seed": "Restaure su billetera desde el código de combinación de 25 palabras i de 13 palabras",
|
||||||
"restore_description_from_seed_keys": "Recupere su billetera de las semillas/claves que ha guardado en un lugar seguro",
|
"restore_description_from_seed_keys": "Recupere su billetera de las semillas/claves que ha guardado en un lugar seguro",
|
||||||
|
@ -503,6 +515,7 @@
|
||||||
"restore_seed_keys_restore": "Restauración de semillas / llaves",
|
"restore_seed_keys_restore": "Restauración de semillas / llaves",
|
||||||
"restore_spend_key_private": "Spend clave (privado)",
|
"restore_spend_key_private": "Spend clave (privado)",
|
||||||
"restore_title_from_backup": "Restaurar desde un archivo de respaldo",
|
"restore_title_from_backup": "Restaurar desde un archivo de respaldo",
|
||||||
|
"restore_title_from_hardware_wallet": "Restaurar desde la billetera de hardware",
|
||||||
"restore_title_from_keys": "De las claves",
|
"restore_title_from_keys": "De las claves",
|
||||||
"restore_title_from_seed": "De la semilla",
|
"restore_title_from_seed": "De la semilla",
|
||||||
"restore_title_from_seed_keys": "Restaurar desde semilla/claves",
|
"restore_title_from_seed_keys": "Restaurar desde semilla/claves",
|
||||||
|
@ -753,6 +766,7 @@
|
||||||
"unsupported_asset": "No admitimos esta acción para este activo. Cree o cambie a una billetera de un tipo de activo admitido.",
|
"unsupported_asset": "No admitimos esta acción para este activo. Cree o cambie a una billetera de un tipo de activo admitido.",
|
||||||
"uptime": "Tiempo de actividad",
|
"uptime": "Tiempo de actividad",
|
||||||
"upto": "hasta ${value}",
|
"upto": "hasta ${value}",
|
||||||
|
"usb": "USB",
|
||||||
"use": "Utilizar a ",
|
"use": "Utilizar a ",
|
||||||
"use_card_info_three": "Utilice la tarjeta digital en línea o con métodos de pago sin contacto.",
|
"use_card_info_three": "Utilice la tarjeta digital en línea o con métodos de pago sin contacto.",
|
||||||
"use_card_info_two": "Los fondos se convierten a USD cuando se mantienen en la cuenta prepaga, no en monedas digitales.",
|
"use_card_info_two": "Los fondos se convierten a USD cuando se mantienen en la cuenta prepaga, no en monedas digitales.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "Thème léger Bitcoin",
|
"bitcoin_light_theme": "Thème léger Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Les paiements Bitcoin nécessitent 1 confirmation, ce qui peut prendre 20 minutes ou plus. Merci pour votre patience ! Vous serez averti par e-mail lorsque le paiement sera confirmé.",
|
"bitcoin_payments_require_1_confirmation": "Les paiements Bitcoin nécessitent 1 confirmation, ce qui peut prendre 20 minutes ou plus. Merci pour votre patience ! Vous serez averti par e-mail lorsque le paiement sera confirmé.",
|
||||||
"Blocks_remaining": "Blocs Restants : ${status}",
|
"Blocks_remaining": "Blocs Restants : ${status}",
|
||||||
|
"bluetooth": "Bluetooth",
|
||||||
"bright_theme": "Vif",
|
"bright_theme": "Vif",
|
||||||
"bump_fee": "Frais de bosse",
|
"bump_fee": "Frais de bosse",
|
||||||
"buy": "Acheter",
|
"buy": "Acheter",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "Félicitations !",
|
"congratulations": "Félicitations !",
|
||||||
"connect_an_existing_yat": "Connecter un Yat existant",
|
"connect_an_existing_yat": "Connecter un Yat existant",
|
||||||
"connect_yats": "Connecter Yats",
|
"connect_yats": "Connecter Yats",
|
||||||
|
"connect_your_hardware_wallet": "Connectez votre portefeuille matériel à l'aide de Bluetooth ou USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "Connectez votre portefeuille matériel à l'aide de Bluetooth",
|
||||||
"connection_sync": "Connexion et synchronisation",
|
"connection_sync": "Connexion et synchronisation",
|
||||||
"connectWalletPrompt": "Connectez votre portefeuille (wallet) avec WalletConnect pour effectuer des transactions",
|
"connectWalletPrompt": "Connectez votre portefeuille (wallet) avec WalletConnect pour effectuer des transactions",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "est",
|
"is_percentage": "est",
|
||||||
"last_30_days": "30 derniers jours",
|
"last_30_days": "30 derniers jours",
|
||||||
"learn_more": "En savoir plus",
|
"learn_more": "En savoir plus",
|
||||||
|
"ledger_connection_error": "Impossible de se connecter à votre grand livre. Veuillez réessayer.",
|
||||||
|
"ledger_error_device_locked": "Le grand livre est verrouillé",
|
||||||
|
"ledger_error_tx_rejected_by_user": "Transaction rejetée sur l'appareil",
|
||||||
|
"ledger_error_wrong_app": "Veuillez vous assurer d'ouvrir la bonne application sur votre grand livre",
|
||||||
|
"ledger_please_enable_bluetooth": "Veuillez activer Bluetooth pour détecter votre grand livre",
|
||||||
"light_theme": "Clair",
|
"light_theme": "Clair",
|
||||||
|
"load_more": "Charger plus",
|
||||||
"loading_your_wallet": "Chargement de votre portefeuille (wallet)",
|
"loading_your_wallet": "Chargement de votre portefeuille (wallet)",
|
||||||
"login": "Utilisateur",
|
"login": "Utilisateur",
|
||||||
"logout": "Déconnexion",
|
"logout": "Déconnexion",
|
||||||
|
@ -442,6 +451,8 @@
|
||||||
"privacy_settings": "Paramètres de confidentialité",
|
"privacy_settings": "Paramètres de confidentialité",
|
||||||
"private_key": "Clef privée",
|
"private_key": "Clef privée",
|
||||||
"proceed_after_one_minute": "Si l'écran ne s'affiche pas après 1 minute, vérifiez vos e-mails.",
|
"proceed_after_one_minute": "Si l'écran ne s'affiche pas après 1 minute, vérifiez vos e-mails.",
|
||||||
|
"proceed_on_device": "Continuez sur votre appareil",
|
||||||
|
"proceed_on_device_description": "Veuillez suivre les instructions invitées sur votre portefeuille matériel",
|
||||||
"profile": "Profil",
|
"profile": "Profil",
|
||||||
"provider_error": "Erreur de ${provider}",
|
"provider_error": "Erreur de ${provider}",
|
||||||
"public_key": "Clef publique",
|
"public_key": "Clef publique",
|
||||||
|
@ -490,6 +501,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "Restaurer votre portefeuille (wallet) à l'aide d'une phrase secrète (seed) de 24 mots",
|
"restore_bitcoin_description_from_seed": "Restaurer votre portefeuille (wallet) à l'aide d'une phrase secrète (seed) de 24 mots",
|
||||||
"restore_bitcoin_title_from_keys": "Restaurer depuis la chaîne WIF",
|
"restore_bitcoin_title_from_keys": "Restaurer depuis la chaîne WIF",
|
||||||
"restore_description_from_backup": "Vous pouvez restaurer l'intégralité de l'application Cake Wallet depuis un fichier de sauvegarde",
|
"restore_description_from_backup": "Vous pouvez restaurer l'intégralité de l'application Cake Wallet depuis un fichier de sauvegarde",
|
||||||
|
"restore_description_from_hardware_wallet": "Restaurer à partir d'un portefeuille matériel de grand livre",
|
||||||
"restore_description_from_keys": "Restaurer votre portefeuille (wallet) d'après les séquences de touches générées d'après vos clefs privées",
|
"restore_description_from_keys": "Restaurer votre portefeuille (wallet) d'après les séquences de touches générées d'après vos clefs privées",
|
||||||
"restore_description_from_seed": "Restaurer votre portefeuille (wallet) depuis une phrase secrète (seed) de 25 ou 13 mots",
|
"restore_description_from_seed": "Restaurer votre portefeuille (wallet) depuis une phrase secrète (seed) de 25 ou 13 mots",
|
||||||
"restore_description_from_seed_keys": "Restaurez votre portefeuille (wallet) depuis une phrase secrète (seed) ou des clefs que vous avez stockées en lieu sûr",
|
"restore_description_from_seed_keys": "Restaurez votre portefeuille (wallet) depuis une phrase secrète (seed) ou des clefs que vous avez stockées en lieu sûr",
|
||||||
|
@ -502,6 +514,7 @@
|
||||||
"restore_seed_keys_restore": "Restaurer depuis Phrase secrète (seed)/Clefs",
|
"restore_seed_keys_restore": "Restaurer depuis Phrase secrète (seed)/Clefs",
|
||||||
"restore_spend_key_private": "Clef de dépense (spend key) (privée)",
|
"restore_spend_key_private": "Clef de dépense (spend key) (privée)",
|
||||||
"restore_title_from_backup": "Restaurer depuis une sauvegarde",
|
"restore_title_from_backup": "Restaurer depuis une sauvegarde",
|
||||||
|
"restore_title_from_hardware_wallet": "Restaurer à partir du portefeuille matériel",
|
||||||
"restore_title_from_keys": "Restaurer depuis des clefs",
|
"restore_title_from_keys": "Restaurer depuis des clefs",
|
||||||
"restore_title_from_seed": "Restaurer depuis une phrase secrète (seed)",
|
"restore_title_from_seed": "Restaurer depuis une phrase secrète (seed)",
|
||||||
"restore_title_from_seed_keys": "Restaurer depuis une phrase secrète (seed) ou des clefs",
|
"restore_title_from_seed_keys": "Restaurer depuis une phrase secrète (seed) ou des clefs",
|
||||||
|
@ -752,6 +765,7 @@
|
||||||
"unsupported_asset": "Nous ne prenons pas en charge cette action pour cet élément. Veuillez créer ou passer à un portefeuille d'un type d'actif pris en charge.",
|
"unsupported_asset": "Nous ne prenons pas en charge cette action pour cet élément. Veuillez créer ou passer à un portefeuille d'un type d'actif pris en charge.",
|
||||||
"uptime": "Durée de la baisse",
|
"uptime": "Durée de la baisse",
|
||||||
"upto": "jusqu'à ${value}",
|
"upto": "jusqu'à ${value}",
|
||||||
|
"usb": "USB",
|
||||||
"use": "Changer vers code PIN à ",
|
"use": "Changer vers code PIN à ",
|
||||||
"use_card_info_three": "Utilisez la carte numérique en ligne ou avec des méthodes de paiement sans contact.",
|
"use_card_info_three": "Utilisez la carte numérique en ligne ou avec des méthodes de paiement sans contact.",
|
||||||
"use_card_info_two": "Les fonds sont convertis en USD lorsqu'ils sont détenus sur le compte prépayé, et non en devises numériques.",
|
"use_card_info_two": "Les fonds sont convertis en USD lorsqu'ils sont détenus sur le compte prépayé, et non en devises numériques.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "Jigon Hasken Bitcoin",
|
"bitcoin_light_theme": "Jigon Hasken Bitcoin",
|
||||||
"bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.",
|
"bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.",
|
||||||
"Blocks_remaining": "${status} Katanga ya rage",
|
"Blocks_remaining": "${status} Katanga ya rage",
|
||||||
|
"bluetooth": "Bluetooth",
|
||||||
"bright_theme": "Mai haske",
|
"bright_theme": "Mai haske",
|
||||||
"bump_fee": "Buin",
|
"bump_fee": "Buin",
|
||||||
"buy": "Sayi",
|
"buy": "Sayi",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "Taya murna!",
|
"congratulations": "Taya murna!",
|
||||||
"connect_an_existing_yat": "Haɗa Yat da ke akwai",
|
"connect_an_existing_yat": "Haɗa Yat da ke akwai",
|
||||||
"connect_yats": "Haɗa Yats",
|
"connect_yats": "Haɗa Yats",
|
||||||
|
"connect_your_hardware_wallet": "Haɗa Wallake Wallware ɗinku ta Bluetooth ko USB",
|
||||||
|
"connect_your_hardware_wallet_ios": "Haɗa kayan aikinku ta Bluetooth",
|
||||||
"connection_sync": "Haɗi da daidaitawa",
|
"connection_sync": "Haɗi da daidaitawa",
|
||||||
"connectWalletPrompt": "Haɗa walat ɗin ku tare da WalletConnect don yin ma'amala",
|
"connectWalletPrompt": "Haɗa walat ɗin ku tare da WalletConnect don yin ma'amala",
|
||||||
"contact": "Tuntuɓar",
|
"contact": "Tuntuɓar",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "shine",
|
"is_percentage": "shine",
|
||||||
"last_30_days": "Kwanaki 30 na ƙarshe",
|
"last_30_days": "Kwanaki 30 na ƙarshe",
|
||||||
"learn_more": "Ƙara Koyi",
|
"learn_more": "Ƙara Koyi",
|
||||||
|
"ledger_connection_error": "Ba a yi nasarar haɗawa da ku ba. Da fatan za a sake gwadawa.",
|
||||||
|
"ledger_error_device_locked": "An kulle Ledger",
|
||||||
|
"ledger_error_tx_rejected_by_user": "Ma'amala da aka ƙi akan na'urar",
|
||||||
|
"ledger_error_wrong_app": "Da fatan za a tabbata kun yi amfani da app ɗin dama akan dillalarku",
|
||||||
|
"ledger_please_enable_bluetooth": "Da fatan za a kunna Bluetooth don gano Ledger ɗinku",
|
||||||
"light_theme": "Haske",
|
"light_theme": "Haske",
|
||||||
|
"load_more": "Like more",
|
||||||
"loading_your_wallet": "Ana loda walat ɗin ku",
|
"loading_your_wallet": "Ana loda walat ɗin ku",
|
||||||
"login": "Shiga",
|
"login": "Shiga",
|
||||||
"logout": "Fita",
|
"logout": "Fita",
|
||||||
|
@ -444,6 +453,8 @@
|
||||||
"privacy_settings": "Saitunan sirri",
|
"privacy_settings": "Saitunan sirri",
|
||||||
"private_key": "Keɓaɓɓen maɓalli",
|
"private_key": "Keɓaɓɓen maɓalli",
|
||||||
"proceed_after_one_minute": "Idan allon bai ci gaba ba bayan minti 1, duba imel ɗin ku.",
|
"proceed_after_one_minute": "Idan allon bai ci gaba ba bayan minti 1, duba imel ɗin ku.",
|
||||||
|
"proceed_on_device": "Ci gaba akan na'urarka",
|
||||||
|
"proceed_on_device_description": "Da fatan za a bi umarnin akan walatware",
|
||||||
"profile": "Rabin fuska",
|
"profile": "Rabin fuska",
|
||||||
"provider_error": "${provider} kuskure",
|
"provider_error": "${provider} kuskure",
|
||||||
"public_key": "Maɓallin jama'a",
|
"public_key": "Maɓallin jama'a",
|
||||||
|
@ -492,6 +503,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "Dawo da kwalinku daga 24 lambar haɗin kalma",
|
"restore_bitcoin_description_from_seed": "Dawo da kwalinku daga 24 lambar haɗin kalma",
|
||||||
"restore_bitcoin_title_from_keys": "Dawo daga WIF",
|
"restore_bitcoin_title_from_keys": "Dawo daga WIF",
|
||||||
"restore_description_from_backup": "Kuna iya dawo da duk aikace-aikacen Wallet ɗin Cake daga fayil ɗin ajiyar ku",
|
"restore_description_from_backup": "Kuna iya dawo da duk aikace-aikacen Wallet ɗin Cake daga fayil ɗin ajiyar ku",
|
||||||
|
"restore_description_from_hardware_wallet": "Maidowa da walatware mai wanki",
|
||||||
"restore_description_from_keys": "Maido da walat ɗin ku daga maɓallan maɓalli da aka ƙera da aka ajiye daga maɓallan ku na sirri",
|
"restore_description_from_keys": "Maido da walat ɗin ku daga maɓallan maɓalli da aka ƙera da aka ajiye daga maɓallan ku na sirri",
|
||||||
"restore_description_from_seed": "Dawo da kwalinku daga 25 ko 13 lambar haɗin kalma",
|
"restore_description_from_seed": "Dawo da kwalinku daga 25 ko 13 lambar haɗin kalma",
|
||||||
"restore_description_from_seed_keys": "Maido da walat ɗin ku daga iri/maɓallan da kuka adana don amintaccen wuri",
|
"restore_description_from_seed_keys": "Maido da walat ɗin ku daga iri/maɓallan da kuka adana don amintaccen wuri",
|
||||||
|
@ -504,6 +516,7 @@
|
||||||
"restore_seed_keys_restore": "Mayar da iri/Maɓallai",
|
"restore_seed_keys_restore": "Mayar da iri/Maɓallai",
|
||||||
"restore_spend_key_private": "Maɓallin kashewa (key kalmar sirri)",
|
"restore_spend_key_private": "Maɓallin kashewa (key kalmar sirri)",
|
||||||
"restore_title_from_backup": "Dawo daga madadin",
|
"restore_title_from_backup": "Dawo daga madadin",
|
||||||
|
"restore_title_from_hardware_wallet": "Dawowa daga walatware mai wuya",
|
||||||
"restore_title_from_keys": "Dawo daga maɓallai",
|
"restore_title_from_keys": "Dawo daga maɓallai",
|
||||||
"restore_title_from_seed": "Maidowa daga iri",
|
"restore_title_from_seed": "Maidowa daga iri",
|
||||||
"restore_title_from_seed_keys": "Dawo da iri/maɓallai",
|
"restore_title_from_seed_keys": "Dawo da iri/maɓallai",
|
||||||
|
@ -754,6 +767,7 @@
|
||||||
"unsupported_asset": "Ba mu goyi bayan wannan aikin don wannan kadara. Da fatan za a ƙirƙira ko canza zuwa walat na nau'in kadara mai tallafi.",
|
"unsupported_asset": "Ba mu goyi bayan wannan aikin don wannan kadara. Da fatan za a ƙirƙira ko canza zuwa walat na nau'in kadara mai tallafi.",
|
||||||
"uptime": "Sama",
|
"uptime": "Sama",
|
||||||
"upto": "har zuwa ${value}",
|
"upto": "har zuwa ${value}",
|
||||||
|
"usb": "Alib",
|
||||||
"use": "Canja zuwa",
|
"use": "Canja zuwa",
|
||||||
"use_card_info_three": "Yi amfani da katin dijital akan layi ko tare da hanyoyin biyan kuɗi mara lamba.",
|
"use_card_info_three": "Yi amfani da katin dijital akan layi ko tare da hanyoyin biyan kuɗi mara lamba.",
|
||||||
"use_card_info_two": "Ana canza kuɗi zuwa dalar Amurka lokacin da ake riƙe su a cikin asusun da aka riga aka biya, ba cikin agogon dijital ba.",
|
"use_card_info_two": "Ana canza kuɗi zuwa dalar Amurka lokacin da ake riƙe su a cikin asusun da aka riga aka biya, ba cikin agogon dijital ba.",
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
"bitcoin_light_theme": "बिटकॉइन लाइट थीम",
|
"bitcoin_light_theme": "बिटकॉइन लाइट थीम",
|
||||||
"bitcoin_payments_require_1_confirmation": "बिटकॉइन भुगतान के लिए 1 पुष्टिकरण की आवश्यकता होती है, जिसमें 20 मिनट या अधिक समय लग सकता है। आपके धैर्य के लिए धन्यवाद! भुगतान की पुष्टि होने पर आपको ईमेल किया जाएगा।",
|
"bitcoin_payments_require_1_confirmation": "बिटकॉइन भुगतान के लिए 1 पुष्टिकरण की आवश्यकता होती है, जिसमें 20 मिनट या अधिक समय लग सकता है। आपके धैर्य के लिए धन्यवाद! भुगतान की पुष्टि होने पर आपको ईमेल किया जाएगा।",
|
||||||
"Blocks_remaining": "${status} शेष रहते हैं",
|
"Blocks_remaining": "${status} शेष रहते हैं",
|
||||||
|
"bluetooth": "ब्लूटूथ",
|
||||||
"bright_theme": "उज्ज्वल",
|
"bright_theme": "उज्ज्वल",
|
||||||
"bump_fee": "बम्प फीस",
|
"bump_fee": "बम्प फीस",
|
||||||
"buy": "खरीदें",
|
"buy": "खरीदें",
|
||||||
|
@ -144,6 +145,8 @@
|
||||||
"congratulations": "बधाई!",
|
"congratulations": "बधाई!",
|
||||||
"connect_an_existing_yat": "मौजूदा Yat कनेक्ट करें",
|
"connect_an_existing_yat": "मौजूदा Yat कनेक्ट करें",
|
||||||
"connect_yats": "कनेक्ट Yats",
|
"connect_yats": "कनेक्ट Yats",
|
||||||
|
"connect_your_hardware_wallet": "ब्लूटूथ या यूएसबी का उपयोग करके अपने हार्डवेयर वॉलेट को कनेक्ट करें",
|
||||||
|
"connect_your_hardware_wallet_ios": "ब्लूटूथ का उपयोग करके अपने हार्डवेयर वॉलेट को कनेक्ट करें",
|
||||||
"connection_sync": "कनेक्शन और सिंक",
|
"connection_sync": "कनेक्शन और सिंक",
|
||||||
"connectWalletPrompt": "लेन-देन करने के लिए अपने वॉलेट को वॉलेटकनेक्ट से कनेक्ट करें",
|
"connectWalletPrompt": "लेन-देन करने के लिए अपने वॉलेट को वॉलेटकनेक्ट से कनेक्ट करें",
|
||||||
"contact": "संपर्क करें",
|
"contact": "संपर्क करें",
|
||||||
|
@ -328,7 +331,13 @@
|
||||||
"is_percentage": "है",
|
"is_percentage": "है",
|
||||||
"last_30_days": "पिछले 30 दिन",
|
"last_30_days": "पिछले 30 दिन",
|
||||||
"learn_more": "और अधिक जानें",
|
"learn_more": "और अधिक जानें",
|
||||||
|
"ledger_connection_error": "आप लेजर से जुड़ने में विफल रहे। कृपया पुन: प्रयास करें।",
|
||||||
|
"ledger_error_device_locked": "खाता बंद है",
|
||||||
|
"ledger_error_tx_rejected_by_user": "डिवाइस पर लेनदेन खारिज कर दिया गया",
|
||||||
|
"ledger_error_wrong_app": "कृपया सुनिश्चित करें कि आप अपने लेजर पर सही ऐप को खोलते हैं",
|
||||||
|
"ledger_please_enable_bluetooth": "कृपया अपने बहीखाने का पता लगाने के लिए ब्लूटूथ को सक्षम करें",
|
||||||
"light_theme": "रोशनी",
|
"light_theme": "रोशनी",
|
||||||
|
"load_more": "और लोड करें",
|
||||||
"loading_your_wallet": "अपना बटुआ लोड कर रहा है",
|
"loading_your_wallet": "अपना बटुआ लोड कर रहा है",
|
||||||
"login": "लॉग इन करें",
|
"login": "लॉग इन करें",
|
||||||
"logout": "लॉगआउट",
|
"logout": "लॉगआउट",
|
||||||
|
@ -443,6 +452,8 @@
|
||||||
"privacy_settings": "गोपनीयता सेटिंग्स",
|
"privacy_settings": "गोपनीयता सेटिंग्स",
|
||||||
"private_key": "निजी चाबी",
|
"private_key": "निजी चाबी",
|
||||||
"proceed_after_one_minute": "यदि 1 मिनट के बाद भी स्क्रीन आगे नहीं बढ़ती है, तो अपना ईमेल देखें।",
|
"proceed_after_one_minute": "यदि 1 मिनट के बाद भी स्क्रीन आगे नहीं बढ़ती है, तो अपना ईमेल देखें।",
|
||||||
|
"proceed_on_device": "अपने डिवाइस पर आगे बढ़ें",
|
||||||
|
"proceed_on_device_description": "कृपया अपने हार्डवेयर वॉलेट पर दिए गए निर्देशों का पालन करें",
|
||||||
"profile": "प्रोफ़ाइल",
|
"profile": "प्रोफ़ाइल",
|
||||||
"provider_error": "${provider} त्रुटि",
|
"provider_error": "${provider} त्रुटि",
|
||||||
"public_key": "सार्वजनिक कुंजी",
|
"public_key": "सार्वजनिक कुंजी",
|
||||||
|
@ -492,6 +503,7 @@
|
||||||
"restore_bitcoin_description_from_seed": "24 शब्द संयोजन कोड से अपने वॉलेट को पुनर्स्थापित करें",
|
"restore_bitcoin_description_from_seed": "24 शब्द संयोजन कोड से अपने वॉलेट को पुनर्स्थापित करें",
|
||||||
"restore_bitcoin_title_from_keys": "WIF से पुनर्स्थापित करें",
|
"restore_bitcoin_title_from_keys": "WIF से पुनर्स्थापित करें",
|
||||||
"restore_description_from_backup": "आप से पूरे केक वॉलेट एप्लिकेशन को पुनर्स्थापित कर सकते हैं आपकी बैक-अप फ़ाइल",
|
"restore_description_from_backup": "आप से पूरे केक वॉलेट एप्लिकेशन को पुनर्स्थापित कर सकते हैं आपकी बैक-अप फ़ाइल",
|
||||||
|
"restore_description_from_hardware_wallet": "एक लेजर हार्डवेयर वॉलेट से पुनर्स्थापित करें",
|
||||||
"restore_description_from_keys": "अपने वॉलेट को जेनरेट से पुनर्स्थापित करें आपकी निजी कुंजी से कीस्ट्रोक्स सहेजे गए",
|
"restore_description_from_keys": "अपने वॉलेट को जेनरेट से पुनर्स्थापित करें आपकी निजी कुंजी से कीस्ट्रोक्स सहेजे गए",
|
||||||
"restore_description_from_seed": "या तो 25 शब्द से अपने वॉलेट को पुनर्स्थापित करें या 13 शब्द संयोजन कोड",
|
"restore_description_from_seed": "या तो 25 शब्द से अपने वॉलेट को पुनर्स्थापित करें या 13 शब्द संयोजन कोड",
|
||||||
"restore_description_from_seed_keys": "अपने बटुए को बीज से वापस लें/वे कुंजियाँ जिन्हें आपने सुरक्षित स्थान पर सहेजा है",
|
"restore_description_from_seed_keys": "अपने बटुए को बीज से वापस लें/वे कुंजियाँ जिन्हें आपने सुरक्षित स्थान पर सहेजा है",
|
||||||
|
@ -504,6 +516,7 @@
|
||||||
"restore_seed_keys_restore": "बीज / कुंजी पुनर्स्थापित करें",
|
"restore_seed_keys_restore": "बीज / कुंजी पुनर्स्थापित करें",
|
||||||
"restore_spend_key_private": "कुंजी खर्च करें (निजीe)",
|
"restore_spend_key_private": "कुंजी खर्च करें (निजीe)",
|
||||||
"restore_title_from_backup": "बैक-अप फ़ाइल से पुनर्स्थापित करें",
|
"restore_title_from_backup": "बैक-अप फ़ाइल से पुनर्स्थापित करें",
|
||||||
|
"restore_title_from_hardware_wallet": "हार्डवेयर वॉलेट से पुनर्स्थापित करें",
|
||||||
"restore_title_from_keys": "कुंजी से पुनर्स्थापित करें",
|
"restore_title_from_keys": "कुंजी से पुनर्स्थापित करें",
|
||||||
"restore_title_from_seed": "बीज से पुनर्स्थापित करें",
|
"restore_title_from_seed": "बीज से पुनर्स्थापित करें",
|
||||||
"restore_title_from_seed_keys": "बीज / कुंजियों से पुनर्स्थापित करें",
|
"restore_title_from_seed_keys": "बीज / कुंजियों से पुनर्स्थापित करें",
|
||||||
|
@ -754,6 +767,7 @@
|
||||||
"unsupported_asset": "हम इस संपत्ति के लिए इस कार्रवाई का समर्थन नहीं करते हैं. कृपया समर्थित परिसंपत्ति प्रकार का वॉलेट बनाएं या उस पर स्विच करें।",
|
"unsupported_asset": "हम इस संपत्ति के लिए इस कार्रवाई का समर्थन नहीं करते हैं. कृपया समर्थित परिसंपत्ति प्रकार का वॉलेट बनाएं या उस पर स्विच करें।",
|
||||||
"uptime": "अपटाइम",
|
"uptime": "अपटाइम",
|
||||||
"upto": "${value} तक",
|
"upto": "${value} तक",
|
||||||
|
"usb": "USB",
|
||||||
"use": "उपयोग ",
|
"use": "उपयोग ",
|
||||||
"use_card_info_three": "डिजिटल कार्ड का ऑनलाइन या संपर्क रहित भुगतान विधियों के साथ उपयोग करें।",
|
"use_card_info_three": "डिजिटल कार्ड का ऑनलाइन या संपर्क रहित भुगतान विधियों के साथ उपयोग करें।",
|
||||||
"use_card_info_two": "डिजिटल मुद्राओं में नहीं, प्रीपेड खाते में रखे जाने पर निधियों को यूएसडी में बदल दिया जाता है।",
|
"use_card_info_two": "डिजिटल मुद्राओं में नहीं, प्रीपेड खाते में रखे जाने पर निधियों को यूएसडी में बदल दिया जाता है।",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue