mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-23 19:05:51 +00:00
untested: ltc refactor
This commit is contained in:
parent
611237ecdf
commit
af25da5a59
20 changed files with 3899 additions and 3674 deletions
|
@ -13,11 +13,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/services/mixins/ordinals_interface.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/show_loading.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
|
||||
|
|
|
@ -13,11 +13,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/mixins/ordinals_interface.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/show_loading.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||
|
@ -213,7 +213,6 @@ class _DesktopOrdinals extends ConsumerState<DesktopOrdinalsView> {
|
|||
isDesktop: true,
|
||||
whileFuture: Future.wait<void>([
|
||||
Future.delayed(const Duration(seconds: 2)),
|
||||
// TODO: [prio=high] FIX CAST as ISSUE
|
||||
(ref.read(pWallets).getWallet(widget.walletId)
|
||||
as OrdinalsInterface)
|
||||
.refreshInscriptions()
|
||||
|
|
|
@ -16,7 +16,6 @@ import 'package:stackwallet/models/node_model.dart';
|
|||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/litecoin/litecoin_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/particl/particl_wallet.dart';
|
||||
|
@ -85,26 +84,10 @@ abstract class CoinServiceAPI {
|
|||
throw UnimplementedError("moved");
|
||||
|
||||
case Coin.litecoin:
|
||||
return LitecoinWallet(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
coin: coin,
|
||||
secureStore: secureStorageInterface,
|
||||
client: client,
|
||||
cachedClient: cachedClient,
|
||||
tracker: tracker,
|
||||
);
|
||||
throw UnimplementedError("moved");
|
||||
|
||||
case Coin.litecoinTestNet:
|
||||
return LitecoinWallet(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
coin: coin,
|
||||
secureStore: secureStorageInterface,
|
||||
client: client,
|
||||
cachedClient: cachedClient,
|
||||
tracker: tracker,
|
||||
);
|
||||
throw UnimplementedError("moved");
|
||||
|
||||
case Coin.bitcoinTestNet:
|
||||
throw UnimplementedError("moved");
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,94 +1,48 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/db/isar/main_db.dart';
|
||||
import 'package:stackwallet/dto/ordinals/inscription_data.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
|
||||
import 'package:stackwallet/models/isar/ordinal.dart';
|
||||
import 'package:stackwallet/services/litescribe_api.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
||||
mixin OrdinalsInterface {
|
||||
late final String _walletId;
|
||||
late final Coin _coin;
|
||||
late final MainDB _db;
|
||||
|
||||
void initOrdinalsInterface({
|
||||
required String walletId,
|
||||
required Coin coin,
|
||||
required MainDB db,
|
||||
}) {
|
||||
_walletId = walletId;
|
||||
_coin = coin;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
final LitescribeAPI litescribeAPI =
|
||||
LitescribeAPI(baseUrl: 'https://litescribe.io/api');
|
||||
|
||||
Future<void> refreshInscriptions() async {
|
||||
final uniqueAddresses = await _db
|
||||
.getUTXOs(_walletId)
|
||||
.filter()
|
||||
.addressIsNotNull()
|
||||
.distinctByAddress()
|
||||
.addressProperty()
|
||||
.findAll();
|
||||
final inscriptions =
|
||||
await _getInscriptionDataFromAddresses(uniqueAddresses.cast<String>());
|
||||
|
||||
final ords = inscriptions
|
||||
.map((e) => Ordinal.fromInscriptionData(e, _walletId))
|
||||
.toList();
|
||||
|
||||
await _db.isar.writeTxn(() async {
|
||||
await _db.isar.ordinals
|
||||
.where()
|
||||
.filter()
|
||||
.walletIdEqualTo(_walletId)
|
||||
.deleteAll();
|
||||
await _db.isar.ordinals.putAll(ords);
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<InscriptionData>> _getInscriptionDataFromAddresses(
|
||||
List<String> addresses) async {
|
||||
List<InscriptionData> allInscriptions = [];
|
||||
for (String address in addresses) {
|
||||
try {
|
||||
var inscriptions =
|
||||
await litescribeAPI.getInscriptionsByAddress(address);
|
||||
allInscriptions.addAll(inscriptions);
|
||||
} catch (e) {
|
||||
throw Exception("Error fetching inscriptions for address $address: $e");
|
||||
}
|
||||
}
|
||||
return allInscriptions;
|
||||
}
|
||||
|
||||
// check if an inscription is in a given <UTXO> output
|
||||
Future<bool> inscriptionInOutput(UTXO output) async {
|
||||
if (output.address != null) {
|
||||
var inscriptions =
|
||||
await litescribeAPI.getInscriptionsByAddress("${output.address}");
|
||||
if (inscriptions.isNotEmpty) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw UnimplementedError(
|
||||
'TODO look up utxo without address. utxo->txid:output->address');
|
||||
}
|
||||
}
|
||||
|
||||
// check if an inscription is in a given <UTXO> output
|
||||
Future<bool> inscriptionInAddress(String address) async {
|
||||
var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
|
||||
if (inscriptions.isNotEmpty) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// late final String _walletId;
|
||||
// late final Coin _coin;
|
||||
// late final MainDB _db;
|
||||
//
|
||||
// void initOrdinalsInterface({
|
||||
// required String walletId,
|
||||
// required Coin coin,
|
||||
// required MainDB db,
|
||||
// }) {
|
||||
// _walletId = walletId;
|
||||
// _coin = coin;
|
||||
// _db = db;
|
||||
// }
|
||||
//
|
||||
// final LitescribeAPI litescribeAPI =
|
||||
// LitescribeAPI(baseUrl: 'https://litescribe.io/api');
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// // // check if an inscription is in a given <UTXO> output
|
||||
// // Future<bool> inscriptionInOutput(UTXO output) async {
|
||||
// // if (output.address != null) {
|
||||
// // var inscriptions =
|
||||
// // await litescribeAPI.getInscriptionsByAddress("${output.address}");
|
||||
// // if (inscriptions.isNotEmpty) {
|
||||
// // return true;
|
||||
// // } else {
|
||||
// // return false;
|
||||
// // }
|
||||
// // } else {
|
||||
// // throw UnimplementedError(
|
||||
// // 'TODO look up utxo without address. utxo->txid:output->address');
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// // check if an inscription is in a given <UTXO> output
|
||||
// Future<bool> inscriptionInAddress(String address) async {
|
||||
// var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
|
||||
// if (inscriptions.isNotEmpty) {
|
||||
// return true;
|
||||
// } else {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -67,11 +67,13 @@ class BitcoinWallet extends Bip39HDWallet
|
|||
}
|
||||
|
||||
@override
|
||||
({String? blockedReason, bool blocked}) checkBlockUTXO(
|
||||
Future<({String? blockedReason, bool blocked, String? utxoLabel})>
|
||||
checkBlockUTXO(
|
||||
Map<String, dynamic> jsonUTXO,
|
||||
String? scriptPubKeyHex,
|
||||
Map<String, dynamic>? jsonTX,
|
||||
) {
|
||||
String? utxoOwnerAddress,
|
||||
) async {
|
||||
bool blocked = false;
|
||||
String? blockedReason;
|
||||
|
||||
|
@ -97,7 +99,7 @@ class BitcoinWallet extends Bip39HDWallet
|
|||
}
|
||||
}
|
||||
|
||||
return (blockedReason: blockedReason, blocked: blocked);
|
||||
return (blockedReason: blockedReason, blocked: blocked, utxoLabel: null);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -303,11 +303,13 @@ class BitcoincashWallet extends Bip39HDWallet
|
|||
}
|
||||
|
||||
@override
|
||||
({String? blockedReason, bool blocked}) checkBlockUTXO(
|
||||
Future<({String? blockedReason, bool blocked, String? utxoLabel})>
|
||||
checkBlockUTXO(
|
||||
Map<String, dynamic> jsonUTXO,
|
||||
String? scriptPubKeyHex,
|
||||
Map<String, dynamic> jsonTX,
|
||||
) {
|
||||
String? utxoOwnerAddress,
|
||||
) async {
|
||||
bool blocked = false;
|
||||
String? blockedReason;
|
||||
|
||||
|
@ -337,7 +339,7 @@ class BitcoincashWallet extends Bip39HDWallet
|
|||
}
|
||||
}
|
||||
|
||||
return (blockedReason: blockedReason, blocked: blocked);
|
||||
return (blockedReason: blockedReason, blocked: blocked, utxoLabel: null);
|
||||
}
|
||||
|
||||
// TODO: correct formula for bch?
|
||||
|
|
|
@ -63,11 +63,13 @@ class DogecoinWallet extends Bip39HDWallet
|
|||
}
|
||||
|
||||
@override
|
||||
({String? blockedReason, bool blocked}) checkBlockUTXO(
|
||||
Future<({String? blockedReason, bool blocked, String? utxoLabel})>
|
||||
checkBlockUTXO(
|
||||
Map<String, dynamic> jsonUTXO,
|
||||
String? scriptPubKeyHex,
|
||||
Map<String, dynamic> jsonTX,
|
||||
) {
|
||||
String? utxoOwnerAddress,
|
||||
) async {
|
||||
bool blocked = false;
|
||||
String? blockedReason;
|
||||
|
||||
|
@ -91,7 +93,7 @@ class DogecoinWallet extends Bip39HDWallet
|
|||
}
|
||||
}
|
||||
|
||||
return (blockedReason: blockedReason, blocked: blocked);
|
||||
return (blockedReason: blockedReason, blocked: blocked, utxoLabel: null);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -298,11 +298,13 @@ class EcashWallet extends Bip39HDWallet
|
|||
}
|
||||
|
||||
@override
|
||||
({String? blockedReason, bool blocked}) checkBlockUTXO(
|
||||
Future<({String? blockedReason, bool blocked, String? utxoLabel})>
|
||||
checkBlockUTXO(
|
||||
Map<String, dynamic> jsonUTXO,
|
||||
String? scriptPubKeyHex,
|
||||
Map<String, dynamic> jsonTX,
|
||||
) {
|
||||
String? utxoOwnerAddress,
|
||||
) async {
|
||||
bool blocked = false;
|
||||
String? blockedReason;
|
||||
|
||||
|
@ -332,7 +334,7 @@ class EcashWallet extends Bip39HDWallet
|
|||
}
|
||||
}
|
||||
|
||||
return (blockedReason: blockedReason, blocked: blocked);
|
||||
return (blockedReason: blockedReason, blocked: blocked, utxoLabel: null);
|
||||
}
|
||||
|
||||
// TODO: correct formula for ecash?
|
||||
|
|
|
@ -57,13 +57,13 @@ class EpiccashWallet extends Bip39Wallet {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> updateUTXOs() {
|
||||
Future<bool> updateUTXOs() {
|
||||
// TODO: implement updateUTXOs
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateNode() {
|
||||
Future<bool> updateNode() {
|
||||
// TODO: implement updateNode
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
|
|
@ -494,11 +494,13 @@ class FiroWallet extends Bip39HDWallet
|
|||
}
|
||||
|
||||
@override
|
||||
({String? blockedReason, bool blocked}) checkBlockUTXO(
|
||||
Future<({String? blockedReason, bool blocked, String? utxoLabel})>
|
||||
checkBlockUTXO(
|
||||
Map<String, dynamic> jsonUTXO,
|
||||
String? scriptPubKeyHex,
|
||||
Map<String, dynamic>? jsonTX,
|
||||
) {
|
||||
String? utxoOwnerAddress,
|
||||
) async {
|
||||
bool blocked = false;
|
||||
String? blockedReason;
|
||||
//
|
||||
|
@ -524,7 +526,7 @@ class FiroWallet extends Bip39HDWallet
|
|||
// }
|
||||
// }
|
||||
//
|
||||
return (blockedReason: blockedReason, blocked: blocked);
|
||||
return (blockedReason: blockedReason, blocked: blocked, utxoLabel: null);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
143
lib/wallets/wallet/impl/litecoin_wallet.dart
Normal file
143
lib/wallets/wallet/impl/litecoin_wallet.dart
Normal file
|
@ -0,0 +1,143 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/coins/litecoin.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/ordinals_interface.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class LitecoinWallet extends Bip39HDWallet
|
||||
with ElectrumXInterface, CoinControlInterface, OrdinalsInterface {
|
||||
@override
|
||||
int get isarTransactionVersion => 1; // TODO actually set this to 2
|
||||
|
||||
LitecoinWallet(CryptoCurrencyNetwork network) : super(Litecoin(network));
|
||||
|
||||
@override
|
||||
FilterOperation? get changeAddressFilterOperation =>
|
||||
FilterGroup.and(standardChangeAddressFilters);
|
||||
|
||||
@override
|
||||
FilterOperation? get receivingAddressFilterOperation =>
|
||||
FilterGroup.and(standardReceivingAddressFilters);
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
@override
|
||||
Future<List<Address>> fetchAddressesForElectrumXScan() async {
|
||||
final allAddresses = await mainDB
|
||||
.getAddresses(walletId)
|
||||
.filter()
|
||||
.not()
|
||||
.group(
|
||||
(q) => q
|
||||
.typeEqualTo(AddressType.nonWallet)
|
||||
.or()
|
||||
.subTypeEqualTo(AddressSubType.nonWallet),
|
||||
)
|
||||
.findAll();
|
||||
return allAddresses;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
@override
|
||||
Future<void> updateTransactions() async {
|
||||
final currentChainHeight = await fetchChainHeight();
|
||||
|
||||
// TODO: [prio=med] switch to V2 transactions
|
||||
final data = await fetchTransactionsV1(
|
||||
addresses: await fetchAddressesForElectrumXScan(),
|
||||
currentChainHeight: currentChainHeight,
|
||||
);
|
||||
|
||||
await mainDB.addNewTransactionData(
|
||||
data
|
||||
.map((e) => Tuple2(
|
||||
e.transaction,
|
||||
e.address,
|
||||
))
|
||||
.toList(),
|
||||
walletId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
|
||||
return Amount(
|
||||
rawValue: BigInt.from(
|
||||
((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
|
||||
(feeRatePerKB / 1000).ceil()),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
int estimateTxFee({required int vSize, required int feeRatePerKB}) {
|
||||
return vSize * (feeRatePerKB / 1000).ceil();
|
||||
}
|
||||
//
|
||||
// @override
|
||||
// Future<TxData> coinSelection({required TxData txData}) async {
|
||||
// final isCoinControl = txData.utxos != null;
|
||||
// final isSendAll = txData.amount == info.cachedBalance.spendable;
|
||||
//
|
||||
// final utxos =
|
||||
// txData.utxos?.toList() ?? await mainDB.getUTXOs(walletId).findAll();
|
||||
//
|
||||
// final currentChainHeight = await chainHeight;
|
||||
// final List<UTXO> spendableOutputs = [];
|
||||
// int spendableSatoshiValue = 0;
|
||||
//
|
||||
// // Build list of spendable outputs and totaling their satoshi amount
|
||||
// for (final utxo in utxos) {
|
||||
// if (utxo.isBlocked == false &&
|
||||
// utxo.isConfirmed(currentChainHeight, cryptoCurrency.minConfirms) &&
|
||||
// utxo.used != true) {
|
||||
// spendableOutputs.add(utxo);
|
||||
// spendableSatoshiValue += utxo.value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (isCoinControl && spendableOutputs.length < utxos.length) {
|
||||
// throw ArgumentError("Attempted to use an unavailable utxo");
|
||||
// }
|
||||
//
|
||||
// if (spendableSatoshiValue < txData.amount!.raw.toInt()) {
|
||||
// throw Exception("Insufficient balance");
|
||||
// } else if (spendableSatoshiValue == txData.amount!.raw.toInt() &&
|
||||
// !isSendAll) {
|
||||
// throw Exception("Insufficient balance to pay transaction fee");
|
||||
// }
|
||||
//
|
||||
// if (isCoinControl) {
|
||||
// } else {
|
||||
// final selection = cs.coinSelection(
|
||||
// spendableOutputs
|
||||
// .map((e) => cs.InputModel(
|
||||
// i: e.vout,
|
||||
// txid: e.txid,
|
||||
// value: e.value,
|
||||
// address: e.address,
|
||||
// ))
|
||||
// .toList(),
|
||||
// txData.recipients!
|
||||
// .map((e) => cs.OutputModel(
|
||||
// address: e.address,
|
||||
// value: e.amount.raw.toInt(),
|
||||
// ))
|
||||
// .toList(),
|
||||
// txData.feeRateAmount!,
|
||||
// 10, // TODO: ???????????????????????????????
|
||||
// );
|
||||
//
|
||||
// // .inputs and .outputs will be null if no solution was found
|
||||
// if (selection.inputs!.isEmpty || selection.outputs!.isEmpty) {
|
||||
// throw Exception("coin selection failed");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -599,7 +599,8 @@ class TezosWallet extends Bip39Wallet<Tezos> {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> updateUTXOs() async {
|
||||
Future<bool> updateUTXOs() async {
|
||||
// do nothing. Not used in tezos
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:stackwallet/wallets/crypto_currency/intermediate/cryptonote_currency.dart';
|
||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
||||
|
||||
abstract class CryptonoteWallet<T extends CryptonoteCurrency> extends Wallet<T>
|
||||
with MnemonicInterface<T> {
|
||||
|
@ -28,7 +28,8 @@ abstract class CryptonoteWallet<T extends CryptonoteCurrency> extends Wallet<T>
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> updateUTXOs() async {
|
||||
Future<bool> updateUTXOs() async {
|
||||
// do nothing for now
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import 'package:stackwallet/wallets/wallet/impl/dogecoin_wallet.dart';
|
|||
import 'package:stackwallet/wallets/wallet/impl/ecash_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/litecoin_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/nano_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/tezos_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/wownero_wallet.dart';
|
||||
|
@ -273,6 +274,11 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
case Coin.firoTestNet:
|
||||
return FiroWallet(CryptoCurrencyNetwork.test);
|
||||
|
||||
case Coin.litecoin:
|
||||
return LitecoinWallet(CryptoCurrencyNetwork.main);
|
||||
case Coin.litecoinTestNet:
|
||||
return LitecoinWallet(CryptoCurrencyNetwork.test);
|
||||
|
||||
case Coin.nano:
|
||||
return NanoWallet(CryptoCurrencyNetwork.main);
|
||||
|
||||
|
@ -360,9 +366,11 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
Future<void> updateNode();
|
||||
|
||||
Future<void> updateTransactions();
|
||||
Future<void> updateUTXOs();
|
||||
Future<void> updateBalance();
|
||||
|
||||
// returns true if new utxos were added to local db
|
||||
Future<bool> updateUTXOs();
|
||||
|
||||
/// updates the wallet info's cachedChainHeight
|
||||
Future<void> updateChainHeight();
|
||||
|
||||
|
|
|
@ -647,6 +647,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
utxoSigningData[i].utxo.vout,
|
||||
null,
|
||||
utxoSigningData[i].output!,
|
||||
cryptoCurrency.networkParams.bech32Hrp,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -655,6 +656,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
txb.addOutput(
|
||||
txData.recipients![i].address,
|
||||
txData.recipients![i].amount.raw.toInt(),
|
||||
cryptoCurrency.networkParams.bech32Hrp,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -666,6 +668,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
keyPair: utxoSigningData[i].keyPair!,
|
||||
witnessValue: utxoSigningData[i].utxo.value,
|
||||
redeemScript: utxoSigningData[i].redeemScript,
|
||||
overridePrefix: cryptoCurrency.networkParams.bech32Hrp,
|
||||
);
|
||||
}
|
||||
} catch (e, s) {
|
||||
|
@ -674,7 +677,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
rethrow;
|
||||
}
|
||||
|
||||
final builtTx = txb.build();
|
||||
final builtTx = txb.build(cryptoCurrency.networkParams.bech32Hrp);
|
||||
final vSize = builtTx.virtualSize();
|
||||
|
||||
return txData.copyWith(
|
||||
|
@ -1051,14 +1054,19 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
final checkBlockResult = checkBlockUTXO(jsonUTXO, scriptPubKey, txn);
|
||||
final checkBlockResult = await checkBlockUTXO(
|
||||
jsonUTXO,
|
||||
scriptPubKey,
|
||||
txn,
|
||||
utxoOwnerAddress,
|
||||
);
|
||||
|
||||
final utxo = UTXO(
|
||||
walletId: walletId,
|
||||
txid: txn["txid"] as String,
|
||||
vout: vout,
|
||||
value: jsonUTXO["value"] as int,
|
||||
name: "",
|
||||
name: checkBlockResult.utxoLabel ?? "",
|
||||
isBlocked: checkBlockResult.blocked,
|
||||
blockedReason: checkBlockResult.blockedReason,
|
||||
isCoinbase: txn["is_coinbase"] as bool? ?? false,
|
||||
|
@ -1650,7 +1658,7 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> updateUTXOs() async {
|
||||
Future<bool> updateUTXOs() async {
|
||||
final allAddresses = await fetchAddressesForElectrumXScan();
|
||||
|
||||
try {
|
||||
|
@ -1710,12 +1718,13 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
}
|
||||
}
|
||||
|
||||
await mainDB.updateUTXOs(walletId, outputArray);
|
||||
return await mainDB.updateUTXOs(walletId, outputArray);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Output fetch unsuccessful: $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1870,10 +1879,12 @@ mixin ElectrumXInterface on Bip39HDWallet {
|
|||
|
||||
/// Certain coins need to check if the utxo should be marked
|
||||
/// as blocked as well as give a reason.
|
||||
({String? blockedReason, bool blocked}) checkBlockUTXO(
|
||||
Future<({String? blockedReason, bool blocked, String? utxoLabel})>
|
||||
checkBlockUTXO(
|
||||
Map<String, dynamic> jsonUTXO,
|
||||
String? scriptPubKeyHex,
|
||||
Map<String, dynamic> jsonTX,
|
||||
String? utxoOwnerAddress,
|
||||
);
|
||||
|
||||
// ===========================================================================
|
||||
|
|
|
@ -651,8 +651,9 @@ mixin NanoInterface<T extends NanoCurrency> on Bip39Wallet<T> {
|
|||
FilterGroup.and(standardReceivingAddressFilters);
|
||||
|
||||
@override
|
||||
Future<void> updateUTXOs() async {
|
||||
Future<bool> updateUTXOs() async {
|
||||
// do nothing for nano based coins
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/dto/ordinals/inscription_data.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
|
||||
import 'package:stackwallet/models/isar/ordinal.dart';
|
||||
import 'package:stackwallet/services/litescribe_api.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||
|
||||
mixin OrdinalsInterface on ElectrumXInterface {
|
||||
final LitescribeAPI litescribeAPI =
|
||||
LitescribeAPI(baseUrl: 'https://litescribe.io/api');
|
||||
|
||||
// check if an inscription is in a given <UTXO> output
|
||||
Future<bool> _inscriptionInAddress(String address) async {
|
||||
var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
|
||||
if (inscriptions.isNotEmpty) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refreshInscriptions() async {
|
||||
final uniqueAddresses = await mainDB
|
||||
.getUTXOs(walletId)
|
||||
.filter()
|
||||
.addressIsNotNull()
|
||||
.distinctByAddress()
|
||||
.addressProperty()
|
||||
.findAll();
|
||||
final inscriptions =
|
||||
await _getInscriptionDataFromAddresses(uniqueAddresses.cast<String>());
|
||||
|
||||
final ords = inscriptions
|
||||
.map((e) => Ordinal.fromInscriptionData(e, walletId))
|
||||
.toList();
|
||||
|
||||
await mainDB.isar.writeTxn(() async {
|
||||
await mainDB.isar.ordinals
|
||||
.where()
|
||||
.filter()
|
||||
.walletIdEqualTo(walletId)
|
||||
.deleteAll();
|
||||
await mainDB.isar.ordinals.putAll(ords);
|
||||
});
|
||||
}
|
||||
// =================== Overrides =============================================
|
||||
|
||||
@override
|
||||
Future<({bool blocked, String? blockedReason, String? utxoLabel})>
|
||||
checkBlockUTXO(
|
||||
Map<String, dynamic> jsonUTXO,
|
||||
String? scriptPubKeyHex,
|
||||
Map<String, dynamic> jsonTX,
|
||||
String? utxoOwnerAddress,
|
||||
) async {
|
||||
bool shouldBlock = false;
|
||||
String? blockReason;
|
||||
String? label;
|
||||
|
||||
final utxoAmount = jsonUTXO["value"] as int;
|
||||
|
||||
// TODO: [prio=med] check following 3 todos
|
||||
|
||||
// TODO check the specific output, not just the address in general
|
||||
// TODO optimize by freezing output in OrdinalsInterface, so one ordinal API calls is made (or at least many less)
|
||||
if (utxoOwnerAddress != null) {
|
||||
if (await _inscriptionInAddress(utxoOwnerAddress)) {
|
||||
shouldBlock = true;
|
||||
blockReason = "Ordinal";
|
||||
label = "Ordinal detected at address";
|
||||
}
|
||||
} else {
|
||||
// TODO implement inscriptionInOutput
|
||||
if (utxoAmount <= 10000) {
|
||||
shouldBlock = true;
|
||||
blockReason = "May contain ordinal";
|
||||
label = "Possible ordinal";
|
||||
}
|
||||
}
|
||||
|
||||
return (blockedReason: blockReason, blocked: shouldBlock, utxoLabel: label);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> updateUTXOs() async {
|
||||
final newUtxosAdded = await super.updateUTXOs();
|
||||
if (newUtxosAdded) {
|
||||
await refreshInscriptions();
|
||||
}
|
||||
|
||||
return newUtxosAdded;
|
||||
}
|
||||
|
||||
// ===================== Private =============================================
|
||||
Future<List<InscriptionData>> _getInscriptionDataFromAddresses(
|
||||
List<String> addresses) async {
|
||||
List<InscriptionData> allInscriptions = [];
|
||||
for (String address in addresses) {
|
||||
try {
|
||||
var inscriptions =
|
||||
await litescribeAPI.getInscriptionsByAddress(address);
|
||||
allInscriptions.addAll(inscriptions);
|
||||
} catch (e) {
|
||||
throw Exception("Error fetching inscriptions for address $address: $e");
|
||||
}
|
||||
}
|
||||
return allInscriptions;
|
||||
}
|
||||
}
|
|
@ -280,7 +280,7 @@ packages:
|
|||
source: git
|
||||
version: "1.1.0"
|
||||
coinlib_flutter:
|
||||
dependency: "direct overridden"
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
path: coinlib_flutter
|
||||
ref: "4f549b8b511a63fdc1f44796ab43b10f586635cd"
|
||||
|
|
|
@ -186,6 +186,11 @@ dev_dependencies:
|
|||
import_sorter: ^4.6.0
|
||||
flutter_lints: ^2.0.1
|
||||
isar_generator: 3.0.5
|
||||
coinlib_flutter:
|
||||
git:
|
||||
url: https://github.com/cypherstack/coinlib.git
|
||||
path: coinlib_flutter
|
||||
ref: 4f549b8b511a63fdc1f44796ab43b10f586635cd
|
||||
|
||||
flutter_launcher_icons:
|
||||
android: true
|
||||
|
|
Loading…
Reference in a new issue