mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'dev' into cw-1497-transaction-history-improved-layout
# Conflicts: # lib/new-ui/widgets/coins_page/assets_history/history_section.dart # lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart
This commit is contained in:
@@ -9,7 +9,7 @@ jobs:
|
||||
PR_test_build:
|
||||
runs-on: [Linux, amd64, forlinux]
|
||||
container:
|
||||
image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.32.0-ndkr28-go1.24.1-ruststablenightly
|
||||
image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly
|
||||
env:
|
||||
STORE_PASS: test@cake_wallet
|
||||
KEY_PASS: test@cake_wallet
|
||||
@@ -227,6 +227,9 @@ jobs:
|
||||
xmessage -timeout 30 "restore_wallet_through_seeds_flow_test" &
|
||||
rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
|
||||
exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart
|
||||
- name: Test [cw_core]
|
||||
timeout-minutes: 15
|
||||
run: cd cw_core && flutter test
|
||||
- name: Test [cw_monero]
|
||||
timeout-minutes: 15
|
||||
run: cd cw_monero && flutter test
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
PR_test_build:
|
||||
runs-on: [Linux, amd64, android]
|
||||
container:
|
||||
image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.32.0-ndkr28-go1.24.1-ruststablenightly
|
||||
image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly
|
||||
env:
|
||||
STORE_PASS: test@cake_wallet
|
||||
KEY_PASS: test@cake_wallet
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian13-flutter3.32.0-ndkr28-go1.24.1-ruststablenightly
|
||||
# docker buildx build --push --pull --platform linux/amd64,linux/arm64 . -f Dockerfile -t ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly
|
||||
|
||||
# Heavily inspired by cirrusci images
|
||||
# https://github.com/cirruslabs/docker-images-android/blob/master/sdk/tools/Dockerfile
|
||||
@@ -15,7 +15,7 @@ LABEL org.opencontainers.image.source=https://github.com/cake-tech/cake_wallet
|
||||
ENV GOLANG_VERSION=1.24.1
|
||||
|
||||
# Pin Flutter version to latest known-working version
|
||||
ENV FLUTTER_VERSION=3.32.0
|
||||
ENV FLUTTER_VERSION=3.41.9
|
||||
|
||||
# Pin Android Studio, platform, and build tools versions to latest known-working version
|
||||
# Comes from https://developer.android.com/studio/#command-tools
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx6144M
|
||||
android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.ndk.suppressMinSdkVersionError=21
|
||||
android.ndk.suppressMinSdkVersionError=21
|
||||
+1
-1
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-all.zip
|
||||
|
||||
@@ -18,8 +18,8 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version "8.7.1" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
|
||||
id "com.android.application" version "8.9.1" apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.3.20" apply false
|
||||
id "com.google.gms.google-services" version "4.3.8" apply false
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ import 'package:cw_bitcoin/psbt/signer.dart';
|
||||
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
|
||||
import 'package:cw_bitcoin/psbt/v0_deserialize.dart';
|
||||
import 'package:cw_bitcoin/psbt/v0_finalizer.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/output_info.dart';
|
||||
import 'package:cw_core/parse_fixed.dart';
|
||||
import 'package:cw_core/payjoin_session.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
@@ -347,13 +347,15 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
|
||||
final lBalance = await lightningWallet!.getBalance();
|
||||
|
||||
this.balance[CryptoCurrency.btcln] =
|
||||
ElectrumBalance(confirmed: lBalance.toInt(), unconfirmed: 0, frozen: 0);
|
||||
this.balance[CryptoCurrency.btcln] = ElectrumBalance(
|
||||
confirmed: lBalance,
|
||||
unconfirmed: Money.zero(CryptoCurrency.btcln),
|
||||
frozen: Money.zero(CryptoCurrency.btcln));
|
||||
|
||||
return ElectrumBalance(
|
||||
confirmed: balance.confirmed,
|
||||
unconfirmed: balance.unconfirmed,
|
||||
frozen: balance.frozen.toInt(),
|
||||
frozen: balance.frozen ?? Money.zero(currency),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -493,19 +495,19 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
final isLNCompatible = await lightningWallet?.isCompatible(lnAddr);
|
||||
if ((credentials.coinTypeToSpendFrom == UnspentCoinType.lightning && lightningWallet != null) ||
|
||||
isLNCompatible == true) {
|
||||
BigInt amount;
|
||||
Money amount;
|
||||
if (credentials.outputs.first.sendAll) {
|
||||
amount = await lightningWallet!.getBalance();
|
||||
} else {
|
||||
amount = parseFixed(
|
||||
credentials.outputs.first.cryptoAmount?.isNotEmpty == true
|
||||
? credentials.outputs.first.cryptoAmount!
|
||||
: "0",
|
||||
8);
|
||||
amount = credentials.outputs.first.cryptoAmount;
|
||||
}
|
||||
|
||||
return lightningWallet!.createTransaction(lnAddr, amount > BigInt.zero ? amount : null,
|
||||
credentials.priority, credentials.outputs.first.sendAll);
|
||||
|
||||
return lightningWallet!.createTransaction(
|
||||
lnAddr,
|
||||
amount.amount > BigInt.zero ? amount.amount : null,
|
||||
credentials.priority,
|
||||
credentials.outputs.first.sendAll,);
|
||||
}
|
||||
|
||||
final tx = (await super.createTransaction(credentials)) as PendingBitcoinTransaction;
|
||||
@@ -524,7 +526,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
))
|
||||
.toList(),
|
||||
cwOutputs: credentials.outputs,
|
||||
fee: BigInt.from(tx.fee),
|
||||
fee: tx.fee.amount,
|
||||
network: network,
|
||||
memo: credentials.outputs.first.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
@@ -543,8 +545,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
tx.commitOverride = () async {
|
||||
final sender =
|
||||
await payjoinManager.initSender(payjoinUri!, originalPsbt, int.parse(tx.feeRate));
|
||||
payjoinManager.spawnNewSender(
|
||||
sender: sender, pjUrl: payjoinUri, amount: BigInt.from(tx.amount));
|
||||
payjoinManager.spawnNewSender(sender: sender, pjUrl: payjoinUri, amount: tx.amount.amount);
|
||||
};
|
||||
|
||||
return tx;
|
||||
@@ -564,8 +565,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
btcTx,
|
||||
type,
|
||||
electrumClient: electrumClient,
|
||||
amount: 0,
|
||||
fee: 0,
|
||||
amount: Money.zero(currency),
|
||||
fee: Money.zero(currency),
|
||||
feeRate: "",
|
||||
network: network,
|
||||
hasChange: true,
|
||||
@@ -622,8 +623,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
||||
btcTx,
|
||||
type,
|
||||
electrumClient: electrumClient,
|
||||
amount: 0,
|
||||
fee: 0,
|
||||
amount: Money.zero(currency),
|
||||
fee: Money.zero(currency),
|
||||
feeRate: "",
|
||||
network: network,
|
||||
hasChange: true,
|
||||
|
||||
@@ -153,11 +153,24 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
||||
|
||||
@override
|
||||
String get addressForExchange {
|
||||
if (addressPageType == LightningAddressType.p2l) {
|
||||
final addresses = receiveAddresses
|
||||
.where((element) => element.type == SegwitAddresType.p2wpkh && !element.isUsed);
|
||||
return addresses.first.address;
|
||||
|
||||
final current = getFreshAddress();
|
||||
final availableReceiveAddresses = receiveAddresses.where((element) =>
|
||||
!element.isUsed &&
|
||||
!element.isHidden &&
|
||||
!hiddenAddresses.contains(element.address));
|
||||
|
||||
final bool isSilentPaymentsPage = addressPageType == SilentPaymentsAddresType.p2sp;
|
||||
final bool isLightningPage = addressPageType == LightningAddressType.p2l;
|
||||
|
||||
if (isSilentPaymentsPage || isLightningPage) {
|
||||
final segwit = availableReceiveAddresses
|
||||
.where((e) => e.type == SegwitAddresType.p2wpkh)
|
||||
.map((e) => e.address)
|
||||
.toList();
|
||||
|
||||
return segwit.isNotEmpty ? segwit.first : current;
|
||||
}
|
||||
return address;
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,52 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
|
||||
class ElectrumBalance extends Balance {
|
||||
ElectrumBalance({
|
||||
required this.confirmed,
|
||||
required this.unconfirmed,
|
||||
required int frozen,
|
||||
this.secondConfirmed = 0,
|
||||
this.secondUnconfirmed = 0,
|
||||
}) : this._frozen = frozen, super.fromInt(confirmed, unconfirmed,
|
||||
secondAvailable: secondConfirmed, secondAdditional: secondUnconfirmed, frozen: frozen);
|
||||
required this.frozen,
|
||||
this.secondConfirmed,
|
||||
this.secondUnconfirmed,
|
||||
}) : super(confirmed, unconfirmed,
|
||||
secondAvailable: secondConfirmed, secondUnavailable: secondUnconfirmed);
|
||||
|
||||
static ElectrumBalance? fromJSON(String? jsonSource) {
|
||||
Money confirmed;
|
||||
Money unconfirmed;
|
||||
Money? secondConfirmed;
|
||||
Money? secondUnconfirmed;
|
||||
|
||||
@override
|
||||
Money get available => (confirmed + unconfirmed) - frozen;
|
||||
|
||||
@override
|
||||
Money get unavailable => unconfirmed;
|
||||
|
||||
@override
|
||||
Money frozen;
|
||||
|
||||
static ElectrumBalance? fromJSON(String? jsonSource, Currency currency) {
|
||||
if (jsonSource == null) return null;
|
||||
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
return ElectrumBalance(
|
||||
confirmed: decoded['confirmed'] as int? ?? 0,
|
||||
unconfirmed: decoded['unconfirmed'] as int? ?? 0,
|
||||
frozen: decoded['frozen'] as int? ?? 0,
|
||||
secondConfirmed: decoded['secondConfirmed'] as int? ?? 0,
|
||||
secondUnconfirmed: decoded['secondUnconfirmed'] as int? ?? 0,
|
||||
confirmed: Money.fromInt(decoded['confirmed'] as int? ?? 0, currency),
|
||||
unconfirmed: Money.fromInt(decoded['unconfirmed'] as int? ?? 0, currency),
|
||||
frozen: Money.fromInt(decoded['frozen'] as int? ?? 0, currency),
|
||||
secondConfirmed: Money.fromInt(decoded['secondConfirmed'] as int? ?? 0, currency),
|
||||
secondUnconfirmed: Money.fromInt(decoded['secondUnconfirmed'] as int? ?? 0, currency),
|
||||
);
|
||||
}
|
||||
|
||||
int confirmed;
|
||||
int unconfirmed;
|
||||
final int _frozen;
|
||||
|
||||
@override
|
||||
BigInt get frozen => BigInt.from(_frozen);
|
||||
|
||||
int secondConfirmed = 0;
|
||||
int secondUnconfirmed = 0;
|
||||
|
||||
@override
|
||||
BigInt get fullAvailableBalance => BigInt.from((confirmed + unconfirmed) + secondConfirmed - _frozen);
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'confirmed': confirmed,
|
||||
'unconfirmed': unconfirmed,
|
||||
'frozen': _frozen,
|
||||
'secondConfirmed': secondConfirmed,
|
||||
'secondUnconfirmed': secondUnconfirmed,
|
||||
'confirmed': confirmed.amount.toInt(),
|
||||
'unconfirmed': unavailable.amount.toInt(),
|
||||
'frozen': frozen.amount.toInt(),
|
||||
'secondConfirmed': secondAvailable?.amount.toInt() ?? 0,
|
||||
'secondUnconfirmed': secondUnavailable?.amount.toInt() ?? 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@ import 'package:cw_bitcoin/address_from_output.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hex/hex.dart';
|
||||
|
||||
@@ -31,8 +32,8 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
this.type, {
|
||||
required String id,
|
||||
int? height,
|
||||
required int amount,
|
||||
int? fee,
|
||||
required Money amount,
|
||||
Money? fee,
|
||||
List<String>? inputAddresses,
|
||||
List<String>? outputAddresses,
|
||||
required TransactionDirection direction,
|
||||
@@ -106,9 +107,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
height: height,
|
||||
isPending: false,
|
||||
isReplaced: false,
|
||||
fee: fee,
|
||||
fee: Money.fromInt(fee, walletTypeToCryptoCurrency(type)),
|
||||
direction: direction,
|
||||
amount: amount,
|
||||
amount: Money.fromInt(amount, walletTypeToCryptoCurrency(type)),
|
||||
date: date,
|
||||
confirmations: confirmations);
|
||||
}
|
||||
@@ -193,9 +194,9 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
isReplaced: false,
|
||||
inputAddresses: inputAddresses,
|
||||
outputAddresses: outputAddresses,
|
||||
fee: fee,
|
||||
fee: Money.fromInt(fee, walletTypeToCryptoCurrency(type)),
|
||||
direction: direction,
|
||||
amount: amount,
|
||||
amount: Money.fromInt(amount, walletTypeToCryptoCurrency(type)),
|
||||
date: date,
|
||||
isHogEx: isHogEx,
|
||||
confirmations: bundle.confirmations);
|
||||
@@ -205,13 +206,17 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
final inputAddresses = data['inputAddresses'] as List<dynamic>? ?? [];
|
||||
final outputAddresses = data['outputAddresses'] as List<dynamic>? ?? [];
|
||||
final unspents = data['unspents'] as List<dynamic>? ?? [];
|
||||
final additionalInfo = data['additionalInfo'] as Map<String, dynamic>?;
|
||||
final isLightning = (additionalInfo?['isLightning'] as bool?) == true;
|
||||
|
||||
final currency = isLightning ? CryptoCurrency.btcln : walletTypeToCryptoCurrency(type);
|
||||
|
||||
return ElectrumTransactionInfo(
|
||||
type,
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int?,
|
||||
amount: data['amount'] as int,
|
||||
fee: data['fee'] as int,
|
||||
amount: Money.fromInt(data['amount'] as int, currency),
|
||||
fee: data['fee'] != null ? Money.fromInt(data['fee'] as int, currency) : null,
|
||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool,
|
||||
@@ -227,29 +232,12 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
BitcoinSilentPaymentsUnspent.fromJSON(null, unspent as Map<String, dynamic>))
|
||||
.toList(),
|
||||
isReceivedSilentPayment: data['isReceivedSilentPayment'] as bool? ?? false,
|
||||
additionalInfo: data['additionalInfo'] as Map<String, dynamic>?,
|
||||
additionalInfo: additionalInfo,
|
||||
);
|
||||
}
|
||||
|
||||
final WalletType type;
|
||||
|
||||
String? _fiatAmount;
|
||||
|
||||
@override
|
||||
String amountFormatted() =>
|
||||
'${walletTypeToCryptoCurrency(type).formatAmount(BigInt.from(amount))} ${walletTypeToCryptoCurrency(type).title}';
|
||||
|
||||
@override
|
||||
String? feeFormatted() => fee != null
|
||||
? '${walletTypeToCryptoCurrency(type).formatAmount(BigInt.from(fee!))} ${walletTypeToCryptoCurrency(type).title}'
|
||||
: '';
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
ElectrumTransactionInfo updated(ElectrumTransactionInfo info) {
|
||||
return ElectrumTransactionInfo(info.type,
|
||||
id: id,
|
||||
@@ -270,13 +258,13 @@ class ElectrumTransactionInfo extends TransactionInfo {
|
||||
final m = <String, dynamic>{};
|
||||
m['id'] = id;
|
||||
m['height'] = height;
|
||||
m['amount'] = amount;
|
||||
m['amount'] = amount.amount.toInt();
|
||||
m['direction'] = direction.index;
|
||||
m['date'] = date.millisecondsSinceEpoch;
|
||||
m['isPending'] = isPending;
|
||||
m['isReplaced'] = isReplaced;
|
||||
m['confirmations'] = confirmations;
|
||||
m['fee'] = fee;
|
||||
m['fee'] = fee?.amount.toInt();
|
||||
m['to'] = to;
|
||||
m['unspents'] = unspents?.map((e) => e.toJson()).toList() ?? [];
|
||||
m['inputAddresses'] = inputAddresses;
|
||||
|
||||
@@ -19,6 +19,7 @@ import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
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.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_wallet_keys.dart';
|
||||
import 'package:cw_bitcoin/electrum.dart' as electrum;
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
@@ -27,20 +28,26 @@ import 'package:cw_bitcoin/electrum_transaction_history.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cw_bitcoin/exceptions.dart';
|
||||
import 'package:cw_bitcoin/litecoin_wallet.dart';
|
||||
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/get_height_by_date.dart';
|
||||
import 'package:cw_core/hardware/hardware_wallet_service.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/output_info.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/root_dir.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cw_core/utils/socket_health_logger.dart';
|
||||
import 'package:cw_core/utils/tor/abstract.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
@@ -52,6 +59,7 @@ import 'package:hex/hex.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:rxdart/subjects.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:sp_scanner/sp_scanner.dart';
|
||||
|
||||
part 'electrum_wallet.g.dart';
|
||||
@@ -94,9 +102,9 @@ abstract class ElectrumWalletBase
|
||||
? {
|
||||
currency: initialBalance ??
|
||||
ElectrumBalance(
|
||||
confirmed: 0,
|
||||
unconfirmed: 0,
|
||||
frozen: 0,
|
||||
confirmed: Money.zero(currency),
|
||||
unconfirmed: Money.zero(currency),
|
||||
frozen: Money.zero(currency),
|
||||
)
|
||||
}
|
||||
: {}),
|
||||
@@ -311,7 +319,7 @@ abstract class ElectrumWalletBase
|
||||
bool get useLightning => _useLightning && LightningWallet.isAvailable;
|
||||
|
||||
set useLightning(bool val) => _useLightning = val && LightningWallet.isAvailable;
|
||||
|
||||
|
||||
@observable
|
||||
bool _useLightning;
|
||||
|
||||
@@ -597,13 +605,13 @@ abstract class ElectrumWalletBase
|
||||
: newUnspents[0].value;
|
||||
|
||||
if (existingTxInfo.direction == TransactionDirection.incoming) {
|
||||
existingTxInfo.amount += newAmount;
|
||||
existingTxInfo.amount += Money.fromInt(newAmount, currency);
|
||||
}
|
||||
|
||||
// Updates existing TX
|
||||
transactionHistory.addOne(existingTxInfo);
|
||||
// Update balance record
|
||||
balance[currency]!.confirmed += newAmount;
|
||||
balance[currency]!.confirmed += Money.fromInt(newAmount, currency);
|
||||
}
|
||||
} else {
|
||||
// else: First time seeing this TX after scanning
|
||||
@@ -865,9 +873,9 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
int get networkDustAmount => 546;
|
||||
BigInt get networkDustAmount => BigInt.from(546);
|
||||
|
||||
bool _isBelowDust(int amount) => amount <= networkDustAmount && network != BitcoinNetwork.testnet;
|
||||
bool _isBelowDust(BigInt amount) => amount <= networkDustAmount && network != BitcoinNetwork.testnet;
|
||||
|
||||
UtxoDetails _createUTXOS({
|
||||
required bool sendAll,
|
||||
@@ -1031,9 +1039,9 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
// Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change
|
||||
int amount = utxoDetails.allInputsAmount - fee;
|
||||
final amount = BigInt.from(utxoDetails.allInputsAmount - fee);
|
||||
|
||||
if (amount <= 0) {
|
||||
if (amount <= BigInt.zero) {
|
||||
throw BitcoinTransactionWrongBalanceException(amount: utxoDetails.allInputsAmount + fee);
|
||||
}
|
||||
|
||||
@@ -1045,7 +1053,7 @@ abstract class ElectrumWalletBase
|
||||
if (outputs.length == 1) {
|
||||
outputs[0] = BitcoinOutput(
|
||||
address: outputs.last.address,
|
||||
value: BigInt.from(amount),
|
||||
value: amount,
|
||||
isSilentPayment: hasSilentPayment,
|
||||
);
|
||||
}
|
||||
@@ -1054,8 +1062,8 @@ abstract class ElectrumWalletBase
|
||||
utxos: utxoDetails.utxos,
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
publicKeys: utxoDetails.publicKeys,
|
||||
fee: fee,
|
||||
amount: amount,
|
||||
fee: Money.fromInt(fee, currency),
|
||||
amount: Money(amount, currency),
|
||||
isSendAll: true,
|
||||
hasChange: false,
|
||||
memo: memo,
|
||||
@@ -1065,7 +1073,7 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
Future<EstimatedTxResult> estimateTxForAmount(
|
||||
int credentialsAmount,
|
||||
Money credentialsAmount,
|
||||
List<BitcoinOutput> outputs,
|
||||
List<BitcoinOutput> updatedOutputs,
|
||||
int feeRate, {
|
||||
@@ -1076,7 +1084,7 @@ abstract class ElectrumWalletBase
|
||||
UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any,
|
||||
}) async {
|
||||
// Attempting to send less than the dust limit
|
||||
if (_isBelowDust(credentialsAmount)) {
|
||||
if (_isBelowDust(credentialsAmount.amount)) {
|
||||
throw BitcoinTransactionNoDustException();
|
||||
}
|
||||
|
||||
@@ -1122,7 +1130,7 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final utxoDetails = _createUTXOS(
|
||||
sendAll: false,
|
||||
credentialsAmount: credentialsAmount,
|
||||
credentialsAmount: credentialsAmount.amount.toInt(),
|
||||
inputsCount: inputsCount,
|
||||
paysToSilentPayment: hasSilentPayment,
|
||||
coinTypeToSpendFrom: coinTypeToSpendFrom,
|
||||
@@ -1133,8 +1141,7 @@ abstract class ElectrumWalletBase
|
||||
utxoDetails.utxos.length ==
|
||||
utxoDetails.availableInputs.length - utxoDetails.unconfirmedCoins.length;
|
||||
|
||||
// How much is being spent - how much is being sent
|
||||
int amountLeftForChangeAndFee = utxoDetails.allInputsAmount - credentialsAmount;
|
||||
final amountLeftForChangeAndFee = utxoDetails.allInputsAmount - credentialsAmount.amount.toInt();
|
||||
|
||||
if (amountLeftForChangeAndFee <= 0) {
|
||||
if (!spendingAllCoins) {
|
||||
@@ -1213,9 +1220,9 @@ abstract class ElectrumWalletBase
|
||||
throw BitcoinTransactionNoFeeException();
|
||||
}
|
||||
|
||||
int amount = credentialsAmount;
|
||||
var amount = credentialsAmount;
|
||||
final lastOutput = updatedOutputs.last;
|
||||
final amountLeftForChange = amountLeftForChangeAndFee - fee;
|
||||
final amountLeftForChange = BigInt.from(amountLeftForChangeAndFee - fee);
|
||||
|
||||
if (_isBelowDust(amountLeftForChange)) {
|
||||
// If has change that is lower than dust, will end up with tx rejected by network rules
|
||||
@@ -1226,7 +1233,7 @@ abstract class ElectrumWalletBase
|
||||
// If the computed change is negative or below dust:
|
||||
// - negative: try a no-change tx (recalculate fee without change)
|
||||
// - non-negative but dust: drop change and add remainder to fee
|
||||
if (amountLeftForChange < 0) {
|
||||
if (amountLeftForChange < BigInt.zero) {
|
||||
final tempNoChange = outputs.map((o) => o).toList();
|
||||
final feeNoChange = await calcFee(
|
||||
utxos: utxoDetails.utxos,
|
||||
@@ -1237,7 +1244,7 @@ abstract class ElectrumWalletBase
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
vinOutpoints: utxoDetails.vinOutpoints,
|
||||
);
|
||||
final leftover = utxoDetails.allInputsAmount - credentialsAmount - feeNoChange;
|
||||
final leftover = utxoDetails.allInputsAmount - credentialsAmount.amount.toInt() - feeNoChange;
|
||||
|
||||
if (leftover >= 0) {
|
||||
final finalFee = feeNoChange + leftover; // absorb tiny remainder
|
||||
@@ -1245,7 +1252,7 @@ abstract class ElectrumWalletBase
|
||||
utxos: utxoDetails.utxos,
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
publicKeys: utxoDetails.publicKeys,
|
||||
fee: finalFee,
|
||||
fee: Money.fromInt(finalFee, currency),
|
||||
amount: amount,
|
||||
hasChange: false,
|
||||
isSendAll: spendingAllCoins,
|
||||
@@ -1274,13 +1281,13 @@ abstract class ElectrumWalletBase
|
||||
|
||||
// if the amount left for change is less than dust, but not less than 0
|
||||
// then add it to the fees
|
||||
fee += amountLeftForChange;
|
||||
fee += amountLeftForChange.toInt();
|
||||
|
||||
return EstimatedTxResult(
|
||||
utxos: utxoDetails.utxos,
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
publicKeys: utxoDetails.publicKeys,
|
||||
fee: fee,
|
||||
fee: Money.fromInt(fee, currency),
|
||||
amount: amount,
|
||||
hasChange: false,
|
||||
isSendAll: spendingAllCoins,
|
||||
@@ -1292,13 +1299,13 @@ abstract class ElectrumWalletBase
|
||||
// Here, lastOutput already is change, return the amount left without the fee to the user's address.
|
||||
updatedOutputs[updatedOutputs.length - 1] = BitcoinOutput(
|
||||
address: lastOutput.address,
|
||||
value: BigInt.from(amountLeftForChange),
|
||||
value: amountLeftForChange,
|
||||
isSilentPayment: lastOutput.isSilentPayment,
|
||||
isChange: true,
|
||||
);
|
||||
outputs[outputs.length - 1] = BitcoinOutput(
|
||||
address: lastOutput.address,
|
||||
value: BigInt.from(amountLeftForChange),
|
||||
value: amountLeftForChange,
|
||||
isSilentPayment: lastOutput.isSilentPayment,
|
||||
isChange: true,
|
||||
);
|
||||
@@ -1307,7 +1314,7 @@ abstract class ElectrumWalletBase
|
||||
utxos: utxoDetails.utxos,
|
||||
inputPrivKeyInfos: utxoDetails.inputPrivKeyInfos,
|
||||
publicKeys: utxoDetails.publicKeys,
|
||||
fee: fee,
|
||||
fee: Money.fromInt(fee, currency),
|
||||
amount: amount,
|
||||
hasChange: true,
|
||||
isSendAll: spendingAllCoins,
|
||||
@@ -1318,7 +1325,7 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> _maxSpendableNoChangeAmount({
|
||||
Future<Money> _maxSpendableNoChangeAmount({
|
||||
required BitcoinOutput initialOutput,
|
||||
required int feeRate,
|
||||
String? memo,
|
||||
@@ -1350,7 +1357,7 @@ abstract class ElectrumWalletBase
|
||||
);
|
||||
|
||||
final maxSpendable = utxoDetailsAll.allInputsAmount - feeNoChange;
|
||||
return maxSpendable > 0 ? maxSpendable : 0;
|
||||
return Money.fromInt(maxSpendable > 0 ? maxSpendable : 0, currency);
|
||||
}
|
||||
|
||||
Future<int> calcFee({
|
||||
@@ -1397,13 +1404,13 @@ abstract class ElectrumWalletBase
|
||||
final memo = transactionCredentials.outputs.first.memo;
|
||||
final coinTypeToSpendFrom = transactionCredentials.coinTypeToSpendFrom;
|
||||
|
||||
int credentialsAmount = 0;
|
||||
bool hasSilentPayment = false;
|
||||
var credentialsAmount = Money.zero(currency);
|
||||
var hasSilentPayment = false;
|
||||
|
||||
for (final out in transactionCredentials.outputs) {
|
||||
final outputAmount = out.formattedCryptoAmount!;
|
||||
final outputAmount = out.cryptoAmount;
|
||||
|
||||
if (!sendAll && _isBelowDust(outputAmount)) {
|
||||
if (!sendAll && _isBelowDust(outputAmount.amount)) {
|
||||
throw BitcoinTransactionNoDustException();
|
||||
}
|
||||
|
||||
@@ -1427,13 +1434,13 @@ abstract class ElectrumWalletBase
|
||||
// The value will be changed after estimating the Tx size and deducting the fee from the total to be sent
|
||||
outputs.add(BitcoinOutput(
|
||||
address: address,
|
||||
value: BigInt.from(0),
|
||||
value: BigInt.zero,
|
||||
isSilentPayment: isSilentPayment,
|
||||
));
|
||||
} else {
|
||||
outputs.add(BitcoinOutput(
|
||||
address: address,
|
||||
value: BigInt.from(outputAmount),
|
||||
value: outputAmount.amount,
|
||||
isSilentPayment: isSilentPayment,
|
||||
));
|
||||
}
|
||||
@@ -1486,7 +1493,7 @@ abstract class ElectrumWalletBase
|
||||
utxos: estimatedTx.utxos,
|
||||
outputs: updatedOutputs,
|
||||
publicKeys: estimatedTx.publicKeys,
|
||||
fee: BigInt.from(estimatedTx.fee),
|
||||
fee: estimatedTx.fee.amount,
|
||||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
@@ -1504,7 +1511,8 @@ abstract class ElectrumWalletBase
|
||||
network: network,
|
||||
hasChange: estimatedTx.hasChange,
|
||||
isSendAll: estimatedTx.isSendAll,
|
||||
hasTaprootInputs: false, // ToDo: (Konsti) Support Taproot,
|
||||
hasTaprootInputs: false,
|
||||
// ToDo: (Konsti) Support Taproot,
|
||||
isViewOnly: false,
|
||||
)..addListener((transaction) async {
|
||||
transactionHistory.addOne(transaction);
|
||||
@@ -1518,7 +1526,7 @@ abstract class ElectrumWalletBase
|
||||
txb = ForkedTransactionBuilder(
|
||||
utxos: estimatedTx.utxos,
|
||||
outputs: updatedOutputs,
|
||||
fee: BigInt.from(estimatedTx.fee),
|
||||
fee: estimatedTx.fee.amount,
|
||||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
@@ -1528,7 +1536,7 @@ abstract class ElectrumWalletBase
|
||||
txb = BitcoinTransactionBuilder(
|
||||
utxos: estimatedTx.utxos,
|
||||
outputs: updatedOutputs,
|
||||
fee: BigInt.from(estimatedTx.fee),
|
||||
fee: estimatedTx.fee.amount,
|
||||
network: network,
|
||||
memo: estimatedTx.memo,
|
||||
outputOrdering: BitcoinOrdering.none,
|
||||
@@ -2038,6 +2046,7 @@ abstract class ElectrumWalletBase
|
||||
unspentCoinsInfo.values.where((record) => record.walletId == id);
|
||||
|
||||
for (final element in currentWalletUnspentCoins) {
|
||||
if (element.isFrozen) continue;
|
||||
if (RegexUtils.addressTypeFromStr(element.address, network) is MwebAddress) continue;
|
||||
|
||||
final existUnspentCoins = unspentCoins.where((coin) => element == coin);
|
||||
@@ -2114,12 +2123,12 @@ abstract class ElectrumWalletBase
|
||||
allInputsAmount += outTransaction.amount.toInt();
|
||||
}
|
||||
|
||||
int totalOutAmount = bundle.originalTransaction.outputs
|
||||
final totalOutAmount = bundle.originalTransaction.outputs
|
||||
.fold<int>(0, (previousValue, element) => previousValue + element.amount.toInt());
|
||||
var currentFee = allInputsAmount - totalOutAmount;
|
||||
|
||||
int remainingFee = (newFee - currentFee > 0) ? newFee - currentFee : newFee;
|
||||
return totalBalance - receiverAmount - remainingFee >= networkDustAmount;
|
||||
final remainingFee = (newFee - currentFee > 0) ? newFee - currentFee : newFee;
|
||||
return totalBalance - receiverAmount - remainingFee >= networkDustAmount.toInt();
|
||||
}
|
||||
|
||||
Future<PendingBitcoinTransaction> replaceByFee(String hash, int newFee) async {
|
||||
@@ -2194,14 +2203,14 @@ abstract class ElectrumWalletBase
|
||||
int totalOutAmount =
|
||||
outputs.fold<int>(0, (previousValue, output) => previousValue + output.value.toInt());
|
||||
int currentFee = allInputsAmount - totalOutAmount;
|
||||
int remainingFee = newFee - currentFee;
|
||||
var remainingFee = BigInt.from(newFee - currentFee);
|
||||
|
||||
if (remainingFee <= 0) {
|
||||
if (remainingFee <= BigInt.zero) {
|
||||
throw Exception("New fee must be higher than the current fee.");
|
||||
}
|
||||
|
||||
// Deduct fee from change outputs first, if possible
|
||||
if (remainingFee > 0) {
|
||||
if (remainingFee > BigInt.zero) {
|
||||
final changeAddresses = walletAddresses.allAddresses.where((element) => element.isHidden);
|
||||
for (int i = outputs.length - 1; i >= 0; i--) {
|
||||
final output = outputs[i];
|
||||
@@ -2209,23 +2218,23 @@ abstract class ElectrumWalletBase
|
||||
.any((element) => element.address == output.address.toAddress(network));
|
||||
|
||||
if (isChange) {
|
||||
int outputAmount = output.value.toInt();
|
||||
final outputAmount = output.value;
|
||||
if (outputAmount > networkDustAmount) {
|
||||
int deduction = (outputAmount - networkDustAmount >= remainingFee)
|
||||
final deduction = (outputAmount - networkDustAmount >= remainingFee)
|
||||
? remainingFee
|
||||
: outputAmount - networkDustAmount;
|
||||
outputs[i] = BitcoinOutput(
|
||||
address: output.address, value: BigInt.from(outputAmount - deduction));
|
||||
address: output.address, value: outputAmount - deduction);
|
||||
remainingFee -= deduction;
|
||||
|
||||
if (remainingFee <= 0) break;
|
||||
if (remainingFee <= BigInt.zero) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If still not enough, add UTXOs until the fee is covered
|
||||
if (remainingFee > 0) {
|
||||
if (remainingFee > BigInt.zero) {
|
||||
final unusedUtxos = unspentCoins
|
||||
.where((utxo) => utxo.isSending && !utxo.isFrozen && utxo.confirmations! > 0)
|
||||
.toList();
|
||||
@@ -2253,52 +2262,51 @@ abstract class ElectrumWalletBase
|
||||
));
|
||||
|
||||
allInputsAmount += utxo.value;
|
||||
remainingFee -= utxo.value;
|
||||
remainingFee -= BigInt.from(utxo.value);
|
||||
|
||||
if (remainingFee < 0) {
|
||||
if (remainingFee < BigInt.zero) {
|
||||
final changeOutput = outputs.firstWhereOrNull((output) => walletAddresses.allAddresses
|
||||
.any((addr) => addr.address == output.address.toAddress(network)));
|
||||
if (changeOutput != null) {
|
||||
final newValue = changeOutput.value.toInt() + (-remainingFee);
|
||||
final newValue = changeOutput.value + (-remainingFee);
|
||||
outputs[outputs.indexOf(changeOutput)] =
|
||||
BitcoinOutput(address: changeOutput.address, value: BigInt.from(newValue));
|
||||
BitcoinOutput(address: changeOutput.address, value: newValue);
|
||||
} else {
|
||||
final changeAddress = await walletAddresses.getChangeAddress();
|
||||
outputs.add(BitcoinOutput(
|
||||
address: RegexUtils.addressTypeFromStr(changeAddress.address, network),
|
||||
value: BigInt.from(-remainingFee)));
|
||||
value: -remainingFee));
|
||||
}
|
||||
|
||||
remainingFee = 0;
|
||||
remainingFee = BigInt.zero;
|
||||
break;
|
||||
}
|
||||
|
||||
if (remainingFee <= 0) break;
|
||||
if (remainingFee <= BigInt.zero) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Deduct from the receiver's output if remaining fee is still greater than 0
|
||||
if (remainingFee > 0) {
|
||||
if (remainingFee > BigInt.zero) {
|
||||
for (int i = 0; i < outputs.length; i++) {
|
||||
final output = outputs[i];
|
||||
int outputAmount = output.value.toInt();
|
||||
final outputAmount = output.value;
|
||||
|
||||
if (outputAmount > networkDustAmount) {
|
||||
int deduction = (outputAmount - networkDustAmount >= remainingFee)
|
||||
final deduction = (outputAmount - networkDustAmount >= remainingFee)
|
||||
? remainingFee
|
||||
: outputAmount - networkDustAmount;
|
||||
|
||||
outputs[i] = BitcoinOutput(
|
||||
address: output.address, value: BigInt.from(outputAmount - deduction));
|
||||
outputs[i] = BitcoinOutput(address: output.address, value: outputAmount - deduction);
|
||||
remainingFee -= deduction;
|
||||
|
||||
if (remainingFee <= 0) break;
|
||||
if (remainingFee <= BigInt.zero) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final check if the remaining fee couldn't be deducted
|
||||
if (remainingFee > 0) {
|
||||
if (remainingFee > BigInt.zero) {
|
||||
throw Exception("Not enough funds to cover the fee.");
|
||||
}
|
||||
|
||||
@@ -2343,8 +2351,8 @@ abstract class ElectrumWalletBase
|
||||
transaction,
|
||||
type,
|
||||
electrumClient: electrumClient,
|
||||
amount: sendingAmount,
|
||||
fee: newFee,
|
||||
amount: Money.fromInt(sendingAmount, currency),
|
||||
fee: Money.fromInt(newFee, currency),
|
||||
network: network,
|
||||
hasChange: changeOutputs.isNotEmpty,
|
||||
feeRate: newFee.toString(),
|
||||
@@ -2585,16 +2593,15 @@ abstract class ElectrumWalletBase
|
||||
|
||||
final history = await electrumClient.getHistory(addressRecord.getScriptHash(network));
|
||||
|
||||
|
||||
if (history.isNotEmpty) {
|
||||
addressRecord.setAsUsed();
|
||||
walletAddresses.clearLockIfMatches(addressRecord.type, addressRecord.address);
|
||||
|
||||
if(this is BitcoinWallet) {
|
||||
if (this is BitcoinWallet) {
|
||||
//removes transactions no longer returned by the api, presumed replaced/invalid.
|
||||
transactionHistory.transactions.removeWhere(
|
||||
(hash, tx) =>
|
||||
tx.outputAddresses != null &&
|
||||
(hash, tx) =>
|
||||
tx.outputAddresses != null &&
|
||||
tx.outputAddresses!.contains(addressRecord.address) &&
|
||||
!history.any((newTransaction) => newTransaction['tx_hash'] == hash),
|
||||
);
|
||||
@@ -3480,7 +3487,12 @@ abstract class ElectrumWalletBase
|
||||
// if we got null balance responses from the server, set our connection status to lost and return our last known balance:
|
||||
printV("got null balance responses from the server, setting connection status to lost");
|
||||
syncStatus = LostConnectionSyncStatus();
|
||||
return balance[currency] ?? ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
||||
return balance[currency] ??
|
||||
ElectrumBalance(
|
||||
confirmed: Money.zero(currency),
|
||||
unconfirmed: Money.zero(currency),
|
||||
frozen: Money.zero(currency),
|
||||
);
|
||||
}
|
||||
|
||||
for (var i = 0; i < balances.length; i++) {
|
||||
@@ -3499,9 +3511,9 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
return ElectrumBalance(
|
||||
confirmed: totalConfirmed,
|
||||
unconfirmed: totalUnconfirmed,
|
||||
frozen: totalFrozen,
|
||||
confirmed: Money.fromInt(totalConfirmed, currency),
|
||||
unconfirmed: Money.fromInt(totalUnconfirmed, currency),
|
||||
frozen: Money.fromInt(totalFrozen, currency),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3819,12 +3831,6 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String formatCryptoAmount(String amount) {
|
||||
final amountBigInt = BigInt.parse(amount);
|
||||
return currency.formatAmount(amountBigInt);
|
||||
}
|
||||
|
||||
/// Checks the health of the socket connection
|
||||
/// and triggers a full reconnection if needed
|
||||
@override
|
||||
@@ -4285,8 +4291,8 @@ Future<void> _handleScanSilentPayments(ScanData scanData) async {
|
||||
WalletType.bitcoin,
|
||||
id: txid,
|
||||
height: tweakHeight,
|
||||
amount: 0,
|
||||
fee: 0,
|
||||
amount: Money.zero(CryptoCurrency.btc),
|
||||
fee: Money.zero(CryptoCurrency.btc),
|
||||
direction: TransactionDirection.incoming,
|
||||
isReplaced: false,
|
||||
date: scanData.network == BitcoinNetwork.mainnet
|
||||
@@ -4352,7 +4358,7 @@ Future<void> _handleScanSilentPayments(ScanData scanData) async {
|
||||
txInfo.unspents!.add(unspent);
|
||||
}
|
||||
|
||||
txInfo.amount += unspent.value;
|
||||
txInfo.amount += Money.fromInt(unspent.value, txInfo.amount.currency);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4422,8 +4428,8 @@ class EstimatedTxResult {
|
||||
final List<UtxoWithAddress> utxos;
|
||||
final List<ECPrivateInfo> inputPrivKeyInfos;
|
||||
final Map<String, PublicKeyWithDerivationPath> publicKeys; // PubKey to derivationPath
|
||||
final int fee;
|
||||
final int amount;
|
||||
final Money fee;
|
||||
final Money amount;
|
||||
final bool spendsSilentPayment;
|
||||
|
||||
// final bool sendsToSilentPayment;
|
||||
|
||||
@@ -170,9 +170,22 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
@computed
|
||||
List<BitcoinAddressRecord> get allAddresses => _addresses;
|
||||
|
||||
@observable
|
||||
bool addressRefreshToggle = false;
|
||||
|
||||
@action
|
||||
String getFreshAddress() {
|
||||
addressRefreshToggle = !addressRefreshToggle;
|
||||
return address;
|
||||
}
|
||||
|
||||
@override
|
||||
String get addressForExchange => getFreshAddress();
|
||||
|
||||
@override
|
||||
@computed
|
||||
String get address {
|
||||
final _ = addressRefreshToggle;
|
||||
if (addressPageType == SilentPaymentsAddresType.p2sp) {
|
||||
if (activeSilentAddress != null) {
|
||||
return activeSilentAddress!;
|
||||
@@ -198,7 +211,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
];
|
||||
|
||||
final typeMatchingReceiveAddressesAll =
|
||||
typeMatchingAddressesAll.where((addr) => !addr.isUsed).toList();
|
||||
typeMatchingAddressesAll.where((addr) => !addr.isUsed && !hiddenAddresses.contains(addr.address)).toList();
|
||||
final typeMatchingReceiveAddresses = <BitcoinAddressRecord>[
|
||||
...typeMatchingReceiveAddressesAll.where((a) => !a.isLegacyDerivation),
|
||||
...typeMatchingReceiveAddressesAll.where((a) => a.isLegacyDerivation),
|
||||
@@ -221,10 +234,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
}
|
||||
|
||||
final locked = lockedReceiveAddressByType[addressPageType];
|
||||
if (locked != null) return locked;
|
||||
if (locked != null && !hiddenAddresses.contains(locked)) return locked;
|
||||
|
||||
final prev = previousAddressRecord;
|
||||
if (prev != null && prev.type == addressPageType && !prev.isUsed && !prev.isLegacyDerivation) {
|
||||
if (prev != null &&
|
||||
prev.type == addressPageType &&
|
||||
!prev.isUsed &&
|
||||
!prev.isLegacyDerivation &&
|
||||
!hiddenAddresses.contains(prev.address)) {
|
||||
return prev.address;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'dart:convert';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/electrum_balance.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_bitcoin/electrum_derivations.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
@@ -92,9 +94,15 @@ class ElectrumWalletSnapshot {
|
||||
final useLightning = data['useLightning'] as bool? ?? true;
|
||||
final cachedLightningAddress = data['cachedLightningAddress'] as String?;
|
||||
|
||||
final balance = ElectrumBalance.fromJSON(data['balance'] as String?) ??
|
||||
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
||||
final lightningBalance = ElectrumBalance.fromJSON(data['lightningBalance'] as String?);
|
||||
final currency = walletTypeToCryptoCurrency(type);
|
||||
final balance = ElectrumBalance.fromJSON(data['balance'] as String?, currency) ??
|
||||
ElectrumBalance(
|
||||
confirmed: Money.zero(currency),
|
||||
unconfirmed: Money.zero(currency),
|
||||
frozen: Money.zero(currency),
|
||||
);
|
||||
final lightningBalance =
|
||||
ElectrumBalance.fromJSON(data['lightningBalance'] as String?, currency);
|
||||
|
||||
var regularAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||
var changeAddressIndexByType = {SegwitAddresType.p2wpkh.toString(): 0};
|
||||
|
||||
@@ -6,6 +6,9 @@ import 'package:breez_sdk_spark_flutter/breez_sdk_spark.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_bitcoin/lightning/pending_lightning_transaction.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -20,7 +23,7 @@ class LightningWallet {
|
||||
final String apiKey;
|
||||
final String lnurlDomain;
|
||||
final Network network;
|
||||
late BreezSdk sdk;
|
||||
BreezSdk? _sdk;
|
||||
|
||||
String? cachedAddress;
|
||||
|
||||
@@ -38,6 +41,10 @@ class LightningWallet {
|
||||
|
||||
static bool get isAvailable => Platform.isIOS || Platform.isAndroid || Platform.isMacOS;
|
||||
|
||||
Currency get currency => CryptoCurrency.btcln;
|
||||
|
||||
BreezSdk get sdk => _sdk!;
|
||||
|
||||
StreamSubscription<SdkEvent>? _eventSubscription;
|
||||
Stream<SdkEvent>? _eventStream;
|
||||
|
||||
@@ -47,9 +54,25 @@ class LightningWallet {
|
||||
|
||||
void _subscribeToLogStream(File logFile) {
|
||||
_logSubscription = _logStream?.listen((logEntry) {
|
||||
logFile.writeAsStringSync("[${logEntry.level}] ${logEntry.line}\n", mode: FileMode.append);
|
||||
try {
|
||||
// Check if file exists before writing (optional, but safer)
|
||||
if (!logFile.existsSync()) {
|
||||
logFile.createSync(recursive: true);
|
||||
}
|
||||
logFile.writeAsStringSync("[${logEntry.level}] ${logEntry.line}\n", mode: FileMode.append);
|
||||
} catch (e) {
|
||||
// Silently fail or use printV(e) so it doesn't crash the app
|
||||
printV("Failed to write to log: $e");
|
||||
}
|
||||
}, onError: (e) {
|
||||
logFile.writeAsStringSync("[ERROR] $e\n", mode: FileMode.append);
|
||||
try {
|
||||
if (!logFile.existsSync()) {
|
||||
logFile.createSync(recursive: true);
|
||||
}
|
||||
logFile.writeAsStringSync("[ERROR] $e\n", mode: FileMode.append);
|
||||
} catch (err) {
|
||||
printV("Failed to write error to log: $err");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -76,7 +99,7 @@ class LightningWallet {
|
||||
storageDir: "$appPath/.breez/",
|
||||
);
|
||||
|
||||
sdk = await connect(request: connectRequest);
|
||||
_sdk = await connect(request: connectRequest);
|
||||
|
||||
_eventStream ??= sdk.addEventListener().asBroadcastStream();
|
||||
_logStream ??= initLogging().asBroadcastStream();
|
||||
@@ -100,7 +123,7 @@ class LightningWallet {
|
||||
|
||||
Future<void> close() async {
|
||||
_eventSubscription?.cancel();
|
||||
await sdk.disconnect();
|
||||
await _sdk?.disconnect();
|
||||
_logSubscription?.cancel();
|
||||
}
|
||||
|
||||
@@ -126,13 +149,14 @@ class LightningWallet {
|
||||
request: ReceivePaymentRequest(paymentMethod: ReceivePaymentMethod.bitcoinAddress())))
|
||||
.paymentRequest;
|
||||
|
||||
Future<BigInt> getBalance() async {
|
||||
Future<Money> getBalance() async {
|
||||
try {
|
||||
return (await sdk.getInfo(request: GetInfoRequest(ensureSynced: true))).balanceSats;
|
||||
return Money((await sdk.getInfo(request: GetInfoRequest(ensureSynced: true))).balanceSats,
|
||||
CryptoCurrency.btcln);
|
||||
} on SdkError_Generic catch (_) {
|
||||
} on SdkError_NetworkError catch (_) {}
|
||||
|
||||
return BigInt.zero;
|
||||
return Money.zero(CryptoCurrency.btcln);
|
||||
}
|
||||
|
||||
Future<String> registerAddress(String username) async {
|
||||
@@ -188,11 +212,16 @@ class LightningWallet {
|
||||
final lightningFeeSats = paymentMethod.lightningFeeSats;
|
||||
final sparkTransferFeeSats = paymentMethod.sparkTransferFeeSats;
|
||||
|
||||
final baseAmount = request.amount ?? amountSats;
|
||||
final amount = baseAmount != null
|
||||
? Money(baseAmount, currency)
|
||||
: Money(paymentMethod.invoiceDetails.amountMsat ?? BigInt.zero, currency) /
|
||||
BigInt.from(1000);
|
||||
|
||||
return PendingLightningTransaction(
|
||||
id: paymentMethod.invoiceDetails.paymentHash,
|
||||
amount: request.amount?.toInt() ?? amountSats?.toInt() ??
|
||||
((paymentMethod.invoiceDetails.amountMsat?.toInt() ?? 0) / 1000).round(),
|
||||
fee: lightningFeeSats.toInt() + (sparkTransferFeeSats?.toInt() ?? 0),
|
||||
amount: amount,
|
||||
fee: Money(lightningFeeSats + (sparkTransferFeeSats ?? BigInt.zero), currency),
|
||||
commitOverride: () async {
|
||||
try {
|
||||
final res = await sdk.sendPayment(
|
||||
@@ -230,12 +259,10 @@ class LightningWallet {
|
||||
|
||||
final prepareResponse = await sdk.prepareLnurlPay(request: request);
|
||||
|
||||
final feeSats = prepareResponse.feeSats;
|
||||
|
||||
return PendingLightningTransaction(
|
||||
id: prepareResponse.invoiceDetails.paymentHash,
|
||||
amount: prepareResponse.amountSats.toInt(),
|
||||
fee: feeSats.toInt(),
|
||||
amount: Money(prepareResponse.amountSats, currency),
|
||||
fee: Money(prepareResponse.feeSats, currency),
|
||||
commitOverride: () async {
|
||||
final res =
|
||||
await sdk.lnurlPay(request: LnurlPayRequest(prepareResponse: prepareResponse));
|
||||
@@ -256,27 +283,26 @@ class LightningWallet {
|
||||
final feeQuote = paymentMethod.feeQuote;
|
||||
|
||||
OnchainConfirmationSpeed onchainConfirmationSpeed;
|
||||
int fee;
|
||||
BigInt fee;
|
||||
switch (priority) {
|
||||
case BitcoinTransactionPriority.fast:
|
||||
fee = (feeQuote.speedFast.userFeeSat + feeQuote.speedFast.l1BroadcastFeeSat).toInt();
|
||||
fee = feeQuote.speedFast.userFeeSat + feeQuote.speedFast.l1BroadcastFeeSat;
|
||||
onchainConfirmationSpeed = OnchainConfirmationSpeed.fast;
|
||||
break;
|
||||
case BitcoinTransactionPriority.medium:
|
||||
fee =
|
||||
(feeQuote.speedMedium.userFeeSat + feeQuote.speedMedium.l1BroadcastFeeSat).toInt();
|
||||
fee = feeQuote.speedMedium.userFeeSat + feeQuote.speedMedium.l1BroadcastFeeSat;
|
||||
onchainConfirmationSpeed = OnchainConfirmationSpeed.medium;
|
||||
break;
|
||||
case BitcoinTransactionPriority.slow:
|
||||
default:
|
||||
fee = (feeQuote.speedSlow.userFeeSat + feeQuote.speedSlow.l1BroadcastFeeSat).toInt();
|
||||
fee = feeQuote.speedSlow.userFeeSat + feeQuote.speedSlow.l1BroadcastFeeSat;
|
||||
onchainConfirmationSpeed = OnchainConfirmationSpeed.slow;
|
||||
}
|
||||
|
||||
return PendingLightningTransaction(
|
||||
id: "", // ToDo: Find out where to get it
|
||||
amount: prepareResponse.amount.toInt(),
|
||||
fee: fee,
|
||||
amount: Money(prepareResponse.amount, currency),
|
||||
fee: Money(fee, currency),
|
||||
commitOverride: () async {
|
||||
final options =
|
||||
SendPaymentOptions.bitcoinAddress(confirmationSpeed: onchainConfirmationSpeed);
|
||||
@@ -404,7 +430,7 @@ class LightningWallet {
|
||||
}
|
||||
|
||||
ElectrumTransactionInfo _getElectrumTransactionInfoFromPayment(Payment payment) {
|
||||
TransactionDirection direction = TransactionDirection.outgoing;
|
||||
var direction = TransactionDirection.outgoing;
|
||||
|
||||
if (payment.paymentType == PaymentType.receive) {
|
||||
direction = TransactionDirection.incoming;
|
||||
@@ -416,10 +442,10 @@ class LightningWallet {
|
||||
return ElectrumTransactionInfo(
|
||||
WalletType.bitcoin,
|
||||
id: payment.id,
|
||||
amount: payment.amount.toInt(),
|
||||
amount: Money(payment.amount, currency),
|
||||
direction: direction,
|
||||
isPending: payment.status == PaymentStatus.pending,
|
||||
fee: payment.fees.toInt(),
|
||||
fee: Money(payment.fees, currency),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(payment.timestamp.toInt() * 1000),
|
||||
confirmations: payment.status == PaymentStatus.pending ? 0 : 10,
|
||||
additionalInfo: {"isLightning": true},
|
||||
@@ -430,10 +456,10 @@ class LightningWallet {
|
||||
return ElectrumTransactionInfo(
|
||||
WalletType.bitcoin,
|
||||
id: deposit.txid,
|
||||
amount: deposit.amountSats.toInt(),
|
||||
amount: Money(deposit.amountSats, currency),
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: true,
|
||||
fee: 0,
|
||||
fee: Money.zero(currency),
|
||||
date: DateTime.now(),
|
||||
confirmations: 0,
|
||||
additionalInfo: {"isLightning": true, "isSparkDeposit": true},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
|
||||
class PendingLightningTransaction with PendingTransaction {
|
||||
@@ -10,8 +10,7 @@ class PendingLightningTransaction with PendingTransaction {
|
||||
required this.commitOverride,
|
||||
});
|
||||
|
||||
final int amount;
|
||||
final int fee;
|
||||
|
||||
final bool isSendAll;
|
||||
Future<String> Function() commitOverride;
|
||||
final List<void Function()> _listeners =[];
|
||||
@@ -19,17 +18,17 @@ class PendingLightningTransaction with PendingTransaction {
|
||||
@override
|
||||
String id;
|
||||
|
||||
@override
|
||||
final Money amount;
|
||||
|
||||
@override
|
||||
final Money fee;
|
||||
|
||||
@override
|
||||
String get hex => "";
|
||||
|
||||
@override
|
||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue BTC";
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => bitcoinAmountToString(amount: fee);
|
||||
String get amountFormatted => amount.toString();
|
||||
|
||||
@override
|
||||
int? get outputCount => 1;
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:collection/collection.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cw_bitcoin/address_from_output.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/mweb_utxo.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
@@ -17,12 +18,9 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:bitcoin_base/src/crypto/keypair/sign_utils.dart';
|
||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:blockchain_utils/signer/ecdsa_signing_key.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:convert/convert.dart' as convert;
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||
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/electrum_balance.dart';
|
||||
@@ -35,25 +33,18 @@ import 'package:cw_bitcoin/litecoin_wallet_addresses.dart';
|
||||
import 'package:cw_bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cw_bitcoin/psbt/transaction_builder.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
import 'package:cw_core/mweb_utxo.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/output_info.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/unspent_coin_type.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_keys_file.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_mweb/cw_mweb.dart';
|
||||
import 'package:cw_mweb/mwebd.pbgrpc.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
@@ -587,8 +578,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
WalletType.litecoin,
|
||||
id: utxo.outputId,
|
||||
height: utxo.height,
|
||||
amount: utxo.value.toInt(),
|
||||
fee: 0,
|
||||
amount: Money.fromInt(utxo.value, currency),
|
||||
fee: Money.zero(currency),
|
||||
direction: TransactionDirection.incoming,
|
||||
isPending: utxo.height == 0,
|
||||
date: date,
|
||||
@@ -734,9 +725,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
|
||||
Future<void> checkMwebUtxosSpent() async {
|
||||
printV("checkMwebUtxosSpent() called!");
|
||||
if (!mwebEnabled) {
|
||||
return;
|
||||
}
|
||||
if (!mwebEnabled) return;
|
||||
|
||||
final pendingOutgoingTransactions = transactionHistory.transactions.values
|
||||
.where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending);
|
||||
@@ -763,8 +752,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
final height = await electrumClient.getCurrentBlockChainTip();
|
||||
if (height == null || status.blockHeaderHeight != height) return;
|
||||
if (status.mwebUtxosHeight != height) return; // we aren't synced
|
||||
int amount = 0;
|
||||
Set<String> inputAddresses = {};
|
||||
var amount = 0;
|
||||
var inputAddresses = <String>{};
|
||||
var output = convert.AccumulatorSink<Digest>();
|
||||
var input = sha256.startChunkedConversion(output);
|
||||
|
||||
@@ -777,8 +766,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
if (!inputAddresses.contains(utxo.address)) {
|
||||
addressRecord.txCount++;
|
||||
}
|
||||
addressRecord.balance -= utxo.value.toInt();
|
||||
amount += utxo.value.toInt();
|
||||
addressRecord.balance -= utxo.value;
|
||||
amount += utxo.value;
|
||||
inputAddresses.add(utxo.address);
|
||||
input.add(hex.decode(outputId));
|
||||
}
|
||||
@@ -790,8 +779,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
WalletType.litecoin,
|
||||
id: digest.toString(),
|
||||
height: height,
|
||||
amount: amount,
|
||||
fee: 0,
|
||||
amount: Money.fromInt(amount, currency),
|
||||
fee: Money.zero(currency),
|
||||
direction: TransactionDirection.outgoing,
|
||||
isPending: false,
|
||||
date: DateTime.fromMillisecondsSinceEpoch(status.blockTime * 1000),
|
||||
@@ -921,10 +910,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
// update unspent balances:
|
||||
await updateUnspent();
|
||||
|
||||
int confirmed = balance.confirmed;
|
||||
int unconfirmed = balance.unconfirmed;
|
||||
int confirmedMweb = 0;
|
||||
int unconfirmedMweb = 0;
|
||||
var confirmedMweb = 0;
|
||||
var unconfirmedMweb = 0;
|
||||
try {
|
||||
mwebUtxosBox.values.forEach((utxo) {
|
||||
bool isConfirmed = utxo.height > 0;
|
||||
@@ -933,20 +920,20 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
"utxo: ${isConfirmed ? "confirmed" : "unconfirmed"} ${utxo.spent ? "spent" : "unspent"} ${utxo.outputId} ${utxo.height} ${utxo.value}");
|
||||
|
||||
if (isConfirmed) {
|
||||
confirmedMweb += utxo.value.toInt();
|
||||
confirmedMweb += utxo.value;
|
||||
}
|
||||
|
||||
if (isConfirmed && utxo.spent) {
|
||||
unconfirmedMweb -= utxo.value.toInt();
|
||||
unconfirmedMweb -= utxo.value;
|
||||
}
|
||||
|
||||
if (!isConfirmed && !utxo.spent) {
|
||||
unconfirmedMweb += utxo.value.toInt();
|
||||
unconfirmedMweb += utxo.value;
|
||||
}
|
||||
});
|
||||
} catch (_) {}
|
||||
|
||||
for (var addressRecord in walletAddresses.allAddresses) {
|
||||
for (final addressRecord in walletAddresses.allAddresses) {
|
||||
addressRecord.balance = 0;
|
||||
addressRecord.txCount = 0;
|
||||
}
|
||||
@@ -990,11 +977,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
|
||||
return ElectrumBalance(
|
||||
confirmed: confirmed,
|
||||
unconfirmed: unconfirmed,
|
||||
frozen: balance.frozen.toInt(),
|
||||
secondConfirmed: confirmedMweb,
|
||||
secondUnconfirmed: unconfirmedMweb,
|
||||
confirmed: balance.confirmed,
|
||||
unconfirmed: balance.unconfirmed,
|
||||
frozen: balance.frozen,
|
||||
secondConfirmed: Money.fromInt(confirmedMweb, currency),
|
||||
secondUnconfirmed: Money.fromInt(unconfirmedMweb, currency),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1371,8 +1358,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
||||
btcTx,
|
||||
type,
|
||||
electrumClient: electrumClient,
|
||||
amount: 0,
|
||||
fee: resp.fee.toInt(),
|
||||
amount: Money.zero(currency),
|
||||
fee: Money.fromInt(resp.fee.toInt(), currency),
|
||||
feeRate: "",
|
||||
network: network,
|
||||
hasChange: resp.recipient.length > 1,
|
||||
|
||||
@@ -228,13 +228,21 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
||||
@override
|
||||
String get addressForExchange {
|
||||
// don't use mweb addresses for exchange refund address:
|
||||
try {
|
||||
final addresses = receiveAddresses
|
||||
.where((element) => element.type == SegwitAddresType.p2wpkh && !element.isUsed);
|
||||
return addresses.first.address;
|
||||
} catch (_) {
|
||||
return receiveAddresses.first.address;
|
||||
|
||||
final current = getFreshAddress();
|
||||
final bool isMweb = receiveAddresses
|
||||
.any((e) => e.address == current && e.type == SegwitAddresType.mweb);
|
||||
|
||||
if (isMweb) {
|
||||
final segwit = receiveAddresses
|
||||
.where((e) => e.type == SegwitAddresType.p2wpkh && !e.isUsed && !e.isHidden && !hiddenAddresses.contains(e.address))
|
||||
.map((e) => e.address)
|
||||
.toList();
|
||||
|
||||
return segwit.isNotEmpty ? segwit.first : current;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bbqrdart/bbqrdart.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:cw_bitcoin/exceptions.dart';
|
||||
@@ -9,7 +8,6 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_bitcoin/electrum.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
@@ -42,8 +40,6 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
final WalletType type;
|
||||
final BtcTransaction _tx;
|
||||
final ElectrumClient electrumClient;
|
||||
final int amount;
|
||||
final int fee;
|
||||
final String feeRate;
|
||||
final BasedUtxoNetwork? network;
|
||||
final bool isSendAll;
|
||||
@@ -61,6 +57,12 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
|
||||
Uint8List? unsignedPsbt;
|
||||
|
||||
@override
|
||||
final Money amount;
|
||||
|
||||
@override
|
||||
final Money fee;
|
||||
|
||||
@override
|
||||
String get id => idOverride ?? _tx.txId();
|
||||
|
||||
@@ -68,30 +70,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
String get hex => hexOverride ?? _tx.serialize();
|
||||
|
||||
@override
|
||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue ${_feeCurrency}";
|
||||
|
||||
String get _feeCurrency {
|
||||
switch(type) {
|
||||
case WalletType.bitcoin:
|
||||
return "BTC";
|
||||
case WalletType.litecoin:
|
||||
return "LTC";
|
||||
case WalletType.bitcoinCash:
|
||||
return "BCH";
|
||||
case WalletType.dogecoin:
|
||||
return "DOGE";
|
||||
default:
|
||||
return type.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => bitcoinAmountToString(amount: fee);
|
||||
String get amountFormatted => amount.toString();
|
||||
|
||||
@override
|
||||
int? get outputCount => _tx.outputs.length;
|
||||
@@ -155,14 +134,12 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
|
||||
Future<void> _ltcCommit() async {
|
||||
try {
|
||||
final resp = await CwMweb.broadcast(
|
||||
BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
|
||||
final resp = await CwMweb.broadcast(BroadcastRequest(rawTx: BytesUtils.fromHexString(hex)));
|
||||
idOverride = resp.txid;
|
||||
} on GrpcError catch (e) {
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: e.message);
|
||||
} catch (e) {
|
||||
throw BitcoinTransactionCommitFailed(
|
||||
errorMessage: "Unknown error: ${e.toString()}");
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: "Unknown error: ${e.toString()}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,22 +158,23 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
_listeners.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
||||
void addListener(
|
||||
void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
void addListener(void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
isReplaced: false,
|
||||
confirmations: 0,
|
||||
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
|
||||
outputAddresses: outputAddresses,
|
||||
fee: fee);
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(
|
||||
type,
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
isReplaced: false,
|
||||
confirmations: 0,
|
||||
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
|
||||
outputAddresses: outputAddresses,
|
||||
fee: fee,
|
||||
);
|
||||
|
||||
@override
|
||||
bool shouldCommitUR() => isViewOnly;
|
||||
@@ -207,8 +185,8 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
var cborEncoder = CBOREncoder();
|
||||
cborEncoder.encodeBytes(sourceBytes);
|
||||
var ur = UR("psbt", cborEncoder.getBytes());
|
||||
var urLegacy = UR("crypto-psbt", cborEncoder.getBytes());
|
||||
// var ur = UR("psbt", Uint8List.fromList(List.generate(64*1024, (int x) => x % 256)));
|
||||
var urLegacy = UR("crypto-psbt", cborEncoder.getBytes());
|
||||
// var ur = UR("psbt", Uint8List.fromList(List.generate(64*1024, (int x) => x % 256)));
|
||||
var encoded = UREncoder(ur, 120);
|
||||
var encodedLegacy = UREncoder(urLegacy, 120);
|
||||
List<String> values = [];
|
||||
@@ -230,8 +208,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||
return Future.value({
|
||||
"Cupcake bcur": values.join("\n"),
|
||||
"ColdCard bbqr": bbqr.join("\n"),
|
||||
if (kDebugMode)
|
||||
"SeedSigner bcur legacy": valuesLegacy.join("\n"),
|
||||
if (kDebugMode) "SeedSigner bcur legacy": valuesLegacy.join("\n"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+41
-102
@@ -5,23 +5,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "76.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
version: "92.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.0"
|
||||
version: "9.0.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -151,10 +146,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
||||
sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "4.0.6"
|
||||
build_cli_annotations:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -167,10 +162,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.3.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -179,30 +174,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.4"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||
sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.15"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
version: "2.15.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -232,10 +211,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -326,10 +305,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.8"
|
||||
version: "3.1.3"
|
||||
dart_varuint_bitcoin:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -449,14 +428,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -506,18 +477,10 @@ packages:
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
hive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hive
|
||||
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
hive_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive_generator
|
||||
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||
name: hive
|
||||
sha256: "5184b6ea455689754578267c31c3b0b329cccf20a5f2bfdab6941c29c3d3c09a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
@@ -589,26 +552,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
ledger_bitcoin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -651,38 +614,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -695,26 +650,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mobx
|
||||
sha256: bf1a90e5bcfd2851fc6984e20eef69557c65d9e4d0a88f5be4cf72c9819ce6b0
|
||||
sha256: "25ea9a3ee69243d578fb6288721d8eeb18550d0391c32f896d377d994154bff2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.6.0"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
|
||||
sha256: "6529bcae074838004b58ad01fbe4966be182a176aacfb723686985986ebac19f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.1"
|
||||
version: "2.7.7"
|
||||
mockito:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mockito
|
||||
sha256: f99d8d072e249f719a5531735d146d8cf04c580d93920b04de75bef6dfb2daf6
|
||||
sha256: eff30d002f0c8bf073b6f929df4483b543133fcafce056870163587b03f1d422
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.5"
|
||||
version: "5.6.4"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1000,18 +955,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.5"
|
||||
version: "4.2.3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1153,18 +1100,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "0.7.10"
|
||||
torch_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1306,10 +1245,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1383,5 +1322,5 @@ packages:
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
sdks:
|
||||
dart: ">=3.8.0 <4.0.0"
|
||||
dart: ">=3.9.0 <4.0.0"
|
||||
flutter: ">=3.29.0"
|
||||
|
||||
@@ -14,8 +14,8 @@ dependencies:
|
||||
sdk: flutter
|
||||
path_provider: ^2.0.11
|
||||
http: ^1.1.0
|
||||
mobx: ^2.0.7+4
|
||||
flutter_mobx: ^2.0.6+1
|
||||
mobx: ^2.6.0
|
||||
flutter_mobx: ^2.3.0
|
||||
intl: any
|
||||
shared_preferences: ^2.0.15
|
||||
cw_core:
|
||||
@@ -77,10 +77,10 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.15
|
||||
build_resolvers: ^2.4.4
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^2.0.1
|
||||
build_runner: ^2.15.0
|
||||
mobx_codegen: ^2.7.6
|
||||
# hive_generator: ^2.0.1
|
||||
hive: 2.0.1
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import 'package:cw_bitcoin/exceptions.dart';
|
||||
import 'package:bitbox/bitbox.dart' as bitbox;
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_bitcoin/electrum.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
class PendingBitcoinCashTransaction with PendingTransaction {
|
||||
PendingBitcoinCashTransaction(this._tx, this.type,
|
||||
{required this.electrumClient,
|
||||
required this.amount,
|
||||
required this.fee,
|
||||
required this.hasChange,
|
||||
required this.isSendAll})
|
||||
: _listeners = <void Function(ElectrumTransactionInfo transaction)>[];
|
||||
|
||||
final WalletType type;
|
||||
final bitbox.Transaction _tx;
|
||||
final ElectrumClient electrumClient;
|
||||
final int amount;
|
||||
final int fee;
|
||||
final bool hasChange;
|
||||
final bool isSendAll;
|
||||
|
||||
@override
|
||||
String get id => _tx.getId();
|
||||
|
||||
@override
|
||||
String get hex => _tx.toHex();
|
||||
|
||||
@override
|
||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue BCH";
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => bitcoinAmountToString(amount: fee);
|
||||
|
||||
final List<void Function(ElectrumTransactionInfo transaction)> _listeners;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
int? callId;
|
||||
|
||||
final result = await electrumClient.broadcastTransaction(
|
||||
transactionRaw: hex, idCallback: (id) => callId = id);
|
||||
|
||||
if (result.isEmpty) {
|
||||
if (callId != null) {
|
||||
final error = electrumClient.getErrorMessage(callId!);
|
||||
|
||||
if (error.contains("dust")) {
|
||||
if (hasChange) {
|
||||
throw BitcoinTransactionCommitFailedDustChange();
|
||||
} else if (!isSendAll) {
|
||||
throw BitcoinTransactionCommitFailedDustOutput();
|
||||
} else {
|
||||
throw BitcoinTransactionCommitFailedDustOutputSendAll();
|
||||
}
|
||||
}
|
||||
|
||||
if (error.contains("bad-txns-vout-negative")) {
|
||||
throw BitcoinTransactionCommitFailedVoutNegative();
|
||||
}
|
||||
throw BitcoinTransactionCommitFailed(errorMessage: error);
|
||||
}
|
||||
|
||||
throw BitcoinTransactionCommitFailed();
|
||||
}
|
||||
|
||||
_listeners.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
||||
void addListener(void Function(ElectrumTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type,
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
confirmations: 0,
|
||||
fee: fee,
|
||||
isReplaced: false,
|
||||
);
|
||||
@override
|
||||
Future<Map<String, String>> commitUR() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ dependencies:
|
||||
bip39: ^1.0.6
|
||||
bip32: ^2.0.0
|
||||
path_provider: ^2.0.11
|
||||
mobx: ^2.0.7+4
|
||||
flutter_mobx: ^2.0.6+1
|
||||
mobx: ^2.6.0
|
||||
flutter_mobx: ^2.3.0
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
cw_bitcoin:
|
||||
@@ -33,9 +33,10 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.15
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^2.0.1
|
||||
build_runner: ^2.15.0
|
||||
mobx_codegen: ^2.7.6
|
||||
# hive_generator: ^2.0.1
|
||||
hive: 2.0.1
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'address_info.g.dart';
|
||||
part 'address_info.part.dart';
|
||||
|
||||
@HiveType(typeId: ADDRESS_INFO_TYPE_ID)
|
||||
// @HiveType(typeId: ADDRESS_INFO_TYPE_ID)
|
||||
class AddressInfo extends HiveObject {
|
||||
AddressInfo({required this.address, this.accountIndex, required this.label});
|
||||
|
||||
static const typeId = ADDRESS_INFO_TYPE_ID;
|
||||
static const boxName = 'AddressInfo';
|
||||
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
int? accountIndex;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
// @HiveField(1, defaultValue: '')
|
||||
String address;
|
||||
|
||||
@HiveField(2, defaultValue: '')
|
||||
// @HiveField(2, defaultValue: '')
|
||||
String label;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'address_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class AddressInfoAdapter extends TypeAdapter<AddressInfo> {
|
||||
@override
|
||||
final int typeId = 11;
|
||||
|
||||
@override
|
||||
AddressInfo read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return AddressInfo(
|
||||
address: fields[1] == null ? '' : fields[1] as String,
|
||||
accountIndex: fields[0] as int?,
|
||||
label: fields[2] == null ? '' : fields[2] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, AddressInfo obj) {
|
||||
writer
|
||||
..writeByte(3)
|
||||
..writeByte(0)
|
||||
..write(obj.accountIndex)
|
||||
..writeByte(1)
|
||||
..write(obj.address)
|
||||
..writeByte(2)
|
||||
..write(obj.label);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is AddressInfoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
String _smartAmountSanitizer(String amount) {
|
||||
if (amount.contains(RegExp(r'^(?=.*[.])(?=.*,).*$'))) {
|
||||
if (amount.indexOf(".") > amount.indexOf(",")) {
|
||||
amount = amount.replaceAll(",", "");
|
||||
} else {
|
||||
amount = amount.replaceAll(".", "");
|
||||
}
|
||||
}
|
||||
|
||||
return amount.replaceAll(",", ".");
|
||||
}
|
||||
|
||||
|
||||
extension SanitizerUserAmounts on String {
|
||||
String sanitized() => _smartAmountSanitizer(this);
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/format_fixed.dart';
|
||||
import 'package:cw_core/parse_fixed.dart';
|
||||
|
||||
class Money implements Comparable<Money> {
|
||||
final BigInt amount;
|
||||
final Currency currency;
|
||||
|
||||
const Money(this.amount, this.currency);
|
||||
|
||||
factory Money.zero(Currency currency) => Money(BigInt.zero, currency);
|
||||
|
||||
factory Money.fromInt(int amount, Currency currency) => Money(BigInt.from(amount), currency);
|
||||
|
||||
/// Parse the [source] and turn it into [Money]
|
||||
///
|
||||
/// Throws a [FormatException] if the [source] is not a valid decimal or
|
||||
/// not in canonical representation or if it is a decimal when [isBaseUnit]
|
||||
factory Money.parse(source, Currency currency, {bool isBaseUnit = false}) {
|
||||
final amount = isBaseUnit
|
||||
? BigInt.parse(source.toString())
|
||||
: parseFixed(source.toString(), currency.decimals);
|
||||
|
||||
return Money(amount, currency);
|
||||
}
|
||||
|
||||
/// Parse the [source] and turn it into [Money] if possible
|
||||
///
|
||||
/// As [parse] except that this method returns `null` if the input is not
|
||||
/// valid or if it is a decimal when [isBaseUnit]
|
||||
static Money? tryParse(source, Currency currency, {bool isBaseUnit = false}) {
|
||||
final amount = isBaseUnit
|
||||
? BigInt.tryParse(source.toString())
|
||||
: tryParseFixed(source.toString(), currency.decimals);
|
||||
|
||||
return amount != null ? Money(amount, currency) : null;
|
||||
}
|
||||
|
||||
/// Returns the sign of this [BigInt] amount.
|
||||
/// Returns 0 for zero, -1 for values less than zero and +1 for values
|
||||
/// greater than zero.
|
||||
int get sign => amount.sign;
|
||||
|
||||
/// Returns `true` when amount of this money is zero.
|
||||
bool get isZero => amount == BigInt.zero;
|
||||
|
||||
/// Returns `true` when amount of this money is negative.
|
||||
bool get isNegative => amount.isNegative;
|
||||
|
||||
/// Compares this to [other].
|
||||
///
|
||||
/// [other] has to be in same currency, [ArgumentError] will be thrown
|
||||
/// otherwise.
|
||||
@override
|
||||
int compareTo(Money other) {
|
||||
_assertSameCurrency(other);
|
||||
|
||||
return amount.compareTo(other.amount);
|
||||
}
|
||||
|
||||
/// Returns `true` if [other] is the same amount of money in
|
||||
/// the same currency.
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is Money && other.currency == currency && other.amount == amount);
|
||||
|
||||
/// Returns `true` when this money is less than [other].
|
||||
///
|
||||
/// Both operands have to be in same currency, [ArgumentError] will be thrown
|
||||
/// otherwise.
|
||||
bool operator <(Money other) {
|
||||
_assertSameCurrency(other, "Cannot compare money in different currencies.");
|
||||
|
||||
return amount < other.amount;
|
||||
}
|
||||
|
||||
/// Returns `true` when this money is less than or equal to [other].
|
||||
///
|
||||
/// Both operands have to be in same currency, [ArgumentError] will be thrown
|
||||
/// otherwise.
|
||||
bool operator <=(Money other) {
|
||||
_assertSameCurrency(other, "Cannot compare money in different currencies.");
|
||||
|
||||
return amount <= other.amount;
|
||||
}
|
||||
|
||||
/// Returns `true` when this money is greater than [other].
|
||||
///
|
||||
/// Both operands have to be in same currency, [ArgumentError] will be thrown
|
||||
/// otherwise.
|
||||
bool operator >(Money other) {
|
||||
_assertSameCurrency(other, "Cannot compare money in different currencies.");
|
||||
|
||||
return amount > other.amount;
|
||||
}
|
||||
|
||||
/// Returns `true` when this money is greater than or equal to [other].
|
||||
///
|
||||
/// Both operands have to be in same currency, [ArgumentError] will be thrown
|
||||
/// otherwise.
|
||||
bool operator >=(Money other) {
|
||||
_assertSameCurrency(other, "Cannot compare money in different currencies.");
|
||||
|
||||
return amount >= other.amount;
|
||||
}
|
||||
|
||||
/// Adds the amount of [other] to this amount.
|
||||
///
|
||||
/// Both operands must be in same currency, [ArgumentError] will be thrown
|
||||
/// otherwise.
|
||||
Money operator +(Money other) {
|
||||
_assertSameCurrency(other);
|
||||
|
||||
return _withAmount(amount + other.amount);
|
||||
}
|
||||
|
||||
/// unary minus operator.
|
||||
Money operator -() => _withAmount(-amount);
|
||||
|
||||
/// Subtracts the amount of [other] from this amount.
|
||||
///
|
||||
/// Both operands must be in same currency, [ArgumentError] will be thrown
|
||||
/// otherwise.
|
||||
Money operator -(Money other) {
|
||||
_assertSameCurrency(other);
|
||||
|
||||
return _withAmount(amount - other.amount);
|
||||
}
|
||||
|
||||
/// Returns [Money] multiplied by [other].
|
||||
///
|
||||
/// The result is again [Money].
|
||||
Money operator *(BigInt other) => _withAmount(amount * other);
|
||||
|
||||
/// Returns [Money] divided by [other].
|
||||
///
|
||||
/// The result is again [Money].
|
||||
Money operator /(BigInt other) {
|
||||
if (other == BigInt.zero) throw Exception('Division by zero.');
|
||||
|
||||
final neg = (amount.isNegative) ^ (other.isNegative);
|
||||
final A = amount.abs();
|
||||
final B = other.abs();
|
||||
|
||||
final q = A ~/ B;
|
||||
final r = A % B;
|
||||
|
||||
// Compare 2*r with B to detect half/up/down
|
||||
final twiceR = r << 1;
|
||||
final mag = (twiceR >= B) ? q + BigInt.one : q;
|
||||
|
||||
return _withAmount(neg ? -mag : mag);
|
||||
}
|
||||
|
||||
/// Creates a copy of this [Money] object with optional new values
|
||||
/// for [amount], [currency].
|
||||
///
|
||||
/// If just [currency] is provided and the decimals missmatch
|
||||
/// the amount will be transformed to keep its canonical representation
|
||||
Money copyWith({BigInt? amount, Currency? currency}) {
|
||||
if (currency != null && amount == null && currency.decimals != this.currency.decimals) {
|
||||
return Money(
|
||||
_transformAmount(this.amount, this.currency.decimals, currency.decimals), currency);
|
||||
}
|
||||
|
||||
return Money(amount ?? this.amount, currency ?? this.currency);
|
||||
}
|
||||
|
||||
/// Creates new instance with the same currency and given [amount].
|
||||
Money _withAmount(BigInt amount) => Money(amount, currency);
|
||||
|
||||
void _assertSameCurrency(Money other, [String? message]) {
|
||||
if (currency != other.currency)
|
||||
throw ArgumentError(message ?? "Cannot operate with money values in different currencies.");
|
||||
}
|
||||
|
||||
BigInt _transformAmount(BigInt source, int sourceDecimals, int targetDecimals) {
|
||||
if (sourceDecimals == targetDecimals) return source;
|
||||
|
||||
if (sourceDecimals > targetDecimals) {
|
||||
return parseFixed(
|
||||
formatFixed(source, sourceDecimals).withMaxDecimals(targetDecimals), targetDecimals);
|
||||
} else {
|
||||
return parseFixed(formatFixed(source, sourceDecimals), targetDecimals);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => amount.hashCode ^ currency.hashCode;
|
||||
|
||||
@override
|
||||
String toString() => formatFixed(amount, currency.decimals);
|
||||
|
||||
String toStringWithSymbol(
|
||||
{int? fractionalDigits, bool trimZeros = true, bool useBaseUnit = false}) =>
|
||||
"${toStringWithPrecision(fractionalDigits: fractionalDigits, trimZeros: trimZeros, useBaseUnit: useBaseUnit)} ${_getSymbol(useBaseUnit)}";
|
||||
|
||||
String toStringWithPrecision(
|
||||
{int? fractionalDigits, bool trimZeros = true, bool useBaseUnit = false}) =>
|
||||
formatFixed(amount, useBaseUnit ? 0 : currency.decimals,
|
||||
fractionalDigits: fractionalDigits, trimZeros: trimZeros);
|
||||
|
||||
// To Override the symbol with the ticker of the base unit
|
||||
String _getSymbol(bool useBaseUnit) {
|
||||
if (useBaseUnit && [CryptoCurrency.btc, CryptoCurrency.btcln].contains(currency)) return "sats";
|
||||
return currency.symbol;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +1,4 @@
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:decimal/intl.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class AmountConverter {
|
||||
static const _moneroAmountLength = 12;
|
||||
static const _moneroAmountDivider = 1000000000000;
|
||||
static const _wowneroAmountLength = 11;
|
||||
static const _wowneroAmountDivider = 100000000000;
|
||||
static const _bitcoinAmountDivider = 100000000;
|
||||
static const _bitcoinAmountLength = 8;
|
||||
static final _bitcoinAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = _bitcoinAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
static final _moneroAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = _moneroAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
static final _wowneroAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = _wowneroAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
|
||||
static String amountIntToString(CryptoCurrency cryptoCurrency, int amount) {
|
||||
switch (cryptoCurrency) {
|
||||
case CryptoCurrency.xmr:
|
||||
return _moneroAmountToString(amount);
|
||||
case CryptoCurrency.wow:
|
||||
return _wowneroAmountToString(amount);
|
||||
case CryptoCurrency.btc:
|
||||
case CryptoCurrency.bch:
|
||||
case CryptoCurrency.ltc:
|
||||
case CryptoCurrency.dcr:
|
||||
case CryptoCurrency.doge:
|
||||
return _bitcoinAmountToString(amount);
|
||||
case CryptoCurrency.xhv:
|
||||
case CryptoCurrency.xag:
|
||||
case CryptoCurrency.xau:
|
||||
case CryptoCurrency.xaud:
|
||||
case CryptoCurrency.xbtc:
|
||||
case CryptoCurrency.xcad:
|
||||
case CryptoCurrency.xchf:
|
||||
case CryptoCurrency.xcny:
|
||||
case CryptoCurrency.xeur:
|
||||
case CryptoCurrency.xgbp:
|
||||
case CryptoCurrency.xjpy:
|
||||
case CryptoCurrency.xnok:
|
||||
case CryptoCurrency.xnzd:
|
||||
case CryptoCurrency.xusd:
|
||||
return _moneroAmountToString(amount);
|
||||
case CryptoCurrency.zano:
|
||||
return _moneroAmountToStringUsingDecimals(amount);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
static double cryptoAmountToDouble({required num amount, required num divider}) =>
|
||||
amount / divider;
|
||||
|
||||
static String _moneroAmountToString(int amount) => _moneroAmountFormat
|
||||
.format(cryptoAmountToDouble(amount: amount, divider: _moneroAmountDivider));
|
||||
|
||||
static String _bitcoinAmountToString(int amount) => _bitcoinAmountFormat
|
||||
.format(cryptoAmountToDouble(amount: amount, divider: _bitcoinAmountDivider));
|
||||
|
||||
static String _wowneroAmountToString(int amount) => _wowneroAmountFormat
|
||||
.format(cryptoAmountToDouble(amount: amount, divider: _wowneroAmountDivider));
|
||||
|
||||
static Decimal cryptoAmountToDecimal({required int amount, required int divider}) =>
|
||||
(Decimal.fromInt(amount) / Decimal.fromInt(divider)).toDecimal();
|
||||
|
||||
static String _moneroAmountToStringUsingDecimals(int amount) => _moneroAmountFormat.format(
|
||||
DecimalIntl(cryptoAmountToDecimal(amount: amount, divider: _moneroAmountDivider)));
|
||||
|
||||
|
||||
static String toBaseUnits(String amount, int decimals) {
|
||||
amount = amount.trim();
|
||||
if (amount.isEmpty) return '0';
|
||||
|
||||
+14
-20
@@ -1,26 +1,20 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
|
||||
/// Balance Data class with all amounts in the lowest possible currency (e.g. satoshis or wei)
|
||||
abstract class Balance {
|
||||
const Balance(this.available, this.additional, {this.secondAvailable, this.secondAdditional, this.frozen});
|
||||
const Balance(
|
||||
this.available,
|
||||
this.unavailable, {
|
||||
this.secondAvailable,
|
||||
this.secondUnavailable,
|
||||
this.frozen,
|
||||
});
|
||||
|
||||
Balance.fromInt(int available, int additional, {int? secondAvailable, int? secondAdditional, int? frozen}) :
|
||||
available = BigInt.from(available),
|
||||
additional = BigInt.from(additional),
|
||||
secondAvailable = secondAvailable == null ? null : BigInt.from(secondAvailable),
|
||||
secondAdditional = secondAdditional == null ? null : BigInt.from(secondAdditional),
|
||||
frozen = frozen == null ? null : BigInt.from(frozen) {}
|
||||
final Money available;
|
||||
final Money unavailable;
|
||||
|
||||
final Money? secondAvailable;
|
||||
final Money? secondUnavailable;
|
||||
|
||||
|
||||
final BigInt available;
|
||||
final BigInt additional;
|
||||
|
||||
final BigInt? secondAvailable;
|
||||
final BigInt? secondAdditional;
|
||||
|
||||
final BigInt? frozen;
|
||||
|
||||
BigInt get fullAvailableBalance => available;
|
||||
|
||||
@deprecated
|
||||
String get formattedUnAvailableBalance => '';
|
||||
final Money? frozen;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/currency_groups.dart';
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/format_fixed.dart';
|
||||
import 'package:cw_core/parse_fixed.dart';
|
||||
|
||||
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implements Currency {
|
||||
const CryptoCurrency({
|
||||
@@ -21,18 +21,30 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||
this.groups = const {},
|
||||
}) : super(title: title, raw: raw);
|
||||
|
||||
final String name;
|
||||
final String? tag;
|
||||
final String? fullName;
|
||||
final String? iconPath;
|
||||
final String? flatIconPath;
|
||||
final String? chainIconPath;
|
||||
@override
|
||||
final int decimals;
|
||||
final bool enabled;
|
||||
final bool isPotentialScam;
|
||||
final Set<String> groups;
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@override
|
||||
final String? fullName;
|
||||
|
||||
@override
|
||||
final String? tag;
|
||||
|
||||
@override
|
||||
final String? iconPath;
|
||||
|
||||
@override
|
||||
final int decimals;
|
||||
|
||||
@override
|
||||
String get symbol => title;
|
||||
|
||||
set enabled(bool value) => this.enabled = value;
|
||||
|
||||
static const all = [
|
||||
@@ -432,13 +444,13 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
|
||||
|
||||
bool titleAndTagEqual(CryptoCurrency other) => title == other.title && tag == other.tag;
|
||||
|
||||
@override
|
||||
Money parseAmount(String value) => Money.parse(value, this);
|
||||
|
||||
@override
|
||||
Money? tryParseAmount(String value) => Money.tryParse(value, this);
|
||||
|
||||
/// Format the raw amount into its decimal representation eg. turn Sats into Bitcoin
|
||||
String formatAmount(BigInt amount, {int? fractionalDigits, bool trimZeros = true}) =>
|
||||
formatFixed(amount, decimals, fractionalDigits: fractionalDigits, trimZeros: trimZeros);
|
||||
|
||||
/// Parse the [value] and turn it into the smallest denomination eg. turn Bitcoin into Sats
|
||||
BigInt parseAmount(String value) => parseFixed(value, decimals);
|
||||
|
||||
/// Try parsing the [value] and turn it into the smallest denomination eg. turn Bitcoin into Sats
|
||||
BigInt? tryParseAmount(String value) => tryParseFixed(value, decimals);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
|
||||
abstract class Currency {
|
||||
String get symbol;
|
||||
String get name;
|
||||
String? get tag;
|
||||
String? get fullName;
|
||||
String? get iconPath;
|
||||
int get decimals;
|
||||
String? get tag;
|
||||
|
||||
/// Parse the [value] and turn it into [Money]
|
||||
Money parseAmount(String value);
|
||||
|
||||
/// Try parsing the [value] and turn it into [Money]
|
||||
Money? tryParseAmount(String value);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
other is Currency && other.decimals == decimals && other.symbol == symbol && other.tag == tag;
|
||||
|
||||
@override
|
||||
int get hashCode => decimals.hashCode ^ symbol.hashCode ^ tag.hashCode;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class EnumerableItem<T> {
|
||||
const EnumerableItem({required this.title, required this.raw});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ abstract class Enumerate {
|
||||
String get value;
|
||||
|
||||
@override
|
||||
operator ==(other) {
|
||||
bool operator ==(other) {
|
||||
if (identical(other, this)) return true;
|
||||
if (other is! Enumerate) return false;
|
||||
return other.runtimeType == runtimeType && value == other.value;
|
||||
|
||||
@@ -2,25 +2,25 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'erc20_token.g.dart';
|
||||
part 'erc20_token.part.dart';
|
||||
|
||||
@HiveType(typeId: Erc20Token.typeId)
|
||||
// @HiveType(typeId: Erc20Token.typeId)
|
||||
class Erc20Token extends CryptoCurrency with HiveObjectMixin {
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
final String name;
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
final String symbol;
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
final String contractAddress;
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
final int decimal;
|
||||
@HiveField(4, defaultValue: true)
|
||||
// @HiveField(4, defaultValue: true)
|
||||
bool _enabled;
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
String? iconPath;
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
final String? tag;
|
||||
@HiveField(7, defaultValue: false)
|
||||
// @HiveField(7, defaultValue: false)
|
||||
bool isPotentialScam;
|
||||
|
||||
bool get enabled => _enabled;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'erc20_token.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class Erc20TokenAdapter extends TypeAdapter<Erc20Token> {
|
||||
@override
|
||||
final int typeId = 12;
|
||||
|
||||
@override
|
||||
Erc20Token read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Erc20Token(
|
||||
name: fields[0] as String,
|
||||
symbol: fields[1] as String,
|
||||
contractAddress: fields[2] as String,
|
||||
decimal: fields[3] as int,
|
||||
iconPath: fields[5] as String?,
|
||||
tag: fields[6] as String?,
|
||||
isPotentialScam: fields[7] == null ? false : fields[7] as bool,
|
||||
).._enabled = fields[4] == null ? true : fields[4] as bool;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Erc20Token obj) {
|
||||
writer
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.name)
|
||||
..writeByte(1)
|
||||
..write(obj.symbol)
|
||||
..writeByte(2)
|
||||
..write(obj.contractAddress)
|
||||
..writeByte(3)
|
||||
..write(obj.decimal)
|
||||
..writeByte(4)
|
||||
..write(obj._enabled)
|
||||
..writeByte(5)
|
||||
..write(obj.iconPath)
|
||||
..writeByte(6)
|
||||
..write(obj.tag)
|
||||
..writeByte(7)
|
||||
..write(obj.isPotentialScam);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is Erc20TokenAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -26,9 +26,7 @@ class TransactionCommitFailed implements Exception {
|
||||
TransactionCommitFailed({this.errorMessage});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return errorMessage ?? "unknown error";
|
||||
}
|
||||
String toString() => errorMessage ?? "unknown error";
|
||||
}
|
||||
|
||||
class TransactionCommitFailedDustChange implements Exception {}
|
||||
@@ -57,10 +55,6 @@ class SignSPLTokenTransactionRentException implements Exception {}
|
||||
|
||||
class NoAssociatedTokenAccountException implements Exception {}
|
||||
|
||||
|
||||
/// ==============================================================================
|
||||
/// ==============================================================================
|
||||
|
||||
class RestoreFromSeedException implements Exception {
|
||||
final String message;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:bech32/bech32.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
|
||||
const _BOLT_PREFIXES = ["lnbcrt", "lntbs", "lnbc", "lntb"];
|
||||
@@ -61,14 +63,14 @@ int? _getAmountBolt11Msat(String invoice) {
|
||||
}
|
||||
}
|
||||
|
||||
int? getBolt11Amount(String invoice) {
|
||||
Money? getBolt11Amount(String invoice) {
|
||||
final msat = _getAmountBolt11Msat(invoice);
|
||||
if (msat == null || msat % 1000 != 0) return null;
|
||||
return msat ~/ 1000;
|
||||
return Money.fromInt(msat ~/ 1000, CryptoCurrency.btcln);
|
||||
}
|
||||
|
||||
class LNURL {
|
||||
static Future<int?> getPayRequestAmount(String lnurl) async {
|
||||
static Future<Money?> getPayRequestAmount(String lnurl) async {
|
||||
final url = decode(lnurl);
|
||||
final response = await ProxyWrapper().get(clearnetUri: url);
|
||||
|
||||
@@ -88,7 +90,7 @@ class LNURL {
|
||||
return null;
|
||||
|
||||
if (msat == null || msat % 1000 != 0) return null;
|
||||
return msat ~/ 1000;
|
||||
return Money.fromInt(msat ~/ 1000, CryptoCurrency.btcln);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
|
||||
class MoneroBalance extends Balance {
|
||||
MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
|
||||
: formattedFrozenBalance = moneroAmountToString(amount: frozenBalance),
|
||||
super.fromInt(unlockedBalance, fullBalance - unlockedBalance, frozen: frozenBalance);
|
||||
|
||||
final int fullBalance;
|
||||
final int unlockedBalance;
|
||||
final int frozenBalance;
|
||||
final String formattedFrozenBalance;
|
||||
|
||||
@override
|
||||
String get formattedUnAvailableBalance =>
|
||||
formattedFrozenBalance == '0.0' ? '' : formattedFrozenBalance;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'mweb_utxo.g.dart';
|
||||
part 'mweb_utxo.part.dart';
|
||||
|
||||
@HiveType(typeId: MWEB_UTXO_TYPE_ID)
|
||||
// @HiveType(typeId: MWEB_UTXO_TYPE_ID)
|
||||
class MwebUtxo extends HiveObject {
|
||||
MwebUtxo({
|
||||
required this.height,
|
||||
@@ -17,21 +17,21 @@ class MwebUtxo extends HiveObject {
|
||||
static const typeId = MWEB_UTXO_TYPE_ID;
|
||||
static const boxName = 'MwebUtxo';
|
||||
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
int height;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
int value;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
String address;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String outputId;
|
||||
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
int blockTime;
|
||||
|
||||
@HiveField(5, defaultValue: false)
|
||||
// @HiveField(5, defaultValue: false)
|
||||
bool spent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'mweb_utxo.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class MwebUtxoAdapter extends TypeAdapter<MwebUtxo> {
|
||||
@override
|
||||
final int typeId = 20;
|
||||
|
||||
@override
|
||||
MwebUtxo read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return MwebUtxo(
|
||||
height: fields[0] as int,
|
||||
value: fields[1] as int,
|
||||
address: fields[2] as String,
|
||||
outputId: fields[3] as String,
|
||||
blockTime: fields[4] as int,
|
||||
spent: fields[5] == null ? false : fields[5] as bool,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, MwebUtxo obj) {
|
||||
writer
|
||||
..writeByte(6)
|
||||
..writeByte(0)
|
||||
..write(obj.height)
|
||||
..writeByte(1)
|
||||
..write(obj.value)
|
||||
..writeByte(2)
|
||||
..write(obj.address)
|
||||
..writeByte(3)
|
||||
..write(obj.outputId)
|
||||
..writeByte(4)
|
||||
..write(obj.blockTime)
|
||||
..writeByte(5)
|
||||
..write(obj.spent);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is MwebUtxoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'nano_account.g.dart';
|
||||
part 'nano_account.part.dart';
|
||||
|
||||
@HiveType(typeId: NanoAccount.typeId)
|
||||
// @HiveType(typeId: NanoAccount.typeId)
|
||||
class NanoAccount extends HiveObject {
|
||||
NanoAccount({required this.label, required this.id, this.balance, this.isSelected = false});
|
||||
|
||||
static const typeId = NANO_ACCOUNT_TYPE_ID;
|
||||
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
String label;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
final int id;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
bool isSelected;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
String? balance;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'nano_account.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class NanoAccountAdapter extends TypeAdapter<NanoAccount> {
|
||||
@override
|
||||
final int typeId = 13;
|
||||
|
||||
@override
|
||||
NanoAccount read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return NanoAccount(
|
||||
label: fields[0] as String,
|
||||
id: fields[1] as int,
|
||||
balance: fields[3] as String?,
|
||||
isSelected: fields[2] as bool,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, NanoAccount obj) {
|
||||
writer
|
||||
..writeByte(4)
|
||||
..writeByte(0)
|
||||
..write(obj.label)
|
||||
..writeByte(1)
|
||||
..write(obj.id)
|
||||
..writeByte(2)
|
||||
..write(obj.isSelected)
|
||||
..writeByte(3)
|
||||
..write(obj.balance);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NanoAccountAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import 'package:crypto/crypto.dart';
|
||||
import 'cake_hive.dart';
|
||||
import 'node.dart' as node_new;
|
||||
|
||||
part "node_legacy.g.dart";
|
||||
part 'node_legacy.part.dart';
|
||||
|
||||
Future<void> performNodeHiveMigration() async {
|
||||
if(!CakeHive.isAdapterRegistered(Node.typeId)) {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'node_legacy.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class NodeAdapter extends TypeAdapter<Node> {
|
||||
@override
|
||||
final int typeId = 1;
|
||||
|
||||
@override
|
||||
Node read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return Node(
|
||||
label: fields[12] == null ? '' : fields[12] as String?,
|
||||
login: fields[1] as String?,
|
||||
password: fields[2] as String?,
|
||||
useSSL: fields[4] as bool?,
|
||||
trusted: fields[5] == null ? false : fields[5] as bool,
|
||||
socksProxyAddress: fields[6] as String?,
|
||||
path: fields[7] == null ? '' : fields[7] as String?,
|
||||
isEnabledForAutoSwitching:
|
||||
fields[11] == null ? false : fields[11] as bool,
|
||||
)
|
||||
..uriRaw = fields[0] == null ? '' : fields[0] as String
|
||||
..typeRaw = fields[3] == null ? 0 : fields[3] as int
|
||||
..isElectrs = fields[8] as bool?
|
||||
..supportsSilentPayments = fields[9] as bool?
|
||||
..supportsMweb = fields[10] as bool?;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, Node obj) {
|
||||
writer
|
||||
..writeByte(13)
|
||||
..writeByte(0)
|
||||
..write(obj.uriRaw)
|
||||
..writeByte(1)
|
||||
..write(obj.login)
|
||||
..writeByte(2)
|
||||
..write(obj.password)
|
||||
..writeByte(3)
|
||||
..write(obj.typeRaw)
|
||||
..writeByte(4)
|
||||
..write(obj.useSSL)
|
||||
..writeByte(5)
|
||||
..write(obj.trusted)
|
||||
..writeByte(6)
|
||||
..write(obj.socksProxyAddress)
|
||||
..writeByte(7)
|
||||
..write(obj.path)
|
||||
..writeByte(8)
|
||||
..write(obj.isElectrs)
|
||||
..writeByte(9)
|
||||
..write(obj.supportsSilentPayments)
|
||||
..writeByte(10)
|
||||
..write(obj.supportsMweb)
|
||||
..writeByte(11)
|
||||
..write(obj.isEnabledForAutoSwitching)
|
||||
..writeByte(12)
|
||||
..write(obj.label);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is NodeAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,24 +1,25 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
|
||||
class OutputInfo {
|
||||
const OutputInfo(
|
||||
{required this.address,
|
||||
required this.sendAll,
|
||||
required this.isParsedAddress,
|
||||
this.cryptoAmount,
|
||||
this.formattedCryptoAmount,
|
||||
const OutputInfo({
|
||||
required this.address,
|
||||
required this.sendAll,
|
||||
required this.isParsedAddress,
|
||||
required this.cryptoAmount,
|
||||
this.fiatAmount,
|
||||
this.note,
|
||||
this.extractedAddress,
|
||||
this.memo,
|
||||
this.extra = const {}});
|
||||
this.extra = const {},
|
||||
});
|
||||
|
||||
final String? fiatAmount;
|
||||
final String? cryptoAmount;
|
||||
final String address;
|
||||
final String? note;
|
||||
final String? extractedAddress;
|
||||
final bool sendAll;
|
||||
final bool isParsedAddress;
|
||||
final int? formattedCryptoAmount;
|
||||
final String? memo;
|
||||
final Map<String, dynamic> extra;
|
||||
}
|
||||
final String? fiatAmount;
|
||||
final Money cryptoAmount;
|
||||
final String address;
|
||||
final String? note;
|
||||
final String? extractedAddress;
|
||||
final bool sendAll;
|
||||
final bool isParsedAddress;
|
||||
final String? memo;
|
||||
final Map<String, dynamic> extra;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ BigInt parseFixed(String value, int decimals) {
|
||||
var fraction = (comps.length == 2 ? comps[1] : "0").padRight(decimals, "0");
|
||||
|
||||
if (fraction.length > multiplier.length - 1) {
|
||||
throw FormatException("fractional component exceeds decimals, underflow, parseFixed");
|
||||
throw FormatException("fractional component(${fraction.length}) exceeds decimals(${decimals}), underflow, parseFixed");
|
||||
}
|
||||
|
||||
final wholeValue = BigInt.parse(whole);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'payjoin_session.g.dart';
|
||||
part 'payjoin_session.part.dart';
|
||||
|
||||
@HiveType(typeId: PAYJOIN_SESSION_TYPE_ID)
|
||||
// @HiveType(typeId: PAYJOIN_SESSION_TYPE_ID)
|
||||
class PayjoinSession extends HiveObject {
|
||||
PayjoinSession({
|
||||
required this.walletId,
|
||||
@@ -25,31 +25,31 @@ class PayjoinSession extends HiveObject {
|
||||
static const typeId = PAYJOIN_SESSION_TYPE_ID;
|
||||
static const boxName = 'PayjoinSessions';
|
||||
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
final String walletId;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
final String? sender;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
final String? receiver;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
final String? pjUri;
|
||||
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
String status;
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
DateTime? inProgressSince;
|
||||
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
String? txId;
|
||||
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
String? rawAmount;
|
||||
|
||||
@HiveField(8)
|
||||
// @HiveField(8)
|
||||
String? error;
|
||||
|
||||
bool get isSenderSession => sender != null;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'payjoin_session.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class PayjoinSessionAdapter extends TypeAdapter<PayjoinSession> {
|
||||
@override
|
||||
final int typeId = 23;
|
||||
|
||||
@override
|
||||
PayjoinSession read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return PayjoinSession(
|
||||
walletId: fields[0] as String,
|
||||
receiver: fields[2] as String?,
|
||||
sender: fields[1] as String?,
|
||||
pjUri: fields[3] as String?,
|
||||
status: fields[4] as String,
|
||||
inProgressSince: fields[5] as DateTime?,
|
||||
rawAmount: fields[7] as String?,
|
||||
)
|
||||
..txId = fields[6] as String?
|
||||
..error = fields[8] as String?;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, PayjoinSession obj) {
|
||||
writer
|
||||
..writeByte(9)
|
||||
..writeByte(0)
|
||||
..write(obj.walletId)
|
||||
..writeByte(1)
|
||||
..write(obj.sender)
|
||||
..writeByte(2)
|
||||
..write(obj.receiver)
|
||||
..writeByte(3)
|
||||
..write(obj.pjUri)
|
||||
..writeByte(4)
|
||||
..write(obj.status)
|
||||
..writeByte(5)
|
||||
..write(obj.inProgressSince)
|
||||
..writeByte(6)
|
||||
..write(obj.txId)
|
||||
..writeByte(7)
|
||||
..write(obj.rawAmount)
|
||||
..writeByte(8)
|
||||
..write(obj.error);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is PayjoinSessionAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
|
||||
class PendingChange {
|
||||
final String address;
|
||||
final BigInt amount;
|
||||
@@ -7,22 +9,19 @@ class PendingChange {
|
||||
|
||||
mixin PendingTransaction {
|
||||
String get id;
|
||||
|
||||
Money get amount;
|
||||
Money get fee;
|
||||
|
||||
String get amountFormatted;
|
||||
String get feeFormatted;
|
||||
String get feeFormattedValue;
|
||||
String get feeFormatted => fee.toStringWithSymbol(fractionalDigits: 8);
|
||||
String get feeFormattedValue => fee.toStringWithPrecision(fractionalDigits: 8);
|
||||
String? feeRate;
|
||||
String get hex;
|
||||
String? get evmTxHashFromRawHex => null;
|
||||
int? get outputCount => null;
|
||||
PendingChange? change;
|
||||
|
||||
String get roundedAmountFormatted {
|
||||
if (amountFormatted.split(".").last.length <= 8) {
|
||||
return amountFormatted;
|
||||
}
|
||||
return double.parse(amountFormatted).toStringAsFixed(8);
|
||||
}
|
||||
|
||||
bool shouldCommitUR() => false;
|
||||
|
||||
Future<void> commit();
|
||||
|
||||
+11
-11
@@ -2,39 +2,39 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'spl_token.g.dart';
|
||||
part 'spl_token.part.dart';
|
||||
|
||||
@HiveType(typeId: SPLToken.typeId)
|
||||
// @HiveType(typeId: SPLToken.typeId)
|
||||
class SPLToken extends CryptoCurrency with HiveObjectMixin {
|
||||
@override
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
final String name;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
final String symbol;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
final String mintAddress;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
final int decimal;
|
||||
|
||||
@HiveField(4, defaultValue: true)
|
||||
// @HiveField(4, defaultValue: true)
|
||||
bool _enabled;
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
final String mint;
|
||||
|
||||
@override
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
final String? iconPath;
|
||||
|
||||
@override
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
final String? tag;
|
||||
|
||||
@override
|
||||
@HiveField(8, defaultValue: false)
|
||||
// @HiveField(8, defaultValue: false)
|
||||
bool isPotentialScam;
|
||||
|
||||
SPLToken({
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'spl_token.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class SPLTokenAdapter extends TypeAdapter<SPLToken> {
|
||||
@override
|
||||
final int typeId = 16;
|
||||
|
||||
@override
|
||||
SPLToken read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return SPLToken(
|
||||
name: fields[0] as String,
|
||||
symbol: fields[1] as String,
|
||||
mintAddress: fields[2] as String,
|
||||
decimal: fields[3] as int,
|
||||
mint: fields[5] as String,
|
||||
iconPath: fields[6] as String?,
|
||||
tag: fields[7] as String?,
|
||||
isPotentialScam: fields[8] == null ? false : fields[8] as bool,
|
||||
).._enabled = fields[4] == null ? true : fields[4] as bool;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, SPLToken obj) {
|
||||
writer
|
||||
..writeByte(9)
|
||||
..writeByte(0)
|
||||
..write(obj.name)
|
||||
..writeByte(1)
|
||||
..write(obj.symbol)
|
||||
..writeByte(2)
|
||||
..write(obj.mintAddress)
|
||||
..writeByte(3)
|
||||
..write(obj.decimal)
|
||||
..writeByte(4)
|
||||
..write(obj._enabled)
|
||||
..writeByte(5)
|
||||
..write(obj.mint)
|
||||
..writeByte(6)
|
||||
..write(obj.iconPath)
|
||||
..writeByte(7)
|
||||
..write(obj.tag)
|
||||
..writeByte(8)
|
||||
..write(obj.isPotentialScam);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is SPLTokenAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/keyable.dart';
|
||||
|
||||
abstract class TransactionInfo extends Object with Keyable {
|
||||
late String id;
|
||||
late String txHash = id;
|
||||
late int amount;
|
||||
int? fee;
|
||||
late Money amount;
|
||||
Money? fee;
|
||||
late TransactionDirection direction;
|
||||
late bool isPending;
|
||||
late DateTime date;
|
||||
int? height;
|
||||
int confirmations = 0;
|
||||
String amountFormatted();
|
||||
String fiatAmount();
|
||||
String? feeFormatted();
|
||||
void changeFiatAmount(String amount);
|
||||
String? to;
|
||||
String? from;
|
||||
String? evmSignatureName;
|
||||
@@ -26,4 +24,8 @@ abstract class TransactionInfo extends Object with Keyable {
|
||||
dynamic get keyIndex => id;
|
||||
|
||||
Map<String, dynamic> additionalInfo = {};
|
||||
|
||||
String? _fiatAmount;
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
}
|
||||
|
||||
+10
-10
@@ -4,32 +4,32 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'tron_token.g.dart';
|
||||
part 'tron_token.part.dart';
|
||||
|
||||
@HiveType(typeId: TronToken.typeId)
|
||||
// @HiveType(typeId: TronToken.typeId)
|
||||
class TronToken extends CryptoCurrency with HiveObjectMixin {
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
final String name;
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
final String symbol;
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
final String contractAddress;
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
final int decimal;
|
||||
|
||||
@HiveField(4, defaultValue: true)
|
||||
// @HiveField(4, defaultValue: true)
|
||||
bool _enabled;
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
final String? iconPath;
|
||||
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
final String? tag;
|
||||
|
||||
@HiveField(7, defaultValue: false)
|
||||
// @HiveField(7, defaultValue: false)
|
||||
final bool isPotentialScam;
|
||||
|
||||
bool get enabled => _enabled;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'tron_token.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class TronTokenAdapter extends TypeAdapter<TronToken> {
|
||||
@override
|
||||
final int typeId = 18;
|
||||
|
||||
@override
|
||||
TronToken read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return TronToken(
|
||||
name: fields[0] as String,
|
||||
symbol: fields[1] as String,
|
||||
contractAddress: fields[2] as String,
|
||||
decimal: fields[3] as int,
|
||||
iconPath: fields[5] as String?,
|
||||
tag: fields[6] as String?,
|
||||
isPotentialScam: fields[7] == null ? false : fields[7] as bool,
|
||||
).._enabled = fields[4] == null ? true : fields[4] as bool;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, TronToken obj) {
|
||||
writer
|
||||
..writeByte(8)
|
||||
..writeByte(0)
|
||||
..write(obj.name)
|
||||
..writeByte(1)
|
||||
..write(obj.symbol)
|
||||
..writeByte(2)
|
||||
..write(obj.contractAddress)
|
||||
..writeByte(3)
|
||||
..write(obj.decimal)
|
||||
..writeByte(4)
|
||||
..write(obj._enabled)
|
||||
..writeByte(5)
|
||||
..write(obj.iconPath)
|
||||
..writeByte(6)
|
||||
..write(obj.tag)
|
||||
..writeByte(7)
|
||||
..write(obj.isPotentialScam);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TronTokenAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/unspent_comparable_mixin.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'unspent_coins_info.g.dart';
|
||||
part 'unspent_coins_info.part.dart';
|
||||
|
||||
@HiveType(typeId: UnspentCoinsInfo.typeId)
|
||||
// @HiveType(typeId: UnspentCoinsInfo.typeId)
|
||||
class UnspentCoinsInfo extends HiveObject with UnspentComparable {
|
||||
UnspentCoinsInfo({
|
||||
required this.walletId,
|
||||
@@ -25,40 +25,40 @@ class UnspentCoinsInfo extends HiveObject with UnspentComparable {
|
||||
static const boxName = 'Unspent';
|
||||
static const boxKey = 'unspentBoxKey';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
// @HiveField(0, defaultValue: '')
|
||||
String walletId;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
// @HiveField(1, defaultValue: '')
|
||||
String hash;
|
||||
|
||||
@HiveField(2, defaultValue: false)
|
||||
// @HiveField(2, defaultValue: false)
|
||||
bool isFrozen;
|
||||
|
||||
@HiveField(3, defaultValue: false)
|
||||
// @HiveField(3, defaultValue: false)
|
||||
bool isSending;
|
||||
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
String? noteRaw;
|
||||
|
||||
@HiveField(5, defaultValue: '')
|
||||
// @HiveField(5, defaultValue: '')
|
||||
String address;
|
||||
|
||||
@HiveField(6, defaultValue: 0)
|
||||
// @HiveField(6, defaultValue: 0)
|
||||
int value;
|
||||
|
||||
@HiveField(7, defaultValue: 0)
|
||||
// @HiveField(7, defaultValue: 0)
|
||||
int vout;
|
||||
|
||||
@HiveField(8, defaultValue: null)
|
||||
// @HiveField(8, defaultValue: null)
|
||||
String? keyImage;
|
||||
|
||||
@HiveField(9, defaultValue: false)
|
||||
// @HiveField(9, defaultValue: false)
|
||||
bool isChange;
|
||||
|
||||
@HiveField(10, defaultValue: 0)
|
||||
// @HiveField(10, defaultValue: 0)
|
||||
int accountIndex;
|
||||
|
||||
@HiveField(11, defaultValue: false)
|
||||
// @HiveField(11, defaultValue: false)
|
||||
bool? isSilentPayment;
|
||||
|
||||
String get note => noteRaw ?? '';
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'unspent_coins_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class UnspentCoinsInfoAdapter extends TypeAdapter<UnspentCoinsInfo> {
|
||||
@override
|
||||
final int typeId = 9;
|
||||
|
||||
@override
|
||||
UnspentCoinsInfo read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return UnspentCoinsInfo(
|
||||
walletId: fields[0] == null ? '' : fields[0] as String,
|
||||
hash: fields[1] == null ? '' : fields[1] as String,
|
||||
isFrozen: fields[2] == null ? false : fields[2] as bool,
|
||||
isSending: fields[3] == null ? false : fields[3] as bool,
|
||||
noteRaw: fields[4] as String?,
|
||||
address: fields[5] == null ? '' : fields[5] as String,
|
||||
vout: fields[7] == null ? 0 : fields[7] as int,
|
||||
value: fields[6] == null ? 0 : fields[6] as int,
|
||||
keyImage: fields[8] as String?,
|
||||
isChange: fields[9] == null ? false : fields[9] as bool,
|
||||
accountIndex: fields[10] == null ? 0 : fields[10] as int,
|
||||
isSilentPayment: fields[11] == null ? false : fields[11] as bool?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, UnspentCoinsInfo obj) {
|
||||
writer
|
||||
..writeByte(12)
|
||||
..writeByte(0)
|
||||
..write(obj.walletId)
|
||||
..writeByte(1)
|
||||
..write(obj.hash)
|
||||
..writeByte(2)
|
||||
..write(obj.isFrozen)
|
||||
..writeByte(3)
|
||||
..write(obj.isSending)
|
||||
..writeByte(4)
|
||||
..write(obj.noteRaw)
|
||||
..writeByte(5)
|
||||
..write(obj.address)
|
||||
..writeByte(6)
|
||||
..write(obj.value)
|
||||
..writeByte(7)
|
||||
..write(obj.vout)
|
||||
..writeByte(8)
|
||||
..write(obj.keyImage)
|
||||
..writeByte(9)
|
||||
..write(obj.isChange)
|
||||
..writeByte(10)
|
||||
..write(obj.accountIndex)
|
||||
..writeByte(11)
|
||||
..write(obj.isSilentPayment);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is UnspentCoinsInfoAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -43,8 +43,6 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
|
||||
|
||||
ObservableMap<CryptoCurrency, BalanceType> get balance;
|
||||
|
||||
String formatCryptoAmount(String amount) => amount;
|
||||
|
||||
SyncStatus get syncStatus;
|
||||
|
||||
set syncStatus(SyncStatus status);
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'wallet_type.g.dart';
|
||||
part 'wallet_type.part.dart';
|
||||
|
||||
const walletTypes = [
|
||||
WalletType.monero,
|
||||
@@ -41,66 +41,66 @@ const evmWalletTypes = [
|
||||
WalletType.bsc
|
||||
];
|
||||
|
||||
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||
// @HiveType(typeId: WALLET_TYPE_TYPE_ID)
|
||||
enum WalletType {
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
monero,
|
||||
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
none,
|
||||
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
bitcoin,
|
||||
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
litecoin,
|
||||
|
||||
@HiveField(4)
|
||||
// @HiveField(4)
|
||||
haven,
|
||||
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
ethereum,
|
||||
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
nano,
|
||||
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
banano,
|
||||
|
||||
@HiveField(8)
|
||||
// @HiveField(8)
|
||||
bitcoinCash,
|
||||
|
||||
@HiveField(9)
|
||||
// @HiveField(9)
|
||||
polygon,
|
||||
|
||||
@HiveField(10)
|
||||
// @HiveField(10)
|
||||
solana,
|
||||
|
||||
@HiveField(11)
|
||||
// @HiveField(11)
|
||||
tron,
|
||||
|
||||
@HiveField(12)
|
||||
// @HiveField(12)
|
||||
wownero,
|
||||
|
||||
@HiveField(13)
|
||||
// @HiveField(13)
|
||||
zano,
|
||||
|
||||
@HiveField(14)
|
||||
// @HiveField(14)
|
||||
decred,
|
||||
|
||||
@HiveField(15)
|
||||
// @HiveField(15)
|
||||
dogecoin,
|
||||
|
||||
@HiveField(16)
|
||||
// @HiveField(16)
|
||||
base,
|
||||
|
||||
@HiveField(17)
|
||||
// @HiveField(17)
|
||||
arbitrum,
|
||||
|
||||
@HiveField(18)
|
||||
// @HiveField(18)
|
||||
zcash,
|
||||
|
||||
@HiveField(19)
|
||||
// @HiveField(19)
|
||||
bsc,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'wallet_type.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class WalletTypeAdapter extends TypeAdapter<WalletType> {
|
||||
@override
|
||||
final int typeId = 5;
|
||||
|
||||
@override
|
||||
WalletType read(BinaryReader reader) {
|
||||
switch (reader.readByte()) {
|
||||
case 0:
|
||||
return WalletType.monero;
|
||||
case 1:
|
||||
return WalletType.none;
|
||||
case 2:
|
||||
return WalletType.bitcoin;
|
||||
case 3:
|
||||
return WalletType.litecoin;
|
||||
case 4:
|
||||
return WalletType.haven;
|
||||
case 5:
|
||||
return WalletType.ethereum;
|
||||
case 6:
|
||||
return WalletType.nano;
|
||||
case 7:
|
||||
return WalletType.banano;
|
||||
case 8:
|
||||
return WalletType.bitcoinCash;
|
||||
case 9:
|
||||
return WalletType.polygon;
|
||||
case 10:
|
||||
return WalletType.solana;
|
||||
case 11:
|
||||
return WalletType.tron;
|
||||
case 12:
|
||||
return WalletType.wownero;
|
||||
case 13:
|
||||
return WalletType.zano;
|
||||
case 14:
|
||||
return WalletType.decred;
|
||||
case 15:
|
||||
return WalletType.dogecoin;
|
||||
case 16:
|
||||
return WalletType.base;
|
||||
case 17:
|
||||
return WalletType.arbitrum;
|
||||
case 18:
|
||||
return WalletType.zcash;
|
||||
case 19:
|
||||
return WalletType.bsc;
|
||||
default:
|
||||
return WalletType.monero;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, WalletType obj) {
|
||||
switch (obj) {
|
||||
case WalletType.monero:
|
||||
writer.writeByte(0);
|
||||
break;
|
||||
case WalletType.none:
|
||||
writer.writeByte(1);
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
writer.writeByte(2);
|
||||
break;
|
||||
case WalletType.litecoin:
|
||||
writer.writeByte(3);
|
||||
break;
|
||||
case WalletType.haven:
|
||||
writer.writeByte(4);
|
||||
break;
|
||||
case WalletType.ethereum:
|
||||
writer.writeByte(5);
|
||||
break;
|
||||
case WalletType.nano:
|
||||
writer.writeByte(6);
|
||||
break;
|
||||
case WalletType.banano:
|
||||
writer.writeByte(7);
|
||||
break;
|
||||
case WalletType.bitcoinCash:
|
||||
writer.writeByte(8);
|
||||
break;
|
||||
case WalletType.polygon:
|
||||
writer.writeByte(9);
|
||||
break;
|
||||
case WalletType.solana:
|
||||
writer.writeByte(10);
|
||||
break;
|
||||
case WalletType.tron:
|
||||
writer.writeByte(11);
|
||||
break;
|
||||
case WalletType.wownero:
|
||||
writer.writeByte(12);
|
||||
break;
|
||||
case WalletType.zano:
|
||||
writer.writeByte(13);
|
||||
break;
|
||||
case WalletType.decred:
|
||||
writer.writeByte(14);
|
||||
break;
|
||||
case WalletType.dogecoin:
|
||||
writer.writeByte(15);
|
||||
break;
|
||||
case WalletType.base:
|
||||
writer.writeByte(16);
|
||||
break;
|
||||
case WalletType.arbitrum:
|
||||
writer.writeByte(17);
|
||||
break;
|
||||
case WalletType.zcash:
|
||||
writer.writeByte(18);
|
||||
break;
|
||||
case WalletType.bsc:
|
||||
writer.writeByte(19);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is WalletTypeAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/wownero_amount_format.dart';
|
||||
|
||||
class WowneroBalance extends Balance {
|
||||
WowneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
|
||||
: formattedFrozenBalance = wowneroAmountToString(amount: frozenBalance),
|
||||
super.fromInt(unlockedBalance, fullBalance - unlockedBalance, frozen: frozenBalance);
|
||||
|
||||
final int fullBalance;
|
||||
final int unlockedBalance;
|
||||
final int frozenBalance;
|
||||
final String formattedFrozenBalance;
|
||||
|
||||
@override
|
||||
String get formattedUnAvailableBalance =>
|
||||
formattedFrozenBalance == '0.0' ? '' : formattedFrozenBalance;
|
||||
}
|
||||
+15
-15
@@ -4,38 +4,38 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'zano_asset.g.dart';
|
||||
part 'zano_asset.part.dart';
|
||||
|
||||
@HiveType(typeId: ZanoAsset.typeId)
|
||||
// @HiveType(typeId: ZanoAsset.typeId)
|
||||
class ZanoAsset extends CryptoCurrency with HiveObjectMixin {
|
||||
@HiveField(0)
|
||||
// @HiveField(0)
|
||||
final String fullName;
|
||||
@HiveField(1)
|
||||
// @HiveField(1)
|
||||
final String ticker;
|
||||
@HiveField(2)
|
||||
// @HiveField(2)
|
||||
final String assetId;
|
||||
@HiveField(3)
|
||||
// @HiveField(3)
|
||||
final int decimalPoint;
|
||||
@HiveField(4, defaultValue: true)
|
||||
// @HiveField(4, defaultValue: true)
|
||||
bool _enabled;
|
||||
@HiveField(5)
|
||||
// @HiveField(5)
|
||||
final String? iconPath;
|
||||
|
||||
// @HiveField(6)
|
||||
// final String? tag;
|
||||
@HiveField(6)
|
||||
// @HiveField(6)
|
||||
final String owner;
|
||||
@HiveField(7)
|
||||
// @HiveField(7)
|
||||
final String metaInfo;
|
||||
@HiveField(8)
|
||||
// @HiveField(8)
|
||||
final BigInt currentSupply;
|
||||
@HiveField(9)
|
||||
// @HiveField(9)
|
||||
final bool hiddenSupply;
|
||||
@HiveField(10)
|
||||
// @HiveField(10)
|
||||
final BigInt totalMaxSupply;
|
||||
@HiveField(11)
|
||||
// @HiveField(11)
|
||||
final bool isInGlobalWhitelist;
|
||||
@HiveField(12, defaultValue: null)
|
||||
// @HiveField(12, defaultValue: null)
|
||||
final Map<String, dynamic>? info;
|
||||
|
||||
bool get enabled => _enabled;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'zano_asset.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class ZanoAssetAdapter extends TypeAdapter<ZanoAsset> {
|
||||
@override
|
||||
final int typeId = 22;
|
||||
|
||||
@override
|
||||
ZanoAsset read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return ZanoAsset(
|
||||
fullName: fields[0] as String,
|
||||
ticker: fields[1] as String,
|
||||
assetId: fields[2] as String,
|
||||
decimalPoint: fields[3] as int,
|
||||
iconPath: fields[5] as String?,
|
||||
owner: fields[6] as String,
|
||||
metaInfo: fields[7] as String,
|
||||
currentSupply: fields[8] as BigInt,
|
||||
hiddenSupply: fields[9] as bool,
|
||||
totalMaxSupply: fields[10] as BigInt,
|
||||
isInGlobalWhitelist: fields[11] as bool,
|
||||
info: (fields[12] as Map?)?.cast<String, dynamic>(),
|
||||
).._enabled = fields[4] == null ? true : fields[4] as bool;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, ZanoAsset obj) {
|
||||
writer
|
||||
..writeByte(13)
|
||||
..writeByte(0)
|
||||
..write(obj.fullName)
|
||||
..writeByte(1)
|
||||
..write(obj.ticker)
|
||||
..writeByte(2)
|
||||
..write(obj.assetId)
|
||||
..writeByte(3)
|
||||
..write(obj.decimalPoint)
|
||||
..writeByte(4)
|
||||
..write(obj._enabled)
|
||||
..writeByte(5)
|
||||
..write(obj.iconPath)
|
||||
..writeByte(6)
|
||||
..write(obj.owner)
|
||||
..writeByte(7)
|
||||
..write(obj.metaInfo)
|
||||
..writeByte(8)
|
||||
..write(obj.currentSupply)
|
||||
..writeByte(9)
|
||||
..write(obj.hiddenSupply)
|
||||
..writeByte(10)
|
||||
..write(obj.totalMaxSupply)
|
||||
..writeByte(11)
|
||||
..write(obj.isInGlobalWhitelist)
|
||||
..writeByte(12)
|
||||
..write(obj.info);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ZanoAssetAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
+38
-107
@@ -5,23 +5,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "76.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
version: "92.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.0"
|
||||
version: "9.0.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -76,18 +71,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
||||
sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "4.0.6"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.3.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -96,30 +91,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.4"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||
sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.15"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
version: "2.15.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -149,10 +128,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -169,14 +148,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -221,10 +192,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.8"
|
||||
version: "3.1.3"
|
||||
decimal:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -291,14 +262,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -316,21 +279,13 @@ packages:
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
hive:
|
||||
dependency: transitive
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive
|
||||
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
hive_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive_generator
|
||||
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -391,26 +346,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -419,38 +374,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -463,18 +410,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mobx
|
||||
sha256: bf1a90e5bcfd2851fc6984e20eef69557c65d9e4d0a88f5be4cf72c9819ce6b0
|
||||
sha256: "25ea9a3ee69243d578fb6288721d8eeb18550d0391c32f896d377d994154bff2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.6.0"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
sha256: e0abbbc651a69550440f6b65c99ec222a1e2a4afd7baec8ba0f3088c7ca582a8
|
||||
sha256: "6529bcae074838004b58ad01fbe4966be182a176aacfb723686985986ebac19f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.1"
|
||||
version: "2.7.7"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -663,18 +610,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.5"
|
||||
version: "4.2.3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -799,18 +738,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "0.7.10"
|
||||
torch_dart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -846,10 +777,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -907,5 +838,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.9.0 <4.0.0"
|
||||
flutter: ">=3.27.0"
|
||||
|
||||
@@ -14,8 +14,8 @@ dependencies:
|
||||
http: ^1.1.0
|
||||
file: ^7.0.0
|
||||
path_provider: ^2.1.5
|
||||
mobx: ^2.0.7+4
|
||||
flutter_mobx: ^2.0.6+1
|
||||
mobx: ^2.6.0
|
||||
flutter_mobx: ^2.3.0
|
||||
intl: any
|
||||
encrypt: ^5.0.1
|
||||
decimal: ^2.3.3
|
||||
@@ -53,10 +53,10 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.15
|
||||
build_resolvers: ^2.4.4
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^2.0.1
|
||||
build_runner: ^2.15.0
|
||||
mobx_codegen: ^2.7.7
|
||||
hive: ^2.2.3
|
||||
# hive_generator: ^2.0.1
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:cw_core/amount/amount_sanitizer.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
group('smartAmountSanitizer', () {
|
||||
test('sanitize decimal dot', (){
|
||||
final amount = "1,000.00".sanitized();
|
||||
expect(amount, "1000.00");
|
||||
});
|
||||
|
||||
test('sanitize decimal comma', (){
|
||||
final amount = "1.000,00".sanitized();
|
||||
expect(amount, "1000.00");
|
||||
});
|
||||
|
||||
test('general case', (){
|
||||
final amount = "1000.00".sanitized();
|
||||
expect(amount, "1000.00");
|
||||
});
|
||||
|
||||
test('no decimals', (){
|
||||
final amount = "1,000".sanitized();
|
||||
expect(amount, "1.000");
|
||||
});
|
||||
|
||||
test('no decimals', (){
|
||||
final amount = "1.000".sanitized();
|
||||
expect(amount, "1.000");
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
import "package:cw_core/amount/money.dart";
|
||||
import "package:cw_core/crypto_currency.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
void main() {
|
||||
group("Money", () {
|
||||
test("parse", () {
|
||||
var money = Money.parse("1", CryptoCurrency.btc);
|
||||
expect(money.amount, BigInt.from(100000000));
|
||||
|
||||
money = Money.parse("-1", CryptoCurrency.btc);
|
||||
expect(money.amount, BigInt.from(-100000000));
|
||||
|
||||
money = Money.parse("1.11", CryptoCurrency.btc);
|
||||
expect(money.amount, BigInt.from(111000000));
|
||||
|
||||
money = Money.parse("-1.11", CryptoCurrency.btc);
|
||||
expect(money.amount, BigInt.from(-111000000));
|
||||
|
||||
money = Money.parse("-.11", CryptoCurrency.btc);
|
||||
expect(money.amount, BigInt.from(-11000000));
|
||||
|
||||
money = Money.parse(".11", CryptoCurrency.btc);
|
||||
expect(money.amount, BigInt.from(11000000));
|
||||
|
||||
// Invalid representation
|
||||
expect(() => Money.parse("1,11", CryptoCurrency.btc), throwsFormatException);
|
||||
|
||||
// To many decimals
|
||||
expect(() => Money.parse("-1.000000000000000", CryptoCurrency.btc), throwsFormatException);
|
||||
|
||||
money = Money.parse("1", CryptoCurrency.btc, isBaseUnit: true);
|
||||
expect(money.amount, BigInt.from(1));
|
||||
|
||||
money = Money.parse("0", CryptoCurrency.btc, isBaseUnit: true);
|
||||
expect(money.amount, BigInt.from(0));
|
||||
|
||||
money = Money.parse("-1", CryptoCurrency.btc, isBaseUnit: true);
|
||||
expect(money.amount, BigInt.from(-1));
|
||||
|
||||
// canonical representation when expecting base units
|
||||
expect(() => Money.parse("1.0", CryptoCurrency.btc, isBaseUnit: true), throwsFormatException);
|
||||
});
|
||||
|
||||
test("tryParse", () {
|
||||
var money = Money.tryParse("1", CryptoCurrency.btc);
|
||||
expect(money?.amount, BigInt.from(100000000));
|
||||
|
||||
money = Money.tryParse("-1", CryptoCurrency.btc);
|
||||
expect(money?.amount, BigInt.from(-100000000));
|
||||
|
||||
money = Money.tryParse("1.11", CryptoCurrency.btc);
|
||||
expect(money?.amount, BigInt.from(111000000));
|
||||
|
||||
money = Money.tryParse("-1.11", CryptoCurrency.btc);
|
||||
expect(money?.amount, BigInt.from(-111000000));
|
||||
|
||||
money = Money.tryParse("-.11", CryptoCurrency.btc);
|
||||
expect(money?.amount, BigInt.from(-11000000));
|
||||
|
||||
money = Money.tryParse(".11", CryptoCurrency.btc);
|
||||
expect(money?.amount, BigInt.from(11000000));
|
||||
|
||||
// Invalid representation
|
||||
money = Money.tryParse("1,11", CryptoCurrency.btc);
|
||||
expect(money?.amount, isNull);
|
||||
|
||||
// To many decimals
|
||||
money = Money.tryParse("-1.000000000000000", CryptoCurrency.btc);
|
||||
expect(money?.amount, isNull);
|
||||
|
||||
money = Money.tryParse("1", CryptoCurrency.btc, isBaseUnit: true);
|
||||
expect(money?.amount, BigInt.from(1));
|
||||
|
||||
money = Money.tryParse("0", CryptoCurrency.btc, isBaseUnit: true);
|
||||
expect(money?.amount, BigInt.from(0));
|
||||
|
||||
money = Money.tryParse("-1", CryptoCurrency.btc, isBaseUnit: true);
|
||||
expect(money?.amount, BigInt.from(-1));
|
||||
|
||||
// canonical representation when expecting base units
|
||||
money = Money.tryParse("1.0", CryptoCurrency.btc, isBaseUnit: true);
|
||||
expect(money?.amount, isNull);
|
||||
});
|
||||
|
||||
test("sign", () {
|
||||
expect(Money(BigInt.from(0), CryptoCurrency.btc).sign, 0);
|
||||
|
||||
expect(Money(BigInt.from(1), CryptoCurrency.btc).sign, 1);
|
||||
|
||||
expect(Money(BigInt.from(10), CryptoCurrency.btc).sign, 1);
|
||||
|
||||
expect(Money(BigInt.from(-1), CryptoCurrency.btc).sign, -1);
|
||||
|
||||
expect(Money(BigInt.from(-10), CryptoCurrency.btc).sign, -1);
|
||||
});
|
||||
|
||||
test("isZero", () {
|
||||
expect(Money(BigInt.from(0), CryptoCurrency.btc).isZero, isTrue);
|
||||
|
||||
expect(Money(BigInt.from(1), CryptoCurrency.btc).isZero, isFalse);
|
||||
|
||||
expect(Money(BigInt.from(-1), CryptoCurrency.btc).isZero, isFalse);
|
||||
});
|
||||
|
||||
test("isNegative", () {
|
||||
expect(Money(BigInt.from(0), CryptoCurrency.btc).isNegative, isFalse);
|
||||
|
||||
expect(Money(BigInt.from(1), CryptoCurrency.btc).isNegative, isFalse);
|
||||
|
||||
expect(Money(BigInt.from(-1), CryptoCurrency.btc).isNegative, isTrue);
|
||||
});
|
||||
|
||||
group("Comparison", () {
|
||||
final fourBitcoin = Money(BigInt.from(400000000), CryptoCurrency.btc);
|
||||
final fiveBitcoin = Money(BigInt.from(500000000), CryptoCurrency.btc);
|
||||
final sixBitcoin = Money(BigInt.from(600000000), CryptoCurrency.btc);
|
||||
|
||||
final fiveMonero = Money.parse("5", CryptoCurrency.xmr);
|
||||
|
||||
test("==", () {
|
||||
expect(fiveBitcoin, equals(Money(BigInt.from(500000000), CryptoCurrency.btc)));
|
||||
expect(fiveBitcoin, isNot(equals(fourBitcoin)));
|
||||
expect(fiveBitcoin, isNot(equals(sixBitcoin)));
|
||||
expect(fiveBitcoin, isNot(equals(fiveMonero)));
|
||||
// intentional check of incompatible types.
|
||||
// ignore: unrelated_type_equality_checks
|
||||
expect(fiveBitcoin == "Not money", equals(false));
|
||||
});
|
||||
|
||||
test("<", () {
|
||||
expect(fiveBitcoin < sixBitcoin, isTrue);
|
||||
expect(fiveBitcoin < fiveBitcoin, isFalse);
|
||||
expect(fiveBitcoin < fourBitcoin, isFalse);
|
||||
|
||||
// Cannot compare money in different currencies:
|
||||
expect(() => fiveBitcoin < fiveMonero, throwsArgumentError);
|
||||
});
|
||||
|
||||
test("<=", () {
|
||||
expect(fiveBitcoin <= sixBitcoin, isTrue);
|
||||
expect(fiveBitcoin <= fiveBitcoin, isTrue);
|
||||
expect(fiveBitcoin <= fourBitcoin, isFalse);
|
||||
|
||||
// Cannot compare money in different currencies:
|
||||
expect(() => fiveBitcoin <= fiveMonero, throwsArgumentError);
|
||||
});
|
||||
|
||||
test(">", () {
|
||||
expect(fiveBitcoin > fourBitcoin, isTrue);
|
||||
expect(fiveBitcoin > fiveBitcoin, isFalse);
|
||||
expect(fiveBitcoin > sixBitcoin, isFalse);
|
||||
|
||||
// Cannot compare money in different currencies:
|
||||
expect(() => fiveBitcoin > fiveMonero, throwsArgumentError);
|
||||
});
|
||||
|
||||
test(">=", () {
|
||||
expect(fiveBitcoin >= fourBitcoin, isTrue);
|
||||
expect(fiveBitcoin >= fiveBitcoin, isTrue);
|
||||
expect(fiveBitcoin >= sixBitcoin, isFalse);
|
||||
|
||||
// Cannot compare money in different currencies:
|
||||
expect(() => fiveBitcoin >= fiveMonero, throwsArgumentError);
|
||||
});
|
||||
|
||||
test("conformance to Comparable", () {
|
||||
expect(fiveBitcoin, isA<Comparable<Money>>());
|
||||
|
||||
expect(fiveBitcoin.compareTo(fiveBitcoin), isZero);
|
||||
expect(fiveBitcoin.compareTo(fourBitcoin), isPositive);
|
||||
expect(fiveBitcoin.compareTo(sixBitcoin), isNegative);
|
||||
expect(() => fiveBitcoin.compareTo(fiveMonero), throwsArgumentError);
|
||||
});
|
||||
});
|
||||
|
||||
group("Math", () {
|
||||
test("+", () {
|
||||
final oneBitcoin = Money.parse("1", CryptoCurrency.btc);
|
||||
final twoBitcoin = Money.parse("2", CryptoCurrency.btc);
|
||||
final threeBitcoin = Money.parse("3", CryptoCurrency.btc);
|
||||
|
||||
expect(oneBitcoin + twoBitcoin, equals(threeBitcoin));
|
||||
});
|
||||
|
||||
test("cannot add different currencies", () {
|
||||
final oneBitcoin = Money(BigInt.one, CryptoCurrency.btc);
|
||||
final oneMonero = Money(BigInt.one, CryptoCurrency.xmr);
|
||||
|
||||
expect(() => oneBitcoin + oneMonero, throwsArgumentError);
|
||||
});
|
||||
|
||||
test("invert money", () {
|
||||
final oneSatoshi = Money(BigInt.one, CryptoCurrency.btc);
|
||||
final negativeOneSatoshi = Money(BigInt.from(-1), CryptoCurrency.btc);
|
||||
|
||||
expect(-oneSatoshi, equals(negativeOneSatoshi));
|
||||
expect(-negativeOneSatoshi, equals(oneSatoshi));
|
||||
});
|
||||
|
||||
test("-", () {
|
||||
final oneSatoshi = Money(BigInt.one, CryptoCurrency.btc);
|
||||
final twoSatoshi = Money(BigInt.two, CryptoCurrency.btc);
|
||||
final threeSatoshi = Money(BigInt.from(3), CryptoCurrency.btc);
|
||||
|
||||
expect(threeSatoshi - oneSatoshi, equals(twoSatoshi));
|
||||
});
|
||||
|
||||
test("cannot subtract different currencies", () {
|
||||
final oneSatoshi = Money(BigInt.one, CryptoCurrency.btc);
|
||||
final oneMonero = Money(BigInt.one, CryptoCurrency.xmr);
|
||||
|
||||
expect(() => oneSatoshi - oneMonero, throwsArgumentError);
|
||||
});
|
||||
|
||||
test("*", () {
|
||||
final zeroSatoshi = Money(BigInt.zero, CryptoCurrency.btc);
|
||||
final oneSatoshi = Money(BigInt.one, CryptoCurrency.btc);
|
||||
final twoSatoshi = Money(BigInt.two, CryptoCurrency.btc);
|
||||
|
||||
expect(oneSatoshi * BigInt.zero, equals(zeroSatoshi));
|
||||
expect(oneSatoshi * BigInt.two, equals(twoSatoshi));
|
||||
expect(oneSatoshi * BigInt.from(-2), equals(-twoSatoshi));
|
||||
});
|
||||
|
||||
group("/", () {
|
||||
final threeBI = BigInt.from(3);
|
||||
final fourBI = BigInt.from(4);
|
||||
|
||||
final negThreeBI = BigInt.from(-3);
|
||||
final negFourBI = BigInt.from(-4);
|
||||
|
||||
test("rounds down when fractional part is < 0.5", () {
|
||||
expect((Money(BigInt.from(10), CryptoCurrency.btc) / threeBI).amount, threeBI);
|
||||
expect((Money(BigInt.from(-10), CryptoCurrency.btc) / threeBI).amount, negThreeBI);
|
||||
expect((Money(BigInt.from(10), CryptoCurrency.btc) / negThreeBI).amount, negThreeBI);
|
||||
expect((Money(BigInt.from(-10), CryptoCurrency.btc) / negThreeBI).amount, threeBI);
|
||||
});
|
||||
|
||||
test("rounds up when fractional part is > 0.5", () {
|
||||
expect((Money(BigInt.from(11), CryptoCurrency.btc) / threeBI).amount, fourBI); // 3.66...
|
||||
expect((Money(BigInt.from(-11), CryptoCurrency.btc) / threeBI).amount, negFourBI);
|
||||
expect((Money(BigInt.from(11), CryptoCurrency.btc) / negThreeBI).amount, negFourBI);
|
||||
expect((Money(BigInt.from(-11), CryptoCurrency.btc) / negThreeBI).amount, fourBI);
|
||||
});
|
||||
|
||||
test("rounds AWAY from zero when fractional part is exactly 0.5 (Ties)", () {
|
||||
// 5 / 2 = 2.5 -> 3
|
||||
expect((Money(BigInt.from(5), CryptoCurrency.btc) / BigInt.two).amount, threeBI);
|
||||
// -5 / 2 = -2.5 -> -3
|
||||
expect((Money(BigInt.from(-5), CryptoCurrency.btc) / BigInt.two).amount, negThreeBI);
|
||||
// 5 / -2 = -2.5 -> -3
|
||||
expect((Money(BigInt.from(5), CryptoCurrency.btc) / -BigInt.two).amount, negThreeBI);
|
||||
// -5 / -2 = 2.5 -> 3
|
||||
expect((Money(BigInt.from(-5), CryptoCurrency.btc) / -BigInt.two).amount, threeBI);
|
||||
});
|
||||
|
||||
test("handles zero numerator correctly", () {
|
||||
expect((Money(BigInt.zero, CryptoCurrency.btc) / BigInt.from(5)).amount, BigInt.zero);
|
||||
expect((Money(BigInt.zero, CryptoCurrency.btc) / BigInt.from(-5)).amount, BigInt.zero);
|
||||
});
|
||||
|
||||
test("throws Exception on division by zero", () {
|
||||
expect(
|
||||
() => Money(BigInt.from(5), CryptoCurrency.btc) / BigInt.zero,
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test("handles very large BigInts without precision loss", () {
|
||||
// A number that would lose precision if converted to a standard double
|
||||
final hugeA = BigInt.parse("10000000000000000000000000000000000005");
|
||||
final hugeB = BigInt.parse("10");
|
||||
// 1000...005 / 10 = 1000...000.5 -> Should round up to 1000...001
|
||||
final expected = BigInt.parse("1000000000000000000000000000000000001");
|
||||
|
||||
expect((Money(hugeA, CryptoCurrency.btc) / hugeB).amount, expected);
|
||||
});
|
||||
|
||||
test("handles exact division perfectly", () {
|
||||
expect((Money(BigInt.from(100), CryptoCurrency.btc) / BigInt.from(20)).amount,
|
||||
BigInt.from(5));
|
||||
expect((Money(BigInt.from(-100), CryptoCurrency.btc) / BigInt.from(20)).amount,
|
||||
BigInt.from(-5));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group("copyWith", () {
|
||||
final money = Money.parse("1", CryptoCurrency.btc);
|
||||
|
||||
test("currency", () {
|
||||
final copy = money.copyWith(currency: CryptoCurrency.eth);
|
||||
expect(copy.amount, equals(BigInt.parse("1000000000000000000")));
|
||||
expect(copy.currency.decimals, equals(18));
|
||||
expect(copy.toString(), "1");
|
||||
});
|
||||
|
||||
test("currency nano", () {
|
||||
final copy = money.copyWith(currency: CryptoCurrency.nano);
|
||||
expect(copy.amount, equals(BigInt.parse("1000000000000000000000000000000")));
|
||||
expect(copy.currency.decimals, equals(30));
|
||||
expect(copy.toString(), "1");
|
||||
});
|
||||
|
||||
test("amount", () {
|
||||
final copy = money.copyWith(amount: BigInt.one);
|
||||
expect(copy.amount, BigInt.one);
|
||||
expect(copy.currency.decimals, 8);
|
||||
});
|
||||
|
||||
test("amount and currency", () {
|
||||
final copy = money.copyWith(amount: BigInt.one);
|
||||
expect(copy.amount, BigInt.one);
|
||||
expect(copy.currency.decimals, 8);
|
||||
});
|
||||
});
|
||||
|
||||
group("toStringWithPrecision", (){
|
||||
test("with fractionalDigits and padded with zeros", () {
|
||||
final money = Money.parse("1", CryptoCurrency.btc);
|
||||
expect(money.amount, equals(BigInt.parse("100000000")));
|
||||
expect(money.currency.decimals, equals(8));
|
||||
expect(money.toStringWithPrecision(fractionalDigits: 5, trimZeros: false), "1.00000");
|
||||
});
|
||||
|
||||
test("using base unit sats", () {
|
||||
final money = Money.parse("1", CryptoCurrency.btc);
|
||||
expect(money.amount, equals(BigInt.parse("100000000")));
|
||||
expect(money.currency.decimals, equals(8));
|
||||
expect(money.toStringWithPrecision(useBaseUnit: true), "100000000");
|
||||
});
|
||||
});
|
||||
|
||||
group("toStringWithSymbol", (){
|
||||
test("with fractionalDigits and padded with zeros", () {
|
||||
final money = Money.parse("1", CryptoCurrency.btc);
|
||||
expect(money.amount, equals(BigInt.parse("100000000")));
|
||||
expect(money.currency.decimals, equals(8));
|
||||
expect(money.toStringWithSymbol(fractionalDigits: 5, trimZeros: false), "1.00000 BTC");
|
||||
});
|
||||
|
||||
test("using base unit sats", () {
|
||||
final money = Money.parse("1", CryptoCurrency.btc);
|
||||
expect(money.amount, equals(BigInt.parse("100000000")));
|
||||
expect(money.currency.decimals, equals(8));
|
||||
expect(money.toStringWithSymbol(useBaseUnit: true), "100000000 sats");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/lnurl.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
import 'package:cw_core/utils/tor/abstract.dart';
|
||||
@@ -11,13 +13,14 @@ void main() {
|
||||
expect(content, Uri.parse("https://cake.cash/.well-known/lnurlp/konsti"));
|
||||
});
|
||||
|
||||
test('get amount', () async {
|
||||
CakeTor.instance = await CakeTorInstance.getInstance();
|
||||
|
||||
final content = await LNURL.getPayRequestAmount(
|
||||
"LNURL1DP68GURN8GHJ7CTSWQHXY6T5WFJHZAT9WD6ZU6T09AUZ7URJDAU8JTMKXYHKCM308A5N6MNHVVNXJEPAXGMNWWPEX3JRGDTPXVNXZ0FCX5CRQVPSXQNX602VFE24YNP9XGC8W6T5DQJNYVRPD4HH2MN5GTRNLZ");
|
||||
expect(content, 8500);
|
||||
});
|
||||
// This tests includes a rest request to an URL that expired and due to the lack of an alternative URL we ignore this test
|
||||
// test('get amount', () async {
|
||||
// CakeTor.instance = await CakeTorInstance.getInstance();
|
||||
//
|
||||
// final content = await LNURL.getPayRequestAmount(
|
||||
// "LNURL1DP68GURN8GHJ7CTSWQHXY6T5WFJHZAT9WD6ZU6T09AUZ7URJDAU8JTMKXYHKCM308A5N6MNHVVNXJEPAXGMNWWPEX3JRGDTPXVNXZ0FCX5CRQVPSXQNX602VFE24YNP9XGC8W6T5DQJNYVRPD4HH2MN5GTRNLZ");
|
||||
// expect(content, Money.fromInt(8500, CryptoCurrency.btcln));
|
||||
// });
|
||||
|
||||
test('get no amount', () async {
|
||||
CakeTor.instance = await CakeTorInstance.getInstance();
|
||||
@@ -64,22 +67,22 @@ void main() {
|
||||
|
||||
test("should get the amount 1 BTC encoded as bitcoin", () {
|
||||
final invoice = "lnbc11p5exaxspp5u6e6lx90rw2vqr2e5axt8ur3v98qnmqfsmh8r0l0mcm7ry70vyvsdqqcqzzsxqrrsssp5eqydmrufk39dwhuxzajwx4xyffn0mlcu3qa8vjwry6qev5gsepkq9qxpqysgqt0zldve9uxt3txjt8wp0gev52lquljgugy26nneue85pjwk5yak4hs2d8533tus07pq5ka6zrnd0ruaeepwres8p7ws5mx63xlwff5sqlxcy8a";
|
||||
expect(getBolt11Amount(invoice), 100000000);
|
||||
expect(getBolt11Amount(invoice), Money.fromInt(100000000, CryptoCurrency.btcln));
|
||||
});
|
||||
|
||||
test("should get the amount 0,1 BTC encoded as millibitcoin", () {
|
||||
final invoice = "lnbc100m1p5exagmpp5n4c5tml3h8h7k4gthc9papkyeymceqqvcldkng4nvxwzj7nu9jnsdqqcqzzsxqrrsssp58kgzrh7y3pevantduc4yg33aa6r79ed6xgmhr0dy40zc8pxrz5ms9qxpqysgqcq27km3v3wcz6pntmsfnz259zy6tff0uwjz9kamdrvmxmdv09gp44mh0ygn7gweqf4zdev4x8e67p5kcvahethchdkkjexhtk7emuqcpasz0lp";
|
||||
expect(getBolt11Amount(invoice), 10000000);
|
||||
expect(getBolt11Amount(invoice), Money.fromInt(10000000, CryptoCurrency.btcln));
|
||||
});
|
||||
|
||||
test("should get the amount 1000 SATS encoded as microbitcoin", () {
|
||||
final invoice = "lnbc10u1p5exfgvpp5n3s9dsw9ddax5c3h8437ya7y4g582uyufyna626yr9vqdfjrhwzqdqqcqzzsxqrrsssp5mmkyp5h35fpaxm4063f3kc7d4nmgd4gd8fv8086wxd784m76kmpq9qxpqysgqye9r0vahpjy9l2je6vakxzk3cjnsyx76r29c4amkz4cmapq5tqt5l7rl0emkd003jjvz2d8jqrw6wc4cduvapuyeseh7a855l82wutcpeq30kg";
|
||||
expect(getBolt11Amount(invoice), 1000);
|
||||
expect(getBolt11Amount(invoice), Money.fromInt(1000, CryptoCurrency.btcln));
|
||||
});
|
||||
|
||||
test("should get the amount 1 SAT encoded as nanobitcoin", () {
|
||||
final invoice = "lnbc10n1p5exazppp5szh273r6nzvwydzsdlg0ws0vhxwnc6nqtqt4sjt0vu6x6l8t5csqdqqcqzzsxqrrsssp5nkup6uht7xlfqaj8lxpe5kx0ejum9p4pvv3nlnndr9ykzge3393s9qxpqysgqufh05a7qs0ezeckvmh7tjkr9ngng96nq274twg20z45ysp7afpdrttm2ky73cpjwpjqsmtzzd79w6lyvszgmkanwn8mj0jcfunrwv3qprg3z3j";
|
||||
expect(getBolt11Amount(invoice), 1);
|
||||
expect(getBolt11Amount(invoice), Money.fromInt(1, CryptoCurrency.btcln));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
|
||||
const decredAmountLength = 8;
|
||||
const decredAmountDivider = 100000000;
|
||||
final decredAmountFormat = NumberFormat()
|
||||
..maximumFractionDigits = decredAmountLength
|
||||
..minimumFractionDigits = 1;
|
||||
|
||||
String decredAmountToString({required int amount}) =>
|
||||
decredAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: decredAmountDivider));
|
||||
|
||||
double decredAmountToDouble({required int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: decredAmountDivider);
|
||||
|
||||
int stringDoubleToDecredAmount(String amount) {
|
||||
int result = 0;
|
||||
|
||||
try {
|
||||
result = (double.parse(amount) * decredAmountDivider).round();
|
||||
} catch (e) {
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,26 +1,16 @@
|
||||
import 'package:cw_decred/amount_format.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class DecredBalance extends Balance {
|
||||
DecredBalance({required this.confirmed, required this.unconfirmed, required int frozen})
|
||||
: _frozen = frozen, super.fromInt(confirmed, unconfirmed);
|
||||
DecredBalance({required Money confirmed, required Money unconfirmed, required Money frozen})
|
||||
: super(confirmed, unconfirmed, frozen: frozen);
|
||||
|
||||
factory DecredBalance.zero() => DecredBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
|
||||
|
||||
final int confirmed;
|
||||
final int unconfirmed;
|
||||
final int _frozen;
|
||||
BigInt get frozen => BigInt.from(_frozen);
|
||||
factory DecredBalance.zero() => DecredBalance(
|
||||
confirmed: Money.zero(CryptoCurrency.dcr),
|
||||
unconfirmed: Money.zero(CryptoCurrency.dcr),
|
||||
frozen: Money.zero(CryptoCurrency.dcr));
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance => decredAmountToString(amount: confirmed - _frozen);
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance => decredAmountToString(amount: unconfirmed);
|
||||
|
||||
@override
|
||||
String get formattedUnAvailableBalance {
|
||||
final frozenFormatted = decredAmountToString(amount: _frozen);
|
||||
return frozenFormatted == '0.0' ? '' : frozenFormatted;
|
||||
}
|
||||
Money get available => super.available - (frozen ?? Money.zero(CryptoCurrency.dcr));
|
||||
}
|
||||
|
||||
@@ -1,42 +1,33 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_decred/amount_format.dart';
|
||||
|
||||
class DecredPendingTransaction with PendingTransaction {
|
||||
DecredPendingTransaction(
|
||||
{required this.txid,
|
||||
required this.amount,
|
||||
required this.fee,
|
||||
required this.rawHex,
|
||||
required this.send});
|
||||
DecredPendingTransaction({
|
||||
required this.txId,
|
||||
required this.amount,
|
||||
required this.fee,
|
||||
required this.rawHex,
|
||||
required this.send,
|
||||
});
|
||||
|
||||
final int amount;
|
||||
final int fee;
|
||||
final String txid;
|
||||
final Money amount;
|
||||
final Money fee;
|
||||
final String txId;
|
||||
final String rawHex;
|
||||
final Future<void> Function() send;
|
||||
|
||||
@override
|
||||
String get id => txid;
|
||||
String get id => txId;
|
||||
|
||||
@override
|
||||
String get amountFormatted => decredAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue DCR";
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => decredAmountToString(amount: fee);
|
||||
String get amountFormatted => amount.toString();
|
||||
|
||||
@override
|
||||
String get hex => rawHex;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
return send();
|
||||
}
|
||||
Future<void> commit() => send();
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> commitUR() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
Future<Map<String, String>> commitUR() => throw UnimplementedError();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import 'package:cw_core/currency_for_wallet_type.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_decred/amount_format.dart';
|
||||
|
||||
class DecredTransactionInfo extends TransactionInfo {
|
||||
DecredTransactionInfo({
|
||||
required String id,
|
||||
required int amount,
|
||||
required int fee,
|
||||
required Money amount,
|
||||
required Money fee,
|
||||
required TransactionDirection direction,
|
||||
required bool isPending,
|
||||
required DateTime date,
|
||||
@@ -27,20 +24,4 @@ class DecredTransactionInfo extends TransactionInfo {
|
||||
this.confirmations = confirmations;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
String? _fiatAmount;
|
||||
|
||||
@override
|
||||
String amountFormatted() =>
|
||||
'${formatAmount(decredAmountToString(amount: amount))} ${walletTypeToCryptoCurrency(WalletType.decred).title}';
|
||||
|
||||
@override
|
||||
String? feeFormatted() =>
|
||||
'${formatAmount(decredAmountToString(amount: fee ?? 0))} ${walletTypeToCryptoCurrency(WalletType.decred).title}';
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
}
|
||||
|
||||
+33
-38
@@ -1,13 +1,13 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:cw_core/exceptions.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_decred/amount_format.dart';
|
||||
import 'package:cw_decred/pending_transaction.dart';
|
||||
import 'package:cw_decred/transaction_credentials.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -121,12 +121,7 @@ abstract class DecredWalletBase
|
||||
@override
|
||||
bool isTestnet;
|
||||
|
||||
String get pubkey {
|
||||
return _pubkey;
|
||||
}
|
||||
|
||||
@override
|
||||
String formatCryptoAmount(String amount) => decredAmountToString(amount: int.parse(amount));
|
||||
String get pubkey => _pubkey;
|
||||
|
||||
Future<void> init() async {
|
||||
final getSeed = () async {
|
||||
@@ -354,13 +349,12 @@ abstract class DecredWalletBase
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
if (watchingOnly) {
|
||||
return DecredPendingTransaction(
|
||||
txid: "",
|
||||
amount: 0,
|
||||
fee: 0,
|
||||
rawHex: "",
|
||||
send: () async {
|
||||
throw "unable to send with watching only wallet";
|
||||
});
|
||||
txId: "",
|
||||
amount: Money.zero(currency),
|
||||
fee: Money.zero(currency),
|
||||
rawHex: "",
|
||||
send: () async => throw "unable to send with watching only wallet",
|
||||
);
|
||||
}
|
||||
var totalIn = 0;
|
||||
final ignoreInputs = [];
|
||||
@@ -380,14 +374,12 @@ abstract class DecredWalletBase
|
||||
for (final out in creds.outputs) {
|
||||
var amt = 0;
|
||||
if (out.sendAll) {
|
||||
if (creds.outputs.length != 1) {
|
||||
throw "can only send all to one output";
|
||||
}
|
||||
if (creds.outputs.length != 1) throw "can only send all to one output";
|
||||
|
||||
sendAll = true;
|
||||
totalAmt = totalIn;
|
||||
} else if (out.cryptoAmount != null) {
|
||||
final coins = double.parse(out.cryptoAmount!);
|
||||
amt = (coins * 1e8).round();
|
||||
} else {
|
||||
amt = out.cryptoAmount.amount.toInt();
|
||||
}
|
||||
totalAmt += amt;
|
||||
final o = {
|
||||
@@ -416,6 +408,7 @@ abstract class DecredWalletBase
|
||||
final res = await _libwallet.createSignedTransaction(walletInfo.name, jsonEncode(signReq));
|
||||
final decoded = json.decode(res);
|
||||
final signedHex = decoded["signedhex"];
|
||||
|
||||
final send = () async {
|
||||
await _libwallet.sendRawTransaction(walletInfo.name, signedHex);
|
||||
await updateBalance();
|
||||
@@ -424,8 +417,14 @@ abstract class DecredWalletBase
|
||||
if (sendAll) {
|
||||
totalAmt = (totalAmt - fee).round();
|
||||
}
|
||||
|
||||
return DecredPendingTransaction(
|
||||
txid: decoded["txid"] ?? "", amount: totalAmt, fee: fee, rawHex: signedHex, send: send);
|
||||
txId: decoded["txid"] ?? "",
|
||||
amount: Money.fromInt(totalAmt, currency),
|
||||
fee: Money.fromInt(fee, currency),
|
||||
rawHex: signedHex,
|
||||
send: send,
|
||||
);
|
||||
}
|
||||
|
||||
int feeRate(TransactionPriority priority) {
|
||||
@@ -477,15 +476,14 @@ abstract class DecredWalletBase
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, DecredTransactionInfo>> fetchTransactions() async {
|
||||
return this.fetchFiveTransactions(0);
|
||||
}
|
||||
Future<Map<String, DecredTransactionInfo>> fetchTransactions() => fetchFiveTransactions(0);
|
||||
|
||||
Future<Map<String, DecredTransactionInfo>> fetchFiveTransactions(int from) async {
|
||||
try {
|
||||
final res = await _libwallet.listTransactions(walletInfo.name, from.toString(), "5");
|
||||
final decoded = json.decode(res);
|
||||
var txs = <String, DecredTransactionInfo>{};
|
||||
final txs = <String, DecredTransactionInfo>{};
|
||||
|
||||
for (final d in decoded) {
|
||||
final txid = uniqueTxID(d["txid"] ?? "", d["vout"] ?? 0);
|
||||
var direction = TransactionDirection.outgoing;
|
||||
@@ -498,19 +496,18 @@ abstract class DecredWalletBase
|
||||
final fee = (feeDouble * 1e8).round().abs();
|
||||
final confs = d["confirmations"] ?? 0;
|
||||
final sendTime = d["time"] ?? 0;
|
||||
final height = d["height"] ?? 0;
|
||||
final txInfo = DecredTransactionInfo(
|
||||
|
||||
txs[txid] = DecredTransactionInfo(
|
||||
id: txid,
|
||||
amount: amount,
|
||||
fee: fee,
|
||||
amount: Money.fromInt(amount, currency),
|
||||
fee: Money.fromInt(fee, currency),
|
||||
direction: direction,
|
||||
isPending: confs == 0,
|
||||
date: DateTime.fromMillisecondsSinceEpoch(sendTime * 1000, isUtc: false),
|
||||
height: height,
|
||||
height: d["height"] ?? 0,
|
||||
confirmations: confs,
|
||||
to: d["address"] ?? "",
|
||||
);
|
||||
txs[txid] = txInfo;
|
||||
}
|
||||
return txs;
|
||||
} catch (e) {
|
||||
@@ -520,9 +517,7 @@ abstract class DecredWalletBase
|
||||
}
|
||||
|
||||
// uniqueTxID combines the tx id and vout to create a unique id.
|
||||
String uniqueTxID(String id, int vout) {
|
||||
return id + ":" + vout.toString();
|
||||
}
|
||||
String uniqueTxID(String id, int vout) => "$id:$vout";
|
||||
|
||||
@override
|
||||
Future<void> save() async {}
|
||||
@@ -587,9 +582,9 @@ abstract class DecredWalletBase
|
||||
});
|
||||
|
||||
balance[CryptoCurrency.dcr] = DecredBalance(
|
||||
confirmed: balanceMap["confirmed"] ?? 0,
|
||||
unconfirmed: balanceMap["unconfirmed"] ?? 0,
|
||||
frozen: totalFrozen,
|
||||
confirmed: Money.fromInt(balanceMap["confirmed"] ?? 0, currency),
|
||||
unconfirmed: Money.fromInt(balanceMap["unconfirmed"] ?? 0, currency),
|
||||
frozen: Money.fromInt(totalFrozen, currency),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -648,7 +643,7 @@ abstract class DecredWalletBase
|
||||
try {
|
||||
final res = await _libwallet.listUnspents(walletInfo.name);
|
||||
final decoded = json.decode(res);
|
||||
var unspents = <Unspent>[];
|
||||
final unspents = <Unspent>[];
|
||||
for (final d in decoded) {
|
||||
final spendable = d["spendable"] ?? false;
|
||||
if (!spendable) {
|
||||
|
||||
@@ -10,7 +10,6 @@ import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
|
||||
class DecredWalletService extends WalletService<
|
||||
|
||||
+42
-111
@@ -5,23 +5,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "76.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
version: "93.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.0"
|
||||
version: "10.0.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -76,18 +71,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "4.0.6"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.3.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -96,30 +91,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||
sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.15"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
version: "2.15.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -132,10 +111,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4
|
||||
sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.5"
|
||||
version: "8.12.6"
|
||||
cake_backup:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -149,10 +128,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -177,14 +156,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -236,10 +207,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
||||
sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.7"
|
||||
version: "3.1.7"
|
||||
decimal:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -276,10 +247,10 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: ffigen
|
||||
sha256: "2119b4fe3aad0db94dc9531b90283c4640a6231070e613c400b426a4da08c704"
|
||||
sha256: b7803707faeec4ce3c1b0c2274906504b796e3b70ad573577e72333bd1c9b3ba
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "16.1.0"
|
||||
version: "20.1.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -314,14 +285,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -339,21 +302,13 @@ packages:
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
hive:
|
||||
dependency: transitive
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive
|
||||
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
hive_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive_generator
|
||||
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -414,26 +369,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -442,38 +397,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -486,18 +433,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mobx
|
||||
sha256: bf1a90e5bcfd2851fc6984e20eef69557c65d9e4d0a88f5be4cf72c9819ce6b0
|
||||
sha256: "25ea9a3ee69243d578fb6288721d8eeb18550d0391c32f896d377d994154bff2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.6.0"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
sha256: "990da80722f7d7c0017dec92040b31545d625b15d40204c36a1e63d167c73cdc"
|
||||
sha256: "6529bcae074838004b58ad01fbe4966be182a176aacfb723686985986ebac19f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
version: "2.7.7"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -694,18 +641,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.5"
|
||||
version: "4.2.3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -830,18 +769,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "0.7.10"
|
||||
torch_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -877,10 +808,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -946,5 +877,5 @@ packages:
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.10.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
@@ -18,11 +18,10 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.15
|
||||
build_resolvers: ^2.4.4
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^2.0.1
|
||||
ffigen: ^16.1.0
|
||||
build_runner: ^2.15.0
|
||||
mobx_codegen: ^2.7.7
|
||||
ffigen: ^20.1.1
|
||||
hive: ^2.2.3
|
||||
|
||||
ffigen:
|
||||
name: libdcrwallet
|
||||
|
||||
@@ -68,7 +68,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
|
||||
}
|
||||
|
||||
@override
|
||||
int get networkDustAmount => 100000000; // 1 DOGE = 1e8 koinu
|
||||
BigInt get networkDustAmount => BigInt.from(100000000); // 1 DOGE = 1e8 koinu
|
||||
|
||||
static int estimatedDogeCoinTransactionSize(int inputsCount, int outputsCounts) =>
|
||||
inputsCount * 180 + outputsCounts * 34 + 10;
|
||||
|
||||
@@ -15,8 +15,8 @@ dependencies:
|
||||
bip39: ^1.0.6
|
||||
bip32: ^2.0.0
|
||||
path_provider: ^2.0.11
|
||||
mobx: ^2.0.7+4
|
||||
flutter_mobx: ^2.0.6+1
|
||||
mobx: ^2.6.0
|
||||
flutter_mobx: ^2.3.0
|
||||
cw_core:
|
||||
path: ../cw_core
|
||||
cw_bitcoin:
|
||||
@@ -30,9 +30,10 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.15
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^2.0.1
|
||||
build_runner: ^2.15.0
|
||||
mobx_codegen: ^2.7.7
|
||||
# hive_generator: ^2.0.1
|
||||
hive: 2.0.1
|
||||
|
||||
dependency_overrides:
|
||||
watcher: ^1.1.0
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
@@ -144,7 +145,8 @@ class EVMChainClient {
|
||||
|
||||
final jsonResponse = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0) {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300 && jsonResponse['status'] != 0 &&
|
||||
jsonResponse['result'] is List) {
|
||||
final symbol = EVMChainUtils.getFeeCurrency(chainId);
|
||||
|
||||
return (jsonResponse['result'] as List)
|
||||
@@ -297,14 +299,13 @@ class EVMChainClient {
|
||||
Future<PendingEVMChainTransaction> signTransaction({
|
||||
required Credentials privateKey,
|
||||
required String toAddress,
|
||||
required BigInt amount,
|
||||
required BigInt gasFee,
|
||||
required Money amount,
|
||||
required Money gasFee,
|
||||
required int estimatedGasUnits,
|
||||
required int maxFeePerGas,
|
||||
required EVMChainTransactionPriority? priority,
|
||||
required CryptoCurrency currency,
|
||||
required String feeCurrency,
|
||||
required int exponent,
|
||||
String? contractAddress,
|
||||
String? data,
|
||||
int? gasPrice,
|
||||
@@ -317,11 +318,13 @@ class EVMChainClient {
|
||||
currency == CryptoCurrency.bnb ||
|
||||
contractAddress != null);
|
||||
|
||||
bool isNativeToken = currency == CryptoCurrency.eth ||
|
||||
currency == CryptoCurrency.maticpoly ||
|
||||
currency == CryptoCurrency.baseEth ||
|
||||
currency == CryptoCurrency.arbEth ||
|
||||
currency == CryptoCurrency.bnb;
|
||||
final isNativeToken = [
|
||||
CryptoCurrency.eth,
|
||||
CryptoCurrency.maticpoly,
|
||||
CryptoCurrency.baseEth,
|
||||
CryptoCurrency.arbEth,
|
||||
CryptoCurrency.bnb
|
||||
].contains(currency);
|
||||
|
||||
// Get nonce with "pending" block tag to include pending transactions
|
||||
// This prevents "Nonce too low" errors when sending multiple transactions quickly
|
||||
@@ -335,7 +338,7 @@ class EVMChainClient {
|
||||
to: EthereumAddress.fromHex(toAddress),
|
||||
maxPriorityFeePerGas:
|
||||
priority != null ? EtherAmount.fromInt(EtherUnit.gwei, priority.tip) : null,
|
||||
amount: isNativeToken ? EtherAmount.inWei(amount) : EtherAmount.zero(),
|
||||
amount: isNativeToken ? EtherAmount.inWei(amount.amount) : EtherAmount.zero(),
|
||||
data: data != null ? hexToBytes(data) : null,
|
||||
maxGas: estimatedGasUnits,
|
||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||
@@ -358,7 +361,7 @@ class EVMChainClient {
|
||||
|
||||
signedTransaction = await erc20.transfer(
|
||||
EthereumAddress.fromHex(toAddress),
|
||||
amount,
|
||||
amount.amount,
|
||||
credentials: privateKey,
|
||||
transaction: transaction,
|
||||
);
|
||||
@@ -369,24 +372,20 @@ class EVMChainClient {
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
signedTransaction: prepareSignedTransactionForSending(signedTransaction),
|
||||
amount: amount.toString(),
|
||||
amount: amount,
|
||||
fee: gasFee,
|
||||
feeCurrency: feeCurrency,
|
||||
sendTransaction: _sendTransaction,
|
||||
exponent: exponent,
|
||||
);
|
||||
}
|
||||
|
||||
Future<PendingEVMChainTransaction> signApprovalTransaction({
|
||||
required Credentials privateKey,
|
||||
required String spender,
|
||||
required BigInt amount,
|
||||
required BigInt gasFee,
|
||||
required String feeCurrency,
|
||||
required Money amount,
|
||||
required Money gasFee,
|
||||
required int estimatedGasUnits,
|
||||
required int maxFeePerGas,
|
||||
required EVMChainTransactionPriority? priority,
|
||||
required int exponent,
|
||||
required String contractAddress,
|
||||
int? gasPrice,
|
||||
bool useBlinkProtection = true,
|
||||
@@ -416,20 +415,18 @@ class EVMChainClient {
|
||||
|
||||
final signedTransaction = await erc20.approve(
|
||||
EthereumAddress.fromHex(spender),
|
||||
amount,
|
||||
amount.amount,
|
||||
credentials: privateKey,
|
||||
transaction: transaction,
|
||||
);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
signedTransaction: prepareSignedTransactionForSending(signedTransaction),
|
||||
amount: amount.toString(),
|
||||
amount: amount,
|
||||
fee: gasFee,
|
||||
feeCurrency: feeCurrency,
|
||||
sendTransaction: () =>
|
||||
sendTransaction(signedTransaction, useBlinkProtection: useBlinkProtection),
|
||||
exponent: exponent,
|
||||
isInfiniteApproval: amount.toRadixString(16) ==
|
||||
isInfiniteApproval: amount.amount.toRadixString(16) ==
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
);
|
||||
}
|
||||
@@ -523,20 +520,17 @@ class EVMChainClient {
|
||||
*/
|
||||
}
|
||||
|
||||
Future<EVMChainERC20Balance> fetchERC20Balances(
|
||||
EthereumAddress userAddress, String contractAddress) async {
|
||||
Future<EVMChainERC20Balance> fetchERC20Balances(EthereumAddress userAddress, Erc20Token token) async {
|
||||
try {
|
||||
final erc20 = ERC20(address: EthereumAddress.fromHex(contractAddress), client: _client!);
|
||||
final erc20 = ERC20(address: EthereumAddress.fromHex(token.contractAddress), client: _client!);
|
||||
final balance = await erc20.balanceOf(userAddress);
|
||||
|
||||
int exponent = (await erc20.decimals()).toInt();
|
||||
|
||||
return EVMChainERC20Balance(balance, exponent: exponent);
|
||||
return EVMChainERC20Balance(Money(balance, token));
|
||||
} on RangeError catch (_) {
|
||||
throw Exception('Invalid token contract for this network.');
|
||||
} catch (e) {
|
||||
if (e.toString().contains("hostUnreachable")) {
|
||||
return EVMChainERC20Balance(BigInt.zero);
|
||||
return EVMChainERC20Balance(Money.zero(token));
|
||||
}
|
||||
throw Exception('Could not fetch balances: ${e.toString()}');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_evm/deuro/deuro_savings_contract.dart';
|
||||
import 'package:cw_evm/deuro/deuro_savings_gateway_contract.dart' as v1;
|
||||
import 'package:cw_evm/deuro/deuro_savings_contract.dart' as v2;
|
||||
import 'package:cw_evm/evm_chain_wallet.dart';
|
||||
import 'package:cw_evm/contract/erc20.dart';
|
||||
import 'package:cw_evm/evm_chain_exceptions.dart';
|
||||
@@ -9,21 +11,27 @@ import 'package:web3dart/crypto.dart';
|
||||
import 'package:web3dart/web3dart.dart';
|
||||
|
||||
const String savingsGatewayAddress = "0x073493d73258C4BEb6542e8dd3e1b2891C972303";
|
||||
const String savingsV2Address = "0x760233b90e45d186A9A98E911B115F7F4B90d3D9";
|
||||
|
||||
const String dEuroAddress = "0xbA3f535bbCcCcA2A154b573Ca6c5A49BAAE0a3ea";
|
||||
const String frontendCode = "0x00000000000000000000000000000000000000000043616b652057616c6c6574";
|
||||
|
||||
class DEuro {
|
||||
final SavingsGateway _savingsGateway;
|
||||
final v2.Savings _savings;
|
||||
final ERC20 _dEuro;
|
||||
final EVMChainWallet _wallet;
|
||||
|
||||
DEuro(EVMChainWallet wallet)
|
||||
: _wallet = wallet,
|
||||
_savingsGateway = _getSavingsGateway(wallet.getWeb3Client()!),
|
||||
_savings = _getSavings(wallet.getWeb3Client()!),
|
||||
_dEuro = _getDEuroToken(wallet.getWeb3Client()!);
|
||||
|
||||
static SavingsGateway _getSavingsGateway(Web3Client client) => SavingsGateway(
|
||||
static v2.Savings _getSavings(Web3Client client) => v2.Savings(
|
||||
address: EthereumAddress.fromHex(savingsV2Address),
|
||||
client: client,
|
||||
);
|
||||
|
||||
static v1.SavingsGateway _getSavingsGateway(Web3Client client) => v1.SavingsGateway(
|
||||
address: EthereumAddress.fromHex(savingsGatewayAddress),
|
||||
client: client,
|
||||
);
|
||||
@@ -35,26 +43,31 @@ class DEuro {
|
||||
|
||||
EthereumAddress get _address => EthereumAddress.fromHex(_wallet.walletAddresses.primaryAddress);
|
||||
|
||||
Future<BigInt> get savingsBalance async =>
|
||||
(await _savingsGateway.savings(accountOwner: _address)).saved;
|
||||
Future<Money> get savingsBalance async =>
|
||||
Money((await _savings.savings(accountOwner: _address)).saved, CryptoCurrency.deuro);
|
||||
|
||||
Future<BigInt> get accruedInterest => _savingsGateway.accruedInterest(accountOwner: _address);
|
||||
Future<Money> get savingsBalanceV1 async => Money(
|
||||
(await _getSavingsGateway(_wallet.getWeb3Client()!).savings(accountOwner: _address)).saved,
|
||||
CryptoCurrency.deuro);
|
||||
|
||||
Future<BigInt> get interestRate => _savingsGateway.currentRatePPM();
|
||||
Future<Money> get accruedInterest async =>
|
||||
Money(await _savings.accruedInterest(accountOwner: _address), CryptoCurrency.deuro);
|
||||
|
||||
Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savingsGateway.self.address);
|
||||
Future<BigInt> get interestRate => _savings.currentRatePPM();
|
||||
|
||||
Future<BigInt> get approvedBalance => _dEuro.allowance(_address, _savings.self.address);
|
||||
|
||||
Future<void> _checkEthBalanceForGasFees(EVMChainTransactionPriority priority) async {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
final currentBalance = ethBalance.getInWei;
|
||||
final savings = _getSavingsGateway(_wallet.getWeb3Client()!);
|
||||
|
||||
final gasFeesModel = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: BigInt.zero,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: savings.self.address.hexEip55,
|
||||
receivingAddressHex: savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[17]
|
||||
.encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
|
||||
data: savings.self.abi.functions[17].encodeCall([BigInt.zero, hexToBytes(frontendCode)]),
|
||||
);
|
||||
|
||||
final estimatedGasFee = BigInt.from(gasFeesModel.estimatedGasFee);
|
||||
@@ -73,28 +86,26 @@ class DEuro {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.save(
|
||||
(amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||
final signedTransaction = await _savings.save(
|
||||
(amount: amount, compound: true),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: amount,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: _savings.self.address.hexEip55,
|
||||
receivingAddressHex: _savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[17].encodeCall([amount, hexToBytes(frontendCode)]),
|
||||
data: _savings.self.abi.functions[18].encodeCall([amount, true]),
|
||||
);
|
||||
|
||||
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
feeCurrency: "ETH",
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: amount.toString(),
|
||||
exponent: 18,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: Money(amount, CryptoCurrency.deuro),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
@@ -110,28 +121,65 @@ class DEuro {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.withdraw(
|
||||
final signedTransaction = await _savings.withdraw(
|
||||
(target: _address, amount: amount),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: _savings.self.address.hexEip55,
|
||||
receivingAddressHex: _savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savings.self.abi.functions[23].encodeCall([_address, amount]),
|
||||
);
|
||||
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: Money(amount, CryptoCurrency.deuro),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
throw InsufficientGasFeeException(currentBalance: ethBalance.getInWei);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingEVMChainTransaction> withdrawSavingsV1(EVMChainTransactionPriority priority) async {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
final withdrawAmount = await savingsBalanceV1;
|
||||
|
||||
// Withdraw at least a million to overflow and close the savings position
|
||||
final amount = BigInt.parse("1000000000000000000000000");
|
||||
final savings = _getSavingsGateway(_wallet.getWeb3Client()!);
|
||||
final signedTransaction = await savings.withdraw(
|
||||
(target: _address, amount: amount, frontendCode: hexToBytes(frontendCode)),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: amount,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: savings.self.address.hexEip55,
|
||||
receivingAddressHex: savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[24].encodeCall([_address, amount, hexToBytes(frontendCode)]),
|
||||
data: savings.self.abi.functions[24].encodeCall([_address, amount, hexToBytes(frontendCode)]),
|
||||
);
|
||||
|
||||
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: amount.toString(),
|
||||
feeCurrency: "ETH",
|
||||
exponent: 18);
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: withdrawAmount,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
final ethBalance = await _wallet.getWeb3Client()!.getBalance(_address);
|
||||
@@ -145,27 +193,25 @@ class DEuro {
|
||||
try {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
final signedTransaction = await _savingsGateway.refreshBalance((owner: _address),
|
||||
final signedTransaction = await _savings.refreshBalance((owner: _address),
|
||||
credentials: _wallet.evmChainPrivateKey,
|
||||
);
|
||||
|
||||
final fee = await _wallet.calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: BigInt.zero,
|
||||
contractAddress: _savingsGateway.self.address.hexEip55,
|
||||
receivingAddressHex: _savingsGateway.self.address.hexEip55,
|
||||
amount: Money.zero(_wallet.currency),
|
||||
contractAddress: _savings.self.address.hexEip55,
|
||||
receivingAddressHex: _savings.self.address.hexEip55,
|
||||
priority: priority,
|
||||
data: _savingsGateway.self.abi.functions[15].encodeCall([_address]),
|
||||
data: _savings.self.abi.functions[16].encodeCall([_address]),
|
||||
);
|
||||
|
||||
final sendTransaction = () => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
sendTransaction() => _wallet.getWeb3Client()!.sendRawTransaction(signedTransaction);
|
||||
|
||||
return PendingEVMChainTransaction(
|
||||
feeCurrency: "ETH",
|
||||
sendTransaction: sendTransaction,
|
||||
signedTransaction: signedTransaction,
|
||||
fee: BigInt.from(fee.estimatedGasFee),
|
||||
amount: 0.toString(),
|
||||
exponent: 18,
|
||||
fee: Money(BigInt.from(fee.estimatedGasFee), CryptoCurrency.eth),
|
||||
amount: Money.zero(CryptoCurrency.deuro),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
@@ -182,14 +228,12 @@ class DEuro {
|
||||
await _checkEthBalanceForGasFees(priority);
|
||||
|
||||
return (await _wallet.createApprovalTransaction(
|
||||
BigInt.parse(
|
||||
Money(BigInt.parse(
|
||||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
radix: 16,
|
||||
),
|
||||
_savingsGateway.self.address.hexEip55,
|
||||
CryptoCurrency.deuro,
|
||||
), CryptoCurrency.deuro),
|
||||
_savings.self.address.hexEip55,
|
||||
priority,
|
||||
"ETH",
|
||||
)) as PendingEVMChainTransaction;
|
||||
} catch (e) {
|
||||
if (e.toString().contains('insufficient funds for gas')) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,18 +1,19 @@
|
||||
// ignore_for_file: overridden_fields, annotate_overrides
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/format_fixed.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:cw_evm/evm_chain_registry.dart';
|
||||
import 'package:cw_evm/utils/evm_chain_utils.dart';
|
||||
|
||||
class EVMChainTransactionInfo extends TransactionInfo {
|
||||
EVMChainTransactionInfo({
|
||||
required this.id,
|
||||
required this.height,
|
||||
required this.ethAmount,
|
||||
required this.ethFee,
|
||||
required this.amount,
|
||||
required this.fee,
|
||||
required this.tokenSymbol,
|
||||
this.exponent = 18,
|
||||
required this.direction,
|
||||
@@ -24,19 +25,16 @@ class EVMChainTransactionInfo extends TransactionInfo {
|
||||
this.evmSignatureName,
|
||||
this.contractAddress,
|
||||
required this.chainId,
|
||||
}) : amount = ethAmount.toInt(),
|
||||
fee = ethFee.toInt();
|
||||
});
|
||||
|
||||
final String id;
|
||||
final int height;
|
||||
final int amount;
|
||||
final BigInt ethAmount;
|
||||
final Money amount;
|
||||
final int exponent;
|
||||
final TransactionDirection direction;
|
||||
final DateTime date;
|
||||
final bool isPending;
|
||||
final int fee;
|
||||
final BigInt ethFee;
|
||||
final Money fee;
|
||||
final int confirmations;
|
||||
final String tokenSymbol;
|
||||
String? _fiatAmount;
|
||||
@@ -49,39 +47,32 @@ class EVMChainTransactionInfo extends TransactionInfo {
|
||||
/// Get fee currency symbol based on wallet type
|
||||
String get feeCurrency => EVMChainUtils.getFeeCurrency(chainId);
|
||||
|
||||
String get _displayTokenSymbol {
|
||||
if (chainId == 56 && tokenSymbol.toUpperCase() == 'BSC-USD') return 'USDT';
|
||||
|
||||
return tokenSymbol;
|
||||
}
|
||||
|
||||
@override
|
||||
String amountFormatted() {
|
||||
final amount = formatAmount((ethAmount / BigInt.from(10).pow(exponent)).toString());
|
||||
return '${amount.substring(0, min(10, amount.length))} $_displayTokenSymbol';
|
||||
}
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
@override
|
||||
String feeFormatted() => '${formatFixed(ethFee, 18)} $feeCurrency';
|
||||
|
||||
factory EVMChainTransactionInfo.fromJson(Map<String, dynamic> data, int chainId) {
|
||||
final decimals = data['exponent'] as int? ?? 18;
|
||||
final tokenSymbol = data['tokenSymbol'] as String;
|
||||
final currency =
|
||||
Erc20Token(name: '', symbol: tokenSymbol, contractAddress: '', decimal: decimals);
|
||||
|
||||
final feeCurrency =
|
||||
EvmChainRegistry().getChainConfig(chainId)?.nativeCurrency ?? CryptoCurrency.eth;
|
||||
|
||||
return EVMChainTransactionInfo(
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int,
|
||||
ethAmount: BigInt.parse(data['amount'] as String),
|
||||
exponent: data['exponent'] as int? ?? 18,
|
||||
ethFee: BigInt.parse(data['fee'] as String),
|
||||
amount: Money(BigInt.parse(data['amount'] as String), currency),
|
||||
exponent: decimals,
|
||||
fee: Money(BigInt.parse(data['fee'] as String), feeCurrency),
|
||||
direction: TransactionDirection.values[data['direction'] as int],
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool? ?? false,
|
||||
confirmations: data['confirmations'] as int,
|
||||
tokenSymbol: data['tokenSymbol'] as String,
|
||||
tokenSymbol: tokenSymbol,
|
||||
to: data['to'] as String?,
|
||||
from: data['from'] as String?,
|
||||
evmSignatureName: data['evmSignatureName'] as String?,
|
||||
@@ -93,9 +84,9 @@ class EVMChainTransactionInfo extends TransactionInfo {
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'height': height,
|
||||
'amount': ethAmount.toString(),
|
||||
'amount': amount.amount.toString(),
|
||||
'exponent': exponent,
|
||||
'fee': ethFee.toString(),
|
||||
'fee': fee.amount.toString(),
|
||||
'direction': direction.index,
|
||||
'date': date.millisecondsSinceEpoch,
|
||||
'isPending': isPending,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:bip32/bip32.dart' as bip32;
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
@@ -28,7 +29,6 @@ import 'package:cw_evm/evm_chain_default_tokens.dart';
|
||||
import 'package:cw_evm/evm_chain_exceptions.dart';
|
||||
import 'package:cw_evm/evm_chain_registry.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_credentials.dart';
|
||||
import 'package:cw_evm/utils/evm_chain_formatter.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_history.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_model.dart';
|
||||
import 'package:cw_evm/evm_chain_transaction_priority.dart';
|
||||
@@ -103,7 +103,7 @@ abstract class EVMChainWalletBase
|
||||
walletInfo, initialChainId ?? _getInitialChainId(walletInfo.type)),
|
||||
balance = ObservableMap<CryptoCurrency, EVMChainERC20Balance>.of(
|
||||
{
|
||||
nativeCurrency: initialBalance ?? EVMChainERC20Balance(BigInt.zero),
|
||||
nativeCurrency: initialBalance ?? EVMChainERC20Balance(Money.zero(nativeCurrency)),
|
||||
},
|
||||
),
|
||||
super(walletInfo, derivationInfo) {
|
||||
@@ -411,20 +411,29 @@ abstract class EVMChainWalletBase
|
||||
EVMChainTransactionModel transactionModel,
|
||||
String address,
|
||||
) {
|
||||
final decimals = transactionModel.tokenDecimal ?? 18;
|
||||
final tokenSymbol = transactionModel.tokenSymbol ??
|
||||
EVMChainUtils.getDefaultTokenSymbol(transactionModel.chainId);
|
||||
|
||||
final amountCurrency = Erc20Token(
|
||||
name: '',
|
||||
contractAddress: transactionModel.contractAddress,
|
||||
decimal: decimals,
|
||||
symbol: tokenSymbol,
|
||||
);
|
||||
return EVMChainTransactionInfo(
|
||||
id: transactionModel.hash,
|
||||
height: transactionModel.blockNumber,
|
||||
ethAmount: transactionModel.amount,
|
||||
amount: Money(transactionModel.amount, amountCurrency),
|
||||
direction: transactionModel.from == address
|
||||
? TransactionDirection.outgoing
|
||||
: TransactionDirection.incoming,
|
||||
isPending: false,
|
||||
date: transactionModel.date,
|
||||
confirmations: transactionModel.confirmations,
|
||||
ethFee: BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice,
|
||||
fee: Money(BigInt.from(transactionModel.gasUsed) * transactionModel.gasPrice, currency),
|
||||
exponent: transactionModel.tokenDecimal ?? 18,
|
||||
tokenSymbol: transactionModel.tokenSymbol ??
|
||||
EVMChainUtils.getDefaultTokenSymbol(transactionModel.chainId),
|
||||
tokenSymbol: tokenSymbol,
|
||||
to: transactionModel.to,
|
||||
from: transactionModel.from,
|
||||
evmSignatureName: transactionModel.evmSignatureName,
|
||||
@@ -473,11 +482,10 @@ abstract class EVMChainWalletBase
|
||||
@override
|
||||
Future<bool> checkNodeHealth() async {
|
||||
try {
|
||||
// Check native balance
|
||||
await _client.getBalance(_evmChainPrivateKey.address);
|
||||
|
||||
// Check USDC token balance
|
||||
String usdcContractAddress = _getUSDCContractAddress();
|
||||
final usdcContractAddress = Erc20Token(
|
||||
name: "USDC", symbol: "USDC", contractAddress: _getUSDCContractAddress(), decimal: 6);
|
||||
|
||||
await _client.fetchERC20Balances(_evmChainPrivateKey.address, usdcContractAddress);
|
||||
|
||||
@@ -531,7 +539,7 @@ abstract class EVMChainWalletBase
|
||||
|
||||
// Ensure balance is initialized for current currency (in case currency changed)
|
||||
if (!balance.containsKey(currency)) {
|
||||
balance[currency] = EVMChainERC20Balance(BigInt.zero);
|
||||
balance[currency] = EVMChainERC20Balance(Money.zero(currency));
|
||||
}
|
||||
|
||||
await save();
|
||||
@@ -757,7 +765,7 @@ abstract class EVMChainWalletBase
|
||||
/// - The addressHex for the receiving wallet,
|
||||
/// - A contract address which would be essential in determining if to calculate the estimate for ERC20 or native ETH
|
||||
Future<GasParamsHandler> calculateActualEstimatedFeeForCreateTransaction({
|
||||
required amount,
|
||||
required Money amount,
|
||||
required String? contractAddress,
|
||||
required String receivingAddressHex,
|
||||
required TransactionPriority? priority,
|
||||
@@ -774,7 +782,6 @@ abstract class EVMChainWalletBase
|
||||
final gasBaseFee = await _client.getGasBaseFee();
|
||||
final gasPrice = await _client.getGasUnitPrice();
|
||||
|
||||
// Validate that we got valid gas price
|
||||
if (gasPrice <= 0) {
|
||||
printV('Invalid gas price received: $gasPrice');
|
||||
throw EVMChainTransactionFeesException('Failed to retrieve gas price from node');
|
||||
@@ -791,7 +798,7 @@ abstract class EVMChainWalletBase
|
||||
final estimatedGas = await _client.getEstimatedGasUnitsForTransaction(
|
||||
contractAddress: contractAddress,
|
||||
senderAddress: _evmChainPrivateKey.address,
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, amount!),
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, amount.amount),
|
||||
gasPrice: EtherAmount.fromInt(EtherUnit.wei, adjustedGasPrice),
|
||||
toAddress: EthereumAddress.fromHex(receivingAddressHex),
|
||||
maxFeePerGas: EtherAmount.fromInt(EtherUnit.wei, maxFeePerGas),
|
||||
@@ -932,15 +939,15 @@ abstract class EVMChainWalletBase
|
||||
'Currency ${_credentials.currency.title} ${_credentials.currency.tag} is not accessible in the wallet, try to enable it first.'));
|
||||
|
||||
final currencyBalance = balance[transactionCurrency]!;
|
||||
BigInt totalAmount = BigInt.zero;
|
||||
BigInt estimatedFeesForTransaction = BigInt.zero;
|
||||
int exponent = transactionCurrency is Erc20Token ? transactionCurrency.decimal : 18;
|
||||
String? contractAddress;
|
||||
int estimatedGasUnitsForTransaction = 0;
|
||||
int maxFeePerGasForTransaction = 0;
|
||||
String toAddress = _credentials.outputs.first.isParsedAddress
|
||||
final toAddress = _credentials.outputs.first.isParsedAddress
|
||||
? _credentials.outputs.first.extractedAddress!
|
||||
: _credentials.outputs.first.address;
|
||||
var totalAmount = Money.zero(transactionCurrency);
|
||||
var estimatedFeesForTransaction = Money.zero(currency);
|
||||
var estimatedGasUnitsForTransaction = 0;
|
||||
var maxFeePerGasForTransaction = 0;
|
||||
|
||||
String? contractAddress;
|
||||
|
||||
if (transactionCurrency is Erc20Token) {
|
||||
contractAddress = transactionCurrency.contractAddress;
|
||||
@@ -948,23 +955,12 @@ abstract class EVMChainWalletBase
|
||||
|
||||
// so far this can not be made with Ethereum as Ethereum does not support multiple recipients
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
if (outputs.any((item) => item.sendAll || item.cryptoAmount.amount <= BigInt.zero)) {
|
||||
throw EVMChainTransactionCreationException(transactionCurrency);
|
||||
}
|
||||
|
||||
totalAmount = outputs.fold<BigInt>(
|
||||
BigInt.zero,
|
||||
(acc, output) {
|
||||
if (output.cryptoAmount != null && output.cryptoAmount!.isNotEmpty) {
|
||||
return acc +
|
||||
EVMChainFormatter.parseEVMChainAmountToBigInt(
|
||||
output.cryptoAmount!,
|
||||
decimals: exponent,
|
||||
);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
);
|
||||
totalAmount = outputs.fold<Money>(Money.zero(transactionCurrency),
|
||||
(acc, output) => acc + output.cryptoAmount.copyWith(currency: transactionCurrency));
|
||||
|
||||
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: totalAmount,
|
||||
@@ -973,28 +969,22 @@ abstract class EVMChainWalletBase
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
|
||||
estimatedFeesForTransaction = BigInt.from(gasFeesModel.estimatedGasFee);
|
||||
estimatedFeesForTransaction =
|
||||
estimatedFeesForTransaction.copyWith(amount: BigInt.from(gasFeesModel.estimatedGasFee));
|
||||
estimatedGasUnitsForTransaction = gasFeesModel.estimatedGasUnits;
|
||||
maxFeePerGasForTransaction = gasFeesModel.maxFeePerGas;
|
||||
|
||||
if (currencyBalance.balance < totalAmount) {
|
||||
if (currencyBalance.available < totalAmount) {
|
||||
throw EVMChainTransactionCreationException(transactionCurrency);
|
||||
}
|
||||
} else {
|
||||
final output = outputs.first;
|
||||
if (!output.sendAll) {
|
||||
if (output.cryptoAmount != null && output.cryptoAmount!.isNotEmpty) {
|
||||
totalAmount = EVMChainFormatter.parseEVMChainAmountToBigInt(
|
||||
output.cryptoAmount!,
|
||||
decimals: exponent,
|
||||
);
|
||||
} else {
|
||||
totalAmount = BigInt.zero;
|
||||
}
|
||||
totalAmount = output.cryptoAmount.copyWith(currency: transactionCurrency);
|
||||
}
|
||||
|
||||
if (output.sendAll && transactionCurrency is Erc20Token) {
|
||||
totalAmount = currencyBalance.balance;
|
||||
totalAmount = currencyBalance.available;
|
||||
}
|
||||
|
||||
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
@@ -1004,7 +994,8 @@ abstract class EVMChainWalletBase
|
||||
contractAddress: contractAddress,
|
||||
);
|
||||
|
||||
estimatedFeesForTransaction = BigInt.from(gasFeesModel.estimatedGasFee);
|
||||
estimatedFeesForTransaction =
|
||||
estimatedFeesForTransaction.copyWith(amount: BigInt.from(gasFeesModel.estimatedGasFee));
|
||||
estimatedGasUnitsForTransaction = gasFeesModel.estimatedGasUnits;
|
||||
maxFeePerGasForTransaction = gasFeesModel.maxFeePerGas;
|
||||
|
||||
@@ -1012,30 +1003,30 @@ abstract class EVMChainWalletBase
|
||||
if (selectedChainId == 8453) {
|
||||
// Applying a small buffer to account for gas price fluctuations
|
||||
// 10% or minimum 10,000 wei, whichever is higher
|
||||
final refinedGasFee = estimatedFeesForTransaction;
|
||||
final refinedGasFee = estimatedFeesForTransaction.amount;
|
||||
final gasBufferPercent = refinedGasFee * BigInt.from(110) ~/ BigInt.from(100);
|
||||
final gasBufferMin = refinedGasFee + BigInt.from(10000);
|
||||
final gasBuffer = gasBufferPercent > gasBufferMin ? gasBufferPercent : gasBufferMin;
|
||||
|
||||
// Using the buffered fee for the final amount
|
||||
totalAmount = (currencyBalance.balance - gasBuffer);
|
||||
estimatedFeesForTransaction = gasBuffer;
|
||||
totalAmount = totalAmount.copyWith(amount: currencyBalance.available.amount - gasBuffer);
|
||||
estimatedFeesForTransaction = estimatedFeesForTransaction.copyWith(amount: gasBuffer);
|
||||
} else {
|
||||
// Calculating the final amount with the estimated gas fee
|
||||
totalAmount = (currencyBalance.balance - estimatedFeesForTransaction);
|
||||
totalAmount = currencyBalance.available - estimatedFeesForTransaction;
|
||||
}
|
||||
}
|
||||
|
||||
// check the fees on the base currency
|
||||
if (estimatedFeesForTransaction > balance[currency]!.balance) {
|
||||
if (estimatedFeesForTransaction > balance[currency]!.available) {
|
||||
throw EVMChainTransactionFeesException.fromCurrency(currency.title);
|
||||
}
|
||||
|
||||
if (currencyBalance.balance < totalAmount) {
|
||||
if (currencyBalance.available < totalAmount) {
|
||||
throw EVMChainTransactionCreationException(transactionCurrency);
|
||||
}
|
||||
if (transactionCurrency is! Erc20Token &&
|
||||
totalAmount + estimatedFeesForTransaction > currencyBalance.balance) {
|
||||
totalAmount + estimatedFeesForTransaction > currencyBalance.available) {
|
||||
throw EVMChainTransactionFeesException.fromCurrency(currency.title);
|
||||
}
|
||||
}
|
||||
@@ -1056,7 +1047,6 @@ abstract class EVMChainWalletBase
|
||||
currency: transactionCurrency,
|
||||
feeCurrency: EVMChainUtils.getFeeCurrency(selectedChainId),
|
||||
maxFeePerGas: maxFeePerGasForTransaction,
|
||||
exponent: exponent,
|
||||
contractAddress:
|
||||
transactionCurrency is Erc20Token ? transactionCurrency.contractAddress : null,
|
||||
data: hexOpReturnMemo,
|
||||
@@ -1070,7 +1060,7 @@ abstract class EVMChainWalletBase
|
||||
Future<PendingTransaction> createCallDataTransaction(
|
||||
String to,
|
||||
String dataHex,
|
||||
BigInt valueWei,
|
||||
Money valueWei,
|
||||
EVMChainTransactionPriority? priority,
|
||||
String? sourceTokenAddress,
|
||||
BigInt? sourceTokenAmount, {
|
||||
@@ -1101,37 +1091,36 @@ abstract class EVMChainWalletBase
|
||||
gas = GasParamsHandler.zero();
|
||||
}
|
||||
|
||||
// Validate NATIVE Balance (for Gas + Value)
|
||||
final nativeBal = balance[nativeCurrency]?.balance ?? BigInt.zero;
|
||||
final requiredNative = valueWei + BigInt.from(gas.estimatedGasFee);
|
||||
final nativeBal = balance[nativeCurrency]?.available ?? Money.zero(nativeCurrency);
|
||||
var requiredNative = Money.fromInt(gas.estimatedGasFee, nativeCurrency);
|
||||
|
||||
if (valueWei.currency == nativeCurrency) {
|
||||
requiredNative += valueWei;
|
||||
}
|
||||
|
||||
if (requiredNative > nativeBal) {
|
||||
throw Exception('Not enough ${nativeCurrency.title} to cover value and fees.');
|
||||
}
|
||||
|
||||
// Validate ERC20 Balance (Only if source is NOT native)
|
||||
final cleanAddress = sourceTokenAddress?.toLowerCase() ?? '';
|
||||
|
||||
// Check for both 0xeeee... AND 0x0000... (Zero Address)
|
||||
bool isNativeSource = sourceTokenAddress == null ||
|
||||
final isNativeSource = sourceTokenAddress == null ||
|
||||
cleanAddress == '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ||
|
||||
cleanAddress == '0x0000000000000000000000000000000000000000';
|
||||
|
||||
if (!isNativeSource && sourceTokenAmount != null && sourceTokenAmount > BigInt.zero) {
|
||||
// Filter list to find match.
|
||||
final matchingTokens = balance.keys
|
||||
.where((k) => k is Erc20Token && k.contractAddress.toLowerCase() == cleanAddress);
|
||||
|
||||
if (matchingTokens.isEmpty) {
|
||||
// Token is not in the wallet balance map -> Balance is 0
|
||||
throw Exception('Insufficient token balance (Token not found in wallet).');
|
||||
}
|
||||
|
||||
final tokenKey = matchingTokens.first;
|
||||
final tokenBalance = balance[tokenKey]?.balance ?? BigInt.zero;
|
||||
final tokenBalance = balance[tokenKey]?.available ?? Money.zero(tokenKey);
|
||||
|
||||
if (tokenBalance < sourceTokenAmount) {
|
||||
throw Exception('Insufficient ${tokenKey.title} balance to cover the transaction amount.');
|
||||
if (tokenBalance < Money(sourceTokenAmount, tokenKey)) {
|
||||
throw Exception('Insufficient ${tokenKey.symbol} balance to cover the transaction amount.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1144,13 +1133,12 @@ abstract class EVMChainWalletBase
|
||||
privateKey: _evmChainPrivateKey,
|
||||
toAddress: to,
|
||||
amount: valueWei,
|
||||
gasFee: BigInt.from(gas.estimatedGasFee),
|
||||
gasFee: Money.fromInt(gas.estimatedGasFee, currency),
|
||||
estimatedGasUnits: gasUnits,
|
||||
maxFeePerGas: gas.maxFeePerGas,
|
||||
priority: priority,
|
||||
currency: nativeCurrency,
|
||||
feeCurrency: nativeCurrency.title,
|
||||
exponent: 18,
|
||||
contractAddress: null,
|
||||
data: dataHex,
|
||||
gasPrice: gas.gasPrice,
|
||||
@@ -1161,23 +1149,23 @@ abstract class EVMChainWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
Future<PendingTransaction> createApprovalTransaction(BigInt amount, String spender,
|
||||
CryptoCurrency token, EVMChainTransactionPriority? priority, String feeCurrency,
|
||||
Future<PendingTransaction> createApprovalTransaction(
|
||||
Money amount, String spender, EVMChainTransactionPriority? priority,
|
||||
{bool useBlinkProtection = true}) async {
|
||||
final CryptoCurrency transactionCurrency =
|
||||
balance.keys.firstWhere((element) => element.title == token.title);
|
||||
final transactionCurrency =
|
||||
balance.keys.firstWhere((element) => element.symbol == amount.currency.symbol);
|
||||
assert(transactionCurrency is Erc20Token);
|
||||
|
||||
final data = _client.getEncodedDataForApprovalTransaction(
|
||||
contractAddress: EthereumAddress.fromHex((transactionCurrency as Erc20Token).contractAddress),
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
|
||||
value: EtherAmount.fromBigInt(EtherUnit.wei, amount.amount),
|
||||
toAddress: EthereumAddress.fromHex(spender),
|
||||
);
|
||||
|
||||
final tokenContract = (transactionCurrency as Erc20Token).contractAddress;
|
||||
final tokenContract = transactionCurrency.contractAddress;
|
||||
|
||||
final gasFeesModel = await calculateActualEstimatedFeeForCreateTransaction(
|
||||
amount: BigInt.zero,
|
||||
amount: Money.zero(currency),
|
||||
receivingAddressHex: tokenContract,
|
||||
priority: priority,
|
||||
contractAddress: tokenContract,
|
||||
@@ -1193,11 +1181,9 @@ abstract class EVMChainWalletBase
|
||||
spender: spender,
|
||||
amount: amount,
|
||||
priority: priority,
|
||||
gasFee: BigInt.from(gasFeesModel.estimatedGasFee),
|
||||
gasFee: Money.fromInt(gasFeesModel.estimatedGasFee, currency),
|
||||
maxFeePerGas: gasFeesModel.maxFeePerGas,
|
||||
feeCurrency: feeCurrency,
|
||||
estimatedGasUnits: safeGasUnits,
|
||||
exponent: transactionCurrency.decimal,
|
||||
contractAddress: tokenContract,
|
||||
gasPrice: gasFeesModel.gasPrice,
|
||||
useBlinkProtection: useBlinkProtection,
|
||||
@@ -1354,7 +1340,8 @@ abstract class EVMChainWalletBase
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': _mnemonic,
|
||||
'private_key': privateKey,
|
||||
'balance': balance[currency]?.toJSON() ?? EVMChainERC20Balance(BigInt.zero).toJSON(),
|
||||
'balance':
|
||||
balance[currency]?.toJSON() ?? EVMChainERC20Balance(Money.zero(currency)).toJSON(),
|
||||
'passphrase': passphrase,
|
||||
'selected_chain_id': selectedChainId,
|
||||
});
|
||||
@@ -1370,9 +1357,9 @@ abstract class EVMChainWalletBase
|
||||
try {
|
||||
final balance = await _client.getBalance(_evmChainPrivateKey.address);
|
||||
|
||||
return EVMChainERC20Balance(balance.getInWei);
|
||||
return EVMChainERC20Balance(Money(balance.getInWei, currency));
|
||||
} catch (_) {
|
||||
return balance[currency] ?? EVMChainERC20Balance(BigInt.zero);
|
||||
return balance[currency] ?? EVMChainERC20Balance(Money.zero(currency));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1429,10 +1416,7 @@ abstract class EVMChainWalletBase
|
||||
|
||||
try {
|
||||
if (token.enabled) {
|
||||
balance[token] = await _client.fetchERC20Balances(
|
||||
_evmChainPrivateKey.address,
|
||||
token.contractAddress,
|
||||
);
|
||||
balance[token] = await _client.fetchERC20Balances(_evmChainPrivateKey.address, token);
|
||||
} else {
|
||||
balance.remove(token);
|
||||
}
|
||||
@@ -1536,12 +1520,7 @@ abstract class EVMChainWalletBase
|
||||
|
||||
if (newToken.enabled) {
|
||||
try {
|
||||
final erc20Balance = await _client.fetchERC20Balances(
|
||||
_evmChainPrivateKey.address,
|
||||
newToken.contractAddress,
|
||||
);
|
||||
|
||||
balance[newToken] = erc20Balance;
|
||||
balance[newToken] = await _client.fetchERC20Balances(_evmChainPrivateKey.address, newToken);
|
||||
|
||||
await evmChainErc20TokensBox.put(newToken.contractAddress, newToken);
|
||||
} on Exception catch (_) {
|
||||
@@ -1611,9 +1590,6 @@ abstract class EVMChainWalletBase
|
||||
if (!hasKeysFile) rethrow;
|
||||
}
|
||||
|
||||
final balance = EVMChainERC20Balance.fromJSON(data?['balance'] as String?) ??
|
||||
EVMChainERC20Balance(BigInt.zero);
|
||||
|
||||
final WalletKeysData keysData;
|
||||
// Migrate wallet from the old scheme to the new .keys file scheme
|
||||
if (!hasKeysFile) {
|
||||
@@ -1658,6 +1634,10 @@ abstract class EVMChainWalletBase
|
||||
// Use saved chainId if available, otherwise use the computed chainId
|
||||
final initialChainIdForWallet = savedChainId ?? chainId;
|
||||
|
||||
final balance =
|
||||
EVMChainERC20Balance.fromJSON(data?['balance'] as String?, chainConfig.nativeCurrency) ??
|
||||
EVMChainERC20Balance(Money.zero(chainConfig.nativeCurrency));
|
||||
|
||||
return EVMChainWallet(
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: await walletInfo.getDerivationInfo(),
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
import 'dart:convert';
|
||||
import "dart:convert";
|
||||
|
||||
import 'package:cw_core/balance.dart';
|
||||
import "package:cw_core/amount/money.dart";
|
||||
import "package:cw_core/balance.dart";
|
||||
import "package:cw_core/currency.dart";
|
||||
|
||||
class EVMChainERC20Balance extends Balance {
|
||||
EVMChainERC20Balance(this.balance, {this.exponent = 18})
|
||||
: super(balance, balance);
|
||||
EVMChainERC20Balance(Money balance) : super(balance, Money.zero(balance.currency));
|
||||
|
||||
final BigInt balance;
|
||||
final int exponent;
|
||||
String toJSON() => json.encode({ "balanceInWei": available.amount.toString() });
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'balanceInWei': balance.toString(),
|
||||
'exponent': exponent,
|
||||
});
|
||||
|
||||
static EVMChainERC20Balance? fromJSON(String? jsonSource) {
|
||||
if (jsonSource == null) {
|
||||
return null;
|
||||
}
|
||||
static EVMChainERC20Balance? fromJSON(String? jsonSource, Currency currency) {
|
||||
if (jsonSource == null) return null;
|
||||
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
try {
|
||||
return EVMChainERC20Balance(
|
||||
BigInt.parse(decoded['balanceInWei']),
|
||||
exponent: decoded['exponent'],
|
||||
);
|
||||
return EVMChainERC20Balance(Money(BigInt.parse(decoded["balanceInWei"]), currency));
|
||||
} catch (e) {
|
||||
return EVMChainERC20Balance(BigInt.zero);
|
||||
return EVMChainERC20Balance(Money.zero(currency));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:cw_core/format_fixed.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:web3dart/crypto.dart';
|
||||
import 'package:hex/hex.dart' as Hex;
|
||||
@@ -8,36 +8,36 @@ import 'package:hex/hex.dart' as Hex;
|
||||
class PendingEVMChainTransaction with PendingTransaction {
|
||||
final Function sendTransaction;
|
||||
final Uint8List signedTransaction;
|
||||
final BigInt fee;
|
||||
final String feeCurrency;
|
||||
final String amount;
|
||||
final int exponent;
|
||||
final bool isInfiniteApproval;
|
||||
|
||||
PendingEVMChainTransaction({
|
||||
required this.sendTransaction,
|
||||
required this.signedTransaction,
|
||||
required this.fee,
|
||||
required this.feeCurrency,
|
||||
required this.amount,
|
||||
required this.exponent,
|
||||
this.isInfiniteApproval = false,
|
||||
});
|
||||
|
||||
@override
|
||||
String get amountFormatted {
|
||||
if (isInfiniteApproval) return "∞";
|
||||
return formatFixed(BigInt.parse(amount), exponent);
|
||||
return amount.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
final Money amount;
|
||||
|
||||
@override
|
||||
final Money fee;
|
||||
|
||||
@override
|
||||
Future<void> commit() async => await sendTransaction();
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue $feeCurrency";
|
||||
String get feeFormatted => fee.toStringWithSymbol(fractionalDigits: 10);
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => formatFixed(fee, 18, fractionalDigits: 10);
|
||||
String get feeFormattedValue => fee.toStringWithPrecision(fractionalDigits: 10);
|
||||
|
||||
@override
|
||||
String get hex => bytesToHex(signedTransaction, include0x: true);
|
||||
@@ -61,7 +61,5 @@ class PendingEVMChainTransaction with PendingTransaction {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> commitUR() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
Future<Map<String, String>> commitUR() => throw UnimplementedError();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_evm/contract/erc20.dart';
|
||||
@@ -80,11 +81,9 @@ class USDT0Service {
|
||||
);
|
||||
|
||||
final pendingApproval = await wallet.createApprovalTransaction(
|
||||
maxUint,
|
||||
Money(maxUint,token),
|
||||
adapter,
|
||||
token,
|
||||
priority,
|
||||
EVMChainUtils.getFeeCurrency(wallet.selectedChainId),
|
||||
useBlinkProtection: useBlinkProtection,
|
||||
);
|
||||
await pendingApproval.commit();
|
||||
@@ -116,7 +115,7 @@ class USDT0Service {
|
||||
return wallet.createCallDataTransaction(
|
||||
oftAddress,
|
||||
encoded.dataHex,
|
||||
encoded.valueWei,
|
||||
Money(encoded.valueWei, token),
|
||||
priority,
|
||||
token.contractAddress,
|
||||
amount,
|
||||
|
||||
@@ -11,40 +11,6 @@ class EVMChainFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse EVM chain amount to BigInt to avoid integer overflow for large amounts
|
||||
/// [decimals] defaults to 18 for native ETH/POL, but should be set to token decimals for ERC20 tokens
|
||||
static BigInt parseEVMChainAmountToBigInt(String amount, {int decimals = evmDecimals}) {
|
||||
try {
|
||||
String cleanAmount = amount.replaceAll(',', '.');
|
||||
|
||||
bool isNegative = cleanAmount.startsWith('-');
|
||||
if (isNegative) {
|
||||
cleanAmount = cleanAmount.substring(1);
|
||||
}
|
||||
|
||||
final parts = cleanAmount.split('.');
|
||||
String whole = parts[0].isEmpty ? '0' : parts[0];
|
||||
String fraction = parts.length > 1 ? parts[1] : '';
|
||||
|
||||
// 3. Strict Truncation Logic
|
||||
// If fraction is longer than decimals, strictly cut it off.
|
||||
// If shorter, pad it with zeros.
|
||||
if (fraction.length > decimals) {
|
||||
fraction = fraction.substring(0, decimals);
|
||||
} else {
|
||||
fraction = fraction.padRight(decimals, '0');
|
||||
}
|
||||
|
||||
final wholeBigInt = BigInt.parse(whole);
|
||||
final fractionBigInt = BigInt.parse(fraction);
|
||||
final multiplier = BigInt.from(10).pow(decimals);
|
||||
|
||||
return (wholeBigInt * multiplier) + fractionBigInt;
|
||||
} catch (_) {
|
||||
return BigInt.zero;
|
||||
}
|
||||
}
|
||||
|
||||
static String truncateDecimals(String amount, int decimals) {
|
||||
final parts = amount.split(".");
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ dev_dependencies:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.15
|
||||
mobx_codegen: ^2.0.7
|
||||
hive_generator: ^2.0.1
|
||||
# hive_generator: ^2.0.1
|
||||
flutter_lints: ^2.0.0
|
||||
|
||||
flutter:
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'dart:ffi';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_monero/api/account_list.dart';
|
||||
import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
|
||||
@@ -118,9 +120,7 @@ String getSeedLegacy(String? language) {
|
||||
return legacy;
|
||||
}
|
||||
|
||||
String getPassphrase() {
|
||||
return currentWallet?.getCacheAttribute(key: "cakewallet.passphrase") ?? "";
|
||||
}
|
||||
String getPassphrase() => currentWallet?.getCacheAttribute(key: "cakewallet.passphrase") ?? "";
|
||||
|
||||
Map<int, Map<int, Map<int, String>>> addressCache = {};
|
||||
|
||||
@@ -146,12 +146,11 @@ String getAddress({int accountIndex = 0, int addressIndex = 0}) {
|
||||
return addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex]!;
|
||||
}
|
||||
|
||||
int getFullBalance({int accountIndex = 0}) {
|
||||
return currentWallet?.balance(accountIndex: accountIndex) ?? 0;
|
||||
}
|
||||
Money getFullBalance({int accountIndex = 0}) =>
|
||||
Money.fromInt(currentWallet?.balance(accountIndex: accountIndex) ?? 0, CryptoCurrency.xmr);
|
||||
|
||||
int getUnlockedBalance({int accountIndex = 0}) =>
|
||||
currentWallet?.unlockedBalance(accountIndex: accountIndex) ?? 0;
|
||||
Money getUnlockedBalance({int accountIndex = 0}) => Money.fromInt(
|
||||
currentWallet?.unlockedBalance(accountIndex: accountIndex) ?? 0, CryptoCurrency.xmr);
|
||||
|
||||
int getCurrentHeight() => currentWallet?.blockChainHeight() ?? 0;
|
||||
|
||||
@@ -417,13 +416,11 @@ Future setTrustedDaemon(bool trusted) async =>
|
||||
|
||||
Future<bool> trustedDaemon() async => currentWallet!.trustedDaemon();
|
||||
|
||||
String signMessage(String message, {String address = ""}) {
|
||||
return currentWallet!.signMessage(message: message, address: address);
|
||||
}
|
||||
String signMessage(String message, {String address = ""}) =>
|
||||
currentWallet!.signMessage(message: message, address: address);
|
||||
|
||||
bool verifyMessage(String message, String address, String signature) {
|
||||
return currentWallet!.verifySignedMessage(message: message, address: address, signature: signature);
|
||||
}
|
||||
bool verifyMessage(String message, String address, String signature) =>
|
||||
currentWallet!.verifySignedMessage(message: message, address: address, signature: signature);
|
||||
|
||||
Map<String, List<int>> debugCallLength() => monero.debugCallLength;
|
||||
|
||||
@@ -438,4 +435,4 @@ Map<String, dynamic> getWalletCacheDebug() {
|
||||
"error": e.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class MoneroBalance extends Balance {
|
||||
MoneroBalance({
|
||||
required this.fullBalance,
|
||||
required Money unlockedBalance,
|
||||
Money? frozen,
|
||||
}) : super(
|
||||
unlockedBalance,
|
||||
fullBalance - unlockedBalance,
|
||||
frozen: frozen ?? Money.zero(CryptoCurrency.xmr),
|
||||
);
|
||||
|
||||
final Money fullBalance;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
|
||||
@@ -15,23 +14,10 @@ class MoneroTransactionInfo extends TransactionInfo {
|
||||
final DateTime date;
|
||||
final int accountIndex;
|
||||
final bool isPending;
|
||||
final int amount;
|
||||
final int fee;
|
||||
final Money amount;
|
||||
final Money fee;
|
||||
final int addressIndex;
|
||||
final int confirmations;
|
||||
String? recipientAddress;
|
||||
String? key;
|
||||
String? _fiatAmount;
|
||||
|
||||
@override
|
||||
String amountFormatted() => '${CryptoCurrency.xmr.formatAmount(BigInt.from(amount))} XMR';
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
@override
|
||||
String feeFormatted() => '${CryptoCurrency.xmr.formatAmount(BigInt.from(fee))} XMR';
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/pathForWallet.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
import 'package:cw_core/account.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/monero_balance.dart';
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:cw_core/monero_wallet_keys.dart';
|
||||
import 'package:cw_core/monero_wallet_utils.dart';
|
||||
@@ -30,6 +29,7 @@ import 'package:cw_monero/api/wallet.dart' as monero_wallet;
|
||||
import 'package:cw_monero/api/wallet_manager.dart';
|
||||
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
|
||||
import 'package:cw_monero/ledger.dart';
|
||||
import 'package:cw_monero/monero_balance.dart';
|
||||
import 'package:cw_monero/monero_transaction_creation_credentials.dart';
|
||||
import 'package:cw_monero/monero_transaction_history.dart';
|
||||
import 'package:cw_monero/monero_transaction_info.dart';
|
||||
@@ -375,15 +375,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
};
|
||||
}
|
||||
|
||||
bool needExportOutputs(int amount) {
|
||||
bool needExportOutputs(Money amount) {
|
||||
if (int.tryParse(currentWallet!.secretSpendKey()) != 0) {
|
||||
return false;
|
||||
}
|
||||
// viewOnlyBalance - balance that we can spend
|
||||
// TODO(mrcyjanek): remove hasUnknownKeyImages when we cleanup coin control
|
||||
return (currentWallet!.viewOnlyBalance(
|
||||
accountIndex: walletAddresses.account!.id) < amount) ||
|
||||
currentWallet!.hasUnknownKeyImages();
|
||||
return (currentWallet!.viewOnlyBalance(accountIndex: walletAddresses.account!.id) <
|
||||
amount.amount.toInt()) ||
|
||||
currentWallet!.hasUnknownKeyImages();
|
||||
}
|
||||
|
||||
MoneroTrezorService? trezorService;
|
||||
@@ -444,30 +444,22 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
}
|
||||
|
||||
if (hasMultiDestination) {
|
||||
if (outputs.any(
|
||||
(item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
|
||||
throw MoneroTransactionCreationException(
|
||||
'You do not have enough XMR to send this amount.');
|
||||
if (outputs.any((item) => item.sendAll || item.cryptoAmount.amount <= BigInt.zero)) {
|
||||
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
|
||||
}
|
||||
|
||||
final int totalAmount = outputs.fold(
|
||||
0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
|
||||
final totalAmount = outputs.fold(0, (acc, value) => acc + value.cryptoAmount.amount.toInt());
|
||||
|
||||
if (unlockedBalance < totalAmount) {
|
||||
throw MoneroTransactionCreationException(
|
||||
'You do not have enough XMR to send this amount.');
|
||||
if (unlockedBalance < Money.fromInt(totalAmount, CryptoCurrency.xmr)) {
|
||||
throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
|
||||
}
|
||||
|
||||
if (inputs.isEmpty) MoneroTransactionCreationException(
|
||||
'No inputs selected');
|
||||
if (inputs.isEmpty) MoneroTransactionCreationException('No inputs selected');
|
||||
|
||||
final moneroOutputs = outputs.map((output) {
|
||||
final outputAddress =
|
||||
output.isParsedAddress ? output.extractedAddress : output.address;
|
||||
final outputAddress = output.isParsedAddress ? output.extractedAddress : output.address;
|
||||
|
||||
return MoneroOutput(
|
||||
address: outputAddress!,
|
||||
amount: output.cryptoAmount!.replaceAll(',', '.'));
|
||||
return MoneroOutput(address: outputAddress!, amount: output.cryptoAmount.toString());
|
||||
}).toList();
|
||||
|
||||
pendingTransactionDescription =
|
||||
@@ -481,8 +473,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
final output = outputs.first;
|
||||
final address =
|
||||
output.isParsedAddress ? output.extractedAddress : output.address;
|
||||
final amount =
|
||||
output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
|
||||
final amount = output.sendAll ? null : output.cryptoAmount.toString();
|
||||
|
||||
// if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
|
||||
// (formattedAmount == null && unlockedBalance <= 0)) {
|
||||
@@ -492,8 +483,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
// 'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.');
|
||||
// }
|
||||
|
||||
if (inputs.isEmpty) MoneroTransactionCreationException(
|
||||
'No inputs selected');
|
||||
if (inputs.isEmpty) MoneroTransactionCreationException('No inputs selected');
|
||||
pendingTransactionDescription =
|
||||
await transaction_history.createTransactionSync(
|
||||
address: address!,
|
||||
@@ -835,10 +825,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
: TransactionDirection.incoming,
|
||||
row.timeStamp,
|
||||
row.isPending,
|
||||
row.amount,
|
||||
Money.fromInt(row.amount, currency),
|
||||
row.accountIndex,
|
||||
0,
|
||||
row.fee,
|
||||
Money.fromInt(row.fee,currency),
|
||||
row.confirmations,
|
||||
)..additionalInfo = <String, dynamic>{
|
||||
'key': row.key,
|
||||
@@ -893,8 +883,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
}
|
||||
|
||||
int _getHeightDistance(DateTime date) {
|
||||
final distance =
|
||||
DateTime.now().difference(date).inSeconds;
|
||||
final distance = DateTime.now().difference(date).inSeconds;
|
||||
final daysTmp = (distance / 86400).round();
|
||||
final days = daysTmp < 1 ? 1 : daysTmp;
|
||||
|
||||
@@ -915,31 +904,28 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
|
||||
void _askForUpdateBalance() {
|
||||
final unlockedBalance = _getUnlockedBalance();
|
||||
final fullBalance = monero_wallet.getFullBalance(
|
||||
accountIndex: walletAddresses.account!.id);
|
||||
final fullBalance = monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id);
|
||||
final frozenBalance = _getFrozenBalance();
|
||||
if (balance[currency]!.fullBalance != fullBalance ||
|
||||
balance[currency]!.unlockedBalance != unlockedBalance ||
|
||||
balance[currency]!.frozenBalance != frozenBalance) {
|
||||
balance[currency]!.available != unlockedBalance ||
|
||||
balance[currency]!.frozen != frozenBalance) {
|
||||
balance[currency] = MoneroBalance(
|
||||
fullBalance: fullBalance,
|
||||
unlockedBalance: unlockedBalance,
|
||||
frozenBalance: frozenBalance);
|
||||
fullBalance: fullBalance, unlockedBalance: unlockedBalance, frozen: frozenBalance);
|
||||
}
|
||||
}
|
||||
|
||||
int _getUnlockedBalance() => monero_wallet.getUnlockedBalance(
|
||||
accountIndex: walletAddresses.account!.id);
|
||||
Money _getUnlockedBalance() =>
|
||||
monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
|
||||
|
||||
int _getFrozenBalance() {
|
||||
Money _getFrozenBalance() {
|
||||
var frozenBalance = 0;
|
||||
|
||||
for (var coin in unspentCoinsInfo.values.where((element) =>
|
||||
for (final coin in unspentCoinsInfo.values.where((element) =>
|
||||
element.walletId == id && element.accountIndex == walletAddresses.account!.id)) {
|
||||
if (coin.isFrozen && !coin.isSending) frozenBalance += coin.value;
|
||||
}
|
||||
|
||||
return frozenBalance;
|
||||
return Money.fromInt(frozenBalance, CryptoCurrency.xmr);
|
||||
}
|
||||
|
||||
void _onNewBlock(int height, int blocksLeft, double ptc) async {
|
||||
@@ -1013,9 +999,4 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||
Future<void> setLedgerConnection(LedgerConnection connection) async {
|
||||
await enableLedgerExchange(connection);
|
||||
}
|
||||
|
||||
@override
|
||||
String formatCryptoAmount(String amount) {
|
||||
return moneroAmountToString(amount: int.parse(amount));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/account_list.dart';
|
||||
import 'package:cw_monero/api/structs/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/transaction_history.dart'
|
||||
as monero_transaction_history;
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/amount_converter.dart';
|
||||
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_monero/api/transaction_history.dart' as monero_transaction_history;
|
||||
import 'package:cw_monero/api/wallet.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:monero/monero.dart' as monero;
|
||||
@@ -29,6 +27,10 @@ class PendingMoneroTransaction with PendingTransaction {
|
||||
final PendingTransactionDescription pendingTransactionDescription;
|
||||
final MoneroWalletBase wallet;
|
||||
|
||||
Money get amount => Money.fromInt(pendingTransactionDescription.amount, CryptoCurrency.xmr);
|
||||
|
||||
Money get fee => Money.fromInt(pendingTransactionDescription.fee, CryptoCurrency.xmr);
|
||||
|
||||
@override
|
||||
String get id => pendingTransactionDescription.hash;
|
||||
|
||||
@@ -36,15 +38,7 @@ class PendingMoneroTransaction with PendingTransaction {
|
||||
String get hex => pendingTransactionDescription.hex;
|
||||
|
||||
@override
|
||||
String get amountFormatted => AmountConverter.amountIntToString(
|
||||
CryptoCurrency.xmr, pendingTransactionDescription.amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue XMR";
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => AmountConverter.amountIntToString(
|
||||
CryptoCurrency.xmr, pendingTransactionDescription.fee);
|
||||
String get amountFormatted => amount.toString();
|
||||
|
||||
@override
|
||||
bool shouldCommitUR() => isViewOnly && wallet.hardwareWalletType != HardwareWalletType.trezor;
|
||||
|
||||
+43
-104
@@ -5,23 +5,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "76.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
version: "92.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.0"
|
||||
version: "9.0.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -108,18 +103,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "4.0.6"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||
sha256: "4070d2a59f8eec34c97c86ceb44403834899075f66e8a9d59706f8e7834f6f71"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.3.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -128,30 +123,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
build_resolvers:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||
sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.15"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
version: "2.15.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -164,10 +143,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
|
||||
sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.4"
|
||||
version: "8.12.6"
|
||||
cake_backup:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -181,10 +160,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -260,10 +239,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
|
||||
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.8"
|
||||
version: "3.1.3"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -354,14 +333,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -403,18 +374,10 @@ packages:
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
hive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hive
|
||||
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.3"
|
||||
hive_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: hive_generator
|
||||
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
|
||||
name: hive
|
||||
sha256: "5184b6ea455689754578267c31c3b0b329cccf20a5f2bfdab6941c29c3d3c09a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
@@ -478,26 +441,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
ledger_flutter_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -522,38 +485,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -566,26 +521,26 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mobx
|
||||
sha256: bf1a90e5bcfd2851fc6984e20eef69557c65d9e4d0a88f5be4cf72c9819ce6b0
|
||||
sha256: "25ea9a3ee69243d578fb6288721d8eeb18550d0391c32f896d377d994154bff2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.6.0"
|
||||
mobx_codegen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mobx_codegen
|
||||
sha256: "990da80722f7d7c0017dec92040b31545d625b15d40204c36a1e63d167c73cdc"
|
||||
sha256: "6529bcae074838004b58ad01fbe4966be182a176aacfb723686985986ebac19f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
version: "2.7.7"
|
||||
mockito:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mockito
|
||||
sha256: f99d8d072e249f719a5531735d146d8cf04c580d93920b04de75bef6dfb2daf6
|
||||
sha256: eff30d002f0c8bf073b6f929df4483b543133fcafce056870163587b03f1d422
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.5"
|
||||
version: "5.6.4"
|
||||
monero:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -823,18 +778,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.5"
|
||||
version: "4.2.3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -959,18 +906,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "0.7.10"
|
||||
torch_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1040,10 +979,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1109,5 +1048,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.9.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
@@ -16,8 +16,8 @@ dependencies:
|
||||
ffi: ^2.0.1
|
||||
http: ^1.1.0
|
||||
path_provider: ^2.0.11
|
||||
mobx: ^2.0.7+4
|
||||
flutter_mobx: ^2.0.6+1
|
||||
mobx: ^2.6.0
|
||||
flutter_mobx: ^2.3.0
|
||||
intl: any
|
||||
encrypt: ^5.0.1
|
||||
polyseed: ^0.0.7
|
||||
@@ -39,11 +39,11 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^2.4.15
|
||||
build_resolvers: ^2.4.4
|
||||
mobx_codegen: ^2.0.7
|
||||
build_runner: ^2.15.0
|
||||
mobx_codegen: ^2.7.7
|
||||
mockito: ^5.4.5
|
||||
hive_generator: ^2.0.1
|
||||
# hive_generator: ^2.0.1
|
||||
hive: 2.0.1
|
||||
sqflite_common_ffi: ^2.3.4+4
|
||||
|
||||
dependency_overrides:
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
BigInt stringAmountToBigIntBanano(String amount) {
|
||||
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerBanano));
|
||||
}
|
||||
|
||||
class BananoBalance extends Balance {
|
||||
final BigInt currentBalance;
|
||||
final BigInt receivableBalance;
|
||||
|
||||
BananoBalance({required this.currentBalance, required this.receivableBalance}) : super(BigInt.zero, BigInt.zero);
|
||||
|
||||
BananoBalance.fromFormattedString(
|
||||
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
||||
: currentBalance = stringAmountToBigIntBanano(formattedCurrentBalance),
|
||||
receivableBalance = stringAmountToBigIntBanano(formattedReceivableBalance),
|
||||
super(BigInt.zero, BigInt.zero);
|
||||
|
||||
BananoBalance.fromRawString(
|
||||
{required String currentBalance, required String receivableBalance})
|
||||
: currentBalance = BigInt.parse(currentBalance),
|
||||
receivableBalance = BigInt.parse(receivableBalance),
|
||||
super(BigInt.zero, BigInt.zero);
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance {
|
||||
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerBanano);
|
||||
}
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance {
|
||||
return NanoAmounts.getRawAsUsableString(receivableBalance.toString(), NanoAmounts.rawPerBanano);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,22 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
BigInt stringAmountToBigIntNano(String amount) {
|
||||
return BigInt.parse(NanoAmounts.getAmountAsRaw(amount, NanoAmounts.rawPerNano));
|
||||
}
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class NanoBalance extends Balance {
|
||||
final BigInt currentBalance;
|
||||
final BigInt receivableBalance;
|
||||
final Money currentBalance;
|
||||
final Money receivableBalance;
|
||||
|
||||
NanoBalance({required this.currentBalance, required this.receivableBalance}) : super(currentBalance, receivableBalance);
|
||||
NanoBalance({required this.currentBalance, required this.receivableBalance})
|
||||
: super(currentBalance, receivableBalance);
|
||||
|
||||
NanoBalance.fromFormattedString(
|
||||
{required String formattedCurrentBalance, required String formattedReceivableBalance})
|
||||
: currentBalance = stringAmountToBigIntNano(formattedCurrentBalance),
|
||||
receivableBalance = stringAmountToBigIntNano(formattedReceivableBalance),
|
||||
super(stringAmountToBigIntNano(formattedCurrentBalance), stringAmountToBigIntNano(formattedReceivableBalance));
|
||||
|
||||
NanoBalance.fromRawString(
|
||||
{required String currentBalance, required String receivableBalance})
|
||||
: currentBalance = BigInt.parse(currentBalance),
|
||||
receivableBalance = BigInt.parse(receivableBalance),
|
||||
super(BigInt.parse(currentBalance), BigInt.parse(receivableBalance));
|
||||
|
||||
@override
|
||||
String get formattedAvailableBalance {
|
||||
return NanoAmounts.getRawAsUsableString(currentBalance.toString(), NanoAmounts.rawPerNano);
|
||||
}
|
||||
|
||||
@override
|
||||
String get formattedAdditionalBalance {
|
||||
return NanoAmounts.getRawAsUsableString(receivableBalance.toString(), NanoAmounts.rawPerNano);
|
||||
}
|
||||
factory NanoBalance.fromRawString({
|
||||
required String currentBalance,
|
||||
required String receivableBalance,
|
||||
}) =>
|
||||
NanoBalance(
|
||||
currentBalance:
|
||||
Money.tryParse(currentBalance, CryptoCurrency.nano) ?? Money.zero(CryptoCurrency.nano),
|
||||
receivableBalance: Money.tryParse(receivableBalance, CryptoCurrency.nano) ??
|
||||
Money.zero(CryptoCurrency.nano),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/nano_account_info_response.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
@@ -85,10 +87,10 @@ class NanoClient {
|
||||
throw Exception(
|
||||
"Error while trying to get balance! ${data["error"] != null ? data["error"] : ""}");
|
||||
}
|
||||
final String currentBalance = data["balance"] as String;
|
||||
final String receivableBalance = data["receivable"] as String;
|
||||
final BigInt cur = BigInt.parse(currentBalance);
|
||||
final BigInt rec = BigInt.parse(receivableBalance);
|
||||
final currentBalance = data["balance"] as String;
|
||||
final receivableBalance = data["receivable"] as String;
|
||||
final cur = Money(BigInt.parse(currentBalance), CryptoCurrency.nano);
|
||||
final rec = Money(BigInt.parse(receivableBalance), CryptoCurrency.nano);
|
||||
return NanoBalance(currentBalance: cur, receivableBalance: rec);
|
||||
}
|
||||
|
||||
@@ -258,7 +260,7 @@ class NanoClient {
|
||||
|
||||
// first get the current account balance:
|
||||
if (balanceAfterTx == null) {
|
||||
final BigInt currentBalance = (await getBalance(publicAddress)).currentBalance;
|
||||
final BigInt currentBalance = (await getBalance(publicAddress)).currentBalance.amount;
|
||||
final BigInt txAmount = BigInt.parse(amountRaw);
|
||||
balanceAfterTx = currentBalance - txAmount;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:cw_core/transaction_info.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class NanoTransactionInfo extends TransactionInfo {
|
||||
NanoTransactionInfo({
|
||||
required this.id,
|
||||
required this.height,
|
||||
required this.amountRaw,
|
||||
required Money amountRaw,
|
||||
this.tokenSymbol = "XNO",
|
||||
required this.direction,
|
||||
required this.confirmed,
|
||||
@@ -15,12 +16,11 @@ class NanoTransactionInfo extends TransactionInfo {
|
||||
required this.confirmations,
|
||||
required this.to,
|
||||
required this.from,
|
||||
}) : this.amount = amountRaw.toInt();
|
||||
}) : this.amount = amountRaw;
|
||||
|
||||
final String id;
|
||||
final int height;
|
||||
final int amount;
|
||||
final BigInt amountRaw;
|
||||
final Money amount;
|
||||
final TransactionDirection direction;
|
||||
final DateTime date;
|
||||
final bool confirmed;
|
||||
@@ -32,28 +32,17 @@ class NanoTransactionInfo extends TransactionInfo {
|
||||
|
||||
bool get isPending => !this.confirmed;
|
||||
|
||||
@override
|
||||
String amountFormatted() {
|
||||
final String amt =
|
||||
NanoAmounts.getRawAsUsableString(amountRaw.toString(), NanoAmounts.rawPerNano);
|
||||
final String acc = NanoAmounts.getRawAccuracy(amountRaw.toString(), NanoAmounts.rawPerNano);
|
||||
return "$acc$amt $tokenSymbol";
|
||||
}
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
||||
@override
|
||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
@override
|
||||
String feeFormatted() => "0 XNO";
|
||||
|
||||
factory NanoTransactionInfo.fromJson(Map<String, dynamic> data) {
|
||||
return NanoTransactionInfo(
|
||||
id: data['id'] as String,
|
||||
height: data['height'] as int,
|
||||
amountRaw: BigInt.parse(data['amountRaw'] as String),
|
||||
amountRaw: Money(BigInt.parse(data['amountRaw'] as String), CryptoCurrency.nano),
|
||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
confirmed: data['confirmed'] as bool,
|
||||
@@ -67,7 +56,7 @@ class NanoTransactionInfo extends TransactionInfo {
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'height': height,
|
||||
'amountRaw': amountRaw.toString(),
|
||||
'amountRaw': amount.amount.toString(),
|
||||
'direction': direction.index,
|
||||
'date': date.millisecondsSinceEpoch,
|
||||
'confirmed': confirmed,
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/cake_hive.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/encryption_file_utils.dart';
|
||||
@@ -55,7 +56,10 @@ abstract class NanoWalletBase
|
||||
walletAddresses = NanoWalletAddresses(walletInfo),
|
||||
balance = ObservableMap<CryptoCurrency, NanoBalance>.of({
|
||||
CryptoCurrency.nano: initialBalance ??
|
||||
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero)
|
||||
NanoBalance(
|
||||
currentBalance: Money.zero(CryptoCurrency.nano),
|
||||
receivableBalance: Money.zero(CryptoCurrency.nano),
|
||||
)
|
||||
}),
|
||||
super(walletInfo, derivationInfo) {
|
||||
this.walletInfo = walletInfo;
|
||||
@@ -191,7 +195,7 @@ abstract class NanoWalletBase
|
||||
|
||||
BigInt runningAmount = BigInt.zero;
|
||||
await _updateBalance();
|
||||
BigInt runningBalance = balance[currency]?.currentBalance ?? BigInt.zero;
|
||||
BigInt runningBalance = balance[currency]?.currentBalance.amount ?? BigInt.zero;
|
||||
|
||||
final List<Map<String, String>> blocks = [];
|
||||
String? previousHash;
|
||||
@@ -199,14 +203,12 @@ abstract class NanoWalletBase
|
||||
for (var txOut in credentials.outputs) {
|
||||
late BigInt amt;
|
||||
if (txOut.sendAll) {
|
||||
amt = balance[currency]?.currentBalance ?? BigInt.zero;
|
||||
amt = balance[currency]?.currentBalance.amount ?? BigInt.zero;
|
||||
} else {
|
||||
amt = BigInt.tryParse(NanoAmounts.getAmountAsRaw(
|
||||
txOut.cryptoAmount?.replaceAll(',', '.') ?? "0", NanoAmounts.rawPerNano)) ??
|
||||
BigInt.zero;
|
||||
amt = txOut.cryptoAmount.amount;
|
||||
}
|
||||
|
||||
if (balance[currency]?.currentBalance != null && amt > balance[currency]!.currentBalance) {
|
||||
if (balance[currency]?.currentBalance != null && amt > balance[currency]!.currentBalance.amount) {
|
||||
throw Exception("Trying to send more than entire balance!");
|
||||
}
|
||||
|
||||
@@ -233,7 +235,7 @@ abstract class NanoWalletBase
|
||||
}
|
||||
|
||||
try {
|
||||
if (runningAmount > balance[currency]!.currentBalance || runningBalance < BigInt.zero) {
|
||||
if (runningAmount > balance[currency]!.currentBalance.amount || runningBalance < BigInt.zero) {
|
||||
throw Exception(("Trying to send more than entire balance!"));
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -241,7 +243,7 @@ abstract class NanoWalletBase
|
||||
}
|
||||
|
||||
return PendingNanoTransaction(
|
||||
amount: runningAmount,
|
||||
amount: Money(runningAmount, currency),
|
||||
id: "",
|
||||
nanoClient: _client,
|
||||
blocks: blocks,
|
||||
@@ -294,7 +296,7 @@ abstract class NanoWalletBase
|
||||
final bool isSend = transactionModel.type == "send";
|
||||
result[transactionModel.hash] = NanoTransactionInfo(
|
||||
id: transactionModel.hash,
|
||||
amountRaw: transactionModel.amount,
|
||||
amountRaw: Money(transactionModel.amount, currency),
|
||||
height: transactionModel.height,
|
||||
direction: isSend ? TransactionDirection.outgoing : TransactionDirection.incoming,
|
||||
confirmed: transactionModel.confirmed,
|
||||
@@ -360,7 +362,7 @@ abstract class NanoWalletBase
|
||||
// get our balance:
|
||||
await _updateBalance();
|
||||
// if we have anything to receive, process it:
|
||||
if (balance[currency]!.receivableBalance > BigInt.zero) {
|
||||
if (balance[currency]!.receivableBalance.amount > BigInt.zero) {
|
||||
await _receiveAll();
|
||||
}
|
||||
});
|
||||
@@ -465,8 +467,8 @@ abstract class NanoWalletBase
|
||||
// if we don't have a balance, we should at least create one, since it's a late binding
|
||||
// otherwise, it's better to just leave it as whatever it was before:
|
||||
if (balance[currency] == null) {
|
||||
balance[currency] =
|
||||
NanoBalance(currentBalance: BigInt.zero, receivableBalance: BigInt.zero);
|
||||
balance[currency] = NanoBalance(
|
||||
currentBalance: Money.zero(currency), receivableBalance: Money.zero(currency));
|
||||
}
|
||||
}
|
||||
// don't save unnecessarily:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cw_core/amount/money.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/pending_transaction.dart';
|
||||
import 'package:cw_nano/nano_client.dart';
|
||||
import 'package:nanoutil/nanoutil.dart';
|
||||
|
||||
class PendingNanoTransaction with PendingTransaction {
|
||||
PendingNanoTransaction({
|
||||
@@ -11,38 +12,26 @@ class PendingNanoTransaction with PendingTransaction {
|
||||
});
|
||||
|
||||
final NanoClient nanoClient;
|
||||
final BigInt amount;
|
||||
final String id;
|
||||
final List<Map<String, String>> blocks;
|
||||
String hex = "unused";
|
||||
|
||||
@override
|
||||
String get amountFormatted {
|
||||
final String amt = NanoAmounts.getRawAsUsableString(amount.toString(), NanoAmounts.rawPerNano);
|
||||
return amt;
|
||||
}
|
||||
|
||||
String get accurateAmountFormatted {
|
||||
final String amt = NanoAmounts.getRawAsUsableString(amount.toString(), NanoAmounts.rawPerNano);
|
||||
final String acc = NanoAmounts.getRawAccuracy(amount.toString(), NanoAmounts.rawPerNano);
|
||||
return "$acc$amt";
|
||||
}
|
||||
final Money amount;
|
||||
|
||||
@override
|
||||
String get feeFormatted => "$feeFormattedValue XNO";
|
||||
String get amountFormatted => amount.toString();
|
||||
|
||||
@override
|
||||
String get feeFormattedValue => "0";
|
||||
Money get fee => Money.zero(CryptoCurrency.nano);
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
for (var block in blocks) {
|
||||
for (final block in blocks) {
|
||||
await nanoClient.processBlock(block, "send");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> commitUR() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
Future<Map<String, String>> commitUR() => throw UnimplementedError();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user