mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-16 17:27:39 +00:00
WIP bitcoin frost wallet addition
This commit is contained in:
parent
755cc049b0
commit
85b66fd849
26 changed files with 1492 additions and 61 deletions
|
@ -163,6 +163,7 @@ enum AddressType {
|
|||
spark,
|
||||
stellar,
|
||||
tezos,
|
||||
frostMS,
|
||||
;
|
||||
|
||||
String get readableName {
|
||||
|
@ -193,6 +194,8 @@ enum AddressType {
|
|||
return "Stellar";
|
||||
case AddressType.tezos:
|
||||
return "Tezos";
|
||||
case AddressType.frostMS:
|
||||
return "FrostMS";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,6 +266,7 @@ const _AddresstypeEnumValueMap = {
|
|||
'spark': 10,
|
||||
'stellar': 11,
|
||||
'tezos': 12,
|
||||
'frostMS': 13,
|
||||
};
|
||||
const _AddresstypeValueEnumMap = {
|
||||
0: AddressType.p2pkh,
|
||||
|
@ -281,6 +282,7 @@ const _AddresstypeValueEnumMap = {
|
|||
10: AddressType.spark,
|
||||
11: AddressType.stellar,
|
||||
12: AddressType.tezos,
|
||||
13: AddressType.frostMS,
|
||||
};
|
||||
|
||||
Id _addressGetId(Address object) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
|
|||
import 'package:stackwallet/wallets/isar/providers/eth/current_token_wallet_provider.dart';
|
||||
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
|
||||
final feeSheetSessionCacheProvider =
|
||||
|
@ -697,7 +698,7 @@ class _TransactionFeeSelectionSheetState
|
|||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
if (coin.isElectrumXCoin)
|
||||
if (wallet is ElectrumXInterface)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final state =
|
||||
|
@ -766,7 +767,7 @@ class _TransactionFeeSelectionSheetState
|
|||
),
|
||||
),
|
||||
),
|
||||
if (coin.isElectrumXCoin)
|
||||
if (wallet is ElectrumXInterface)
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
|
|
|
@ -166,6 +166,8 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.particl:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
|
@ -757,6 +759,8 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
case Coin.eCash:
|
||||
case Coin.stellar:
|
||||
case Coin.stellarTestnet:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return false;
|
||||
|
||||
case Coin.ethereum:
|
||||
|
|
|
@ -148,6 +148,8 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.eCash:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
final client = ElectrumXClient(
|
||||
host: node!.host,
|
||||
port: node.port,
|
||||
|
|
|
@ -52,6 +52,7 @@ import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
|
|||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/firo_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/paynym_interface.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
|
@ -1566,7 +1567,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
if (!([Coin.nano, Coin.banano, Coin.epicCash, Coin.tezos]
|
||||
.contains(coin)))
|
||||
ConditionalParent(
|
||||
condition: coin.isElectrumXCoin &&
|
||||
condition: ref.watch(pWallets).getWallet(walletId)
|
||||
is ElectrumXInterface &&
|
||||
!(((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
(ref.watch(publicPrivateBalanceStateProvider.state).state ==
|
||||
FiroType.lelantus ||
|
||||
|
|
|
@ -24,6 +24,7 @@ import 'package:stackwallet/services/wallets.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
|
||||
|
||||
import 'exchange/exchange.dart';
|
||||
|
||||
|
@ -123,7 +124,7 @@ class NotificationsService extends ChangeNotifier {
|
|||
|
||||
final node = nodeService.getPrimaryNodeFor(coin: coin);
|
||||
if (node != null) {
|
||||
if (coin.isElectrumXCoin) {
|
||||
if (wallet is ElectrumXInterface) {
|
||||
final eNode = ElectrumXNode(
|
||||
address: node.host,
|
||||
port: node.port,
|
||||
|
|
|
@ -37,6 +37,8 @@ class CoinThemeColorDefault {
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return bitcoin;
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
|
|
|
@ -1680,6 +1680,8 @@ class StackColors extends ThemeExtension<StackColors> {
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return _coin.bitcoin;
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
|
|
|
@ -40,6 +40,8 @@ enum AmountUnit {
|
|||
case Coin.litecoin:
|
||||
case Coin.particl:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
|
|
|
@ -18,6 +18,7 @@ Uri getDefaultBlockExplorerUrlFor({
|
|||
required String txid,
|
||||
}) {
|
||||
switch (coin) {
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoin:
|
||||
return Uri.parse("https://mempool.space/tx/$txid");
|
||||
case Coin.litecoin:
|
||||
|
@ -25,6 +26,7 @@ Uri getDefaultBlockExplorerUrlFor({
|
|||
case Coin.litecoinTestNet:
|
||||
return Uri.parse("https://chain.so/tx/LTCTEST/$txid");
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return Uri.parse("https://mempool.space/testnet/tx/$txid");
|
||||
case Coin.dogecoin:
|
||||
return Uri.parse("https://chain.so/tx/DOGE/$txid");
|
||||
|
|
|
@ -69,6 +69,7 @@ abstract class Constants {
|
|||
static BigInt satsPerCoin(Coin coin) {
|
||||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincash:
|
||||
|
@ -76,6 +77,7 @@ abstract class Constants {
|
|||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.firoTestNet:
|
||||
case Coin.epicCash:
|
||||
|
@ -113,6 +115,7 @@ abstract class Constants {
|
|||
static int decimalPlacesForCoin(Coin coin) {
|
||||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.litecoin:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincash:
|
||||
|
@ -120,6 +123,7 @@ abstract class Constants {
|
|||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.firoTestNet:
|
||||
case Coin.epicCash:
|
||||
|
@ -189,6 +193,10 @@ abstract class Constants {
|
|||
case Coin.wownero:
|
||||
values.addAll([14, 25]);
|
||||
break;
|
||||
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
throw ArgumentError("Frost mnemonic lengths unsupported");
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
@ -198,6 +206,8 @@ abstract class Constants {
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.eCash:
|
||||
|
@ -277,6 +287,10 @@ abstract class Constants {
|
|||
|
||||
case Coin.monero:
|
||||
return 25;
|
||||
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
throw ArgumentError("Frost mnemonic length unsupported");
|
||||
//
|
||||
// default:
|
||||
// -1;
|
||||
|
|
|
@ -312,6 +312,7 @@ abstract class DefaultNodes {
|
|||
static NodeModel getNodeFor(Coin coin) {
|
||||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinFrost:
|
||||
return bitcoin;
|
||||
|
||||
case Coin.litecoin:
|
||||
|
@ -360,6 +361,7 @@ abstract class DefaultNodes {
|
|||
return tezos;
|
||||
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return bitcoinTestnet;
|
||||
|
||||
case Coin.litecoinTestNet:
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:stackwallet/utilities/constants.dart';
|
|||
|
||||
enum Coin {
|
||||
bitcoin,
|
||||
bitcoinFrost,
|
||||
monero,
|
||||
banano,
|
||||
bitcoincash,
|
||||
|
@ -35,6 +36,7 @@ enum Coin {
|
|||
///
|
||||
|
||||
bitcoinTestNet,
|
||||
bitcoinFrostTestNet,
|
||||
bitcoincashTestnet,
|
||||
dogecoinTestNet,
|
||||
firoTestNet,
|
||||
|
@ -47,6 +49,8 @@ extension CoinExt on Coin {
|
|||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
return "Bitcoin";
|
||||
case Coin.bitcoinFrost:
|
||||
return "Bitcoin Frost";
|
||||
case Coin.litecoin:
|
||||
return "Litecoin";
|
||||
case Coin.bitcoincash:
|
||||
|
@ -79,6 +83,8 @@ extension CoinExt on Coin {
|
|||
return "Banano";
|
||||
case Coin.bitcoinTestNet:
|
||||
return "tBitcoin";
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return "tBitcoin Frost";
|
||||
case Coin.litecoinTestNet:
|
||||
return "tLitecoin";
|
||||
case Coin.bitcoincashTestnet:
|
||||
|
@ -95,6 +101,7 @@ extension CoinExt on Coin {
|
|||
String get ticker {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinFrost:
|
||||
return "BTC";
|
||||
case Coin.litecoin:
|
||||
return "LTC";
|
||||
|
@ -127,6 +134,7 @@ extension CoinExt on Coin {
|
|||
case Coin.banano:
|
||||
return "BAN";
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return "tBTC";
|
||||
case Coin.litecoinTestNet:
|
||||
return "tLTC";
|
||||
|
@ -144,6 +152,7 @@ extension CoinExt on Coin {
|
|||
String get uriScheme {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinFrost:
|
||||
return "bitcoin";
|
||||
case Coin.litecoin:
|
||||
return "litecoin";
|
||||
|
@ -177,6 +186,7 @@ extension CoinExt on Coin {
|
|||
case Coin.banano:
|
||||
return "ban";
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return "bitcoin";
|
||||
case Coin.litecoinTestNet:
|
||||
return "litecoin";
|
||||
|
@ -191,36 +201,6 @@ extension CoinExt on Coin {
|
|||
}
|
||||
}
|
||||
|
||||
bool get isElectrumXCoin {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.litecoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.particl:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.firoTestNet:
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.eCash:
|
||||
return true;
|
||||
|
||||
case Coin.epicCash:
|
||||
case Coin.ethereum:
|
||||
case Coin.monero:
|
||||
case Coin.tezos:
|
||||
case Coin.wownero:
|
||||
case Coin.nano:
|
||||
case Coin.banano:
|
||||
case Coin.stellar:
|
||||
case Coin.stellarTestnet:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool get hasMnemonicPassphraseSupport {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
|
@ -241,6 +221,8 @@ extension CoinExt on Coin {
|
|||
case Coin.stellarTestnet:
|
||||
return true;
|
||||
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.epicCash:
|
||||
case Coin.monero:
|
||||
case Coin.wownero:
|
||||
|
@ -260,6 +242,8 @@ extension CoinExt on Coin {
|
|||
case Coin.ethereum:
|
||||
return true;
|
||||
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.particl:
|
||||
|
@ -284,6 +268,7 @@ extension CoinExt on Coin {
|
|||
bool get isTestNet {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.litecoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.dogecoin:
|
||||
|
@ -303,6 +288,7 @@ extension CoinExt on Coin {
|
|||
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.litecoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.firoTestNet:
|
||||
|
@ -314,6 +300,7 @@ extension CoinExt on Coin {
|
|||
Coin get mainNetVersion {
|
||||
switch (this) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.litecoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.dogecoin:
|
||||
|
@ -337,6 +324,9 @@ extension CoinExt on Coin {
|
|||
case Coin.bitcoinTestNet:
|
||||
return Coin.bitcoin;
|
||||
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return Coin.bitcoinFrost;
|
||||
|
||||
case Coin.litecoinTestNet:
|
||||
return Coin.litecoin;
|
||||
|
||||
|
@ -364,6 +354,10 @@ extension CoinExt on Coin {
|
|||
case Coin.particl:
|
||||
return AddressType.p2wpkh;
|
||||
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return AddressType.frostMS;
|
||||
|
||||
case Coin.eCash:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
|
|
|
@ -44,6 +44,8 @@ extension DerivePathTypeExt on DerivePathType {
|
|||
case Coin.ethereum: // TODO: do we need something here?
|
||||
return DerivePathType.eth;
|
||||
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
case Coin.epicCash:
|
||||
case Coin.monero:
|
||||
case Coin.wownero:
|
||||
|
|
|
@ -170,30 +170,10 @@ class Bitcoin extends Bip39HDCurrency with PaynymCurrencyInterface {
|
|||
NodeModel get defaultNode {
|
||||
switch (network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
return NodeModel(
|
||||
host: "bitcoin.stackwallet.com",
|
||||
port: 50002,
|
||||
name: DefaultNodes.defaultName,
|
||||
id: DefaultNodes.buildId(Coin.bitcoin),
|
||||
useSSL: true,
|
||||
enabled: true,
|
||||
coinName: Coin.bitcoin.name,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
);
|
||||
return DefaultNodes.bitcoin;
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
return NodeModel(
|
||||
host: "bitcoin-testnet.stackwallet.com",
|
||||
port: 51002,
|
||||
name: DefaultNodes.defaultName,
|
||||
id: DefaultNodes.buildId(Coin.bitcoinTestNet),
|
||||
useSSL: true,
|
||||
enabled: true,
|
||||
coinName: Coin.bitcoinTestNet.name,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
);
|
||||
return DefaultNodes.bitcoinTestnet;
|
||||
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
|
|
65
lib/wallets/crypto_currency/coins/bitcoin_frost.dart
Normal file
65
lib/wallets/crypto_currency/coins/bitcoin_frost.dart
Normal file
|
@ -0,0 +1,65 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:stackwallet/models/node_model.dart';
|
||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/private_key_currency.dart';
|
||||
|
||||
class BitcoinFrost extends FrostCurrency {
|
||||
BitcoinFrost(super.network) {
|
||||
switch (network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
coin = Coin.bitcoin;
|
||||
case CryptoCurrencyNetwork.test:
|
||||
coin = Coin.bitcoinTestNet;
|
||||
default:
|
||||
throw Exception("Unsupported network: $network");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get minConfirms => 1;
|
||||
|
||||
@override
|
||||
NodeModel get defaultNode {
|
||||
switch (network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
return DefaultNodes.bitcoin;
|
||||
|
||||
case CryptoCurrencyNetwork.test:
|
||||
return DefaultNodes.bitcoinTestnet;
|
||||
|
||||
default:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String get genesisHash {
|
||||
switch (network) {
|
||||
case CryptoCurrencyNetwork.main:
|
||||
return "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
|
||||
case CryptoCurrencyNetwork.test:
|
||||
return "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943";
|
||||
default:
|
||||
throw Exception("Unsupported network: $network");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String pubKeyToScriptHash({required Uint8List pubKey}) {
|
||||
try {
|
||||
return Bip39HDCurrency.convertBytesToScriptHash(pubKey);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool validateAddress(String address) {
|
||||
// TODO: implement validateAddress for frost addresses
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
abstract class FrostCurrency extends CryptoCurrency {
|
||||
FrostCurrency(super.network);
|
||||
|
||||
String pubKeyToScriptHash({required Uint8List pubKey});
|
||||
}
|
38
lib/wallets/isar/models/frost_wallet_info.dart
Normal file
38
lib/wallets/isar/models/frost_wallet_info.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/wallets/isar/isar_id_interface.dart';
|
||||
|
||||
part 'frost_wallet_info.g.dart';
|
||||
|
||||
@Collection(accessor: "frostWalletInfo", inheritance: false)
|
||||
class FrostWalletInfo implements IsarId {
|
||||
@override
|
||||
Id id = Isar.autoIncrement;
|
||||
|
||||
@Index(unique: true, replace: false)
|
||||
final String walletId;
|
||||
|
||||
final List<String> knownSalts;
|
||||
|
||||
FrostWalletInfo({
|
||||
required this.walletId,
|
||||
required this.knownSalts,
|
||||
});
|
||||
|
||||
FrostWalletInfo copyWith({
|
||||
List<String>? knownSalts,
|
||||
}) {
|
||||
return FrostWalletInfo(
|
||||
walletId: walletId,
|
||||
knownSalts: knownSalts ?? this.knownSalts,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateKnownSalts(
|
||||
List<String> knownSalts, {
|
||||
required Isar isar,
|
||||
}) async {
|
||||
// await isar.writeTxn(() async {
|
||||
// await isar.
|
||||
// })
|
||||
}
|
||||
}
|
818
lib/wallets/isar/models/frost_wallet_info.g.dart
Normal file
818
lib/wallets/isar/models/frost_wallet_info.g.dart
Normal file
|
@ -0,0 +1,818 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'frost_wallet_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// IsarCollectionGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters
|
||||
|
||||
extension GetFrostWalletInfoCollection on Isar {
|
||||
IsarCollection<FrostWalletInfo> get frostWalletInfo => this.collection();
|
||||
}
|
||||
|
||||
const FrostWalletInfoSchema = CollectionSchema(
|
||||
name: r'FrostWalletInfo',
|
||||
id: -4182879703273806681,
|
||||
properties: {
|
||||
r'knownSalts': PropertySchema(
|
||||
id: 0,
|
||||
name: r'knownSalts',
|
||||
type: IsarType.stringList,
|
||||
),
|
||||
r'walletId': PropertySchema(
|
||||
id: 1,
|
||||
name: r'walletId',
|
||||
type: IsarType.string,
|
||||
)
|
||||
},
|
||||
estimateSize: _frostWalletInfoEstimateSize,
|
||||
serialize: _frostWalletInfoSerialize,
|
||||
deserialize: _frostWalletInfoDeserialize,
|
||||
deserializeProp: _frostWalletInfoDeserializeProp,
|
||||
idName: r'id',
|
||||
indexes: {
|
||||
r'walletId': IndexSchema(
|
||||
id: -1783113319798776304,
|
||||
name: r'walletId',
|
||||
unique: true,
|
||||
replace: false,
|
||||
properties: [
|
||||
IndexPropertySchema(
|
||||
name: r'walletId',
|
||||
type: IndexType.hash,
|
||||
caseSensitive: true,
|
||||
)
|
||||
],
|
||||
)
|
||||
},
|
||||
links: {},
|
||||
embeddedSchemas: {},
|
||||
getId: _frostWalletInfoGetId,
|
||||
getLinks: _frostWalletInfoGetLinks,
|
||||
attach: _frostWalletInfoAttach,
|
||||
version: '3.0.5',
|
||||
);
|
||||
|
||||
int _frostWalletInfoEstimateSize(
|
||||
FrostWalletInfo object,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
var bytesCount = offsets.last;
|
||||
bytesCount += 3 + object.knownSalts.length * 3;
|
||||
{
|
||||
for (var i = 0; i < object.knownSalts.length; i++) {
|
||||
final value = object.knownSalts[i];
|
||||
bytesCount += value.length * 3;
|
||||
}
|
||||
}
|
||||
bytesCount += 3 + object.walletId.length * 3;
|
||||
return bytesCount;
|
||||
}
|
||||
|
||||
void _frostWalletInfoSerialize(
|
||||
FrostWalletInfo object,
|
||||
IsarWriter writer,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
writer.writeStringList(offsets[0], object.knownSalts);
|
||||
writer.writeString(offsets[1], object.walletId);
|
||||
}
|
||||
|
||||
FrostWalletInfo _frostWalletInfoDeserialize(
|
||||
Id id,
|
||||
IsarReader reader,
|
||||
List<int> offsets,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
final object = FrostWalletInfo(
|
||||
knownSalts: reader.readStringList(offsets[0]) ?? [],
|
||||
walletId: reader.readString(offsets[1]),
|
||||
);
|
||||
object.id = id;
|
||||
return object;
|
||||
}
|
||||
|
||||
P _frostWalletInfoDeserializeProp<P>(
|
||||
IsarReader reader,
|
||||
int propertyId,
|
||||
int offset,
|
||||
Map<Type, List<int>> allOffsets,
|
||||
) {
|
||||
switch (propertyId) {
|
||||
case 0:
|
||||
return (reader.readStringList(offset) ?? []) as P;
|
||||
case 1:
|
||||
return (reader.readString(offset)) as P;
|
||||
default:
|
||||
throw IsarError('Unknown property with id $propertyId');
|
||||
}
|
||||
}
|
||||
|
||||
Id _frostWalletInfoGetId(FrostWalletInfo object) {
|
||||
return object.id;
|
||||
}
|
||||
|
||||
List<IsarLinkBase<dynamic>> _frostWalletInfoGetLinks(FrostWalletInfo object) {
|
||||
return [];
|
||||
}
|
||||
|
||||
void _frostWalletInfoAttach(
|
||||
IsarCollection<dynamic> col, Id id, FrostWalletInfo object) {
|
||||
object.id = id;
|
||||
}
|
||||
|
||||
extension FrostWalletInfoByIndex on IsarCollection<FrostWalletInfo> {
|
||||
Future<FrostWalletInfo?> getByWalletId(String walletId) {
|
||||
return getByIndex(r'walletId', [walletId]);
|
||||
}
|
||||
|
||||
FrostWalletInfo? getByWalletIdSync(String walletId) {
|
||||
return getByIndexSync(r'walletId', [walletId]);
|
||||
}
|
||||
|
||||
Future<bool> deleteByWalletId(String walletId) {
|
||||
return deleteByIndex(r'walletId', [walletId]);
|
||||
}
|
||||
|
||||
bool deleteByWalletIdSync(String walletId) {
|
||||
return deleteByIndexSync(r'walletId', [walletId]);
|
||||
}
|
||||
|
||||
Future<List<FrostWalletInfo?>> getAllByWalletId(List<String> walletIdValues) {
|
||||
final values = walletIdValues.map((e) => [e]).toList();
|
||||
return getAllByIndex(r'walletId', values);
|
||||
}
|
||||
|
||||
List<FrostWalletInfo?> getAllByWalletIdSync(List<String> walletIdValues) {
|
||||
final values = walletIdValues.map((e) => [e]).toList();
|
||||
return getAllByIndexSync(r'walletId', values);
|
||||
}
|
||||
|
||||
Future<int> deleteAllByWalletId(List<String> walletIdValues) {
|
||||
final values = walletIdValues.map((e) => [e]).toList();
|
||||
return deleteAllByIndex(r'walletId', values);
|
||||
}
|
||||
|
||||
int deleteAllByWalletIdSync(List<String> walletIdValues) {
|
||||
final values = walletIdValues.map((e) => [e]).toList();
|
||||
return deleteAllByIndexSync(r'walletId', values);
|
||||
}
|
||||
|
||||
Future<Id> putByWalletId(FrostWalletInfo object) {
|
||||
return putByIndex(r'walletId', object);
|
||||
}
|
||||
|
||||
Id putByWalletIdSync(FrostWalletInfo object, {bool saveLinks = true}) {
|
||||
return putByIndexSync(r'walletId', object, saveLinks: saveLinks);
|
||||
}
|
||||
|
||||
Future<List<Id>> putAllByWalletId(List<FrostWalletInfo> objects) {
|
||||
return putAllByIndex(r'walletId', objects);
|
||||
}
|
||||
|
||||
List<Id> putAllByWalletIdSync(List<FrostWalletInfo> objects,
|
||||
{bool saveLinks = true}) {
|
||||
return putAllByIndexSync(r'walletId', objects, saveLinks: saveLinks);
|
||||
}
|
||||
}
|
||||
|
||||
extension FrostWalletInfoQueryWhereSort
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QWhere> {
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhere> anyId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(const IdWhereClause.any());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension FrostWalletInfoQueryWhere
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QWhereClause> {
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhereClause> idEqualTo(
|
||||
Id id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(
|
||||
lower: id,
|
||||
upper: id,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhereClause>
|
||||
idNotEqualTo(Id id) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
);
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: false),
|
||||
)
|
||||
.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: false),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhereClause>
|
||||
idGreaterThan(Id id, {bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.greaterThan(lower: id, includeLower: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhereClause> idLessThan(
|
||||
Id id,
|
||||
{bool include = false}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(
|
||||
IdWhereClause.lessThan(upper: id, includeUpper: include),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhereClause> idBetween(
|
||||
Id lowerId,
|
||||
Id upperId, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IdWhereClause.between(
|
||||
lower: lowerId,
|
||||
includeLower: includeLower,
|
||||
upper: upperId,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhereClause>
|
||||
walletIdEqualTo(String walletId) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addWhereClause(IndexWhereClause.equalTo(
|
||||
indexName: r'walletId',
|
||||
value: [walletId],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterWhereClause>
|
||||
walletIdNotEqualTo(String walletId) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
if (query.whereSort == Sort.asc) {
|
||||
return query
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'walletId',
|
||||
lower: [],
|
||||
upper: [walletId],
|
||||
includeUpper: false,
|
||||
))
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'walletId',
|
||||
lower: [walletId],
|
||||
includeLower: false,
|
||||
upper: [],
|
||||
));
|
||||
} else {
|
||||
return query
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'walletId',
|
||||
lower: [walletId],
|
||||
includeLower: false,
|
||||
upper: [],
|
||||
))
|
||||
.addWhereClause(IndexWhereClause.between(
|
||||
indexName: r'walletId',
|
||||
lower: [],
|
||||
upper: [walletId],
|
||||
includeUpper: false,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension FrostWalletInfoQueryFilter
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QFilterCondition> {
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
idEqualTo(Id value) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'id',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
idGreaterThan(
|
||||
Id value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
idLessThan(
|
||||
Id value, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'id',
|
||||
value: value,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
idBetween(
|
||||
Id lower,
|
||||
Id upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'id',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementEqualTo(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'knownSalts',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementGreaterThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'knownSalts',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementLessThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'knownSalts',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementBetween(
|
||||
String lower,
|
||||
String upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'knownSalts',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'knownSalts',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'knownSalts',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'knownSalts',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'knownSalts',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'knownSalts',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsElementIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
property: r'knownSalts',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsLengthEqualTo(int length) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'knownSalts',
|
||||
length,
|
||||
true,
|
||||
length,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'knownSalts',
|
||||
0,
|
||||
true,
|
||||
0,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'knownSalts',
|
||||
0,
|
||||
false,
|
||||
999999,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsLengthLessThan(
|
||||
int length, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'knownSalts',
|
||||
0,
|
||||
true,
|
||||
length,
|
||||
include,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsLengthGreaterThan(
|
||||
int length, {
|
||||
bool include = false,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'knownSalts',
|
||||
length,
|
||||
include,
|
||||
999999,
|
||||
true,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
knownSaltsLengthBetween(
|
||||
int lower,
|
||||
int upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.listLength(
|
||||
r'knownSalts',
|
||||
lower,
|
||||
includeLower,
|
||||
upper,
|
||||
includeUpper,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdEqualTo(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'walletId',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdGreaterThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
include: include,
|
||||
property: r'walletId',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdLessThan(
|
||||
String value, {
|
||||
bool include = false,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.lessThan(
|
||||
include: include,
|
||||
property: r'walletId',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdBetween(
|
||||
String lower,
|
||||
String upper, {
|
||||
bool includeLower = true,
|
||||
bool includeUpper = true,
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.between(
|
||||
property: r'walletId',
|
||||
lower: lower,
|
||||
includeLower: includeLower,
|
||||
upper: upper,
|
||||
includeUpper: includeUpper,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdStartsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.startsWith(
|
||||
property: r'walletId',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdEndsWith(
|
||||
String value, {
|
||||
bool caseSensitive = true,
|
||||
}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.endsWith(
|
||||
property: r'walletId',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdContains(String value, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.contains(
|
||||
property: r'walletId',
|
||||
value: value,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdMatches(String pattern, {bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.matches(
|
||||
property: r'walletId',
|
||||
wildcard: pattern,
|
||||
caseSensitive: caseSensitive,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdIsEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.equalTo(
|
||||
property: r'walletId',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterFilterCondition>
|
||||
walletIdIsNotEmpty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addFilterCondition(FilterCondition.greaterThan(
|
||||
property: r'walletId',
|
||||
value: '',
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension FrostWalletInfoQueryObject
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QFilterCondition> {}
|
||||
|
||||
extension FrostWalletInfoQueryLinks
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QFilterCondition> {}
|
||||
|
||||
extension FrostWalletInfoQuerySortBy
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QSortBy> {
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterSortBy>
|
||||
sortByWalletId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'walletId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterSortBy>
|
||||
sortByWalletIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'walletId', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension FrostWalletInfoQuerySortThenBy
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QSortThenBy> {
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterSortBy> thenById() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterSortBy> thenByIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'id', Sort.desc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterSortBy>
|
||||
thenByWalletId() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'walletId', Sort.asc);
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QAfterSortBy>
|
||||
thenByWalletIdDesc() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addSortBy(r'walletId', Sort.desc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension FrostWalletInfoQueryWhereDistinct
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QDistinct> {
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QDistinct>
|
||||
distinctByKnownSalts() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'knownSalts');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, FrostWalletInfo, QDistinct> distinctByWalletId(
|
||||
{bool caseSensitive = true}) {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extension FrostWalletInfoQueryProperty
|
||||
on QueryBuilder<FrostWalletInfo, FrostWalletInfo, QQueryProperty> {
|
||||
QueryBuilder<FrostWalletInfo, int, QQueryOperations> idProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'id');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, List<String>, QQueryOperations>
|
||||
knownSaltsProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'knownSalts');
|
||||
});
|
||||
}
|
||||
|
||||
QueryBuilder<FrostWalletInfo, String, QQueryOperations> walletIdProperty() {
|
||||
return QueryBuilder.apply(this, (query) {
|
||||
return query.addPropertyName(r'walletId');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -265,6 +265,7 @@ const _WalletInfomainAddressTypeEnumValueMap = {
|
|||
'spark': 10,
|
||||
'stellar': 11,
|
||||
'tezos': 12,
|
||||
'frostMS': 13,
|
||||
};
|
||||
const _WalletInfomainAddressTypeValueEnumMap = {
|
||||
0: AddressType.p2pkh,
|
||||
|
@ -280,6 +281,7 @@ const _WalletInfomainAddressTypeValueEnumMap = {
|
|||
10: AddressType.spark,
|
||||
11: AddressType.stellar,
|
||||
12: AddressType.tezos,
|
||||
13: AddressType.frostMS,
|
||||
};
|
||||
|
||||
Id _walletInfoGetId(WalletInfo object) {
|
||||
|
|
475
lib/wallets/wallet/impl/bitcoin_frost_wallet.dart
Normal file
475
lib/wallets/wallet/impl/bitcoin_frost_wallet.dart
Normal file
|
@ -0,0 +1,475 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:frostdart/frostdart.dart' as frost;
|
||||
import 'package:frostdart/frostdart_bindings_generated.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
|
||||
import 'package:stackwallet/models/balance.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
|
||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/extensions/extensions.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin_frost.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/private_key_currency.dart';
|
||||
import 'package:stackwallet/wallets/isar/models/frost_wallet_info.dart';
|
||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/private_key_interface.dart';
|
||||
|
||||
class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
|
||||
with PrivateKeyInterface {
|
||||
FrostWalletInfo get frostInfo => throw UnimplementedError();
|
||||
|
||||
late ElectrumXClient electrumXClient;
|
||||
late CachedElectrumXClient electrumXCachedClient;
|
||||
|
||||
@override
|
||||
int get isarTransactionVersion => 2;
|
||||
|
||||
BitcoinFrostWallet(CryptoCurrencyNetwork network)
|
||||
: super(BitcoinFrost(network) as T);
|
||||
|
||||
@override
|
||||
FilterOperation? get changeAddressFilterOperation => FilterGroup.and(
|
||||
[
|
||||
FilterCondition.equalTo(
|
||||
property: r"type",
|
||||
value: info.mainAddressType,
|
||||
),
|
||||
const FilterCondition.equalTo(
|
||||
property: r"subType",
|
||||
value: AddressSubType.change,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@override
|
||||
FilterOperation? get receivingAddressFilterOperation => FilterGroup.and(
|
||||
[
|
||||
FilterCondition.equalTo(
|
||||
property: r"type",
|
||||
value: info.mainAddressType,
|
||||
),
|
||||
const FilterCondition.equalTo(
|
||||
property: r"subType",
|
||||
value: AddressSubType.receiving,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Future<List<Address>> fetchAddressesForElectrumXScan() async {
|
||||
// final allAddresses = await mainDB
|
||||
// .getAddresses(walletId)
|
||||
// .filter()
|
||||
// .typeEqualTo(AddressType.frostMS)
|
||||
// .and()
|
||||
// .group(
|
||||
// (q) => q
|
||||
// .subTypeEqualTo(AddressSubType.receiving)
|
||||
// .or()
|
||||
// .subTypeEqualTo(AddressSubType.change),
|
||||
// )
|
||||
// .findAll();
|
||||
// return allAddresses;
|
||||
// }
|
||||
|
||||
@override
|
||||
Future<void> updateTransactions() {
|
||||
// TODO: implement updateTransactions
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
int estimateTxFee({required int vSize, required int feeRatePerKB}) {
|
||||
return vSize * (feeRatePerKB / 1000).ceil();
|
||||
}
|
||||
|
||||
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
|
||||
Future<void> checkSaveInitialReceivingAddress() {
|
||||
// TODO: implement checkSaveInitialReceivingAddress
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<TxData> confirmSend({required TxData txData}) {
|
||||
// TODO: implement confirmSend
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Amount> estimateFeeFor(Amount amount, int feeRate) {
|
||||
// TODO: implement estimateFeeFor
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO: implement fees
|
||||
Future<FeeObject> get fees => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<TxData> prepareSend({required TxData txData}) {
|
||||
// TODO: implement prepareSendpu
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> recover({
|
||||
required bool isRescan,
|
||||
String? serializedKeys,
|
||||
String? multisigConfig,
|
||||
}) async {
|
||||
if (serializedKeys == null || multisigConfig == null) {
|
||||
throw Exception(
|
||||
"Failed to recover $runtimeType: "
|
||||
"Missing serializedKeys and/or multisigConfig.",
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await refreshMutex.protect(() async {
|
||||
if (!isRescan) {
|
||||
final salt = frost
|
||||
.multisigSalt(
|
||||
multisigConfig: multisigConfig,
|
||||
)
|
||||
.toHex;
|
||||
final knownSalts = frostInfo.knownSalts;
|
||||
if (knownSalts.contains(salt)) {
|
||||
throw Exception("Known frost multisig salt found!");
|
||||
}
|
||||
knownSalts.add(salt);
|
||||
await frostInfo.updateKnownSalts(knownSalts, isar: mainDB.isar);
|
||||
}
|
||||
|
||||
final keys = frost.deserializeKeys(keys: serializedKeys);
|
||||
await _saveSerializedKeys(serializedKeys);
|
||||
await _saveMultisigConfig(multisigConfig);
|
||||
|
||||
final addressString = frost.addressForKeys(
|
||||
network: cryptoCurrency.network == CryptoCurrencyNetwork.main
|
||||
? Network.Mainnet
|
||||
: Network.Testnet,
|
||||
keys: keys,
|
||||
);
|
||||
|
||||
final publicKey = frost.scriptPubKeyForKeys(keys: keys);
|
||||
|
||||
final address = Address(
|
||||
walletId: walletId,
|
||||
value: addressString,
|
||||
publicKey: publicKey.toUint8ListFromHex,
|
||||
derivationIndex: 0,
|
||||
derivationPath: null,
|
||||
subType: AddressSubType.receiving,
|
||||
type: AddressType.frostMS,
|
||||
);
|
||||
|
||||
await mainDB.updateOrPutAddresses([address]);
|
||||
});
|
||||
|
||||
unawaited(refresh());
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"recoverFromSerializedKeys failed: $e\n$s",
|
||||
level: LogLevel.Fatal,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateBalance() async {
|
||||
final utxos = await mainDB.getUTXOs(walletId).findAll();
|
||||
|
||||
final currentChainHeight = await chainHeight;
|
||||
|
||||
Amount satoshiBalanceTotal = Amount(
|
||||
rawValue: BigInt.zero,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
Amount satoshiBalancePending = Amount(
|
||||
rawValue: BigInt.zero,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
Amount satoshiBalanceSpendable = Amount(
|
||||
rawValue: BigInt.zero,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
Amount satoshiBalanceBlocked = Amount(
|
||||
rawValue: BigInt.zero,
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
|
||||
for (final utxo in utxos) {
|
||||
final utxoAmount = Amount(
|
||||
rawValue: BigInt.from(utxo.value),
|
||||
fractionDigits: cryptoCurrency.fractionDigits,
|
||||
);
|
||||
|
||||
satoshiBalanceTotal += utxoAmount;
|
||||
|
||||
if (utxo.isBlocked) {
|
||||
satoshiBalanceBlocked += utxoAmount;
|
||||
} else {
|
||||
if (utxo.isConfirmed(
|
||||
currentChainHeight,
|
||||
cryptoCurrency.minConfirms,
|
||||
)) {
|
||||
satoshiBalanceSpendable += utxoAmount;
|
||||
} else {
|
||||
satoshiBalancePending += utxoAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final balance = Balance(
|
||||
total: satoshiBalanceTotal,
|
||||
spendable: satoshiBalanceSpendable,
|
||||
blockedTotal: satoshiBalanceBlocked,
|
||||
pendingSpendable: satoshiBalancePending,
|
||||
);
|
||||
|
||||
await info.updateBalance(newBalance: balance, isar: mainDB.isar);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateChainHeight() async {
|
||||
final int height;
|
||||
try {
|
||||
final result = await electrumXClient.getBlockHeadTip();
|
||||
height = result["height"] as int;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
||||
await info.updateCachedChainHeight(
|
||||
newHeight: height,
|
||||
isar: mainDB.isar,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> pingCheck() async {
|
||||
try {
|
||||
final result = await electrumXClient.ping();
|
||||
return result;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateNode() async {
|
||||
await _updateElectrumX();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> updateUTXOs() async {
|
||||
final address = await getCurrentReceivingAddress();
|
||||
|
||||
try {
|
||||
final scriptHash = cryptoCurrency.pubKeyToScriptHash(
|
||||
pubKey: Uint8List.fromList(address!.publicKey),
|
||||
);
|
||||
|
||||
final utxos = await electrumXClient.getUTXOs(scripthash: scriptHash);
|
||||
|
||||
final List<UTXO> outputArray = [];
|
||||
|
||||
for (int i = 0; i < utxos.length; i++) {
|
||||
final utxo = await _parseUTXO(
|
||||
jsonUTXO: utxos[i],
|
||||
);
|
||||
|
||||
outputArray.add(utxo);
|
||||
}
|
||||
|
||||
return await mainDB.updateUTXOs(walletId, outputArray);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Output fetch unsuccessful: $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// =================== Secure storage ========================================
|
||||
|
||||
Future<String?> get getSerializedKeys async =>
|
||||
await secureStorageInterface.read(
|
||||
key: "{$walletId}_serializedFROSTKeys",
|
||||
);
|
||||
Future<void> _saveSerializedKeys(String keys) async {
|
||||
final current = await getSerializedKeys;
|
||||
|
||||
if (current == null) {
|
||||
// do nothing
|
||||
} else if (current == keys) {
|
||||
// should never occur
|
||||
} else {
|
||||
// save current as prev gen before updating current
|
||||
await secureStorageInterface.write(
|
||||
key: "{$walletId}_serializedFROSTKeysPrevGen",
|
||||
value: current,
|
||||
);
|
||||
}
|
||||
|
||||
await secureStorageInterface.write(
|
||||
key: "{$walletId}_serializedFROSTKeys",
|
||||
value: keys,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> get getSerializedKeysPrevGen async =>
|
||||
await secureStorageInterface.read(
|
||||
key: "{$walletId}_serializedFROSTKeysPrevGen",
|
||||
);
|
||||
|
||||
Future<String?> get multisigConfig async => await secureStorageInterface.read(
|
||||
key: "{$walletId}_multisigConfig",
|
||||
);
|
||||
Future<String?> get multisigConfigPrevGen async =>
|
||||
await secureStorageInterface.read(
|
||||
key: "{$walletId}_multisigConfigPrevGen",
|
||||
);
|
||||
Future<void> _saveMultisigConfig(String multisigConfig) async {
|
||||
final current = await this.multisigConfig;
|
||||
|
||||
if (current == null) {
|
||||
// do nothing
|
||||
} else if (current == multisigConfig) {
|
||||
// should never occur
|
||||
} else {
|
||||
// save current as prev gen before updating current
|
||||
await secureStorageInterface.write(
|
||||
key: "{$walletId}_multisigConfigPrevGen",
|
||||
value: current,
|
||||
);
|
||||
}
|
||||
|
||||
await secureStorageInterface.write(
|
||||
key: "{$walletId}_multisigConfig",
|
||||
value: multisigConfig,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Uint8List?> get multisigId async {
|
||||
final id = await secureStorageInterface.read(
|
||||
key: "{$walletId}_multisigIdFROST",
|
||||
);
|
||||
if (id == null) {
|
||||
return null;
|
||||
} else {
|
||||
return id.toUint8ListFromHex;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveMultisigId(Uint8List id) async =>
|
||||
await secureStorageInterface.write(
|
||||
key: "{$walletId}_multisigIdFROST",
|
||||
value: id.toHex,
|
||||
);
|
||||
|
||||
Future<String?> get recoveryString async => await secureStorageInterface.read(
|
||||
key: "{$walletId}_recoveryStringFROST",
|
||||
);
|
||||
Future<void> saveRecoveryString(String recoveryString) async =>
|
||||
await secureStorageInterface.write(
|
||||
key: "{$walletId}_recoveryStringFROST",
|
||||
value: recoveryString,
|
||||
);
|
||||
|
||||
// =================== Private ===============================================
|
||||
|
||||
Future<ElectrumXNode> _getCurrentElectrumXNode() async {
|
||||
final node = getCurrentNode();
|
||||
|
||||
return ElectrumXNode(
|
||||
address: node.host,
|
||||
port: node.port,
|
||||
name: node.name,
|
||||
useSSL: node.useSSL,
|
||||
id: node.id,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _updateElectrumX() async {
|
||||
final failovers = nodeService
|
||||
.failoverNodesFor(coin: cryptoCurrency.coin)
|
||||
.map((e) => ElectrumXNode(
|
||||
address: e.host,
|
||||
port: e.port,
|
||||
name: e.name,
|
||||
id: e.id,
|
||||
useSSL: e.useSSL,
|
||||
))
|
||||
.toList();
|
||||
|
||||
final newNode = await _getCurrentElectrumXNode();
|
||||
electrumXClient = ElectrumXClient.from(
|
||||
node: newNode,
|
||||
prefs: prefs,
|
||||
failovers: failovers,
|
||||
);
|
||||
electrumXCachedClient = CachedElectrumXClient.from(
|
||||
electrumXClient: electrumXClient,
|
||||
);
|
||||
}
|
||||
|
||||
Future<UTXO> _parseUTXO({
|
||||
required Map<String, dynamic> jsonUTXO,
|
||||
}) async {
|
||||
final txn = await electrumXCachedClient.getTransaction(
|
||||
txHash: jsonUTXO["tx_hash"] as String,
|
||||
verbose: true,
|
||||
coin: cryptoCurrency.coin,
|
||||
);
|
||||
|
||||
final vout = jsonUTXO["tx_pos"] as int;
|
||||
|
||||
final outputs = txn["vout"] as List;
|
||||
|
||||
String? scriptPubKey;
|
||||
String? utxoOwnerAddress;
|
||||
// get UTXO owner address
|
||||
for (final output in outputs) {
|
||||
if (output["n"] == vout) {
|
||||
scriptPubKey = output["scriptPubKey"]?["hex"] as String?;
|
||||
utxoOwnerAddress =
|
||||
output["scriptPubKey"]?["addresses"]?[0] as String? ??
|
||||
output["scriptPubKey"]?["address"] as String?;
|
||||
}
|
||||
}
|
||||
|
||||
final utxo = UTXO(
|
||||
walletId: walletId,
|
||||
txid: txn["txid"] as String,
|
||||
vout: vout,
|
||||
value: jsonUTXO["value"] as int,
|
||||
name: "",
|
||||
isBlocked: false,
|
||||
blockedReason: null,
|
||||
isCoinbase: txn["is_coinbase"] as bool? ?? false,
|
||||
blockHash: txn["blockhash"] as String?,
|
||||
blockHeight: jsonUTXO["height"] as int?,
|
||||
blockTime: txn["blocktime"] as int?,
|
||||
address: utxoOwnerAddress,
|
||||
);
|
||||
|
||||
return utxo;
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
|
|||
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
|
||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/banano_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/bitcoin_frost_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/bitcoin_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/bitcoincash_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/dogecoin_wallet.dart';
|
||||
|
@ -311,6 +312,11 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
case Coin.bitcoinTestNet:
|
||||
return BitcoinWallet(CryptoCurrencyNetwork.test);
|
||||
|
||||
case Coin.bitcoinFrost:
|
||||
return BitcoinFrostWallet(CryptoCurrencyNetwork.main);
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
return BitcoinFrostWallet(CryptoCurrencyNetwork.test);
|
||||
|
||||
case Coin.bitcoincash:
|
||||
return BitcoincashWallet(CryptoCurrencyNetwork.main);
|
||||
case Coin.bitcoincashTestnet:
|
||||
|
|
|
@ -832,7 +832,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<ElectrumXNode> getCurrentElectrumXNode() async {
|
||||
Future<ElectrumXNode> _getCurrentElectrumXNode() async {
|
||||
final node = getCurrentNode();
|
||||
|
||||
return ElectrumXNode(
|
||||
|
@ -844,7 +844,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> updateElectrumX({required ElectrumXNode newNode}) async {
|
||||
Future<void> updateElectrumX() async {
|
||||
final failovers = nodeService
|
||||
.failoverNodesFor(coin: cryptoCurrency.coin)
|
||||
.map((e) => ElectrumXNode(
|
||||
|
@ -856,7 +856,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
))
|
||||
.toList();
|
||||
|
||||
final newNode = await getCurrentElectrumXNode();
|
||||
final newNode = await _getCurrentElectrumXNode();
|
||||
electrumXClient = ElectrumXClient.from(
|
||||
node: newNode,
|
||||
prefs: prefs,
|
||||
|
@ -1160,8 +1160,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
|
||||
@override
|
||||
Future<void> updateNode() async {
|
||||
final node = await getCurrentElectrumXNode();
|
||||
await updateElectrumX(newNode: node);
|
||||
await updateElectrumX();
|
||||
}
|
||||
|
||||
FeeObject? _cachedFees;
|
||||
|
|
|
@ -169,6 +169,8 @@ class _NodeCardState extends ConsumerState<NodeCard> {
|
|||
case Coin.namecoin:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.eCash:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
final client = ElectrumXClient(
|
||||
host: node.host,
|
||||
port: node.port,
|
||||
|
|
|
@ -151,6 +151,8 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
case Coin.namecoin:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.eCash:
|
||||
case Coin.bitcoinFrost:
|
||||
case Coin.bitcoinFrostTestNet:
|
||||
final client = ElectrumXClient(
|
||||
host: node.host,
|
||||
port: node.port,
|
||||
|
|
Loading…
Reference in a new issue