mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
parent
b77c22b0df
commit
d332377a2b
36 changed files with 402 additions and 667 deletions
|
@ -17,16 +17,21 @@ BitcoinBaseAddress addressFromScript(Script script,
|
||||||
|
|
||||||
switch (addressType) {
|
switch (addressType) {
|
||||||
case P2pkhAddressType.p2pkh:
|
case P2pkhAddressType.p2pkh:
|
||||||
return P2pkhAddress.fromScriptPubkey(script: script);
|
return P2pkhAddress.fromScriptPubkey(
|
||||||
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
case P2shAddressType.p2pkhInP2sh:
|
case P2shAddressType.p2pkhInP2sh:
|
||||||
case P2shAddressType.p2pkInP2sh:
|
case P2shAddressType.p2pkInP2sh:
|
||||||
return P2shAddress.fromScriptPubkey(script: script);
|
return P2shAddress.fromScriptPubkey(
|
||||||
case SegwitAddressType.p2wpkh:
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
return P2wpkhAddress.fromScriptPubkey(script: script);
|
case SegwitAddresType.p2wpkh:
|
||||||
case SegwitAddressType.p2wsh:
|
return P2wpkhAddress.fromScriptPubkey(
|
||||||
return P2wshAddress.fromScriptPubkey(script: script);
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
case SegwitAddressType.p2tr:
|
case SegwitAddresType.p2wsh:
|
||||||
return P2trAddress.fromScriptPubkey(script: script);
|
return P2wshAddress.fromScriptPubkey(
|
||||||
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
|
case SegwitAddresType.p2tr:
|
||||||
|
return P2trAddress.fromScriptPubkey(
|
||||||
|
script: script, network: BitcoinNetwork.mainnet);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw ArgumentError("Invalid script");
|
throw ArgumentError("Invalid script");
|
||||||
|
|
|
@ -82,7 +82,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
||||||
type: decoded['type'] != null && decoded['type'] != ''
|
type: decoded['type'] != null && decoded['type'] != ''
|
||||||
? BitcoinAddressType.values
|
? BitcoinAddressType.values
|
||||||
.firstWhere((type) => type.toString() == decoded['type'] as String)
|
.firstWhere((type) => type.toString() == decoded['type'] as String)
|
||||||
: SegwitAddressType.p2wpkh,
|
: SegwitAddresType.p2wpkh,
|
||||||
scriptHash: decoded['scriptHash'] as String?,
|
scriptHash: decoded['scriptHash'] as String?,
|
||||||
network: network,
|
network: network,
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,9 +36,9 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
BitcoinAddressType toType() {
|
BitcoinAddressType toType() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case BitcoinReceivePageOption.p2tr:
|
case BitcoinReceivePageOption.p2tr:
|
||||||
return SegwitAddressType.p2tr;
|
return SegwitAddresType.p2tr;
|
||||||
case BitcoinReceivePageOption.p2wsh:
|
case BitcoinReceivePageOption.p2wsh:
|
||||||
return SegwitAddressType.p2wsh;
|
return SegwitAddresType.p2wsh;
|
||||||
case BitcoinReceivePageOption.p2pkh:
|
case BitcoinReceivePageOption.p2pkh:
|
||||||
return P2pkhAddressType.p2pkh;
|
return P2pkhAddressType.p2pkh;
|
||||||
case BitcoinReceivePageOption.p2sh:
|
case BitcoinReceivePageOption.p2sh:
|
||||||
|
@ -46,20 +46,20 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
case BitcoinReceivePageOption.silent_payments:
|
case BitcoinReceivePageOption.silent_payments:
|
||||||
return SilentPaymentsAddresType.p2sp;
|
return SilentPaymentsAddresType.p2sp;
|
||||||
case BitcoinReceivePageOption.mweb:
|
case BitcoinReceivePageOption.mweb:
|
||||||
return SegwitAddressType.mweb;
|
return SegwitAddresType.mweb;
|
||||||
case BitcoinReceivePageOption.p2wpkh:
|
case BitcoinReceivePageOption.p2wpkh:
|
||||||
default:
|
default:
|
||||||
return SegwitAddressType.p2wpkh;
|
return SegwitAddresType.p2wpkh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
|
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SegwitAddressType.p2tr:
|
case SegwitAddresType.p2tr:
|
||||||
return BitcoinReceivePageOption.p2tr;
|
return BitcoinReceivePageOption.p2tr;
|
||||||
case SegwitAddressType.p2wsh:
|
case SegwitAddresType.p2wsh:
|
||||||
return BitcoinReceivePageOption.p2wsh;
|
return BitcoinReceivePageOption.p2wsh;
|
||||||
case SegwitAddressType.mweb:
|
case SegwitAddresType.mweb:
|
||||||
return BitcoinReceivePageOption.mweb;
|
return BitcoinReceivePageOption.mweb;
|
||||||
case P2pkhAddressType.p2pkh:
|
case P2pkhAddressType.p2pkh:
|
||||||
return BitcoinReceivePageOption.p2pkh;
|
return BitcoinReceivePageOption.p2pkh;
|
||||||
|
@ -67,7 +67,7 @@ class BitcoinReceivePageOption implements ReceivePageOption {
|
||||||
return BitcoinReceivePageOption.p2sh;
|
return BitcoinReceivePageOption.p2sh;
|
||||||
case SilentPaymentsAddresType.p2sp:
|
case SilentPaymentsAddresType.p2sp:
|
||||||
return BitcoinReceivePageOption.silent_payments;
|
return BitcoinReceivePageOption.silent_payments;
|
||||||
case SegwitAddressType.p2wpkh:
|
case SegwitAddresType.p2wpkh:
|
||||||
default:
|
default:
|
||||||
return BitcoinReceivePageOption.p2wpkh;
|
return BitcoinReceivePageOption.p2wpkh;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,8 +73,9 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
seedBytes: seedBytes,
|
seedBytes: seedBytes,
|
||||||
encryptionFileUtils: encryptionFileUtils,
|
encryptionFileUtils: encryptionFileUtils,
|
||||||
currency:
|
currency: networkParam == BitcoinNetwork.testnet
|
||||||
networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc,
|
? CryptoCurrency.tbtc
|
||||||
|
: CryptoCurrency.btc,
|
||||||
alwaysScan: alwaysScan,
|
alwaysScan: alwaysScan,
|
||||||
) {
|
) {
|
||||||
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
|
||||||
|
@ -93,12 +94,14 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
mainHd: hd,
|
mainHd: hd,
|
||||||
sideHd: accountHD.childKey(Bip32KeyIndex(1)),
|
sideHd: accountHD.childKey(Bip32KeyIndex(1)),
|
||||||
network: networkParam ?? network,
|
network: networkParam ?? network,
|
||||||
masterHd: seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
|
masterHd:
|
||||||
|
seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
|
||||||
isHardwareWallet: walletInfo.isHardwareWallet,
|
isHardwareWallet: walletInfo.isHardwareWallet,
|
||||||
payjoinManager: payjoinManager);
|
payjoinManager: payjoinManager);
|
||||||
|
|
||||||
autorun((_) {
|
autorun((_) {
|
||||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
this.walletAddresses.isEnabledAutoGenerateSubaddress =
|
||||||
|
this.isEnabledAutoGenerateSubaddress;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +136,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
break;
|
break;
|
||||||
case DerivationType.electrum:
|
case DerivationType.electrum:
|
||||||
default:
|
default:
|
||||||
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
seedBytes =
|
||||||
|
await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,8 +210,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
walletInfo.derivationInfo ??= DerivationInfo();
|
walletInfo.derivationInfo ??= DerivationInfo();
|
||||||
|
|
||||||
// set the default if not present:
|
// set the default if not present:
|
||||||
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
|
walletInfo.derivationInfo!.derivationPath ??=
|
||||||
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
|
snp?.derivationPath ?? electrum_path;
|
||||||
|
walletInfo.derivationInfo!.derivationType ??=
|
||||||
|
snp?.derivationType ?? DerivationType.electrum;
|
||||||
|
|
||||||
Uint8List? seedBytes = null;
|
Uint8List? seedBytes = null;
|
||||||
final mnemonic = keysData.mnemonic;
|
final mnemonic = keysData.mnemonic;
|
||||||
|
@ -216,7 +222,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
if (mnemonic != null) {
|
if (mnemonic != null) {
|
||||||
switch (walletInfo.derivationInfo!.derivationType) {
|
switch (walletInfo.derivationInfo!.derivationType) {
|
||||||
case DerivationType.electrum:
|
case DerivationType.electrum:
|
||||||
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
seedBytes =
|
||||||
|
await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
||||||
break;
|
break;
|
||||||
case DerivationType.bip39:
|
case DerivationType.bip39:
|
||||||
default:
|
default:
|
||||||
|
@ -262,7 +269,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
late final PayjoinManager payjoinManager;
|
late final PayjoinManager payjoinManager;
|
||||||
|
|
||||||
bool get isPayjoinAvailable => unspentCoinsInfo.values
|
bool get isPayjoinAvailable => unspentCoinsInfo.values
|
||||||
.where((element) => element.walletId == id && element.isSending && !element.isFrozen)
|
.where((element) =>
|
||||||
|
element.walletId == id && element.isSending && !element.isFrozen)
|
||||||
.isNotEmpty;
|
.isNotEmpty;
|
||||||
|
|
||||||
Future<PsbtV2> buildPsbt({
|
Future<PsbtV2> buildPsbt({
|
||||||
|
@ -279,8 +287,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
}) async {
|
}) async {
|
||||||
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
|
||||||
for (final utxo in utxos) {
|
for (final utxo in utxos) {
|
||||||
final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
|
final rawTx =
|
||||||
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
|
||||||
|
final publicKeyAndDerivationPath =
|
||||||
|
publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
|
||||||
|
|
||||||
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
|
||||||
utxo: utxo.utxo,
|
utxo: utxo.utxo,
|
||||||
|
@ -292,7 +302,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
|
return PSBTTransactionBuild(
|
||||||
|
inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
|
||||||
.psbt;
|
.psbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,7 +342,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||||
credentials = credentials as BitcoinTransactionCredentials;
|
credentials = credentials as BitcoinTransactionCredentials;
|
||||||
|
|
||||||
final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction;
|
final tx = (await super.createTransaction(credentials))
|
||||||
|
as PendingBitcoinTransaction;
|
||||||
|
|
||||||
final payjoinUri = credentials.payjoinUri;
|
final payjoinUri = credentials.payjoinUri;
|
||||||
if (payjoinUri == null) return tx;
|
if (payjoinUri == null) return tx;
|
||||||
|
@ -354,12 +366,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
publicKeys: tx.publicKeys!,
|
publicKeys: tx.publicKeys!,
|
||||||
masterFingerprint: Uint8List(0));
|
masterFingerprint: Uint8List(0));
|
||||||
|
|
||||||
final originalPsbt =
|
final originalPsbt = await signPsbt(
|
||||||
await signPsbt(base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
|
base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
|
||||||
|
|
||||||
tx.commitOverride = () async {
|
tx.commitOverride = () async {
|
||||||
final sender =
|
final sender = await payjoinManager.initSender(
|
||||||
await payjoinManager.initSender(payjoinUri, originalPsbt, int.parse(tx.feeRate));
|
payjoinUri, originalPsbt, int.parse(tx.feeRate));
|
||||||
payjoinManager.spawnNewSender(
|
payjoinManager.spawnNewSender(
|
||||||
sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
|
sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
|
||||||
};
|
};
|
||||||
|
@ -375,7 +387,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
Future<void> commitPsbt(String finalizedPsbt) {
|
Future<void> commitPsbt(String finalizedPsbt) {
|
||||||
final psbt = PsbtV2()..deserializeV0(base64.decode(finalizedPsbt));
|
final psbt = PsbtV2()..deserializeV0(base64.decode(finalizedPsbt));
|
||||||
|
|
||||||
final btcTx = BtcTransaction.fromRaw(BytesUtils.toHexString(psbt.extract()));
|
final btcTx =
|
||||||
|
BtcTransaction.fromRaw(BytesUtils.toHexString(psbt.extract()));
|
||||||
|
|
||||||
return PendingBitcoinTransaction(
|
return PendingBitcoinTransaction(
|
||||||
btcTx,
|
btcTx,
|
||||||
|
@ -389,11 +402,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
).commit();
|
).commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> signPsbt(String preProcessedPsbt, List<UtxoWithPrivateKey> utxos) async {
|
Future<String> signPsbt(
|
||||||
|
String preProcessedPsbt, List<UtxoWithPrivateKey> utxos) async {
|
||||||
final psbt = PsbtV2()..deserializeV0(base64Decode(preProcessedPsbt));
|
final psbt = PsbtV2()..deserializeV0(base64Decode(preProcessedPsbt));
|
||||||
|
|
||||||
await psbt.signWithUTXO(utxos, (txDigest, utxo, key, sighash) {
|
await psbt.signWithUTXO(utxos, (txDigest, utxo, key, sighash) {
|
||||||
return utxo.utxo.isP2tr
|
return utxo.utxo.isP2tr()
|
||||||
? key.signTapRoot(
|
? key.signTapRoot(
|
||||||
txDigest,
|
txDigest,
|
||||||
sighash: sighash,
|
sighash: sighash,
|
||||||
|
@ -414,15 +428,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||||
Future<String> signMessage(String message, {String? address = null}) async {
|
Future<String> signMessage(String message, {String? address = null}) async {
|
||||||
if (walletInfo.isHardwareWallet) {
|
if (walletInfo.isHardwareWallet) {
|
||||||
final addressEntry = address != null
|
final addressEntry = address != null
|
||||||
? walletAddresses.allAddresses.firstWhere((element) => element.address == address)
|
? walletAddresses.allAddresses
|
||||||
|
.firstWhere((element) => element.address == address)
|
||||||
: null;
|
: null;
|
||||||
final index = addressEntry?.index ?? 0;
|
final index = addressEntry?.index ?? 0;
|
||||||
final isChange = addressEntry?.isHidden == true ? 1 : 0;
|
final isChange = addressEntry?.isHidden == true ? 1 : 0;
|
||||||
final accountPath = walletInfo.derivationInfo?.derivationPath;
|
final accountPath = walletInfo.derivationInfo?.derivationPath;
|
||||||
final derivationPath = accountPath != null ? "$accountPath/$isChange/$index" : null;
|
final derivationPath =
|
||||||
|
accountPath != null ? "$accountPath/$isChange/$index" : null;
|
||||||
|
|
||||||
final signature = await _bitcoinLedgerApp!
|
final signature = await _bitcoinLedgerApp!.signMessage(
|
||||||
.signMessage(message: ascii.encode(message), signDerivationPath: derivationPath);
|
message: ascii.encode(message), signDerivationPath: derivationPath);
|
||||||
return base64Encode(signature);
|
return base64Encode(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,10 +47,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||||
if (addressType == P2pkhAddressType.p2pkh)
|
if (addressType == P2pkhAddressType.p2pkh)
|
||||||
return generateP2PKHAddress(hd: hd, index: index, network: network);
|
return generateP2PKHAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
if (addressType == SegwitAddressType.p2tr)
|
if (addressType == SegwitAddresType.p2tr)
|
||||||
return generateP2TRAddress(hd: hd, index: index, network: network);
|
return generateP2TRAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
if (addressType == SegwitAddressType.p2wsh)
|
if (addressType == SegwitAddresType.p2wsh)
|
||||||
return generateP2WSHAddress(hd: hd, index: index, network: network);
|
return generateP2WSHAddress(hd: hd, index: index, network: network);
|
||||||
|
|
||||||
if (addressType == P2shAddressType.p2wpkhInP2sh)
|
if (addressType == P2shAddressType.p2wpkhInP2sh)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||||
|
import 'package:cw_core/format_amount.dart';
|
||||||
import 'package:cw_core/utils/print_verbose.dart';
|
import 'package:cw_core/utils/print_verbose.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet.dart';
|
||||||
import 'package:cw_bitcoin/litecoin_wallet.dart';
|
import 'package:cw_bitcoin/litecoin_wallet.dart';
|
||||||
|
@ -17,7 +18,7 @@ import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
|
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
|
||||||
import 'package:cw_bitcoin/electrum.dart' as electrum;
|
import 'package:cw_bitcoin/electrum.dart';
|
||||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||||
import 'package:cw_bitcoin/electrum_derivations.dart';
|
import 'package:cw_bitcoin/electrum_derivations.dart';
|
||||||
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
||||||
|
@ -68,7 +69,7 @@ abstract class ElectrumWalletBase
|
||||||
Uint8List? seedBytes,
|
Uint8List? seedBytes,
|
||||||
this.passphrase,
|
this.passphrase,
|
||||||
List<BitcoinAddressRecord>? initialAddresses,
|
List<BitcoinAddressRecord>? initialAddresses,
|
||||||
electrum.ElectrumClient? electrumClient,
|
ElectrumClient? electrumClient,
|
||||||
ElectrumBalance? initialBalance,
|
ElectrumBalance? initialBalance,
|
||||||
CryptoCurrency? currency,
|
CryptoCurrency? currency,
|
||||||
this.alwaysScan,
|
this.alwaysScan,
|
||||||
|
@ -95,7 +96,7 @@ abstract class ElectrumWalletBase
|
||||||
this.isTestnet = !network.isMainnet,
|
this.isTestnet = !network.isMainnet,
|
||||||
this._mnemonic = mnemonic,
|
this._mnemonic = mnemonic,
|
||||||
super(walletInfo) {
|
super(walletInfo) {
|
||||||
this.electrumClient = electrumClient ?? electrum.ElectrumClient();
|
this.electrumClient = electrumClient ?? ElectrumClient();
|
||||||
this.walletInfo = walletInfo;
|
this.walletInfo = walletInfo;
|
||||||
transactionHistory = ElectrumTransactionHistory(
|
transactionHistory = ElectrumTransactionHistory(
|
||||||
walletInfo: walletInfo,
|
walletInfo: walletInfo,
|
||||||
|
@ -166,7 +167,7 @@ abstract class ElectrumWalletBase
|
||||||
@observable
|
@observable
|
||||||
bool isEnabledAutoGenerateSubaddress;
|
bool isEnabledAutoGenerateSubaddress;
|
||||||
|
|
||||||
late electrum.ElectrumClient electrumClient;
|
late ElectrumClient electrumClient;
|
||||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -181,7 +182,7 @@ abstract class ElectrumWalletBase
|
||||||
SyncStatus syncStatus;
|
SyncStatus syncStatus;
|
||||||
|
|
||||||
Set<String> get addressesSet => walletAddresses.allAddresses
|
Set<String> get addressesSet => walletAddresses.allAddresses
|
||||||
.where((element) => element.type != SegwitAddressType.mweb)
|
.where((element) => element.type != SegwitAddresType.mweb)
|
||||||
.map((addr) => addr.address)
|
.map((addr) => addr.address)
|
||||||
.toSet();
|
.toSet();
|
||||||
|
|
||||||
|
@ -332,14 +333,14 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
final receivePort = ReceivePort();
|
final receivePort = ReceivePort();
|
||||||
_isolate = Isolate.spawn(
|
_isolate = Isolate.spawn(
|
||||||
_handleScanSilentPayments,
|
startRefresh,
|
||||||
ScanData(
|
ScanData(
|
||||||
sendPort: receivePort.sendPort,
|
sendPort: receivePort.sendPort,
|
||||||
silentAddress: walletAddresses.silentAddress!,
|
silentAddress: walletAddresses.silentAddress!,
|
||||||
network: network,
|
network: network,
|
||||||
height: height,
|
height: height,
|
||||||
chainTip: chainTip,
|
chainTip: chainTip,
|
||||||
electrumClient: electrum.ElectrumClient(),
|
electrumClient: ElectrumClient(),
|
||||||
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
|
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
|
||||||
node: (await getNodeSupportsSilentPayments()) == true
|
node: (await getNodeSupportsSilentPayments()) == true
|
||||||
? ScanNode(node!.uri, node!.useSSL)
|
? ScanNode(node!.uri, node!.useSSL)
|
||||||
|
@ -438,6 +439,7 @@ abstract class ElectrumWalletBase
|
||||||
BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
|
BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
|
||||||
)
|
)
|
||||||
: silentAddress.B_spend,
|
: silentAddress.B_spend,
|
||||||
|
network: network,
|
||||||
);
|
);
|
||||||
|
|
||||||
final addressRecord = walletAddresses.silentAddresses
|
final addressRecord = walletAddresses.silentAddresses
|
||||||
|
@ -562,7 +564,7 @@ abstract class ElectrumWalletBase
|
||||||
node!.save();
|
node!.save();
|
||||||
return node!.supportsSilentPayments!;
|
return node!.supportsSilentPayments!;
|
||||||
}
|
}
|
||||||
} on electrum.RequestFailedTimeoutException catch (_) {
|
} on RequestFailedTimeoutException catch (_) {
|
||||||
node!.supportsSilentPayments = false;
|
node!.supportsSilentPayments = false;
|
||||||
node!.save();
|
node!.save();
|
||||||
return node!.supportsSilentPayments!;
|
return node!.supportsSilentPayments!;
|
||||||
|
@ -623,9 +625,9 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
switch (coinTypeToSpendFrom) {
|
switch (coinTypeToSpendFrom) {
|
||||||
case UnspentCoinType.mweb:
|
case UnspentCoinType.mweb:
|
||||||
return utx.bitcoinAddressRecord.type == SegwitAddressType.mweb;
|
return utx.bitcoinAddressRecord.type == SegwitAddresType.mweb;
|
||||||
case UnspentCoinType.nonMweb:
|
case UnspentCoinType.nonMweb:
|
||||||
return utx.bitcoinAddressRecord.type != SegwitAddressType.mweb;
|
return utx.bitcoinAddressRecord.type != SegwitAddresType.mweb;
|
||||||
case UnspentCoinType.any:
|
case UnspentCoinType.any:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -633,7 +635,7 @@ abstract class ElectrumWalletBase
|
||||||
final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
|
final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
|
||||||
|
|
||||||
// sort the unconfirmed coins so that mweb coins are last:
|
// sort the unconfirmed coins so that mweb coins are last:
|
||||||
availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddressType.mweb ? 1 : -1);
|
availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddresType.mweb ? 1 : -1);
|
||||||
|
|
||||||
for (int i = 0; i < availableInputs.length; i++) {
|
for (int i = 0; i < availableInputs.length; i++) {
|
||||||
final utx = availableInputs[i];
|
final utx = availableInputs[i];
|
||||||
|
@ -641,7 +643,7 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
if (paysToSilentPayment) {
|
if (paysToSilentPayment) {
|
||||||
// Check inputs for shared secret derivation
|
// Check inputs for shared secret derivation
|
||||||
if (utx.bitcoinAddressRecord.type == SegwitAddressType.p2wsh) {
|
if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) {
|
||||||
throw BitcoinTransactionSilentPaymentsNotSupported();
|
throw BitcoinTransactionSilentPaymentsNotSupported();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -676,7 +678,7 @@ abstract class ElectrumWalletBase
|
||||||
if (privkey != null) {
|
if (privkey != null) {
|
||||||
inputPrivKeyInfos.add(ECPrivateInfo(
|
inputPrivKeyInfos.add(ECPrivateInfo(
|
||||||
privkey,
|
privkey,
|
||||||
address.type == SegwitAddressType.p2tr,
|
address.type == SegwitAddresType.p2tr,
|
||||||
tweak: !isSilentPayment,
|
tweak: !isSilentPayment,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1162,7 +1164,7 @@ abstract class ElectrumWalletBase
|
||||||
throw Exception(error);
|
throw Exception(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utxo.utxo.isP2tr) {
|
if (utxo.utxo.isP2tr()) {
|
||||||
hasTaprootInputs = true;
|
hasTaprootInputs = true;
|
||||||
return key.privkey.signTapRoot(
|
return key.privkey.signTapRoot(
|
||||||
txDigest,
|
txDigest,
|
||||||
|
@ -1174,18 +1176,20 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return PendingBitcoinTransaction(transaction, type,
|
return PendingBitcoinTransaction(
|
||||||
electrumClient: electrumClient,
|
transaction,
|
||||||
amount: estimatedTx.amount,
|
type,
|
||||||
fee: estimatedTx.fee,
|
electrumClient: electrumClient,
|
||||||
feeRate: feeRateInt.toString(),
|
amount: estimatedTx.amount,
|
||||||
network: network,
|
fee: estimatedTx.fee,
|
||||||
hasChange: estimatedTx.hasChange,
|
feeRate: feeRateInt.toString(),
|
||||||
isSendAll: estimatedTx.isSendAll,
|
network: network,
|
||||||
hasTaprootInputs: hasTaprootInputs,
|
hasChange: estimatedTx.hasChange,
|
||||||
utxos: estimatedTx.utxos,
|
isSendAll: estimatedTx.isSendAll,
|
||||||
publicKeys: estimatedTx.publicKeys)
|
hasTaprootInputs: hasTaprootInputs,
|
||||||
..addListener((transaction) async {
|
utxos: estimatedTx.utxos,
|
||||||
|
publicKeys: estimatedTx.publicKeys
|
||||||
|
)..addListener((transaction) async {
|
||||||
transactionHistory.addOne(transaction);
|
transactionHistory.addOne(transaction);
|
||||||
if (estimatedTx.spendsSilentPayment) {
|
if (estimatedTx.spendsSilentPayment) {
|
||||||
transactionHistory.transactions.values.forEach((tx) {
|
transactionHistory.transactions.values.forEach((tx) {
|
||||||
|
@ -1229,7 +1233,7 @@ abstract class ElectrumWalletBase
|
||||||
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
|
||||||
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
|
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
|
||||||
'address_page_type': walletInfo.addressPageType == null
|
'address_page_type': walletInfo.addressPageType == null
|
||||||
? SegwitAddressType.p2wpkh.toString()
|
? SegwitAddresType.p2wpkh.toString()
|
||||||
: walletInfo.addressPageType.toString(),
|
: walletInfo.addressPageType.toString(),
|
||||||
'balance': balance[currency]?.toJSON(),
|
'balance': balance[currency]?.toJSON(),
|
||||||
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
|
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
|
||||||
|
@ -1369,7 +1373,7 @@ abstract class ElectrumWalletBase
|
||||||
List<BitcoinUnspent> updatedUnspentCoins = [];
|
List<BitcoinUnspent> updatedUnspentCoins = [];
|
||||||
|
|
||||||
final previousUnspentCoins = List<BitcoinUnspent>.from(unspentCoins.where((utxo) =>
|
final previousUnspentCoins = List<BitcoinUnspent>.from(unspentCoins.where((utxo) =>
|
||||||
utxo.bitcoinAddressRecord.type != SegwitAddressType.mweb &&
|
utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb &&
|
||||||
utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
|
utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
|
||||||
|
|
||||||
if (hasSilentPaymentsScanning) {
|
if (hasSilentPaymentsScanning) {
|
||||||
|
@ -1383,13 +1387,13 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
// Set the balance of all non-silent payment and non-mweb addresses to 0 before updating
|
// Set the balance of all non-silent payment and non-mweb addresses to 0 before updating
|
||||||
walletAddresses.allAddresses
|
walletAddresses.allAddresses
|
||||||
.where((element) => element.type != SegwitAddressType.mweb)
|
.where((element) => element.type != SegwitAddresType.mweb)
|
||||||
.forEach((addr) {
|
.forEach((addr) {
|
||||||
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
|
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
final addressFutures = walletAddresses.allAddresses
|
final addressFutures = walletAddresses.allAddresses
|
||||||
.where((element) => element.type != SegwitAddressType.mweb)
|
.where((element) => element.type != SegwitAddresType.mweb)
|
||||||
.map((address) => fetchUnspent(address))
|
.map((address) => fetchUnspent(address))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
@ -1830,7 +1834,7 @@ abstract class ElectrumWalletBase
|
||||||
throw Exception("Cannot find private key");
|
throw Exception("Cannot find private key");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (utxo.utxo.isP2tr) {
|
if (utxo.utxo.isP2tr()) {
|
||||||
return key.signTapRoot(txDigest, sighash: sighash);
|
return key.signTapRoot(txDigest, sighash: sighash);
|
||||||
} else {
|
} else {
|
||||||
return key.signInput(txDigest, sigHash: sighash);
|
return key.signInput(txDigest, sigHash: sighash);
|
||||||
|
@ -1980,7 +1984,7 @@ abstract class ElectrumWalletBase
|
||||||
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||||
} else if (type == WalletType.litecoin) {
|
} else if (type == WalletType.litecoin) {
|
||||||
await Future.wait(LITECOIN_ADDRESS_TYPES
|
await Future.wait(LITECOIN_ADDRESS_TYPES
|
||||||
.where((type) => type != SegwitAddressType.mweb)
|
.where((type) => type != SegwitAddresType.mweb)
|
||||||
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2169,7 +2173,7 @@ abstract class ElectrumWalletBase
|
||||||
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
|
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
|
||||||
(address) =>
|
(address) =>
|
||||||
!_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) &&
|
!_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) &&
|
||||||
address.type != SegwitAddressType.mweb,
|
address.type != SegwitAddresType.mweb,
|
||||||
);
|
);
|
||||||
|
|
||||||
await Future.wait(unsubscribedScriptHashes.map((address) async {
|
await Future.wait(unsubscribedScriptHashes.map((address) async {
|
||||||
|
@ -2392,9 +2396,9 @@ abstract class ElectrumWalletBase
|
||||||
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
|
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void _onConnectionStatusChange(electrum.ConnectionStatus status) {
|
void _onConnectionStatusChange(ConnectionStatus status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case electrum.ConnectionStatus.connected:
|
case ConnectionStatus.connected:
|
||||||
if (syncStatus is NotConnectedSyncStatus ||
|
if (syncStatus is NotConnectedSyncStatus ||
|
||||||
syncStatus is LostConnectionSyncStatus ||
|
syncStatus is LostConnectionSyncStatus ||
|
||||||
syncStatus is ConnectingSyncStatus) {
|
syncStatus is ConnectingSyncStatus) {
|
||||||
|
@ -2402,19 +2406,19 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case electrum.ConnectionStatus.disconnected:
|
case ConnectionStatus.disconnected:
|
||||||
if (syncStatus is! NotConnectedSyncStatus &&
|
if (syncStatus is! NotConnectedSyncStatus &&
|
||||||
syncStatus is! ConnectingSyncStatus &&
|
syncStatus is! ConnectingSyncStatus &&
|
||||||
syncStatus is! SyncronizingSyncStatus) {
|
syncStatus is! SyncronizingSyncStatus) {
|
||||||
syncStatus = NotConnectedSyncStatus();
|
syncStatus = NotConnectedSyncStatus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case electrum.ConnectionStatus.failed:
|
case ConnectionStatus.failed:
|
||||||
if (syncStatus is! LostConnectionSyncStatus) {
|
if (syncStatus is! LostConnectionSyncStatus) {
|
||||||
syncStatus = LostConnectionSyncStatus();
|
syncStatus = LostConnectionSyncStatus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case electrum.ConnectionStatus.connecting:
|
case ConnectionStatus.connecting:
|
||||||
if (syncStatus is! ConnectingSyncStatus) {
|
if (syncStatus is! ConnectingSyncStatus) {
|
||||||
syncStatus = ConnectingSyncStatus();
|
syncStatus = ConnectingSyncStatus();
|
||||||
}
|
}
|
||||||
|
@ -2526,7 +2530,7 @@ class ScanData {
|
||||||
final ScanNode? node;
|
final ScanNode? node;
|
||||||
final BasedUtxoNetwork network;
|
final BasedUtxoNetwork network;
|
||||||
final int chainTip;
|
final int chainTip;
|
||||||
final electrum.ElectrumClient electrumClient;
|
final ElectrumClient electrumClient;
|
||||||
final List<String> transactionHistoryIds;
|
final List<String> transactionHistoryIds;
|
||||||
final Map<String, String> labels;
|
final Map<String, String> labels;
|
||||||
final List<int> labelIndexes;
|
final List<int> labelIndexes;
|
||||||
|
@ -2570,234 +2574,6 @@ class SyncResponse {
|
||||||
SyncResponse(this.height, this.syncStatus);
|
SyncResponse(this.height, this.syncStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleScanSilentPayments(ScanData scanData) async {
|
|
||||||
try {
|
|
||||||
// if (scanData.shouldSwitchNodes) {
|
|
||||||
var scanningClient = await ElectrumProvider.connect(
|
|
||||||
ElectrumTCPService.connect(
|
|
||||||
Uri.parse("tcp://electrs.cakewallet.com:50001"),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
// }
|
|
||||||
|
|
||||||
int syncHeight = scanData.height;
|
|
||||||
int initialSyncHeight = syncHeight;
|
|
||||||
|
|
||||||
final receiver = Receiver(
|
|
||||||
scanData.silentAddress.b_scan.toHex(),
|
|
||||||
scanData.silentAddress.B_spend.toHex(),
|
|
||||||
scanData.network == BitcoinNetwork.testnet,
|
|
||||||
scanData.labelIndexes,
|
|
||||||
);
|
|
||||||
|
|
||||||
int getCountToScanPerRequest(int syncHeight) {
|
|
||||||
if (scanData.isSingleScan) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
final amountLeft = scanData.chainTip - syncHeight + 1;
|
|
||||||
return amountLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial status UI update, send how many blocks in total to scan
|
|
||||||
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
|
|
||||||
|
|
||||||
final req = ElectrumTweaksSubscribe(
|
|
||||||
height: syncHeight,
|
|
||||||
count: getCountToScanPerRequest(syncHeight),
|
|
||||||
historicalMode: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
var _scanningStream = await scanningClient.subscribe(req);
|
|
||||||
|
|
||||||
void listenFn(Map<String, dynamic> event, ElectrumTweaksSubscribe req) {
|
|
||||||
final response = req.onResponse(event);
|
|
||||||
|
|
||||||
if (response == null || _scanningStream == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// is success or error msg
|
|
||||||
final noData = response.message != null;
|
|
||||||
|
|
||||||
if (noData) {
|
|
||||||
if (scanData.isSingleScan) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
|
||||||
final nextHeight = syncHeight + 1;
|
|
||||||
|
|
||||||
if (nextHeight <= scanData.chainTip) {
|
|
||||||
final nextStream = scanningClient.subscribe(
|
|
||||||
ElectrumTweaksSubscribe(
|
|
||||||
height: nextHeight,
|
|
||||||
count: getCountToScanPerRequest(nextHeight),
|
|
||||||
historicalMode: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (nextStream != null) {
|
|
||||||
nextStream.listen((event) => listenFn(event, req));
|
|
||||||
} else {
|
|
||||||
scanData.sendPort.send(
|
|
||||||
SyncResponse(scanData.height, LostConnectionSyncStatus()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final tweakHeight = response.block;
|
|
||||||
|
|
||||||
if (initialSyncHeight < tweakHeight) initialSyncHeight = tweakHeight;
|
|
||||||
|
|
||||||
// Continuous status UI update, send how many blocks left to scan
|
|
||||||
final syncingStatus = scanData.isSingleScan
|
|
||||||
? SyncingSyncStatus(1, 0)
|
|
||||||
: SyncingSyncStatus.fromHeightValues(scanData.chainTip, initialSyncHeight, tweakHeight);
|
|
||||||
|
|
||||||
scanData.sendPort.send(SyncResponse(syncHeight, syncingStatus));
|
|
||||||
|
|
||||||
try {
|
|
||||||
final blockTweaks = response.blockTweaks;
|
|
||||||
|
|
||||||
for (final txid in blockTweaks.keys) {
|
|
||||||
final tweakData = blockTweaks[txid];
|
|
||||||
final outputPubkeys = tweakData!.outputPubkeys;
|
|
||||||
final tweak = tweakData.tweak;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final addToWallet = {};
|
|
||||||
|
|
||||||
// receivers.forEach((receiver) {
|
|
||||||
// NOTE: scanOutputs, from sp_scanner package, called from rust here
|
|
||||||
final scanResult = scanOutputs([outputPubkeys.keys.toList()], tweak, receiver);
|
|
||||||
|
|
||||||
if (scanResult.isEmpty) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addToWallet[receiver.BSpend] == null) {
|
|
||||||
addToWallet[receiver.BSpend] = scanResult;
|
|
||||||
} else {
|
|
||||||
addToWallet[receiver.BSpend].addAll(scanResult);
|
|
||||||
}
|
|
||||||
// });
|
|
||||||
|
|
||||||
if (addToWallet.isEmpty) {
|
|
||||||
// no results tx, continue to next tx
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// initial placeholder ElectrumTransactionInfo object to update values based on new scanned unspent(s) on the following loop
|
|
||||||
final txInfo = ElectrumTransactionInfo(
|
|
||||||
WalletType.bitcoin,
|
|
||||||
id: txid,
|
|
||||||
height: tweakHeight,
|
|
||||||
amount: 0,
|
|
||||||
fee: 0,
|
|
||||||
direction: TransactionDirection.incoming,
|
|
||||||
isReplaced: false,
|
|
||||||
date: DateTime.fromMillisecondsSinceEpoch(
|
|
||||||
DateTime.now().millisecondsSinceEpoch * 1000,
|
|
||||||
),
|
|
||||||
confirmations: scanData.chainTip - tweakHeight + 1,
|
|
||||||
isReceivedSilentPayment: true,
|
|
||||||
isPending: false,
|
|
||||||
unspents: [],
|
|
||||||
);
|
|
||||||
|
|
||||||
List<BitcoinUnspent> unspents = [];
|
|
||||||
|
|
||||||
addToWallet.forEach((BSpend, scanResultPerLabel) {
|
|
||||||
scanResultPerLabel.forEach((label, scanOutput) {
|
|
||||||
final labelValue = label == "None" ? null : label.toString();
|
|
||||||
|
|
||||||
(scanOutput as Map<String, dynamic>).forEach((outputPubkey, tweak) {
|
|
||||||
final t_k = tweak as String;
|
|
||||||
|
|
||||||
final receivingOutputAddress = ECPublic.fromHex(outputPubkey)
|
|
||||||
.toTaprootAddress(tweak: false)
|
|
||||||
.toAddress(scanData.network);
|
|
||||||
|
|
||||||
final matchingOutput = outputPubkeys[outputPubkey]!;
|
|
||||||
final amount = matchingOutput.amount;
|
|
||||||
final pos = matchingOutput.vout;
|
|
||||||
|
|
||||||
// final matchingSPWallet = scanData.silentPaymentsWallets.firstWhere(
|
|
||||||
// (receiver) => receiver.B_spend.toHex() == BSpend.toString(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// final labelIndex = labelValue != null ? scanData.labels[label] : 0;
|
|
||||||
// final balance = ElectrumBalance();
|
|
||||||
// balance.confirmed = amount;
|
|
||||||
|
|
||||||
final receivedAddressRecord = BitcoinSilentPaymentAddressRecord(
|
|
||||||
receivingOutputAddress,
|
|
||||||
index: 0,
|
|
||||||
isHidden: false,
|
|
||||||
isUsed: true,
|
|
||||||
network: scanData.network,
|
|
||||||
silentPaymentTweak: t_k,
|
|
||||||
type: SegwitAddressType.p2tr,
|
|
||||||
txCount: 1,
|
|
||||||
balance: amount,
|
|
||||||
);
|
|
||||||
|
|
||||||
final unspent = BitcoinSilentPaymentsUnspent(
|
|
||||||
receivedAddressRecord,
|
|
||||||
txid,
|
|
||||||
amount,
|
|
||||||
pos,
|
|
||||||
silentPaymentTweak: t_k,
|
|
||||||
silentPaymentLabel: labelValue,
|
|
||||||
);
|
|
||||||
|
|
||||||
unspents.add(unspent);
|
|
||||||
txInfo.unspents!.add(unspent);
|
|
||||||
txInfo.amount += unspent.value;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
scanData.sendPort.send({txInfo.id: txInfo});
|
|
||||||
} catch (e, stacktrace) {
|
|
||||||
printV(stacktrace);
|
|
||||||
printV(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e, stacktrace) {
|
|
||||||
printV(stacktrace);
|
|
||||||
printV(e.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
syncHeight = tweakHeight;
|
|
||||||
|
|
||||||
if ((tweakHeight >= scanData.chainTip) || scanData.isSingleScan) {
|
|
||||||
if (tweakHeight >= scanData.chainTip)
|
|
||||||
scanData.sendPort.send(
|
|
||||||
SyncResponse(syncHeight, SyncedTipSyncStatus(scanData.chainTip)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (scanData.isSingleScan) {
|
|
||||||
scanData.sendPort.send(SyncResponse(syncHeight, SyncedSyncStatus()));
|
|
||||||
}
|
|
||||||
|
|
||||||
_scanningStream?.close();
|
|
||||||
_scanningStream = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_scanningStream?.listen((event) => listenFn(event, req));
|
|
||||||
} catch (e) {
|
|
||||||
printV("Error in _handleScanSilentPayments: $e");
|
|
||||||
scanData.sendPort.send(SyncResponse(scanData.height, LostConnectionSyncStatus()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> startRefresh(ScanData scanData) async {
|
Future<void> startRefresh(ScanData scanData) async {
|
||||||
int syncHeight = scanData.height;
|
int syncHeight = scanData.height;
|
||||||
int initialSyncHeight = syncHeight;
|
int initialSyncHeight = syncHeight;
|
||||||
|
@ -2810,7 +2586,7 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
useSSL: scanData.node?.useSSL ?? false,
|
useSSL: scanData.node?.useSSL ?? false,
|
||||||
);
|
);
|
||||||
|
|
||||||
int getCountToScanPerRequest(int syncHeight) {
|
int getCountPerRequest(int syncHeight) {
|
||||||
if (scanData.isSingleScan) {
|
if (scanData.isSingleScan) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -2825,10 +2601,11 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
scanData.silentAddress.B_spend.toHex(),
|
scanData.silentAddress.B_spend.toHex(),
|
||||||
scanData.network == BitcoinNetwork.testnet,
|
scanData.network == BitcoinNetwork.testnet,
|
||||||
scanData.labelIndexes,
|
scanData.labelIndexes,
|
||||||
|
scanData.labelIndexes.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initial status UI update, send how many blocks in total to scan
|
// Initial status UI update, send how many blocks in total to scan
|
||||||
final initialCount = getCountToScanPerRequest(syncHeight);
|
final initialCount = getCountPerRequest(syncHeight);
|
||||||
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
|
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
|
||||||
|
|
||||||
tweaksSubscription = await electrumClient.tweaksSubscribe(
|
tweaksSubscription = await electrumClient.tweaksSubscribe(
|
||||||
|
@ -2839,24 +2616,22 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
Future<void> listenFn(t) async {
|
Future<void> listenFn(t) async {
|
||||||
final tweaks = t as Map<String, dynamic>;
|
final tweaks = t as Map<String, dynamic>;
|
||||||
final msg = tweaks["message"];
|
final msg = tweaks["message"];
|
||||||
|
// success or error msg
|
||||||
// is success or error msg
|
|
||||||
final noData = msg != null;
|
final noData = msg != null;
|
||||||
|
|
||||||
if (noData) {
|
if (noData) {
|
||||||
if (scanData.isSingleScan) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
// re-subscribe to continue receiving messages, starting from the next unscanned height
|
||||||
final nextHeight = syncHeight + 1;
|
final nextHeight = syncHeight + 1;
|
||||||
|
final nextCount = getCountPerRequest(nextHeight);
|
||||||
|
|
||||||
if (nextHeight <= scanData.chainTip) {
|
if (nextCount > 0) {
|
||||||
final nextStream = electrumClient.tweaksSubscribe(
|
tweaksSubscription?.close();
|
||||||
|
|
||||||
|
final nextTweaksSubscription = electrumClient.tweaksSubscribe(
|
||||||
height: nextHeight,
|
height: nextHeight,
|
||||||
count: getCountToScanPerRequest(nextHeight),
|
count: nextCount,
|
||||||
);
|
);
|
||||||
nextStream?.listen(listenFn);
|
nextTweaksSubscription?.listen(listenFn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -2938,7 +2713,7 @@ Future<void> startRefresh(ScanData scanData) async {
|
||||||
isUsed: true,
|
isUsed: true,
|
||||||
network: scanData.network,
|
network: scanData.network,
|
||||||
silentPaymentTweak: t_k,
|
silentPaymentTweak: t_k,
|
||||||
type: SegwitAddressType.p2tr,
|
type: SegwitAddresType.p2tr,
|
||||||
txCount: 1,
|
txCount: 1,
|
||||||
balance: amount!,
|
balance: amount!,
|
||||||
);
|
);
|
||||||
|
@ -3031,15 +2806,15 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
|
||||||
} else if (type is P2shAddress) {
|
} else if (type is P2shAddress) {
|
||||||
return P2shAddressType.p2wpkhInP2sh;
|
return P2shAddressType.p2wpkhInP2sh;
|
||||||
} else if (type is P2wshAddress) {
|
} else if (type is P2wshAddress) {
|
||||||
return SegwitAddressType.p2wsh;
|
return SegwitAddresType.p2wsh;
|
||||||
} else if (type is P2trAddress) {
|
} else if (type is P2trAddress) {
|
||||||
return SegwitAddressType.p2tr;
|
return SegwitAddresType.p2tr;
|
||||||
} else if (type is MwebAddress) {
|
} else if (type is MwebAddress) {
|
||||||
return SegwitAddressType.mweb;
|
return SegwitAddresType.mweb;
|
||||||
} else if (type is SilentPaymentsAddresType) {
|
} else if (type is SilentPaymentsAddresType) {
|
||||||
return SilentPaymentsAddresType.p2sp;
|
return SilentPaymentsAddresType.p2sp;
|
||||||
} else {
|
} else {
|
||||||
return SegwitAddressType.p2wpkh;
|
return SegwitAddresType.p2wpkh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,16 @@ part 'electrum_wallet_addresses.g.dart';
|
||||||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
||||||
|
|
||||||
const List<BitcoinAddressType> BITCOIN_ADDRESS_TYPES = [
|
const List<BitcoinAddressType> BITCOIN_ADDRESS_TYPES = [
|
||||||
SegwitAddressType.p2wpkh,
|
SegwitAddresType.p2wpkh,
|
||||||
P2pkhAddressType.p2pkh,
|
P2pkhAddressType.p2pkh,
|
||||||
SegwitAddressType.p2tr,
|
SegwitAddresType.p2tr,
|
||||||
SegwitAddressType.p2wsh,
|
SegwitAddresType.p2wsh,
|
||||||
P2shAddressType.p2wpkhInP2sh,
|
P2shAddressType.p2wpkhInP2sh,
|
||||||
];
|
];
|
||||||
|
|
||||||
const List<BitcoinAddressType> LITECOIN_ADDRESS_TYPES = [
|
const List<BitcoinAddressType> LITECOIN_ADDRESS_TYPES = [
|
||||||
SegwitAddressType.p2wpkh,
|
SegwitAddresType.p2wpkh,
|
||||||
SegwitAddressType.mweb,
|
SegwitAddresType.mweb,
|
||||||
];
|
];
|
||||||
|
|
||||||
const List<BitcoinAddressType> BITCOIN_CASH_ADDRESS_TYPES = [
|
const List<BitcoinAddressType> BITCOIN_CASH_ADDRESS_TYPES = [
|
||||||
|
@ -62,7 +62,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
_addressPageType = initialAddressPageType ??
|
_addressPageType = initialAddressPageType ??
|
||||||
(walletInfo.addressPageType != null
|
(walletInfo.addressPageType != null
|
||||||
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
||||||
: SegwitAddressType.p2wpkh),
|
: SegwitAddresType.p2wpkh),
|
||||||
silentAddresses = ObservableList<BitcoinSilentPaymentAddressRecord>.of(
|
silentAddresses = ObservableList<BitcoinSilentPaymentAddressRecord>.of(
|
||||||
(initialSilentAddresses ?? []).toSet()),
|
(initialSilentAddresses ?? []).toSet()),
|
||||||
currentSilentAddressIndex = initialSilentAddressIndex,
|
currentSilentAddressIndex = initialSilentAddressIndex,
|
||||||
|
@ -71,12 +71,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
super(walletInfo) {
|
super(walletInfo) {
|
||||||
if (masterHd != null) {
|
if (masterHd != null) {
|
||||||
silentAddress = SilentPaymentOwner.fromPrivateKeys(
|
silentAddress = SilentPaymentOwner.fromPrivateKeys(
|
||||||
b_scan: ECPrivate.fromHex(
|
b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privateKey.toHex()),
|
||||||
masterHd.derivePath("m/352'/1'/0'/1'/0").privateKey.toHex(),
|
b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privateKey.toHex()),
|
||||||
),
|
network: network,
|
||||||
b_spend: ECPrivate.fromHex(
|
|
||||||
masterHd.derivePath("m/352'/1'/0'/0'/0").privateKey.toHex(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (silentAddresses.length == 0) {
|
if (silentAddresses.length == 0) {
|
||||||
|
@ -147,13 +144,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
return silentAddress.toString();
|
return silentAddress.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
final typeMatchingAddresses =
|
final typeMatchingAddresses = _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
|
||||||
_addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
|
final typeMatchingReceiveAddresses = typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
|
||||||
final typeMatchingReceiveAddresses =
|
|
||||||
typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
|
|
||||||
|
|
||||||
if (!isEnabledAutoGenerateSubaddress) {
|
if (!isEnabledAutoGenerateSubaddress) {
|
||||||
if (previousAddressRecord != null && previousAddressRecord!.type == addressPageType) {
|
if (previousAddressRecord != null &&
|
||||||
|
previousAddressRecord!.type == addressPageType) {
|
||||||
return previousAddressRecord!.address;
|
return previousAddressRecord!.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,17 +249,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
if (walletInfo.type == WalletType.bitcoinCash) {
|
if (walletInfo.type == WalletType.bitcoinCash) {
|
||||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||||
} else if (walletInfo.type == WalletType.litecoin) {
|
} else if (walletInfo.type == WalletType.litecoin) {
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.p2wpkh);
|
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||||
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.mweb);
|
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||||
}
|
}
|
||||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||||
await _generateInitialAddresses();
|
await _generateInitialAddresses();
|
||||||
if (!isHardwareWallet) {
|
if (!isHardwareWallet) {
|
||||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.p2tr);
|
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||||
await _generateInitialAddresses(type: SegwitAddressType.p2wsh);
|
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +323,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
|
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
|
||||||
if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
|
if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
|
||||||
final currentSilentAddressIndex = silentAddresses
|
final currentSilentAddressIndex = silentAddresses
|
||||||
.where((addressRecord) => addressRecord.type != SegwitAddressType.p2tr)
|
.where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr)
|
||||||
.length -
|
.length -
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
@ -385,7 +381,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
void addBitcoinAddressTypes() {
|
void addBitcoinAddressTypes() {
|
||||||
final lastP2wpkh = _addresses
|
final lastP2wpkh = _addresses
|
||||||
.where((addressRecord) =>
|
.where((addressRecord) =>
|
||||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
|
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||||
.toList()
|
.toList()
|
||||||
.last;
|
.last;
|
||||||
if (lastP2wpkh.address != address) {
|
if (lastP2wpkh.address != address) {
|
||||||
|
@ -411,7 +407,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
final lastP2tr = _addresses.firstWhere(
|
final lastP2tr = _addresses.firstWhere(
|
||||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2tr));
|
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr));
|
||||||
if (lastP2tr.address != address) {
|
if (lastP2tr.address != address) {
|
||||||
addressesMap[lastP2tr.address] = 'P2TR';
|
addressesMap[lastP2tr.address] = 'P2TR';
|
||||||
} else {
|
} else {
|
||||||
|
@ -419,7 +415,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
final lastP2wsh = _addresses.firstWhere(
|
final lastP2wsh = _addresses.firstWhere(
|
||||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wsh));
|
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh));
|
||||||
if (lastP2wsh.address != address) {
|
if (lastP2wsh.address != address) {
|
||||||
addressesMap[lastP2wsh.address] = 'P2WSH';
|
addressesMap[lastP2wsh.address] = 'P2WSH';
|
||||||
} else {
|
} else {
|
||||||
|
@ -444,7 +440,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
void addLitecoinAddressTypes() {
|
void addLitecoinAddressTypes() {
|
||||||
final lastP2wpkh = _addresses
|
final lastP2wpkh = _addresses
|
||||||
.where((addressRecord) =>
|
.where((addressRecord) =>
|
||||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
|
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||||
.toList()
|
.toList()
|
||||||
.last;
|
.last;
|
||||||
if (lastP2wpkh.address != address) {
|
if (lastP2wpkh.address != address) {
|
||||||
|
@ -454,7 +450,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
final lastMweb = _addresses.firstWhere(
|
final lastMweb = _addresses.firstWhere(
|
||||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.mweb));
|
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb));
|
||||||
if (lastMweb.address != address) {
|
if (lastMweb.address != address) {
|
||||||
addressesMap[lastMweb.address] = 'MWEB';
|
addressesMap[lastMweb.address] = 'MWEB';
|
||||||
} else {
|
} else {
|
||||||
|
@ -564,14 +560,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
addressRecord.isHidden &&
|
addressRecord.isHidden &&
|
||||||
!addressRecord.isUsed &&
|
!addressRecord.isUsed &&
|
||||||
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
|
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
|
||||||
(walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddressType.p2wpkh));
|
(walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh));
|
||||||
changeAddresses.addAll(newAddresses);
|
changeAddresses.addAll(newAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
|
Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
|
||||||
Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
|
Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
|
||||||
{BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
|
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||||
final newAddresses = await _createNewAddresses(gap,
|
final newAddresses = await _createNewAddresses(gap,
|
||||||
startIndex: addressList.length, isHidden: isHidden, type: type);
|
startIndex: addressList.length, isHidden: isHidden, type: type);
|
||||||
addAddresses(newAddresses);
|
addAddresses(newAddresses);
|
||||||
|
@ -585,7 +581,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _generateInitialAddresses(
|
Future<void> _generateInitialAddresses(
|
||||||
{BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
|
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||||
var countOfReceiveAddresses = 0;
|
var countOfReceiveAddresses = 0;
|
||||||
var countOfHiddenAddresses = 0;
|
var countOfHiddenAddresses = 0;
|
||||||
|
|
||||||
|
@ -662,7 +658,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
|
|
||||||
void _validateAddresses() {
|
void _validateAddresses() {
|
||||||
_addresses.forEach((element) async {
|
_addresses.forEach((element) async {
|
||||||
if (element.type == SegwitAddressType.mweb) {
|
if (element.type == SegwitAddresType.mweb) {
|
||||||
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
|
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,8 +87,8 @@ class ElectrumWalletSnapshot {
|
||||||
|
|
||||||
final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
|
final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
|
||||||
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
||||||
var regularAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
|
var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||||
var changeAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
|
var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||||
var silentAddressIndex = 0;
|
var silentAddressIndex = 0;
|
||||||
|
|
||||||
final derivationType = DerivationType
|
final derivationType = DerivationType
|
||||||
|
@ -97,10 +97,10 @@ class ElectrumWalletSnapshot {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
regularAddressIndexByType = {
|
regularAddressIndexByType = {
|
||||||
SegwitAddressType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
|
SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
|
||||||
};
|
};
|
||||||
changeAddressIndexByType = {
|
changeAddressIndexByType = {
|
||||||
SegwitAddressType.p2wpkh.toString():
|
SegwitAddresType.p2wpkh.toString():
|
||||||
int.parse(data['change_address_index'] as String? ?? '0')
|
int.parse(data['change_address_index'] as String? ?? '0')
|
||||||
};
|
};
|
||||||
silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');
|
silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'package:fixnum/fixnum.dart';
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:bip39/bip39.dart' as bip39;
|
||||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||||
|
import 'package:blockchain_utils/signer/ecdsa_signing_key.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||||
|
@ -970,9 +971,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
List<ECPrivateInfo>? inputPrivKeyInfos,
|
List<ECPrivateInfo>? inputPrivKeyInfos,
|
||||||
List<Outpoint>? vinOutpoints,
|
List<Outpoint>? vinOutpoints,
|
||||||
}) async {
|
}) async {
|
||||||
bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddressType.mweb);
|
bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb);
|
||||||
bool paysToMweb = outputs
|
bool paysToMweb = outputs
|
||||||
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddressType.mweb);
|
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb);
|
||||||
|
|
||||||
bool isRegular = !spendsMweb && !paysToMweb;
|
bool isRegular = !spendsMweb && !paysToMweb;
|
||||||
bool isMweb = spendsMweb || paysToMweb;
|
bool isMweb = spendsMweb || paysToMweb;
|
||||||
|
@ -1063,9 +1064,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
tx.isMweb = mwebEnabled;
|
tx.isMweb = mwebEnabled;
|
||||||
|
|
||||||
if (!mwebEnabled) {
|
if (!mwebEnabled) {
|
||||||
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
|
tx.changeAddressOverride =
|
||||||
.getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
|
(await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
|
||||||
.address;
|
.address;
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
await waitForMwebAddresses();
|
await waitForMwebAddresses();
|
||||||
|
@ -1107,7 +1108,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
|
|
||||||
// check if mweb inputs are used:
|
// check if mweb inputs are used:
|
||||||
for (final utxo in tx.utxos) {
|
for (final utxo in tx.utxos) {
|
||||||
if (utxo.utxo.scriptType == SegwitAddressType.mweb) {
|
if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
|
||||||
hasMwebInput = true;
|
hasMwebInput = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1118,9 +1119,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||||
bool isRegular = !hasMwebInput && !hasMwebOutput;
|
bool isRegular = !hasMwebInput && !hasMwebOutput;
|
||||||
bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
|
bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
|
||||||
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
|
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
|
||||||
.getChangeAddress(
|
.getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
|
||||||
coinTypeToSpendFrom:
|
|
||||||
shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
|
|
||||||
.address;
|
.address;
|
||||||
if (isRegular) {
|
if (isRegular) {
|
||||||
tx.isMweb = false;
|
tx.isMweb = false;
|
||||||
|
|
|
@ -106,7 +106,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
.map((e) => BitcoinAddressRecord(
|
.map((e) => BitcoinAddressRecord(
|
||||||
e.value,
|
e.value,
|
||||||
index: e.key,
|
index: e.key,
|
||||||
type: SegwitAddressType.mweb,
|
type: SegwitAddresType.mweb,
|
||||||
network: network,
|
network: network,
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -128,7 +128,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
required Bip32Slip10Secp256k1 hd,
|
required Bip32Slip10Secp256k1 hd,
|
||||||
BitcoinAddressType? addressType,
|
BitcoinAddressType? addressType,
|
||||||
}) {
|
}) {
|
||||||
if (addressType == SegwitAddressType.mweb) {
|
if (addressType == SegwitAddresType.mweb) {
|
||||||
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
|
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
|
||||||
}
|
}
|
||||||
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
return generateP2WPKHAddress(hd: hd, index: index, network: network);
|
||||||
|
@ -140,7 +140,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
required Bip32Slip10Secp256k1 hd,
|
required Bip32Slip10Secp256k1 hd,
|
||||||
BitcoinAddressType? addressType,
|
BitcoinAddressType? addressType,
|
||||||
}) async {
|
}) async {
|
||||||
if (addressType == SegwitAddressType.mweb) {
|
if (addressType == SegwitAddresType.mweb) {
|
||||||
await ensureMwebAddressUpToIndexExists(index);
|
await ensureMwebAddressUpToIndexExists(index);
|
||||||
}
|
}
|
||||||
return getAddress(index: index, hd: hd, addressType: addressType);
|
return getAddress(index: index, hd: hd, addressType: addressType);
|
||||||
|
@ -195,7 +195,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
return BitcoinAddressRecord(
|
return BitcoinAddressRecord(
|
||||||
mwebAddrs[0],
|
mwebAddrs[0],
|
||||||
index: 0,
|
index: 0,
|
||||||
type: SegwitAddressType.mweb,
|
type: SegwitAddresType.mweb,
|
||||||
network: network,
|
network: network,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||||
String get addressForExchange {
|
String get addressForExchange {
|
||||||
// don't use mweb addresses for exchange refund address:
|
// don't use mweb addresses for exchange refund address:
|
||||||
final addresses = receiveAddresses
|
final addresses = receiveAddresses
|
||||||
.where((element) => element.type == SegwitAddressType.p2wpkh && !element.isUsed);
|
.where((element) => element.type == SegwitAddresType.p2wpkh && !element.isUsed);
|
||||||
return addresses.first.address;
|
return addresses.first.address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,8 @@ class PayjoinManager {
|
||||||
'https://ohttp.cakewallet.com',
|
'https://ohttp.cakewallet.com',
|
||||||
];
|
];
|
||||||
|
|
||||||
static Future<PayjoinUri.Url> randomOhttpRelayUrl() =>
|
static Future<PayjoinUri.Url> randomOhttpRelayUrl() => PayjoinUri.Url.fromStr(
|
||||||
PayjoinUri.Url.fromStr(ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
|
ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
|
||||||
|
|
||||||
static const payjoinDirectoryUrl = 'https://payjo.in';
|
static const payjoinDirectoryUrl = 'https://payjo.in';
|
||||||
|
|
||||||
|
@ -59,7 +59,8 @@ class PayjoinManager {
|
||||||
Future<Sender> initSender(
|
Future<Sender> initSender(
|
||||||
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
|
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
|
||||||
try {
|
try {
|
||||||
final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
final pjUri =
|
||||||
|
(await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
|
||||||
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
|
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
|
||||||
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
|
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
|
||||||
psbtBase64: originalPsbt,
|
psbtBase64: originalPsbt,
|
||||||
|
@ -78,7 +79,8 @@ class PayjoinManager {
|
||||||
bool isTestnet = false,
|
bool isTestnet = false,
|
||||||
}) async {
|
}) async {
|
||||||
final pjUri = Uri.parse(pjUrl).queryParameters['pj']!;
|
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);
|
return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri);
|
||||||
}
|
}
|
||||||
|
@ -138,9 +140,11 @@ class PayjoinManager {
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Receiver> initReceiver(String address, [bool isTestnet = false]) async {
|
Future<Receiver> initReceiver(String address,
|
||||||
|
[bool isTestnet = false]) async {
|
||||||
try {
|
try {
|
||||||
final payjoinDirectory = await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
|
final payjoinDirectory =
|
||||||
|
await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
|
||||||
|
|
||||||
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
|
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
|
||||||
ohttpRelay: await randomOhttpRelayUrl(),
|
ohttpRelay: await randomOhttpRelayUrl(),
|
||||||
|
@ -195,7 +199,8 @@ class PayjoinManager {
|
||||||
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
|
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
|
||||||
|
|
||||||
final inputScript = message['input_script'] as Uint8List;
|
final inputScript = message['input_script'] as Uint8List;
|
||||||
final isOwned = _wallet.isMine(Script.fromRaw(bytes: inputScript));
|
final isOwned =
|
||||||
|
_wallet.isMine(Script.fromRaw(byteData: inputScript));
|
||||||
mainToIsolateSendPort?.send({
|
mainToIsolateSendPort?.send({
|
||||||
'requestId': message['requestId'],
|
'requestId': message['requestId'],
|
||||||
'result': isOwned,
|
'result': isOwned,
|
||||||
|
@ -204,7 +209,8 @@ class PayjoinManager {
|
||||||
|
|
||||||
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
|
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
|
||||||
final outputScript = message['output_script'] as Uint8List;
|
final outputScript = message['output_script'] as Uint8List;
|
||||||
final isReceiverOutput = _wallet.isMine(Script.fromRaw(bytes: outputScript));
|
final isReceiverOutput =
|
||||||
|
_wallet.isMine(Script.fromRaw(byteData: outputScript));
|
||||||
mainToIsolateSendPort?.send({
|
mainToIsolateSendPort?.send({
|
||||||
'requestId': message['requestId'],
|
'requestId': message['requestId'],
|
||||||
'result': isReceiverOutput,
|
'result': isReceiverOutput,
|
||||||
|
@ -237,13 +243,15 @@ class PayjoinManager {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_cleanupSession(receiver.id());
|
_cleanupSession(receiver.id());
|
||||||
await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), e.toString());
|
await _payjoinStorage.markReceiverSessionUnrecoverable(
|
||||||
|
receiver.id(), e.toString());
|
||||||
completer.completeError(e);
|
completer.completeError(e);
|
||||||
}
|
}
|
||||||
} else if (message is PayjoinSessionError) {
|
} else if (message is PayjoinSessionError) {
|
||||||
_cleanupSession(receiver.id());
|
_cleanupSession(receiver.id());
|
||||||
if (message is UnrecoverableError) {
|
if (message is UnrecoverableError) {
|
||||||
await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), message.message);
|
await _payjoinStorage.markReceiverSessionUnrecoverable(
|
||||||
|
receiver.id(), message.message);
|
||||||
completer.complete();
|
completer.complete();
|
||||||
} else if (message is RecoverableError) {
|
} else if (message is RecoverableError) {
|
||||||
completer.complete();
|
completer.complete();
|
||||||
|
|
|
@ -40,7 +40,8 @@ extension PsbtSigner on PsbtV2 {
|
||||||
return tx.buffer();
|
return tx.buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> signWithUTXO(List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
Future<void> signWithUTXO(
|
||||||
|
List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
|
||||||
[UTXOGetterCallBack? getTaprootPair]) async {
|
[UTXOGetterCallBack? getTaprootPair]) async {
|
||||||
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
|
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
|
||||||
final tx = BtcTransaction.fromRaw(raw);
|
final tx = BtcTransaction.fromRaw(raw);
|
||||||
|
@ -50,10 +51,10 @@ extension PsbtSigner on PsbtV2 {
|
||||||
List<BigInt> taprootAmounts = [];
|
List<BigInt> taprootAmounts = [];
|
||||||
List<Script> taprootScripts = [];
|
List<Script> taprootScripts = [];
|
||||||
|
|
||||||
if (utxos.any((e) => e.utxo.isP2tr)) {
|
if (utxos.any((e) => e.utxo.isP2tr())) {
|
||||||
for (final input in tx.inputs) {
|
for (final input in tx.inputs) {
|
||||||
final utxo = utxos
|
final utxo = utxos.firstWhereOrNull(
|
||||||
.firstWhereOrNull((u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
(u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
|
||||||
|
|
||||||
if (utxo == null) {
|
if (utxo == null) {
|
||||||
final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
|
final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
|
||||||
|
@ -75,29 +76,37 @@ extension PsbtSigner on PsbtV2 {
|
||||||
/// We receive the owner's ScriptPubKey
|
/// We receive the owner's ScriptPubKey
|
||||||
final script = _findLockingScript(utxo, false);
|
final script = _findLockingScript(utxo, false);
|
||||||
|
|
||||||
final int sighash =
|
final int sighash = utxo.utxo.isP2tr()
|
||||||
utxo.utxo.isP2tr ? BitcoinOpCodeConst.sighashDefault : BitcoinOpCodeConst.sighashAll;
|
? BitcoinOpCodeConst.TAPROOT_SIGHASH_ALL
|
||||||
|
: BitcoinOpCodeConst.SIGHASH_ALL;
|
||||||
|
|
||||||
/// We generate transaction digest for current input
|
/// We generate transaction digest for current input
|
||||||
final digest =
|
final digest = _generateTransactionDigest(
|
||||||
_generateTransactionDigest(script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
|
||||||
|
|
||||||
/// now we need sign the transaction digest
|
/// now we need sign the transaction digest
|
||||||
final sig = signer(digest, utxo, utxo.privateKey, sighash);
|
final sig = signer(digest, utxo, utxo.privateKey, sighash);
|
||||||
|
|
||||||
if (utxo.utxo.isP2tr) {
|
if (utxo.utxo.isP2tr()) {
|
||||||
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||||
} else {
|
} else {
|
||||||
setInputPartialSig(i, Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
setInputPartialSig(
|
||||||
|
i,
|
||||||
|
Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
|
||||||
Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
Uint8List.fromList(BytesUtils.fromHexString(sig)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<int> _generateTransactionDigest(Script scriptPubKeys, int input, BitcoinUtxo utxo,
|
List<int> _generateTransactionDigest(
|
||||||
BtcTransaction transaction, List<BigInt> taprootAmounts, List<Script> tapRootPubKeys) {
|
Script scriptPubKeys,
|
||||||
if (utxo.isSegwit) {
|
int input,
|
||||||
if (utxo.isP2tr) {
|
BitcoinUtxo utxo,
|
||||||
|
BtcTransaction transaction,
|
||||||
|
List<BigInt> taprootAmounts,
|
||||||
|
List<Script> tapRootPubKeys) {
|
||||||
|
if (utxo.isSegwit()) {
|
||||||
|
if (utxo.isP2tr()) {
|
||||||
return transaction.getTransactionTaprootDigset(
|
return transaction.getTransactionTaprootDigset(
|
||||||
txIndex: input,
|
txIndex: input,
|
||||||
scriptPubKeys: tapRootPubKeys,
|
scriptPubKeys: tapRootPubKeys,
|
||||||
|
@ -107,7 +116,8 @@ extension PsbtSigner on PsbtV2 {
|
||||||
return transaction.getTransactionSegwitDigit(
|
return transaction.getTransactionSegwitDigit(
|
||||||
txInIndex: input, script: scriptPubKeys, amount: utxo.value);
|
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) {
|
Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) {
|
||||||
|
@ -119,23 +129,23 @@ extension PsbtSigner on PsbtV2 {
|
||||||
switch (utxo.utxo.scriptType) {
|
switch (utxo.utxo.scriptType) {
|
||||||
case PubKeyAddressType.p2pk:
|
case PubKeyAddressType.p2pk:
|
||||||
return senderPub.toRedeemScript();
|
return senderPub.toRedeemScript();
|
||||||
case SegwitAddressType.p2wsh:
|
case SegwitAddresType.p2wsh:
|
||||||
if (isTaproot) {
|
if (isTaproot) {
|
||||||
return senderPub.toP2wshAddress().toScriptPubKey();
|
return senderPub.toP2wshAddress().toScriptPubKey();
|
||||||
}
|
}
|
||||||
return senderPub.toP2wshRedeemScript();
|
return senderPub.toP2wshRedeemScript();
|
||||||
case P2pkhAddressType.p2pkh:
|
case P2pkhAddressType.p2pkh:
|
||||||
return senderPub.toP2pkhAddress().toScriptPubKey();
|
return senderPub.toP2pkhAddress().toScriptPubKey();
|
||||||
case SegwitAddressType.p2wpkh:
|
case SegwitAddresType.p2wpkh:
|
||||||
if (isTaproot) {
|
if (isTaproot) {
|
||||||
return senderPub.toP2wpkhAddress().toScriptPubKey();
|
return senderPub.toP2wpkhAddress().toScriptPubKey();
|
||||||
}
|
}
|
||||||
return senderPub.toP2pkhAddress().toScriptPubKey();
|
return senderPub.toP2pkhAddress().toScriptPubKey();
|
||||||
case SegwitAddressType.p2tr:
|
case SegwitAddresType.p2tr:
|
||||||
return senderPub
|
return senderPub
|
||||||
.toTaprootAddress(tweak: utxo.utxo.isSilentPayment != true)
|
.toTaprootAddress(tweak: utxo.utxo.isSilentPayment != true)
|
||||||
.toScriptPubKey();
|
.toScriptPubKey();
|
||||||
case SegwitAddressType.mweb:
|
case SegwitAddresType.mweb:
|
||||||
return Script(script: []);
|
return Script(script: []);
|
||||||
case P2shAddressType.p2pkhInP2sh:
|
case P2shAddressType.p2pkhInP2sh:
|
||||||
if (isTaproot) {
|
if (isTaproot) {
|
||||||
|
@ -162,10 +172,11 @@ extension PsbtSigner on PsbtV2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef UTXOSignerCallBack = String Function(
|
typedef UTXOSignerCallBack = String Function(List<int> trDigest,
|
||||||
List<int> trDigest, UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
|
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 {
|
class TaprootAmountScriptPair {
|
||||||
final BigInt value;
|
final BigInt value;
|
||||||
|
@ -205,17 +216,23 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||||
}
|
}
|
||||||
|
|
||||||
return UtxoWithPrivateKey(
|
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) {
|
factory UtxoWithPrivateKey.fromUnspent(
|
||||||
final address = RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
BitcoinUnspent input, BitcoinWalletBase wallet) {
|
||||||
|
final address =
|
||||||
|
RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
|
||||||
|
|
||||||
final newHd = input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.hd;
|
final newHd =
|
||||||
|
input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.hd;
|
||||||
|
|
||||||
ECPrivate privkey;
|
ECPrivate privkey;
|
||||||
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
|
||||||
final unspentAddress = input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
final unspentAddress =
|
||||||
|
input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
|
||||||
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
|
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
|
||||||
BigintUtils.fromBytes(
|
BigintUtils.fromBytes(
|
||||||
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
|
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
|
||||||
|
@ -223,7 +240,9 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
privkey = generateECPrivate(
|
privkey = generateECPrivate(
|
||||||
hd: newHd, index: input.bitcoinAddressRecord.index, network: BitcoinNetwork.mainnet);
|
hd: newHd,
|
||||||
|
index: input.bitcoinAddressRecord.index,
|
||||||
|
network: BitcoinNetwork.mainnet);
|
||||||
}
|
}
|
||||||
|
|
||||||
return UtxoWithPrivateKey(
|
return UtxoWithPrivateKey(
|
||||||
|
@ -232,7 +251,8 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
|
||||||
value: BigInt.from(input.value),
|
value: BigInt.from(input.value),
|
||||||
vout: input.vout,
|
vout: input.vout,
|
||||||
scriptType: input.bitcoinAddressRecord.type,
|
scriptType: input.bitcoinAddressRecord.type,
|
||||||
isSilentPayment: input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
isSilentPayment:
|
||||||
|
input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
|
||||||
),
|
),
|
||||||
ownerDetails: UtxoAddressDetails(
|
ownerDetails: UtxoAddressDetails(
|
||||||
publicKey: privkey.getPublic().toHex(),
|
publicKey: privkey.getPublic().toHex(),
|
||||||
|
|
|
@ -9,9 +9,7 @@ class PSBTTransactionBuild {
|
||||||
final PsbtV2 psbt = PsbtV2();
|
final PsbtV2 psbt = PsbtV2();
|
||||||
|
|
||||||
PSBTTransactionBuild(
|
PSBTTransactionBuild(
|
||||||
{required List<PSBTReadyUtxoWithAddress> inputs,
|
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, bool enableRBF = true}) {
|
||||||
required List<BitcoinBaseOutput> outputs,
|
|
||||||
bool enableRBF = true}) {
|
|
||||||
psbt.setGlobalTxVersion(2);
|
psbt.setGlobalTxVersion(2);
|
||||||
psbt.setGlobalInputCount(inputs.length);
|
psbt.setGlobalInputCount(inputs.length);
|
||||||
psbt.setGlobalOutputCount(outputs.length);
|
psbt.setGlobalOutputCount(outputs.length);
|
||||||
|
@ -19,20 +17,20 @@ class PSBTTransactionBuild {
|
||||||
for (var i = 0; i < inputs.length; i++) {
|
for (var i = 0; i < inputs.length; i++) {
|
||||||
final input = inputs[i];
|
final input = inputs[i];
|
||||||
|
|
||||||
printV(input.utxo.isP2tr);
|
printV(input.utxo.isP2tr());
|
||||||
printV(input.utxo.isSegwit);
|
printV(input.utxo.isSegwit());
|
||||||
printV(input.utxo.isP2shSegwit);
|
printV(input.utxo.isP2shSegwit());
|
||||||
|
|
||||||
psbt.setInputPreviousTxId(
|
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
||||||
i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
|
|
||||||
psbt.setInputOutputIndex(i, input.utxo.vout);
|
psbt.setInputOutputIndex(i, input.utxo.vout);
|
||||||
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
|
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
|
||||||
|
|
||||||
if (input.utxo.isSegwit) {
|
|
||||||
|
if (input.utxo.isSegwit()) {
|
||||||
setInputSegwit(i, input);
|
setInputSegwit(i, input);
|
||||||
} else if (input.utxo.isP2shSegwit) {
|
} else if (input.utxo.isP2shSegwit()) {
|
||||||
setInputP2shSegwit(i, input);
|
setInputP2shSegwit(i, input);
|
||||||
} else if (input.utxo.isP2tr) {
|
} else if (input.utxo.isP2tr()) {
|
||||||
// ToDo: (Konsti) Handle Taproot Inputs
|
// ToDo: (Konsti) Handle Taproot Inputs
|
||||||
} else {
|
} else {
|
||||||
setInputP2pkh(i, input);
|
setInputP2pkh(i, input);
|
||||||
|
@ -51,14 +49,20 @@ class PSBTTransactionBuild {
|
||||||
|
|
||||||
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
|
||||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
psbt.setInputBip32Derivation(
|
||||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
i,
|
||||||
|
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||||
|
input.ownerMasterFingerprint,
|
||||||
|
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
|
||||||
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
|
||||||
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
psbt.setInputBip32Derivation(
|
||||||
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
i,
|
||||||
|
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
|
||||||
|
input.ownerMasterFingerprint,
|
||||||
|
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
|
||||||
|
|
||||||
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
|
||||||
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));
|
||||||
|
|
|
@ -21,7 +21,7 @@ String getOutputAmountFromPsbt(String psbtV0, BitcoinWalletBase wallet) {
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
for (var i = 0; i < psbt.getGlobalOutputCount(); i++) {
|
for (var i = 0; i < psbt.getGlobalOutputCount(); i++) {
|
||||||
final script = psbt.getOutputScript(i);
|
final script = psbt.getOutputScript(i);
|
||||||
if (wallet.isMine(Script.fromRaw(bytes: script))) {
|
if (wallet.isMine(Script.fromRaw(byteData: script))) {
|
||||||
amount += psbt.getOutputAmount(i);
|
amount += psbt.getOutputAmount(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,20 +79,20 @@ packages:
|
||||||
dependency: "direct overridden"
|
dependency: "direct overridden"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v15
|
ref: cake-update-v9
|
||||||
resolved-ref: "29160733cbc4ef2c7b8c8fe9ed0297c9bffecfe2"
|
resolved-ref: "86969a14e337383e14965f5fb45a72a63e5009bc"
|
||||||
url: "https://github.com/cake-tech/bitcoin_base"
|
url: "https://github.com/cake-tech/bitcoin_base"
|
||||||
source: git
|
source: git
|
||||||
version: "6.1.0"
|
version: "4.7.0"
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
bluez:
|
bluez:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -681,11 +681,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -968,8 +968,8 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: "sp_v4.0.0"
|
||||||
resolved-ref: f3c172a7dc5155f5e745e4630b05f197e098a5cd
|
resolved-ref: "2554cb8bd3ee1d026bc63e76a30d1226960c7cb4"
|
||||||
url: "https://github.com/cake-tech/sp_scanner"
|
url: "https://github.com/cake-tech/sp_scanner"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
|
|
|
@ -29,14 +29,14 @@ dependencies:
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
cw_mweb:
|
cw_mweb:
|
||||||
path: ../cw_mweb
|
path: ../cw_mweb
|
||||||
grpc: ^4.0.1
|
grpc: ^4.0.1
|
||||||
sp_scanner:
|
sp_scanner:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/sp_scanner
|
url: https://github.com/cake-tech/sp_scanner
|
||||||
ref: cake-update-v4
|
ref: sp_v4.0.0
|
||||||
bech32:
|
bech32:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bech32.git
|
url: https://github.com/cake-tech/bech32.git
|
||||||
|
@ -69,7 +69,7 @@ dependency_overrides:
|
||||||
bitcoin_base:
|
bitcoin_base:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base
|
url: https://github.com/cake-tech/bitcoin_base
|
||||||
ref: cake-update-v15
|
ref: cake-update-v9
|
||||||
pointycastle: 3.7.4
|
pointycastle: 3.7.4
|
||||||
ffi: 2.1.0
|
ffi: 2.1.0
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ dependencies:
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -42,7 +42,7 @@ dependency_overrides:
|
||||||
bitcoin_base:
|
bitcoin_base:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base
|
url: https://github.com/cake-tech/bitcoin_base
|
||||||
ref: cake-update-v15
|
ref: cake-update-v9
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
@ -1,33 +1,26 @@
|
||||||
|
import 'dart:convert';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:on_chain/solana/solana.dart';
|
import 'package:on_chain/solana/solana.dart';
|
||||||
|
|
||||||
class SolanaRPCHTTPService implements SolanaServiceProvider {
|
class SolanaRPCHTTPService implements SolanaJSONRPCService {
|
||||||
SolanaRPCHTTPService(
|
SolanaRPCHTTPService(
|
||||||
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
|
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
|
||||||
: client = client ?? Client();
|
: client = client ?? Client();
|
||||||
|
@override
|
||||||
final String url;
|
final String url;
|
||||||
final Client client;
|
final Client client;
|
||||||
final Duration defaultRequestTimeout;
|
final Duration defaultRequestTimeout;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SolanaServiceResponse<T>> doRequest<T>(SolanaRequestDetails params,
|
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
|
||||||
{Duration? timeout}) async {
|
final response = await client.post(
|
||||||
if (!params.type.isPostRequest) {
|
Uri.parse(url),
|
||||||
final response = await client.get(
|
body: params.toRequestBody(),
|
||||||
params.toUri(url),
|
headers: {
|
||||||
headers: {'Content-Type': 'application/json'},
|
'Content-Type': 'application/json',
|
||||||
).timeout(timeout ?? defaultRequestTimeout);
|
},
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
).timeout(timeout ?? defaultRequestTimeout);
|
||||||
}
|
final data = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
return data;
|
||||||
final response = await client
|
|
||||||
.post(
|
|
||||||
params.toUri(url),
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
body: params.body(),
|
|
||||||
)
|
|
||||||
.timeout(timeout ?? defaultRequestTimeout);
|
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,11 +50,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -478,11 +478,11 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -30,7 +30,7 @@ dependencies:
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
# tor:
|
# tor:
|
||||||
# git:
|
# git:
|
||||||
# url: https://github.com/cake-tech/tor.git
|
# url: https://github.com/cake-tech/tor.git
|
||||||
|
|
|
@ -50,11 +50,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -501,11 +501,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -66,11 +66,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
bluez:
|
bluez:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -598,11 +598,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -61,11 +61,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -550,11 +550,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -19,7 +19,7 @@ import '.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
class SolanaWalletClient {
|
class SolanaWalletClient {
|
||||||
final httpClient = http.Client();
|
final httpClient = http.Client();
|
||||||
SolanaProvider? _provider;
|
SolanaRPC? _provider;
|
||||||
|
|
||||||
bool connect(Node node) {
|
bool connect(Node node) {
|
||||||
try {
|
try {
|
||||||
|
@ -38,7 +38,7 @@ class SolanaWalletClient {
|
||||||
formattedUrl = '$protocolUsed://${node.uriRaw}';
|
formattedUrl = '$protocolUsed://${node.uriRaw}';
|
||||||
}
|
}
|
||||||
|
|
||||||
_provider = SolanaProvider(SolanaRPCHTTPService(url: formattedUrl));
|
_provider = SolanaRPC(SolanaRPCHTTPService(url: formattedUrl));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -49,7 +49,7 @@ class SolanaWalletClient {
|
||||||
Future<double> getBalance(String walletAddress) async {
|
Future<double> getBalance(String walletAddress) async {
|
||||||
try {
|
try {
|
||||||
final balance = await _provider!.requestWithContext(
|
final balance = await _provider!.requestWithContext(
|
||||||
SolanaRequestGetBalance(
|
SolanaRPCGetBalance(
|
||||||
account: SolAddress(walletAddress),
|
account: SolAddress(walletAddress),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -68,11 +68,11 @@ class SolanaWalletClient {
|
||||||
String mintAddress, String publicKey) async {
|
String mintAddress, String publicKey) async {
|
||||||
try {
|
try {
|
||||||
final result = await _provider!.request(
|
final result = await _provider!.request(
|
||||||
SolanaRequestGetTokenAccountsByOwner(
|
SolanaRPCGetTokenAccountsByOwner(
|
||||||
account: SolAddress(publicKey),
|
account: SolAddress(publicKey),
|
||||||
mint: SolAddress(mintAddress),
|
mint: SolAddress(mintAddress),
|
||||||
commitment: Commitment.confirmed,
|
commitment: Commitment.confirmed,
|
||||||
encoding: SolanaRequestEncoding.base64,
|
encoding: SolanaRPCEncoding.base64,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ class SolanaWalletClient {
|
||||||
|
|
||||||
for (var tokenAccount in tokenAccounts) {
|
for (var tokenAccount in tokenAccounts) {
|
||||||
final tokenAmountResult = await _provider!.request(
|
final tokenAmountResult = await _provider!.request(
|
||||||
SolanaRequestGetTokenAccountBalance(account: tokenAccount.pubkey),
|
SolanaRPCGetTokenAccountBalance(account: tokenAccount.pubkey),
|
||||||
);
|
);
|
||||||
|
|
||||||
final balance = tokenAmountResult.uiAmountString;
|
final balance = tokenAmountResult.uiAmountString;
|
||||||
|
@ -112,7 +112,7 @@ class SolanaWalletClient {
|
||||||
Future<double> getFeeForMessage(String message, Commitment commitment) async {
|
Future<double> getFeeForMessage(String message, Commitment commitment) async {
|
||||||
try {
|
try {
|
||||||
final feeForMessage = await _provider!.request(
|
final feeForMessage = await _provider!.request(
|
||||||
SolanaRequestGetFeeForMessage(
|
SolanaRPCGetFeeForMessage(
|
||||||
encodedMessage: message,
|
encodedMessage: message,
|
||||||
commitment: commitment,
|
commitment: commitment,
|
||||||
),
|
),
|
||||||
|
@ -342,7 +342,7 @@ class SolanaWalletClient {
|
||||||
List<SolanaTransactionModel> transactions = [];
|
List<SolanaTransactionModel> transactions = [];
|
||||||
try {
|
try {
|
||||||
final signatures = await _provider!.request(
|
final signatures = await _provider!.request(
|
||||||
SolanaRequestGetSignaturesForAddress(
|
SolanaRPCGetSignaturesForAddress(
|
||||||
account: address,
|
account: address,
|
||||||
commitment: commitment,
|
commitment: commitment,
|
||||||
),
|
),
|
||||||
|
@ -357,9 +357,9 @@ class SolanaWalletClient {
|
||||||
final batchResponses = await Future.wait(batch.map((signature) async {
|
final batchResponses = await Future.wait(batch.map((signature) async {
|
||||||
try {
|
try {
|
||||||
return await _provider!.request(
|
return await _provider!.request(
|
||||||
SolanaRequestGetTransaction(
|
SolanaRPCGetTransaction(
|
||||||
transactionSignature: signature['signature'],
|
transactionSignature: signature['signature'],
|
||||||
encoding: SolanaRequestEncoding.jsonParsed,
|
encoding: SolanaRPCEncoding.jsonParsed,
|
||||||
maxSupportedTransactionVersion: 0,
|
maxSupportedTransactionVersion: 0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -482,7 +482,7 @@ class SolanaWalletClient {
|
||||||
|
|
||||||
void stop() {}
|
void stop() {}
|
||||||
|
|
||||||
SolanaProvider? get getSolanaProvider => _provider;
|
SolanaRPC? get getSolanaProvider => _provider;
|
||||||
|
|
||||||
Future<PendingSolanaTransaction> signSolanaTransaction({
|
Future<PendingSolanaTransaction> signSolanaTransaction({
|
||||||
required String tokenTitle,
|
required String tokenTitle,
|
||||||
|
@ -523,7 +523,7 @@ class SolanaWalletClient {
|
||||||
|
|
||||||
Future<SolAddress> _getLatestBlockhash(Commitment commitment) async {
|
Future<SolAddress> _getLatestBlockhash(Commitment commitment) async {
|
||||||
final latestBlockhash = await _provider!.request(
|
final latestBlockhash = await _provider!.request(
|
||||||
const SolanaRequestGetLatestBlockhash(),
|
const SolanaRPCGetLatestBlockhash(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return latestBlockhash.blockhash;
|
return latestBlockhash.blockhash;
|
||||||
|
@ -599,7 +599,7 @@ class SolanaWalletClient {
|
||||||
required double fee,
|
required double fee,
|
||||||
}) async {
|
}) async {
|
||||||
final rent = await _provider!.request(
|
final rent = await _provider!.request(
|
||||||
SolanaRequestGetMinimumBalanceForRentExemption(
|
SolanaRPCGetMinimumBalanceForRentExemption(
|
||||||
size: SolanaTokenAccountUtils.accountSize,
|
size: SolanaTokenAccountUtils.accountSize,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -732,7 +732,7 @@ class SolanaWalletClient {
|
||||||
SolanaAccountInfo? accountInfo;
|
SolanaAccountInfo? accountInfo;
|
||||||
try {
|
try {
|
||||||
accountInfo = await _provider!.request(
|
accountInfo = await _provider!.request(
|
||||||
SolanaRequestGetAccountInfo(account: associatedTokenAccount.address),
|
SolanaRPCGetAccountInfo(account: associatedTokenAccount.address),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
accountInfo = null;
|
accountInfo = null;
|
||||||
|
@ -906,7 +906,7 @@ class SolanaWalletClient {
|
||||||
try {
|
try {
|
||||||
/// Send the transaction to the Solana network.
|
/// Send the transaction to the Solana network.
|
||||||
final signature = await _provider!.request(
|
final signature = await _provider!.request(
|
||||||
SolanaRequestSendTransaction(
|
SolanaRPCSendTransaction(
|
||||||
encodedTransaction: serializedTransaction,
|
encodedTransaction: serializedTransaction,
|
||||||
commitment: commitment,
|
commitment: commitment,
|
||||||
),
|
),
|
||||||
|
|
|
@ -611,7 +611,7 @@ abstract class SolanaWalletBase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SolanaProvider? get solanaProvider => _client.getSolanaProvider;
|
SolanaRPC? get solanaProvider => _client.getSolanaProvider;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get password => _password;
|
String get password => _password;
|
||||||
|
|
|
@ -23,11 +23,11 @@ dependencies:
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
@ -235,7 +235,7 @@ class TronClient {
|
||||||
String contractAddress = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
|
String contractAddress = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
|
||||||
String constantAmount =
|
String constantAmount =
|
||||||
'0'; // We're using 0 as the base amount here as we get an error when balance is zero i.e for new wallets.
|
'0'; // We're using 0 as the base amount here as we get an error when balance is zero i.e for new wallets.
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final function = contract.functionFromName("transfer");
|
final function = contract.functionFromName("transfer");
|
||||||
|
|
||||||
|
@ -405,7 +405,7 @@ class TronClient {
|
||||||
String contractAddress,
|
String contractAddress,
|
||||||
BigInt tronBalance,
|
BigInt tronBalance,
|
||||||
) async {
|
) async {
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final function = contract.functionFromName("transfer");
|
final function = contract.functionFromName("transfer");
|
||||||
|
|
||||||
|
@ -483,7 +483,7 @@ class TronClient {
|
||||||
|
|
||||||
final tokenAddress = TronAddress(contractAddress);
|
final tokenAddress = TronAddress(contractAddress);
|
||||||
|
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final function = contract.functionFromName("balanceOf");
|
final function = contract.functionFromName("balanceOf");
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ class TronClient {
|
||||||
|
|
||||||
final ownerAddress = TronAddress(userAddress);
|
final ownerAddress = TronAddress(userAddress);
|
||||||
|
|
||||||
final contract = ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final name =
|
final name =
|
||||||
(await getTokenDetail(contract, "name", ownerAddress, tokenAddress) as String?) ?? '';
|
(await getTokenDetail(contract, "name", ownerAddress, tokenAddress) as String?) ?? '';
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import '.secrets.g.dart' as secrets;
|
|
||||||
import 'package:on_chain/tron/tron.dart';
|
import 'package:on_chain/tron/tron.dart';
|
||||||
|
import '.secrets.g.dart' as secrets;
|
||||||
|
|
||||||
class TronHTTPProvider implements TronServiceProvider {
|
class TronHTTPProvider implements TronServiceProvider {
|
||||||
TronHTTPProvider(
|
TronHTTPProvider(
|
||||||
|
@ -8,37 +10,34 @@ class TronHTTPProvider implements TronServiceProvider {
|
||||||
http.Client? client,
|
http.Client? client,
|
||||||
this.defaultRequestTimeout = const Duration(seconds: 30)})
|
this.defaultRequestTimeout = const Duration(seconds: 30)})
|
||||||
: client = client ?? http.Client();
|
: client = client ?? http.Client();
|
||||||
|
@override
|
||||||
final String url;
|
final String url;
|
||||||
final http.Client client;
|
final http.Client client;
|
||||||
final Duration defaultRequestTimeout;
|
final Duration defaultRequestTimeout;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<TronServiceResponse<T>> doRequest<T>(TronRequestDetails params,
|
Future<Map<String, dynamic>> get(TronRequestDetails params, [Duration? timeout]) async {
|
||||||
{Duration? timeout}) async {
|
final response = await client.get(Uri.parse(params.url(url)), headers: {
|
||||||
if (!params.type.isPostRequest) {
|
'Content-Type': 'application/json',
|
||||||
final response = await client.get(
|
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
||||||
params.toUri(url),
|
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
||||||
headers: {
|
}).timeout(timeout ?? defaultRequestTimeout);
|
||||||
'Content-Type': 'application/json',
|
final data = json.decode(response.body) as Map<String, dynamic>;
|
||||||
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
return data;
|
||||||
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
}
|
||||||
},
|
|
||||||
).timeout(timeout ?? defaultRequestTimeout);
|
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Map<String, dynamic>> post(TronRequestDetails params, [Duration? timeout]) async {
|
||||||
final response = await client
|
final response = await client
|
||||||
.post(
|
.post(Uri.parse(params.url(url)),
|
||||||
params.toUri(url),
|
headers: {
|
||||||
headers: {
|
'Content-Type': 'application/json',
|
||||||
'Content-Type': 'application/json',
|
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
||||||
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
|
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
||||||
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
|
},
|
||||||
},
|
body: params.toRequestBody())
|
||||||
body: params.body(),
|
|
||||||
)
|
|
||||||
.timeout(timeout ?? defaultRequestTimeout);
|
.timeout(timeout ?? defaultRequestTimeout);
|
||||||
return params.toResponse(response.bodyBytes, response.statusCode);
|
final data = json.decode(response.body) as Map<String, dynamic>;
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:blockchain_utils/hex/hex.dart';
|
import 'package:blockchain_utils/hex/hex.dart';
|
||||||
import 'package:on_chain/on_chain.dart';
|
import 'package:on_chain/on_chain.dart';
|
||||||
import 'package:on_chain/solidity/address/core.dart';
|
|
||||||
|
|
||||||
class TronTRC20TransactionModel extends TronTransactionModel {
|
class TronTRC20TransactionModel extends TronTransactionModel {
|
||||||
String? transactionId;
|
String? transactionId;
|
||||||
|
@ -189,7 +188,7 @@ class Value {
|
||||||
output = output.replaceFirst('0x', '').substring(8);
|
output = output.replaceFirst('0x', '').substring(8);
|
||||||
final abiCoder = ABICoder.fromType('address');
|
final abiCoder = ABICoder.fromType('address');
|
||||||
final decoded = abiCoder.decode(AbiParameter.bytes, hex.decode(output));
|
final decoded = abiCoder.decode(AbiParameter.bytes, hex.decode(output));
|
||||||
final tronAddress = TronAddress.fromEthAddress((decoded.result as SolidityAddress).toBytes());
|
final tronAddress = TronAddress.fromEthAddress((decoded.result as ETHAddress).toBytes());
|
||||||
|
|
||||||
return tronAddress.toString();
|
return tronAddress.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import 'package:cw_tron/tron_transaction_info.dart';
|
||||||
import 'package:cw_tron/tron_wallet_addresses.dart';
|
import 'package:cw_tron/tron_wallet_addresses.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:on_chain/on_chain.dart' as on_chain;
|
import 'package:on_chain/on_chain.dart';
|
||||||
|
|
||||||
part 'tron_wallet.g.dart';
|
part 'tron_wallet.g.dart';
|
||||||
|
|
||||||
|
@ -74,13 +74,13 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
late final Box<TronToken> tronTokensBox;
|
late final Box<TronToken> tronTokensBox;
|
||||||
|
|
||||||
late final on_chain.TronPrivateKey _tronPrivateKey;
|
late final TronPrivateKey _tronPrivateKey;
|
||||||
|
|
||||||
late final on_chain.TronPublicKey _tronPublicKey;
|
late final TronPublicKey _tronPublicKey;
|
||||||
|
|
||||||
on_chain.TronPublicKey get tronPublicKey => _tronPublicKey;
|
TronPublicKey get tronPublicKey => _tronPublicKey;
|
||||||
|
|
||||||
on_chain.TronPrivateKey get tronPrivateKey => _tronPrivateKey;
|
TronPrivateKey get tronPrivateKey => _tronPrivateKey;
|
||||||
|
|
||||||
late String _tronAddress;
|
late String _tronAddress;
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
String idFor(String name, WalletType type) => '${walletTypeToString(type).toLowerCase()}_$name';
|
String idFor(String name, WalletType type) => '${walletTypeToString(type).toLowerCase()}_$name';
|
||||||
|
|
||||||
Future<on_chain.TronPrivateKey> getPrivateKey({
|
Future<TronPrivateKey> getPrivateKey({
|
||||||
String? mnemonic,
|
String? mnemonic,
|
||||||
String? privateKey,
|
String? privateKey,
|
||||||
required String password,
|
required String password,
|
||||||
|
@ -198,7 +198,7 @@ abstract class TronWalletBase
|
||||||
}) async {
|
}) async {
|
||||||
assert(mnemonic != null || privateKey != null);
|
assert(mnemonic != null || privateKey != null);
|
||||||
|
|
||||||
if (privateKey != null) return on_chain.TronPrivateKey(privateKey);
|
if (privateKey != null) return TronPrivateKey(privateKey);
|
||||||
|
|
||||||
final seed = bip39.mnemonicToSeed(mnemonic!, passphrase: passphrase ?? '');
|
final seed = bip39.mnemonicToSeed(mnemonic!, passphrase: passphrase ?? '');
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
final childKey = bip44.deriveDefaultPath;
|
final childKey = bip44.deriveDefaultPath;
|
||||||
|
|
||||||
return on_chain.TronPrivateKey.fromBytes(childKey.privateKey.raw);
|
return TronPrivateKey.fromBytes(childKey.privateKey.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -242,10 +242,10 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
Future<void> _getEstimatedFees() async {
|
Future<void> _getEstimatedFees() async {
|
||||||
final nativeFee = await _getNativeTxFee();
|
final nativeFee = await _getNativeTxFee();
|
||||||
nativeTxEstimatedFee = on_chain.TronHelper.fromSun(BigInt.from(nativeFee));
|
nativeTxEstimatedFee = TronHelper.fromSun(BigInt.from(nativeFee));
|
||||||
|
|
||||||
final trc20Fee = await _getTrc20TxFee();
|
final trc20Fee = await _getTrc20TxFee();
|
||||||
trc20EstimatedFee = on_chain.TronHelper.fromSun(BigInt.from(trc20Fee));
|
trc20EstimatedFee = TronHelper.fromSun(BigInt.from(trc20Fee));
|
||||||
|
|
||||||
log('Native Estimated Fee: $nativeTxEstimatedFee');
|
log('Native Estimated Fee: $nativeTxEstimatedFee');
|
||||||
log('TRC20 Estimated Fee: $trc20EstimatedFee');
|
log('TRC20 Estimated Fee: $trc20EstimatedFee');
|
||||||
|
@ -323,7 +323,7 @@ abstract class TronWalletBase
|
||||||
totalAmount = walletBalanceForCurrency;
|
totalAmount = walletBalanceForCurrency;
|
||||||
} else {
|
} else {
|
||||||
final totalOriginalAmount = double.parse(output.cryptoAmount ?? '0.0');
|
final totalOriginalAmount = double.parse(output.cryptoAmount ?? '0.0');
|
||||||
totalAmount = on_chain.TronHelper.toSun(totalOriginalAmount.toString());
|
totalAmount = TronHelper.toSun(totalOriginalAmount.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (walletBalanceForCurrency < totalAmount || totalAmount < BigInt.zero) {
|
if (walletBalanceForCurrency < totalAmount || totalAmount < BigInt.zero) {
|
||||||
|
@ -338,7 +338,7 @@ abstract class TronWalletBase
|
||||||
toAddress: tronCredentials.outputs.first.isParsedAddress
|
toAddress: tronCredentials.outputs.first.isParsedAddress
|
||||||
? tronCredentials.outputs.first.extractedAddress!
|
? tronCredentials.outputs.first.extractedAddress!
|
||||||
: tronCredentials.outputs.first.address,
|
: tronCredentials.outputs.first.address,
|
||||||
amount: on_chain.TronHelper.fromSun(totalAmount),
|
amount: TronHelper.fromSun(totalAmount),
|
||||||
currency: transactionCurrency,
|
currency: transactionCurrency,
|
||||||
tronBalance: tronBalance,
|
tronBalance: tronBalance,
|
||||||
sendAll: shouldSendAll,
|
sendAll: shouldSendAll,
|
||||||
|
@ -355,9 +355,9 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
final Map<String, TronTransactionInfo> result = {};
|
final Map<String, TronTransactionInfo> result = {};
|
||||||
|
|
||||||
final contract = on_chain.ContractABI.fromJson(trc20Abi);
|
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
|
||||||
|
|
||||||
final ownerAddress = on_chain.TronAddress(_tronAddress);
|
final ownerAddress = TronAddress(_tronAddress);
|
||||||
|
|
||||||
for (var transactionModel in transactions) {
|
for (var transactionModel in transactions) {
|
||||||
if (transactionModel.isError) {
|
if (transactionModel.isError) {
|
||||||
|
@ -371,7 +371,7 @@ abstract class TronWalletBase
|
||||||
|
|
||||||
String? tokenSymbol;
|
String? tokenSymbol;
|
||||||
if (transactionModel.contractAddress != null) {
|
if (transactionModel.contractAddress != null) {
|
||||||
final tokenAddress = on_chain.TronAddress(transactionModel.contractAddress!);
|
final tokenAddress = TronAddress(transactionModel.contractAddress!);
|
||||||
|
|
||||||
tokenSymbol = (await _client.getTokenDetail(
|
tokenSymbol = (await _client.getTokenDetail(
|
||||||
contract,
|
contract,
|
||||||
|
@ -385,10 +385,9 @@ abstract class TronWalletBase
|
||||||
result[transactionModel.hash] = TronTransactionInfo(
|
result[transactionModel.hash] = TronTransactionInfo(
|
||||||
id: transactionModel.hash,
|
id: transactionModel.hash,
|
||||||
tronAmount: transactionModel.amount ?? BigInt.zero,
|
tronAmount: transactionModel.amount ?? BigInt.zero,
|
||||||
direction:
|
direction: TronAddress(transactionModel.from!, visible: false).toAddress() == address
|
||||||
on_chain.TronAddress(transactionModel.from!, visible: false).toAddress() == address
|
? TransactionDirection.outgoing
|
||||||
? TransactionDirection.outgoing
|
: TransactionDirection.incoming,
|
||||||
: TransactionDirection.incoming,
|
|
||||||
blockTime: transactionModel.date,
|
blockTime: transactionModel.date,
|
||||||
txFee: transactionModel.fee,
|
txFee: transactionModel.fee,
|
||||||
tokenSymbol: tokenSymbol ?? "TRX",
|
tokenSymbol: tokenSymbol ?? "TRX",
|
||||||
|
@ -605,13 +604,11 @@ abstract class TronWalletBase
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
on_chain.TronPublicKey pubKey =
|
TronPublicKey pubKey = TronPublicKey.fromPersonalSignature(ascii.encode(message), signature)!;
|
||||||
on_chain.TronPublicKey.fromPersonalSignature(ascii.encode(message), signature)!;
|
|
||||||
return pubKey.toAddress().toString() == address;
|
return pubKey.toAddress().toString() == address;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTronBase58AddressFromHex(String hexAddress) =>
|
String getTronBase58AddressFromHex(String hexAddress) => TronAddress(hexAddress).toAddress();
|
||||||
on_chain.TronAddress(hexAddress).toAddress();
|
|
||||||
|
|
||||||
void updateScanProviderUsageState(bool isEnabled) {
|
void updateScanProviderUsageState(bool isEnabled) {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
|
|
|
@ -18,11 +18,11 @@ dependencies:
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
mobx: ^2.3.0+1
|
mobx: ^2.3.0+1
|
||||||
bip39: ^1.0.6
|
bip39: ^1.0.6
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
|
|
|
@ -45,11 +45,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -505,11 +505,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -45,11 +45,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
|
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
|
||||||
url: "https://github.com/cake-tech/blockchain_utils"
|
url: "https://github.com/cake-tech/blockchain_utils"
|
||||||
source: git
|
source: git
|
||||||
version: "4.3.0"
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -502,11 +502,11 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
|
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
|
||||||
url: "https://github.com/cake-tech/on_chain.git"
|
url: "https://github.com/cake-tech/on_chain.git"
|
||||||
source: git
|
source: git
|
||||||
version: "6.2.0"
|
version: "3.7.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -213,9 +213,9 @@ class CWBitcoin extends Bitcoin {
|
||||||
return bitcoinWallet.unspentCoins.where((element) {
|
return bitcoinWallet.unspentCoins.where((element) {
|
||||||
switch (coinTypeToSpendFrom) {
|
switch (coinTypeToSpendFrom) {
|
||||||
case UnspentCoinType.mweb:
|
case UnspentCoinType.mweb:
|
||||||
return element.bitcoinAddressRecord.type == SegwitAddressType.mweb;
|
return element.bitcoinAddressRecord.type == SegwitAddresType.mweb;
|
||||||
case UnspentCoinType.nonMweb:
|
case UnspentCoinType.nonMweb:
|
||||||
return element.bitcoinAddressRecord.type != SegwitAddressType.mweb;
|
return element.bitcoinAddressRecord.type != SegwitAddresType.mweb;
|
||||||
case UnspentCoinType.any:
|
case UnspentCoinType.any:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -296,14 +296,14 @@ class CWBitcoin extends Bitcoin {
|
||||||
case BitcoinReceivePageOption.p2sh:
|
case BitcoinReceivePageOption.p2sh:
|
||||||
return P2shAddressType.p2wpkhInP2sh;
|
return P2shAddressType.p2wpkhInP2sh;
|
||||||
case BitcoinReceivePageOption.p2tr:
|
case BitcoinReceivePageOption.p2tr:
|
||||||
return SegwitAddressType.p2tr;
|
return SegwitAddresType.p2tr;
|
||||||
case BitcoinReceivePageOption.p2wsh:
|
case BitcoinReceivePageOption.p2wsh:
|
||||||
return SegwitAddressType.p2wsh;
|
return SegwitAddresType.p2wsh;
|
||||||
case BitcoinReceivePageOption.mweb:
|
case BitcoinReceivePageOption.mweb:
|
||||||
return SegwitAddressType.mweb;
|
return SegwitAddresType.mweb;
|
||||||
case BitcoinReceivePageOption.p2wpkh:
|
case BitcoinReceivePageOption.p2wpkh:
|
||||||
default:
|
default:
|
||||||
return SegwitAddressType.p2wpkh;
|
return SegwitAddresType.p2wpkh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +527,7 @@ class CWBitcoin extends Bitcoin {
|
||||||
List<ElectrumSubAddress> getSilentPaymentAddresses(Object wallet) {
|
List<ElectrumSubAddress> getSilentPaymentAddresses(Object wallet) {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
return bitcoinWallet.walletAddresses.silentAddresses
|
return bitcoinWallet.walletAddresses.silentAddresses
|
||||||
.where((addr) => addr.type != SegwitAddressType.p2tr)
|
.where((addr) => addr.type != SegwitAddresType.p2tr)
|
||||||
.map((addr) => ElectrumSubAddress(
|
.map((addr) => ElectrumSubAddress(
|
||||||
id: addr.index,
|
id: addr.index,
|
||||||
name: addr.name,
|
name: addr.name,
|
||||||
|
@ -542,7 +542,7 @@ class CWBitcoin extends Bitcoin {
|
||||||
List<ElectrumSubAddress> getSilentPaymentReceivedAddresses(Object wallet) {
|
List<ElectrumSubAddress> getSilentPaymentReceivedAddresses(Object wallet) {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
return bitcoinWallet.walletAddresses.silentAddresses
|
return bitcoinWallet.walletAddresses.silentAddresses
|
||||||
.where((addr) => addr.type == SegwitAddressType.p2tr)
|
.where((addr) => addr.type == SegwitAddresType.p2tr)
|
||||||
.map((addr) => ElectrumSubAddress(
|
.map((addr) => ElectrumSubAddress(
|
||||||
id: addr.index,
|
id: addr.index,
|
||||||
name: addr.name,
|
name: addr.name,
|
||||||
|
@ -712,7 +712,7 @@ class CWBitcoin extends Bitcoin {
|
||||||
try {
|
try {
|
||||||
final electrumWallet = wallet as ElectrumWallet;
|
final electrumWallet = wallet as ElectrumWallet;
|
||||||
final segwitAddress = electrumWallet.walletAddresses.allAddresses
|
final segwitAddress = electrumWallet.walletAddresses.allAddresses
|
||||||
.firstWhere((element) => !element.isUsed && element.type == SegwitAddressType.p2wpkh);
|
.firstWhere((element) => !element.isUsed && element.type == SegwitAddresType.p2wpkh);
|
||||||
return segwitAddress.address;
|
return segwitAddress.address;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -115,12 +115,12 @@ dependencies:
|
||||||
on_chain:
|
on_chain:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/on_chain.git
|
url: https://github.com/cake-tech/on_chain.git
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
reown_walletkit: ^1.1.2
|
reown_walletkit: ^1.1.2
|
||||||
blockchain_utils:
|
blockchain_utils:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/blockchain_utils
|
url: https://github.com/cake-tech/blockchain_utils
|
||||||
ref: cake-update-v4
|
ref: cake-update-v2
|
||||||
flutter_daemon:
|
flutter_daemon:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/MrCyjaneK/flutter_daemon
|
url: https://github.com/MrCyjaneK/flutter_daemon
|
||||||
|
@ -160,7 +160,7 @@ dependency_overrides:
|
||||||
bitcoin_base:
|
bitcoin_base:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_base
|
url: https://github.com/cake-tech/bitcoin_base
|
||||||
ref: cake-update-v15
|
ref: cake-update-v9
|
||||||
ffi: 2.1.0
|
ffi: 2.1.0
|
||||||
ledger_flutter_plus:
|
ledger_flutter_plus:
|
||||||
git:
|
git:
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import requests
|
|
||||||
import lzma
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Define triplets list
|
|
||||||
triplets = [
|
|
||||||
"x86_64-linux-gnu",
|
|
||||||
"x86_64-linux-android",
|
|
||||||
"aarch64-linux-android",
|
|
||||||
"armv7a-linux-androideabi",
|
|
||||||
# "x86_64-w64-mingw32",
|
|
||||||
# "aarch64-apple-darwin",
|
|
||||||
# "x86_64-apple-darwin",
|
|
||||||
"aarch64-host-apple-darwin",
|
|
||||||
# "aarch64-apple-ios",
|
|
||||||
# "aarch64-apple-iossimulator",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# Get the latest release data
|
|
||||||
resp = requests.get("https://api.github.com/repos/mrcyjanek/monero_c/releases")
|
|
||||||
data = resp.json()[0]
|
|
||||||
tag_name = data["tag_name"]
|
|
||||||
print(f"Downloading artifacts for: {tag_name}")
|
|
||||||
|
|
||||||
assets = data["assets"]
|
|
||||||
for asset in assets:
|
|
||||||
for triplet in triplets:
|
|
||||||
filename = asset["name"]
|
|
||||||
if triplet not in filename:
|
|
||||||
continue
|
|
||||||
|
|
||||||
coin = filename.split("_")[0]
|
|
||||||
local_filename = filename.replace(f"{coin}_{triplet}_", "")
|
|
||||||
local_filename = (
|
|
||||||
f"scripts/monero_c/release/{coin}/{triplet}_{local_filename}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create directory if it doesn't exist
|
|
||||||
os.makedirs(os.path.dirname(local_filename), exist_ok=True)
|
|
||||||
|
|
||||||
url = asset["browser_download_url"]
|
|
||||||
print(f"- downloading {local_filename}")
|
|
||||||
|
|
||||||
# Download the file
|
|
||||||
response = requests.get(url)
|
|
||||||
with open(local_filename, "wb") as f:
|
|
||||||
f.write(response.content)
|
|
||||||
|
|
||||||
# Extract if it's an .xz file
|
|
||||||
if local_filename.endswith(".xz"):
|
|
||||||
print(f" extracting {local_filename}")
|
|
||||||
with lzma.open(local_filename) as f_in:
|
|
||||||
with open(local_filename.replace(".xz", ""), "wb") as f_out:
|
|
||||||
shutil.copyfileobj(f_in, f_out)
|
|
||||||
|
|
||||||
# Generate iOS framework if on macOS
|
|
||||||
if os.uname().sysname == "Darwin": # Check if on macOS
|
|
||||||
print("Generating ios framework")
|
|
||||||
result = subprocess.run(
|
|
||||||
["bash", "-c", "cd scripts/ios && ./gen_framework.sh && cd ../.."],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
print(result.stdout.strip() + result.stderr.strip())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
Loading…
Add table
Add a link
Reference in a new issue