derivation updates

This commit is contained in:
fosse 2023-07-28 10:36:50 -04:00
parent 490618ebd6
commit 33c99cb2a5
7 changed files with 168 additions and 68 deletions

View file

@ -13,6 +13,9 @@ import 'package:web3dart/contracts/erc20.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
class NanoClient { class NanoClient {
// bit of a hack since we need access to a node in a weird location:
static const String BACKUP_NODE_URI = "rpc.nano.to";
final _httpClient = Client(); final _httpClient = Client();
StreamSubscription<Transfer>? subscription; StreamSubscription<Transfer>? subscription;
Node? _node; Node? _node;
@ -97,23 +100,4 @@ class NanoClient {
return []; return [];
} }
} }
// Future<int> _getDecimalPlacesForContract(DeployedContract contract) async {
// final String abi = await rootBundle.loadString("assets/abi_json/erc20_abi.json");
// final contractAbi = ContractAbi.fromJson(abi, "ERC20");
//
// final contract = DeployedContract(
// contractAbi,
// EthereumAddress.fromHex(_erc20Currencies[erc20Currency]!),
// );
// final decimalsFunction = contract.function('decimals');
// final decimals = await _client!.call(
// contract: contract,
// function: decimalsFunction,
// params: [],
// );
//
// int exponent = int.parse(decimals.first.toString());
// return exponent;
// }
} }

View file

@ -15,6 +15,7 @@ import 'package:cw_nano/nano_client.dart';
import 'package:cw_nano/nano_transaction_history.dart'; import 'package:cw_nano/nano_transaction_history.dart';
import 'package:cw_nano/nano_transaction_info.dart'; import 'package:cw_nano/nano_transaction_info.dart';
import 'package:cw_nano/nano_util.dart'; import 'package:cw_nano/nano_util.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'dart:async'; import 'dart:async';
import 'package:cw_nano/nano_wallet_addresses.dart'; import 'package:cw_nano/nano_wallet_addresses.dart';
@ -27,20 +28,17 @@ part 'nano_wallet.g.dart';
class NanoWallet = NanoWalletBase with _$NanoWallet; class NanoWallet = NanoWalletBase with _$NanoWallet;
enum DerivationType { bip39, nano }
abstract class NanoWalletBase abstract class NanoWalletBase
extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store { extends WalletBase<NanoBalance, NanoTransactionHistory, NanoTransactionInfo> with Store {
NanoWalletBase({ NanoWalletBase({
required WalletInfo walletInfo, required NanoWalletInfo walletInfo,
required String mnemonic, required String mnemonic,
required String password, required String password,
required DerivationType derivationType,
NanoBalance? initialBalance, NanoBalance? initialBalance,
}) : syncStatus = NotConnectedSyncStatus(), }) : syncStatus = NotConnectedSyncStatus(),
_password = password, _password = password,
_mnemonic = mnemonic, _mnemonic = mnemonic,
_derivationType = derivationType, _derivationType = walletInfo.derivationType,
_isTransactionUpdating = false, _isTransactionUpdating = false,
_client = NanoClient(), _client = NanoClient(),
walletAddresses = NanoWalletAddresses(walletInfo), walletAddresses = NanoWalletAddresses(walletInfo),
@ -86,8 +84,6 @@ abstract class NanoWalletBase
await walletAddresses.init(); await walletAddresses.init();
await transactionHistory.init(); await transactionHistory.init();
// walletAddresses.address = _privateKey.address.toString();
await save(); await save();
} }
@ -127,7 +123,58 @@ abstract class NanoWalletBase
@override @override
Future<PendingTransaction> createTransaction(Object credentials) async { Future<PendingTransaction> createTransaction(Object credentials) async {
print("g"); print("g");
throw UnimplementedError(); print(credentials);
// throw UnimplementedError();
// final _credentials = credentials as EthereumTransactionCredentials;
// final outputs = _credentials.outputs;
// final hasMultiDestination = outputs.length > 1;
// final _erc20Balance = balance[_credentials.currency]!;
// BigInt totalAmount = BigInt.zero;
// int exponent =
// _credentials.currency is Erc20Token ? (_credentials.currency as Erc20Token).decimal : 18;
// BigInt amountToEthereumMultiplier = BigInt.from(pow(10, exponent));
// if (hasMultiDestination) {
// if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
// throw EthereumTransactionCreationException(_credentials.currency);
// }
// final totalOriginalAmount = EthereumFormatter.parseEthereumAmountToDouble(
// outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)));
// totalAmount = BigInt.from(totalOriginalAmount) * amountToEthereumMultiplier;
// if (_erc20Balance.balance < totalAmount) {
// throw EthereumTransactionCreationException(_credentials.currency);
// }
// } else {
// final output = outputs.first;
// final BigInt allAmount = _erc20Balance.balance - BigInt.from(feeRate(_credentials.priority!));
// final totalOriginalAmount =
// EthereumFormatter.parseEthereumAmountToDouble(output.formattedCryptoAmount ?? 0);
// totalAmount = output.sendAll
// ? allAmount
// : BigInt.from(totalOriginalAmount) * amountToEthereumMultiplier;
// if (_erc20Balance.balance < totalAmount) {
// throw EthereumTransactionCreationException(_credentials.currency);
// }
// }
// final pendingEthereumTransaction = await _client.signTransaction(
// privateKey: _privateKey,
// toAddress: _credentials.outputs.first.address,
// amount: totalAmount.toString(),
// gas: _priorityFees[_credentials.priority!.raw],
// priority: _credentials.priority!,
// currency: _credentials.currency,
// exponent: exponent,
// contractAddress: _credentials.currency is Erc20Token
// ? (_credentials.currency as Erc20Token).contractAddress
// : null,
// );
// return pendingEthereumTransaction;
} }
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
@ -178,9 +225,10 @@ abstract class NanoWalletBase
} }
@override @override
Future<void> rescan({required int height}) { Future<void> rescan({required int height}) async {
print("k"); fetchTransactions();
throw UnimplementedError("rescan"); _updateBalance();
return;
} }
@override @override
@ -235,17 +283,20 @@ abstract class NanoWalletBase
derivationType = DerivationType.nano; derivationType = DerivationType.nano;
} }
return NanoWallet( final nanoWalletInfo = NanoWalletInfo(
walletInfo: walletInfo, walletInfo: walletInfo,
derivationType: derivationType,
);
return NanoWallet(
walletInfo: nanoWalletInfo,
password: password, password: password,
mnemonic: mnemonic, mnemonic: mnemonic,
initialBalance: balance, initialBalance: balance,
derivationType: derivationType,
); );
} }
Future<void> _updateBalance() async { Future<void> _updateBalance() async {
// this.balance.update(CryptoCurrency.nano, (value) => (await _client.getBalance(_publicAddress)));
balance[currency] = await _client.getBalance(_publicAddress); balance[currency] = await _client.getBalance(_publicAddress);
await save(); await save();
} }

View file

@ -0,0 +1,23 @@
import 'package:cw_core/wallet_info.dart';
enum DerivationType { bip39, nano }
class NanoWalletInfo extends WalletInfo {
DerivationType derivationType;
NanoWalletInfo({required WalletInfo walletInfo, required this.derivationType})
: super(
walletInfo.id,
walletInfo.name,
walletInfo.type,
walletInfo.isRecovery,
walletInfo.restoreHeight,
walletInfo.timestamp,
walletInfo.dirPath,
walletInfo.path,
walletInfo.address,
walletInfo.yatEid,
walletInfo.yatLastUsedAddressRaw,
walletInfo.showIntroCakePayCard,
);
}

View file

@ -1,15 +1,18 @@
import 'dart:io'; import 'dart:io';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.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';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cw_nano/nano_balance.dart';
import 'package:cw_nano/nano_client.dart';
import 'package:cw_nano/nano_mnemonic.dart'; import 'package:cw_nano/nano_mnemonic.dart';
// import 'package:cw_nano/nano_mnemonics.dart'; import 'package:cw_nano/nano_util.dart';
import 'package:cw_nano/nano_wallet.dart'; import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_creation_credentials.dart'; import 'package:cw_nano/nano_wallet_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:bip39/bip39.dart' as bip39; import 'package:bip39/bip39.dart' as bip39;
@ -62,11 +65,16 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
Future<WalletBase> create(NanoNewWalletCredentials credentials) async { Future<WalletBase> create(NanoNewWalletCredentials credentials) async {
print("nano_wallet_service create"); print("nano_wallet_service create");
final mnemonic = bip39.generateMnemonic(); final mnemonic = bip39.generateMnemonic();
final wallet = NanoWallet(
final nanoWalletInfo = NanoWalletInfo(
walletInfo: credentials.walletInfo!, walletInfo: credentials.walletInfo!,
derivationType: DerivationType.nano,
);
final wallet = NanoWallet(
walletInfo: nanoWalletInfo,
mnemonic: mnemonic, mnemonic: mnemonic,
password: credentials.password!, password: credentials.password!,
derivationType: DerivationType.bip39,
); );
return wallet; return wallet;
} }
@ -102,35 +110,69 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
} }
Future<DerivationType> compareDerivationMethods({String? mnemonic, String? seedKey}) async { Future<DerivationType> compareDerivationMethods({String? mnemonic, String? seedKey}) async {
// TODO: if (seedKey?.length == 128) {
return DerivationType.nano; return DerivationType.bip39;
}
if (mnemonic?.split(' ').length == 12) {
return DerivationType.bip39;
}
try {
NanoClient nanoClient = NanoClient();
// TODO: figure out how to load the current node uri in this context:
nanoClient.connect(Node(
uri: NanoClient.BACKUP_NODE_URI,
type: WalletType.nano,
));
late String publicAddressStandard;
late String publicAddressBip39;
if (seedKey == null) {
seedKey = bip39.mnemonicToEntropy(mnemonic).toUpperCase();
}
publicAddressBip39 = await NanoUtil.hdSeedToAddress(seedKey, 0);
publicAddressStandard = await NanoUtil.seedToAddress(seedKey, 0);
// check if either has a balance:
NanoBalance bip39Balance = await nanoClient.getBalance(publicAddressBip39);
NanoBalance standardBalance = await nanoClient.getBalance(publicAddressStandard);
// TODO: this is super basic implementation, and if both addresses have balances
// it might not be the one that the user wants, though it is unlikely
if (bip39Balance.currentBalance > standardBalance.currentBalance) {
return DerivationType.bip39;
} else {
return DerivationType.nano;
}
} catch (e) {
return DerivationType.nano;
}
} }
@override @override
Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials) async { Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials) async {
throw UnimplementedError("restoreFromKeys"); throw UnimplementedError("restoreFromKeys");
DerivationType derivationType = DerivationType.bip39; // TODO: mnemonic can't be derived from the seedKey in the nanostandard derivation
// which complicates things
if (credentials.seedKey.length == 128) { // DerivationType derivationType = await compareDerivationMethods(seedKey: credentials.seedKey);
derivationType = DerivationType.bip39; // String? mnemonic;
} else { // final nanoWalletInfo = NanoWalletInfo(
// we don't know for sure, but probably the nano standard: // walletInfo: credentials.walletInfo!,
derivationType = await compareDerivationMethods(seedKey: credentials.seedKey); // derivationType: derivationType,
} // );
// final wallet = await NanoWallet(
String? mnemonic; // password: credentials.password!,
// mnemonic: mnemonic ?? "", // we can't derive the mnemonic from the key in all cases
final wallet = await NanoWallet( // walletInfo: nanoWalletInfo,
password: credentials.password!, // );
mnemonic: mnemonic ?? "", // we can't derive the mnemonic from the key in all cases // await wallet.init();
walletInfo: credentials.walletInfo!, // await wallet.save();
derivationType: derivationType, // return wallet;
);
await wallet.init();
await wallet.save();
return wallet;
} }
@override @override
@ -143,20 +185,17 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
throw NanoMnemonicIsIncorrectException(); throw NanoMnemonicIsIncorrectException();
} }
DerivationType derivationType = DerivationType.bip39; DerivationType derivationType = await compareDerivationMethods(mnemonic: credentials.mnemonic);
if (credentials.mnemonic.split(' ').length == 12) { final nanoWalletInfo = NanoWalletInfo(
derivationType = DerivationType.bip39; walletInfo: credentials.walletInfo!,
} else { derivationType: derivationType,
// we don't know for sure, but probably the nano standard: );
derivationType = await compareDerivationMethods(mnemonic: credentials.mnemonic);
}
final wallet = await NanoWallet( final wallet = await NanoWallet(
password: credentials.password!, password: credentials.password!,
mnemonic: credentials.mnemonic, mnemonic: credentials.mnemonic,
walletInfo: credentials.walletInfo!, walletInfo: nanoWalletInfo,
derivationType: derivationType,
); );
await wallet.init(); await wallet.init();

View file

@ -1,4 +1,5 @@
import 'package:cw_nano/nano_wallet.dart'; import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:cw_nano/nano_wallet_service.dart'; import 'package:cw_nano/nano_wallet_service.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';

View file

@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/nano/nano.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cw_nano/nano_wallet.dart'; import 'package:cw_nano/nano_wallet.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';

View file

@ -581,6 +581,7 @@ Future<void> generateNano(bool hasImplementation) async {
const nanoCWHeaders = """ const nanoCWHeaders = """
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_nano/nano_wallet_info.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';