LCW-1128-Add-support-for-Trezor (#2565)

* feat: add support for Trezor hardware wallet integration Restore Wallets

* feat: add Trezor hardware wallet support, implement RLP decoding utility, and update transaction signing logic

* feat: add bitcoin trezor signing

* chore: update Trezor Connect dependency to specific commit hash in pubspec_base.yaml

* feat: add trezor support for litecoin

* feat: add colorFilter for device manufacturer icons to align with theme styles

* refactor: optimize Trezor callback handling

* feat: add BitBox and Trezor support for Bitcoin and Litecoin, update transaction signing logic, and improve modularity in hardware wallet services

* refactor: simplify constructor assertions in `BaseBottomSheetWidget` and cleanup Trezor chainId handling logic

* feat: conditionally render device manufacturers based on platform and hide SeedSigner for now
This commit is contained in:
Konstantin Ullrich
2025-10-11 02:12:27 +03:00
committed by GitHub
parent 75ca159d2c
commit 8b76391a2e
38 changed files with 996 additions and 184 deletions
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 32 32">
<path fill="#000" fill-rule="evenodd" d="M7 6a4 4 0 0 1 4-4h10a4 4 0 0 1 4 4v12.191a9 9 0 0 1-1.204 4.498l-3.353 5.81A3 3 0 0 1 17.845 30h-3.69a3 3 0 0 1-2.598-1.5l-3.353-5.811A9 9 0 0 1 7 18.19zm4-2a2 2 0 0 0-2 2v12.191a7 7 0 0 0 .937 3.498l3.352 5.81a1 1 0 0 0 .866.501h3.69a1 1 0 0 0 .866-.5l3.352-5.81a7 7 0 0 0 .937-3.5V6a2 2 0 0 0-2-2z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 447 B

+1 -4
View File
@@ -47,7 +47,6 @@ import 'package:cw_core/unspent_coin_type.dart';
import 'package:cw_core/output_info.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:sp_scanner/sp_scanner.dart';
@@ -163,7 +162,7 @@ abstract class ElectrumWalletBase
[HardwareWalletType? hardwareWalletType]) {
switch (network) {
case LitecoinNetwork.mainnet:
if (hardwareWalletType == HardwareWalletType.ledger)
if ([HardwareWalletType.ledger, HardwareWalletType.trezor].contains(hardwareWalletType))
return Bip44Conf.litecoinMainNet.altKeyNetVer;
return null;
default:
@@ -1423,8 +1422,6 @@ abstract class ElectrumWalletBase
HardwareWalletService? hardwareWalletService;
void setLedgerConnection(ledger.LedgerConnection connection) => throw UnimplementedError();
Future<BtcTransaction> buildHardwareWalletTransaction({
required List<BitcoinBaseOutput> outputs,
required BigInt fee,
+39 -8
View File
@@ -6,7 +6,9 @@ import 'dart:typed_data';
import 'package:bitbox_flutter/bitbox_manager.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/hardware/bitcoin_hardware_wallet_service.dart';
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
import 'package:cw_bitcoin/psbt/v0_deserialize.dart';
import 'package:cw_bitcoin/psbt/v0_finalizer.dart';
import 'package:cw_bitcoin/utils.dart';
@@ -17,7 +19,8 @@ import 'package:ledger_bitcoin/psbt.dart';
class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService {
BitcoinBitboxService(this.manager);
final int bitboxCoinType = 0; // https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L40
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L40
static const int bitboxCoinType = 0;
final BitboxManager manager;
@override
@@ -25,9 +28,11 @@ class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWal
final accounts = <HardwareAccountData>[];
final indexRange = List.generate(limit, (i) => i + index);
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L252
final keyType = 1; // xPUB
for (final i in indexRange) {
final derivationPath = "m/84'/0'/$i'";
final xPub = await manager.getBTCXPub(bitboxCoinType, derivationPath, 1);
final xPub = await manager.getBTCXPub(bitboxCoinType, derivationPath, keyType);
final hd = Bip32Slip10Secp256k1.fromExtendedKey(xPub).childKey(Bip32KeyIndex(0));
final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
@@ -42,7 +47,6 @@ class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWal
return accounts;
}
@override
Future<Uint8List> signTransaction({required String transaction}) async {
final psbt = PsbtV2()..deserialize(base64Decode(transaction));
@@ -56,27 +60,31 @@ class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWal
}
@override
Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) async {
throw UnimplementedError(); // ToDo (Konsti)
}
Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) =>
manager.signBTCMessage(bitboxCoinType, derivationPath ?? "m/84'/0'/0'/0/0", message);
@override
Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
}
class LitecoinBitboxService extends HardwareWalletService {
class LitecoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
LitecoinBitboxService(this.manager);
final BitboxManager manager;
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L40
static const int bitboxCoinType = 2;
@override
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
final accounts = <HardwareAccountData>[];
final indexRange = List.generate(limit, (i) => i + index);
// https://github.com/BitBoxSwiss/bitbox02-api-go/blob/ae070b1d41bf1cf00588fa6f498b4734b5ecd6fc/api/firmware/messages/btc.pb.go#L252
final keyType = 1; // xPUB
for (final i in indexRange) {
final derivationPath = "m/84'/2'/$i'";
final xPub = await manager.getBTCXPub(0, derivationPath, 1);
final xPub = await manager.getBTCXPub(bitboxCoinType, derivationPath, keyType);
final hd = Bip32Slip10Secp256k1.fromExtendedKey(xPub).childKey(Bip32KeyIndex(0));
final address = generateP2WPKHAddress(hd: hd, index: 0, network: LitecoinNetwork.mainnet);
@@ -90,4 +98,27 @@ class LitecoinBitboxService extends HardwareWalletService {
return accounts;
}
@override
Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) =>
manager.signBTCMessage(bitboxCoinType, derivationPath ?? "m/84'/0'/0'/0/0", message);
@override
Future<String> signLitecoinTransaction({
required List<BitcoinBaseOutput> outputs,
required List<PSBTReadyUtxoWithAddress> inputs,
required Map<String, PublicKeyWithDerivationPath> publicKeys,
}) async {
final psbt = PSBTTransactionBuild(inputs: inputs, outputs: outputs, cwOutputs: []).psbt;
final signedPsbt = await manager.signBTCPsbt(bitboxCoinType, base64Encode(psbt.asPsbtV0()));
final transactionRes = PsbtV2()
..deserializeV0(base64Decode(signedPsbt))
..finalizeV0();
return hex.encode(transactionRes.extract());
}
@override
Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
}
@@ -1,5 +1,17 @@
import 'dart:typed_data';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
mixin BitcoinHardwareWalletService {
Future<Uint8List> getMasterFingerprint();
Future<Uint8List> getMasterFingerprint() async => Uint8List.fromList([0, 0, 0, 0]);
}
mixin LitecoinHardwareWalletService on BitcoinHardwareWalletService {
Future<String> signLitecoinTransaction({
required List<BitcoinBaseOutput> outputs,
required List<PSBTReadyUtxoWithAddress> inputs,
required Map<String, PublicKeyWithDerivationPath> publicKeys,
});
}
@@ -1,22 +1,26 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/hardware/bitcoin_hardware_wallet_service.dart';
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_core/hardware/hardware_account_data.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:ledger_litecoin/ledger_litecoin.dart';
class LitecoinLedgerService extends HardwareWalletService {
LitecoinLedgerService(this.ledgerConnection);
class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
LitecoinLedgerService(this.ledgerConnection)
: litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection);
final LedgerConnection ledgerConnection;
final LitecoinLedgerApp litecoinLedgerApp;
@override
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
final litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection);
await litecoinLedgerApp.getVersion();
final accounts = <HardwareAccountData>[];
@@ -42,4 +46,43 @@ class LitecoinLedgerService extends HardwareWalletService {
return accounts;
}
@override
Future<String> signLitecoinTransaction({
required List<BitcoinBaseOutput> outputs,
required List<PSBTReadyUtxoWithAddress> inputs,
required Map<String, PublicKeyWithDerivationPath> publicKeys,
}) {
final readyInputs = <LedgerTransaction>[];
for (final utxo in inputs) {
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
readyInputs.add(LedgerTransaction(
rawTx: utxo.rawTx,
outputIndex: utxo.utxo.vout,
ownerPublicKey: Uint8List.fromList(hex.decode(publicKeyAndDerivationPath.publicKey)),
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
));
}
// Check if we have the key to one of the output addresses to hide change on the device
String? changePath;
for (final output in outputs) {
final maybeChangePath = publicKeys[(output as BitcoinOutput).address.pubKeyHash()];
if (maybeChangePath != null) changePath ??= maybeChangePath.derivationPath;
}
return litecoinLedgerApp.createTransaction(
inputs: readyInputs,
outputs: outputs
.map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value,
Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
.toList(),
changePath: changePath,
sigHashType: 0x01,
additionals: ["bech32"],
isSegWit: true,
useTrustedInputForSegwit: true);
}
}
+179
View File
@@ -0,0 +1,179 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/hardware/bitcoin_hardware_wallet_service.dart';
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_core/hardware/hardware_account_data.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:ledger_bitcoin/psbt.dart';
import 'package:trezor_connect/trezor_connect.dart';
class BitcoinTrezorService extends HardwareWalletService with BitcoinHardwareWalletService {
BitcoinTrezorService(this.connect);
final TrezorConnect connect;
@override
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
final indexRange = List.generate(limit, (i) => i + index);
final requestParams = <TrezorGetPublicKeyParams>[];
for (final i in indexRange) {
requestParams.add(TrezorGetPublicKeyParams(path: "m/84'/0'/$i'"));
}
final accounts = await connect.getPublicKeyBundle(requestParams);
return accounts?.map((account) {
final hd = Bip32Slip10Secp256k1.fromExtendedKey(account.xpub).childKey(Bip32KeyIndex(0));
final address = generateP2WPKHAddress(hd: hd, index: 0, network: BitcoinNetwork.mainnet);
return HardwareAccountData(
address: address,
xpub: account.xpub,
accountIndex: account.path[2] - 0x80000000, // unharden the path to get the index
derivationPath: account.serializedPath,
);
}).toList() ??
[];
}
@override
Future<Uint8List> signTransaction({required String transaction}) async {
final psbt = PsbtV2()..deserialize(base64Decode(transaction));
final inputs = <TrezorTxInput>[];
final inputCount = psbt.getGlobalInputCount();
for (var i = 0; i < inputCount; i++) {
final inputTxRaw = psbt.getInputNonWitnessUtxo(i);
final inputTx = BtcTransaction.fromRaw(hex.encode(inputTxRaw!));
final inputOutputIndex = psbt.getInputOutputIndex(i);
final publicKeys = psbt.inputMaps[i].keys.where((e) => e.startsWith("06"));
final pubkey = Uint8List.fromList(hex.decode(publicKeys.first.substring(2)));
inputs.add(TrezorTxInput(
prevHash: hex.encode(psbt.getInputPreviousTxid(i).reversed.toList()),
prevIndex: inputOutputIndex,
amount: inputTx.outputs[inputOutputIndex].amount.toInt(),
addressPath: psbt.getInputBip32Derivation(i, pubkey)!.$2,
sequence: psbt.getInputSequence(i),
scriptType: "SPENDWITNESS"));
}
final outputs = <TrezorTxOutput>[];
final outputCount = psbt.getGlobalOutputCount();
for (var i = 0; i < outputCount; i++) {
final script = Script.fromRaw(byteData: psbt.getOutputScript(i));
outputs.add(TrezorTxOutput(
amount: psbt.getOutputAmount(i),
address: script.toAddress(),
scriptType: _getScriptType(script.getAddressType()!)
// ToDo: addressPath: psbt.getOutputBip32Derivation(i, pubkey).$2, // To highlight change outputs
));
}
final signedTx = await connect.signTransaction(coin: 'btc', inputs: inputs, outputs: outputs);
return Uint8List.fromList(BytesUtils.fromHexString(signedTx!.serializedTx));
}
@override
Future<Uint8List> signMessage({required Uint8List message, String? derivationPath}) async {
final sig = await connect.signMessage(derivationPath ?? "m/84'/0'/0'/0/0",
message: hex.encode(message), hex: true);
return base64Decode(sig!.signature);
}
}
class LitecoinTrezorService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
LitecoinTrezorService(this.connect);
final TrezorConnect connect;
@override
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
final indexRange = List.generate(limit, (i) => i + index);
final requestParams = <TrezorGetPublicKeyParams>[];
final xpubVersion = Bip44Conf.litecoinMainNet.altKeyNetVer;
for (final i in indexRange) {
final derivationPath = "m/84'/2'/$i'";
requestParams.add(TrezorGetPublicKeyParams(path: derivationPath, coin: "LTC"));
}
final accounts = await connect.getPublicKeyBundle(requestParams);
return accounts?.map((account) {
final hd = Bip32Slip10Secp256k1.fromExtendedKey(account.xpub, xpubVersion)
.childKey(Bip32KeyIndex(0));
final address = generateP2WPKHAddress(hd: hd, index: 0, network: LitecoinNetwork.mainnet);
return HardwareAccountData(
address: address,
xpub: account.xpub,
accountIndex: account.path[2] - 0x80000000, // unharden the path to get the index
derivationPath: account.serializedPath,
);
}).toList() ??
[];
}
@override
Future<String> signLitecoinTransaction({
required List<BitcoinBaseOutput> outputs,
required List<PSBTReadyUtxoWithAddress> inputs,
required Map<String, PublicKeyWithDerivationPath> publicKeys,
}) async {
final readyInputs = inputs
.map((input) => TrezorTxInput(
prevHash: input.utxo.txHash,
prevIndex: input.utxo.vout,
amount: input.utxo.value.toInt(),
addressPath: Bip32PathParser.parse(input.ownerDerivationPath).toList(),
scriptType: "SPENDWITNESS",
))
.toList();
final readyOutputs = outputs.map((output) {
final maybeChangePath = publicKeys[(output as BitcoinOutput).address.pubKeyHash()];
return TrezorTxOutput(
amount: output.toOutput.amount.toInt(),
address: maybeChangePath != null
? null
: output.toOutput.scriptPubKey.toAddress(network: LitecoinNetwork.mainnet),
scriptType: _getScriptType(output.toOutput.scriptPubKey.getAddressType()!),
addressPath: maybeChangePath != null
? Bip32PathParser.parse(maybeChangePath.derivationPath).toList()
: null,
);
}).toList();
final signedTx =
await connect.signTransaction(coin: 'LTC', inputs: readyInputs, outputs: readyOutputs);
return signedTx!.serializedTx;
}
}
String _getScriptType(BitcoinAddressType addressType) {
switch (addressType) {
case P2pkhAddressType.p2pkh:
return "PAYTOADDRESS";
case P2shAddressType.p2wpkhInP2sh:
return "PAYTOSCRIPTHASH";
case SegwitAddresType.p2tr:
return "PAYTOTAPROOT";
case SegwitAddresType.p2wsh:
return "PAYTOP2SHWITNESS";
case SegwitAddresType.p2wpkh:
return "PAYTOWITNESS";
default:
throw Exception("Unknown Address Type");
}
}
+46 -64
View File
@@ -1,53 +1,53 @@
import 'dart:async';
import 'dart:convert';
import 'package:convert/convert.dart' as convert;
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/mweb_utxo.dart';
import 'package:cw_core/unspent_coin_type.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/node.dart';
import 'package:cw_mweb/mwebd.pbgrpc.dart';
import 'package:fixnum/fixnum.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:bitcoin_base/src/crypto/keypair/sign_utils.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:blockchain_utils/signer/ecdsa_signing_key.dart';
import 'package:collection/collection.dart';
import 'package:convert/convert.dart' as convert;
import 'package:crypto/crypto.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/bitcoin_unspent.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_bitcoin/electrum_transaction_info.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
import 'package:cw_bitcoin/hardware/bitcoin_hardware_wallet_service.dart';
import 'package:cw_bitcoin/litecoin_wallet_addresses.dart';
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
import 'package:cw_bitcoin/utils.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:cw_core/mweb_utxo.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/output_info.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/electrum_wallet_snapshot.dart';
import 'package:cw_bitcoin/litecoin_wallet_addresses.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/unspent_coin_type.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_keys_file.dart';
import 'package:cw_core/output_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_mweb/cw_mweb.dart';
import 'package:cw_mweb/mwebd.pbgrpc.dart';
import 'package:fixnum/fixnum.dart';
import 'package:flutter/foundation.dart';
import 'package:grpc/grpc.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:ledger_litecoin/ledger_litecoin.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_mweb/cw_mweb.dart';
import 'package:bitcoin_base/src/crypto/keypair/sign_utils.dart';
import 'package:pointycastle/ecc/api.dart';
import 'package:pointycastle/ecc/curves/secp256k1.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -144,6 +144,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
}
});
}
late final Bip32Slip10Secp256k1? mwebHd;
late final Box<MwebUtxo> mwebUtxosBox;
Timer? _syncTimer;
@@ -160,6 +161,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
bool get hasRescan => true;
List<int> get scanSecret => mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
List<int> get spendSecret => mwebHd!.childKey(Bip32KeyIndex(0x80000001)).privateKey.privKey.raw;
static Future<LitecoinWallet> create(
@@ -1064,9 +1066,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
tx.isMweb = mwebEnabled;
if (!mwebEnabled) {
tx.changeAddressOverride =
(await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
.address;
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
.getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
.address;
return tx;
}
await waitForMwebAddresses();
@@ -1119,7 +1121,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
bool isRegular = !hasMwebInput && !hasMwebOutput;
bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
.getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
.getChangeAddress(
coinTypeToSpendFrom:
shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
.address;
if (isRegular) {
tx.isMweb = false;
@@ -1368,16 +1372,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
return false;
}
LedgerConnection? _ledgerConnection;
LitecoinLedgerApp? _litecoinLedgerApp;
@override
void setLedgerConnection(LedgerConnection connection) {
_ledgerConnection = connection;
_litecoinLedgerApp = LitecoinLedgerApp(_ledgerConnection!,
derivationPath: walletInfo.derivationInfo!.derivationPath!);
}
@override
Future<BtcTransaction> buildHardwareWalletTransaction({
required List<BitcoinBaseOutput> outputs,
@@ -1391,38 +1385,26 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
BitcoinOrdering inputOrdering = BitcoinOrdering.bip69,
BitcoinOrdering outputOrdering = BitcoinOrdering.bip69,
}) async {
final readyInputs = <LedgerTransaction>[];
final masterFingerprint =
await (hardwareWalletService as BitcoinHardwareWalletService).getMasterFingerprint();
final readyInputs = <PSBTReadyUtxoWithAddress>[];
for (final utxo in utxos) {
final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
readyInputs.add(LedgerTransaction(
readyInputs.add(PSBTReadyUtxoWithAddress(
utxo: utxo.utxo,
rawTx: rawTx,
outputIndex: utxo.utxo.vout,
ownerPublicKey: Uint8List.fromList(hex.decode(publicKeyAndDerivationPath.publicKey)),
ownerDetails: utxo.ownerDetails,
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
// sequence: enableRBF ? 0x1 : 0xffffffff,
sequence: 0xffffffff,
ownerMasterFingerprint:masterFingerprint,
ownerPublicKey: publicKeyAndDerivationPath.publicKey,
));
}
String? changePath;
for (final output in outputs) {
final maybeChangePath = publicKeys[(output as BitcoinOutput).address.pubKeyHash()];
if (maybeChangePath != null) changePath ??= maybeChangePath.derivationPath;
}
final rawHex = await _litecoinLedgerApp!.createTransaction(
inputs: readyInputs,
outputs: outputs
.map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value,
Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
.toList(),
changePath: changePath,
sigHashType: 0x01,
additionals: ["bech32"],
isSegWit: true,
useTrustedInputForSegwit: true);
final rawHex = await (hardwareWalletService as LitecoinHardwareWalletService)
.signLitecoinTransaction(outputs: outputs, inputs: readyInputs, publicKeys: publicKeys);
return BtcTransaction.fromRaw(rawHex);
}
+77 -4
View File
@@ -604,8 +604,8 @@ packages:
dependency: "direct main"
description:
path: "packages/ledger-bitcoin"
ref: "6e6ce17c2ba5cfe3d16a60d53f930db8e827b8f3"
resolved-ref: "6e6ce17c2ba5cfe3d16a60d53f930db8e827b8f3"
ref: dc42621f55702d0732681afc2f90bc0047f26633
resolved-ref: dc42621f55702d0732681afc2f90bc0047f26633
url: "https://github.com/cake-tech/ledger-flutter-plus-plugins"
source: git
version: "0.0.3"
@@ -1091,6 +1091,15 @@ packages:
relative: true
source: path
version: "0.0.1"
trezor_connect:
dependency: "direct main"
description:
path: "."
ref: "59b3ceec158d3393b20d0c596cab2abcc62ac8e4"
resolved-ref: "59b3ceec158d3393b20d0c596cab2abcc62ac8e4"
url: "https://github.com/cake-tech/trezor_connect"
source: git
version: "0.0.1"
tuple:
dependency: transitive
description:
@@ -1140,6 +1149,70 @@ packages:
url: "https://github.com/bukata-sa/bc-ur-dart"
source: git
version: "0.1.0"
url_launcher:
dependency: transitive
description:
name: url_launcher
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
url: "https://pub.dev"
source: hosted
version: "6.3.2"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "81777b08c498a292d93ff2feead633174c386291e35612f8da438d6e92c4447e"
url: "https://pub.dev"
source: hosted
version: "6.3.20"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7
url: "https://pub.dev"
source: hosted
version: "6.3.4"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f
url: "https://pub.dev"
source: hosted
version: "3.2.3"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
uuid:
dependency: transitive
description:
@@ -1229,5 +1302,5 @@ packages:
source: hosted
version: "2.2.2"
sdks:
dart: ">=3.7.0-0 <4.0.0"
flutter: ">=3.27.0"
dart: ">=3.8.0 <4.0.0"
flutter: ">=3.29.0"
+5 -1
View File
@@ -49,11 +49,15 @@ dependencies:
git:
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
path: packages/ledger-bitcoin
ref: 6e6ce17c2ba5cfe3d16a60d53f930db8e827b8f3
ref: dc42621f55702d0732681afc2f90bc0047f26633
ledger_litecoin:
git:
url: https://github.com/cake-tech/ledger-flutter-plus-plugins
path: packages/ledger-litecoin
trezor_connect:
git:
url: https://github.com/cake-tech/trezor_connect
ref: 59b3ceec158d3393b20d0c596cab2abcc62ac8e4
bitbox_flutter:
path: ../scripts/bitbox_flutter
socks_socket:
@@ -13,7 +13,7 @@ enum DeviceConnectionType {
case HardwareWalletType.bitbox:
isSupported = [
WalletType.bitcoin,
// WalletType.litecoin,
WalletType.litecoin,
WalletType.ethereum,
WalletType.polygon
].contains(walletType);
@@ -27,6 +27,15 @@ enum DeviceConnectionType {
WalletType.polygon
].contains(walletType);
break;
case HardwareWalletType.trezor:
isSupported = [
// WalletType.monero,
WalletType.bitcoin,
WalletType.litecoin,
WalletType.ethereum,
WalletType.polygon,
].contains(walletType);
break;
}
return isSupported
+7 -2
View File
@@ -35,6 +35,8 @@ enum HardwareWalletType {
seedsigner,
@HiveField(5)
keystone,
@HiveField(6)
trezor,
}
@HiveType(typeId: DerivationInfo.typeId)
@@ -234,8 +236,11 @@ class WalletInfo extends HiveObject {
return showIntroCakePayCard!;
}
bool get isHardwareWallet =>
[HardwareWalletType.ledger, HardwareWalletType.bitbox].contains(hardwareWalletType);
bool get isHardwareWallet => [
HardwareWalletType.bitbox,
HardwareWalletType.ledger,
HardwareWalletType.trezor
].contains(hardwareWalletType);
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
-1
View File
@@ -9,7 +9,6 @@ import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_evm/evm_chain_transaction_model.dart';
import 'package:cw_evm/evm_chain_transaction_priority.dart';
import 'package:cw_evm/evm_erc20_balance.dart';
import 'package:cw_evm/hardware/evm_bitbox_credentials.dart';
import 'package:cw_evm/pending_evm_chain_transaction.dart';
import 'package:cw_evm/.secrets.g.dart' as secrets;
import 'package:flutter/foundation.dart';
+6 -2
View File
@@ -29,8 +29,9 @@ 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_priority.dart';
import 'package:cw_evm/evm_chain_wallet_addresses.dart';
import 'package:cw_evm/hardware/evm_bitbox_credentials.dart';
import 'package:cw_evm/hardware/evm_ledger_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_bitbox_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_ledger_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_trezor_credentials.dart';
import 'package:hex/hex.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
@@ -214,6 +215,9 @@ abstract class EVMChainWalletBase
} else if (walletInfo.hardwareWalletType == HardwareWalletType.bitbox) {
_evmChainPrivateKey = EvmBitboxCredentials(walletInfo.address);
walletAddresses.address = walletInfo.address;
} else if (walletInfo.hardwareWalletType == HardwareWalletType.trezor) {
_evmChainPrivateKey = EvmTrezorCredentials(walletInfo.address);
walletAddresses.address = walletInfo.address;
} else {
_evmChainPrivateKey = await getPrivateKey(
mnemonic: _mnemonic,
@@ -0,0 +1,108 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:cw_core/hardware/device_not_connected_exception.dart' as exception;
import 'package:trezor_connect/trezor_connect.dart';
import 'package:web3dart/crypto.dart';
import 'package:web3dart/web3dart.dart';
import '../utils/rlp_decode.dart' as rlp;
class EvmTrezorCredentials extends CredentialsWithKnownAddress {
final String _address;
TrezorConnect? trezorConnect;
String? derivationPath;
EvmTrezorCredentials(this._address);
@override
EthereumAddress get address => EthereumAddress.fromHex(_address);
void setTrezorConnect(TrezorConnect connection, [String? derivationPath_]) {
trezorConnect = connection;
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 (trezorConnect == null) {
throw exception.DeviceNotConnectedException();
}
if (isEIP1559) payload = payload.sublist(1);
final data = rlp.decode(payload);
TrezorEthereumTransaction tx;
if (isEIP1559) {
final chainId = data[0][0];
final nonce = bytesToHex(data[1], include0x: true);
final maxPriorityFeePerGas = bytesToHex(data[2], include0x: true);
final maxFeePerGas = bytesToHex(data[3], include0x: true);
final maxGas = bytesToHex(data[4], include0x: true);
final to = bytesToHex(data[5], include0x: true);
final value = bytesToHex(data[6], include0x: true);
final txData = bytesToHex(data[7], include0x: true);
tx = TrezorEthereumTransaction(
to: to,
value: value,
maxFeePerGas: maxFeePerGas,
maxPriorityFeePerGas: maxPriorityFeePerGas,
gasLimit: maxGas,
nonce: nonce,
chainId: chainId,
data: txData,
);
} else {
final nonce = bytesToHex(data[0]);
final gasPrice = bytesToHex(data[1]);
final maxGas = bytesToHex(data[2]);
final to = bytesToHex(data[3]);
final value = bytesToHex(data[4]);
final txData = bytesToHex(data[5]);
tx = TrezorEthereumTransaction(
to: to,
value: value,
gasPrice: gasPrice,
gasLimit: maxGas,
nonce: nonce,
chainId: chainId ?? 1,
data: txData,
);
}
final sig = (await trezorConnect!.ethereumSignTransaction(derivationPath!, transaction: tx))!;
final v = int.parse(strip0x(sig.v), radix: 16);
if (isEIP1559) {
return MsgSignature(
BigInt.parse(strip0x(sig.r), radix: 16), BigInt.parse(strip0x(sig.s), radix: 16), v);
}
return MsgSignature(
BigInt.parse(strip0x(sig.r), radix: 16), BigInt.parse(strip0x(sig.s), radix: 16), v);
}
@override
Future<Uint8List> signPersonalMessage(Uint8List payload, {int? chainId}) async {
if (isNotConnected) throw exception.DeviceNotConnectedException();
return hexToBytes((await trezorConnect!
.ethereumSignMessage(derivationPath!, message: bytesToHex(payload), hex: true))
?.signature ??
'');
}
@override
Uint8List signPersonalMessageToUint8List(Uint8List payload, {int? chainId}) =>
throw UnimplementedError("EvmLedgerCredentials.signPersonalMessageToUint8List");
bool get isNotConnected => trezorConnect == null;
}
@@ -0,0 +1,33 @@
import 'dart:async';
import 'package:cw_core/hardware/hardware_account_data.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:trezor_connect/trezor_connect.dart';
class EVMChainTrezorService extends HardwareWalletService {
EVMChainTrezorService(this.connect, {this.chainId = 1});
final TrezorConnect connect;
final int chainId;
@override
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
final indexRange = List.generate(limit, (i) => i + index);
final requestParams = <TrezorGetAddressParams>[];
for (final i in indexRange) {
final derivationPath = "m/44'/60'/$i'/0/0";
requestParams.add(TrezorGetAddressParams(path: derivationPath));
}
final accounts = await connect.ethereumGetAddressBundle(requestParams);
return accounts
?.map((account) => HardwareAccountData(
address: account.address,
accountIndex: account.path[2] - 0x80000000, // unharden the path to get the index
derivationPath: account.serializedPath,
))
.toList() ??
[];
}
}
+101
View File
@@ -0,0 +1,101 @@
import 'dart:typed_data';
import 'package:web3dart/crypto.dart';
/// RLP Decode
///
/// Adapted from https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/rlp
class _Decoded {
Uint8List remainder;
List data;
_Decoded(this.data, this.remainder);
}
int _decodeLength(Uint8List v) {
if (v[0] == 0) throw Exception('invalid RLP: extra zeros');
return int.parse(bytesToHex(v), radix: 16);
}
_Decoded _decode(Uint8List input) {
final firstByte = input[0];
if (firstByte <= 0x7f) {
// a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding.
return _Decoded(input.sublist(0, 1), input.sublist(1));
} else if (firstByte <= 0xb7) {
// string is 0-55 bytes long. A single byte with value 0x80 plus the length of the string followed by the string
// The range of the first byte is [0x80, 0xb7]
final length = firstByte - 0x7f;
// set 0x80 null to 0
final data = firstByte == 0x80 ? Uint8List(0) : input.sublist(1, length);
if (length == 2 && data[0] < 0x80) {
throw Exception('invalid RLP encoding: invalid prefix, single byte < 0x80 are not prefixed');
}
return _Decoded(data, input.sublist(length));
} else if (firstByte <= 0xbf) {
// string is greater than 55 bytes long. A single byte with the value (0xb7 plus the length of the length),
// followed by the length, followed by the string
final lLength = firstByte - 0xb6;
if (input.length - 1 < lLength) {
throw Exception('invalid RLP: not enough bytes for string length');
}
final length = _decodeLength(input.sublist(1, lLength));
if (length <= 55) {
throw Exception('invalid RLP: expected string length to be greater than 55');
}
final data = input.sublist(lLength, length + lLength);
return _Decoded(data, input.sublist(length + lLength));
} else if (firstByte <= 0xf7) {
// a list between 0-55 bytes long
final length = firstByte - 0xbf;
var innerRemainder = input.sublist(1, length);
final decoded = [];
while (innerRemainder.isNotEmpty) {
final d = _decode(innerRemainder);
decoded.add(d.data);
innerRemainder = d.remainder;
}
return _Decoded(decoded, input.sublist(length));
} else {
// a list over 55 bytes long
final lLength = firstByte - 0xf6;
final length = _decodeLength(input.sublist(1, lLength));
if (length < 56) {
throw Exception('invalid RLP: encoded list too short');
}
final totalLength = lLength + length;
if (totalLength > input.length) {
throw Exception('invalid RLP: total length is larger than the data');
}
var innerRemainder = input.sublist(lLength, totalLength);
final decoded = [];
while (innerRemainder.isNotEmpty) {
final d = _decode(innerRemainder);
decoded.add(d.data);
innerRemainder = d.remainder;
}
return _Decoded(decoded, input.sublist(totalLength));
}
}
/// RLP Decoding based on https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
List decode(Uint8List input) {
final decoded = _decode(input);
if (decoded.remainder.isNotEmpty) throw Exception('invalid RLP: remainder must be zero');
return decoded.data;
}
+4
View File
@@ -33,6 +33,10 @@ dependencies:
ref: f4761cd5171d4c1e2e42fd3298261650539fb2db
bitbox_flutter:
path: ../scripts/bitbox_flutter
trezor_connect:
git:
url: https://github.com/cake-tech/trezor_connect
ref: 59b3ceec158d3393b20d0c596cab2abcc62ac8e4
dependency_overrides:
web3dart:
+37 -8
View File
@@ -1,16 +1,45 @@
import 'package:cw_evm/evm_chain_formatter.dart';
import 'package:flutter_test/flutter_test.dart';
import "dart:typed_data";
import "package:cw_evm/evm_chain_formatter.dart";
import "package:cw_evm/utils/rlp_decode.dart";
import "package:flutter_test/flutter_test.dart";
import "package:web3dart/crypto.dart";
import "package:web3dart/src/utils/rlp.dart" as rlp;
void main() {
group('EVMChainFormatter', () {
group('truncateDecimals', () {
test('no decimals', () => expect(EVMChainFormatter.truncateDecimals("5", 6), "5"));
test('less than max decimals',
group("EVMChainFormatter", () {
group("truncateDecimals", () {
test("no decimals", () => expect(EVMChainFormatter.truncateDecimals("5", 6), "5"));
test("less than max decimals",
() => expect(EVMChainFormatter.truncateDecimals("5.00001", 6), "5.00001"));
test('max decimals',
test("max decimals",
() => expect(EVMChainFormatter.truncateDecimals("5.000001", 6), "5.000001"));
test('more than max decimals',
test("more than max decimals",
() => expect(EVMChainFormatter.truncateDecimals("5.0000001", 6), "5.000000"));
});
});
group("rlp decode", () {
test("0x05", () => expect(decode(hexToBytes("0x05")), [5]));
test(
"0xc88363617483646f67",
() => expect(
decode(hexToBytes("0xc88363617483646f67")),
[
[99, 97, 116],
[100, 111, 103]
],
),
);
test(
"cat dog",
() => expect(
decode(Uint8List.fromList(rlp.encode(["cat", "dog"]))),
[
[99, 97, 116],
[100, 111, 103]
],
),
);
});
}
+7 -5
View File
@@ -536,11 +536,6 @@ class CWBitcoin extends Bitcoin {
return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 10).round();
}
@override
void setLedgerConnection(WalletBase wallet, ledger.LedgerConnection connection) {
(wallet as ElectrumWallet).setLedgerConnection(connection);
}
@override
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service) {
(wallet as ElectrumWallet).hardwareWalletService = service;
@@ -560,6 +555,13 @@ class CWBitcoin extends Bitcoin {
return LitecoinBitboxService(manager);
}
@override
HardwareWalletService getTrezorHardwareWalletService(
trezor.TrezorConnect connect, bool isBitcoin) {
if (isBitcoin) return BitcoinTrezorService(connect);
return LitecoinTrezorService(connect);
}
@override
List<ElectrumSubAddress> getSilentPaymentAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
+7
View File
@@ -56,6 +56,7 @@ import 'package:cake_wallet/view_model/dev/secure_preferences.dart';
import 'package:cake_wallet/view_model/dev/shared_preferences.dart';
import 'package:cake_wallet/view_model/hardware_wallet/bitbox_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/trezor_view_model.dart';
import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cake_wallet/tron/tron.dart';
@@ -281,6 +282,7 @@ import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:trezor_connect/trezor_connect.dart';
import 'buy/kryptonim/kryptonim.dart';
import 'buy/meld/meld_buy_provider.dart';
import 'dogecoin/dogecoin.dart';
@@ -400,6 +402,7 @@ Future<void> setup({
switch(type) {
case HardwareWalletType.bitbox: return getIt<BitboxViewModel>();
case HardwareWalletType.ledger: return getIt<LedgerViewModel>();
case HardwareWalletType.trezor: return getIt<TrezorViewModel>();
case HardwareWalletType.cupcake:
case HardwareWalletType.coldcard:
case HardwareWalletType.seedsigner:
@@ -412,6 +415,10 @@ Future<void> setup({
getIt.registerLazySingleton(() => BitboxViewModel());
getIt.registerLazySingleton(() => TrezorConnect("cakewallet://trezor_connect",
appName: "Cake Wallet"));
getIt.registerLazySingleton(() => TrezorViewModel(getIt<TrezorConnect>()));
final secretStore = await SecretStoreBase.load(getIt.get<SecureStorage>());
getIt.registerSingleton<SecretStore>(secretStore);
+7
View File
@@ -188,6 +188,9 @@ class CWEthereum extends Ethereum {
} else if (service is EVMChainBitboxService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
.setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
} else if (service is EVMChainTrezorService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
.setTrezorConnect(service.connect, wallet.walletInfo.derivationInfo?.derivationPath);
}
}
@override
@@ -198,6 +201,10 @@ class CWEthereum extends Ethereum {
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager) =>
EVMChainBitboxService(manager);
@override
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect) =>
EVMChainTrezorService(connect);
@override
List<String> getDefaultTokenContractAddresses() {
return DefaultEthereumErc20Tokens().initialErc20Tokens.map((e) => e.contractAddress).toList();
+2
View File
@@ -62,6 +62,7 @@ import 'package:cw_core/window_size.dart';
import 'package:logging/logging.dart';
import 'package:cake_wallet/core/trade_monitor.dart';
import 'package:cake_wallet/core/reset_service.dart';
import 'package:trezor_connect/trezor_connect.dart';
final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>();
@@ -375,6 +376,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
linkViewModel: linkViewModel,
tradeMonitor: tradeMonitor,
nodeSwitchingService: nodeSwitchingService,
trezorConnect: getIt<TrezorConnect>(),
child: ThemeProvider(
themeStore: appStore.themeStore,
materialAppBuilder: (context, theme, darkTheme, themeMode) => MaterialApp(
+7
View File
@@ -206,6 +206,9 @@ class CWPolygon extends Polygon {
} else if (service is EVMChainBitboxService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmBitboxCredentials)
.setBitbox(service.manager, wallet.walletInfo.derivationInfo?.derivationPath);
} else if (service is EVMChainTrezorService) {
((wallet as EVMChainWallet).evmChainPrivateKey as EvmTrezorCredentials)
.setTrezorConnect(service.connect, wallet.walletInfo.derivationInfo?.derivationPath);
}
}
@@ -217,6 +220,10 @@ class CWPolygon extends Polygon {
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager) =>
EVMChainBitboxService(manager, chainId: 137);
@override
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect) =>
EVMChainTrezorService(connect, chainId: 137);
@override
List<String> getDefaultTokenContractAddresses() =>
DefaultPolygonErc20Tokens().initialPolygonErc20Tokens.map((e) => e.contractAddress).toList();
+6
View File
@@ -345,6 +345,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
(_) => getIt.get<NewWalletTypePage>(
param1: NewWalletTypeArguments(
onTypeSelected: (BuildContext context, WalletType type) {
if (hardwareWalletType == HardwareWalletType.trezor) {
Navigator.of(context).pushNamed(Routes.chooseHardwareWalletAccount,
arguments: [type, hardwareWalletType]);
return;
}
final arguments = ConnectDevicePageParams(
walletType: type,
hardwareWalletType: hardwareWalletType,
@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@@ -29,28 +31,62 @@ class SelectDeviceManufacturerPage extends BasePage {
@override
AppBarStyle get appBarStyle => AppBarStyle.regular;
ColorFilter get _colorFilter =>
ColorFilter.mode(currentTheme.colorScheme.onSurface, BlendMode.srcIn);
List<_DeviceManufacturer> get availableManufacturers => [
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/ledger_man.svg', height: 25),
image: SvgPicture.asset(
'assets/images/hardware_wallet/ledger_man.svg',
height: 25,
colorFilter: _colorFilter,
),
hardwareWalletType: HardwareWalletType.ledger,
),
// _DeviceManufacturer(
// image: SvgPicture.asset('assets/images/hardware_wallet/bitbox_man.svg', height: 25),
// hardwareWalletType: HardwareWalletType.bitbox,
// tag: S.current.new_tag,
// ),
if (Platform.isAndroid) ...[
_DeviceManufacturer(
image: SvgPicture.asset(
'assets/images/hardware_wallet/trezor_man.svg',
height: 25,
colorFilter: _colorFilter,
),
hardwareWalletType: HardwareWalletType.trezor,
tag: S.current.new_tag,
),
_DeviceManufacturer(
image: SvgPicture.asset(
'assets/images/hardware_wallet/bitbox_man.svg',
height: 25,
colorFilter: _colorFilter,
),
hardwareWalletType: HardwareWalletType.bitbox,
tag: S.current.new_tag,
),
],
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/cupcake_man.svg', height: 25),
image: SvgPicture.asset(
'assets/images/hardware_wallet/cupcake_man.svg',
height: 25,
colorFilter: _colorFilter,
),
hardwareWalletType: HardwareWalletType.cupcake,
tag: S.current.new_tag,
),
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/coldcard_man.svg', height: 25),
image: SvgPicture.asset(
'assets/images/hardware_wallet/coldcard_man.svg',
height: 25,
colorFilter: _colorFilter,
),
hardwareWalletType: HardwareWalletType.coldcard,
tag: S.current.new_tag,
),
// _DeviceManufacturer(
// image: SvgPicture.asset('assets/images/hardware_wallet/seedsigner_man.svg', height: 25),
// image: SvgPicture.asset(
// 'assets/images/hardware_wallet/seedsigner_man.svg',
// height: 25,
// colorFilter: _colorFilter,
// ),
// hardwareWalletType: HardwareWalletType.seedsigner,
// tag: S.current.new_tag,
// ),
@@ -61,26 +97,40 @@ class SelectDeviceManufacturerPage extends BasePage {
.toList();
List<_DeviceManufacturer> get comingManufacturers => [
if (!Platform.isAndroid) ...[
_DeviceManufacturer(
image: SvgPicture.asset(
'assets/images/hardware_wallet/bitbox_man.svg',
height: 25,
colorFilter: _colorFilter,
),
hardwareWalletType: HardwareWalletType.bitbox,
tag: S.current.coming_soon_tag,
),
],
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/trezor_man.svg', height: 25),
tag: S.current.coming_soon_tag,
),
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/bitbox_man.svg', height: 25),
hardwareWalletType: HardwareWalletType.bitbox,
tag: S.current.coming_soon_tag,
),
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/seedsigner_man.svg', height: 25),
image: SvgPicture.asset(
'assets/images/hardware_wallet/seedsigner_man.svg',
height: 25,
colorFilter: _colorFilter,
),
hardwareWalletType: HardwareWalletType.seedsigner,
tag: S.current.coming_soon_tag,
),
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/foundation_man.svg', height: 25),
image: SvgPicture.asset(
'assets/images/hardware_wallet/foundation_man.svg',
height: 25,
colorFilter: _colorFilter,
),
tag: S.current.coming_soon_tag,
),
_DeviceManufacturer(
image: SvgPicture.asset('assets/images/hardware_wallet/keystone_man.svg', height: 25),
image: SvgPicture.asset(
'assets/images/hardware_wallet/keystone_man.svg',
height: 25,
colorFilter: _colorFilter,
),
tag: S.current.coming_soon_tag,
),
];
@@ -90,48 +140,52 @@ class SelectDeviceManufacturerPage extends BasePage {
child: Center(
child: Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Column(
children: [
...availableManufacturers.map(
(manufacturer) => Padding(
padding: EdgeInsets.only(top: 10),
child: ManufacturerOptionTile(
image: manufacturer.image,
tag: manufacturer.tag,
onPressed: () {
if (onSelect != null)
return onSelect!.call(context, manufacturer.hardwareWalletType!);
if (isAirgappedWallet(manufacturer.hardwareWalletType)) {
_onScanQRCode(context, manufacturer.hardwareWalletType!);
} else if (manufacturer.hardwareWalletType != null) {
Navigator.pushNamed(context, Routes.connectHardwareWallet,
arguments: [manufacturer.hardwareWalletType]);
}
},
),
),
),
if (showUnavailable) ...[
Padding(
padding: EdgeInsets.only(top: 20, bottom: 10),
child: DottedDivider(color: Theme.of(context).colorScheme.surfaceContainer),
),
...comingManufacturers.map(
height: double.infinity,
child: SingleChildScrollView(
child: Column(
children: [
...availableManufacturers.map(
(manufacturer) => Padding(
padding: EdgeInsets.only(top: 10),
child: ManufacturerOptionTile(
image: manufacturer.image,
tag: manufacturer.tag,
onPressed: () =>
Fluttertoast.showToast(msg: 'One more tap and it might work'),
// Ester egg
isUnavailable: true,
onPressed: () {
if (onSelect != null)
return onSelect!.call(context, manufacturer.hardwareWalletType!);
if (isAirgappedWallet(manufacturer.hardwareWalletType)) {
_onScanQRCode(context, manufacturer.hardwareWalletType!);
} else if (manufacturer.hardwareWalletType != null) {
Navigator.pushNamed(context, Routes.connectHardwareWallet,
arguments: [manufacturer.hardwareWalletType]);
}
},
),
),
),
]
],
if (showUnavailable) ...[
Padding(
padding: EdgeInsets.only(top: 20, bottom: 10),
child: DottedDivider(color: Theme.of(context).colorScheme.surfaceContainer),
),
...comingManufacturers.map(
(manufacturer) => Padding(
padding: EdgeInsets.only(top: 10),
child: ManufacturerOptionTile(
image: manufacturer.image,
tag: manufacturer.tag,
onPressed: () =>
Fluttertoast.showToast(msg: 'One more tap and it might work'),
// Ester egg
isUnavailable: true,
),
),
),
]
],
),
),
),
),
@@ -33,10 +33,12 @@ class CryptoBalanceWidget extends StatelessWidget {
switch (dashboardViewModel.wallet.hardwareWalletType) {
case null:
return null;
case HardwareWalletType.ledger:
return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
case HardwareWalletType.bitbox:
return "assets/images/hardware_wallet/device_bitbox.svg";
case HardwareWalletType.ledger:
return "assets/images/hardware_wallet/device_ledger_nano_x.svg";
case HardwareWalletType.trezor:
return "assets/images/hardware_wallet/device_trezor_safe_5.svg";
case HardwareWalletType.cupcake:
return "assets/images/cupcake.svg";
case HardwareWalletType.coldcard:
+17 -8
View File
@@ -1,26 +1,28 @@
import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/node_switching_service.dart';
import 'package:cake_wallet/core/totp_request_details.dart';
import 'package:cake_wallet/core/trade_monitor.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:cake_wallet/reactions/wallet_utils.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/utils/socket_health_logger.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:mobx/mobx.dart';
import 'package:trezor_connect/trezor_connect.dart';
import 'package:uni_links/uni_links.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
import 'package:cake_wallet/reactions/wallet_utils.dart';
import 'package:cw_core/utils/socket_health_logger.dart';
class Root extends StatefulWidget {
Root({
@@ -33,6 +35,7 @@ class Root extends StatefulWidget {
required this.linkViewModel,
required this.tradeMonitor,
required this.nodeSwitchingService,
required this.trezorConnect,
}) : super(key: key);
final AuthenticationStore authenticationStore;
@@ -43,6 +46,7 @@ class Root extends StatefulWidget {
final LinkViewModel linkViewModel;
final TradeMonitor tradeMonitor;
final NodeSwitchingService nodeSwitchingService;
final TrezorConnect trezorConnect;
@override
RootState createState() => RootState();
@@ -110,6 +114,11 @@ class RootState extends State<Root> with WidgetsBindingObserver {
widget.linkViewModel.currentLink = uri;
if (uri.toString().startsWith(widget.trezorConnect.callbackBackUri)) {
widget.trezorConnect.handleCallback(uri);
return;
}
bool requireAuth = await widget.authService.requireAuth();
if (!requireAuth && widget.authenticationStore.state == AuthenticationState.allowed) {
@@ -23,8 +23,7 @@ abstract class BaseBottomSheet extends StatelessWidget {
this.leftActionButtonKey,
this.rightActionButtonKey,
required this.maxHeight,
}) : assert(footerType == FooterType.none,
'currentTheme is required unless footerType is none');
});
final String titleText;
final String? titleIconPath;
@@ -11,6 +11,7 @@ import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/root_dir.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:bitbox_flutter/bitbox_flutter.dart' as sdk;
import 'package:mobx/mobx.dart';
@@ -28,6 +29,9 @@ abstract class BitboxViewModelBase extends HardwareWalletViewModel with Store {
}
}
@override
HardwareWalletType get hardwareWalletType => HardwareWalletType.bitbox;
@override
@observable
bool isBleEnabled = false;
@@ -1,9 +1,11 @@
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
abstract class HardwareWalletViewModel {
HardwareWalletType get hardwareWalletType;
bool get isConnected;
bool get isBleEnabled;
bool get hasBluetooth;
@@ -54,6 +54,9 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
}
}
@override
HardwareWalletType get hardwareWalletType => HardwareWalletType.ledger;
@override
@observable
bool isBleEnabled = false;
@@ -0,0 +1,82 @@
import 'dart:async';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/hardware_wallet/hardware_wallet_device.dart';
import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cake_wallet/polygon/polygon.dart';
import 'package:cake_wallet/view_model/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:cw_core/hardware/hardware_wallet_service.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:trezor_connect/trezor_connect.dart' as sdk;
import 'package:mobx/mobx.dart';
part 'trezor_view_model.g.dart';
class TrezorViewModel = TrezorViewModelBase with _$TrezorViewModel;
abstract class TrezorViewModelBase extends HardwareWalletViewModel with Store {
final sdk.TrezorConnect trezorConnect;
TrezorViewModelBase(this.trezorConnect);
@override
HardwareWalletType get hardwareWalletType => HardwareWalletType.trezor;
@override
@observable
bool isBleEnabled = false;
@override
bool get hasBluetooth => false;
@override
Future<void> updateBleState() async {}
@override
Stream<HardwareWalletDevice> scanForBleDevices() => throw UnimplementedError();
@override
Future<List<HardwareWalletDevice>> getAllUsbDevices() async => [];
@override
Future<void> stopScanning() async {}
@override
Future<bool> connectDevice(HardwareWalletDevice device, WalletType type) async => true;
@override
bool get isConnected => true;
@override
HardwareWalletService getHardwareWalletService(WalletType type) {
switch (type) {
case WalletType.bitcoin:
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, true);
case WalletType.litecoin:
return bitcoin!.getTrezorHardwareWalletService(trezorConnect, false);
case WalletType.ethereum:
return ethereum!.getTrezorHardwareWalletService(trezorConnect);
case WalletType.polygon:
return polygon!.getTrezorHardwareWalletService(trezorConnect);
default:
throw UnimplementedError();
}
}
@override
void initWallet(WalletBase wallet) {
switch (wallet.type) {
case WalletType.bitcoin:
case WalletType.litecoin:
return bitcoin!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
case WalletType.ethereum:
return ethereum!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
case WalletType.polygon:
return polygon!.setHardwareWalletService(wallet, getHardwareWalletService(wallet.type));
default:
throw Exception('Unexpected wallet type: ${wallet.type}');
}
}
}
+1 -2
View File
@@ -37,7 +37,6 @@ import 'package:cake_wallet/utils/payment_request.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/hardware_wallet/hardware_wallet_view_model.dart';
import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
import 'package:cake_wallet/view_model/send/fees_view_model.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
@@ -942,7 +941,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
);
if (token != null) {
selectedCryptoCurrency = token as CryptoCurrency;
selectedCryptoCurrency = token;
}
}
}
@@ -102,10 +102,7 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with
throw Exception('Unexpected type: ${type.toString()}');
}
if (hardwareWalletVM is LedgerViewModel)
credentials.hardwareWalletType = HardwareWalletType.ledger;
else
credentials.hardwareWalletType = HardwareWalletType.bitbox;
credentials.hardwareWalletType = hardwareWalletVM.hardwareWalletType;
return credentials;
}
+5 -1
View File
@@ -4,7 +4,7 @@ dependencies:
flutter_localizations:
sdk: flutter
intl: any
url_launcher: 6.3.1
url_launcher: 6.3.2
qr_flutter:
git:
url: https://github.com/cake-tech/qr.flutter.git
@@ -116,6 +116,10 @@ dependencies:
git:
url: https://github.com/vespr-wallet/ledger-flutter-plus
ref: 60817d4b20144f9da9029f5034790272795b9d38
trezor_connect:
git:
url: https://github.com/cake-tech/trezor_connect
ref: 59b3ceec158d3393b20d0c596cab2abcc62ac8e4
bitbox_flutter:
path: ./scripts/bitbox_flutter
hashlib: 1.19.2
+15 -5
View File
@@ -112,6 +112,7 @@ import 'package:cw_core/get_height_by_date.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
import 'package:bitbox_flutter/bitbox_flutter.dart' as bitbox;
import 'package:trezor_connect/trezor_connect.dart' as trezor;
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:bip39/bip39.dart' as bip39;
""";
@@ -138,6 +139,7 @@ import 'package:cw_bitcoin/litecoin_wallet.dart';
import 'package:cw_bitcoin/hardware/bitcoin_ledger_service.dart';
import 'package:cw_bitcoin/hardware/litecoin_ledger_service.dart';
import 'package:cw_bitcoin/hardware/bitbox_service.dart';
import 'package:cw_bitcoin/hardware/trezor_service.dart';
import 'package:mobx/mobx.dart';
""";
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
@@ -245,10 +247,10 @@ abstract class Bitcoin {
void deleteSilentPaymentAddress(Object wallet, String address);
Future<void> updateFeeRates(Object wallet);
int getMaxCustomFeeRate(Object wallet);
void setLedgerConnection(WalletBase wallet, ledger.LedgerConnection connection);
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection, bool isBitcoin);
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager, bool isBitcoin);
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect, bool isBitcoin);
List<Output> updateOutputs(PendingTransaction pendingTransaction, List<Output> outputs);
bool txIsReceivedSilentPayment(TransactionInfo txInfo);
bool txIsMweb(TransactionInfo txInfo);
@@ -697,6 +699,7 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
import 'package:bitbox_flutter/bitbox_flutter.dart' as bitbox;
import 'package:trezor_connect/trezor_connect.dart' as trezor;
import 'package:web3dart/web3dart.dart';
""";
@@ -707,11 +710,13 @@ import 'package:cw_evm/evm_chain_transaction_credentials.dart';
import 'package:cw_evm/evm_chain_transaction_info.dart';
import 'package:cw_evm/evm_chain_transaction_priority.dart';
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
import 'package:cw_evm/hardware/evm_ledger_credentials.dart';
import 'package:cw_evm/hardware/evm_bitbox_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_bitbox_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_ledger_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_trezor_credentials.dart';
import 'package:cw_evm/evm_chain_wallet.dart';
import 'package:cw_evm/hardware/evm_chain_bitbox_service.dart';
import 'package:cw_evm/hardware/evm_chain_ledger_service.dart';
import 'package:cw_evm/hardware/evm_chain_trezor_service.dart';
import 'package:cw_ethereum/ethereum_client.dart';
import 'package:cw_ethereum/ethereum_wallet.dart';
@@ -780,6 +785,7 @@ abstract class Ethereum {
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager);
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect);
List<String> getDefaultTokenContractAddresses();
bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress);
}
@@ -822,6 +828,7 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as ledger;
import 'package:bitbox_flutter/bitbox_flutter.dart' as bitbox;
import 'package:trezor_connect/trezor_connect.dart' as trezor;
import 'package:web3dart/web3dart.dart';
""";
@@ -832,11 +839,13 @@ import 'package:cw_evm/evm_chain_transaction_credentials.dart';
import 'package:cw_evm/evm_chain_transaction_info.dart';
import 'package:cw_evm/evm_chain_transaction_priority.dart';
import 'package:cw_evm/evm_chain_wallet_creation_credentials.dart';
import 'package:cw_evm/hardware/evm_ledger_credentials.dart';
import 'package:cw_evm/hardware/evm_bitbox_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_bitbox_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_ledger_credentials.dart';
import 'package:cw_evm/hardware/evm_chain_trezor_credentials.dart';
import 'package:cw_evm/evm_chain_wallet.dart';
import 'package:cw_evm/hardware/evm_chain_bitbox_service.dart';
import 'package:cw_evm/hardware/evm_chain_ledger_service.dart';
import 'package:cw_evm/hardware/evm_chain_trezor_service.dart';
import 'package:cw_polygon/polygon_client.dart';
import 'package:cw_polygon/polygon_wallet.dart';
@@ -895,6 +904,7 @@ abstract class Polygon {
void setHardwareWalletService(WalletBase wallet, HardwareWalletService service);
HardwareWalletService getLedgerHardwareWalletService(ledger.LedgerConnection connection);
HardwareWalletService getBitboxHardwareWalletService(bitbox.BitboxManager manager);
HardwareWalletService getTrezorHardwareWalletService(trezor.TrezorConnect connect);
List<String> getDefaultTokenContractAddresses();
bool isTokenAlreadyAdded(WalletBase wallet, String contractAddress);
}