* FIX!

* resolve conflicts with main

* undo debug changes

* fix: methods

* fix: methods2

* Fix Tron issue

* fix: 1k limit & reaching top

* fix: missing unspents

* fix: missing commit

---------

Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
rafael_xmr 2025-05-25 16:28:08 -03:00 committed by GitHub
parent e52bceda3a
commit 7b8ddf9685
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 667 additions and 402 deletions

View file

@ -17,21 +17,16 @@ BitcoinBaseAddress addressFromScript(Script script,
switch (addressType) {
case P2pkhAddressType.p2pkh:
return P2pkhAddress.fromScriptPubkey(
script: script, network: BitcoinNetwork.mainnet);
return P2pkhAddress.fromScriptPubkey(script: script);
case P2shAddressType.p2pkhInP2sh:
case P2shAddressType.p2pkInP2sh:
return P2shAddress.fromScriptPubkey(
script: script, network: BitcoinNetwork.mainnet);
case SegwitAddresType.p2wpkh:
return P2wpkhAddress.fromScriptPubkey(
script: script, network: BitcoinNetwork.mainnet);
case SegwitAddresType.p2wsh:
return P2wshAddress.fromScriptPubkey(
script: script, network: BitcoinNetwork.mainnet);
case SegwitAddresType.p2tr:
return P2trAddress.fromScriptPubkey(
script: script, network: BitcoinNetwork.mainnet);
return P2shAddress.fromScriptPubkey(script: script);
case SegwitAddressType.p2wpkh:
return P2wpkhAddress.fromScriptPubkey(script: script);
case SegwitAddressType.p2wsh:
return P2wshAddress.fromScriptPubkey(script: script);
case SegwitAddressType.p2tr:
return P2trAddress.fromScriptPubkey(script: script);
}
throw ArgumentError("Invalid script");

View file

@ -82,7 +82,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
type: decoded['type'] != null && decoded['type'] != ''
? BitcoinAddressType.values
.firstWhere((type) => type.toString() == decoded['type'] as String)
: SegwitAddresType.p2wpkh,
: SegwitAddressType.p2wpkh,
scriptHash: decoded['scriptHash'] as String?,
network: network,
);

View file

@ -36,9 +36,9 @@ class BitcoinReceivePageOption implements ReceivePageOption {
BitcoinAddressType toType() {
switch (this) {
case BitcoinReceivePageOption.p2tr:
return SegwitAddresType.p2tr;
return SegwitAddressType.p2tr;
case BitcoinReceivePageOption.p2wsh:
return SegwitAddresType.p2wsh;
return SegwitAddressType.p2wsh;
case BitcoinReceivePageOption.p2pkh:
return P2pkhAddressType.p2pkh;
case BitcoinReceivePageOption.p2sh:
@ -46,20 +46,20 @@ class BitcoinReceivePageOption implements ReceivePageOption {
case BitcoinReceivePageOption.silent_payments:
return SilentPaymentsAddresType.p2sp;
case BitcoinReceivePageOption.mweb:
return SegwitAddresType.mweb;
return SegwitAddressType.mweb;
case BitcoinReceivePageOption.p2wpkh:
default:
return SegwitAddresType.p2wpkh;
return SegwitAddressType.p2wpkh;
}
}
factory BitcoinReceivePageOption.fromType(BitcoinAddressType type) {
switch (type) {
case SegwitAddresType.p2tr:
case SegwitAddressType.p2tr:
return BitcoinReceivePageOption.p2tr;
case SegwitAddresType.p2wsh:
case SegwitAddressType.p2wsh:
return BitcoinReceivePageOption.p2wsh;
case SegwitAddresType.mweb:
case SegwitAddressType.mweb:
return BitcoinReceivePageOption.mweb;
case P2pkhAddressType.p2pkh:
return BitcoinReceivePageOption.p2pkh;
@ -67,7 +67,7 @@ class BitcoinReceivePageOption implements ReceivePageOption {
return BitcoinReceivePageOption.p2sh;
case SilentPaymentsAddresType.p2sp:
return BitcoinReceivePageOption.silent_payments;
case SegwitAddresType.p2wpkh:
case SegwitAddressType.p2wpkh:
default:
return BitcoinReceivePageOption.p2wpkh;
}

View file

@ -73,9 +73,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialBalance: initialBalance,
seedBytes: seedBytes,
encryptionFileUtils: encryptionFileUtils,
currency: networkParam == BitcoinNetwork.testnet
? CryptoCurrency.tbtc
: CryptoCurrency.btc,
currency:
networkParam == BitcoinNetwork.testnet ? CryptoCurrency.tbtc : CryptoCurrency.btc,
alwaysScan: alwaysScan,
) {
// in a standard BIP44 wallet, mainHd derivation path = m/84'/0'/0'/0 (account 0, index unspecified here)
@ -94,14 +93,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
mainHd: hd,
sideHd: accountHD.childKey(Bip32KeyIndex(1)),
network: networkParam ?? network,
masterHd:
seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
masterHd: seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
isHardwareWallet: walletInfo.isHardwareWallet,
payjoinManager: payjoinManager);
autorun((_) {
this.walletAddresses.isEnabledAutoGenerateSubaddress =
this.isEnabledAutoGenerateSubaddress;
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
});
}
@ -136,8 +133,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
break;
case DerivationType.electrum:
default:
seedBytes =
await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
}
@ -210,10 +206,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
walletInfo.derivationInfo ??= DerivationInfo();
// set the default if not present:
walletInfo.derivationInfo!.derivationPath ??=
snp?.derivationPath ?? electrum_path;
walletInfo.derivationInfo!.derivationType ??=
snp?.derivationType ?? DerivationType.electrum;
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
Uint8List? seedBytes = null;
final mnemonic = keysData.mnemonic;
@ -222,8 +216,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
if (mnemonic != null) {
switch (walletInfo.derivationInfo!.derivationType) {
case DerivationType.electrum:
seedBytes =
await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
break;
case DerivationType.bip39:
default:
@ -269,8 +262,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
late final PayjoinManager payjoinManager;
bool get isPayjoinAvailable => unspentCoinsInfo.values
.where((element) =>
element.walletId == id && element.isSending && !element.isFrozen)
.where((element) => element.walletId == id && element.isSending && !element.isFrozen)
.isNotEmpty;
Future<PsbtV2> buildPsbt({
@ -287,10 +279,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
}) async {
final psbtReadyInputs = <PSBTReadyUtxoWithAddress>[];
for (final utxo in utxos) {
final rawTx =
await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
final publicKeyAndDerivationPath =
publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
final rawTx = await electrumClient.getTransactionHex(hash: utxo.utxo.txHash);
final publicKeyAndDerivationPath = publicKeys[utxo.ownerDetails.address.pubKeyHash()]!;
psbtReadyInputs.add(PSBTReadyUtxoWithAddress(
utxo: utxo.utxo,
@ -302,8 +292,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
));
}
return PSBTTransactionBuild(
inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
return PSBTTransactionBuild(inputs: psbtReadyInputs, outputs: outputs, enableRBF: enableRBF)
.psbt;
}
@ -342,8 +331,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Future<PendingTransaction> createTransaction(Object credentials) async {
credentials = credentials as BitcoinTransactionCredentials;
final tx = (await super.createTransaction(credentials))
as PendingBitcoinTransaction;
final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction;
final payjoinUri = credentials.payjoinUri;
if (payjoinUri == null) return tx;
@ -366,12 +354,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
publicKeys: tx.publicKeys!,
masterFingerprint: Uint8List(0));
final originalPsbt = await signPsbt(
base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
final originalPsbt =
await signPsbt(base64.encode(transaction.asPsbtV0()), getUtxoWithPrivateKeys());
tx.commitOverride = () async {
final sender = await payjoinManager.initSender(
payjoinUri, originalPsbt, int.parse(tx.feeRate));
final sender =
await payjoinManager.initSender(payjoinUri, originalPsbt, int.parse(tx.feeRate));
payjoinManager.spawnNewSender(
sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
};
@ -387,8 +375,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Future<void> commitPsbt(String 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(
btcTx,
@ -402,12 +389,11 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
).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));
await psbt.signWithUTXO(utxos, (txDigest, utxo, key, sighash) {
return utxo.utxo.isP2tr()
return utxo.utxo.isP2tr
? key.signTapRoot(
txDigest,
sighash: sighash,
@ -428,17 +414,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
Future<String> signMessage(String message, {String? address = null}) async {
if (walletInfo.isHardwareWallet) {
final addressEntry = address != null
? walletAddresses.allAddresses
.firstWhere((element) => element.address == address)
? walletAddresses.allAddresses.firstWhere((element) => element.address == address)
: null;
final index = addressEntry?.index ?? 0;
final isChange = addressEntry?.isHidden == true ? 1 : 0;
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!.signMessage(
message: ascii.encode(message), signDerivationPath: derivationPath);
final signature = await _bitcoinLedgerApp!
.signMessage(message: ascii.encode(message), signDerivationPath: derivationPath);
return base64Encode(signature);
}

View file

@ -47,10 +47,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
if (addressType == P2pkhAddressType.p2pkh)
return generateP2PKHAddress(hd: hd, index: index, network: network);
if (addressType == SegwitAddresType.p2tr)
if (addressType == SegwitAddressType.p2tr)
return generateP2TRAddress(hd: hd, index: index, network: network);
if (addressType == SegwitAddresType.p2wsh)
if (addressType == SegwitAddressType.p2wsh)
return generateP2WSHAddress(hd: hd, index: index, network: network);
if (addressType == P2shAddressType.p2wpkhInP2sh)

View file

@ -5,7 +5,6 @@ import 'dart:isolate';
import 'package:bitcoin_base/bitcoin_base.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_bitcoin/bitcoin_wallet.dart';
import 'package:cw_bitcoin/litecoin_wallet.dart';
@ -18,7 +17,7 @@ import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
import 'package:cw_bitcoin/bitcoin_unspent.dart';
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
import 'package:cw_bitcoin/electrum.dart';
import 'package:cw_bitcoin/electrum.dart' as electrum;
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/electrum_derivations.dart';
import 'package:cw_bitcoin/electrum_transaction_history.dart';
@ -69,7 +68,7 @@ abstract class ElectrumWalletBase
Uint8List? seedBytes,
this.passphrase,
List<BitcoinAddressRecord>? initialAddresses,
ElectrumClient? electrumClient,
electrum.ElectrumClient? electrumClient,
ElectrumBalance? initialBalance,
CryptoCurrency? currency,
this.alwaysScan,
@ -96,7 +95,7 @@ abstract class ElectrumWalletBase
this.isTestnet = !network.isMainnet,
this._mnemonic = mnemonic,
super(walletInfo) {
this.electrumClient = electrumClient ?? ElectrumClient();
this.electrumClient = electrumClient ?? electrum.ElectrumClient();
this.walletInfo = walletInfo;
transactionHistory = ElectrumTransactionHistory(
walletInfo: walletInfo,
@ -167,7 +166,7 @@ abstract class ElectrumWalletBase
@observable
bool isEnabledAutoGenerateSubaddress;
late ElectrumClient electrumClient;
late electrum.ElectrumClient electrumClient;
Box<UnspentCoinsInfo> unspentCoinsInfo;
@override
@ -182,7 +181,7 @@ abstract class ElectrumWalletBase
SyncStatus syncStatus;
Set<String> get addressesSet => walletAddresses.allAddresses
.where((element) => element.type != SegwitAddresType.mweb)
.where((element) => element.type != SegwitAddressType.mweb)
.map((addr) => addr.address)
.toSet();
@ -333,14 +332,14 @@ abstract class ElectrumWalletBase
final receivePort = ReceivePort();
_isolate = Isolate.spawn(
startRefresh,
_handleScanSilentPayments,
ScanData(
sendPort: receivePort.sendPort,
silentAddress: walletAddresses.silentAddress!,
network: network,
height: height,
chainTip: chainTip,
electrumClient: ElectrumClient(),
electrumClient: electrum.ElectrumClient(),
transactionHistoryIds: transactionHistory.transactions.keys.toList(),
node: (await getNodeSupportsSilentPayments()) == true
? ScanNode(node!.uri, node!.useSSL)
@ -439,7 +438,6 @@ abstract class ElectrumWalletBase
BigintUtils.fromBytes(BytesUtils.fromHexString(unspent.silentPaymentLabel!)),
)
: silentAddress.B_spend,
network: network,
);
final addressRecord = walletAddresses.silentAddresses
@ -564,7 +562,7 @@ abstract class ElectrumWalletBase
node!.save();
return node!.supportsSilentPayments!;
}
} on RequestFailedTimeoutException catch (_) {
} on electrum.RequestFailedTimeoutException catch (_) {
node!.supportsSilentPayments = false;
node!.save();
return node!.supportsSilentPayments!;
@ -625,9 +623,9 @@ abstract class ElectrumWalletBase
switch (coinTypeToSpendFrom) {
case UnspentCoinType.mweb:
return utx.bitcoinAddressRecord.type == SegwitAddresType.mweb;
return utx.bitcoinAddressRecord.type == SegwitAddressType.mweb;
case UnspentCoinType.nonMweb:
return utx.bitcoinAddressRecord.type != SegwitAddresType.mweb;
return utx.bitcoinAddressRecord.type != SegwitAddressType.mweb;
case UnspentCoinType.any:
return true;
}
@ -635,7 +633,7 @@ abstract class ElectrumWalletBase
final unconfirmedCoins = availableInputs.where((utx) => utx.confirmations == 0).toList();
// sort the unconfirmed coins so that mweb coins are last:
availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddresType.mweb ? 1 : -1);
availableInputs.sort((a, b) => a.bitcoinAddressRecord.type == SegwitAddressType.mweb ? 1 : -1);
for (int i = 0; i < availableInputs.length; i++) {
final utx = availableInputs[i];
@ -643,7 +641,7 @@ abstract class ElectrumWalletBase
if (paysToSilentPayment) {
// Check inputs for shared secret derivation
if (utx.bitcoinAddressRecord.type == SegwitAddresType.p2wsh) {
if (utx.bitcoinAddressRecord.type == SegwitAddressType.p2wsh) {
throw BitcoinTransactionSilentPaymentsNotSupported();
}
}
@ -678,7 +676,7 @@ abstract class ElectrumWalletBase
if (privkey != null) {
inputPrivKeyInfos.add(ECPrivateInfo(
privkey,
address.type == SegwitAddresType.p2tr,
address.type == SegwitAddressType.p2tr,
tweak: !isSilentPayment,
));
@ -1164,7 +1162,7 @@ abstract class ElectrumWalletBase
throw Exception(error);
}
if (utxo.utxo.isP2tr()) {
if (utxo.utxo.isP2tr) {
hasTaprootInputs = true;
return key.privkey.signTapRoot(
txDigest,
@ -1176,20 +1174,18 @@ abstract class ElectrumWalletBase
}
});
return PendingBitcoinTransaction(
transaction,
type,
electrumClient: electrumClient,
amount: estimatedTx.amount,
fee: estimatedTx.fee,
feeRate: feeRateInt.toString(),
network: network,
hasChange: estimatedTx.hasChange,
isSendAll: estimatedTx.isSendAll,
hasTaprootInputs: hasTaprootInputs,
utxos: estimatedTx.utxos,
publicKeys: estimatedTx.publicKeys
)..addListener((transaction) async {
return PendingBitcoinTransaction(transaction, type,
electrumClient: electrumClient,
amount: estimatedTx.amount,
fee: estimatedTx.fee,
feeRate: feeRateInt.toString(),
network: network,
hasChange: estimatedTx.hasChange,
isSendAll: estimatedTx.isSendAll,
hasTaprootInputs: hasTaprootInputs,
utxos: estimatedTx.utxos,
publicKeys: estimatedTx.publicKeys)
..addListener((transaction) async {
transactionHistory.addOne(transaction);
if (estimatedTx.spendsSilentPayment) {
transactionHistory.transactions.values.forEach((tx) {
@ -1233,7 +1229,7 @@ abstract class ElectrumWalletBase
'change_address_index': walletAddresses.currentChangeAddressIndexByType,
'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(),
'address_page_type': walletInfo.addressPageType == null
? SegwitAddresType.p2wpkh.toString()
? SegwitAddressType.p2wpkh.toString()
: walletInfo.addressPageType.toString(),
'balance': balance[currency]?.toJSON(),
'derivationTypeIndex': walletInfo.derivationInfo?.derivationType?.index,
@ -1373,7 +1369,7 @@ abstract class ElectrumWalletBase
List<BitcoinUnspent> updatedUnspentCoins = [];
final previousUnspentCoins = List<BitcoinUnspent>.from(unspentCoins.where((utxo) =>
utxo.bitcoinAddressRecord.type != SegwitAddresType.mweb &&
utxo.bitcoinAddressRecord.type != SegwitAddressType.mweb &&
utxo.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord));
if (hasSilentPaymentsScanning) {
@ -1387,13 +1383,13 @@ abstract class ElectrumWalletBase
// Set the balance of all non-silent payment and non-mweb addresses to 0 before updating
walletAddresses.allAddresses
.where((element) => element.type != SegwitAddresType.mweb)
.where((element) => element.type != SegwitAddressType.mweb)
.forEach((addr) {
if (addr is! BitcoinSilentPaymentAddressRecord) addr.balance = 0;
});
final addressFutures = walletAddresses.allAddresses
.where((element) => element.type != SegwitAddresType.mweb)
.where((element) => element.type != SegwitAddressType.mweb)
.map((address) => fetchUnspent(address))
.toList();
@ -1834,7 +1830,7 @@ abstract class ElectrumWalletBase
throw Exception("Cannot find private key");
}
if (utxo.utxo.isP2tr()) {
if (utxo.utxo.isP2tr) {
return key.signTapRoot(txDigest, sighash: sighash);
} else {
return key.signInput(txDigest, sigHash: sighash);
@ -1984,7 +1980,7 @@ abstract class ElectrumWalletBase
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
} else if (type == WalletType.litecoin) {
await Future.wait(LITECOIN_ADDRESS_TYPES
.where((type) => type != SegwitAddresType.mweb)
.where((type) => type != SegwitAddressType.mweb)
.map((type) => fetchTransactionsForAddressType(historiesWithDetails, type)));
}
@ -2173,7 +2169,7 @@ abstract class ElectrumWalletBase
final unsubscribedScriptHashes = walletAddresses.allAddresses.where(
(address) =>
!_scripthashesUpdateSubject.containsKey(address.getScriptHash(network)) &&
address.type != SegwitAddresType.mweb,
address.type != SegwitAddressType.mweb,
);
await Future.wait(unsubscribedScriptHashes.map((address) async {
@ -2396,9 +2392,9 @@ abstract class ElectrumWalletBase
derivationPath.substring(0, derivationPath.lastIndexOf("'") + 1);
@action
void _onConnectionStatusChange(ConnectionStatus status) {
void _onConnectionStatusChange(electrum.ConnectionStatus status) {
switch (status) {
case ConnectionStatus.connected:
case electrum.ConnectionStatus.connected:
if (syncStatus is NotConnectedSyncStatus ||
syncStatus is LostConnectionSyncStatus ||
syncStatus is ConnectingSyncStatus) {
@ -2406,19 +2402,19 @@ abstract class ElectrumWalletBase
}
break;
case ConnectionStatus.disconnected:
case electrum.ConnectionStatus.disconnected:
if (syncStatus is! NotConnectedSyncStatus &&
syncStatus is! ConnectingSyncStatus &&
syncStatus is! SyncronizingSyncStatus) {
syncStatus = NotConnectedSyncStatus();
}
break;
case ConnectionStatus.failed:
case electrum.ConnectionStatus.failed:
if (syncStatus is! LostConnectionSyncStatus) {
syncStatus = LostConnectionSyncStatus();
}
break;
case ConnectionStatus.connecting:
case electrum.ConnectionStatus.connecting:
if (syncStatus is! ConnectingSyncStatus) {
syncStatus = ConnectingSyncStatus();
}
@ -2530,7 +2526,7 @@ class ScanData {
final ScanNode? node;
final BasedUtxoNetwork network;
final int chainTip;
final ElectrumClient electrumClient;
final electrum.ElectrumClient electrumClient;
final List<String> transactionHistoryIds;
final Map<String, String> labels;
final List<int> labelIndexes;
@ -2574,6 +2570,234 @@ class SyncResponse {
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 {
int syncHeight = scanData.height;
int initialSyncHeight = syncHeight;
@ -2586,7 +2810,7 @@ Future<void> startRefresh(ScanData scanData) async {
useSSL: scanData.node?.useSSL ?? false,
);
int getCountPerRequest(int syncHeight) {
int getCountToScanPerRequest(int syncHeight) {
if (scanData.isSingleScan) {
return 1;
}
@ -2601,11 +2825,10 @@ Future<void> startRefresh(ScanData scanData) async {
scanData.silentAddress.B_spend.toHex(),
scanData.network == BitcoinNetwork.testnet,
scanData.labelIndexes,
scanData.labelIndexes.length,
);
// Initial status UI update, send how many blocks in total to scan
final initialCount = getCountPerRequest(syncHeight);
final initialCount = getCountToScanPerRequest(syncHeight);
scanData.sendPort.send(SyncResponse(syncHeight, StartingScanSyncStatus(syncHeight)));
tweaksSubscription = await electrumClient.tweaksSubscribe(
@ -2616,22 +2839,24 @@ Future<void> startRefresh(ScanData scanData) async {
Future<void> listenFn(t) async {
final tweaks = t as Map<String, dynamic>;
final msg = tweaks["message"];
// success or error msg
// is success or error msg
final noData = msg != null;
if (noData) {
if (scanData.isSingleScan) {
return;
}
// re-subscribe to continue receiving messages, starting from the next unscanned height
final nextHeight = syncHeight + 1;
final nextCount = getCountPerRequest(nextHeight);
if (nextCount > 0) {
tweaksSubscription?.close();
final nextTweaksSubscription = electrumClient.tweaksSubscribe(
if (nextHeight <= scanData.chainTip) {
final nextStream = electrumClient.tweaksSubscribe(
height: nextHeight,
count: nextCount,
count: getCountToScanPerRequest(nextHeight),
);
nextTweaksSubscription?.listen(listenFn);
nextStream?.listen(listenFn);
}
return;
@ -2713,7 +2938,7 @@ Future<void> startRefresh(ScanData scanData) async {
isUsed: true,
network: scanData.network,
silentPaymentTweak: t_k,
type: SegwitAddresType.p2tr,
type: SegwitAddressType.p2tr,
txCount: 1,
balance: amount!,
);
@ -2806,15 +3031,15 @@ BitcoinAddressType _getScriptType(BitcoinBaseAddress type) {
} else if (type is P2shAddress) {
return P2shAddressType.p2wpkhInP2sh;
} else if (type is P2wshAddress) {
return SegwitAddresType.p2wsh;
return SegwitAddressType.p2wsh;
} else if (type is P2trAddress) {
return SegwitAddresType.p2tr;
return SegwitAddressType.p2tr;
} else if (type is MwebAddress) {
return SegwitAddresType.mweb;
return SegwitAddressType.mweb;
} else if (type is SilentPaymentsAddresType) {
return SilentPaymentsAddresType.p2sp;
} else {
return SegwitAddresType.p2wpkh;
return SegwitAddressType.p2wpkh;
}
}

View file

@ -17,16 +17,16 @@ part 'electrum_wallet_addresses.g.dart';
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
const List<BitcoinAddressType> BITCOIN_ADDRESS_TYPES = [
SegwitAddresType.p2wpkh,
SegwitAddressType.p2wpkh,
P2pkhAddressType.p2pkh,
SegwitAddresType.p2tr,
SegwitAddresType.p2wsh,
SegwitAddressType.p2tr,
SegwitAddressType.p2wsh,
P2shAddressType.p2wpkhInP2sh,
];
const List<BitcoinAddressType> LITECOIN_ADDRESS_TYPES = [
SegwitAddresType.p2wpkh,
SegwitAddresType.mweb,
SegwitAddressType.p2wpkh,
SegwitAddressType.mweb,
];
const List<BitcoinAddressType> BITCOIN_CASH_ADDRESS_TYPES = [
@ -62,7 +62,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
_addressPageType = initialAddressPageType ??
(walletInfo.addressPageType != null
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
: SegwitAddresType.p2wpkh),
: SegwitAddressType.p2wpkh),
silentAddresses = ObservableList<BitcoinSilentPaymentAddressRecord>.of(
(initialSilentAddresses ?? []).toSet()),
currentSilentAddressIndex = initialSilentAddressIndex,
@ -71,9 +71,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
super(walletInfo) {
if (masterHd != null) {
silentAddress = SilentPaymentOwner.fromPrivateKeys(
b_scan: ECPrivate.fromHex(masterHd.derivePath(SCAN_PATH).privateKey.toHex()),
b_spend: ECPrivate.fromHex(masterHd.derivePath(SPEND_PATH).privateKey.toHex()),
network: network,
b_scan: ECPrivate.fromHex(
masterHd.derivePath("m/352'/1'/0'/1'/0").privateKey.toHex(),
),
b_spend: ECPrivate.fromHex(
masterHd.derivePath("m/352'/1'/0'/0'/0").privateKey.toHex(),
),
);
if (silentAddresses.length == 0) {
@ -144,12 +147,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return silentAddress.toString();
}
final typeMatchingAddresses = _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
final typeMatchingReceiveAddresses = typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
final typeMatchingAddresses =
_addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
final typeMatchingReceiveAddresses =
typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
if (!isEnabledAutoGenerateSubaddress) {
if (previousAddressRecord != null &&
previousAddressRecord!.type == addressPageType) {
if (previousAddressRecord != null && previousAddressRecord!.type == addressPageType) {
return previousAddressRecord!.address;
}
@ -249,17 +253,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (walletInfo.type == WalletType.bitcoinCash) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
} else if (walletInfo.type == WalletType.litecoin) {
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
await _generateInitialAddresses(type: SegwitAddressType.p2wpkh);
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
await _generateInitialAddresses(type: SegwitAddresType.mweb);
await _generateInitialAddresses(type: SegwitAddressType.mweb);
}
} else if (walletInfo.type == WalletType.bitcoin) {
await _generateInitialAddresses();
if (!isHardwareWallet) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
await _generateInitialAddresses(type: SegwitAddressType.p2tr);
await _generateInitialAddresses(type: SegwitAddressType.p2wsh);
}
}
@ -323,7 +327,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {
if (addressPageType == SilentPaymentsAddresType.p2sp && silentAddress != null) {
final currentSilentAddressIndex = silentAddresses
.where((addressRecord) => addressRecord.type != SegwitAddresType.p2tr)
.where((addressRecord) => addressRecord.type != SegwitAddressType.p2tr)
.length -
1;
@ -381,7 +385,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
void addBitcoinAddressTypes() {
final lastP2wpkh = _addresses
.where((addressRecord) =>
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
_isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
.toList()
.last;
if (lastP2wpkh.address != address) {
@ -407,7 +411,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
final lastP2tr = _addresses.firstWhere(
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr));
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2tr));
if (lastP2tr.address != address) {
addressesMap[lastP2tr.address] = 'P2TR';
} else {
@ -415,7 +419,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
final lastP2wsh = _addresses.firstWhere(
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh));
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wsh));
if (lastP2wsh.address != address) {
addressesMap[lastP2wsh.address] = 'P2WSH';
} else {
@ -440,7 +444,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
void addLitecoinAddressTypes() {
final lastP2wpkh = _addresses
.where((addressRecord) =>
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
_isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.p2wpkh))
.toList()
.last;
if (lastP2wpkh.address != address) {
@ -450,7 +454,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
final lastMweb = _addresses.firstWhere(
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb));
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddressType.mweb));
if (lastMweb.address != address) {
addressesMap[lastMweb.address] = 'MWEB';
} else {
@ -560,14 +564,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
addressRecord.isHidden &&
!addressRecord.isUsed &&
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
(walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh));
(walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddressType.p2wpkh));
changeAddresses.addAll(newAddresses);
}
@action
Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
{BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
final newAddresses = await _createNewAddresses(gap,
startIndex: addressList.length, isHidden: isHidden, type: type);
addAddresses(newAddresses);
@ -581,7 +585,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
Future<void> _generateInitialAddresses(
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
{BitcoinAddressType type = SegwitAddressType.p2wpkh}) async {
var countOfReceiveAddresses = 0;
var countOfHiddenAddresses = 0;
@ -658,7 +662,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
void _validateAddresses() {
_addresses.forEach((element) async {
if (element.type == SegwitAddresType.mweb) {
if (element.type == SegwitAddressType.mweb) {
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
return;
}

View file

@ -87,8 +87,8 @@ class ElectrumWalletSnapshot {
final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
var regularAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
var changeAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
var silentAddressIndex = 0;
final derivationType = DerivationType
@ -97,10 +97,10 @@ class ElectrumWalletSnapshot {
try {
regularAddressIndexByType = {
SegwitAddresType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
SegwitAddressType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
};
changeAddressIndexByType = {
SegwitAddresType.p2wpkh.toString():
SegwitAddressType.p2wpkh.toString():
int.parse(data['change_address_index'] as String? ?? '0')
};
silentAddressIndex = int.parse(data['silent_address_index'] as String? ?? '0');

View file

@ -16,7 +16,6 @@ import 'package:fixnum/fixnum.dart';
import 'package:bip39/bip39.dart' as bip39;
import 'package:bitcoin_base/bitcoin_base.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_mnemonic.dart';
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
@ -971,9 +970,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
List<ECPrivateInfo>? inputPrivKeyInfos,
List<Outpoint>? vinOutpoints,
}) async {
bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb);
bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddressType.mweb);
bool paysToMweb = outputs
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb);
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddressType.mweb);
bool isRegular = !spendsMweb && !paysToMweb;
bool isMweb = spendsMweb || paysToMweb;
@ -1064,9 +1063,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
tx.isMweb = mwebEnabled;
if (!mwebEnabled) {
tx.changeAddressOverride =
(await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
.address;
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
.getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
.address;
return tx;
}
await waitForMwebAddresses();
@ -1108,7 +1107,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
// check if mweb inputs are used:
for (final utxo in tx.utxos) {
if (utxo.utxo.scriptType == SegwitAddresType.mweb) {
if (utxo.utxo.scriptType == SegwitAddressType.mweb) {
hasMwebInput = true;
}
}
@ -1119,7 +1118,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
bool isRegular = !hasMwebInput && !hasMwebOutput;
bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
.getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
.getChangeAddress(
coinTypeToSpendFrom:
shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
.address;
if (isRegular) {
tx.isMweb = false;

View file

@ -106,7 +106,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
.map((e) => BitcoinAddressRecord(
e.value,
index: e.key,
type: SegwitAddresType.mweb,
type: SegwitAddressType.mweb,
network: network,
))
.toList();
@ -128,7 +128,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) {
if (addressType == SegwitAddresType.mweb) {
if (addressType == SegwitAddressType.mweb) {
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
}
return generateP2WPKHAddress(hd: hd, index: index, network: network);
@ -140,7 +140,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) async {
if (addressType == SegwitAddresType.mweb) {
if (addressType == SegwitAddressType.mweb) {
await ensureMwebAddressUpToIndexExists(index);
}
return getAddress(index: index, hd: hd, addressType: addressType);
@ -195,7 +195,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
return BitcoinAddressRecord(
mwebAddrs[0],
index: 0,
type: SegwitAddresType.mweb,
type: SegwitAddressType.mweb,
network: network,
);
}
@ -207,7 +207,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
String get addressForExchange {
// don't use mweb addresses for exchange refund address:
final addresses = receiveAddresses
.where((element) => element.type == SegwitAddresType.p2wpkh && !element.isUsed);
.where((element) => element.type == SegwitAddressType.p2wpkh && !element.isUsed);
return addresses.first.address;
}
}

View file

@ -31,8 +31,8 @@ class PayjoinManager {
'https://ohttp.cakewallet.com',
];
static Future<PayjoinUri.Url> randomOhttpRelayUrl() => PayjoinUri.Url.fromStr(
ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
static Future<PayjoinUri.Url> randomOhttpRelayUrl() =>
PayjoinUri.Url.fromStr(ohttpRelayUrls[Random.secure().nextInt(ohttpRelayUrls.length)]);
static const payjoinDirectoryUrl = 'https://payjo.in';
@ -59,8 +59,7 @@ class PayjoinManager {
Future<Sender> initSender(
String pjUriString, String originalPsbt, int networkFeesSatPerVb) async {
try {
final pjUri =
(await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
final pjUri = (await PayjoinUri.Uri.fromStr(pjUriString)).checkPjSupported();
final minFeeRateSatPerKwu = BigInt.from(networkFeesSatPerVb * 250);
final senderBuilder = await SenderBuilder.fromPsbtAndUri(
psbtBase64: originalPsbt,
@ -79,8 +78,7 @@ class PayjoinManager {
bool isTestnet = false,
}) async {
final pjUri = Uri.parse(pjUrl).queryParameters['pj']!;
await _payjoinStorage.insertSenderSession(
sender, pjUri, _wallet.id, amount);
await _payjoinStorage.insertSenderSession(sender, pjUri, _wallet.id, amount);
return _spawnSender(isTestnet: isTestnet, sender: sender, pjUri: pjUri);
}
@ -140,11 +138,9 @@ class PayjoinManager {
return completer.future;
}
Future<Receiver> initReceiver(String address,
[bool isTestnet = false]) async {
Future<Receiver> initReceiver(String address, [bool isTestnet = false]) async {
try {
final payjoinDirectory =
await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
final payjoinDirectory = await PayjoinUri.Url.fromStr(payjoinDirectoryUrl);
final ohttpKeys = await PayjoinUri.fetchOhttpKeys(
ohttpRelay: await randomOhttpRelayUrl(),
@ -199,8 +195,7 @@ class PayjoinManager {
_payjoinStorage.markReceiverSessionInProgress(receiver.id());
final inputScript = message['input_script'] as Uint8List;
final isOwned =
_wallet.isMine(Script.fromRaw(byteData: inputScript));
final isOwned = _wallet.isMine(Script.fromRaw(bytes: inputScript));
mainToIsolateSendPort?.send({
'requestId': message['requestId'],
'result': isOwned,
@ -209,8 +204,7 @@ class PayjoinManager {
case PayjoinReceiverRequestTypes.checkIsReceiverOutput:
final outputScript = message['output_script'] as Uint8List;
final isReceiverOutput =
_wallet.isMine(Script.fromRaw(byteData: outputScript));
final isReceiverOutput = _wallet.isMine(Script.fromRaw(bytes: outputScript));
mainToIsolateSendPort?.send({
'requestId': message['requestId'],
'result': isReceiverOutput,
@ -243,15 +237,13 @@ class PayjoinManager {
}
} catch (e) {
_cleanupSession(receiver.id());
await _payjoinStorage.markReceiverSessionUnrecoverable(
receiver.id(), e.toString());
await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), e.toString());
completer.completeError(e);
}
} else if (message is PayjoinSessionError) {
_cleanupSession(receiver.id());
if (message is UnrecoverableError) {
await _payjoinStorage.markReceiverSessionUnrecoverable(
receiver.id(), message.message);
await _payjoinStorage.markReceiverSessionUnrecoverable(receiver.id(), message.message);
completer.complete();
} else if (message is RecoverableError) {
completer.complete();

View file

@ -40,8 +40,7 @@ extension PsbtSigner on PsbtV2 {
return tx.buffer();
}
Future<void> signWithUTXO(
List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
Future<void> signWithUTXO(List<UtxoWithPrivateKey> utxos, UTXOSignerCallBack signer,
[UTXOGetterCallBack? getTaprootPair]) async {
final raw = BytesUtils.toHexString(extractUnsignedTX(getSegwit: false));
final tx = BtcTransaction.fromRaw(raw);
@ -51,10 +50,10 @@ extension PsbtSigner on PsbtV2 {
List<BigInt> taprootAmounts = [];
List<Script> taprootScripts = [];
if (utxos.any((e) => e.utxo.isP2tr())) {
if (utxos.any((e) => e.utxo.isP2tr)) {
for (final input in tx.inputs) {
final utxo = utxos.firstWhereOrNull(
(u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
final utxo = utxos
.firstWhereOrNull((u) => u.utxo.txHash == input.txId && u.utxo.vout == input.txIndex);
if (utxo == null) {
final trPair = await getTaprootPair!.call(input.txId, input.txIndex);
@ -76,37 +75,29 @@ extension PsbtSigner on PsbtV2 {
/// We receive the owner's ScriptPubKey
final script = _findLockingScript(utxo, false);
final int sighash = utxo.utxo.isP2tr()
? BitcoinOpCodeConst.TAPROOT_SIGHASH_ALL
: BitcoinOpCodeConst.SIGHASH_ALL;
final int sighash =
utxo.utxo.isP2tr ? BitcoinOpCodeConst.sighashDefault : BitcoinOpCodeConst.sighashAll;
/// We generate transaction digest for current input
final digest = _generateTransactionDigest(
script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
final digest =
_generateTransactionDigest(script, i, utxo.utxo, tx, taprootAmounts, taprootScripts);
/// now we need sign the transaction digest
final sig = signer(digest, utxo, utxo.privateKey, sighash);
if (utxo.utxo.isP2tr()) {
if (utxo.utxo.isP2tr) {
setInputTapKeySig(i, Uint8List.fromList(BytesUtils.fromHexString(sig)));
} else {
setInputPartialSig(
i,
Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
setInputPartialSig(i, Uint8List.fromList(BytesUtils.fromHexString(utxo.public().toHex())),
Uint8List.fromList(BytesUtils.fromHexString(sig)));
}
}
}
List<int> _generateTransactionDigest(
Script scriptPubKeys,
int input,
BitcoinUtxo utxo,
BtcTransaction transaction,
List<BigInt> taprootAmounts,
List<Script> tapRootPubKeys) {
if (utxo.isSegwit()) {
if (utxo.isP2tr()) {
List<int> _generateTransactionDigest(Script scriptPubKeys, int input, BitcoinUtxo utxo,
BtcTransaction transaction, List<BigInt> taprootAmounts, List<Script> tapRootPubKeys) {
if (utxo.isSegwit) {
if (utxo.isP2tr) {
return transaction.getTransactionTaprootDigset(
txIndex: input,
scriptPubKeys: tapRootPubKeys,
@ -116,8 +107,7 @@ extension PsbtSigner on PsbtV2 {
return transaction.getTransactionSegwitDigit(
txInIndex: input, script: scriptPubKeys, amount: utxo.value);
}
return transaction.getTransactionDigest(
txInIndex: input, script: scriptPubKeys);
return transaction.getTransactionDigest(txInIndex: input, script: scriptPubKeys);
}
Script _findLockingScript(UtxoWithAddress utxo, bool isTaproot) {
@ -129,23 +119,23 @@ extension PsbtSigner on PsbtV2 {
switch (utxo.utxo.scriptType) {
case PubKeyAddressType.p2pk:
return senderPub.toRedeemScript();
case SegwitAddresType.p2wsh:
case SegwitAddressType.p2wsh:
if (isTaproot) {
return senderPub.toP2wshAddress().toScriptPubKey();
}
return senderPub.toP2wshRedeemScript();
case P2pkhAddressType.p2pkh:
return senderPub.toP2pkhAddress().toScriptPubKey();
case SegwitAddresType.p2wpkh:
case SegwitAddressType.p2wpkh:
if (isTaproot) {
return senderPub.toP2wpkhAddress().toScriptPubKey();
}
return senderPub.toP2pkhAddress().toScriptPubKey();
case SegwitAddresType.p2tr:
case SegwitAddressType.p2tr:
return senderPub
.toTaprootAddress(tweak: utxo.utxo.isSilentPayment != true)
.toScriptPubKey();
case SegwitAddresType.mweb:
case SegwitAddressType.mweb:
return Script(script: []);
case P2shAddressType.p2pkhInP2sh:
if (isTaproot) {
@ -172,11 +162,10 @@ extension PsbtSigner on PsbtV2 {
}
}
typedef UTXOSignerCallBack = String Function(List<int> trDigest,
UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
typedef UTXOSignerCallBack = String Function(
List<int> trDigest, UtxoWithAddress utxo, ECPrivate privateKey, int sighash);
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(
String txId, int vout);
typedef UTXOGetterCallBack = Future<TaprootAmountScriptPair> Function(String txId, int vout);
class TaprootAmountScriptPair {
final BigInt value;
@ -216,23 +205,17 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
}
return UtxoWithPrivateKey(
utxo: input.utxo,
ownerDetails: input.ownerDetails,
privateKey: key.privkey);
utxo: input.utxo, ownerDetails: input.ownerDetails, privateKey: key.privkey);
}
factory UtxoWithPrivateKey.fromUnspent(
BitcoinUnspent input, BitcoinWalletBase wallet) {
final address =
RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
factory UtxoWithPrivateKey.fromUnspent(BitcoinUnspent input, BitcoinWalletBase wallet) {
final address = RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
final newHd =
input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.hd;
final newHd = input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.hd;
ECPrivate privkey;
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
final unspentAddress =
input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
final unspentAddress = input.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
privkey = wallet.walletAddresses.silentAddress!.b_spend.tweakAdd(
BigintUtils.fromBytes(
BytesUtils.fromHexString(unspentAddress.silentPaymentTweak!),
@ -240,9 +223,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
);
} else {
privkey = generateECPrivate(
hd: newHd,
index: input.bitcoinAddressRecord.index,
network: BitcoinNetwork.mainnet);
hd: newHd, index: input.bitcoinAddressRecord.index, network: BitcoinNetwork.mainnet);
}
return UtxoWithPrivateKey(
@ -251,8 +232,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
value: BigInt.from(input.value),
vout: input.vout,
scriptType: input.bitcoinAddressRecord.type,
isSilentPayment:
input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
isSilentPayment: input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord,
),
ownerDetails: UtxoAddressDetails(
publicKey: privkey.getPublic().toHex(),

View file

@ -9,7 +9,9 @@ class PSBTTransactionBuild {
final PsbtV2 psbt = PsbtV2();
PSBTTransactionBuild(
{required List<PSBTReadyUtxoWithAddress> inputs, required List<BitcoinBaseOutput> outputs, bool enableRBF = true}) {
{required List<PSBTReadyUtxoWithAddress> inputs,
required List<BitcoinBaseOutput> outputs,
bool enableRBF = true}) {
psbt.setGlobalTxVersion(2);
psbt.setGlobalInputCount(inputs.length);
psbt.setGlobalOutputCount(outputs.length);
@ -17,20 +19,20 @@ class PSBTTransactionBuild {
for (var i = 0; i < inputs.length; i++) {
final input = inputs[i];
printV(input.utxo.isP2tr());
printV(input.utxo.isSegwit());
printV(input.utxo.isP2shSegwit());
printV(input.utxo.isP2tr);
printV(input.utxo.isSegwit);
printV(input.utxo.isP2shSegwit);
psbt.setInputPreviousTxId(i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
psbt.setInputPreviousTxId(
i, Uint8List.fromList(hex.decode(input.utxo.txHash).reversed.toList()));
psbt.setInputOutputIndex(i, input.utxo.vout);
psbt.setInputSequence(i, enableRBF ? 0x1 : 0xffffffff);
if (input.utxo.isSegwit()) {
if (input.utxo.isSegwit) {
setInputSegwit(i, input);
} else if (input.utxo.isP2shSegwit()) {
} else if (input.utxo.isP2shSegwit) {
setInputP2shSegwit(i, input);
} else if (input.utxo.isP2tr()) {
} else if (input.utxo.isP2tr) {
// ToDo: (Konsti) Handle Taproot Inputs
} else {
setInputP2pkh(i, input);
@ -49,20 +51,14 @@ class PSBTTransactionBuild {
void setInputP2pkh(int i, PSBTReadyUtxoWithAddress input) {
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
psbt.setInputBip32Derivation(
i,
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
input.ownerMasterFingerprint,
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
}
void setInputSegwit(int i, PSBTReadyUtxoWithAddress input) {
psbt.setInputNonWitnessUtxo(i, Uint8List.fromList(hex.decode(input.rawTx)));
psbt.setInputBip32Derivation(
i,
Uint8List.fromList(hex.decode(input.ownerPublicKey)),
input.ownerMasterFingerprint,
BIPPath.fromString(input.ownerDerivationPath).toPathArray());
psbt.setInputBip32Derivation(i, Uint8List.fromList(hex.decode(input.ownerPublicKey)),
input.ownerMasterFingerprint, BIPPath.fromString(input.ownerDerivationPath).toPathArray());
psbt.setInputWitnessUtxo(i, Uint8List.fromList(bigIntToUint64LE(input.utxo.value)),
Uint8List.fromList(input.ownerDetails.address.toScriptPubKey().toBytes()));

View file

@ -21,7 +21,7 @@ String getOutputAmountFromPsbt(String psbtV0, BitcoinWalletBase wallet) {
int amount = 0;
for (var i = 0; i < psbt.getGlobalOutputCount(); i++) {
final script = psbt.getOutputScript(i);
if (wallet.isMine(Script.fromRaw(byteData: script))) {
if (wallet.isMine(Script.fromRaw(bytes: script))) {
amount += psbt.getOutputAmount(i);
}
}

View file

@ -79,20 +79,20 @@ packages:
dependency: "direct overridden"
description:
path: "."
ref: cake-update-v9
resolved-ref: "86969a14e337383e14965f5fb45a72a63e5009bc"
ref: cake-update-v15
resolved-ref: "29160733cbc4ef2c7b8c8fe9ed0297c9bffecfe2"
url: "https://github.com/cake-tech/bitcoin_base"
source: git
version: "4.7.0"
version: "6.1.0"
blockchain_utils:
dependency: "direct main"
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
bluez:
dependency: transitive
description:
@ -681,11 +681,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
ref: cake-update-v4
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:
@ -968,8 +968,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "sp_v4.0.0"
resolved-ref: "2554cb8bd3ee1d026bc63e76a30d1226960c7cb4"
ref: cake-update-v4
resolved-ref: f3c172a7dc5155f5e745e4630b05f197e098a5cd
url: "https://github.com/cake-tech/sp_scanner"
source: git
version: "0.0.1"

View file

@ -29,14 +29,14 @@ dependencies:
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
ref: cake-update-v4
cw_mweb:
path: ../cw_mweb
grpc: ^4.0.1
sp_scanner:
git:
url: https://github.com/cake-tech/sp_scanner
ref: sp_v4.0.0
ref: cake-update-v4
bech32:
git:
url: https://github.com/cake-tech/bech32.git
@ -69,7 +69,7 @@ dependency_overrides:
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v9
ref: cake-update-v15
pointycastle: 3.7.4
ffi: 2.1.0

View file

@ -28,7 +28,7 @@ dependencies:
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
ref: cake-update-v4
dev_dependencies:
flutter_test:
@ -42,7 +42,7 @@ dependency_overrides:
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v9
ref: cake-update-v15
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -1,26 +1,33 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:on_chain/solana/solana.dart';
class SolanaRPCHTTPService implements SolanaJSONRPCService {
class SolanaRPCHTTPService implements SolanaServiceProvider {
SolanaRPCHTTPService(
{required this.url, Client? client, this.defaultRequestTimeout = const Duration(seconds: 30)})
: client = client ?? Client();
@override
final String url;
final Client client;
final Duration defaultRequestTimeout;
@override
Future<Map<String, dynamic>> call(SolanaRequestDetails params, [Duration? timeout]) async {
final response = await client.post(
Uri.parse(url),
body: params.toRequestBody(),
headers: {
'Content-Type': 'application/json',
},
).timeout(timeout ?? defaultRequestTimeout);
final data = json.decode(response.body) as Map<String, dynamic>;
return data;
Future<SolanaServiceResponse<T>> doRequest<T>(SolanaRequestDetails params,
{Duration? timeout}) async {
if (!params.type.isPostRequest) {
final response = await client.get(
params.toUri(url),
headers: {'Content-Type': 'application/json'},
).timeout(timeout ?? defaultRequestTimeout);
return params.toResponse(response.bodyBytes, response.statusCode);
}
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);
}
}

View file

@ -50,11 +50,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
boolean_selector:
dependency: transitive
description:
@ -478,11 +478,11 @@ packages:
dependency: "direct main"
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
ref: cake-update-v4
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:

View file

@ -30,7 +30,7 @@ dependencies:
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
ref: cake-update-v4
# tor:
# git:
# url: https://github.com/cake-tech/tor.git

View file

@ -50,11 +50,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
boolean_selector:
dependency: transitive
description:
@ -501,11 +501,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
ref: cake-update-v4
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:

View file

@ -66,11 +66,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
bluez:
dependency: transitive
description:
@ -598,11 +598,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
ref: cake-update-v4
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:

View file

@ -61,11 +61,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
boolean_selector:
dependency: transitive
description:
@ -550,11 +550,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
ref: cake-update-v4
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:

View file

@ -19,7 +19,7 @@ import '.secrets.g.dart' as secrets;
class SolanaWalletClient {
final httpClient = http.Client();
SolanaRPC? _provider;
SolanaProvider? _provider;
bool connect(Node node) {
try {
@ -38,7 +38,7 @@ class SolanaWalletClient {
formattedUrl = '$protocolUsed://${node.uriRaw}';
}
_provider = SolanaRPC(SolanaRPCHTTPService(url: formattedUrl));
_provider = SolanaProvider(SolanaRPCHTTPService(url: formattedUrl));
return true;
} catch (e) {
@ -49,7 +49,7 @@ class SolanaWalletClient {
Future<double> getBalance(String walletAddress) async {
try {
final balance = await _provider!.requestWithContext(
SolanaRPCGetBalance(
SolanaRequestGetBalance(
account: SolAddress(walletAddress),
),
);
@ -68,11 +68,11 @@ class SolanaWalletClient {
String mintAddress, String publicKey) async {
try {
final result = await _provider!.request(
SolanaRPCGetTokenAccountsByOwner(
SolanaRequestGetTokenAccountsByOwner(
account: SolAddress(publicKey),
mint: SolAddress(mintAddress),
commitment: Commitment.confirmed,
encoding: SolanaRPCEncoding.base64,
encoding: SolanaRequestEncoding.base64,
),
);
@ -96,7 +96,7 @@ class SolanaWalletClient {
for (var tokenAccount in tokenAccounts) {
final tokenAmountResult = await _provider!.request(
SolanaRPCGetTokenAccountBalance(account: tokenAccount.pubkey),
SolanaRequestGetTokenAccountBalance(account: tokenAccount.pubkey),
);
final balance = tokenAmountResult.uiAmountString;
@ -112,7 +112,7 @@ class SolanaWalletClient {
Future<double> getFeeForMessage(String message, Commitment commitment) async {
try {
final feeForMessage = await _provider!.request(
SolanaRPCGetFeeForMessage(
SolanaRequestGetFeeForMessage(
encodedMessage: message,
commitment: commitment,
),
@ -342,7 +342,7 @@ class SolanaWalletClient {
List<SolanaTransactionModel> transactions = [];
try {
final signatures = await _provider!.request(
SolanaRPCGetSignaturesForAddress(
SolanaRequestGetSignaturesForAddress(
account: address,
commitment: commitment,
),
@ -357,9 +357,9 @@ class SolanaWalletClient {
final batchResponses = await Future.wait(batch.map((signature) async {
try {
return await _provider!.request(
SolanaRPCGetTransaction(
SolanaRequestGetTransaction(
transactionSignature: signature['signature'],
encoding: SolanaRPCEncoding.jsonParsed,
encoding: SolanaRequestEncoding.jsonParsed,
maxSupportedTransactionVersion: 0,
),
);
@ -482,7 +482,7 @@ class SolanaWalletClient {
void stop() {}
SolanaRPC? get getSolanaProvider => _provider;
SolanaProvider? get getSolanaProvider => _provider;
Future<PendingSolanaTransaction> signSolanaTransaction({
required String tokenTitle,
@ -523,7 +523,7 @@ class SolanaWalletClient {
Future<SolAddress> _getLatestBlockhash(Commitment commitment) async {
final latestBlockhash = await _provider!.request(
const SolanaRPCGetLatestBlockhash(),
const SolanaRequestGetLatestBlockhash(),
);
return latestBlockhash.blockhash;
@ -599,7 +599,7 @@ class SolanaWalletClient {
required double fee,
}) async {
final rent = await _provider!.request(
SolanaRPCGetMinimumBalanceForRentExemption(
SolanaRequestGetMinimumBalanceForRentExemption(
size: SolanaTokenAccountUtils.accountSize,
),
);
@ -732,7 +732,7 @@ class SolanaWalletClient {
SolanaAccountInfo? accountInfo;
try {
accountInfo = await _provider!.request(
SolanaRPCGetAccountInfo(account: associatedTokenAccount.address),
SolanaRequestGetAccountInfo(account: associatedTokenAccount.address),
);
} catch (e) {
accountInfo = null;
@ -890,7 +890,7 @@ class SolanaWalletClient {
}) async {
/// Sign the transaction with the owner's private key.
final ownerSignature = ownerPrivateKey.sign(transaction.serializeMessage());
transaction.addSignature(ownerPrivateKey.publicKey().toAddress(), ownerSignature);
/// Serialize the transaction.
@ -906,7 +906,7 @@ class SolanaWalletClient {
try {
/// Send the transaction to the Solana network.
final signature = await _provider!.request(
SolanaRPCSendTransaction(
SolanaRequestSendTransaction(
encodedTransaction: serializedTransaction,
commitment: commitment,
),

View file

@ -611,7 +611,7 @@ abstract class SolanaWalletBase
);
}
SolanaRPC? get solanaProvider => _client.getSolanaProvider;
SolanaProvider? get solanaProvider => _client.getSolanaProvider;
@override
String get password => _password;

View file

@ -23,11 +23,11 @@ dependencies:
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
ref: cake-update-v4
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
ref: cake-update-v4
dev_dependencies:
flutter_test:

View file

@ -235,7 +235,7 @@ class TronClient {
String contractAddress = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
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.
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
final contract = ContractABI.fromJson(trc20Abi);
final function = contract.functionFromName("transfer");
@ -405,7 +405,7 @@ class TronClient {
String contractAddress,
BigInt tronBalance,
) async {
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
final contract = ContractABI.fromJson(trc20Abi);
final function = contract.functionFromName("transfer");
@ -483,7 +483,7 @@ class TronClient {
final tokenAddress = TronAddress(contractAddress);
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
final contract = ContractABI.fromJson(trc20Abi);
final function = contract.functionFromName("balanceOf");
@ -510,7 +510,7 @@ class TronClient {
final ownerAddress = TronAddress(userAddress);
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
final contract = ContractABI.fromJson(trc20Abi);
final name =
(await getTokenDetail(contract, "name", ownerAddress, tokenAddress) as String?) ?? '';

View file

@ -1,8 +1,6 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:on_chain/tron/tron.dart';
import '.secrets.g.dart' as secrets;
import 'package:on_chain/tron/tron.dart';
class TronHTTPProvider implements TronServiceProvider {
TronHTTPProvider(
@ -10,34 +8,37 @@ class TronHTTPProvider implements TronServiceProvider {
http.Client? client,
this.defaultRequestTimeout = const Duration(seconds: 30)})
: client = client ?? http.Client();
@override
final String url;
final http.Client client;
final Duration defaultRequestTimeout;
@override
Future<Map<String, dynamic>> get(TronRequestDetails params, [Duration? timeout]) async {
final response = await client.get(Uri.parse(params.url(url)), headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
}).timeout(timeout ?? defaultRequestTimeout);
final data = json.decode(response.body) as Map<String, dynamic>;
return data;
}
Future<TronServiceResponse<T>> doRequest<T>(TronRequestDetails params,
{Duration? timeout}) async {
if (!params.type.isPostRequest) {
final response = await client.get(
params.toUri(url),
headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
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
.post(Uri.parse(params.url(url)),
headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
},
body: params.toRequestBody())
.post(
params.toUri(url),
headers: {
'Content-Type': 'application/json',
if (url.contains("trongrid")) 'TRON-PRO-API-KEY': secrets.tronGridApiKey,
if (url.contains("nownodes")) 'api-key': secrets.tronNowNodesApiKey,
},
body: params.body(),
)
.timeout(timeout ?? defaultRequestTimeout);
final data = json.decode(response.body) as Map<String, dynamic>;
return data;
return params.toResponse(response.bodyBytes, response.statusCode);
}
}

View file

@ -1,5 +1,6 @@
import 'package:blockchain_utils/hex/hex.dart';
import 'package:on_chain/on_chain.dart';
import 'package:on_chain/solidity/address/core.dart';
class TronTRC20TransactionModel extends TronTransactionModel {
String? transactionId;
@ -188,7 +189,7 @@ class Value {
output = output.replaceFirst('0x', '').substring(8);
final abiCoder = ABICoder.fromType('address');
final decoded = abiCoder.decode(AbiParameter.bytes, hex.decode(output));
final tronAddress = TronAddress.fromEthAddress((decoded.result as ETHAddress).toBytes());
final tronAddress = TronAddress.fromEthAddress((decoded.result as SolidityAddress).toBytes());
return tronAddress.toString();
}

View file

@ -31,7 +31,7 @@ import 'package:cw_tron/tron_transaction_info.dart';
import 'package:cw_tron/tron_wallet_addresses.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:on_chain/on_chain.dart';
import 'package:on_chain/on_chain.dart' as on_chain;
part 'tron_wallet.g.dart';
@ -74,13 +74,13 @@ abstract class TronWalletBase
late final Box<TronToken> tronTokensBox;
late final TronPrivateKey _tronPrivateKey;
late final on_chain.TronPrivateKey _tronPrivateKey;
late final TronPublicKey _tronPublicKey;
late final on_chain.TronPublicKey _tronPublicKey;
TronPublicKey get tronPublicKey => _tronPublicKey;
on_chain.TronPublicKey get tronPublicKey => _tronPublicKey;
TronPrivateKey get tronPrivateKey => _tronPrivateKey;
on_chain.TronPrivateKey get tronPrivateKey => _tronPrivateKey;
late String _tronAddress;
@ -190,7 +190,7 @@ abstract class TronWalletBase
String idFor(String name, WalletType type) => '${walletTypeToString(type).toLowerCase()}_$name';
Future<TronPrivateKey> getPrivateKey({
Future<on_chain.TronPrivateKey> getPrivateKey({
String? mnemonic,
String? privateKey,
required String password,
@ -198,7 +198,7 @@ abstract class TronWalletBase
}) async {
assert(mnemonic != null || privateKey != null);
if (privateKey != null) return TronPrivateKey(privateKey);
if (privateKey != null) return on_chain.TronPrivateKey(privateKey);
final seed = bip39.mnemonicToSeed(mnemonic!, passphrase: passphrase ?? '');
@ -207,7 +207,7 @@ abstract class TronWalletBase
final childKey = bip44.deriveDefaultPath;
return TronPrivateKey.fromBytes(childKey.privateKey.raw);
return on_chain.TronPrivateKey.fromBytes(childKey.privateKey.raw);
}
@override
@ -242,10 +242,10 @@ abstract class TronWalletBase
Future<void> _getEstimatedFees() async {
final nativeFee = await _getNativeTxFee();
nativeTxEstimatedFee = TronHelper.fromSun(BigInt.from(nativeFee));
nativeTxEstimatedFee = on_chain.TronHelper.fromSun(BigInt.from(nativeFee));
final trc20Fee = await _getTrc20TxFee();
trc20EstimatedFee = TronHelper.fromSun(BigInt.from(trc20Fee));
trc20EstimatedFee = on_chain.TronHelper.fromSun(BigInt.from(trc20Fee));
log('Native Estimated Fee: $nativeTxEstimatedFee');
log('TRC20 Estimated Fee: $trc20EstimatedFee');
@ -323,7 +323,7 @@ abstract class TronWalletBase
totalAmount = walletBalanceForCurrency;
} else {
final totalOriginalAmount = double.parse(output.cryptoAmount ?? '0.0');
totalAmount = TronHelper.toSun(totalOriginalAmount.toString());
totalAmount = on_chain.TronHelper.toSun(totalOriginalAmount.toString());
}
if (walletBalanceForCurrency < totalAmount || totalAmount < BigInt.zero) {
@ -338,7 +338,7 @@ abstract class TronWalletBase
toAddress: tronCredentials.outputs.first.isParsedAddress
? tronCredentials.outputs.first.extractedAddress!
: tronCredentials.outputs.first.address,
amount: TronHelper.fromSun(totalAmount),
amount: on_chain.TronHelper.fromSun(totalAmount),
currency: transactionCurrency,
tronBalance: tronBalance,
sendAll: shouldSendAll,
@ -355,9 +355,9 @@ abstract class TronWalletBase
final Map<String, TronTransactionInfo> result = {};
final contract = ContractABI.fromJson(trc20Abi, isTron: true);
final contract = on_chain.ContractABI.fromJson(trc20Abi);
final ownerAddress = TronAddress(_tronAddress);
final ownerAddress = on_chain.TronAddress(_tronAddress);
for (var transactionModel in transactions) {
if (transactionModel.isError) {
@ -371,7 +371,7 @@ abstract class TronWalletBase
String? tokenSymbol;
if (transactionModel.contractAddress != null) {
final tokenAddress = TronAddress(transactionModel.contractAddress!);
final tokenAddress = on_chain.TronAddress(transactionModel.contractAddress!);
tokenSymbol = (await _client.getTokenDetail(
contract,
@ -385,9 +385,10 @@ abstract class TronWalletBase
result[transactionModel.hash] = TronTransactionInfo(
id: transactionModel.hash,
tronAmount: transactionModel.amount ?? BigInt.zero,
direction: TronAddress(transactionModel.from!, visible: false).toAddress() == address
? TransactionDirection.outgoing
: TransactionDirection.incoming,
direction:
on_chain.TronAddress(transactionModel.from!, visible: false).toAddress() == address
? TransactionDirection.outgoing
: TransactionDirection.incoming,
blockTime: transactionModel.date,
txFee: transactionModel.fee,
tokenSymbol: tokenSymbol ?? "TRX",
@ -604,11 +605,13 @@ abstract class TronWalletBase
if (address == null) {
return false;
}
TronPublicKey pubKey = TronPublicKey.fromPersonalSignature(ascii.encode(message), signature)!;
on_chain.TronPublicKey pubKey =
on_chain.TronPublicKey.fromPersonalSignature(ascii.encode(message), signature)!;
return pubKey.toAddress().toString() == address;
}
String getTronBase58AddressFromHex(String hexAddress) => TronAddress(hexAddress).toAddress();
String getTronBase58AddressFromHex(String hexAddress) =>
on_chain.TronAddress(hexAddress).toAddress();
void updateScanProviderUsageState(bool isEnabled) {
if (isEnabled) {

View file

@ -18,11 +18,11 @@ dependencies:
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
ref: cake-update-v4
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
ref: cake-update-v4
mobx: ^2.3.0+1
bip39: ^1.0.6
hive: ^2.2.3

View file

@ -45,11 +45,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
boolean_selector:
dependency: transitive
description:
@ -505,11 +505,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
ref: cake-update-v4
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:

View file

@ -45,11 +45,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "59fdf29d72068e0522a96a8953ed7272833a9f57"
ref: cake-update-v4
resolved-ref: "437dadd0bd9bf73ec6a551299577799341f6486a"
url: "https://github.com/cake-tech/blockchain_utils"
source: git
version: "3.3.0"
version: "4.3.0"
boolean_selector:
dependency: transitive
description:
@ -502,11 +502,11 @@ packages:
dependency: transitive
description:
path: "."
ref: cake-update-v2
resolved-ref: "93440dc5126369b873ca1fccc13c3c1240b1c5c2"
ref: cake-update-v4
resolved-ref: "084fb7bf13ec42d74f26ac08c883ce07c10fca7e"
url: "https://github.com/cake-tech/on_chain.git"
source: git
version: "3.7.0"
version: "6.2.0"
package_config:
dependency: transitive
description:

View file

@ -213,9 +213,9 @@ class CWBitcoin extends Bitcoin {
return bitcoinWallet.unspentCoins.where((element) {
switch (coinTypeToSpendFrom) {
case UnspentCoinType.mweb:
return element.bitcoinAddressRecord.type == SegwitAddresType.mweb;
return element.bitcoinAddressRecord.type == SegwitAddressType.mweb;
case UnspentCoinType.nonMweb:
return element.bitcoinAddressRecord.type != SegwitAddresType.mweb;
return element.bitcoinAddressRecord.type != SegwitAddressType.mweb;
case UnspentCoinType.any:
return true;
}
@ -296,14 +296,14 @@ class CWBitcoin extends Bitcoin {
case BitcoinReceivePageOption.p2sh:
return P2shAddressType.p2wpkhInP2sh;
case BitcoinReceivePageOption.p2tr:
return SegwitAddresType.p2tr;
return SegwitAddressType.p2tr;
case BitcoinReceivePageOption.p2wsh:
return SegwitAddresType.p2wsh;
return SegwitAddressType.p2wsh;
case BitcoinReceivePageOption.mweb:
return SegwitAddresType.mweb;
return SegwitAddressType.mweb;
case BitcoinReceivePageOption.p2wpkh:
default:
return SegwitAddresType.p2wpkh;
return SegwitAddressType.p2wpkh;
}
}
@ -527,7 +527,7 @@ class CWBitcoin extends Bitcoin {
List<ElectrumSubAddress> getSilentPaymentAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.silentAddresses
.where((addr) => addr.type != SegwitAddresType.p2tr)
.where((addr) => addr.type != SegwitAddressType.p2tr)
.map((addr) => ElectrumSubAddress(
id: addr.index,
name: addr.name,
@ -542,7 +542,7 @@ class CWBitcoin extends Bitcoin {
List<ElectrumSubAddress> getSilentPaymentReceivedAddresses(Object wallet) {
final bitcoinWallet = wallet as ElectrumWallet;
return bitcoinWallet.walletAddresses.silentAddresses
.where((addr) => addr.type == SegwitAddresType.p2tr)
.where((addr) => addr.type == SegwitAddressType.p2tr)
.map((addr) => ElectrumSubAddress(
id: addr.index,
name: addr.name,
@ -712,7 +712,7 @@ class CWBitcoin extends Bitcoin {
try {
final electrumWallet = wallet as ElectrumWallet;
final segwitAddress = electrumWallet.walletAddresses.allAddresses
.firstWhere((element) => !element.isUsed && element.type == SegwitAddresType.p2wpkh);
.firstWhere((element) => !element.isUsed && element.type == SegwitAddressType.p2wpkh);
return segwitAddress.address;
} catch (_) {
return null;

View file

@ -115,12 +115,12 @@ dependencies:
on_chain:
git:
url: https://github.com/cake-tech/on_chain.git
ref: cake-update-v2
ref: cake-update-v4
reown_walletkit: ^1.1.2
blockchain_utils:
git:
url: https://github.com/cake-tech/blockchain_utils
ref: cake-update-v2
ref: cake-update-v4
flutter_daemon:
git:
url: https://github.com/MrCyjaneK/flutter_daemon
@ -160,7 +160,7 @@ dependency_overrides:
bitcoin_base:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v9
ref: cake-update-v15
ffi: 2.1.0
ledger_flutter_plus:
git:

View file

@ -0,0 +1,76 @@
#!/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()