mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
4b4d8a4840
* Add passphrase support for Eth, Polygon, and Tron * move passphrase to advanced settings even for restore
417 lines
12 KiB
Dart
417 lines
12 KiB
Dart
part of 'nano.dart';
|
|
|
|
class CWNanoAccountList extends NanoAccountList {
|
|
CWNanoAccountList(this._wallet);
|
|
final Object _wallet;
|
|
|
|
@override
|
|
@computed
|
|
ObservableList<NanoAccount> get accounts {
|
|
final nanoWallet = _wallet as NanoWallet;
|
|
final accounts = nanoWallet.walletAddresses.accountList.accounts
|
|
.map((acc) => NanoAccount(id: acc.id, label: acc.label, balance: acc.balance))
|
|
.toList();
|
|
return ObservableList<NanoAccount>.of(accounts);
|
|
}
|
|
|
|
@override
|
|
void update(Object wallet) {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
nanoWallet.walletAddresses.accountList.update(null);
|
|
}
|
|
|
|
@override
|
|
void refresh(Object wallet) {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
nanoWallet.walletAddresses.accountList.refresh();
|
|
}
|
|
|
|
@override
|
|
Future<List<NanoAccount>> getAll(Object wallet) async {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
return (await nanoWallet.walletAddresses.accountList.getAll())
|
|
.map((acc) => NanoAccount(id: acc.id, label: acc.label, balance: acc.balance))
|
|
.toList();
|
|
}
|
|
|
|
@override
|
|
Future<void> addAccount(Object wallet, {required String label}) async {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
await nanoWallet.walletAddresses.accountList.addAccount(label: label);
|
|
}
|
|
|
|
@override
|
|
Future<void> setLabelAccount(Object wallet,
|
|
{required int accountIndex, required String label}) async {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
await nanoWallet.walletAddresses.accountList
|
|
.setLabelAccount(accountIndex: accountIndex, label: label);
|
|
}
|
|
}
|
|
|
|
class CWNano extends Nano {
|
|
@override
|
|
NanoAccountList getAccountList(Object wallet) {
|
|
return CWNanoAccountList(wallet);
|
|
}
|
|
|
|
@override
|
|
Account getCurrentAccount(Object wallet) {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
final acc = nanoWallet.walletAddresses.account;
|
|
return Account(id: acc!.id, label: acc.label, balance: acc.balance);
|
|
}
|
|
|
|
@override
|
|
void setCurrentAccount(Object wallet, int id, String label, String? balance) {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
nanoWallet.walletAddresses.account = NanoAccount(id: id, label: label, balance: balance);
|
|
nanoWallet.regenerateAddress();
|
|
}
|
|
|
|
@override
|
|
List<String> getNanoWordList(String language) {
|
|
return NanoMnemomics.WORDLIST;
|
|
}
|
|
|
|
@override
|
|
WalletService createNanoWalletService(Box<WalletInfo> walletInfoSource, bool isDirect) {
|
|
return NanoWalletService(walletInfoSource, isDirect);
|
|
}
|
|
|
|
@override
|
|
Map<String, String> getKeys(Object wallet) {
|
|
final nanoWallet = wallet as NanoWallet;
|
|
final keys = nanoWallet.keys;
|
|
return <String, String>{
|
|
"seedKey": keys.seedKey,
|
|
};
|
|
}
|
|
|
|
@override
|
|
WalletCredentials createNanoNewWalletCredentials({
|
|
required String name,
|
|
WalletInfo? walletInfo,
|
|
String? password,
|
|
String? mnemonic,
|
|
String? parentAddress,
|
|
String? passphrase,
|
|
}) =>
|
|
NanoNewWalletCredentials(
|
|
name: name,
|
|
password: password,
|
|
mnemonic: mnemonic,
|
|
parentAddress: parentAddress,
|
|
walletInfo: walletInfo,
|
|
passphrase: passphrase,
|
|
);
|
|
|
|
@override
|
|
WalletCredentials createNanoRestoreWalletFromSeedCredentials({
|
|
required String name,
|
|
required String password,
|
|
required String mnemonic,
|
|
required DerivationType derivationType,
|
|
String? passphrase,
|
|
}) {
|
|
if (mnemonic.split(" ").length == 12 && derivationType != DerivationType.bip39) {
|
|
throw Exception("Invalid mnemonic for derivation type!");
|
|
}
|
|
|
|
return NanoRestoreWalletFromSeedCredentials(
|
|
name: name,
|
|
password: password,
|
|
mnemonic: mnemonic,
|
|
derivationType: derivationType,
|
|
passphrase: passphrase,
|
|
);
|
|
}
|
|
|
|
@override
|
|
WalletCredentials createNanoRestoreWalletFromKeysCredentials({
|
|
required String name,
|
|
required String password,
|
|
required String seedKey,
|
|
required DerivationType derivationType,
|
|
}) {
|
|
if (seedKey.length == 128 && derivationType != DerivationType.bip39) {
|
|
throw Exception("Invalid seed key length for derivation type!");
|
|
}
|
|
|
|
return NanoRestoreWalletFromKeysCredentials(
|
|
name: name,
|
|
password: password,
|
|
seedKey: seedKey,
|
|
derivationType: derivationType,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Object createNanoTransactionCredentials(List<Output> outputs) {
|
|
return NanoTransactionCredentials(
|
|
outputs
|
|
.map((out) => OutputInfo(
|
|
fiatAmount: out.fiatAmount,
|
|
cryptoAmount: out.cryptoAmount,
|
|
address: out.address,
|
|
note: out.note,
|
|
sendAll: out.sendAll,
|
|
extractedAddress: out.extractedAddress,
|
|
isParsedAddress: out.isParsedAddress,
|
|
formattedCryptoAmount: out.formattedCryptoAmount,
|
|
))
|
|
.toList(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> changeRep(Object wallet, String address) async {
|
|
if ((wallet as NanoWallet).transactionHistory.transactions.isEmpty) {
|
|
throw Exception("Can't change representative without an existing transaction history");
|
|
}
|
|
return wallet.changeRep(address);
|
|
}
|
|
|
|
@override
|
|
Future<bool> updateTransactions(Object wallet) async {
|
|
return (wallet as NanoWallet).updateTransactions();
|
|
}
|
|
|
|
@override
|
|
BigInt getTransactionAmountRaw(TransactionInfo transactionInfo) {
|
|
return (transactionInfo as NanoTransactionInfo).amountRaw;
|
|
}
|
|
|
|
@override
|
|
String getRepresentative(Object wallet) {
|
|
return (wallet as NanoWallet).representative;
|
|
}
|
|
|
|
@override
|
|
Future<List<N2Node>> getN2Reps(Object wallet) async {
|
|
return (wallet as NanoWallet).getN2Reps();
|
|
}
|
|
|
|
@override
|
|
bool isRepOk(Object wallet) {
|
|
return (wallet as NanoWallet).isRepOk;
|
|
}
|
|
}
|
|
|
|
class CWNanoUtil extends NanoUtil {
|
|
@override
|
|
bool isValidBip39Seed(String seed) {
|
|
return NanoDerivations.isValidBip39Seed(seed);
|
|
}
|
|
|
|
// number util:
|
|
|
|
static const int maxDecimalDigits = 6; // Max digits after decimal
|
|
BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
|
|
BigInt rawPerNyano = BigInt.parse("1000000000000000000000000");
|
|
BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
|
|
BigInt rawPerXMR = BigInt.parse("1000000000000");
|
|
BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
|
|
|
|
@override
|
|
String getRawAsUsableString(String? raw, BigInt rawPerCur) {
|
|
return NanoAmounts.getRawAsUsableString(raw, rawPerCur);
|
|
}
|
|
|
|
@override
|
|
String getRawAccuracy(String? raw, BigInt rawPerCur) {
|
|
return NanoAmounts.getRawAccuracy(raw, rawPerCur);
|
|
}
|
|
|
|
@override
|
|
String getAmountAsRaw(String amount, BigInt rawPerCur) {
|
|
return NanoAmounts.getAmountAsRaw(amount, rawPerCur);
|
|
}
|
|
|
|
@override
|
|
Future<AccountInfoResponse?> getInfoFromSeedOrMnemonic(
|
|
DerivationType derivationType, {
|
|
String? seedKey,
|
|
String? mnemonic,
|
|
required Node node,
|
|
}) async {
|
|
NanoClient nanoClient = NanoClient();
|
|
nanoClient.connect(node);
|
|
String? publicAddress;
|
|
|
|
if (seedKey == null && mnemonic == null) {
|
|
throw Exception("One of seed key OR mnemonic must be provided!");
|
|
}
|
|
|
|
try {
|
|
if (seedKey != null) {
|
|
if (seedKey.length == 64) {
|
|
try {
|
|
mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
|
|
} catch (e) {
|
|
print("not a valid 'nano' seed key");
|
|
}
|
|
}
|
|
if (derivationType == DerivationType.bip39) {
|
|
publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
|
} else if (derivationType == DerivationType.nano) {
|
|
publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
|
}
|
|
}
|
|
|
|
if (derivationType == DerivationType.bip39) {
|
|
if (mnemonic != null) {
|
|
seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
|
|
publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
|
}
|
|
}
|
|
|
|
if (derivationType == DerivationType.nano) {
|
|
if (mnemonic != null) {
|
|
seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
|
|
publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
|
}
|
|
}
|
|
} catch (_) {}
|
|
|
|
if (publicAddress == null) {
|
|
// we couldn't derive a public address for the derivation type provided
|
|
// i.e. a bip39 seed was provided and we were instructed to derive a "nano" type address
|
|
return null;
|
|
}
|
|
|
|
AccountInfoResponse? accountInfo = await nanoClient.getAccountInfo(publicAddress);
|
|
if (accountInfo == null) {
|
|
accountInfo = AccountInfoResponse(
|
|
frontier: "", balance: "0", representative: "", confirmationHeight: 0);
|
|
}
|
|
accountInfo.address = publicAddress;
|
|
return accountInfo;
|
|
}
|
|
|
|
@override
|
|
Future<List<DerivationType>> compareDerivationMethods({
|
|
String? mnemonic,
|
|
String? privateKey,
|
|
required Node node,
|
|
}) async {
|
|
String? seedKey = privateKey;
|
|
|
|
if (mnemonic?.split(' ').length == 12) {
|
|
return [DerivationType.bip39];
|
|
}
|
|
if (seedKey?.length == 128) {
|
|
return [DerivationType.bip39];
|
|
} else if (seedKey?.length == 64) {
|
|
try {
|
|
mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey!);
|
|
} catch (e) {
|
|
print("not a valid 'nano' seed key");
|
|
}
|
|
}
|
|
|
|
late String publicAddressStandard;
|
|
late String publicAddressBip39;
|
|
|
|
try {
|
|
NanoClient nanoClient = NanoClient();
|
|
nanoClient.connect(node);
|
|
|
|
if (mnemonic != null) {
|
|
seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
|
|
publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
|
|
|
seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
|
|
publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
|
} else if (seedKey != null) {
|
|
try {
|
|
publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
|
|
} catch (e) {
|
|
return [DerivationType.nano];
|
|
}
|
|
try {
|
|
publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
|
|
} catch (e) {
|
|
return [DerivationType.bip39];
|
|
}
|
|
}
|
|
|
|
// check if account has a history:
|
|
AccountInfoResponse? bip39Info;
|
|
AccountInfoResponse? standardInfo;
|
|
|
|
try {
|
|
bip39Info = await nanoClient.getAccountInfo(publicAddressBip39);
|
|
} catch (e) {
|
|
bip39Info = null;
|
|
}
|
|
try {
|
|
standardInfo = await nanoClient.getAccountInfo(publicAddressStandard);
|
|
} catch (e) {
|
|
standardInfo = null;
|
|
}
|
|
|
|
// one of these is *probably* null:
|
|
if (bip39Info == null && standardInfo != null) {
|
|
return [DerivationType.nano];
|
|
} else if (standardInfo == null && bip39Info != null) {
|
|
return [DerivationType.bip39];
|
|
}
|
|
|
|
// we don't know for sure:
|
|
return [DerivationType.nano, DerivationType.bip39];
|
|
} catch (e) {
|
|
return [DerivationType.nano, DerivationType.bip39];
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<List<DerivationInfo>> getDerivationsFromMnemonic({
|
|
String? mnemonic,
|
|
String? seedKey,
|
|
required Node node,
|
|
}) async {
|
|
List<DerivationInfo> list = [];
|
|
|
|
List<DerivationType> possibleDerivationTypes = await compareDerivationMethods(
|
|
mnemonic: mnemonic,
|
|
privateKey: seedKey,
|
|
node: node,
|
|
);
|
|
if (possibleDerivationTypes.length == 1) {
|
|
return [DerivationInfo(derivationType: possibleDerivationTypes.first)];
|
|
}
|
|
|
|
AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic(
|
|
DerivationType.bip39,
|
|
mnemonic: mnemonic,
|
|
seedKey: seedKey,
|
|
node: node,
|
|
);
|
|
AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic(
|
|
DerivationType.nano,
|
|
mnemonic: mnemonic,
|
|
seedKey: seedKey,
|
|
node: node,
|
|
);
|
|
|
|
if (standardInfo?.confirmationHeight != null && standardInfo!.confirmationHeight > 0) {
|
|
list.add(DerivationInfo(
|
|
derivationType: DerivationType.nano,
|
|
balance: nanoUtil!.getRawAsUsableString(standardInfo.balance, nanoUtil!.rawPerNano),
|
|
address: standardInfo.address!,
|
|
transactionsCount: standardInfo.confirmationHeight,
|
|
));
|
|
}
|
|
|
|
if (bip39Info?.confirmationHeight != null && bip39Info!.confirmationHeight > 0) {
|
|
list.add(DerivationInfo(
|
|
derivationType: DerivationType.bip39,
|
|
balance: nanoUtil!.getRawAsUsableString(bip39Info.balance, nanoUtil!.rawPerNano),
|
|
address: bip39Info.address!,
|
|
transactionsCount: bip39Info.confirmationHeight,
|
|
));
|
|
}
|
|
return list;
|
|
}
|
|
}
|