diff --git a/cw_tron/lib/tron_client.dart b/cw_tron/lib/tron_client.dart index a38b9ac8a..433803860 100644 --- a/cw_tron/lib/tron_client.dart +++ b/cw_tron/lib/tron_client.dart @@ -27,6 +27,8 @@ class TronClient { // This is an internal tracker, so we don't have to "refetch". int _nativeTxEstimatedFee = 0; + static const _historyRequestTimeout = Duration(seconds: 30); + Future> fetchTransactions(String address, {String? contractAddress}) async { try { @@ -43,7 +45,7 @@ class TronClient { 'Content-Type': 'application/json', 'TRON-PRO-API-KEY': secrets.tronGridApiKey, }, - ); + ).timeout(_historyRequestTimeout); final jsonResponse = json.decode(response.body) as Map; if (response.statusCode >= 200 && @@ -76,7 +78,7 @@ class TronClient { 'Content-Type': 'application/json', 'TRON-PRO-API-KEY': secrets.tronGridApiKey, }, - ); + ).timeout(_historyRequestTimeout); final jsonResponse = json.decode(response.body) as Map; if (response.statusCode >= 200 && diff --git a/cw_tron/lib/tron_transaction_model.dart b/cw_tron/lib/tron_transaction_model.dart index 5aca17b74..21611cceb 100644 --- a/cw_tron/lib/tron_transaction_model.dart +++ b/cw_tron/lib/tron_transaction_model.dart @@ -82,6 +82,8 @@ class TronTransactionModel { String? get contractAddress => contracts?.first.parameter?.value?.contractAddress; + bool get isTrc20TransferCall => contracts?.first.parameter?.value?.isTrc20TransferCall ?? false; + TronTransactionModel({ this.ret, this.txID, @@ -147,29 +149,58 @@ class Value { String? ownerAddress; String? contractAddress; int? amount; + int? callValue; String? toAddress; String? assetName; + /// Function selector of TRC20 `transfer(address,uint256)`. + static const _transferSelector = "a9059cbb"; + + /// Hex length of `transfer` calldata: the selector plus two 32-byte arguments. + static const _transferCallDataLength = 8 + 64 + 64; + + String? get _callData { + final callData = data?.toLowerCase(); + if (callData != null && callData.startsWith("0x")) { + return callData.substring(2); + } + + return callData; + } + + /// Only a TRC20 `transfer` carries a receiver and a token amount in its calldata. + /// Any other contract call (approve, swaps, deposit(), claim(), ...) has a different layout. + bool get isTrc20TransferCall { + final callData = _callData; + + return contractAddress != null && + callData != null && + callData.length >= _transferCallDataLength && + callData.startsWith(_transferSelector); + } + //Getters to extract address for tron transactions /// If the contract address is null, it returns the toAddress - /// If it's not null, it decodes the data field and gets the receiver address. + /// For a TRC20 transfer, it decodes the data field and gets the receiver address. + /// For any other contract call, it returns the called contract. String? get receiverAddress { if (contractAddress == null) return toAddress; - if (data == null) return null; + if (!isTrc20TransferCall) return contractAddress; - return _decodeAddressFromEncodedDataField(data!); + return _decodeAddressFromEncodedDataField(_callData!); } //Getters to extract amount for tron transactions /// If the contract address is null, it returns the amount - /// If it's not null, it decodes the data field and gets the tx amount. + /// For a TRC20 transfer, it decodes the data field and gets the token amount. + /// For any other contract call, it returns the TRX sent along with the call. BigInt? get txAmount { if (contractAddress == null) return BigInt.from(amount ?? 0); - if (data == null) return null; + if (!isTrc20TransferCall) return BigInt.from(callValue ?? 0); - return _decodeAmountInvolvedFromEncodedDataField(data!); + return _decodeAmountInvolvedFromEncodedDataField(_callData!); } Value( @@ -177,6 +208,7 @@ class Value { this.ownerAddress, this.contractAddress, this.amount, + this.callValue, this.toAddress, this.assetName}); @@ -185,6 +217,7 @@ class Value { ownerAddress = json['owner_address']; contractAddress = json['contract_address']; amount = json['amount']; + callValue = int.tryParse(json['call_value']?.toString() ?? ""); toAddress = json['to_address']; assetName = json['asset_name']; } @@ -192,7 +225,7 @@ class Value { /// To get the address from the encoded data field String _decodeAddressFromEncodedDataField(String output) { // To get the receiver address from the encoded params - output = output.replaceFirst('0x', '').substring(8); + output = output.substring(8); final abiCoder = ABICoder.fromType('address'); final decoded = abiCoder.decode(AbiParameter.bytes, hex.decode(output)); final tronAddress = TronAddress.fromEthAddress((decoded.result as ETHAddress).toBytes()); @@ -200,13 +233,8 @@ class Value { return tronAddress.toString(); } - /// To get the amount from the encoded data field - BigInt _decodeAmountInvolvedFromEncodedDataField(String output) { - output = output.replaceFirst('0x', '').substring(72); - final amountAbiCoder = ABICoder.fromType('uint256'); - final decodedA = amountAbiCoder.decode(AbiParameter.uint256, hex.decode(output)); - final amount = decodedA.result as BigInt; - - return amount; - } + /// To get the amount from the encoded data field. + /// Parsed directly, as on_chain's uint256 decoder rejects any value of 2^255 or more. + BigInt _decodeAmountInvolvedFromEncodedDataField(String output) => + BigInt.parse(output.substring(72, _transferCallDataLength), radix: 16); } diff --git a/cw_tron/lib/tron_wallet.dart b/cw_tron/lib/tron_wallet.dart index ba6d2a444..488717fd8 100644 --- a/cw_tron/lib/tron_wallet.dart +++ b/cw_tron/lib/tron_wallet.dart @@ -85,6 +85,11 @@ abstract class TronWalletBase Timer? _transactionsUpdateTimer; + bool _isTimerUpdatingHistory = false; + + /// TRC20 symbol and decimals by contract address; they never change on-chain. + final Map _trc20Currencies = {}; + @override WalletAddresses walletAddresses; @@ -305,8 +310,14 @@ abstract class TronWalletBase return; } await _updateBalance(); - await fetchTransactions(); - fetchTrc20ExcludedTransactions(); + + // History is best-effort: a TronGrid or parsing failure must not fail the sync. + try { + await fetchTransactions(); + fetchTrc20ExcludedTransactions(); + } catch (e) { + printV("Failed to fetch the Tron transaction history: $e"); + } syncStatus = SyncedSyncStatus(); } catch (e) { @@ -400,52 +411,44 @@ abstract class TronWalletBase final ownerAddress = TronAddress(_tronAddress); + // Looked up once per token contract in this update, not once per transaction. + final Map trc20Currencies = {}; + for (var transactionModel in transactions) { - if (transactionModel.isError) { - continue; + // A transaction that can't be parsed must not drop the rest of the history. + try { + if (transactionModel.isError) { + continue; + } + + // Filter out spam transactions that involve receiving TRC10 assets transaction, we deal with TRX and TRC20 transactions + if (transactionModel.contracts?.first.type == "TransferAssetContract") { + continue; + } + + // Only a TRC20 transfer moves tokens, any other contract call is a TRX transaction. + var txCurrency = currency; + if (transactionModel.isTrc20TransferCall) { + final contractAddress = transactionModel.contractAddress!; + txCurrency = trc20Currencies[contractAddress] ??= + await _getTrc20Currency(contractAddress, contract, ownerAddress); + } + + result[transactionModel.hash] = TronTransactionInfo( + id: transactionModel.hash, + amount: Money(transactionModel.amount ?? BigInt.zero, txCurrency), + direction: TronAddress(transactionModel.from!, visible: false).toAddress() == address + ? TransactionDirection.outgoing + : TransactionDirection.incoming, + blockTime: transactionModel.date, + fee: transactionModel.fee != null ? Money.fromInt(transactionModel.fee!, currency) : null, + to: transactionModel.to, + from: transactionModel.from, + isPending: false, + ); + } catch (e) { + printV("Skipping Tron transaction ${transactionModel.txID}: $e"); } - - // Filter out spam transactions that involve receiving TRC10 assets transaction, we deal with TRX and TRC20 transactions - if (transactionModel.contracts?.first.type == "TransferAssetContract") { - continue; - } - - var txCurrency = currency; - if (transactionModel.contractAddress != null) { - final tokenAddress = TronAddress(transactionModel.contractAddress!); - - final tokenSymbol = (await _client.getTokenDetail( - contract, - "symbol", - ownerAddress, - tokenAddress, - ) as String?) ?? - ''; - - final decimals = (await _client.getTokenDetail( - contract, - "decimals", - ownerAddress, - tokenAddress, - ) as BigInt?) - ?.toInt() ?? - txCurrency.decimals; - - txCurrency = CryptoCurrency(name: tokenSymbol, title: tokenSymbol, decimals: decimals); - } - - result[transactionModel.hash] = TronTransactionInfo( - id: transactionModel.hash, - amount: Money(transactionModel.amount ?? BigInt.zero, txCurrency), - direction: TronAddress(transactionModel.from!, visible: false).toAddress() == address - ? TransactionDirection.outgoing - : TransactionDirection.incoming, - blockTime: transactionModel.date, - fee: transactionModel.fee != null ? Money.fromInt(transactionModel.fee!, currency) : null, - to: transactionModel.to, - from: transactionModel.from, - isPending: false, - ); } transactionHistory.addMany(result); @@ -455,6 +458,47 @@ abstract class TronWalletBase return transactionHistory.transactions; } + Future _getTrc20Currency( + String contractAddress, + ContractABI contract, + TronAddress ownerAddress, + ) async { + final cachedCurrency = _trc20Currencies[contractAddress]; + if (cachedCurrency != null) { + return cachedCurrency; + } + + final tokenAddress = TronAddress(contractAddress); + + final tokenSymbol = await _client.getTokenDetail( + contract, + "symbol", + ownerAddress, + tokenAddress, + ) as String?; + + final decimals = (await _client.getTokenDetail( + contract, + "decimals", + ownerAddress, + tokenAddress, + ) as BigInt?) + ?.toInt(); + + final txCurrency = CryptoCurrency( + name: tokenSymbol ?? "", + title: tokenSymbol ?? "", + decimals: decimals ?? currency.decimals, + ); + + // Failed lookups aren't cached, so the next history update retries them. + if (tokenSymbol != null && decimals != null) { + _trc20Currencies[contractAddress] = txCurrency; + } + + return txCurrency; + } + Future fetchTrc20ExcludedTransactions() async { if (!await checkIfScanProviderIsEnabled()) { return; @@ -467,22 +511,26 @@ abstract class TronWalletBase final Map result = {}; for (final transactionModel in transactions) { - if (transactionHistory.transactions.containsKey(transactionModel.hash)) { - continue; - } + try { + if (transactionHistory.transactions.containsKey(transactionModel.hash)) { + continue; + } - result[transactionModel.hash] = TronTransactionInfo( - id: transactionModel.hash, - amount: Money(transactionModel.amount ?? BigInt.zero, transactionModel.currency), - direction: transactionModel.from! == address - ? TransactionDirection.outgoing - : TransactionDirection.incoming, - blockTime: transactionModel.date, - fee: transactionModel.fee != null ? Money.fromInt(transactionModel.fee!, currency) : null, - to: transactionModel.to, - from: transactionModel.from, - isPending: false, - ); + result[transactionModel.hash] = TronTransactionInfo( + id: transactionModel.hash, + amount: Money(transactionModel.amount ?? BigInt.zero, transactionModel.currency), + direction: transactionModel.from! == address + ? TransactionDirection.outgoing + : TransactionDirection.incoming, + blockTime: transactionModel.date, + fee: transactionModel.fee != null ? Money.fromInt(transactionModel.fee!, currency) : null, + to: transactionModel.to, + from: transactionModel.from, + isPending: false, + ); + } catch (e) { + printV("Skipping Tron TRC20 transaction ${transactionModel.transactionId}: $e"); + } } transactionHistory.addMany(result); @@ -639,8 +687,21 @@ abstract class TronWalletBase _transactionsUpdateTimer = Timer.periodic(const Duration(seconds: 30), (_) async { _updateBalance(); - await fetchTransactions(); - fetchTrc20ExcludedTransactions(); + + // A history update can outlast the period; don't stack another one on top of it. + if (_isTimerUpdatingHistory) { + return; + } + + _isTimerUpdatingHistory = true; + try { + await fetchTransactions(); + await fetchTrc20ExcludedTransactions(); + } catch (e) { + printV("Failed to update the Tron transaction history: $e"); + } finally { + _isTimerUpdatingHistory = false; + } }); } diff --git a/cw_tron/test/tron_transaction_model_test.dart b/cw_tron/test/tron_transaction_model_test.dart new file mode 100644 index 000000000..c9808b208 --- /dev/null +++ b/cw_tron/test/tron_transaction_model_test.dart @@ -0,0 +1,179 @@ +import "package:cw_tron/tron_transaction_model.dart"; +import "package:flutter_test/flutter_test.dart"; + +// Wallet TCFuFnoos25WL99j9y3u92LR67mVwrTvrQ and the USDT contract TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t. +const _owner = "41191882614f605dd84703252b196c0c006b5482f8"; +const _usdtContract = "41a614f803b6fd780986a42c78ec9c7f77e6ded13c"; + +// Real TronGrid entries of TCFuFnoos25WL99j9y3u92LR67mVwrTvrQ. +const _unlimitedApprove = { + "ret": [ + {"contractRet": "SUCCESS", "fee": 9976400}, + ], + "txID": "fc20c9c9489842cf105c56c80e9fce540e55f1158d68459bc625332bae5b9806", + "block_timestamp": 1769113440000, + "raw_data": { + "contract": [ + { + "parameter": { + "value": { + "data": "095ea7b3000000000000000000000000bbbd3bd87c268b8030bc2edae7021830af599ed2" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "owner_address": _owner, + "contract_address": _usdtContract, + }, + "type_url": "type.googleapis.com/protocol.TriggerSmartContract", + }, + "type": "TriggerSmartContract", + }, + ], + }, +}; + +const _usdtTransfer = { + "ret": [ + {"contractRet": "SUCCESS", "fee": 6428500}, + ], + "txID": "0584051e5208526f48d05965a4e15c48f6ec84c23c71441ed6e38a8a49c17faf", + "block_timestamp": 1768779375000, + "raw_data": { + "contract": [ + { + "parameter": { + "value": { + "data": "a9059cbb000000000000000000000000ca5125c5947162497b1b924d6c5e282282e3351f" + "00000000000000000000000000000000000000000000000000000000139c2440", + "owner_address": _owner, + "contract_address": _usdtContract, + }, + "type_url": "type.googleapis.com/protocol.TriggerSmartContract", + }, + "type": "TriggerSmartContract", + }, + ], + }, +}; + +const _trxTransfer = { + "ret": [ + {"contractRet": "SUCCESS", "fee": 0}, + ], + "txID": "a5bf8fb4aa16496e53ad36fcfbe3f49c92c0e0cc0caffc97d3cc2fe8a9e16ede", + "block_timestamp": 1791643617000, + "raw_data": { + "contract": [ + { + "parameter": { + "value": { + "amount": 1, + "owner_address": "415576b7edd2a7cb0ebb6f794d7f2c28aa5dc85a82", + "to_address": _owner, + }, + "type_url": "type.googleapis.com/protocol.TransferContract", + }, + "type": "TransferContract", + }, + ], + }, +}; + +TronTransactionModel _contractCall(String? data, {Object? callValue}) => + TronTransactionModel.fromJson({ + "ret": [ + {"contractRet": "SUCCESS", "fee": 1}, + ], + "txID": "test", + "block_timestamp": 1, + "raw_data": { + "contract": [ + { + "parameter": { + "value": { + if (data != null) "data": data, + if (callValue != null) "call_value": callValue, + "owner_address": _owner, + "contract_address": _usdtContract, + }, + }, + "type": "TriggerSmartContract", + }, + ], + }, + }); + +String _word(String hex) => hex.padLeft(64, "0"); + +void main() { + group("TronTransactionModel", () { + test("decodes the receiver and token amount of a TRC20 transfer", () { + final transaction = TronTransactionModel.fromJson(_usdtTransfer); + + expect(transaction.isTrc20TransferCall, isTrue); + expect(transaction.amount, BigInt.from(329000000)); + expect(transaction.to, "TUQxi3DkhbrL9qL5EHEfAmkZbsPBGP1UnH"); + expect(transaction.from, _owner); + expect(transaction.contractAddress, _usdtContract); + }); + + test("reads an unlimited approve as a TRX call to the contract instead of throwing", () { + final transaction = TronTransactionModel.fromJson(_unlimitedApprove); + + expect(transaction.isError, isFalse); + expect(transaction.isTrc20TransferCall, isFalse); + expect(transaction.amount, BigInt.zero); + expect(transaction.to, _usdtContract); + expect(transaction.fee, 9976400); + }); + + test("decodes transfer amounts of 2^255 and above", () { + final maxUint256 = (BigInt.one << 256) - BigInt.one; + final transaction = _contractCall( + "a9059cbb${_word("ca5125c5947162497b1b924d6c5e282282e3351f")}${"f" * 64}", + ); + + expect(transaction.isTrc20TransferCall, isTrue); + expect(transaction.amount, maxUint256); + }); + + test("decodes 0x-prefixed, upper-case transfer calldata", () { + final transaction = _contractCall( + "0xA9059CBB${_word("CA5125C5947162497B1B924D6C5E282282E3351F")}${_word("139C2440")}", + ); + + expect(transaction.isTrc20TransferCall, isTrue); + expect(transaction.amount, BigInt.from(329000000)); + expect(transaction.to, "TUQxi3DkhbrL9qL5EHEfAmkZbsPBGP1UnH"); + }); + + test("reads other contract calls as the TRX sent with them", () { + final deposit = _contractCall("d0e30db0", callValue: 5000000); + final withdraw = _contractCall("2e1a7d4d${_word("3e8")}"); + final noCalldata = _contractCall(null, callValue: 7); + final truncatedTransfer = + _contractCall("a9059cbb${_word("ca5125c5947162497b1b924d6c5e282282e3351f")}"); + + for (final transaction in [deposit, withdraw, noCalldata, truncatedTransfer]) { + expect(transaction.isTrc20TransferCall, isFalse); + expect(transaction.to, _usdtContract); + } + expect(deposit.amount, BigInt.from(5000000)); + expect(withdraw.amount, BigInt.zero); + expect(noCalldata.amount, BigInt.from(7)); + expect(truncatedTransfer.amount, BigInt.zero); + }); + + test("parses call_value sent as a string", () { + expect(_contractCall("d0e30db0", callValue: "42").amount, BigInt.from(42)); + }); + + test("keeps TRX transfers unchanged", () { + final transaction = TronTransactionModel.fromJson(_trxTransfer); + + expect(transaction.isTrc20TransferCall, isFalse); + expect(transaction.contractAddress, isNull); + expect(transaction.amount, BigInt.one); + expect(transaction.to, _owner); + expect(transaction.from, "415576b7edd2a7cb0ebb6f794d7f2c28aa5dc85a82"); + }); + }); +} diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index a9687c824..0936a065a 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -924,9 +924,10 @@ class CWBitcoin extends Bitcoin { } @override - Balance balanceForAccount(Object wallet, int accountIndex) { + Money balanceForAccount(Object wallet, int accountIndex) { final bitcoinWallet = wallet as ElectrumWallet; - return bitcoinWallet.balanceForAccount(accountIndex); + final balance = bitcoinWallet.balanceForAccount(accountIndex); + return balance.confirmed + balance.unconfirmed; } @override diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index d95cc2b9c..7a6512ad2 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1727,7 +1727,6 @@ abstract class DashboardViewModelBase with Store { static const walletTypesToBeRemoved = [ WalletType.zano, - WalletType.decred, ]; Future shouldShowRemovalPopup() async { diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index f44dff6d2..989fc4e91 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -303,7 +303,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with ChainflipExchangeProvider(), if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(), SwapTradeExchangeProvider(), - LetsExchangeExchangeProvider(), + // LetsExchangeExchangeProvider(), // StealthExExchangeProvider(), XOSwapExchangeProvider(), SwapsXyzExchangeProvider(), diff --git a/lib/view_model/wallet_account_list/bitcoin_account_list/bitcoin_account_list_view_model.dart b/lib/view_model/wallet_account_list/bitcoin_account_list/bitcoin_account_list_view_model.dart index 44f10b35d..ac75536bf 100644 --- a/lib/view_model/wallet_account_list/bitcoin_account_list/bitcoin_account_list_view_model.dart +++ b/lib/view_model/wallet_account_list/bitcoin_account_list/bitcoin_account_list_view_model.dart @@ -99,10 +99,7 @@ abstract class BitcoinAccountListViewModelBase with Store implements WalletAccou }); } - Money _fullBalance(int accountIndex) { - final balance = bitcoin!.balanceForAccount(_wallet, accountIndex); - return balance.confirmed + balance.unconfirmed; - } + Money _fullBalance(int accountIndex) => bitcoin!.balanceForAccount(_wallet, accountIndex); @override @action diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 7d8c7881d..c4d95ed08 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,14 +15,14 @@ APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" MONERO_COM_VERSION="6.5.0" -MONERO_COM_BUILD_NUMBER=4170 +MONERO_COM_BUILD_NUMBER=4171 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="6.5.0" -CAKEWALLET_BUILD_NUMBER=4426 +CAKEWALLET_BUILD_NUMBER=4428 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index af438918e..e3a6a580c 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,12 +13,12 @@ APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" MONERO_COM_VERSION="6.5.0" -MONERO_COM_BUILD_NUMBER=177 +MONERO_COM_BUILD_NUMBER=178 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="6.5.0" -CAKEWALLET_BUILD_NUMBER=446 +CAKEWALLET_BUILD_NUMBER=448 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" diff --git a/scripts/linux/app_env.sh b/scripts/linux/app_env.sh index 5b452fdb3..ba7be5950 100755 --- a/scripts/linux/app_env.sh +++ b/scripts/linux/app_env.sh @@ -15,7 +15,7 @@ fi CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="6.5.0" -CAKEWALLET_BUILD_NUMBER=83 +CAKEWALLET_BUILD_NUMBER=84 if ! [[ " ${TYPES[*]} " =~ " ${APP_LINUX_TYPE} " ]]; then echo "Wrong app type." diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index 96d9102f7..b5e107765 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -17,12 +17,12 @@ fi MONERO_COM_NAME="Monero.com" MONERO_COM_VERSION="6.5.0" -MONERO_COM_BUILD_NUMBER=86 +MONERO_COM_BUILD_NUMBER=87 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="6.5.0" -CAKEWALLET_BUILD_NUMBER=154 +CAKEWALLET_BUILD_NUMBER=155 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/tool/configure.dart b/tool/configure.dart index 6260f8282..ef4200927 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -308,7 +308,7 @@ abstract class Bitcoin { Future getLightningInvoice(Object wallet, BigInt amount); String? getBreezSdkError(Object exception); Future getCurrentAccount(Object wallet); - Balance balanceForAccount(Object wallet, int accountIndex); + Money balanceForAccount(Object wallet, int accountIndex); Map accountBalancesSnapshot(Object wallet); Future setCurrentAccount(Object wallet, int accountIndex); List getCurrentAccountBitcoinTransactions(Object wallet); @@ -2092,9 +2092,9 @@ Future generateWalletTypes({ outputContent += '\tWalletType.nano,\n'; } - // if (hasDecred) { - // outputContent += '\tWalletType.decred,\n'; - // } + if (hasDecred) { + outputContent += '\tWalletType.decred,\n'; + } // if (hasZano) { // outputContent += '\tWalletType.zano,\n';