From 9f404139d57594cf8b35334197d18c744fe10361 Mon Sep 17 00:00:00 2001 From: polstianka Date: Mon, 16 Sep 2024 13:40:22 -0700 Subject: [PATCH] bug fixeds --- .../com/tonapps/wallet/data/account/Wallet.kt | 2 +- .../wallet/data/token/TokenRepository.kt | 32 +---- .../tonkeeper/core/entities/TransferEntity.kt | 7 +- .../tonkeeper/extensions/RawMessageEntity.kt | 39 ++++-- .../tonkeeper/extensions/SignRequestEntity.kt | 29 +++- .../com/tonapps/tonkeeper/koin/KoinModule.kt | 2 +- .../tonkeeper/manager/AssetsManager.kt | 37 +++-- .../{sign => manager}/SignManager.kt | 39 ++++-- .../manager/tx/TransactionManager.kt | 2 +- .../ui/screen/action/ActionViewModel.kt | 12 +- .../ui/screen/browser/dapp/DAppScreen.kt | 6 +- .../collectibles/CollectiblesViewModel.kt | 7 +- .../tonkeeper/ui/screen/init/InitViewModel.kt | 33 +++-- .../ui/screen/init/step/LabelScreen.kt | 13 +- .../tonkeeper/ui/screen/root/RootViewModel.kt | 2 +- .../ui/screen/send/main/SendViewModel.kt | 129 +++++++++++------- .../tonkeeper/ui/screen/wallet/main/State.kt | 2 + .../ui/screen/wallet/main/WalletViewModel.kt | 89 +++++------- .../screen/wallet/picker/PickerViewModel.kt | 5 +- 19 files changed, 286 insertions(+), 201 deletions(-) rename apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/{sign => manager}/SignManager.kt (80%) diff --git a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/Wallet.kt b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/Wallet.kt index d1558fd94..7d3d74c37 100644 --- a/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/Wallet.kt +++ b/apps/wallet/data/account/src/main/java/com/tonapps/wallet/data/account/Wallet.kt @@ -27,7 +27,7 @@ sealed class Wallet { get() = accountName.isBlank() && emoji.isBlank() val name: String - get() = accountName.ifBlank { "Tonkeeper" } + get() = accountName val title: CharSequence? get() = if (isEmpty) { diff --git a/apps/wallet/data/tokens/src/main/java/com/tonapps/wallet/data/token/TokenRepository.kt b/apps/wallet/data/tokens/src/main/java/com/tonapps/wallet/data/token/TokenRepository.kt index 3ada09d6a..decf1d8b7 100644 --- a/apps/wallet/data/tokens/src/main/java/com/tonapps/wallet/data/token/TokenRepository.kt +++ b/apps/wallet/data/tokens/src/main/java/com/tonapps/wallet/data/token/TokenRepository.kt @@ -33,37 +33,15 @@ class TokenRepository( fun getToken(accountId: String, testnet: Boolean) = remoteDataSource.getJetton(accountId, testnet) - suspend fun getTotalBalances( - currency: WalletCurrency, - accountId: String, - testnet: Boolean - ): Coins? { - return totalBalanceCache[cacheKey(accountId, testnet)] ?: loadTotalBalances(currency, accountId, testnet) - } - - private suspend fun loadTotalBalances( - currency: WalletCurrency, - accountId: String, - testnet: Boolean - ): Coins? { - val tokens = get(currency, accountId, testnet) ?: return null - var fiatBalance = Coins.of(0) - if (testnet) { - fiatBalance = tokens.first().balance.value - } else { - for (token in tokens) { - fiatBalance += token.fiat - } - } - totalBalanceCache[cacheKey(accountId, testnet)] = fiatBalance - return fiatBalance - } - suspend fun get( currency: WalletCurrency, accountId: String, - testnet: Boolean + testnet: Boolean, + refresh: Boolean = false, ): List? { + if (refresh) { + return getRemote(currency, accountId, testnet) + } val tokens = getLocal(currency, accountId, testnet) if (tokens.isNotEmpty()) { return tokens diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/TransferEntity.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/TransferEntity.kt index bef9771f9..fad60f3f5 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/TransferEntity.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/core/entities/TransferEntity.kt @@ -4,6 +4,7 @@ import com.tonapps.blockchain.ton.TONOpCode import com.tonapps.blockchain.ton.TonSendMode import com.tonapps.blockchain.ton.TonTransferHelper import com.tonapps.blockchain.ton.contract.BaseWalletContract +import com.tonapps.blockchain.ton.extensions.EmptyPrivateKeyEd25519 import com.tonapps.blockchain.ton.extensions.storeOpCode import com.tonapps.blockchain.ton.extensions.storeStringTail import com.tonapps.blockchain.ton.extensions.toAccountId @@ -52,6 +53,10 @@ data class TransferEntity( val contract: BaseWalletContract get() = wallet.contract + val fakePrivateKey: PrivateKeyEd25519 by lazy { + if (commentEncrypted) PrivateKeyEd25519() else EmptyPrivateKeyEd25519 + } + val isTon: Boolean get() = token.isTon @@ -261,7 +266,7 @@ data class TransferEntity( } fun toSignedMessage( - privateKey: PrivateKeyEd25519, + privateKey: PrivateKeyEd25519 = fakePrivateKey, internalMessage: Boolean, excessesAddress: AddrStd? = null, additionalGifts: List = emptyList() diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt index 90afe6509..aeb7b1cdb 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/RawMessageEntity.kt @@ -9,6 +9,7 @@ import com.tonapps.wallet.data.core.entity.RawMessageEntity import org.ton.block.AddrStd import org.ton.block.Coins import org.ton.block.MsgAddressInt +import org.ton.block.StateInit import org.ton.cell.Cell import org.ton.cell.CellBuilder import org.ton.contract.wallet.WalletTransfer @@ -48,18 +49,23 @@ private fun RawMessageEntity.rebuildBodyWithCustomExcessesAccount( } private fun RawMessageEntity.rebuildJettonTransferWithCustomPayload( - customPayload: Cell, + newCustomPayload: Cell, ): Cell { val slice = payload.beginParse() val opCode = slice.loadOpCode() if (opCode != TONOpCode.JETTON_TRANSFER) { - return customPayload + return newCustomPayload } + val queryId = slice.loadUInt(64) val jettonAmount = slice.loadTlb(Coins.tlbCodec()) val receiverAddress = slice.loadTlb(AddrStd.tlbCodec()) val excessesAddress = slice.loadTlb(AddrStd.tlbCodec()) - slice.loadMaybeRef() + val customPayload = slice.loadMaybeRef() + if (customPayload != null) { + return payload + } + val forwardAmount = slice.loadTlb(Coins.tlbCodec()).amount.toLong() val forwardBody = slice.loadMaybeRef() @@ -70,22 +76,39 @@ private fun RawMessageEntity.rebuildJettonTransferWithCustomPayload( queryId = queryId, forwardAmount = forwardAmount, forwardPayload = forwardBody, - customPayload = customPayload + customPayload = newCustomPayload ) } +fun RawMessageEntity.getJettonAddress(): AddrStd? { + val slice = payload.beginParse() + val opCode = slice.loadOpCode() + if (opCode != TONOpCode.JETTON_TRANSFER) { + return null + } + slice.loadUInt(64) + slice.loadTlb(Coins.tlbCodec()) + slice.loadTlb(AddrStd.tlbCodec()) + val excessesAddress = slice.loadTlb(AddrStd.tlbCodec()) + val customPayload = slice.loadMaybeRef() + if (customPayload != null) { + return null + } + return excessesAddress +} fun RawMessageEntity.getWalletTransfer( excessesAddress: AddrStd? = null, - customPayload: Cell? = null + newStateInit: StateInit? = null, + newCustomPayload: Cell? = null, ): WalletTransfer { val builder = WalletTransferBuilder() - builder.stateInit = stateInit + builder.stateInit = newStateInit ?: stateInit builder.destination = address builder.body = if (excessesAddress != null) { rebuildBodyWithCustomExcessesAccount(excessesAddress) - } else if (customPayload != null) { - rebuildJettonTransferWithCustomPayload(customPayload) + } else if (newCustomPayload != null) { + rebuildJettonTransferWithCustomPayload(newCustomPayload) } else { payload } diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/SignRequestEntity.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/SignRequestEntity.kt index 45c50d61e..acd28fb02 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/SignRequestEntity.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/extensions/SignRequestEntity.kt @@ -1,8 +1,33 @@ package com.tonapps.tonkeeper.extensions +import com.tonapps.blockchain.ton.extensions.toAccountId +import com.tonapps.wallet.api.API +import com.tonapps.wallet.data.account.entities.WalletEntity import com.tonapps.wallet.data.core.entity.SignRequestEntity +import com.tonapps.wallet.data.token.entities.AccountTokenEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.ton.block.AddrStd import org.ton.contract.wallet.WalletTransfer -suspend fun SignRequestEntity.getTransfers(): List { - return messages.map { it.getWalletTransfer() } +suspend fun SignRequestEntity.getTransfers( + wallet: WalletEntity, + compressedTokens: List, + excessesAddress: AddrStd? = null, + api: API, +): List = withContext(Dispatchers.IO) { + val transfers = mutableListOf() + for (message in messages) { + val jettonCustomPayload = message.getJettonAddress()?.toAccountId()?.let { + api.getJettonCustomPayload(wallet.accountId, wallet.testnet, it) + } + + val transfer = message.getWalletTransfer( + excessesAddress = excessesAddress, + newStateInit = jettonCustomPayload?.stateInit, + newCustomPayload = jettonCustomPayload?.customPayload, + ) + transfers.add(transfer) + } + transfers } \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/KoinModule.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/KoinModule.kt index 4c592d986..4d08da27b 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/KoinModule.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/koin/KoinModule.kt @@ -8,7 +8,7 @@ import com.tonapps.tonkeeper.manager.tx.TransactionManager import com.tonapps.tonkeeper.core.history.HistoryHelper import com.tonapps.tonkeeper.ui.screen.main.MainViewModel import com.tonapps.tonkeeper.ui.screen.root.RootViewModel -import com.tonapps.tonkeeper.sign.SignManager +import com.tonapps.tonkeeper.manager.SignManager import com.tonapps.tonkeeper.ui.base.BaseWalletVM import com.tonapps.tonkeeper.ui.screen.add.imprt.ImportWalletViewModel import com.tonapps.tonkeeper.ui.screen.battery.BatteryViewModel diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/AssetsManager.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/AssetsManager.kt index d29464bb7..9a4c22ee4 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/AssetsManager.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/AssetsManager.kt @@ -21,39 +21,54 @@ class AssetsManager( suspend fun getAssets( wallet: WalletEntity, - currency: WalletCurrency = settingsRepository.currency + currency: WalletCurrency = settingsRepository.currency, + refresh: Boolean, ): List? { - val tokens = getTokens(wallet, currency) ?: return null - val staked = getStaked(wallet, tokens.map { it.token }, currency) + val tokens = getTokens(wallet, currency, refresh) + val staked = getStaked(wallet, tokens.map { it.token }, currency, refresh) + val liquid = staked.find { it.isTonstakers }?.liquidToken val filteredTokens = if (liquid == null) { tokens } else { tokens.filter { !liquid.token.address.contains(it.address) } } - return (filteredTokens + staked).sortedBy { it.fiat }.reversed() + val list = (filteredTokens + staked).sortedBy { it.fiat }.reversed() + if (list.isEmpty()) { + return null + } + return list } private suspend fun getTokens( wallet: WalletEntity, - currency: WalletCurrency = settingsRepository.currency - ): List? { - val tokens = tokenRepository.get(currency, wallet.accountId, wallet.testnet) ?: return null + currency: WalletCurrency = settingsRepository.currency, + refresh: Boolean, + ): List { + val tokens = tokenRepository.get(currency, wallet.accountId, wallet.testnet, refresh) ?: return emptyList() return tokens.map { AssetsEntity.Token(it) } } private suspend fun getStaked( wallet: WalletEntity, tokens: List, - currency: WalletCurrency = settingsRepository.currency + currency: WalletCurrency = settingsRepository.currency, + refresh: Boolean, ): List { - val staking = getStaking(wallet) + val staking = getStaking(wallet, refresh) val staked = StakedEntity.create(staking, tokens, currency, ratesRepository) return staked.map { AssetsEntity.Staked(it) } } - private suspend fun getStaking(wallet: WalletEntity): StakingEntity { - return stakingRepository.get(wallet.accountId, wallet.testnet) + private suspend fun getStaking( + wallet: WalletEntity, + refresh: Boolean + ): StakingEntity { + return stakingRepository.get( + accountId = wallet.accountId, + testnet = wallet.testnet, + ignoreCache = refresh + ) } } diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/sign/SignManager.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/SignManager.kt similarity index 80% rename from apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/sign/SignManager.kt rename to apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/SignManager.kt index 6d496d5e7..c1a576630 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/sign/SignManager.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/SignManager.kt @@ -1,7 +1,6 @@ -package com.tonapps.tonkeeper.sign +package com.tonapps.tonkeeper.manager import android.os.CancellationSignal -import android.util.Log import com.tonapps.blockchain.ton.extensions.EmptyPrivateKeyEd25519 import com.tonapps.tonkeeper.core.AnalyticsHelper import com.tonapps.tonkeeper.core.SendBlockchainException @@ -21,6 +20,8 @@ import com.tonapps.wallet.data.core.entity.SignRequestEntity import com.tonapps.wallet.data.rates.RatesRepository import com.tonapps.wallet.data.settings.BatteryTransaction import com.tonapps.wallet.data.settings.SettingsRepository +import com.tonapps.wallet.data.token.TokenRepository +import com.tonapps.wallet.data.token.entities.AccountTokenEntity import com.tonapps.wallet.localization.Localization import kotlinx.coroutines.suspendCancellableCoroutine import uikit.navigation.Navigation @@ -35,6 +36,7 @@ class SignManager( private val historyHelper: HistoryHelper, private val batteryRepository: BatteryRepository, private val transactionManager: TransactionManager, + private val tokenRepository: TokenRepository, ) { suspend fun action( @@ -45,18 +47,28 @@ class SignManager( batteryTxType: BatteryTransaction? = null, forceRelayer: Boolean = false, ): String { - Log.d("ActionLog", "SignManager.action #1") navigation.toastLoading(true) + + val compressedTokens = tokenRepository.get(settingsRepository.currency, wallet.accountId, wallet.testnet)?.filter { it.isCompressed } ?: emptyList() + var isBattery = batteryTxType != null && settingsRepository.batteryIsEnabledTx(wallet.accountId, batteryTxType) - Log.d("ActionLog", "SignManager.action #2") val details: HistoryHelper.Details? if (isBattery || forceRelayer) { - val result = emulateBattery(request, wallet, forceRelayer = forceRelayer) + val result = emulateBattery( + request = request, + wallet = wallet, + forceRelayer = forceRelayer, + compressedTokens = compressedTokens + ) details = result.first isBattery = result.second } else { - details = emulate(request, wallet) + details = emulate( + request = request, + wallet = wallet, + compressedTokens = compressedTokens + ) } navigation.toastLoading(false) @@ -101,6 +113,7 @@ class SignManager( request: SignRequestEntity, wallet: WalletEntity, currency: WalletCurrency = settingsRepository.currency, + compressedTokens: List, ): HistoryHelper.Details? { val rates = ratesRepository.getRates(currency, "TON") @@ -118,7 +131,7 @@ class SignManager( seqno = seqno, privateKeyEd25519 = EmptyPrivateKeyEd25519, validUntil = validUntil, - transfers = request.getTransfers() + transfers = request.getTransfers(wallet, compressedTokens, api = api) ) val emulated = api.emulate(cell, wallet.testnet) ?: return null historyHelper.create(wallet, emulated, rates) @@ -132,6 +145,7 @@ class SignManager( wallet: WalletEntity, currency: WalletCurrency = settingsRepository.currency, forceRelayer: Boolean, + compressedTokens: List, ): Pair { return try { if (api.config.isBatteryDisabled) { @@ -142,7 +156,14 @@ class SignManager( val rates = ratesRepository.getRates(currency, "TON") val seqno = accountRepository.getSeqno(wallet) - val cell = accountRepository.createSignedMessage(wallet, seqno, EmptyPrivateKeyEd25519, request.validUntil, request.getTransfers(), internalMessage = true) + val cell = accountRepository.createSignedMessage( + wallet = wallet, + seqno = seqno, + privateKeyEd25519 = EmptyPrivateKeyEd25519, + validUntil = request.validUntil, + transfers = request.getTransfers(wallet, compressedTokens, api = api), + internalMessage = true + ) val (consequences, withBattery) = batteryRepository.emulate( tonProofToken = tonProofToken, @@ -155,7 +176,7 @@ class SignManager( val details = historyHelper.create(wallet, consequences, rates, isBattery = true) Pair(details, withBattery) } catch (e: Throwable) { - Pair(emulate(request, wallet, currency), false) + Pair(emulate(request, wallet, currency, compressedTokens), false) } } } \ No newline at end of file diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tx/TransactionManager.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tx/TransactionManager.kt index bbc6d5fd9..875e38dbe 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tx/TransactionManager.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/manager/tx/TransactionManager.kt @@ -89,7 +89,7 @@ class TransactionManager( emit(initialTx) if (initialTx.pending) { - delay(15.seconds) + delay(20.seconds) while (currentCoroutineContext().isActive) { val finalTx = getTransaction(wallet, hash) if (finalTx == null || finalTx.pending) { diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/action/ActionViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/action/ActionViewModel.kt index d1de49cac..465a74d3d 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/action/ActionViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/action/ActionViewModel.kt @@ -18,6 +18,8 @@ import com.tonapps.wallet.data.account.entities.WalletEntity import com.tonapps.wallet.data.account.AccountRepository import com.tonapps.wallet.data.battery.BatteryRepository import com.tonapps.wallet.data.passcode.PasscodeManager +import com.tonapps.wallet.data.settings.SettingsRepository +import com.tonapps.wallet.data.token.TokenRepository import com.tonapps.wallet.localization.Localization import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -39,6 +41,8 @@ class ActionViewModel( private val api: API, private val batteryRepository: BatteryRepository, private val transactionManager: TransactionManager, + private val tokenRepository: TokenRepository, + private val settingsRepository: SettingsRepository, ) : BaseWalletVM(app) { private val _walletFlow = MutableStateFlow(null) @@ -54,7 +58,9 @@ class ActionViewModel( wallet: WalletEntity, seqno: Int, ): String { - val transactions = args.request.getTransfers().map { transfer -> + val compressedTokens = tokenRepository.get(settingsRepository.currency, wallet.accountId, wallet.testnet)?.filter { it.isCompressed } ?: emptyList() + + val transactions = args.request.getTransfers(wallet, compressedTokens, api = api).map { transfer -> Transaction.fromWalletTransfer( transfer, seqno = seqno, @@ -84,12 +90,14 @@ class ActionViewModel( wallet: WalletEntity, seqno: Int, ): String { + val compressedTokens = tokenRepository.get(settingsRepository.currency, wallet.accountId, wallet.testnet)?.filter { it.isCompressed } ?: emptyList() + val secretKey = accountRepository.getPrivateKey(wallet.id) val excessesAddress = if (args.isBattery) { batteryRepository.getConfig(wallet.testnet).excessesAddress } else null - val transfers = args.messages.map { it.getWalletTransfer(excessesAddress) } + val transfers = args.request.getTransfers(wallet, compressedTokens, excessesAddress, api) val message = accountRepository.createSignedMessage( wallet, diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppScreen.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppScreen.kt index 13a6190d5..453541083 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppScreen.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/browser/dapp/DAppScreen.kt @@ -168,13 +168,11 @@ class DAppScreen(wallet: WalletEntity): BaseWalletScreen(R headerView.updateLayoutParams { topMargin = statusInsets.top } - val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()) + val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.ime() or WindowInsetsCompat.Type.navigationBars()) refreshView.updateLayoutParams { - bottomMargin = imeInsets.bottom + bottomMargin = bottomInsets.bottom } - val navInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) - webView.setPaddingBottom(navInsets.bottom) insets } } diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/collectibles/CollectiblesViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/collectibles/CollectiblesViewModel.kt index c1875c717..4275307ab 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/collectibles/CollectiblesViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/collectibles/CollectiblesViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.tonapps.extensions.flattenFirst import com.tonapps.network.NetworkMonitor import com.tonapps.tonkeeper.extensions.with +import com.tonapps.tonkeeper.manager.tx.TransactionManager import com.tonapps.tonkeeper.ui.base.UiListState import com.tonapps.tonkeeper.ui.base.BaseWalletVM import com.tonapps.tonkeeper.ui.screen.collectibles.list.Item @@ -32,14 +33,16 @@ class CollectiblesViewModel( private val wallet: WalletEntity, private val collectiblesRepository: CollectiblesRepository, private val networkMonitor: NetworkMonitor, - private val settingsRepository: SettingsRepository + private val settingsRepository: SettingsRepository, + private val transactionManager: TransactionManager, ): BaseWalletVM(app) { val uiListStateFlow = combine( networkMonitor.isOnlineFlow, settingsRepository.hiddenBalancesFlow, settingsRepository.tokenPrefsChangedFlow, - ) { isOnline, hiddenBalances, _ -> + transactionManager.getEventsFlow(wallet), + ) { isOnline, hiddenBalances, _, _ -> stateFlow( wallet = wallet, hiddenBalances = hiddenBalances, diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/InitViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/InitViewModel.kt index f12711102..7fdaff0c4 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/InitViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/InitViewModel.kt @@ -333,22 +333,24 @@ class InitViewModel( return label } - fun getLabel(): Wallet.Label { - return savedState.label ?: Wallet.Label( - accountName = "", + suspend fun getLabel(): Wallet.Label = withContext(Dispatchers.IO) { + savedState.label ?: Wallet.Label( + accountName = getDefaultWalletName(), emoji = Emoji.WALLET_ICON, color = WalletColor.all.first() ) } fun setLabelName(name: String) { - val oldLabel = getLabel() - val emoji = Emoji.getEmojiFromPrefix(name) ?: oldLabel.emoji - - setLabel(oldLabel.copy( - accountName = name.replace(emoji.toString(), "").trim(), - emoji = emoji - )) + viewModelScope.launch { + val oldLabel = getLabel() + val emoji = Emoji.getEmojiFromPrefix(name) ?: oldLabel.emoji + + setLabel(oldLabel.copy( + accountName = name.replace(emoji.toString(), "").trim(), + emoji = emoji + )) + } } fun nextStep(context: Context, from: InitRoute) { @@ -369,12 +371,13 @@ class InitViewModel( } private fun applyNameFromSelectedAccounts() { - val selected = getSelectedAccounts().filter { !it.name.isNullOrBlank() } - if (selected.isEmpty()) { - return + viewModelScope.launch { + val walletName = getSelectedAccounts().firstOrNull { + !it.name.isNullOrBlank() + }?.name ?: getDefaultWalletName() + + setLabelName(walletName) } - val name = selected.first().name ?: return - setLabelName(name) } private fun execute(context: Context) { diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/step/LabelScreen.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/step/LabelScreen.kt index b7a4b1e08..7bd3d319f 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/step/LabelScreen.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/init/step/LabelScreen.kt @@ -43,12 +43,15 @@ class LabelScreen: BaseFragment(R.layout.fragment_init_label) { view.updatePadding(top = it) } - val label = initViewModel.getLabel() - with(editorView) { - name = label.name - emoji = label.emoji - color = label.color + lifecycleScope.launch { + val label = initViewModel.getLabel() + with(editorView) { + name = label.name + emoji = label.emoji + color = label.color + } } + } override fun onResume() { diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/root/RootViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/root/RootViewModel.kt index 787e87225..f3dcc9f14 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/root/RootViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/root/RootViewModel.kt @@ -19,7 +19,7 @@ import com.tonapps.tonkeeper.core.signer.SingerArgs import com.tonapps.tonkeeper.core.widget.Widget import com.tonapps.tonkeeper.helper.ShortcutHelper import com.tonapps.wallet.data.push.GooglePushService -import com.tonapps.tonkeeper.sign.SignManager +import com.tonapps.tonkeeper.manager.SignManager import com.tonapps.tonkeeper.ui.base.BaseWalletVM import com.tonapps.tonkeeper.ui.screen.init.list.AccountItem import com.tonapps.tonkeeper.ui.screen.main.MainScreen diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt index 8e1f8e87b..a15d094d1 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/send/main/SendViewModel.kt @@ -454,12 +454,6 @@ class SendViewModel( retryWithoutRelayer: Boolean = false, ignoreGasless: Boolean = false, ): Pair = withContext(Dispatchers.IO) { - val fakePrivateKey = if (transfer.commentEncrypted) { - PrivateKeyEd25519() - } else { - EmptyPrivateKeyEd25519 - } - val wallet = transfer.wallet val withRelayer = shouldAttemptWithRelayer(transfer) val tonProofToken = accountRepository.requestTonProofToken(wallet) @@ -474,66 +468,95 @@ class SendViewModel( wallet.isSupportedFeature(WalletFeature.GASLESS) && tonProofToken != null && excessesAddress != null && isGaslessToken val isPreferGasless = batteryRepository.getPreferGasless(wallet.testnet) - if (withRelayer && !retryWithoutRelayer && tonProofToken != null) { + if (ignoreGasless && retryWithoutRelayer) { + calculateFeeDefault(transfer, isSupportsGasless) + } else if (withRelayer && !retryWithoutRelayer && tonProofToken != null && excessesAddress != null) { try { - if (api.config.isBatteryDisabled) { - throw IllegalStateException("Battery is disabled") - } - - val message = transfer.toSignedMessage(fakePrivateKey, true) - - val (consequences, _) = batteryRepository.emulate( - tonProofToken = tonProofToken, - publicKey = wallet.publicKey, - testnet = wallet.testnet, - boc = message - ) ?: throw IllegalStateException("Failed to emulate battery") - - sendTransferType = SendTransferType.Battery(excessesAddress!!) - - Pair(Coins.of(consequences.totalFees), isSupportsGasless) + calculateFeeBattery(transfer, excessesAddress, isSupportsGasless, tonProofToken) } catch (e: Throwable) { calculateFee(transfer, retryWithoutRelayer = true) } } else if (!ignoreGasless && isPreferGasless && isSupportsGasless && tonProofToken != null && excessesAddress != null) { try { - val message = transfer.toSignedMessage( - privateKey = fakePrivateKey, - internalMessage = true, - additionalGifts = listOf( - transfer.gaslessInternalGift( - jettonAmount = Coins.ONE, batteryAddress = excessesAddress - ) - ), - excessesAddress = excessesAddress, - ) - - val commission = api.estimateGaslessCost( - tonProofToken = tonProofToken, - jettonMaster = tokenAddress, - cell = message, - testnet = wallet.testnet, - ) ?: throw IllegalStateException("Failed to estimate gasless cost") - - sendTransferType = SendTransferType.Gasless( - excessesAddress = excessesAddress, - gaslessFee = Coins.of(commission, transfer.token.decimals) - ) - - Pair(Coins.ofNano(commission, transfer.token.decimals), true) + calculateFeeGasless(transfer, excessesAddress, tonProofToken, tokenAddress) } catch (e: Throwable) { calculateFee(transfer, ignoreGasless = true) } } else { - val message = transfer.toSignedMessage(fakePrivateKey, false) - val emulated = api.emulate(message, transfer.wallet.testnet) + calculateFeeDefault(transfer, isSupportsGasless) + } + } + + private suspend fun calculateFeeBattery( + transfer: TransferEntity, + excessesAddress: AddrStd, + isSupportsGasless: Boolean, + tonProofToken: String, + ): Pair { + if (api.config.isBatteryDisabled) { + return calculateFeeDefault(transfer, isSupportsGasless) + } - val fee = emulated?.totalFees ?: 0 + val message = transfer.toSignedMessage(internalMessage = true) - sendTransferType = SendTransferType.Default + val (consequences, _) = batteryRepository.emulate( + tonProofToken = tonProofToken, + publicKey = wallet.publicKey, + testnet = wallet.testnet, + boc = message + ) ?: return calculateFeeDefault(transfer, isSupportsGasless) - Pair(Coins.of(fee), isSupportsGasless) - } + sendTransferType = SendTransferType.Battery(excessesAddress) + + return Pair(Coins.of(consequences.totalFees), isSupportsGasless) + } + + private suspend fun calculateFeeGasless( + transfer: TransferEntity, + excessesAddress: AddrStd, + tonProofToken: String, + tokenAddress: String + ): Pair { + val message = transfer.toSignedMessage( + internalMessage = true, + additionalGifts = listOf( + transfer.gaslessInternalGift( + jettonAmount = Coins.ONE, + batteryAddress = excessesAddress + ) + ), + excessesAddress = excessesAddress, + ) + + val commission = api.estimateGaslessCost( + tonProofToken = tonProofToken, + jettonMaster = tokenAddress, + cell = message, + testnet = wallet.testnet, + ) ?: return calculateFeeDefault(transfer, true) + + sendTransferType = SendTransferType.Gasless( + excessesAddress = excessesAddress, + gaslessFee = Coins.of(commission, transfer.token.decimals) + ) + + return Pair(Coins.ofNano(commission, transfer.token.decimals), true) + } + + private suspend fun calculateFeeDefault( + transfer: TransferEntity, + isSupportsGasless: Boolean, + ): Pair { + val message = transfer.toSignedMessage( + internalMessage = false + ) + val emulated = api.emulate(message, transfer.wallet.testnet) + + val fee = emulated?.totalFees ?: 0 + + sendTransferType = SendTransferType.Default + + return Pair(Coins.of(fee), isSupportsGasless) } private suspend fun eventFee( diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/State.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/State.kt index a529b3d87..7a0a35ca6 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/State.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/State.kt @@ -82,6 +82,8 @@ sealed class State { val assets: Assets, val hasBackup: Boolean, val battery: Battery, + val lt: Long?, + val isOnline: Boolean, ): State() { private val totalBalanceFormat: CharSequence diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/WalletViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/WalletViewModel.kt index 7e802c02d..15dc8dd01 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/WalletViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/main/WalletViewModel.kt @@ -11,6 +11,7 @@ import com.tonapps.tonkeeper.core.entities.AssetsEntity.Companion.sort import com.tonapps.tonkeeper.core.entities.StakedEntity import com.tonapps.tonkeeper.extensions.hasPushPermission import com.tonapps.tonkeeper.helper.DateHelper +import com.tonapps.tonkeeper.manager.AssetsManager import com.tonapps.tonkeeper.manager.tx.TransactionManager import com.tonapps.tonkeeper.ui.base.BaseWalletVM import com.tonapps.tonkeeper.ui.screen.wallet.main.list.Item @@ -36,10 +37,12 @@ import com.tonapps.wallet.data.token.entities.AccountTokenEntity import com.tonapps.wallet.data.tonconnect.TonConnectRepository import com.tonapps.wallet.data.tonconnect.entities.DConnectEntity import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filterNotNull @@ -51,6 +54,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import uikit.extensions.collectFlow import uikit.extensions.context +import kotlin.time.Duration.Companion.seconds class WalletViewModel( app: Application, @@ -68,7 +72,8 @@ class WalletViewModel( private val ratesRepository: RatesRepository, private val batteryRepository: BatteryRepository, private val billingManager: BillingManager, - private val transactionManager: TransactionManager + private val transactionManager: TransactionManager, + private val assetsManager: AssetsManager, ): BaseWalletVM(app) { private val alertNotificationsFlow = MutableStateFlow>(emptyList()) @@ -107,7 +112,7 @@ class WalletViewModel( } }.map { !it } - private val _streamFLow = combine(updateWalletSettings, _lastLtFlow) { _, _ -> } + private val _streamFlow = combine(updateWalletSettings, _lastLtFlow) { _, lastLt -> lastLt } init { collectFlow(transactionManager.getEventsFlow(wallet)) { event -> @@ -133,11 +138,17 @@ class WalletViewModel( settingsRepository.currencyFlow, backupRepository.stream, networkMonitor.isOnlineFlow, - _streamFLow, - ) { currency, backups, isOnline, _ -> - if (isOnline) { + _streamFlow, + ) { currency, backups, currentIsOnline, currentLt -> + val lastLt = _stateMainFlow.value?.lt ?: 0 + val lastIsOnline = _stateMainFlow.value?.isOnline + + val isRequestUpdate = _stateMainFlow.value == null || lastLt != currentLt || lastIsOnline != currentIsOnline + + if (isRequestUpdate) { setStatus(Status.Updating) } + _uiLabelFlow.value = wallet.label val hasBackup = if (!wallet.hasPrivateKey) { @@ -148,7 +159,7 @@ class WalletViewModel( val walletCurrency = getCurrency(wallet, currency) - val localAssets = getLocalAssets(walletCurrency, wallet) + val localAssets = getAssets(walletCurrency, false) val batteryBalance = getBatteryBalance(wallet) if (localAssets != null) { _stateMainFlow.value = State.Main( @@ -161,11 +172,13 @@ class WalletViewModel( disabled = api.config.batteryDisabled, viewed = settingsRepository.batteryViewed, ), + lt = currentLt, + isOnline = currentIsOnline, ) } - if (isOnline) { - val remoteAssets = getRemoteAssets(walletCurrency, wallet) + if (isRequestUpdate) { + val remoteAssets = getAssets(walletCurrency, true) val batteryBalance = getBatteryBalance(wallet, true) if (remoteAssets != null) { _stateMainFlow.value = State.Main( @@ -178,6 +191,8 @@ class WalletViewModel( disabled = api.config.batteryDisabled, viewed = settingsRepository.batteryViewed, ), + lt = currentLt, + isOnline = currentIsOnline, ) settingsRepository.setWalletLastUpdated(wallet.id) setStatus(Status.Default) @@ -275,58 +290,18 @@ class WalletViewModel( return@withContext battery.balance } - private suspend fun getLocalAssets( + private suspend fun getAssets( currency: WalletCurrency, - wallet: WalletEntity + refresh: Boolean ): State.Assets? = withContext(Dispatchers.IO) { - val tokens = tokenRepository.getLocal(currency, wallet.accountId, wallet.testnet) - if (tokens.isEmpty()) { - return@withContext null - } - val initializedAccount = tokens.firstOrNull()?.balance?.initializedAccount ?: false - val staking = stakingRepository.get(wallet.accountId, wallet.testnet, initializedAccount = initializedAccount) - buildStateTokens(wallet, currency, tokens, staking, true) - } - - private suspend fun getRemoteAssets( - currency: WalletCurrency, - wallet: WalletEntity - ): State.Assets? = withContext(Dispatchers.IO) { - try { - val tokens = tokenRepository.getRemote(currency, wallet.accountId, wallet.testnet) ?: return@withContext null - val initializedAccount = tokens.first().balance.initializedAccount - val staking = stakingRepository.get(wallet.accountId, wallet.testnet, ignoreCache = true, initializedAccount = initializedAccount) - buildStateTokens(wallet, currency, tokens, staking, false) - } catch (e: Throwable) { - return@withContext null - } - } - - private suspend fun buildStateTokens( - wallet: WalletEntity, - currency: WalletCurrency, - tokens: List, - staking: StakingEntity, - fromCache: Boolean - ): State.Assets? { - val fiatRates = ratesRepository.getTONRates(currency) - val staked = StakedEntity.create(staking, tokens, currency, ratesRepository) - val liquid = staked.find { it.isTonstakers }?.liquidToken - - val filteredTokens = if (liquid == null) { - tokens - } else { - tokens.filter { !liquid.token.address.contains(it.address) } - } - if (filteredTokens.isEmpty() && staked.isEmpty()) { - return null + assetsManager.getAssets(wallet, currency, refresh)?.let { + State.Assets( + currency = currency, + list = it.sort(wallet, settingsRepository), + fromCache = !refresh, + rates = ratesRepository.getTONRates(currency) + ) } - - val assets = (filteredTokens.map { AssetsEntity.Token(it) } + staked.map { - AssetsEntity.Staked(it) - }).sortedBy { it.fiat }.reversed() - - return State.Assets(currency, assets.sort(wallet, settingsRepository), fromCache, fiatRates) } private fun getApps( diff --git a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/picker/PickerViewModel.kt b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/picker/PickerViewModel.kt index ac7b57e85..8eab3c41c 100644 --- a/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/picker/PickerViewModel.kt +++ b/apps/wallet/instance/app/src/main/java/com/tonapps/tonkeeper/ui/screen/wallet/picker/PickerViewModel.kt @@ -104,7 +104,10 @@ class PickerViewModel( private suspend fun getAssets( wallet: WalletEntity - ) = assetsManager.getAssets(wallet)?.sort(wallet, settingsRepository) + ) = assetsManager.getAssets( + wallet = wallet, + refresh = false + )?.sort(wallet, settingsRepository) private suspend fun getBalance( wallet: WalletEntity