mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-27 06:18:50 +01:00
style: format Dart at 100 columns, as upstream does
Upstream reformatted its whole tree in 16ddc0837. Matching it removes
formatting from the conflict surface of this and every later sync.
This commit is contained in:
@@ -17,21 +17,16 @@ BitcoinBaseAddress addressFromScript(Script script,
|
||||
|
||||
switch (addressType) {
|
||||
case P2pkhAddressType.p2pkh:
|
||||
return P2pkhAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2pkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case P2shAddressType.p2pkhInP2sh:
|
||||
case P2shAddressType.p2pkInP2sh:
|
||||
return P2shAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2shAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case SegwitAddresType.p2wpkh:
|
||||
return P2wpkhAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2wpkhAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case SegwitAddresType.p2wsh:
|
||||
return P2wshAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2wshAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
case SegwitAddresType.p2tr:
|
||||
return P2trAddress.fromScriptPubkey(
|
||||
script: script, network: BitcoinNetwork.mainnet);
|
||||
return P2trAddress.fromScriptPubkey(script: script, network: BitcoinNetwork.mainnet);
|
||||
}
|
||||
|
||||
throw ArgumentError("Invalid script");
|
||||
|
||||
@@ -72,17 +72,16 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||
required super.type,
|
||||
String? scriptHash,
|
||||
required super.network,
|
||||
}) {
|
||||
}) {
|
||||
try {
|
||||
this.scriptHash = scriptHash ??
|
||||
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null);
|
||||
(network != null ? BitcoinAddressUtils.scriptHash(address, network: network!) : null);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool _legacyDefaultForType(BitcoinAddressType type) {
|
||||
|
||||
// Some address types (p2wpkh, p2wsh) were historically derived from the same account/path as our new standard
|
||||
// but using legacy formats. For these, we default to legacy = false.
|
||||
if (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh) return false;
|
||||
@@ -154,6 +153,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||
}
|
||||
return scriptHash!;
|
||||
}
|
||||
|
||||
@override
|
||||
String get derivationPath {
|
||||
if (type == SegwitAddresType.mweb) {
|
||||
@@ -162,9 +162,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||
|
||||
final coinType = _coinTypeForNetwork();
|
||||
final purpose = _purposeForType(type);
|
||||
final accountPath = isLegacyDerivation
|
||||
? electrum_path
|
||||
: "m/$purpose'/$coinType'/0'";
|
||||
final accountPath = isLegacyDerivation ? electrum_path : "m/$purpose'/$coinType'/0'";
|
||||
|
||||
final chain = isHidden ? 1 : 0;
|
||||
return "$accountPath/$chain/$index";
|
||||
|
||||
@@ -7,8 +7,8 @@ final bitcoinAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = bitcoinAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
|
||||
String bitcoinAmountToString({required int amount}) => bitcoinAmountFormat.format(
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
|
||||
String bitcoinAmountToString({required int amount}) =>
|
||||
bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
|
||||
|
||||
double bitcoinAmountToDouble({required int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
|
||||
@@ -5,4 +5,3 @@ class BitcoinCommitTransactionException implements Exception {
|
||||
@override
|
||||
String toString() => errorMessage;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class BitcoinTransactionPriority extends TransactionPriority {
|
||||
const BitcoinTransactionPriority({required String title, required int raw, String? description, String? hint})
|
||||
const BitcoinTransactionPriority(
|
||||
{required String title, required int raw, String? description, String? hint})
|
||||
: super(title: title, raw: raw, description: description, hint: hint);
|
||||
|
||||
static const List<BitcoinTransactionPriority> all = [fast, medium, slow, custom];
|
||||
@@ -10,10 +11,10 @@ class BitcoinTransactionPriority extends TransactionPriority {
|
||||
BitcoinTransactionPriority(title: 'Slow', description: "2 sat/byte", hint: "~ 24 h", raw: 0);
|
||||
static const BitcoinTransactionPriority medium =
|
||||
BitcoinTransactionPriority(title: 'Medium', description: "3 sat/byte", hint: "~ 1 h", raw: 1);
|
||||
static const BitcoinTransactionPriority fast =
|
||||
BitcoinTransactionPriority(title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2);
|
||||
static const BitcoinTransactionPriority fast = BitcoinTransactionPriority(
|
||||
title: 'Fast', description: "4 sat/byte", hint: "~ 30 min", raw: 2);
|
||||
static const BitcoinTransactionPriority custom =
|
||||
BitcoinTransactionPriority(title: 'Custom', raw: 3);
|
||||
BitcoinTransactionPriority(title: 'Custom', raw: 3);
|
||||
|
||||
static BitcoinTransactionPriority deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
@@ -116,19 +117,19 @@ class LitecoinTransactionPriority extends BitcoinTransactionPriority {
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BitcoinCashTransactionPriority extends BitcoinTransactionPriority {
|
||||
const BitcoinCashTransactionPriority({required String title, required int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const List<BitcoinCashTransactionPriority> all = [fast, medium, slow];
|
||||
static const BitcoinCashTransactionPriority slow =
|
||||
BitcoinCashTransactionPriority(title: 'Slow', raw: 0);
|
||||
BitcoinCashTransactionPriority(title: 'Slow', raw: 0);
|
||||
static const BitcoinCashTransactionPriority medium =
|
||||
BitcoinCashTransactionPriority(title: 'Medium', raw: 1);
|
||||
BitcoinCashTransactionPriority(title: 'Medium', raw: 1);
|
||||
static const BitcoinCashTransactionPriority fast =
|
||||
BitcoinCashTransactionPriority(title: 'Fast', raw: 2);
|
||||
BitcoinCashTransactionPriority(title: 'Fast', raw: 2);
|
||||
|
||||
static BitcoinCashTransactionPriority deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
@@ -170,4 +171,3 @@ class BitcoinCashTransactionPriority extends BitcoinTransactionPriority {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
|
||||
|
||||
reaction((_) => this.useLightning, (bool useLightning) {
|
||||
if (useLightning && LightningWallet.isAvailable) {
|
||||
if (mnemonic != null) {
|
||||
@@ -306,28 +306,28 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
|
||||
return BitcoinWallet(
|
||||
mnemonic: mnemonic,
|
||||
xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: derivationInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp?.addresses,
|
||||
initialSilentAddresses: snp?.silentAddresses,
|
||||
initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
|
||||
initialBalance: snp?.balance,
|
||||
initialLightningBalance: snp?.lightningBalance,
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
seedBytes: seedBytes,
|
||||
initialRegularAddressIndex: snp?.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp?.changeAddressIndex,
|
||||
addressPageType: snp?.addressPageType,
|
||||
networkParam: network,
|
||||
alwaysScan: snp?.alwaysScan,
|
||||
useLightning: snp?.useLightning,
|
||||
cachedLightningAddress: snp?.cachedLightningAddress,
|
||||
payjoinBox: payjoinBox,
|
||||
mnemonic: mnemonic,
|
||||
xpub: keysData.xPub != null ? convertZpubToXpub(keysData.xPub!) : null,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: derivationInfo,
|
||||
unspentCoinsInfo: unspentCoinsInfo,
|
||||
initialAddresses: snp?.addresses,
|
||||
initialSilentAddresses: snp?.silentAddresses,
|
||||
initialSilentAddressIndex: snp?.silentAddressIndex ?? 0,
|
||||
initialBalance: snp?.balance,
|
||||
initialLightningBalance: snp?.lightningBalance,
|
||||
encryptionFileUtils: encryptionFileUtils,
|
||||
seedBytes: seedBytes,
|
||||
initialRegularAddressIndex: snp?.regularAddressIndex,
|
||||
initialChangeAddressIndex: snp?.changeAddressIndex,
|
||||
addressPageType: snp?.addressPageType,
|
||||
networkParam: network,
|
||||
alwaysScan: snp?.alwaysScan,
|
||||
useLightning: snp?.useLightning,
|
||||
cachedLightningAddress: snp?.cachedLightningAddress,
|
||||
payjoinBox: payjoinBox,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -488,7 +488,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
@override
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
credentials = credentials as BitcoinTransactionCredentials;
|
||||
final lnAddr = credentials.outputs.first.isParsedAddress ? credentials.outputs.first.extractedAddress! : credentials.outputs.first.address;
|
||||
final lnAddr = credentials.outputs.first.isParsedAddress
|
||||
? credentials.outputs.first.extractedAddress!
|
||||
: credentials.outputs.first.address;
|
||||
|
||||
final isLNCompatible = await lightningWallet?.isCompatible(lnAddr);
|
||||
if ((credentials.coinTypeToSpendFrom == UnspentCoinType.lightning && lightningWallet != null) ||
|
||||
|
||||
@@ -137,7 +137,8 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsAddress(String address) => super.containsAddress(address) || address == lightningAddress;
|
||||
bool containsAddress(String address) =>
|
||||
super.containsAddress(address) || address == lightningAddress;
|
||||
|
||||
@override
|
||||
String get addressForBuy {
|
||||
|
||||
@@ -59,27 +59,27 @@ class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||
}
|
||||
|
||||
class BitcoinWalletFromKeysCredentials extends WalletCredentials {
|
||||
BitcoinWalletFromKeysCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType
|
||||
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||
BitcoinWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String xpub;
|
||||
}
|
||||
|
||||
class LitecoinWalletFromKeysCredentials extends WalletCredentials {
|
||||
LitecoinWalletFromKeysCredentials({
|
||||
required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
required this.scanSecret,
|
||||
required this.spendPubkey,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType
|
||||
}) : super(name: name, password: password, walletInfo: walletInfo);
|
||||
LitecoinWalletFromKeysCredentials(
|
||||
{required String name,
|
||||
required String password,
|
||||
required this.xpub,
|
||||
required this.scanSecret,
|
||||
required this.spendPubkey,
|
||||
WalletInfo? walletInfo,
|
||||
super.hardwareWalletType})
|
||||
: super(name: name, password: password, walletInfo: walletInfo);
|
||||
|
||||
final String xpub;
|
||||
final String scanSecret;
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
class BitcoinWalletKeys {
|
||||
const BitcoinWalletKeys({required this.wif, required this.privateKey, required this.publicKey, required this.xpub});
|
||||
const BitcoinWalletKeys(
|
||||
{required this.wif, required this.privateKey, required this.publicKey, required this.xpub});
|
||||
|
||||
final String wif;
|
||||
final String privateKey;
|
||||
final String publicKey;
|
||||
final String xpub;
|
||||
|
||||
Map<String, String> toJson() => {
|
||||
'wif': wif,
|
||||
'privateKey': privateKey,
|
||||
'publicKey': publicKey,
|
||||
'xpub': xpub
|
||||
};
|
||||
}
|
||||
Map<String, String> toJson() =>
|
||||
{'wif': wif, 'privateKey': privateKey, 'publicKey': publicKey, 'xpub': xpub};
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@ class BitcoinWalletService extends WalletService<
|
||||
BitcoinRestoreWalletFromSeedCredentials,
|
||||
BitcoinWalletFromKeysCredentials,
|
||||
BitcoinRestoreWalletFromHardware> {
|
||||
BitcoinWalletService(this.unspentCoinsInfoSource,
|
||||
this.payjoinSessionSource, this.isDirect);
|
||||
BitcoinWalletService(this.unspentCoinsInfoSource, this.payjoinSessionSource, this.isDirect);
|
||||
|
||||
final Box<UnspentCoinsInfo> unspentCoinsInfoSource;
|
||||
final Box<PayjoinSession> payjoinSessionSource;
|
||||
@@ -39,9 +38,12 @@ class BitcoinWalletService extends WalletService<
|
||||
|
||||
final String mnemonic;
|
||||
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
|
||||
derivationInfo.derivationType = credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType;
|
||||
derivationInfo.derivationPath = credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath;
|
||||
derivationInfo.description = credentials.derivationInfo?.description ?? derivationInfo.description;
|
||||
derivationInfo.derivationType =
|
||||
credentials.derivationInfo?.derivationType ?? derivationInfo.derivationType;
|
||||
derivationInfo.derivationPath =
|
||||
credentials.derivationInfo?.derivationPath ?? derivationInfo.derivationPath;
|
||||
derivationInfo.description =
|
||||
credentials.derivationInfo?.description ?? derivationInfo.description;
|
||||
derivationInfo.scriptType = credentials.derivationInfo?.scriptType ?? derivationInfo.scriptType;
|
||||
await derivationInfo.save();
|
||||
switch (derivationInfo.derivationType) {
|
||||
@@ -120,8 +122,9 @@ class BitcoinWalletService extends WalletService<
|
||||
}
|
||||
await WalletInfo.delete(walletInfo);
|
||||
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values
|
||||
.where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
|
||||
.toList();
|
||||
|
||||
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||
|
||||
@@ -161,11 +164,10 @@ class BitcoinWalletService extends WalletService<
|
||||
final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
|
||||
derivationInfo.derivationPath =
|
||||
credentials.hwAccountData.derivationPath;
|
||||
|
||||
derivationInfo.derivationPath = credentials.hwAccountData.derivationPath;
|
||||
|
||||
final xpub = convertAnyToXpub(credentials.hwAccountData.xpub!);
|
||||
|
||||
|
||||
await credentials.walletInfo!.save();
|
||||
final wallet = await BitcoinWallet(
|
||||
password: credentials.password!,
|
||||
|
||||
@@ -303,9 +303,9 @@ class ElectrumClient {
|
||||
});
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> getBatchHistory(
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> scriptHashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final paramsList = scriptHashes.map((h) => <Object>[h]).toList(growable: false);
|
||||
|
||||
final batchResults = await callBatchWithTimeout(
|
||||
@@ -341,9 +341,9 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> getBatchTransactionVerbose(
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final result = <String, Map<String, dynamic>>{};
|
||||
if (hashes.isEmpty) return result;
|
||||
|
||||
@@ -368,9 +368,9 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
Future<Map<String, String?>> getBatchTransactionHex(
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
List<String> hashes, {
|
||||
int timeout = 10000,
|
||||
}) async {
|
||||
final result = <String, String?>{};
|
||||
if (hashes.isEmpty) return result;
|
||||
|
||||
@@ -408,12 +408,8 @@ class ElectrumClient {
|
||||
// Build the Batch Array
|
||||
final List<Map<String, dynamic>> batchPayload = [];
|
||||
for (int i = 0; i < paramsList.length; i++) {
|
||||
batchPayload.add({
|
||||
"jsonrpc": "2.0",
|
||||
"method": method,
|
||||
"params": paramsList[i],
|
||||
"id": "$batchBaseId-$i"
|
||||
});
|
||||
batchPayload.add(
|
||||
{"jsonrpc": "2.0", "method": method, "params": paramsList[i], "id": "$batchBaseId-$i"});
|
||||
}
|
||||
|
||||
// Register the task
|
||||
@@ -700,7 +696,6 @@ class ElectrumClient {
|
||||
}
|
||||
|
||||
void _handleResponse(dynamic response) {
|
||||
|
||||
// Handle batch response
|
||||
if (response is List) {
|
||||
if (response.isEmpty) return;
|
||||
|
||||
@@ -9,7 +9,8 @@ class ElectrumBalance extends Balance {
|
||||
required int frozen,
|
||||
this.secondConfirmed = 0,
|
||||
this.secondUnconfirmed = 0,
|
||||
}) : this._frozen = frozen, super.fromInt(confirmed, unconfirmed,
|
||||
}) : this._frozen = frozen,
|
||||
super.fromInt(confirmed, unconfirmed,
|
||||
secondAvailable: secondConfirmed, secondAdditional: secondUnconfirmed, frozen: frozen);
|
||||
|
||||
static ElectrumBalance? fromJSON(String? jsonSource) {
|
||||
@@ -37,7 +38,8 @@ class ElectrumBalance extends Balance {
|
||||
int secondUnconfirmed = 0;
|
||||
|
||||
@override
|
||||
BigInt get fullAvailableBalance => BigInt.from((confirmed + unconfirmed) + secondConfirmed - _frozen);
|
||||
BigInt get fullAvailableBalance =>
|
||||
BigInt.from((confirmed + unconfirmed) + secondConfirmed - _frozen);
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'confirmed': confirmed,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
|
||||
@@ -178,8 +178,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
|
||||
// MWEB HogEx
|
||||
final isHogExTx = (BtcTransaction tx) {
|
||||
if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty)
|
||||
return false;
|
||||
if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty) return false;
|
||||
final b = tx.outputs.first.scriptPubKey.toBytes();
|
||||
return b.length == 34 && b[0] == 88 && b[1] == 32;
|
||||
};
|
||||
|
||||
@@ -148,7 +148,6 @@ abstract class ElectrumWalletBase
|
||||
sideHdByType[type] = sideHd;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int _purposeForType(BitcoinAddressType type) {
|
||||
@@ -187,7 +186,6 @@ abstract class ElectrumWalletBase
|
||||
/// For LEGACY addresses, returns the wallet's legacy derivation base (derivationInfo.derivationPath)
|
||||
/// which is already the account path used historically (e.g. m/0' or m/84'/0'/0').
|
||||
String _accountDerivationPathForRecord(BaseBitcoinAddressRecord record) {
|
||||
|
||||
if (derivationInfo.derivationType == DerivationType.electrum) {
|
||||
return derivationInfo.derivationPath ?? electrum_path; // m/0'
|
||||
}
|
||||
@@ -945,8 +943,7 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final baseDerivationPath = _accountDerivationPathForRecord(utx.bitcoinAddressRecord);
|
||||
|
||||
final derivationPath =
|
||||
"${_hardenedDerivationPath(baseDerivationPath)}"
|
||||
final derivationPath = "${_hardenedDerivationPath(baseDerivationPath)}"
|
||||
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
|
||||
"/${utx.bitcoinAddressRecord.index}";
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
@@ -1160,11 +1157,9 @@ abstract class ElectrumWalletBase
|
||||
isChange: true,
|
||||
));
|
||||
|
||||
|
||||
// Must match the address' account root (purpose/coinType) and legacy derivation when applicable.
|
||||
final changeBaseDerivationPath = _accountDerivationPathForRecord(changeAddress);
|
||||
final changeDerivationPath =
|
||||
"${_hardenedDerivationPath(changeBaseDerivationPath)}"
|
||||
final changeDerivationPath = "${_hardenedDerivationPath(changeBaseDerivationPath)}"
|
||||
"/${changeAddress.isHidden ? "1" : "0"}"
|
||||
"/${changeAddress.index}";
|
||||
utxoDetails.publicKeys[address.pubKeyHash()] =
|
||||
@@ -2058,10 +2053,7 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final hd = _hdFor(record: addressRecord);
|
||||
|
||||
final privkey = generateECPrivate(
|
||||
hd: hd,
|
||||
index: addressRecord.index,
|
||||
network: network);
|
||||
final privkey = generateECPrivate(hd: hd, index: addressRecord.index, network: network);
|
||||
|
||||
privateKeys.add(privkey);
|
||||
|
||||
@@ -2490,16 +2482,15 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final history = await electrumClient.getHistory(addressRecord.getScriptHash(network));
|
||||
|
||||
|
||||
if (history.isNotEmpty) {
|
||||
addressRecord.setAsUsed();
|
||||
walletAddresses.clearLockIfMatches(addressRecord.type, addressRecord.address);
|
||||
|
||||
if(this is BitcoinWallet) {
|
||||
if (this is BitcoinWallet) {
|
||||
//removes transactions no longer returned by the api, presumed replaced/invalid.
|
||||
transactionHistory.transactions.removeWhere(
|
||||
(hash, tx) =>
|
||||
tx.outputAddresses != null &&
|
||||
(hash, tx) =>
|
||||
tx.outputAddresses != null &&
|
||||
tx.outputAddresses!.contains(addressRecord.address) &&
|
||||
!history.any((newTransaction) => newTransaction['tx_hash'] == hash),
|
||||
);
|
||||
@@ -2566,7 +2557,7 @@ abstract class ElectrumWalletBase
|
||||
Future<void> fetchTransactionsForAddressTypeBatch(
|
||||
Map<String, ElectrumTransactionInfo> historiesWithDetails, BitcoinAddressType type) async {
|
||||
final addressesByType =
|
||||
walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
|
||||
walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
|
||||
final hiddenAddresses = addressesByType.where((addr) => addr.isHidden).toList();
|
||||
final receiveAddresses = addressesByType.where((addr) => !addr.isHidden).toList();
|
||||
walletAddresses.hiddenAddresses.addAll(hiddenAddresses.map((e) => e.address));
|
||||
@@ -2574,7 +2565,8 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final tip = await getCurrentChainTip();
|
||||
|
||||
final addressHistory = await _processChunksToMap<BitcoinAddressRecord, String, ElectrumTransactionInfo>(
|
||||
final addressHistory =
|
||||
await _processChunksToMap<BitcoinAddressRecord, String, ElectrumTransactionInfo>(
|
||||
items: addressesByType,
|
||||
chunkSize: addressHistoryChunkSize,
|
||||
processChunk: (chunk) => _fetchBatchAddressHistory(chunk, tip, addressHistoryChunkSize),
|
||||
@@ -2585,18 +2577,16 @@ abstract class ElectrumWalletBase
|
||||
for (final addressRecord in addressesByType) {
|
||||
final matchedAddresses = addressRecord.isHidden ? hiddenAddresses : receiveAddresses;
|
||||
|
||||
final isUsedAddressUnderGap =
|
||||
matchedAddresses.indexOf(addressRecord) >=
|
||||
matchedAddresses.length - ElectrumWalletAddressesBase.gap;
|
||||
final isUsedAddressUnderGap = matchedAddresses.indexOf(addressRecord) >=
|
||||
matchedAddresses.length - ElectrumWalletAddressesBase.gap;
|
||||
|
||||
if (isUsedAddressUnderGap && addressRecord.isUsed) {
|
||||
final prevLength = walletAddresses.allAddresses.length;
|
||||
|
||||
|
||||
await walletAddresses.discoverAddressesBatch(
|
||||
matchedAddresses,
|
||||
addressRecord.isHidden,
|
||||
(newAddresses) async {
|
||||
(newAddresses) async {
|
||||
await _fetchBatchAddressHistory(
|
||||
newAddresses,
|
||||
tip,
|
||||
@@ -2614,9 +2604,7 @@ abstract class ElectrumWalletBase
|
||||
final newLength = walletAddresses.allAddresses.length;
|
||||
|
||||
if (newLength > prevLength) {
|
||||
await fetchTransactionsForAddressTypeBatch(
|
||||
historiesWithDetails,
|
||||
type);
|
||||
await fetchTransactionsForAddressTypeBatch(historiesWithDetails, type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2624,9 +2612,7 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<Map<String, ElectrumTransactionInfo>> _fetchBatchAddressHistory(
|
||||
List<BitcoinAddressRecord> addressRecords,
|
||||
int? currentHeight,
|
||||
int historyChunkSize) async {
|
||||
List<BitcoinAddressRecord> addressRecords, int? currentHeight, int historyChunkSize) async {
|
||||
String lastTxId = '';
|
||||
bool didUpdateHistory = false;
|
||||
|
||||
@@ -2637,11 +2623,8 @@ abstract class ElectrumWalletBase
|
||||
final scriptHashes = addressRecords.map((a) => a.getScriptHash(network)).toList();
|
||||
|
||||
final historyByScriptHash =
|
||||
await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
|
||||
items: scriptHashes,
|
||||
chunkSize: historyChunkSize,
|
||||
processChunk: _getHistoryBatch
|
||||
);
|
||||
await _processChunksToMap<String, String, List<Map<String, dynamic>>>(
|
||||
items: scriptHashes, chunkSize: historyChunkSize, processChunk: _getHistoryBatch);
|
||||
|
||||
// Map scriptHash -> addressRecord
|
||||
final byScriptHash = <String, BitcoinAddressRecord>{};
|
||||
@@ -2737,8 +2720,7 @@ abstract class ElectrumWalletBase
|
||||
.toList(growable: false);
|
||||
|
||||
final heightsByHash = <String, int?>{
|
||||
for (final e in chunkHistory)
|
||||
(e['tx_hash'] as String): (e['height'] as int?),
|
||||
for (final e in chunkHistory) (e['tx_hash'] as String): (e['height'] as int?),
|
||||
};
|
||||
|
||||
final infosByHash = await fetchTransactionInfoBatch(
|
||||
@@ -2787,24 +2769,21 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> _getTransactionVerboseBatch(
|
||||
List<String> hashes) {
|
||||
Future<Map<String, Map<String, dynamic>>> _getTransactionVerboseBatch(List<String> hashes) {
|
||||
return electrumClient.getBatchTransactionVerbose(
|
||||
hashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, String?>> _getTransactionHexBatch(
|
||||
List<String> hashes) {
|
||||
Future<Map<String, String?>> _getTransactionHexBatch(List<String> hashes) {
|
||||
return electrumClient.getBatchTransactionHex(
|
||||
hashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> _getHistoryBatch(
|
||||
List<String> scriptHashes) {
|
||||
Future<Map<String, List<Map<String, dynamic>>>> _getHistoryBatch(List<String> scriptHashes) {
|
||||
return electrumClient.getBatchHistory(
|
||||
scriptHashes,
|
||||
timeout: transactionBatchTimeoutMs,
|
||||
@@ -2818,8 +2797,7 @@ abstract class ElectrumWalletBase
|
||||
Duration retryDelay = const Duration(seconds: 2),
|
||||
}) async {
|
||||
final result = <String, ElectrumTransactionInfo?>{};
|
||||
final uniqueHashes =
|
||||
hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList();
|
||||
final uniqueHashes = hashes.map((h) => h.trim()).where((h) => h.isNotEmpty).toSet().toList();
|
||||
|
||||
if (uniqueHashes.isEmpty) return result;
|
||||
|
||||
@@ -2852,9 +2830,8 @@ abstract class ElectrumWalletBase
|
||||
required Map<String, int?>? heightsByHash,
|
||||
}) async {
|
||||
for (var i = 0; i < txIds.length; i += transactionChunkSize) {
|
||||
final end = (i + transactionChunkSize < txIds.length)
|
||||
? i + transactionChunkSize
|
||||
: txIds.length;
|
||||
final end =
|
||||
(i + transactionChunkSize < txIds.length) ? i + transactionChunkSize : txIds.length;
|
||||
final chunk = txIds.sublist(i, end);
|
||||
|
||||
final bundlesByHash = await getTransactionExpandedBatch(
|
||||
@@ -2886,9 +2863,8 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, ElectrumTransactionBundle>> getTransactionExpandedBatch({
|
||||
required List<String> hashes,
|
||||
Map<String, int?>? heightsByHash}) async {
|
||||
Future<Map<String, ElectrumTransactionBundle>> getTransactionExpandedBatch(
|
||||
{required List<String> hashes, Map<String, int?>? heightsByHash}) async {
|
||||
final bundles = <String, ElectrumTransactionBundle>{};
|
||||
if (hashes.isEmpty) return bundles;
|
||||
|
||||
@@ -2898,8 +2874,7 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final inputTxIdsByHash = _collectInputTxIdsByHash(originalByHash);
|
||||
|
||||
final inputVerboseByTxId = await _fetchInputTransactionVerboseBatch(
|
||||
inputTxIdsByHash);
|
||||
final inputVerboseByTxId = await _fetchInputTransactionVerboseBatch(inputTxIdsByHash);
|
||||
|
||||
final parsedInputTxById = _parseTransactions(inputVerboseByTxId);
|
||||
|
||||
@@ -2916,8 +2891,8 @@ abstract class ElectrumWalletBase
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> _fetchTransactionVerboseBatch(
|
||||
List<String> txIds) async {
|
||||
|
||||
final verboseTransactionByHash = await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
final verboseTransactionByHash =
|
||||
await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
items: txIds,
|
||||
chunkSize: transactionChunkSize,
|
||||
processChunk: _getTransactionVerboseBatch,
|
||||
@@ -2954,8 +2929,8 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Map<String, BtcTransaction> _parseTransactions(
|
||||
Map<String, Map<String, dynamic>> verboseByHash,
|
||||
) {
|
||||
Map<String, Map<String, dynamic>> verboseByHash,
|
||||
) {
|
||||
final result = <String, BtcTransaction>{};
|
||||
|
||||
for (final entry in verboseByHash.entries) {
|
||||
@@ -2970,10 +2945,9 @@ abstract class ElectrumWalletBase
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Map<String, List<String>> _collectInputTxIdsByHash(
|
||||
Map<String, BtcTransaction> originalByHash,
|
||||
) {
|
||||
Map<String, BtcTransaction> originalByHash,
|
||||
) {
|
||||
final inputTxIdsByHash = <String, List<String>>{};
|
||||
|
||||
for (final entry in originalByHash.entries) {
|
||||
@@ -2991,7 +2965,6 @@ abstract class ElectrumWalletBase
|
||||
return inputTxIdsByHash;
|
||||
}
|
||||
|
||||
|
||||
Future<Map<String, Map<String, dynamic>>> _fetchInputTransactionVerboseBatch(
|
||||
Map<String, List<String>> inputTxidsByHash) async {
|
||||
final allInputTxids = <String>{};
|
||||
@@ -3002,7 +2975,7 @@ abstract class ElectrumWalletBase
|
||||
final inputTxIds = allInputTxids.toList(growable: false);
|
||||
|
||||
final verboseTransactionByHash =
|
||||
await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
await _processChunksToMap<String, String, Map<String, dynamic>>(
|
||||
items: inputTxIds,
|
||||
chunkSize: inputTransactionChunkSize,
|
||||
processChunk: _getTransactionVerboseBatch,
|
||||
@@ -3049,7 +3022,6 @@ abstract class ElectrumWalletBase
|
||||
return verboseTransactionByHash;
|
||||
}
|
||||
|
||||
|
||||
Future<Map<String, ElectrumTransactionBundle>> _buildTransactionBundlesBatch({
|
||||
required List<String> unique,
|
||||
required Map<String, int?>? heightsByHash,
|
||||
@@ -3125,8 +3097,8 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<Map<int, int>> _fetchBlockTimestampsFromMempoolByHeights(
|
||||
Set<int> heights,
|
||||
) async {
|
||||
Set<int> heights,
|
||||
) async {
|
||||
final out = <int, int>{};
|
||||
if (heights.isEmpty) return out;
|
||||
if (!(await checkIfMempoolAPIIsEnabled())) return out;
|
||||
@@ -3136,10 +3108,10 @@ abstract class ElectrumWalletBase
|
||||
try {
|
||||
final blockHashResp = await ProxyWrapper()
|
||||
.get(
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.space/api/block-height/$h',
|
||||
),
|
||||
)
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.space/api/block-height/$h',
|
||||
),
|
||||
)
|
||||
.timeout(const Duration(seconds: 15));
|
||||
|
||||
if (blockHashResp.statusCode != 200 || blockHashResp.body.isEmpty) return;
|
||||
@@ -3149,10 +3121,10 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final blockResp = await ProxyWrapper()
|
||||
.get(
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.space/api/block/$blockHash',
|
||||
),
|
||||
)
|
||||
clearnetUri: Uri.parse(
|
||||
'https://mempool.space/api/block/$blockHash',
|
||||
),
|
||||
)
|
||||
.timeout(const Duration(seconds: 15));
|
||||
|
||||
if (blockResp.statusCode != 200 || blockResp.body.isEmpty) return;
|
||||
@@ -3200,7 +3172,6 @@ abstract class ElectrumWalletBase
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Future<void> updateTransactions() async {
|
||||
printV("updateTransactions() called!");
|
||||
try {
|
||||
@@ -3413,7 +3384,6 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<void> _checkIfBatchSupported() async {
|
||||
|
||||
if (_isBatchSupported != null) {
|
||||
printV('[BATCH_TEST] Already checked: $_isBatchSupported');
|
||||
return;
|
||||
|
||||
@@ -182,13 +182,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
}
|
||||
|
||||
if (addressPageType == LightningAddressType.p2l) {
|
||||
return lightningAddress ??
|
||||
"Error: Unable to fetch your Lightning address, please check your network connection.";
|
||||
return lightningAddress ??
|
||||
"Error: Unable to fetch your Lightning address, please check your network connection.";
|
||||
}
|
||||
|
||||
final typeMatchingAddressesAll = _addresses
|
||||
.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr))
|
||||
.toList();
|
||||
final typeMatchingAddressesAll =
|
||||
_addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
|
||||
|
||||
// Prefer standard derivation addresses for the current/active address,
|
||||
// but keep legacy addresses present in the overall address lists.
|
||||
@@ -337,34 +336,35 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return acc;
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
if (walletInfo.type == WalletType.bitcoinCash) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.litecoin) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||
}
|
||||
} else if (walletInfo.type == WalletType.dogecoin) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||
await _generateInitialAddresses(isLegacyDerivation: true);
|
||||
await _generateInitialAddresses();
|
||||
if (!isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
}
|
||||
@override
|
||||
Future<void> init() async {
|
||||
if (walletInfo.type == WalletType.bitcoinCash) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.litecoin) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||
}
|
||||
} else if (walletInfo.type == WalletType.dogecoin) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||
await _generateInitialAddresses(isLegacyDerivation: true);
|
||||
await _generateInitialAddresses();
|
||||
if (!isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
|
||||
await _generateInitialAddresses(
|
||||
type: P2shAddressType.p2wpkhInP2sh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
}
|
||||
}
|
||||
|
||||
updateAddressesByMatch();
|
||||
updateReceiveAddresses();
|
||||
@@ -663,9 +663,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
@action
|
||||
void updateReceiveAddresses() {
|
||||
receiveAddresses.removeRange(0, receiveAddresses.length);
|
||||
final newAddresses = _addresses.where((addressRecord) =>
|
||||
!addressRecord.isHidden &&
|
||||
!addressRecord.isUsed);
|
||||
final newAddresses =
|
||||
_addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
|
||||
receiveAddresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
@@ -682,12 +681,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
|
||||
@action
|
||||
Future<void> discoverAddresses(
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<String?> Function(BitcoinAddressRecord) getAddressHistory, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<String?> Function(BitcoinAddressRecord) getAddressHistory, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
required bool isLegacyDerivation,
|
||||
}) async {
|
||||
final newAddresses = await _createNewAddresses(
|
||||
gap,
|
||||
startIndex: addressList.length,
|
||||
@@ -699,8 +698,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
addAddresses(newAddresses);
|
||||
addressList.addAll(newAddresses);
|
||||
|
||||
final addressesWithHistory =
|
||||
await Future.wait(newAddresses.map(getAddressHistory));
|
||||
final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory));
|
||||
final isLastAddressUsed = addressesWithHistory.last != null;
|
||||
|
||||
if (isLastAddressUsed) {
|
||||
@@ -716,11 +714,11 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
|
||||
@action
|
||||
Future<bool> discoverAddressesBatch(
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<Set<String>> Function(List<BitcoinAddressRecord>) getUsedAddresses, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
}) async {
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden,
|
||||
Future<Set<String>> Function(List<BitcoinAddressRecord>) getUsedAddresses, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
}) async {
|
||||
final newAddresses = await _createNewAddresses(
|
||||
gap,
|
||||
startIndex: addressList.length,
|
||||
@@ -749,64 +747,69 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Future<void> _generateInitialAddresses(
|
||||
{BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
bool isLegacyDerivation = false }) async {
|
||||
|
||||
// Legacy derivation produces the same addresses as standard for these types.
|
||||
// Don't generate a legacy set to avoid duplicates.
|
||||
if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var countOfReceiveAddresses = 0;
|
||||
var countOfHiddenAddresses = 0;
|
||||
|
||||
_addresses.forEach((addr) {
|
||||
if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) {
|
||||
if (addr.isHidden) {
|
||||
countOfHiddenAddresses += 1;
|
||||
} else {
|
||||
countOfReceiveAddresses += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfReceiveAddresses, isHidden: false, type: type, isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses, isHidden: true, type: type, isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
{BitcoinAddressType type = SegwitAddresType.p2wpkh, bool isLegacyDerivation = false}) async {
|
||||
// Legacy derivation produces the same addresses as standard for these types.
|
||||
// Don't generate a legacy set to avoid duplicates.
|
||||
if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||
{int startIndex = 0, bool isHidden = false, BitcoinAddressType? type, bool isLegacyDerivation = false}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
var countOfReceiveAddresses = 0;
|
||||
var countOfHiddenAddresses = 0;
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
|
||||
final addrType = type ?? addressPageType;
|
||||
final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation);
|
||||
|
||||
final address = BitcoinAddressRecord(
|
||||
await getAddressAsync(index: i, hd: hd, addressType: addrType),
|
||||
index: i,
|
||||
isHidden: isHidden,
|
||||
isLegacyDerivation: isLegacyDerivation,
|
||||
type: addrType,
|
||||
network: network,
|
||||
);
|
||||
list.add(address);
|
||||
_addresses.forEach((addr) {
|
||||
if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) {
|
||||
if (addr.isHidden) {
|
||||
countOfHiddenAddresses += 1;
|
||||
} else {
|
||||
countOfReceiveAddresses += 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfReceiveAddresses,
|
||||
isHidden: false,
|
||||
type: type,
|
||||
isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses,
|
||||
isHidden: true,
|
||||
type: type,
|
||||
isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||
{int startIndex = 0,
|
||||
bool isHidden = false,
|
||||
BitcoinAddressType? type,
|
||||
bool isLegacyDerivation = false}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
final addrType = type ?? addressPageType;
|
||||
final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation);
|
||||
|
||||
final address = BitcoinAddressRecord(
|
||||
await getAddressAsync(index: i, hd: hd, addressType: addrType),
|
||||
index: i,
|
||||
isHidden: isHidden,
|
||||
isLegacyDerivation: isLegacyDerivation,
|
||||
type: addrType,
|
||||
network: network,
|
||||
);
|
||||
list.add(address);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -845,8 +848,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return;
|
||||
}
|
||||
|
||||
final mainHd = _hdFor(isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
final sideHd = _hdFor(isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
final mainHd = _hdFor(
|
||||
isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
final sideHd = _hdFor(
|
||||
isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
|
||||
if (!element.isHidden &&
|
||||
element.address !=
|
||||
await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) {
|
||||
@@ -874,7 +879,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
return _isAddressByType(addressRecord, addressPageType);
|
||||
}
|
||||
|
||||
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||
|
||||
bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
|
||||
!addr.isHidden && !addr.isUsed && addr.type == type;
|
||||
@@ -884,9 +889,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
final addressRecord = silentAddresses.firstWhere((addressRecord) =>
|
||||
addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address);
|
||||
|
||||
silentAddresses.remove(addressRecord);
|
||||
updateAddressesByMatch();
|
||||
}
|
||||
silentAddresses.remove(addressRecord);
|
||||
updateAddressesByMatch();
|
||||
}
|
||||
|
||||
Bip32Slip10Secp256k1 _hdFor({
|
||||
required bool isHidden,
|
||||
@@ -900,7 +905,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
if (hd == null) throw Exception("HD not found for type $type");
|
||||
return hd;
|
||||
}
|
||||
|
||||
|
||||
@action
|
||||
Future<void> setLightningAddress(String walletName, {String newAddress = ""}) async {
|
||||
if (lightningWallet == null) return;
|
||||
|
||||
@@ -30,7 +30,7 @@ class BitcoinTransactionCommitFailed extends TransactionCommitFailed {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return errorMessage??"unknown error";
|
||||
return errorMessage ?? "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ class BitcoinBitboxService extends HardwareWalletService with BitcoinHardwareWal
|
||||
Future<Uint8List> getMasterFingerprint() => manager.getMasterFingerprint();
|
||||
}
|
||||
|
||||
class LitecoinBitboxService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
class LitecoinBitboxService extends HardwareWalletService
|
||||
with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
LitecoinBitboxService(this.manager);
|
||||
|
||||
final BitboxManager manager;
|
||||
|
||||
@@ -12,7 +12,8 @@ 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 with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
class LitecoinLedgerService extends HardwareWalletService
|
||||
with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
LitecoinLedgerService(this.ledgerConnection)
|
||||
: litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection);
|
||||
|
||||
@@ -53,7 +54,6 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa
|
||||
required List<PSBTReadyUtxoWithAddress> inputs,
|
||||
required Map<String, PublicKeyWithDerivationPath> publicKeys,
|
||||
}) {
|
||||
|
||||
final readyInputs = <LedgerTransaction>[];
|
||||
for (final utxo in inputs) {
|
||||
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||
@@ -77,7 +77,7 @@ class LitecoinLedgerService extends HardwareWalletService with BitcoinHardwareWa
|
||||
inputs: readyInputs,
|
||||
outputs: outputs
|
||||
.map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value,
|
||||
Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
|
||||
Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
|
||||
.toList(),
|
||||
changePath: changePath,
|
||||
sigHashType: 0x01,
|
||||
|
||||
@@ -90,7 +90,8 @@ class BitcoinTrezorService extends HardwareWalletService with BitcoinHardwareWal
|
||||
}
|
||||
}
|
||||
|
||||
class LitecoinTrezorService extends HardwareWalletService with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
class LitecoinTrezorService extends HardwareWalletService
|
||||
with BitcoinHardwareWalletService, LitecoinHardwareWalletService {
|
||||
LitecoinTrezorService(this.connect);
|
||||
|
||||
final TrezorConnect connect;
|
||||
|
||||
@@ -67,8 +67,7 @@ class LightningWallet {
|
||||
lnurlDomain: lnurlDomain,
|
||||
apiKey: apiKey,
|
||||
privateEnabledDefault: true,
|
||||
maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5))
|
||||
);
|
||||
maxDepositClaimFee: MaxFee.rate(satPerVbyte: BigInt.from(5)));
|
||||
|
||||
final connectRequest = ConnectRequest(
|
||||
config: config,
|
||||
@@ -82,8 +81,7 @@ class LightningWallet {
|
||||
_logStream ??= initLogging().asBroadcastStream();
|
||||
|
||||
try {
|
||||
final logFile = File("$appPath/lightning.log")
|
||||
..createSync();
|
||||
final logFile = File("$appPath/lightning.log")..createSync();
|
||||
_subscribeToLogStream(logFile);
|
||||
} catch (e) {
|
||||
printV(e);
|
||||
@@ -172,15 +170,17 @@ class LightningWallet {
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingLightningTransaction> createTransaction(
|
||||
String address, BigInt? amountSats, BitcoinTransactionPriority? priority, bool feesIncluded) async {
|
||||
Future<PendingLightningTransaction> createTransaction(String address, BigInt? amountSats,
|
||||
BitcoinTransactionPriority? priority, bool feesIncluded) async {
|
||||
final inputType = await sdk.parse(input: address);
|
||||
|
||||
final feePolicy = feesIncluded ? FeePolicy.feesIncluded : FeePolicy.feesExcluded;
|
||||
|
||||
if (inputType is InputType_Bolt11Invoice) {
|
||||
final request = PrepareSendPaymentRequest(
|
||||
paymentRequest: inputType.field0.invoice.bolt11, amount: amountSats, feePolicy: feePolicy);
|
||||
paymentRequest: inputType.field0.invoice.bolt11,
|
||||
amount: amountSats,
|
||||
feePolicy: feePolicy);
|
||||
final prepareResponse = await sdk.prepareSendPayment(request: request);
|
||||
|
||||
final paymentMethod = prepareResponse.paymentMethod;
|
||||
@@ -190,7 +190,8 @@ class LightningWallet {
|
||||
|
||||
return PendingLightningTransaction(
|
||||
id: paymentMethod.invoiceDetails.paymentHash,
|
||||
amount: request.amount?.toInt() ?? amountSats?.toInt() ??
|
||||
amount: request.amount?.toInt() ??
|
||||
amountSats?.toInt() ??
|
||||
((paymentMethod.invoiceDetails.amountMsat?.toInt() ?? 0) / 1000).round(),
|
||||
fee: lightningFeeSats.toInt() + (sparkTransferFeeSats?.toInt() ?? 0),
|
||||
commitOverride: () async {
|
||||
@@ -361,14 +362,16 @@ class LightningWallet {
|
||||
return _getElectrumTransactionInfoFromPayment(response.payment);
|
||||
}
|
||||
|
||||
Future<String> refundDeposit(String txId, int vout, String destinationAddress,
|
||||
BigInt feeRate) async {
|
||||
final response = await sdk.refundDeposit(request: RefundDepositRequest(
|
||||
txid: txId,
|
||||
vout: vout,
|
||||
destinationAddress: destinationAddress,
|
||||
fee: Fee.rate(satPerVbyte: feeRate),
|
||||
),);
|
||||
Future<String> refundDeposit(
|
||||
String txId, int vout, String destinationAddress, BigInt feeRate) async {
|
||||
final response = await sdk.refundDeposit(
|
||||
request: RefundDepositRequest(
|
||||
txid: txId,
|
||||
vout: vout,
|
||||
destinationAddress: destinationAddress,
|
||||
fee: Fee.rate(satPerVbyte: feeRate),
|
||||
),
|
||||
);
|
||||
|
||||
return response.txHex;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class PendingLightningTransaction with PendingTransaction {
|
||||
final int fee;
|
||||
final bool isSendAll;
|
||||
Future<void> Function() commitOverride;
|
||||
final List<void Function()> _listeners =[];
|
||||
final List<void Function()> _listeners = [];
|
||||
|
||||
@override
|
||||
final String id;
|
||||
|
||||
@@ -184,7 +184,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
@override
|
||||
bool get hasRescan => true;
|
||||
|
||||
|
||||
final String? scanSecretOverride;
|
||||
final String? spendPubkeyOverride;
|
||||
List<int> get scanSecret => (scanSecretOverride != null && scanSecretOverride?.isNotEmpty == true)
|
||||
@@ -241,8 +240,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
WalletKeysData get walletKeysData =>
|
||||
WalletKeysData(mnemonic: seed, xPub: xpub, passphrase: passphrase, scanSecret: scanSecretOverride, spendPubkey: spendPubkeyOverride);
|
||||
WalletKeysData get walletKeysData => WalletKeysData(
|
||||
mnemonic: seed,
|
||||
xPub: xpub,
|
||||
passphrase: passphrase,
|
||||
scanSecret: scanSecretOverride,
|
||||
spendPubkey: spendPubkeyOverride);
|
||||
|
||||
static Future<LitecoinWallet> open({
|
||||
required String name,
|
||||
@@ -1115,15 +1118,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
for (final utxo in transaction.utxos) {
|
||||
if (utxo.utxo.scriptType != SegwitAddresType.mweb) {
|
||||
inputs.add(utxo.utxo.toInput());
|
||||
txouts.add(TxOut(value: Int64(utxo.utxo.value.toInt()),
|
||||
pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
txouts.add(TxOut(
|
||||
value: Int64(utxo.utxo.value.toInt()),
|
||||
pkScript: utxo.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
}
|
||||
}
|
||||
var resp = await CwMweb.psbtCreate(PsbtCreateRequest(
|
||||
rawTx: inputs.isEmpty ? null : BtcTransaction(
|
||||
inputs: inputs,
|
||||
outputs: isMweb ? [] : transaction.outputs,
|
||||
).toBytes(),
|
||||
rawTx: inputs.isEmpty
|
||||
? null
|
||||
: BtcTransaction(
|
||||
inputs: inputs,
|
||||
outputs: isMweb ? [] : transaction.outputs,
|
||||
).toBytes(),
|
||||
witnessUtxo: txouts,
|
||||
));
|
||||
for (final utxo in transaction.utxos) {
|
||||
@@ -1136,17 +1142,18 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
));
|
||||
}
|
||||
}
|
||||
if (isMweb) for (final output in transaction.outputs) {
|
||||
var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet);
|
||||
if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) {
|
||||
address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes());
|
||||
if (isMweb)
|
||||
for (final output in transaction.outputs) {
|
||||
var address = addressFromOutputScript(output.scriptPubKey, LitecoinNetwork.mainnet);
|
||||
if (output.scriptPubKey.getAddressType() == SegwitAddresType.mweb) {
|
||||
address = SegwitBech32Encoder.encode("ltcmweb", 0, output.scriptPubKey.toBytes());
|
||||
}
|
||||
resp = await CwMweb.psbtAddRecipient(PsbtAddRecipientRequest(
|
||||
psbtB64: resp.psbtB64,
|
||||
recipient: PsbtRecipient(address: address, value: Int64(output.amount.toInt())),
|
||||
feeRatePerKb: Int64.parseInt(transaction.feeRate) * 1000,
|
||||
));
|
||||
}
|
||||
resp = await CwMweb.psbtAddRecipient(PsbtAddRecipientRequest(
|
||||
psbtB64: resp.psbtB64,
|
||||
recipient: PsbtRecipient(address: address, value: Int64(output.amount.toInt())),
|
||||
feeRatePerKb: Int64.parseInt(transaction.feeRate) * 1000,
|
||||
));
|
||||
}
|
||||
return base64.decode(resp.psbtB64);
|
||||
}
|
||||
|
||||
@@ -1212,7 +1219,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
for (final utxo in tx.utxos) {
|
||||
if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
|
||||
hasMwebInput = true;
|
||||
|
||||
} else {
|
||||
// check if any of the inputs of this transaction are hog-ex:
|
||||
// this list is only non-mweb inputs:
|
||||
@@ -1265,9 +1271,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
final utxo = unspentCoins
|
||||
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
|
||||
final key = generateECPrivate(
|
||||
hd: utxo.bitcoinAddressRecord.isHidden
|
||||
? sideHd
|
||||
: mainHd,
|
||||
hd: utxo.bitcoinAddressRecord.isHidden ? sideHd : mainHd,
|
||||
index: utxo.bitcoinAddressRecord.index,
|
||||
network: network);
|
||||
final digest = tx2.getTransactionSegwitDigit(
|
||||
@@ -1293,8 +1297,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
}
|
||||
|
||||
void addTransactionListener(PendingBitcoinTransaction tx,
|
||||
List<String> inputAddresses, bool isPegIn, bool isPegOut) {
|
||||
void addTransactionListener(
|
||||
PendingBitcoinTransaction tx, List<String> inputAddresses, bool isPegIn, bool isPegOut) {
|
||||
tx.addListener((transaction) async {
|
||||
final addresses = <String>{};
|
||||
transaction.inputAddresses?.addAll(inputAddresses);
|
||||
@@ -1584,7 +1588,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
rawTx: rawTx,
|
||||
ownerDetails: utxo.ownerDetails,
|
||||
ownerDerivationPath: publicKeyAndDerivationPath.derivationPath,
|
||||
ownerMasterFingerprint:masterFingerprint,
|
||||
ownerMasterFingerprint: masterFingerprint,
|
||||
ownerPublicKey: publicKeyAndDerivationPath.publicKey,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -59,9 +59,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||
? hex.decode(scanSecretOverride!)
|
||||
: mwebHd?.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw ?? List.filled(32, 0);
|
||||
|
||||
List<int> get spendPubkey => (spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true)
|
||||
? hex.decode(spendPubkeyOverride!)
|
||||
: mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ?? List.filled(32, 0);
|
||||
List<int> get spendPubkey =>
|
||||
(spendPubkeyOverride != null && spendPubkeyOverride?.isNotEmpty == true)
|
||||
? hex.decode(spendPubkeyOverride!)
|
||||
: mwebHd?.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed ??
|
||||
List.filled(32, 0);
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
@@ -81,7 +83,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||
return null;
|
||||
}
|
||||
if ((scanSecret.length < 1 || scanSecret.reduce((a, b) => a + b) == 0) &&
|
||||
(spendPubkey.length < 1 || spendPubkey.reduce((a, b) => a + b) == 0)) {
|
||||
(spendPubkey.length < 1 || spendPubkey.reduce((a, b) => a + b) == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ class LitecoinWalletService extends WalletService<
|
||||
password: credentials.password!,
|
||||
passphrase: credentials.passphrase,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
derivationInfo:
|
||||
credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
@@ -66,7 +67,6 @@ class LitecoinWalletService extends WalletService<
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> openWallet(String name, String password) async {
|
||||
|
||||
final walletInfo = await WalletInfo.get(name, getType());
|
||||
if (walletInfo == null) {
|
||||
throw Exception('Wallet not found');
|
||||
@@ -127,8 +127,9 @@ class LitecoinWalletService extends WalletService<
|
||||
}
|
||||
}
|
||||
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values
|
||||
.where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
|
||||
.toList();
|
||||
|
||||
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||
|
||||
@@ -167,8 +168,7 @@ class LitecoinWalletService extends WalletService<
|
||||
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
|
||||
derivationInfo.derivationPath =
|
||||
credentials.hwAccountData.derivationPath;
|
||||
derivationInfo.derivationPath = credentials.hwAccountData.derivationPath;
|
||||
await derivationInfo.save();
|
||||
credentials.walletInfo!.save();
|
||||
|
||||
@@ -187,7 +187,7 @@ class LitecoinWalletService extends WalletService<
|
||||
|
||||
@override
|
||||
Future<LitecoinWallet> restoreFromKeys(LitecoinWalletFromKeysCredentials credentials,
|
||||
{bool? isTestnet}) async {
|
||||
{bool? isTestnet}) async {
|
||||
final network = isTestnet == true ? LitecoinNetwork.testnet : LitecoinNetwork.mainnet;
|
||||
credentials.walletInfo?.network = network.value;
|
||||
|
||||
@@ -219,7 +219,8 @@ class LitecoinWalletService extends WalletService<
|
||||
passphrase: credentials.passphrase,
|
||||
mnemonic: credentials.mnemonic,
|
||||
walletInfo: credentials.walletInfo!,
|
||||
derivationInfo: credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
derivationInfo:
|
||||
credentials.derivationInfo ?? (await credentials.walletInfo!.getDerivationInfo()),
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
|
||||
@@ -108,16 +108,14 @@ class PayjoinManager {
|
||||
Future<Sender> initSender(
|
||||
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
|
||||
try {
|
||||
final pjUri =
|
||||
(await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
||||
final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
||||
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
|
||||
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
|
||||
psbtBase64: originalPsbt,
|
||||
pjUri: pjUri,
|
||||
);
|
||||
final persister = PayjoinSenderPersister.impl();
|
||||
final newSender =
|
||||
await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
|
||||
final newSender = await senderBuilder.buildRecommended(minFeeRate: minFeeRateSatPerKwu);
|
||||
final senderToken = await newSender.persist(persister: persister);
|
||||
|
||||
return Sender.load(token: senderToken, persister: persister);
|
||||
@@ -133,8 +131,7 @@ class PayjoinManager {
|
||||
bool isTestnet = false,
|
||||
}) async {
|
||||
final pjUri = Uri.parse(pjUrl).queryParameters['pj']!;
|
||||
await _payjoinStorage.insertSenderSession(
|
||||
sender, pjUri, _wallet.id, amount);
|
||||
await _payjoinStorage.insertSenderSession(sender, pjUri, _wallet.id, amount);
|
||||
|
||||
return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri);
|
||||
}
|
||||
@@ -207,8 +204,7 @@ class PayjoinManager {
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<Receiver> getUnusedReceiver(String address,
|
||||
[bool isTestnet = false]) async {
|
||||
Future<Receiver> getUnusedReceiver(String address, [bool isTestnet = false]) async {
|
||||
final session = _payjoinStorage.getUnusedActiveReceiverSession(_wallet.id);
|
||||
|
||||
if (session != null) {
|
||||
@@ -220,7 +216,8 @@ class PayjoinManager {
|
||||
return initReceiver(address);
|
||||
}
|
||||
|
||||
Future<Receiver> initReceiver(String address, [bool isTestnet = false, int retryCount = 0]) async {
|
||||
Future<Receiver> initReceiver(String address,
|
||||
[bool isTestnet = false, int retryCount = 0]) async {
|
||||
if (retryCount > 0) writePayjoinLog("Retrying initReceiver ${retryCount + 1} attempt");
|
||||
|
||||
try {
|
||||
@@ -245,7 +242,6 @@ class PayjoinManager {
|
||||
} catch (e) {
|
||||
writePayjoinLog(e.toString());
|
||||
if (e.toString().contains("error sending request for url") && retryCount < 5) {
|
||||
|
||||
return initReceiver(address, isTestnet, ++retryCount);
|
||||
} else {
|
||||
rethrow;
|
||||
@@ -273,13 +269,11 @@ class PayjoinManager {
|
||||
rawAmount = getOutputAmountFromTx(tx, _wallet);
|
||||
break;
|
||||
case PayjoinReceiverRequestTypes.checkIsOwned:
|
||||
(_wallet.walletAddresses as BitcoinWalletAddresses)
|
||||
.newPayjoinReceiver();
|
||||
(_wallet.walletAddresses as BitcoinWalletAddresses).newPayjoinReceiver();
|
||||
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
|
||||
|
||||
final inputScript = message['input_script'] as Uint8List;
|
||||
final isOwned =
|
||||
_wallet.isMine(Script.fromRaw(byteData: inputScript));
|
||||
final isOwned = _wallet.isMine(Script.fromRaw(byteData: inputScript));
|
||||
mainToIsolateSendPort?.send({
|
||||
'requestId': message['requestId'],
|
||||
'result': isOwned,
|
||||
@@ -288,8 +282,7 @@ class PayjoinManager {
|
||||
|
||||
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
|
||||
final outputScript = message['output_script'] as Uint8List;
|
||||
final isReceiverOutput =
|
||||
_wallet.isMine(Script.fromRaw(byteData: outputScript));
|
||||
final isReceiverOutput = _wallet.isMine(Script.fromRaw(byteData: outputScript));
|
||||
mainToIsolateSendPort?.send({
|
||||
'requestId': message['requestId'],
|
||||
'result': isReceiverOutput,
|
||||
@@ -310,7 +303,8 @@ class PayjoinManager {
|
||||
|
||||
case PayjoinReceiverRequestTypes.processPsbt:
|
||||
final psbt = message['psbt'] as String;
|
||||
writePayjoinLog("Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.processPsbt: $psbt");
|
||||
writePayjoinLog(
|
||||
"Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.processPsbt: $psbt");
|
||||
|
||||
final signedPsbt = await _wallet.signPsbt(psbt, utxos);
|
||||
mainToIsolateSendPort?.send({
|
||||
@@ -322,7 +316,8 @@ class PayjoinManager {
|
||||
case PayjoinReceiverRequestTypes.proposalSent:
|
||||
_cleanupSession(receiver.id());
|
||||
final psbt = message['psbt'] as String;
|
||||
writePayjoinLog("Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt");
|
||||
writePayjoinLog(
|
||||
"Receiver(${receiver.id()}) PayjoinReceiverRequestTypes.proposalSent: $psbt");
|
||||
|
||||
await _payjoinStorage.markReceiverSessionComplete(
|
||||
receiver.id(), getTxIdFromPsbtV0(psbt), rawAmount);
|
||||
|
||||
@@ -47,8 +47,7 @@ class PayjoinReceiverWorker {
|
||||
try {
|
||||
final receiver = Receiver.fromJson(json: receiverJson);
|
||||
|
||||
final uncheckedProposal =
|
||||
await worker.receiveUncheckedProposal(receiver);
|
||||
final uncheckedProposal = await worker.receiveUncheckedProposal(receiver);
|
||||
|
||||
final originalTx = await uncheckedProposal.extractTxToScheduleBroadcast();
|
||||
sendPort.send({
|
||||
@@ -112,8 +111,7 @@ class PayjoinReceiverWorker {
|
||||
final httpRequest = await client.post(url,
|
||||
headers: {'Content-Type': request.contentType}, body: request.body);
|
||||
|
||||
final proposal = await session.processRes(
|
||||
body: httpRequest.bodyBytes, ctx: extractReq.$2);
|
||||
final proposal = await session.processRes(body: httpRequest.bodyBytes, ctx: extractReq.$2);
|
||||
if (proposal != null) return proposal;
|
||||
sleep(Duration(seconds: 2));
|
||||
}
|
||||
@@ -140,8 +138,7 @@ class PayjoinReceiverWorker {
|
||||
return await finalProposal.psbt();
|
||||
}
|
||||
|
||||
Future<PayjoinProposal> processPayjoinProposal(
|
||||
UncheckedProposal proposal) async {
|
||||
Future<PayjoinProposal> processPayjoinProposal(UncheckedProposal proposal) async {
|
||||
await proposal.extractTxToScheduleBroadcast();
|
||||
// TODO Handle this. send to the main port on a timer?
|
||||
|
||||
@@ -174,8 +171,7 @@ class PayjoinReceiverWorker {
|
||||
);
|
||||
final pj5 = await pj4.commitOutputs();
|
||||
|
||||
final listUnspent =
|
||||
await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
||||
final listUnspent = await _sendRequest(PayjoinReceiverRequestTypes.getCandidateInputs);
|
||||
final unspent = listUnspent as List<UtxoWithPrivateKey>;
|
||||
if (unspent.isEmpty) throw RecoverableError('No unspent outputs available');
|
||||
|
||||
@@ -186,8 +182,8 @@ class PayjoinReceiverWorker {
|
||||
// Finalize proposal
|
||||
final payjoinProposal = await pj7.finalizeProposal(
|
||||
processPsbt: (String psbt) async {
|
||||
final result = await _sendRequest(
|
||||
PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt});
|
||||
final result =
|
||||
await _sendRequest(PayjoinReceiverRequestTypes.processPsbt, {'psbt': psbt});
|
||||
return result as String;
|
||||
},
|
||||
// TODO set maxFeeRateSatPerVb
|
||||
@@ -203,15 +199,12 @@ class PayjoinReceiverWorker {
|
||||
Future<InputPair> _inputPairFromUtxo(UtxoWithPrivateKey utxo) async {
|
||||
final txout = TxOut(
|
||||
value: utxo.utxo.value,
|
||||
scriptPubkey: Uint8List.fromList(
|
||||
utxo.ownerDetails.address.toScriptPubKey().toBytes()),
|
||||
scriptPubkey: Uint8List.fromList(utxo.ownerDetails.address.toScriptPubKey().toBytes()),
|
||||
);
|
||||
|
||||
final psbtin =
|
||||
PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null);
|
||||
final psbtin = PsbtInput(witnessUtxo: txout, redeemScript: null, witnessScript: null);
|
||||
|
||||
final previousOutput =
|
||||
OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout);
|
||||
final previousOutput = OutPoint(txid: utxo.utxo.txHash, vout: utxo.utxo.vout);
|
||||
|
||||
final txin = TxIn(
|
||||
previousOutput: previousOutput,
|
||||
|
||||
@@ -46,11 +46,11 @@ class PayjoinSenderWorker {
|
||||
sendPort.send(e);
|
||||
}
|
||||
}
|
||||
|
||||
final client = ProxyWrapper().getHttpIOClient();
|
||||
|
||||
/// Run a payjoin sender (V2 protocol first, fallback to V1).
|
||||
Future<String> runSender(Sender sender) async {
|
||||
|
||||
try {
|
||||
return await _runSenderV2(sender);
|
||||
} catch (e) {
|
||||
@@ -70,13 +70,11 @@ class PayjoinSenderWorker {
|
||||
Future<String> _runSenderV2(Sender sender) async {
|
||||
try {
|
||||
final postRequest = await sender.extractV2(
|
||||
ohttpProxyUrl:
|
||||
await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
|
||||
ohttpProxyUrl: await pj_uri.Url.fromStr(PayjoinManager.randomOhttpRelayUrl()),
|
||||
);
|
||||
|
||||
final postResult = await _postRequest(postRequest.$1);
|
||||
final getContext =
|
||||
await postRequest.$2.processResponse(response: postResult);
|
||||
final getContext = await postRequest.$2.processResponse(response: postResult);
|
||||
|
||||
sendPort.send({'type': PayjoinSenderRequestTypes.requestPosted, "pj": pjUrl});
|
||||
|
||||
|
||||
@@ -23,16 +23,14 @@ class PayjoinStorage {
|
||||
),
|
||||
);
|
||||
|
||||
PayjoinSession? getUnusedActiveReceiverSession(String walletId) =>
|
||||
_payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
session.status == PayjoinSessionStatus.created.name &&
|
||||
!session.isSenderSession)
|
||||
.firstOrNull;
|
||||
PayjoinSession? getUnusedActiveReceiverSession(String walletId) => _payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
session.status == PayjoinSessionStatus.created.name &&
|
||||
!session.isSenderSession)
|
||||
.firstOrNull;
|
||||
|
||||
Future<void> markReceiverSessionComplete(
|
||||
String sessionId, String txId, String amount) async {
|
||||
Future<void> markReceiverSessionComplete(String sessionId, String txId, String amount) async {
|
||||
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
||||
|
||||
session.status = PayjoinSessionStatus.success.name;
|
||||
@@ -41,8 +39,7 @@ class PayjoinStorage {
|
||||
await session.save();
|
||||
}
|
||||
|
||||
Future<void> markReceiverSessionUnrecoverable(
|
||||
String sessionId, String reason) async {
|
||||
Future<void> markReceiverSessionUnrecoverable(String sessionId, String reason) async {
|
||||
final session = _payjoinSessionSources.get("$_receiverPrefix${sessionId}")!;
|
||||
|
||||
session.status = PayjoinSessionStatus.unrecoverable.name;
|
||||
@@ -92,13 +89,10 @@ class PayjoinStorage {
|
||||
await session.save();
|
||||
}
|
||||
|
||||
List<PayjoinSession> readAllOpenSessions(String walletId) =>
|
||||
_payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
![
|
||||
PayjoinSessionStatus.success.name,
|
||||
PayjoinSessionStatus.unrecoverable.name
|
||||
].contains(session.status))
|
||||
.toList();
|
||||
List<PayjoinSession> readAllOpenSessions(String walletId) => _payjoinSessionSources.values
|
||||
.where((session) =>
|
||||
session.walletId == walletId &&
|
||||
![PayjoinSessionStatus.success.name, PayjoinSessionStatus.unrecoverable.name]
|
||||
.contains(session.status))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
String get feeFormatted => "$feeFormattedValue ${_feeCurrency}";
|
||||
|
||||
String get _feeCurrency {
|
||||
switch(type) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return "BTC";
|
||||
case WalletType.litecoin:
|
||||
@@ -86,10 +86,8 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
default:
|
||||
return type.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => bitcoinAmountToString(amount: fee);
|
||||
|
||||
@@ -155,14 +153,12 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
|
||||
Future<void> _ltcCommit() async {
|
||||
try {
|
||||
final resp = await CwMweb.broadcast(
|
||||
BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
|
||||
final resp = await CwMweb.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
|
||||
idOverride = resp.txid;
|
||||
} on GrpcError catch (e) {
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: e.message);
|
||||
} catch (e) {
|
||||
throw BitcoinTransactionCommitFailed(
|
||||
errorMessage: "Unknown error: ${e.toString()}");
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: "Unknown error: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,8 +177,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
_listeners.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
||||
void addListener(
|
||||
void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
void addListener(void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||
@@ -207,8 +202,8 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
var cborEncoder = CBOREncoder();
|
||||
cborEncoder.encodeBytes(sourceBytes);
|
||||
var ur = UR("psbt", cborEncoder.getBytes());
|
||||
var urLegacy = UR("crypto-psbt", cborEncoder.getBytes());
|
||||
// var ur = UR("psbt", Uint8List.fromList(List.generate(64*1024, (int x) => x % 256)));
|
||||
var urLegacy = UR("crypto-psbt", cborEncoder.getBytes());
|
||||
// var ur = UR("psbt", Uint8List.fromList(List.generate(64*1024, (int x) => x % 256)));
|
||||
var encoded = UREncoder(ur, 120);
|
||||
var encodedLegacy = UREncoder(urLegacy, 120);
|
||||
List<String> values = [];
|
||||
@@ -230,8 +225,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
return Future.value({
|
||||
"Cupcake bcur": values.join("\n"),
|
||||
"ColdCard bbqr": bbqr.join("\n"),
|
||||
if (kDebugMode)
|
||||
"SeedSigner bcur legacy": valuesLegacy.join("\n"),
|
||||
if (kDebugMode) "SeedSigner bcur legacy": valuesLegacy.join("\n"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,7 @@ extension PsbtSigner on PsbtV2 {
|
||||
return tx.buffer();
|
||||
}
|
||||
|
||||
Future<void> signWithUTXO(
|
||||
List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
||||
Future<void> signWithUTXO(List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
||||
[UTXOGetterCallBack? getTaprootPair]) async {
|
||||
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
|
||||
final tx = BtcTransaction.fromRaw(raw);
|
||||
@@ -53,8 +52,8 @@ extension PsbtSigner on PsbtV2 {
|
||||
|
||||
if (utxos.any((e) => e.utxo.isP2tr())) {
|
||||
for (final input in tx.inputs) {
|
||||
final utxo = utxos.firstWhereOrNull(
|
||||
(u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
||||
final utxo = utxos
|
||||
.firstWhereOrNull((u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
||||
|
||||
if (utxo == null) {
|
||||
final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
|
||||
@@ -81,8 +80,8 @@ extension PsbtSigner on PsbtV2 {
|
||||
: BitcoinOpCodeConst.SIGHASH_ALL;
|
||||
|
||||
/// We generate transaction digest for current input
|
||||
final digest = _generateTransactionDigest(
|
||||
script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
||||
final digest =
|
||||
_generateTransactionDigest(script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
||||
|
||||
/// now we need sign the transaction digest
|
||||
final sig = signer(digest, utxo, utxo.privateKey, sighash);
|
||||
@@ -90,21 +89,14 @@ extension PsbtSigner on PsbtV2 {
|
||||
if (utxo.utxo.isP2tr()) {
|
||||
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||
} else {
|
||||
setInputPartialSig(
|
||||
i,
|
||||
Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
||||
setInputPartialSig(i, Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
||||
Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<int> _generateTransactionDigest(
|
||||
Script scriptPubKeys,
|
||||
int input,
|
||||
BitcoinUtxo utxo,
|
||||
BtcTransaction transaction,
|
||||
List<BigInt> taprootAmounts,
|
||||
List<Script> tapRootPubKeys) {
|
||||
List<int> _generateTransactionDigest(Script scriptPubKeys, int input, BitcoinUtxo utxo,
|
||||
BtcTransaction transaction, List<BigInt> taprootAmounts, List<Script> tapRootPubKeys) {
|
||||
if (utxo.isSegwit()) {
|
||||
if (utxo.isP2tr()) {
|
||||
return transaction.getTransactionTaprootDigset(
|
||||
@@ -116,8 +108,7 @@ extension PsbtSigner on PsbtV2 {
|
||||
return transaction.getTransactionSegwitDigit(
|
||||
txInIndex: input, script: scriptPubKeys, amount: utxo.value);
|
||||
}
|
||||
return transaction.getTransactionDigest(
|
||||
txInIndex: input, script: scriptPubKeys);
|
||||
return transaction.getTransactionDigest(txInIndex: input, script: scriptPubKeys);
|
||||
}
|
||||
|
||||
Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) {
|
||||
@@ -172,11 +163,10 @@ extension PsbtSigner on PsbtV2 {
|
||||
}
|
||||
}
|
||||
|
||||
typedef UTXOSignerCallBack = String Function(List<int> trDigest,
|
||||
UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
|
||||
typedef UTXOSignerCallBack = String Function(
|
||||
List<int> trDigest, UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
|
||||
|
||||
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(
|
||||
String txId, int vout);
|
||||
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(String txId, int vout);
|
||||
|
||||
class TaprootAmountScriptPair {
|
||||
final BigInt value;
|
||||
@@ -216,23 +206,17 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||
}
|
||||
|
||||
return UtxoWithPrivateKey(
|
||||
utxo: input.utxo,
|
||||
ownerDetails: input.ownerDetails,
|
||||
privateKey: key.privkey);
|
||||
utxo: input.utxo, ownerDetails: input.ownerDetails, privateKey: key.privkey);
|
||||
}
|
||||
|
||||
factory UtxoWithPrivateKey.fromUnspent(
|
||||
BitcoinUnspent input, BitcoinWalletBase wallet) {
|
||||
final address =
|
||||
RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
||||
factory UtxoWithPrivateKey.fromUnspent(BitcoinUnspent input, BitcoinWalletBase wallet) {
|
||||
final address = RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
||||
|
||||
final newHd =
|
||||
input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.mainHd;
|
||||
final newHd = input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.mainHd;
|
||||
|
||||
ECPrivate privkey;
|
||||
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
||||
final unspentAddress =
|
||||
input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||
final unspentAddress = input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
|
||||
BigintUtils.fromBytes(
|
||||
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
|
||||
@@ -240,9 +224,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||
);
|
||||
} else {
|
||||
privkey = generateECPrivate(
|
||||
hd: newHd,
|
||||
index: input.bitcoinAddressRecord.index,
|
||||
network: BitcoinNetwork.mainnet);
|
||||
hd: newHd, index: input.bitcoinAddressRecord.index, network: BitcoinNetwork.mainnet);
|
||||
}
|
||||
|
||||
return UtxoWithPrivateKey(
|
||||
@@ -251,8 +233,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||
value: BigInt.from(input.value),
|
||||
vout: input.vout,
|
||||
scriptType: input.bitcoinAddressRecord.type,
|
||||
isSilentPayment:
|
||||
input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
||||
isSilentPayment: input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
||||
),
|
||||
ownerDetails: UtxoAddressDetails(
|
||||
publicKey: privkey.getPublic().toHex(),
|
||||
|
||||
@@ -11,7 +11,10 @@ class PSBTTransactionBuild {
|
||||
final PsbtV2 psbt = PsbtV2();
|
||||
|
||||
PSBTTransactionBuild(
|
||||
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, required List<OutputInfo> cwOutputs, bool enableRBF = true}) {
|
||||
{required List<PSBTReadyUtxoWithAddress> inputs,
|
||||
required List<BitcoinBaseOutput> outputs,
|
||||
required List<OutputInfo> cwOutputs,
|
||||
bool enableRBF = true}) {
|
||||
psbt.setGlobalTxVersion(2);
|
||||
psbt.setGlobalInputCount(inputs.length);
|
||||
psbt.setGlobalOutputCount(outputs.length);
|
||||
@@ -23,10 +26,10 @@ class PSBTTransactionBuild {
|
||||
printV(input.utxo.isSegwit());
|
||||
printV(input.utxo.isP2shSegwit());
|
||||
|
||||
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||
psbt.setInputPreviousTxId(
|
||||
i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||
psbt.setInputOutputIndex(i, input.utxo.vout);
|
||||
psbt.setInputSequence(i, enableRBF ? 0xfffffffd : 0xffffffff);
|
||||
|
||||
|
||||
if (input.utxo.isSegwit()) {
|
||||
setInputSegwit(i, input);
|
||||
@@ -48,10 +51,10 @@ class PSBTTransactionBuild {
|
||||
if (cwOutputs.isNotEmpty) {
|
||||
try {
|
||||
final cwOutput = cwOutputs
|
||||
.where((e) => [e.address, e.extractedAddress]
|
||||
.map((e) => e?.toLowerCase())
|
||||
.contains(output.address.toAddress().toLowerCase()))
|
||||
.firstOrNull;
|
||||
.where((e) => [e.address, e.extractedAddress]
|
||||
.map((e) => e?.toLowerCase())
|
||||
.contains(output.address.toAddress().toLowerCase()))
|
||||
.firstOrNull;
|
||||
if (cwOutput != null &&
|
||||
cwOutput.extra.containsKey('bip353_name') &&
|
||||
cwOutput.extra.containsKey('bip353_proof')) {
|
||||
@@ -80,20 +83,14 @@ class PSBTTransactionBuild {
|
||||
|
||||
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
}
|
||||
|
||||
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||
psbt.setInputBip32Derivation(
|
||||
i,
|
||||
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint,
|
||||
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||
|
||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||
|
||||
@@ -10,9 +10,7 @@ import 'package:ledger_bitcoin/psbt.dart';
|
||||
String getTxIdFromPsbtV0(String psbt) {
|
||||
final psbtV2 = PsbtV2()..deserializeV0(base64.decode(psbt));
|
||||
|
||||
return BtcTransaction.fromRaw(
|
||||
BytesUtils.toHexString(psbtV2.extractUnsignedTX(false)))
|
||||
.txId();
|
||||
return BtcTransaction.fromRaw(BytesUtils.toHexString(psbtV2.extractUnsignedTX(false))).txId();
|
||||
}
|
||||
|
||||
String getOutputAmountFromPsbt(String psbtV0, BitcoinWalletBase wallet) {
|
||||
|
||||
@@ -7,10 +7,10 @@ import 'package:ledger_bitcoin/src/utils/buffer_reader.dart';
|
||||
import 'package:ledger_bitcoin/src/utils/uint8list_extension.dart' as ext;
|
||||
|
||||
extension PsbtSigner on PsbtV2 {
|
||||
|
||||
void deserializeV0(Uint8List psbt) {
|
||||
final bufferReader = BufferReader(psbt);
|
||||
if (!listEquals(bufferReader.readSlice(5), Uint8List.fromList([0x70, 0x73, 0x62, 0x74, 0xff]))) {
|
||||
if (!listEquals(
|
||||
bufferReader.readSlice(5), Uint8List.fromList([0x70, 0x73, 0x62, 0x74, 0xff]))) {
|
||||
throw Exception("Invalid magic bytes");
|
||||
}
|
||||
while (_readKeyPair(globalMap, bufferReader)) {}
|
||||
@@ -26,7 +26,8 @@ extension PsbtSigner on PsbtV2 {
|
||||
while (_readKeyPair(inputMaps[i], bufferReader)) {}
|
||||
final input = tx.inputs[i];
|
||||
setInputOutputIndex(i, input.txIndex);
|
||||
setInputPreviousTxId(i, Uint8List.fromList(BytesUtils.fromHexString(input.txId).reversed.toList()));
|
||||
setInputPreviousTxId(
|
||||
i, Uint8List.fromList(BytesUtils.fromHexString(input.txId).reversed.toList()));
|
||||
setInputSequence(i, Uint8List.fromList(input.sequence).readUint32LE(0));
|
||||
}
|
||||
for (var i = 0; i < getGlobalOutputCount(); i++) {
|
||||
|
||||
@@ -18,7 +18,6 @@ import "package:ledger_bitcoin/src/utils/buffer_writer.dart";
|
||||
/// through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
|
||||
extension InputFinalizer on PsbtV2 {
|
||||
void finalizeV0() {
|
||||
|
||||
// First check that each input has a signature
|
||||
for (var i = 0; i < getGlobalInputCount(); i++) {
|
||||
if (_isFinalized(i)) continue;
|
||||
@@ -31,8 +30,7 @@ extension InputFinalizer on PsbtV2 {
|
||||
}
|
||||
if (legacyPubkeys.isNotEmpty) {
|
||||
if (legacyPubkeys.length > 1) {
|
||||
throw Exception(
|
||||
'Expected exactly one signature, got ${legacyPubkeys.length}');
|
||||
throw Exception('Expected exactly one signature, got ${legacyPubkeys.length}');
|
||||
}
|
||||
if (taprootSig != null) {
|
||||
throw Exception('Both taproot and non-taproot signatures present.');
|
||||
@@ -55,8 +53,7 @@ extension InputFinalizer on PsbtV2 {
|
||||
setInputFinalScriptwitness(i, witnessBuf.buffer());
|
||||
if (isWrappedSegwit) {
|
||||
if (redeemScript.isEmpty) {
|
||||
throw Exception(
|
||||
"Expected non-empty redeemscript. Can't finalize input $i");
|
||||
throw Exception("Expected non-empty redeemscript. Can't finalize input $i");
|
||||
}
|
||||
final scriptSigBuf = BufferWriter()
|
||||
..writeUInt8(redeemScript.length) // Push redeemScript length
|
||||
|
||||
@@ -52,4 +52,4 @@ String generateP2TRAddress({
|
||||
}) =>
|
||||
ECPublic.fromBip32(hd.childKey(Bip32KeyIndex(index)).publicKey)
|
||||
.toTaprootAddress()
|
||||
.toAddress(network);
|
||||
.toAddress(network);
|
||||
|
||||
@@ -89,8 +89,9 @@ class BitcoinCashWalletService extends WalletService<
|
||||
}
|
||||
await WalletInfo.delete(walletInfo);
|
||||
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||
final unspentCoinsToDelete = unspentCoinsInfoSource.values
|
||||
.where((unspentCoin) => unspentCoin.walletId == walletInfo.id)
|
||||
.toList();
|
||||
|
||||
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||
|
||||
@@ -147,8 +148,7 @@ class BitcoinCashWalletService extends WalletService<
|
||||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
passphrase: credentials.passphrase
|
||||
);
|
||||
passphrase: credentials.passphrase);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export 'bitcoin_cash_mnemonic_is_incorrect_exception.dart';
|
||||
export 'bitcoin_cash_mnemonic_is_incorrect_exception.dart';
|
||||
|
||||
@@ -77,16 +77,17 @@ class PendingBitcoinCashTransaction with PendingTransaction {
|
||||
void addListener(void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
confirmations: 0,
|
||||
fee: fee,
|
||||
isReplaced: false,
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(
|
||||
type,
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
confirmations: 0,
|
||||
fee: fee,
|
||||
isReplaced: false,
|
||||
);
|
||||
@override
|
||||
Future<Map<String, String>> commitUR() {
|
||||
|
||||
@@ -9,4 +9,4 @@ class Account {
|
||||
final int id;
|
||||
final String label;
|
||||
final String? balance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
abstract class AccountList<T> {
|
||||
|
||||
ObservableList<T> get accounts;
|
||||
|
||||
void update();
|
||||
|
||||
@@ -67,11 +67,10 @@ class AmountConverter {
|
||||
.format(cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider));
|
||||
|
||||
static Decimal cryptoAmountToDecimal({required int amount, required int divider}) =>
|
||||
(Decimal.fromInt(amount) / Decimal.fromInt(divider)).toDecimal();
|
||||
|
||||
static String _moneroAmountToStringUsingDecimals(int amount) => _moneroAmountFormat.format(
|
||||
DecimalIntl(cryptoAmountToDecimal(amount: amount, divider: _moneroAmountDivider)));
|
||||
(Decimal.fromInt(amount) / Decimal.fromInt(divider)).toDecimal();
|
||||
|
||||
static String _moneroAmountToStringUsingDecimals(int amount) => _moneroAmountFormat
|
||||
.format(DecimalIntl(cryptoAmountToDecimal(amount: amount, divider: _moneroAmountDivider)));
|
||||
|
||||
static String toBaseUnits(String amount, int decimals) {
|
||||
amount = amount.trim();
|
||||
@@ -83,19 +82,18 @@ class AmountConverter {
|
||||
amount = amount.replaceAll(',', '');
|
||||
final parts = amount.split('.');
|
||||
final whole = parts[0].isEmpty ? '0' : parts[0];
|
||||
final frac = parts.length > 1 ? parts[1] : '';
|
||||
final frac = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
final fracPadded = (frac + '0' * decimals).substring(0, decimals);
|
||||
|
||||
final pow10 = BigInt.from(10).pow(decimals);
|
||||
final wholeBI = BigInt.parse(whole);
|
||||
final fracBI = fracPadded.isEmpty ? BigInt.zero : BigInt.parse(fracPadded);
|
||||
final fracBI = fracPadded.isEmpty ? BigInt.zero : BigInt.parse(fracPadded);
|
||||
|
||||
final res = wholeBI * pow10 + fracBI;
|
||||
return neg ? '-${res.toString()}' : res.toString();
|
||||
}
|
||||
|
||||
|
||||
static String fromBaseUnits(String units, int decimals) {
|
||||
units = units.trim();
|
||||
if (units.isEmpty) return '0';
|
||||
@@ -109,14 +107,14 @@ class AmountConverter {
|
||||
if (units.length <= decimals) {
|
||||
final s = units.padLeft(decimals + 1, '0'); // ensures at least "0xxxx"
|
||||
final intPart = s.substring(0, s.length - decimals);
|
||||
var fracPart = s.substring(s.length - decimals);
|
||||
var fracPart = s.substring(s.length - decimals);
|
||||
fracPart = fracPart.replaceFirst(RegExp(r'0+$'), '');
|
||||
final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart';
|
||||
return neg ? '-$out' : out;
|
||||
}
|
||||
|
||||
final intPart = units.substring(0, units.length - decimals);
|
||||
var fracPart = units.substring(units.length - decimals);
|
||||
var fracPart = units.substring(units.length - decimals);
|
||||
fracPart = fracPart.replaceFirst(RegExp(r'0+$'), '');
|
||||
final out = fracPart.isEmpty ? intPart : '$intPart.$fracPart';
|
||||
return neg ? '-$out' : out;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/// Balance Data class with all amounts in the lowest possible currency (e.g. satoshis or wei)
|
||||
abstract class Balance {
|
||||
const Balance(this.available, this.additional, {this.secondAvailable, this.secondAdditional, this.frozen});
|
||||
|
||||
Balance.fromInt(int available, int additional, {int? secondAvailable, int? secondAdditional, int? frozen}) :
|
||||
available = BigInt.from(available),
|
||||
additional = BigInt.from(additional),
|
||||
secondAvailable = secondAvailable == null ? null : BigInt.from(secondAvailable),
|
||||
secondAdditional = secondAdditional == null ? null : BigInt.from(secondAdditional),
|
||||
frozen = frozen == null ? null : BigInt.from(frozen) {}
|
||||
|
||||
const Balance(this.available, this.additional,
|
||||
{this.secondAvailable, this.secondAdditional, this.frozen});
|
||||
|
||||
Balance.fromInt(int available, int additional,
|
||||
{int? secondAvailable, int? secondAdditional, int? frozen})
|
||||
: available = BigInt.from(available),
|
||||
additional = BigInt.from(additional),
|
||||
secondAvailable = secondAvailable == null ? null : BigInt.from(secondAvailable),
|
||||
secondAdditional = secondAdditional == null ? null : BigInt.from(secondAdditional),
|
||||
frozen = frozen == null ? null : BigInt.from(frozen) {}
|
||||
|
||||
final BigInt available;
|
||||
final BigInt additional;
|
||||
|
||||
@@ -12,11 +12,11 @@ class BalanceCardStyleSettings {
|
||||
|
||||
BalanceCardStyleSettings(
|
||||
{required this.walletInfoId,
|
||||
required this.accountIndex,
|
||||
required this.gradientIndex,
|
||||
required this.useSpecialDesign,
|
||||
required this.backgroundImagePath,
|
||||
required this.cardOrder});
|
||||
required this.accountIndex,
|
||||
required this.gradientIndex,
|
||||
required this.useSpecialDesign,
|
||||
required this.backgroundImagePath,
|
||||
required this.cardOrder});
|
||||
|
||||
static const tableName = "BalanceCardStyleSettings";
|
||||
|
||||
@@ -51,7 +51,7 @@ class BalanceCardStyleSettings {
|
||||
gradientIndex: CardDesign.allGradients.indexOf(design.gradient),
|
||||
useSpecialDesign: design.backgroundType == CardDesignBackgroundTypes.svgFull,
|
||||
backgroundImagePath:
|
||||
design.backgroundType == CardDesignBackgroundTypes.image ? design.imagePath : "",
|
||||
design.backgroundType == CardDesignBackgroundTypes.image ? design.imagePath : "",
|
||||
cardOrder: cardOrder,
|
||||
);
|
||||
}
|
||||
@@ -82,4 +82,4 @@ class BalanceCardStyleSettings {
|
||||
Future<void> insert() async {
|
||||
db!.insert(tableName, toJson(), conflictAlgorithm: ConflictAlgorithm.replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+108
-90
@@ -12,14 +12,13 @@ class CardColorCombination {
|
||||
|
||||
const CardColorCombination(
|
||||
{required this.textColor,
|
||||
required this.textColorSecondary,
|
||||
required this.backgroundImageColor});
|
||||
required this.textColorSecondary,
|
||||
required this.backgroundImageColor});
|
||||
|
||||
static const light = CardColorCombination(
|
||||
textColor: Color.fromARGB(200, 255, 255, 255),
|
||||
textColorSecondary: Colors.white54,
|
||||
backgroundImageColor: Color.fromARGB(200, 0, 0, 0)
|
||||
);
|
||||
backgroundImageColor: Color.fromARGB(200, 0, 0, 0));
|
||||
|
||||
static const dark = CardColorCombination(
|
||||
textColor: Color.fromARGB(200, 0, 0, 0),
|
||||
@@ -42,9 +41,10 @@ class CardDesign {
|
||||
|
||||
const CardDesign(
|
||||
{this.backgroundType = CardDesignBackgroundTypes.svgIcon,
|
||||
this.gradient = const LinearGradient(colors: [Colors.black], begin: Alignment.topCenter, end: Alignment.bottomCenter),
|
||||
this.imagePath = "assets/new-ui/blank.svg",
|
||||
this.colors = CardColorCombination.dark});
|
||||
this.gradient = const LinearGradient(
|
||||
colors: [Colors.black], begin: Alignment.topCenter, end: Alignment.bottomCenter),
|
||||
this.imagePath = "assets/new-ui/blank.svg",
|
||||
this.colors = CardColorCombination.dark});
|
||||
|
||||
static const LinearGradient gradientOrange = LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF7C02), Color(0xFFFF5602)],
|
||||
@@ -120,67 +120,47 @@ class CardDesign {
|
||||
|
||||
static const genericDefault = CardDesign(gradient: gradientBlue);
|
||||
|
||||
static const btc = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/bitcoin.svg");
|
||||
static const btc = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bitcoin.svg");
|
||||
|
||||
static const eth = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/ethereum.svg");
|
||||
static const eth = CardDesign(imagePath: "assets/new-ui/balance_card_icons/ethereum.svg");
|
||||
|
||||
static const btcln = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/lightning.svg");
|
||||
static const btcln = CardDesign(imagePath: "assets/new-ui/balance_card_icons/lightning.svg");
|
||||
|
||||
static const xmr = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/monero.svg");
|
||||
static const xmr = CardDesign(imagePath: "assets/new-ui/balance_card_icons/monero.svg");
|
||||
|
||||
static const ltc = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/litecoin.svg");
|
||||
static const ltc = CardDesign(imagePath: "assets/new-ui/balance_card_icons/litecoin.svg");
|
||||
|
||||
static const bch = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/bitcoin_cash.svg");
|
||||
static const bch = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bitcoin_cash.svg");
|
||||
|
||||
static const doge = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/dogecoin.svg");
|
||||
static const doge = CardDesign(imagePath: "assets/new-ui/balance_card_icons/dogecoin.svg");
|
||||
|
||||
static const base = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/base.svg");
|
||||
static const base = CardDesign(imagePath: "assets/new-ui/balance_card_icons/base.svg");
|
||||
|
||||
static const pol = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/polygon.svg");
|
||||
static const pol = CardDesign(imagePath: "assets/new-ui/balance_card_icons/polygon.svg");
|
||||
|
||||
static const sol = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/solana.svg");
|
||||
static const sol = CardDesign(imagePath: "assets/new-ui/balance_card_icons/solana.svg");
|
||||
|
||||
static const tron = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/tron.svg");
|
||||
static const tron = CardDesign(imagePath: "assets/new-ui/balance_card_icons/tron.svg");
|
||||
|
||||
static const nano = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/nano.svg");
|
||||
static const nano = CardDesign(imagePath: "assets/new-ui/balance_card_icons/nano.svg");
|
||||
|
||||
static const zano = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/zano.svg");
|
||||
static const zano = CardDesign(imagePath: "assets/new-ui/balance_card_icons/zano.svg");
|
||||
|
||||
static const wow = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/wownero.svg");
|
||||
static const wow = CardDesign(imagePath: "assets/new-ui/balance_card_icons/wownero.svg");
|
||||
|
||||
static const dcr = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/decred.svg");
|
||||
static const dcr = CardDesign(imagePath: "assets/new-ui/balance_card_icons/decred.svg");
|
||||
|
||||
static const arbitrum = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/arbitrum.svg");
|
||||
|
||||
static const zec = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/zcash.svg");
|
||||
|
||||
static const bnb = CardDesign(
|
||||
imagePath: "assets/new-ui/balance_card_icons/bnb.svg");
|
||||
static const arbitrum = CardDesign(imagePath: "assets/new-ui/balance_card_icons/arbitrum.svg");
|
||||
|
||||
static const zec = CardDesign(imagePath: "assets/new-ui/balance_card_icons/zcash.svg");
|
||||
|
||||
static const bnb = CardDesign(imagePath: "assets/new-ui/balance_card_icons/bnb.svg");
|
||||
|
||||
static const ethSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF6259FF), Color(0xFF3B20E6)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/ethereum.svg");
|
||||
@@ -188,24 +168,24 @@ class CardDesign {
|
||||
static const btcSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin.svg");
|
||||
|
||||
static const xmrSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF5900), Color(0xFFE62E00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/monero.svg");
|
||||
|
||||
static const ltcSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF2145BF), Color(0xFF072071)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/litecoin.svg");
|
||||
@@ -213,8 +193,8 @@ class CardDesign {
|
||||
static const lnSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFFBF00), Color(0xFFFF6A00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.dark,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/lightning.svg");
|
||||
@@ -222,8 +202,8 @@ class CardDesign {
|
||||
static const tronSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF1313), Color(0xFFB40000)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/tron.svg");
|
||||
@@ -231,8 +211,8 @@ class CardDesign {
|
||||
static const solSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF4701AA), Color(0xFF19004B)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/solana.svg");
|
||||
@@ -240,40 +220,40 @@ class CardDesign {
|
||||
static const bchSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF36DA4C), Color(0xFF008D57)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/bitcoin-cash.svg");
|
||||
|
||||
static const wowSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFFF6CD3), Color(0xFFF200A9)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/wownero.svg");
|
||||
|
||||
static const dogeSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFCBA818), Color(0xFF885D00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/dogecoin.svg");
|
||||
|
||||
static const nanoSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF209CE9), Color(0xFF0073CB)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/nano.svg");
|
||||
|
||||
static const polSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF863FE3), Color(0xFF59098E)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
colors: CardColorCombination.light,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/polygon.svg");
|
||||
@@ -281,8 +261,8 @@ class CardDesign {
|
||||
static const dcrSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF2871FF), Color(0xFF0057FF)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/decred.svg");
|
||||
@@ -290,8 +270,8 @@ class CardDesign {
|
||||
static const zanoSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF0004B4), Color(0xFF170069)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.black,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/zano.svg");
|
||||
@@ -299,8 +279,8 @@ class CardDesign {
|
||||
static const baseSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF003CFF), Color(0xFF000087)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/base.svg");
|
||||
@@ -308,8 +288,8 @@ class CardDesign {
|
||||
static const arbitrumSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF173F76), Color(0xFF031A39)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/arbitrum.svg");
|
||||
@@ -317,27 +297,68 @@ class CardDesign {
|
||||
static const zecSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFFF6C527), Color(0xFFCD8C00)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/zcash.svg");
|
||||
|
||||
static const bnbSpecial = CardDesign(
|
||||
gradient: const LinearGradient(
|
||||
colors: <Color>[Color(0xFF603000), Color(0xFFF0B90B)],
|
||||
begin: Alignment.topCenter, end: Alignment.bottomCenter
|
||||
),
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter),
|
||||
colors: CardColorCombination.light,
|
||||
backgroundType: CardDesignBackgroundTypes.svgFull,
|
||||
imagePath: "assets/new-ui/balance_card_backgrounds/bnb.svg");
|
||||
|
||||
CardDesign withGradient(Gradient gradient) => CardDesign(
|
||||
gradient: gradient, colors: preferredColorCombinations[gradient] ?? colors, imagePath: imagePath, backgroundType: backgroundType);
|
||||
gradient: gradient,
|
||||
colors: preferredColorCombinations[gradient] ?? colors,
|
||||
imagePath: imagePath,
|
||||
backgroundType: backgroundType);
|
||||
|
||||
CardDesign withGradientAndColorCombination(Gradient gradient, CardColorCombination cardColorCombination) => CardDesign(
|
||||
gradient: gradient, colors: cardColorCombination, imagePath: imagePath, backgroundType: backgroundType);
|
||||
CardDesign withGradientAndColorCombination(
|
||||
Gradient gradient, CardColorCombination cardColorCombination) =>
|
||||
CardDesign(
|
||||
gradient: gradient,
|
||||
colors: cardColorCombination,
|
||||
imagePath: imagePath,
|
||||
backgroundType: backgroundType);
|
||||
|
||||
static const List<CardDesign> all = [genericDefault, btc, eth, xmr, ltc, eth, pol, doge, base, sol, btcln, tron, zano, dcr, arbitrum, zec, bnb, ethSpecial, btcSpecial, xmrSpecial, ltcSpecial, lnSpecial, tronSpecial, bchSpecial, wowSpecial, dogeSpecial, polSpecial, dcrSpecial, zanoSpecial, arbitrumSpecial, zecSpecial, bnbSpecial];
|
||||
static const List<CardDesign> all = [
|
||||
genericDefault,
|
||||
btc,
|
||||
eth,
|
||||
xmr,
|
||||
ltc,
|
||||
eth,
|
||||
pol,
|
||||
doge,
|
||||
base,
|
||||
sol,
|
||||
btcln,
|
||||
tron,
|
||||
zano,
|
||||
dcr,
|
||||
arbitrum,
|
||||
zec,
|
||||
bnb,
|
||||
ethSpecial,
|
||||
btcSpecial,
|
||||
xmrSpecial,
|
||||
ltcSpecial,
|
||||
lnSpecial,
|
||||
tronSpecial,
|
||||
bchSpecial,
|
||||
wowSpecial,
|
||||
dogeSpecial,
|
||||
polSpecial,
|
||||
dcrSpecial,
|
||||
zanoSpecial,
|
||||
arbitrumSpecial,
|
||||
zecSpecial,
|
||||
bnbSpecial
|
||||
];
|
||||
|
||||
static CardDesign forCurrencySpecial(CryptoCurrency currency) {
|
||||
return specialDesignsForCurrencies[currency] ?? genericDefault;
|
||||
@@ -366,7 +387,6 @@ class CardDesign {
|
||||
CryptoCurrency.zec: zec,
|
||||
CryptoCurrency.dcr: dcr,
|
||||
CryptoCurrency.bnb: bnb,
|
||||
|
||||
};
|
||||
|
||||
static const Map<CryptoCurrency, CardDesign> specialDesignsForCurrencies = {
|
||||
@@ -415,9 +435,7 @@ class CardDesign {
|
||||
return CardDesign.forCurrencySpecial(walletCurrency)
|
||||
.withGradient(CardDesign.allGradients[setting.gradientIndex]);
|
||||
} else if (!setting.useSpecialDesign && setting.gradientIndex == -1) {
|
||||
final specialColors =
|
||||
specialDesignsForCurrencies[walletCurrency] ??
|
||||
genericDefault;
|
||||
final specialColors = specialDesignsForCurrencies[walletCurrency] ?? genericDefault;
|
||||
return CardDesign.forCurrencyIcon(walletCurrency)
|
||||
.withGradientAndColorCombination(specialColors.gradient, specialColors.colors);
|
||||
} else if (setting.useSpecialDesign) {
|
||||
@@ -430,4 +448,4 @@ class CardDesign {
|
||||
return CardDesign.forCurrencySpecial(walletCurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ extension MaxDecimals on String {
|
||||
return parts.join(".");
|
||||
}
|
||||
|
||||
|
||||
/// Format a stringified number to a localized representation
|
||||
/// 1.000.000,00 in de_DE
|
||||
/// 1’000’000.00 in de_CH
|
||||
|
||||
+828
-143
File diff suppressed because it is too large
Load Diff
@@ -78,7 +78,8 @@ int? getChainIdByCryptoCurrency(CryptoCurrency currency) {
|
||||
}
|
||||
}
|
||||
|
||||
CryptoCurrency getCryptoCurrencyForWalletListItem(WalletType type, {bool isTestnet = false, int? chainId}) {
|
||||
CryptoCurrency getCryptoCurrencyForWalletListItem(WalletType type,
|
||||
{bool isTestnet = false, int? chainId}) {
|
||||
if (type == WalletType.arbitrum) {
|
||||
return CryptoCurrency.arb;
|
||||
}
|
||||
|
||||
+48
-57
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cw_core/root_dir.dart';
|
||||
@@ -7,8 +6,6 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
Database? db;
|
||||
|
||||
|
||||
|
||||
Future<void> _addColumnIfNotExists(
|
||||
Database db, {
|
||||
required String table,
|
||||
@@ -42,10 +39,10 @@ Future<void> initDb({String? pathOverride}) async {
|
||||
}
|
||||
await db?.close();
|
||||
db = await openDatabase(dbFile.path, version: 6,
|
||||
onUpgrade: (Database db, int oldVersion, int newVersion) async {
|
||||
printV("migrating: $oldVersion, $newVersion");
|
||||
if (oldVersion <= 1) {
|
||||
await db.execute('''
|
||||
onUpgrade: (Database db, int oldVersion, int newVersion) async {
|
||||
printV("migrating: $oldVersion, $newVersion");
|
||||
if (oldVersion <= 1) {
|
||||
await db.execute('''
|
||||
DELETE FROM WalletInfo
|
||||
WHERE walletInfoId NOT IN (
|
||||
SELECT MIN(walletInfoId)
|
||||
@@ -56,9 +53,9 @@ WHERE walletInfoId NOT IN (
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_walletinfo_id_unique
|
||||
ON WalletInfo (id);
|
||||
''');
|
||||
}
|
||||
if (oldVersion <= 2) {
|
||||
await db.execute('''
|
||||
}
|
||||
if (oldVersion <= 2) {
|
||||
await db.execute('''
|
||||
CREATE TABLE IF NOT EXISTS BalanceCardStyleSettings (
|
||||
walletInfoId INTEGER,
|
||||
accountIndex INTEGER DEFAULT -1,
|
||||
@@ -69,37 +66,37 @@ CREATE TABLE IF NOT EXISTS BalanceCardStyleSettings (
|
||||
FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
|
||||
);
|
||||
''');
|
||||
await _addColumnIfNotExists(
|
||||
db,
|
||||
table: 'WalletInfo',
|
||||
column: 'receiveInfoboxDismissed',
|
||||
definition: 'BOOLEAN DEFAULT FALSE',
|
||||
);
|
||||
await _addColumnIfNotExists(
|
||||
db,
|
||||
table: 'WalletInfo',
|
||||
column: 'receiveInfoboxDismissed',
|
||||
definition: 'BOOLEAN DEFAULT FALSE',
|
||||
);
|
||||
|
||||
await _addColumnIfNotExists(
|
||||
db,
|
||||
table: 'BalanceCardStyleSettings',
|
||||
column: 'cardOrder',
|
||||
definition: 'INTEGER DEFAULT 0',
|
||||
);
|
||||
}
|
||||
if (oldVersion <= 3) {
|
||||
await _addColumnIfNotExists(db, table: "WalletInfo", column: "showCombinedBalance", definition: "BOOLEAN DEFAULT TRUE");
|
||||
// null - primary token (eth, sol etc)
|
||||
// not null - address of fav token
|
||||
// if address doesn't correspond to a valid token, fallback to primary token
|
||||
await _addColumnIfNotExists(db, table: "WalletInfo", column: "favoriteTokenAddress", definition: "TEXT DEFAULT NULL");
|
||||
}
|
||||
if (oldVersion <= 4) {
|
||||
await _createBridgeTransferTable(db);
|
||||
}
|
||||
if (oldVersion <= 5) {
|
||||
await _createTradeTable(db);
|
||||
}
|
||||
},
|
||||
onCreate: (Database db, int version) async {
|
||||
await db.execute(
|
||||
'''
|
||||
await _addColumnIfNotExists(
|
||||
db,
|
||||
table: 'BalanceCardStyleSettings',
|
||||
column: 'cardOrder',
|
||||
definition: 'INTEGER DEFAULT 0',
|
||||
);
|
||||
}
|
||||
if (oldVersion <= 3) {
|
||||
await _addColumnIfNotExists(db,
|
||||
table: "WalletInfo", column: "showCombinedBalance", definition: "BOOLEAN DEFAULT TRUE");
|
||||
// null - primary token (eth, sol etc)
|
||||
// not null - address of fav token
|
||||
// if address doesn't correspond to a valid token, fallback to primary token
|
||||
await _addColumnIfNotExists(db,
|
||||
table: "WalletInfo", column: "favoriteTokenAddress", definition: "TEXT DEFAULT NULL");
|
||||
}
|
||||
if (oldVersion <= 4) {
|
||||
await _createBridgeTransferTable(db);
|
||||
}
|
||||
if (oldVersion <= 5) {
|
||||
await _createTradeTable(db);
|
||||
}
|
||||
}, onCreate: (Database db, int version) async {
|
||||
await db.execute('''
|
||||
CREATE TABLE WalletInfo (
|
||||
walletInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
id TEXT NOT NULL,
|
||||
@@ -128,8 +125,7 @@ CREATE TABLE WalletInfo (
|
||||
);
|
||||
''');
|
||||
|
||||
await db.execute(
|
||||
'''
|
||||
await db.execute('''
|
||||
CREATE TABLE WalletInfoDerivationInfo (
|
||||
walletInfoDerivationInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
address TEXT NOT NULL,
|
||||
@@ -142,8 +138,7 @@ CREATE TABLE WalletInfoDerivationInfo (
|
||||
);
|
||||
''');
|
||||
|
||||
await db.execute(
|
||||
'''
|
||||
await db.execute('''
|
||||
CREATE TABLE WalletInfoAddress (
|
||||
walletInfoAddressId INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
walletInfoId INTEGER,
|
||||
@@ -153,8 +148,7 @@ CREATE TABLE WalletInfoAddress (
|
||||
);
|
||||
''');
|
||||
|
||||
await db.execute(
|
||||
'''
|
||||
await db.execute('''
|
||||
CREATE TABLE WalletInfoAddressInfo (
|
||||
walletInfoAddressInfoId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
walletInfoId INTEGER NOT NULL,
|
||||
@@ -166,8 +160,7 @@ CREATE TABLE WalletInfoAddressInfo (
|
||||
);
|
||||
''');
|
||||
|
||||
await db.execute(
|
||||
'''
|
||||
await db.execute('''
|
||||
CREATE TABLE "WalletInfoAddressMap" (
|
||||
walletInfoAddressMapId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
walletInfoId INTEGER NOT NULL,
|
||||
@@ -175,13 +168,12 @@ CREATE TABLE "WalletInfoAddressMap" (
|
||||
addressValue TEXT NOT NULL,
|
||||
CONSTRAINT WalletInfoAddress_WalletInfo_FK FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
|
||||
);
|
||||
'''
|
||||
);
|
||||
await db.execute('''
|
||||
''');
|
||||
await db.execute('''
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_walletinfo_id_unique
|
||||
ON WalletInfo (id);
|
||||
''');
|
||||
await db.execute('''
|
||||
await db.execute('''
|
||||
CREATE TABLE BalanceCardStyleSettings (
|
||||
walletInfoId INTEGER,
|
||||
accountIndex INTEGER DEFAULT -1,
|
||||
@@ -193,10 +185,9 @@ CREATE TABLE BalanceCardStyleSettings (
|
||||
FOREIGN KEY (walletInfoId) REFERENCES WalletInfo(walletInfoId)
|
||||
);
|
||||
''');
|
||||
await _createBridgeTransferTable(db);
|
||||
await _createTradeTable(db);
|
||||
}
|
||||
);
|
||||
await _createBridgeTransferTable(db);
|
||||
await _createTradeTable(db);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _createTradeTable(Database db) async {
|
||||
@@ -321,4 +312,4 @@ CREATE TABLE IF NOT EXISTS BridgeTransfer (
|
||||
CREATE INDEX IF NOT EXISTS idx_bridgetransfer_wallet_id
|
||||
ON BridgeTransfer(wallet_id);
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,40 +3,39 @@ import 'dart:typed_data';
|
||||
import 'package:cw_core/utils/file.dart' as file;
|
||||
import 'package:cake_backup/backup.dart' as cwb;
|
||||
|
||||
EncryptionFileUtils encryptionFileUtilsFor(bool direct)
|
||||
=> direct
|
||||
? XChaCha20EncryptionFileUtils()
|
||||
: Salsa20EncryhptionFileUtils();
|
||||
EncryptionFileUtils encryptionFileUtilsFor(bool direct) =>
|
||||
direct ? XChaCha20EncryptionFileUtils() : Salsa20EncryhptionFileUtils();
|
||||
|
||||
abstract class EncryptionFileUtils {
|
||||
Future<void> write({required String path, required String password, required String data});
|
||||
Future<String> read({required String path, required String password});
|
||||
Future<void> write({required String path, required String password, required String data});
|
||||
Future<String> read({required String path, required String password});
|
||||
}
|
||||
|
||||
class Salsa20EncryhptionFileUtils extends EncryptionFileUtils {
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<void> write({required String path, required String password, required String data}) async
|
||||
=> await file.write(path: path, password: password, data: data);
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<void> write(
|
||||
{required String path, required String password, required String data}) async =>
|
||||
await file.write(path: path, password: password, data: data);
|
||||
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async
|
||||
=> await file.read(path: path, password: password);
|
||||
// Requires legacy complex key + iv as password
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async =>
|
||||
await file.read(path: path, password: password);
|
||||
}
|
||||
|
||||
class XChaCha20EncryptionFileUtils extends EncryptionFileUtils {
|
||||
@override
|
||||
Future<void> write({required String path, required String password, required String data}) async {
|
||||
final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits));
|
||||
await File(path).writeAsBytes(encrypted);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async {
|
||||
final file = File(path);
|
||||
final encrypted = await file.readAsBytes();
|
||||
final bytes = await cwb.decrypt(password, encrypted);
|
||||
return String.fromCharCodes(bytes);
|
||||
}
|
||||
}
|
||||
@override
|
||||
Future<void> write({required String path, required String password, required String data}) async {
|
||||
final encrypted = await cwb.encrypt(password, Uint8List.fromList(data.codeUnits));
|
||||
await File(path).writeAsBytes(encrypted);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> read({required String path, required String password}) async {
|
||||
final file = File(path);
|
||||
final encrypted = await file.readAsBytes();
|
||||
final bytes = await cwb.decrypt(password, encrypted);
|
||||
return String.fromCharCodes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ class SignSPLTokenTransactionRentException implements Exception {}
|
||||
|
||||
class NoAssociatedTokenAccountException implements Exception {}
|
||||
|
||||
|
||||
/// ==============================================================================
|
||||
/// ==============================================================================
|
||||
|
||||
@@ -65,4 +64,4 @@ class RestoreFromSeedException implements Exception {
|
||||
final String message;
|
||||
|
||||
RestoreFromSeedException(this.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
String formatAmount(String amount) {
|
||||
if ((!amount.contains('.'))&&(!amount.contains(','))) {
|
||||
if ((!amount.contains('.')) && (!amount.contains(','))) {
|
||||
return amount + '.00';
|
||||
} else if ((amount.endsWith('.'))||(amount.endsWith(','))) {
|
||||
} else if ((amount.endsWith('.')) || (amount.endsWith(','))) {
|
||||
return amount + '00';
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:cw_core/parse_fixed.dart';
|
||||
|
||||
String formatFixed(BigInt value, int? decimals,
|
||||
{int? fractionalDigits, bool trimZeros = true}) {
|
||||
String formatFixed(BigInt value, int? decimals, {int? fractionalDigits, bool trimZeros = true}) {
|
||||
decimals ??= 0;
|
||||
fractionalDigits ??= decimals;
|
||||
|
||||
@@ -10,10 +9,8 @@ String formatFixed(BigInt value, int? decimals,
|
||||
var negative = value.isNegative;
|
||||
if (negative) value = value * BigInt.from(-1);
|
||||
|
||||
var fraction = value
|
||||
.modPow(BigInt.one, BigInt.parse(multiplier))
|
||||
.toString()
|
||||
.padLeft(decimals, "0");
|
||||
var fraction =
|
||||
value.modPow(BigInt.one, BigInt.parse(multiplier)).toString().padLeft(decimals, "0");
|
||||
|
||||
if (fractionalDigits < 0) fractionalDigits = 0;
|
||||
if (fractionalDigits > decimals) fractionalDigits = decimals;
|
||||
|
||||
@@ -6,7 +6,8 @@ import 'dart:convert';
|
||||
|
||||
// FIXME: Hardcoded values; Works only for monero
|
||||
|
||||
int getMoneroHeigthByDate({required DateTime date}) => MoneroHeight.getBlockHeightByTime(date..subtract(Duration(days: 1)));
|
||||
int getMoneroHeigthByDate({required DateTime date}) =>
|
||||
MoneroHeight.getBlockHeightByTime(date..subtract(Duration(days: 1)));
|
||||
|
||||
const havenDates = {
|
||||
"2023-05": 1352995,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
class MoneroHeight {
|
||||
// for i in $(seq 1 364); do echo "_Checkpoint(_t($(curl https://xmrchain.net/block/${i}0000 | grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}' | xargs | sed 's/ /\n/g' | head -5 | tail -1 | tr '()' ' ' | sed 's/ /\n/g' | head -2 | xargs)), ${i}0000),"; done > points.txt
|
||||
static final List<_Checkpoint> _points = [
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
const CONTACT_TYPE_ID = 0;
|
||||
const NODE_TYPE_ID = 1;
|
||||
const TRANSACTION_TYPE_ID = 2;
|
||||
const TRADE_TYPE_ID = 3;
|
||||
const WALLET_INFO_TYPE_ID = 4;
|
||||
const WALLET_TYPE_TYPE_ID = 5;
|
||||
const TEMPLATE_TYPE_ID = 6;
|
||||
const EXCHANGE_TEMPLATE_TYPE_ID = 7;
|
||||
const ORDER_TYPE_ID = 8;
|
||||
const UNSPENT_COINS_INFO_TYPE_ID = 9;
|
||||
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
||||
const ADDRESS_INFO_TYPE_ID = 11;
|
||||
const ERC20_TOKEN_TYPE_ID = 12;
|
||||
const NANO_ACCOUNT_TYPE_ID = 13;
|
||||
const POW_NODE_TYPE_ID = 14;
|
||||
const DERIVATION_TYPE_TYPE_ID = 15;
|
||||
const SPL_TOKEN_TYPE_ID = 16;
|
||||
const DERIVATION_INFO_TYPE_ID = 17;
|
||||
const TRON_TOKEN_TYPE_ID = 18;
|
||||
const HARDWARE_WALLET_TYPE_TYPE_ID = 19;
|
||||
const MWEB_UTXO_TYPE_ID = 20;
|
||||
const HAVEN_SEED_STORE_TYPE_ID = 21;
|
||||
const ZANO_ASSET_TYPE_ID = 22;
|
||||
const PAYJOIN_SESSION_TYPE_ID = 23;
|
||||
const CONTACT_TYPE_ID = 0;
|
||||
const NODE_TYPE_ID = 1;
|
||||
const TRANSACTION_TYPE_ID = 2;
|
||||
const TRADE_TYPE_ID = 3;
|
||||
const WALLET_INFO_TYPE_ID = 4;
|
||||
const WALLET_TYPE_TYPE_ID = 5;
|
||||
const TEMPLATE_TYPE_ID = 6;
|
||||
const EXCHANGE_TEMPLATE_TYPE_ID = 7;
|
||||
const ORDER_TYPE_ID = 8;
|
||||
const UNSPENT_COINS_INFO_TYPE_ID = 9;
|
||||
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
|
||||
const ADDRESS_INFO_TYPE_ID = 11;
|
||||
const ERC20_TOKEN_TYPE_ID = 12;
|
||||
const NANO_ACCOUNT_TYPE_ID = 13;
|
||||
const POW_NODE_TYPE_ID = 14;
|
||||
const DERIVATION_TYPE_TYPE_ID = 15;
|
||||
const SPL_TOKEN_TYPE_ID = 16;
|
||||
const DERIVATION_INFO_TYPE_ID = 17;
|
||||
const TRON_TOKEN_TYPE_ID = 18;
|
||||
const HARDWARE_WALLET_TYPE_TYPE_ID = 19;
|
||||
const MWEB_UTXO_TYPE_ID = 20;
|
||||
const HAVEN_SEED_STORE_TYPE_ID = 21;
|
||||
const ZANO_ASSET_TYPE_ID = 22;
|
||||
const PAYJOIN_SESSION_TYPE_ID = 23;
|
||||
|
||||
@@ -16,7 +16,8 @@ List<String> extractKeys(String key) {
|
||||
return [_key, iv];
|
||||
}
|
||||
|
||||
Future<String> encode({required encrypt.Key key, required encrypt.IV iv, required String data}) async {
|
||||
Future<String> encode(
|
||||
{required encrypt.Key key, required encrypt.IV iv, required String data}) async {
|
||||
final encrypter = encrypt.Encrypter(encrypt.Salsa20(key));
|
||||
final encrypted = encrypter.encrypt(data, iv: iv);
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
mixin Keyable {
|
||||
dynamic keyIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ const _LUD17_PREFIXES = ['lnurlw', 'lnurlc', 'lnurlp', 'keyauth'];
|
||||
bool isBolt11ZeroInvoice(String invoice) {
|
||||
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
|
||||
|
||||
final prefix = _BOLT_PREFIXES
|
||||
.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
|
||||
final prefix =
|
||||
_BOLT_PREFIXES.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
|
||||
|
||||
return request.hrp.length == prefix.length;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ int? _getAmountBolt11Msat(String invoice) {
|
||||
final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
|
||||
|
||||
final prefix = _BOLT_PREFIXES.firstWhere(
|
||||
(prefix) => request.hrp.startsWith(prefix),
|
||||
(prefix) => request.hrp.startsWith(prefix),
|
||||
orElse: () => throw FormatException('Invalid BOLT11 invoice: unknown HRP prefix.'),
|
||||
);
|
||||
|
||||
@@ -79,8 +79,7 @@ class LNURL {
|
||||
final maxSendable = body["maxSendable"] as int?;
|
||||
|
||||
// if minSendable and maxSendable are the same we assume a specific payment request
|
||||
if (msat != maxSendable)
|
||||
return null;
|
||||
if (msat != maxSendable) return null;
|
||||
|
||||
if (msat == null || msat % 1000 != 0) return null;
|
||||
return msat ~/ 1000;
|
||||
@@ -129,8 +128,7 @@ class LNURL {
|
||||
String _findLnUrl(String input) {
|
||||
final res = RegExp(r',*?((lnurl)([0-9]+[a-z0-9]+))').allMatches(input.toLowerCase());
|
||||
|
||||
if (res.length != 1)
|
||||
throw ArgumentError('Not a valid lnurl string');
|
||||
if (res.length != 1) throw ArgumentError('Not a valid lnurl string');
|
||||
return res.first.group(0)!;
|
||||
}
|
||||
|
||||
|
||||
@@ -1627,4 +1627,4 @@ class ChineseSimplifiedMnemonics {
|
||||
"浙",
|
||||
"貌"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,4 +1627,4 @@ class DutchMnemonics {
|
||||
"zwijmel",
|
||||
"zworen"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+1629
-1629
File diff suppressed because it is too large
Load Diff
@@ -1627,4 +1627,4 @@ class GermanMnemonics {
|
||||
"Zweck",
|
||||
"Zyklop"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+1629
-1629
File diff suppressed because it is too large
Load Diff
@@ -1627,4 +1627,4 @@ class JapaneseMnemonics {
|
||||
"びじゅつかん",
|
||||
"ひしょ"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,4 +1627,4 @@ class PortugueseMnemonics {
|
||||
"zenite",
|
||||
"zumbi"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,4 +1627,4 @@ class RussianMnemonics {
|
||||
"ячейка",
|
||||
"ящик"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,4 +1627,4 @@ class SpanishMnemonics {
|
||||
"ritmo",
|
||||
"rito"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
class MoneroWalletKeys {
|
||||
const MoneroWalletKeys(
|
||||
{required this.primaryAddress,
|
||||
required this.privateSpendKey,
|
||||
required this.privateViewKey,
|
||||
required this.publicSpendKey,
|
||||
required this.publicViewKey,
|
||||
required this.passphrase});
|
||||
required this.privateSpendKey,
|
||||
required this.privateViewKey,
|
||||
required this.publicSpendKey,
|
||||
required this.publicViewKey,
|
||||
required this.passphrase});
|
||||
|
||||
final String primaryAddress;
|
||||
final String publicViewKey;
|
||||
@@ -13,4 +13,4 @@ class MoneroWalletKeys {
|
||||
final String publicSpendKey;
|
||||
final String privateSpendKey;
|
||||
final String passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +297,6 @@ class Node extends HiveObject with Keyable {
|
||||
final ProxySocket socket;
|
||||
socket = await ProxyWrapper().getSocksSocket(useSSL ?? false, uri.host, uri.port);
|
||||
|
||||
|
||||
socket.destroy();
|
||||
return true;
|
||||
} catch (_) {
|
||||
@@ -317,7 +316,7 @@ class Node extends HiveObject with Keyable {
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
final data = jsonDecode(response.body);
|
||||
if (response.statusCode != 200 ||
|
||||
data["error"] != null ||
|
||||
@@ -334,9 +333,12 @@ class Node extends HiveObject with Keyable {
|
||||
|
||||
Future<bool> requestEthereumServer() async {
|
||||
try {
|
||||
final req = await ProxyWrapper().getHttpClient()
|
||||
.getUrl(uri,)
|
||||
.timeout(Duration(seconds: 15));
|
||||
final req = await ProxyWrapper()
|
||||
.getHttpClient()
|
||||
.getUrl(
|
||||
uri,
|
||||
)
|
||||
.timeout(Duration(seconds: 15));
|
||||
final response = await req.close();
|
||||
|
||||
return response.statusCode >= 200 && response.statusCode < 300;
|
||||
@@ -345,7 +347,6 @@ class Node extends HiveObject with Keyable {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// https://github.com/ManyMath/digest_auth/
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
class OutputInfo {
|
||||
const OutputInfo(
|
||||
{required this.address,
|
||||
required this.sendAll,
|
||||
required this.isParsedAddress,
|
||||
this.cryptoAmount,
|
||||
this.formattedCryptoAmount,
|
||||
this.fiatAmount,
|
||||
this.note,
|
||||
this.extractedAddress,
|
||||
this.memo,
|
||||
this.extra = const {}});
|
||||
const OutputInfo(
|
||||
{required this.address,
|
||||
required this.sendAll,
|
||||
required this.isParsedAddress,
|
||||
this.cryptoAmount,
|
||||
this.formattedCryptoAmount,
|
||||
this.fiatAmount,
|
||||
this.note,
|
||||
this.extractedAddress,
|
||||
this.memo,
|
||||
this.extra = const {}});
|
||||
|
||||
final String? fiatAmount;
|
||||
final String? cryptoAmount;
|
||||
final String address;
|
||||
final String? note;
|
||||
final String? extractedAddress;
|
||||
final bool sendAll;
|
||||
final bool isParsedAddress;
|
||||
final int? formattedCryptoAmount;
|
||||
final String? memo;
|
||||
final Map<String, dynamic> extra;
|
||||
}
|
||||
final String? fiatAmount;
|
||||
final String? cryptoAmount;
|
||||
final String address;
|
||||
final String? note;
|
||||
final String? extractedAddress;
|
||||
final bool sendAll;
|
||||
final bool isParsedAddress;
|
||||
final int? formattedCryptoAmount;
|
||||
final String? memo;
|
||||
final Map<String, dynamic> extra;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
bool parseBoolFromString(String string) {
|
||||
return string.toString() == 'true';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ Future<String> pathForWalletDir({required String name, required WalletType type}
|
||||
}
|
||||
|
||||
Future<String> pathForWallet({required String name, required WalletType type}) async =>
|
||||
await pathForWalletDir(name: name, type: type)
|
||||
.then((path) => path + '/$name');
|
||||
await pathForWalletDir(name: name, type: type).then((path) => path + '/$name');
|
||||
|
||||
Future<String> outdatedAndroidPathForWalletDir({required String name}) async {
|
||||
final directory = await getAppDir();
|
||||
|
||||
@@ -56,7 +56,6 @@ class PayjoinSession extends HiveObject {
|
||||
|
||||
BigInt get amount => BigInt.parse(rawAmount ?? "0");
|
||||
set amount(BigInt amount) => rawAmount = amount.toString();
|
||||
|
||||
}
|
||||
|
||||
enum PayjoinSessionStatus {
|
||||
|
||||
@@ -383,14 +383,14 @@ class ERC681URI extends PaymentURI {
|
||||
|
||||
static int _getChainID(String path) {
|
||||
return int.parse(RegExp(
|
||||
r'@\d*',
|
||||
).firstMatch(path)?.group(0)?.replaceAll("@", "") ??
|
||||
r'@\d*',
|
||||
).firstMatch(path)?.group(0)?.replaceAll("@", "") ??
|
||||
"1");
|
||||
}
|
||||
|
||||
static (bool, String) _getTargetAddress(String path) {
|
||||
final targetAddress =
|
||||
RegExp(r'^(0x)?[0-9a-f]{40}', caseSensitive: false).firstMatch(path)!.group(0)!;
|
||||
RegExp(r'^(0x)?[0-9a-f]{40}', caseSensitive: false).firstMatch(path)!.group(0)!;
|
||||
return (path.contains("/"), targetAddress);
|
||||
}
|
||||
|
||||
@@ -483,4 +483,3 @@ class LightningPaymentRequest extends PaymentURI {
|
||||
@override
|
||||
String toString() => bolt11Invoice ?? "lightning:$lnURL";
|
||||
}
|
||||
|
||||
|
||||
+17
-11
@@ -27,11 +27,12 @@ void setRootDirFromEnv() =>
|
||||
// HASH_WALLET_DIR is the canonical override; CAKE_WALLET_DIR kept as a
|
||||
// fallback so users migrating from a Cake install via env-var override
|
||||
// don't lose their path.
|
||||
_rootDirPath = Platform.environment['HASH_WALLET_DIR'] ?? Platform.environment['CAKE_WALLET_DIR'];
|
||||
_rootDirPath =
|
||||
Platform.environment['HASH_WALLET_DIR'] ?? Platform.environment['CAKE_WALLET_DIR'];
|
||||
|
||||
void copyDirectory(Directory source, Directory destination) {
|
||||
source.listSync(recursive: false).forEach((var entity) {
|
||||
if (entity is Directory) {
|
||||
if (entity is Directory) {
|
||||
var newDirectory = Directory(p.join(destination.absolute.path, p.basename(entity.path)));
|
||||
newDirectory.createSync(recursive: true);
|
||||
copyDirectory(entity.absolute, newDirectory);
|
||||
@@ -44,7 +45,8 @@ void copyDirectory(Directory source, Directory destination) {
|
||||
|
||||
Future<void> linuxSymlinkSharedPreferences() async {
|
||||
if (!Platform.isLinux) return; // nuh-uh
|
||||
final dataHome = Platform.environment["XDG_DATA_HOME"] ?? p.join(Platform.environment["HOME"] ?? "", ".local", "share");
|
||||
final dataHome = Platform.environment["XDG_DATA_HOME"] ??
|
||||
p.join(Platform.environment["HOME"] ?? "", ".local", "share");
|
||||
// Names to migrate from on startup. Includes the legacy Cake paths so users
|
||||
// forking from a previous Cake install get their data automatically
|
||||
// symlinked into Hash Bags's new home (~/.local/share/hash_wallet).
|
||||
@@ -59,8 +61,8 @@ Future<void> linuxSymlinkSharedPreferences() async {
|
||||
if (oldLink.existsSync()) {
|
||||
printV("not creating, link exists");
|
||||
} else {
|
||||
if (newDir.existsSync()) {
|
||||
newDir.renameSync("${newPath}_${DateTime.now().millisecondsSinceEpoch~/1000}");
|
||||
if (newDir.existsSync()) {
|
||||
newDir.renameSync("${newPath}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}");
|
||||
}
|
||||
copyDirectory(oldDir, newDir);
|
||||
oldDir.deleteSync(recursive: true);
|
||||
@@ -100,14 +102,16 @@ Future<Directory> getAppDir() async {
|
||||
// for storing things that users in general back-up
|
||||
var linuxAppPath = [
|
||||
if (appDirPath != null) appDirPath, // old preferred
|
||||
p.join('/home', Platform.environment['USER']??"null", appName), // old fallback
|
||||
if (Platform.environment['HOME'] != null) p.join(Platform.environment['HOME']!, ".$appName"), // old fallback but using HOME
|
||||
if (Platform.environment['HOME'] != null) p.join(Platform.environment['HOME']!, '.config', appName), // old fallback but using HOME
|
||||
p.join('/home', Platform.environment['USER'] ?? "null", appName), // old fallback
|
||||
if (Platform.environment['HOME'] != null)
|
||||
p.join(Platform.environment['HOME']!, ".$appName"), // old fallback but using HOME
|
||||
if (Platform.environment['HOME'] != null)
|
||||
p.join(Platform.environment['HOME']!, '.config', appName), // old fallback but using HOME
|
||||
if (isNonAmnesticTails) p.join(_tailsData, ".$appName") // tails (if persistance is enabled)
|
||||
];
|
||||
|
||||
String preferredPath = linuxAppPath.last;
|
||||
|
||||
|
||||
preferredLoop:
|
||||
for (String notSoPreferredPath in linuxAppPath) {
|
||||
if (notSoPreferredPath == linuxAppPath.last) continue;
|
||||
@@ -115,8 +119,10 @@ Future<Directory> getAppDir() async {
|
||||
if (useThisOne) {
|
||||
if (showNotice) {
|
||||
showNotice = false;
|
||||
printV("Not using $preferredPath because $notSoPreferredPath exists, falling back for backwards compatibility");
|
||||
printV("Can't see your wallet? Check\n - ${linuxAppPath.join("\n - ")}\n and move directory that to $preferredPath");
|
||||
printV(
|
||||
"Not using $preferredPath because $notSoPreferredPath exists, falling back for backwards compatibility");
|
||||
printV(
|
||||
"Can't see your wallet? Check\n - ${linuxAppPath.join("\n - ")}\n and move directory that to $preferredPath");
|
||||
printV("Or use CAKE_WALLET_DIR=/path/to/app/ ${Platform.executable}");
|
||||
}
|
||||
preferredPath = notSoPreferredPath;
|
||||
|
||||
@@ -4,14 +4,12 @@ import 'package:on_chain/solana/solana.dart';
|
||||
|
||||
class SolanaRPCHTTPService implements SolanaJSONRPCService {
|
||||
SolanaRPCHTTPService(
|
||||
{required this.url,
|
||||
this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||
{required this.url, this.defaultRequestTimeout = const Duration(seconds: 30)});
|
||||
@override
|
||||
final String url;
|
||||
final Duration defaultRequestTimeout;
|
||||
|
||||
Future<Map<String, dynamic>> call(SolanaRequestDetails params,
|
||||
[Duration? timeout]) async {
|
||||
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
|
||||
final response = await ProxyWrapper().post(
|
||||
clearnetUri: Uri.parse(url),
|
||||
body: params.toRequestBody(),
|
||||
|
||||
@@ -7,7 +7,8 @@ TransactionDirection parseTransactionDirectionFromInt(int raw) {
|
||||
case 1:
|
||||
return TransactionDirection.outgoing;
|
||||
default:
|
||||
throw Exception('Unexpected token: raw for TransactionDirection parseTransactionDirectionFromInt');
|
||||
throw Exception(
|
||||
'Unexpected token: raw for TransactionDirection parseTransactionDirectionFromInt');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +19,7 @@ TransactionDirection parseTransactionDirectionFromNumber(String raw) {
|
||||
case "1":
|
||||
return TransactionDirection.outgoing;
|
||||
default:
|
||||
throw Exception('Unexpected token: raw for TransactionDirection parseTransactionDirectionFromNumber');
|
||||
throw Exception(
|
||||
'Unexpected token: raw for TransactionDirection parseTransactionDirectionFromNumber');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
|
||||
abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
|
||||
TransactionHistoryBase()
|
||||
: transactions = ObservableMap<String, TransactionType>();
|
||||
TransactionHistoryBase() : transactions = ObservableMap<String, TransactionType>();
|
||||
|
||||
@observable
|
||||
ObservableMap<String, TransactionType> transactions;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
abstract class TransactionPriority extends EnumerableItem<int>
|
||||
with Serializable<int> {
|
||||
abstract class TransactionPriority extends EnumerableItem<int> with Serializable<int> {
|
||||
final String? description;
|
||||
final String? hint;
|
||||
|
||||
const TransactionPriority({required String title, required int raw, this.description, this.hint}) : super(title: title, raw: raw);
|
||||
const TransactionPriority({required String title, required int raw, this.description, this.hint})
|
||||
: super(title: title, raw: raw);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ Future<void> write({required String path, required String password, required Str
|
||||
writeData(path: path, password: password, data: data);
|
||||
|
||||
Future<void> writeData(
|
||||
{required String path,
|
||||
required String password,
|
||||
required String data}) async {
|
||||
{required String path, required String password, required String data}) async {
|
||||
final keys = extractKeys(password);
|
||||
final key = encrypt.Key.fromBase64(keys.first);
|
||||
final iv = encrypt.IV.fromBase64(keys.last);
|
||||
|
||||
@@ -11,7 +11,7 @@ enum RequestMethod {
|
||||
post,
|
||||
put,
|
||||
delete,
|
||||
|
||||
|
||||
newHttpClient,
|
||||
newHttpIOClient,
|
||||
newProxySocket,
|
||||
@@ -23,7 +23,7 @@ abstract class ProxyLogger {
|
||||
required RequestMethod method,
|
||||
required Uint8List body,
|
||||
required very_insecure_http_do_not_use.Response? response,
|
||||
required RequestNetwork network,
|
||||
required RequestNetwork network,
|
||||
required String? error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +46,20 @@ class MemoryProxyLogger implements ProxyLogger {
|
||||
required RequestMethod method,
|
||||
required Uint8List body,
|
||||
required very_insecure_http_do_not_use.Response? response,
|
||||
required RequestNetwork network,
|
||||
required RequestNetwork network,
|
||||
required String? error,
|
||||
}) {
|
||||
final trace = StackTrace.current;
|
||||
logs.add(MemoryProxyLoggerEntry(
|
||||
method: method,
|
||||
trace: trace,
|
||||
uri: uri,
|
||||
body: body,
|
||||
network: network,
|
||||
response: response,
|
||||
error: error,),
|
||||
logs.add(
|
||||
MemoryProxyLoggerEntry(
|
||||
method: method,
|
||||
trace: trace,
|
||||
uri: uri,
|
||||
body: body,
|
||||
network: network,
|
||||
response: response,
|
||||
error: error,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class SilentProxyLogger implements ProxyLogger {
|
||||
required RequestMethod method,
|
||||
required Uint8List body,
|
||||
required very_insecure_http_do_not_use.Response? response,
|
||||
required RequestNetwork network,
|
||||
required RequestNetwork network,
|
||||
required String? error,
|
||||
}) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,21 @@ class ProxyAddress {
|
||||
}
|
||||
|
||||
abstract class ProxySocket {
|
||||
static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address, {Duration? connectionTimeout}) async {
|
||||
static Future<ProxySocket> connect(bool sslEnabled, ProxyAddress address,
|
||||
{Duration? connectionTimeout}) async {
|
||||
if (CakeTor.instance!.started) {
|
||||
var socksSocket = await SOCKSSocket.create(
|
||||
proxyHost: InternetAddress.loopbackIPv4.address,
|
||||
proxyPort: CakeTor.instance!.port,
|
||||
sslEnabled: sslEnabled,
|
||||
proxyHost: InternetAddress.loopbackIPv4.address,
|
||||
proxyPort: CakeTor.instance!.port,
|
||||
sslEnabled: sslEnabled,
|
||||
);
|
||||
await socksSocket.connect();
|
||||
await socksSocket.connectTo(address.host, address.port);
|
||||
return ProxySocketSocks(socksSocket);
|
||||
}
|
||||
if (sslEnabled == false) {
|
||||
return ProxySocketInsecure(await Socket.connect(address.host, address.port, timeout: connectionTimeout));
|
||||
return ProxySocketInsecure(
|
||||
await Socket.connect(address.host, address.port, timeout: connectionTimeout));
|
||||
} else {
|
||||
return ProxySocketSecure(await SecureSocket.connect(
|
||||
address.host,
|
||||
@@ -43,6 +45,7 @@ abstract class ProxySocket {
|
||||
void destroy();
|
||||
void write(String data);
|
||||
bool get isClosed;
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function (Object error)? onError, Function ()? onDone, bool cancelOnError = true});
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true});
|
||||
ProxyAddress get address;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/utils/proxy_socket/abstract.dart';
|
||||
import 'dart:async';
|
||||
@@ -9,15 +8,16 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
final Socket socket;
|
||||
|
||||
ProxySocketInsecure(this.socket);
|
||||
|
||||
|
||||
bool isClosed = false;
|
||||
|
||||
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
ProxyAddress get address =>
|
||||
ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
if (isClosed) return;
|
||||
if (isClosed) return;
|
||||
isClosed = true;
|
||||
return socket.close();
|
||||
} catch (e) {
|
||||
@@ -25,7 +25,7 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void destroy() async {
|
||||
try {
|
||||
@@ -37,7 +37,7 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void write(String data) {
|
||||
try {
|
||||
@@ -51,9 +51,10 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
return socket.listen(
|
||||
(data) {
|
||||
onData(Uint8List.fromList(data));
|
||||
@@ -63,4 +64,4 @@ class ProxySocketInsecure implements ProxySocket {
|
||||
cancelOnError: cancelOnError,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ class ProxySocketSecure implements ProxySocket {
|
||||
bool isClosed = false;
|
||||
|
||||
ProxySocketSecure(this.socket);
|
||||
|
||||
ProxyAddress get address => ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
|
||||
ProxyAddress get address =>
|
||||
ProxyAddress(host: socket.remoteAddress.host, port: socket.remotePort);
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
@@ -25,11 +26,11 @@ class ProxySocketSecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void destroy() async {
|
||||
try {
|
||||
if (isClosed) return;
|
||||
if (isClosed) return;
|
||||
isClosed = true;
|
||||
socket.destroy();
|
||||
} catch (e) {
|
||||
@@ -37,7 +38,7 @@ class ProxySocketSecure implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void write(String data) {
|
||||
runZonedGuarded(() {
|
||||
@@ -57,9 +58,10 @@ class ProxySocketSecure implements ProxySocket {
|
||||
printV("ProxySocketSecure: write (async error): $e");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
return socket.listen(
|
||||
(data) {
|
||||
onData(Uint8List.fromList(data));
|
||||
|
||||
@@ -9,10 +9,10 @@ class ProxySocketSocks implements ProxySocket {
|
||||
final SOCKSSocket socket;
|
||||
bool isClosed = false;
|
||||
ProxySocketSocks(this.socket);
|
||||
|
||||
|
||||
@override
|
||||
ProxyAddress get address => ProxyAddress(host: socket.proxyHost, port: socket.proxyPort);
|
||||
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
try {
|
||||
@@ -24,10 +24,10 @@ class ProxySocketSocks implements ProxySocket {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void destroy() => close();
|
||||
|
||||
|
||||
@override
|
||||
void write(String data) {
|
||||
try {
|
||||
@@ -43,7 +43,8 @@ class ProxySocketSocks implements ProxySocket {
|
||||
}
|
||||
|
||||
@override
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData, {Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
StreamSubscription<List<int>> listen(Function(Uint8List event) onData,
|
||||
{Function(Object error)? onError, Function()? onDone, bool cancelOnError = true}) {
|
||||
return socket.listen(
|
||||
(data) {
|
||||
onData(Uint8List.fromList(data));
|
||||
@@ -54,4 +55,3 @@ class ProxySocketSocks implements ProxySocket {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,22 +16,23 @@ class ProxyWrapper {
|
||||
factory ProxyWrapper() {
|
||||
return _proxyWrapper;
|
||||
}
|
||||
|
||||
|
||||
ProxyWrapper._internal();
|
||||
Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port, {Duration? connectionTimeout}) async {
|
||||
Future<ProxySocket> getSocksSocket(bool sslEnabled, String host, int port,
|
||||
{Duration? connectionTimeout}) async {
|
||||
logger?.log(
|
||||
uri: Uri(
|
||||
scheme: sslEnabled ? "https" : "http",
|
||||
host: host,
|
||||
port: port,
|
||||
),
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null
|
||||
);
|
||||
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port), connectionTimeout: connectionTimeout);
|
||||
uri: Uri(
|
||||
scheme: sslEnabled ? "https" : "http",
|
||||
host: host,
|
||||
port: port,
|
||||
),
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null);
|
||||
return ProxySocket.connect(sslEnabled, ProxyAddress(host: host, port: port),
|
||||
connectionTimeout: connectionTimeout);
|
||||
}
|
||||
|
||||
RequestNetwork requestNetwork() {
|
||||
@@ -56,17 +57,17 @@ class ProxyWrapper {
|
||||
|
||||
int getPort() => CakeTor.instance!.port;
|
||||
|
||||
@Deprecated('Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
|
||||
@Deprecated(
|
||||
'Use ProxyWrapper().get/post/put methods instead, and provide proper clearnet and onion uri.')
|
||||
HttpClient getHttpClient({int? portOverride, bool internal = false}) {
|
||||
if (!internal) {
|
||||
logger?.log(
|
||||
uri: null,
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null
|
||||
);
|
||||
uri: null,
|
||||
method: RequestMethod.newProxySocket,
|
||||
body: Uint8List(0),
|
||||
response: null,
|
||||
network: requestNetwork(),
|
||||
error: null);
|
||||
}
|
||||
if (CakeTor.instance!.started) {
|
||||
// Assign connection factory.
|
||||
@@ -84,8 +85,6 @@ class ProxyWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<Response> _make({
|
||||
required RequestMethod method,
|
||||
required ioc.IOClient client,
|
||||
@@ -216,7 +215,6 @@ class ProxyWrapper {
|
||||
|
||||
throw Exception("Unable to connect to server");
|
||||
}
|
||||
|
||||
|
||||
Future<Response> post({
|
||||
Map<String, String>? headers,
|
||||
@@ -235,7 +233,6 @@ class ProxyWrapper {
|
||||
|
||||
ioc.IOClient clearnetClient = ioc.IOClient(cleatnetHttpClient);
|
||||
|
||||
|
||||
bool torEnabled = CakeTor.instance!.started;
|
||||
|
||||
if (torEnabled) {
|
||||
@@ -439,7 +436,6 @@ class ProxyWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CakeTor {
|
||||
static CakeTorInstance? instance;
|
||||
}
|
||||
|
||||
@@ -65,4 +65,4 @@ CakeTorInstance(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,4 @@ CakeTorDisabled(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,4 @@ CakeTorSocks(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,4 +88,4 @@ CakeTorTorch(
|
||||
)
|
||||
""";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,4 +1,3 @@
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
@@ -15,31 +14,32 @@ String convertAnyToXpub(String any) {
|
||||
String convertZpubToXpub(String zpub) {
|
||||
try {
|
||||
final decoded = Base58Decoder.checkDecode(zpub);
|
||||
|
||||
|
||||
if (decoded.length < 4) {
|
||||
throw ArgumentError('Invalid extended public key length');
|
||||
}
|
||||
|
||||
|
||||
final versionBytes = decoded.sublist(0, 4);
|
||||
final zpubVersionBytes = [0x04, 0xb2, 0x47, 0x46]; // zpub mainnet version
|
||||
final zpubTestnetVersionBytes = [0x04, 0x5f, 0x1c, 0xf6]; // vpub testnet version
|
||||
|
||||
|
||||
bool isZpub = listEquals(versionBytes, zpubVersionBytes);
|
||||
bool isVpub = listEquals(versionBytes, zpubTestnetVersionBytes);
|
||||
|
||||
|
||||
if (!isZpub && !isVpub) {
|
||||
return zpub;
|
||||
}
|
||||
|
||||
final xpubVersionBytes = isZpub ?
|
||||
[0x04, 0x88, 0xb2, 0x1e] : // xpub mainnet
|
||||
[0x04, 0x35, 0x87, 0xcf]; // tpub testnet
|
||||
|
||||
|
||||
final xpubVersionBytes = isZpub
|
||||
? [0x04, 0x88, 0xb2, 0x1e]
|
||||
: // xpub mainnet
|
||||
[0x04, 0x35, 0x87, 0xcf]; // tpub testnet
|
||||
|
||||
final newExtendedKey = Uint8List.fromList([
|
||||
...xpubVersionBytes,
|
||||
...decoded.sublist(4),
|
||||
]);
|
||||
|
||||
|
||||
return Base58Encoder.checkEncode(newExtendedKey);
|
||||
} catch (e) {
|
||||
throw ArgumentError('Failed to convert zpub to xpub: $e');
|
||||
|
||||
+119
-121
@@ -327,79 +327,77 @@ class DerivationInfo {
|
||||
|
||||
class WalletInfo {
|
||||
WalletInfo(
|
||||
this.internalId,
|
||||
this.id,
|
||||
this.name,
|
||||
this.type,
|
||||
this.isRecovery,
|
||||
this.restoreHeight,
|
||||
this.timestamp,
|
||||
this.dirPath,
|
||||
this.path,
|
||||
this.address,
|
||||
this.yatEid,
|
||||
this.yatLastUsedAddressRaw,
|
||||
this.showIntroCakePayCard,
|
||||
this.derivationInfoId,
|
||||
this.hardwareWalletType,
|
||||
this.parentAddress,
|
||||
this.hashedWalletIdentifier,
|
||||
this.isNonSeedWallet,
|
||||
this.sortOrder,
|
||||
this.addressPageType,
|
||||
this.receiveInfoboxDismissed,
|
||||
this.showCombinedBalance,
|
||||
this.favoriteTokenAddress
|
||||
) : _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
this.internalId,
|
||||
this.id,
|
||||
this.name,
|
||||
this.type,
|
||||
this.isRecovery,
|
||||
this.restoreHeight,
|
||||
this.timestamp,
|
||||
this.dirPath,
|
||||
this.path,
|
||||
this.address,
|
||||
this.yatEid,
|
||||
this.yatLastUsedAddressRaw,
|
||||
this.showIntroCakePayCard,
|
||||
this.derivationInfoId,
|
||||
this.hardwareWalletType,
|
||||
this.parentAddress,
|
||||
this.hashedWalletIdentifier,
|
||||
this.isNonSeedWallet,
|
||||
this.sortOrder,
|
||||
this.addressPageType,
|
||||
this.receiveInfoboxDismissed,
|
||||
this.showCombinedBalance,
|
||||
this.favoriteTokenAddress)
|
||||
: _yatLastUsedAddressController = StreamController<String>.broadcast();
|
||||
|
||||
factory WalletInfo.external({
|
||||
required String id,
|
||||
required String name,
|
||||
required WalletType type,
|
||||
required bool isRecovery,
|
||||
required int restoreHeight,
|
||||
required DateTime date,
|
||||
required String dirPath,
|
||||
required String path,
|
||||
required String address,
|
||||
bool? showIntroCakePayCard,
|
||||
String yatEid = '',
|
||||
String yatLastUsedAddressRaw = '',
|
||||
int? derivationInfoId,
|
||||
HardwareWalletType? hardwareWalletType,
|
||||
String? parentAddress,
|
||||
String? hashedWalletIdentifier,
|
||||
bool? isNonSeedWallet,
|
||||
int? sortOrder,
|
||||
bool? receiveInfoboxDismissed,
|
||||
bool? showCombinedBalance,
|
||||
String? favoriteTokenAddress
|
||||
}) {
|
||||
factory WalletInfo.external(
|
||||
{required String id,
|
||||
required String name,
|
||||
required WalletType type,
|
||||
required bool isRecovery,
|
||||
required int restoreHeight,
|
||||
required DateTime date,
|
||||
required String dirPath,
|
||||
required String path,
|
||||
required String address,
|
||||
bool? showIntroCakePayCard,
|
||||
String yatEid = '',
|
||||
String yatLastUsedAddressRaw = '',
|
||||
int? derivationInfoId,
|
||||
HardwareWalletType? hardwareWalletType,
|
||||
String? parentAddress,
|
||||
String? hashedWalletIdentifier,
|
||||
bool? isNonSeedWallet,
|
||||
int? sortOrder,
|
||||
bool? receiveInfoboxDismissed,
|
||||
bool? showCombinedBalance,
|
||||
String? favoriteTokenAddress}) {
|
||||
return WalletInfo(
|
||||
0,
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
isRecovery,
|
||||
restoreHeight,
|
||||
date.millisecondsSinceEpoch,
|
||||
dirPath,
|
||||
path,
|
||||
address,
|
||||
yatEid,
|
||||
yatLastUsedAddressRaw,
|
||||
showIntroCakePayCard,
|
||||
derivationInfoId ?? -1,
|
||||
hardwareWalletType,
|
||||
parentAddress,
|
||||
hashedWalletIdentifier,
|
||||
isNonSeedWallet ?? false,
|
||||
sortOrder ?? 0,
|
||||
null,
|
||||
receiveInfoboxDismissed ?? false,
|
||||
showCombinedBalance ?? true,
|
||||
favoriteTokenAddress
|
||||
);
|
||||
0,
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
isRecovery,
|
||||
restoreHeight,
|
||||
date.millisecondsSinceEpoch,
|
||||
dirPath,
|
||||
path,
|
||||
address,
|
||||
yatEid,
|
||||
yatLastUsedAddressRaw,
|
||||
showIntroCakePayCard,
|
||||
derivationInfoId ?? -1,
|
||||
hardwareWalletType,
|
||||
parentAddress,
|
||||
hashedWalletIdentifier,
|
||||
isNonSeedWallet ?? false,
|
||||
sortOrder ?? 0,
|
||||
null,
|
||||
receiveInfoboxDismissed ?? false,
|
||||
showCombinedBalance ?? true,
|
||||
favoriteTokenAddress);
|
||||
}
|
||||
|
||||
static String get tableName => 'walletInfo';
|
||||
@@ -564,59 +562,59 @@ class WalletInfo {
|
||||
StreamController<String> _yatLastUsedAddressController;
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
selfIdColumn: internalId,
|
||||
"id": id,
|
||||
"name": name,
|
||||
"type": type.index,
|
||||
"isRecovery": isRecovery ? 1 : 0,
|
||||
"restoreHeight": restoreHeight,
|
||||
"timestamp": timestamp,
|
||||
"dirPath": dirPath,
|
||||
"path": path,
|
||||
"address": address,
|
||||
"yatEid": yatEid,
|
||||
"yatLastUsedAddressRaw": yatLastUsedAddressRaw,
|
||||
"showIntroCakePayCard": showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false
|
||||
"walletInfoDerivationInfoId": derivationInfoId,
|
||||
"hardwareWalletType": hardwareWalletType?.index,
|
||||
"parentAddress": parentAddress,
|
||||
"hashedWalletIdentifier": hashedWalletIdentifier,
|
||||
"isNonSeedWallet": isNonSeedWallet ? 1 : 0,
|
||||
"sortOrder": sortOrder,
|
||||
"addressPageType": addressPageType,
|
||||
"receiveInfoboxDismissed": receiveInfoboxDismissed ? 1 : 0,
|
||||
"showCombinedBalance": showCombinedBalance ? 1 : 0,
|
||||
"favoriteTokenAddress": favoriteTokenAddress
|
||||
};
|
||||
selfIdColumn: internalId,
|
||||
"id": id,
|
||||
"name": name,
|
||||
"type": type.index,
|
||||
"isRecovery": isRecovery ? 1 : 0,
|
||||
"restoreHeight": restoreHeight,
|
||||
"timestamp": timestamp,
|
||||
"dirPath": dirPath,
|
||||
"path": path,
|
||||
"address": address,
|
||||
"yatEid": yatEid,
|
||||
"yatLastUsedAddressRaw": yatLastUsedAddressRaw,
|
||||
"showIntroCakePayCard":
|
||||
showIntroCakePayCard == true ? 1 : 0, // SQL regression: null -> false
|
||||
"walletInfoDerivationInfoId": derivationInfoId,
|
||||
"hardwareWalletType": hardwareWalletType?.index,
|
||||
"parentAddress": parentAddress,
|
||||
"hashedWalletIdentifier": hashedWalletIdentifier,
|
||||
"isNonSeedWallet": isNonSeedWallet ? 1 : 0,
|
||||
"sortOrder": sortOrder,
|
||||
"addressPageType": addressPageType,
|
||||
"receiveInfoboxDismissed": receiveInfoboxDismissed ? 1 : 0,
|
||||
"showCombinedBalance": showCombinedBalance ? 1 : 0,
|
||||
"favoriteTokenAddress": favoriteTokenAddress
|
||||
};
|
||||
|
||||
factory WalletInfo.fromJson(Map<String, dynamic> json) {
|
||||
return WalletInfo(
|
||||
json[selfIdColumn] as int,
|
||||
json['id'] as String,
|
||||
json['name'] as String,
|
||||
WalletType.values[json['type'] as int],
|
||||
(json['isRecovery'] as int) == 1,
|
||||
json['restoreHeight'] as int,
|
||||
json['timestamp'] as int,
|
||||
json['dirPath'] as String,
|
||||
json['path'] as String,
|
||||
json['address'] as String,
|
||||
json['yatEid'] as String?,
|
||||
json['yatLastUsedAddressRaw'] as String?,
|
||||
(json['showIntroCakePayCard'] as int) == 1,
|
||||
json['walletInfoDerivationInfoId'] as int,
|
||||
json['hardwareWalletType'] == null
|
||||
? null
|
||||
: HardwareWalletType.values[json['hardwareWalletType'] as int],
|
||||
json['parentAddress'] as String?,
|
||||
json['hashedWalletIdentifier'] as String?,
|
||||
(json['isNonSeedWallet'] as int) == 1,
|
||||
json['sortOrder'] as int? ?? 0,
|
||||
json['addressPageType'] as String? ?? null,
|
||||
json['receiveInfoboxDismissed'] != 0,
|
||||
json["showCombinedBalance"] != 0,
|
||||
json["favoriteTokenAddress"] as String? ?? null
|
||||
);
|
||||
json[selfIdColumn] as int,
|
||||
json['id'] as String,
|
||||
json['name'] as String,
|
||||
WalletType.values[json['type'] as int],
|
||||
(json['isRecovery'] as int) == 1,
|
||||
json['restoreHeight'] as int,
|
||||
json['timestamp'] as int,
|
||||
json['dirPath'] as String,
|
||||
json['path'] as String,
|
||||
json['address'] as String,
|
||||
json['yatEid'] as String?,
|
||||
json['yatLastUsedAddressRaw'] as String?,
|
||||
(json['showIntroCakePayCard'] as int) == 1,
|
||||
json['walletInfoDerivationInfoId'] as int,
|
||||
json['hardwareWalletType'] == null
|
||||
? null
|
||||
: HardwareWalletType.values[json['hardwareWalletType'] as int],
|
||||
json['parentAddress'] as String?,
|
||||
json['hashedWalletIdentifier'] as String?,
|
||||
(json['isNonSeedWallet'] as int) == 1,
|
||||
json['sortOrder'] as int? ?? 0,
|
||||
json['addressPageType'] as String? ?? null,
|
||||
json['receiveInfoboxDismissed'] != 0,
|
||||
json["showCombinedBalance"] != 0,
|
||||
json["favoriteTokenAddress"] as String? ?? null);
|
||||
}
|
||||
|
||||
Future<int> save() async {
|
||||
|
||||
@@ -201,7 +201,7 @@ class WalletInfo extends HiveObject {
|
||||
|
||||
// @HiveField(22)
|
||||
String? parentAddress;
|
||||
|
||||
|
||||
// @HiveField(23)
|
||||
List<String>? hiddenAddresses;
|
||||
|
||||
|
||||
@@ -53,9 +53,7 @@ class DerivationInfoAdapter extends TypeAdapter<DerivationInfo> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DerivationInfoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is DerivationInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
|
||||
@@ -88,8 +86,8 @@ class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
|
||||
fields[26] == null ? false : fields[26] as bool,
|
||||
)
|
||||
..addresses = (fields[10] as Map?)?.cast<String, String>()
|
||||
..addressInfos = (fields[14] as Map?)?.map((dynamic k, dynamic v) =>
|
||||
MapEntry(k as int, (v as List).cast<AddressInfo>()))
|
||||
..addressInfos = (fields[14] as Map?)
|
||||
?.map((dynamic k, dynamic v) => MapEntry(k as int, (v as List).cast<AddressInfo>()))
|
||||
..usedAddresses = (fields[15] as List?)?.cast<String>()
|
||||
..derivationType = fields[16] as newWi.DerivationType?
|
||||
..derivationPath = fields[17] as String?
|
||||
@@ -163,9 +161,7 @@ class WalletInfoAdapter extends TypeAdapter<WalletInfo> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is WalletInfoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is WalletInfoAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
|
||||
@@ -217,9 +213,7 @@ class DerivationTypeAdapter extends TypeAdapter<newWi.DerivationType> {
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is DerivationTypeAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
other is DerivationTypeAdapter && runtimeType == other.runtimeType && typeId == other.typeId;
|
||||
}
|
||||
|
||||
class HardwareWalletTypeAdapter extends TypeAdapter<newWi.HardwareWalletType> {
|
||||
|
||||
@@ -101,7 +101,14 @@ class WalletKeysData {
|
||||
final String? scanSecret;
|
||||
final String? spendPubkey;
|
||||
|
||||
WalletKeysData({this.privateKey, this.mnemonic, this.altMnemonic, this.passphrase, this.xPub, this.scanSecret, this.spendPubkey});
|
||||
WalletKeysData(
|
||||
{this.privateKey,
|
||||
this.mnemonic,
|
||||
this.altMnemonic,
|
||||
this.passphrase,
|
||||
this.xPub,
|
||||
this.scanSecret,
|
||||
this.spendPubkey});
|
||||
|
||||
String toJSON() => jsonEncode({
|
||||
"privateKey": privateKey,
|
||||
|
||||
@@ -252,11 +252,11 @@ WalletType? _cryptoCurrencyToWalletType(CryptoCurrency type) {
|
||||
}
|
||||
|
||||
WalletType? cryptoCurrencyOrTokenToWalletType(CryptoCurrency type) {
|
||||
if(type.tag == CryptoCurrency.bnb.tag) {
|
||||
if (type.tag == CryptoCurrency.bnb.tag) {
|
||||
return _cryptoCurrencyToWalletType(CryptoCurrency.bnb);
|
||||
}
|
||||
|
||||
if(type.tag != null && ![CryptoCurrency.btcln.tag, CryptoCurrency.bnb.tag].contains(type.tag)) {
|
||||
if (type.tag != null && ![CryptoCurrency.btcln.tag, CryptoCurrency.bnb.tag].contains(type.tag)) {
|
||||
return _cryptoCurrencyToWalletType(CryptoCurrency.fromString(type.tag!));
|
||||
} else {
|
||||
return _cryptoCurrencyToWalletType(type);
|
||||
|
||||
@@ -15,4 +15,4 @@ double wowneroAmountToDouble({required int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: wowneroAmountDivider);
|
||||
|
||||
int wowneroParseAmount({required String amount}) =>
|
||||
(double.parse(amount) * wowneroAmountDivider).round();
|
||||
(double.parse(amount) * wowneroAmountDivider).round();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user