Updated on 2026-08-14

This commit is contained in:
Tangem 2023-03-06 18:19:26 +03:00
parent fec81d0697
commit 25d39f9f0b
11 changed files with 81 additions and 71 deletions

View file

@ -84,7 +84,6 @@ class IntentHandler {
val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL)
if (data.host == successUri.host && data.authority == successUri.authority) {
val currency = store.state.walletState.selectedCurrency ?: return
val currencyType = AnalyticsParam.CurrencyType.Currency(currency)
Analytics.send(Token.Bought(currencyType))
}

View file

@ -161,7 +161,11 @@ class TradeCryptoMiddleware {
private fun openSwap() {
val currency = store.state.walletState.selectedWalletData?.currency?.toSwapCurrency()
val bundle = bundleOf(SwapFragment.CURRENCY_BUNDLE_KEY to Json.encodeToString(currency))
val bundle =
bundleOf(
SwapFragment.CURRENCY_BUNDLE_KEY to Json.encodeToString(currency),
SwapFragment.DERIVATION_PATH to store.state.walletState.selectedWalletData?.currency?.derivationPath
)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Swap, bundle = bundle))
}

View file

@ -48,9 +48,10 @@ class TransactionManagerImpl(
gasLimit: Int,
destinationAddress: String,
dataToSign: String,
derivationPath: String?,
): SendTxResult {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain)
val walletManager = getActualWalletManager(blockchain, derivationPath)
walletManager.update()
val amount = Amount(value = BigDecimal.ZERO, blockchain = blockchain)
return sendTransactionInternal(
@ -73,9 +74,10 @@ class TransactionManagerImpl(
dataToSign: String,
isSwap: Boolean,
currencyToSend: Currency,
derivationPath: String?,
): SendTxResult {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain)
val walletManager = getActualWalletManager(blockchain, derivationPath)
walletManager.update()
val amount = if (isSwap) {
createAmountForSwap(amountToSend, currencyToSend, blockchain)
@ -129,9 +131,9 @@ class TransactionManagerImpl(
return Blockchain.fromNetworkId(networkId)?.decimals() ?: error("blockchain not found")
}
override suspend fun updateWalletManager(networkId: String) {
override suspend fun updateWalletManager(networkId: String, derivationPath: String?) {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
getActualWalletManager(blockchain).update()
getActualWalletManager(blockchain, derivationPath).update()
}
override fun calculateFee(networkId: String, gasPrice: String, estimatedGas: Int): BigDecimal {
@ -147,9 +149,10 @@ class TransactionManagerImpl(
currencyToSend: Currency,
destinationAddress: String,
data: String?,
derivationPath: String?,
): ProxyFee {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain)
val walletManager = getActualWalletManager(blockchain, derivationPath)
if (walletManager is EthereumWalletManager) {
val gasLimit = getGasLimit(
evmWalletManager = walletManager,
@ -293,19 +296,10 @@ class TransactionManagerImpl(
}
}
private fun getActualWalletManager(blockchain: Blockchain): WalletManager {
val card = appStateHolder.getActualCard()
if (card != null) {
val blockchainNetwork = BlockchainNetwork(blockchain, card)
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
if (walletManager != null) {
return walletManager
} else {
error("no wallet manager found")
}
} else {
error("card not found")
}
private fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager {
val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList())
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
return requireNotNull(walletManager) { "no wallet manager found" }
}
private fun createExtras(

View file

@ -85,23 +85,17 @@ class UserWalletManagerImpl(
?: ""
}
override suspend fun isTokenAdded(currency: Currency): Boolean {
val card = requireNotNull(appStateHolder.getActualCard()) { "card is null" }
override suspend fun isTokenAdded(currency: Currency, derivationPath: String?): Boolean {
val blockchain = requireNotNull(Blockchain.fromNetworkId(currency.networkId)) { "blockchain not found" }
val blockchainNetwork = BlockchainNetwork(blockchain, card)
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
if (walletManager != null) {
return walletManager.cardTokens.any {
it.id == currency.id
}
val walletManager = getActualWalletManager(blockchain, derivationPath)
return walletManager.cardTokens.any {
it.id == currency.id
}
return false
}
override suspend fun addToken(currency: Currency) {
val card = requireNotNull(appStateHolder.getActualCard()) { "card not found" }
override suspend fun addToken(currency: Currency, derivationPath: String?) {
val blockchain = requireNotNull(Blockchain.fromNetworkId(currency.networkId)) { "blockchain not found" }
val blockchainNetwork = BlockchainNetwork(blockchain, card)
val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList())
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
Timber.e("Unable to add token, no user wallet selected")
@ -113,34 +107,27 @@ class UserWalletManagerImpl(
)
}
override fun getWalletAddress(networkId: String): String {
override fun getWalletAddress(networkId: String, derivationPath: String?): String {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val card = requireNotNull(appStateHolder.getActualCard()) { "card not found" }
val blockchainNetwork = BlockchainNetwork(blockchain, card)
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
if (walletManager != null) {
return walletManager.wallet.address
} else {
error("no wallet manager found")
}
val walletManager = getActualWalletManager(blockchain, derivationPath)
return walletManager.wallet.address
}
override fun getLastTransactionHash(networkId: String): String? {
override fun getLastTransactionHash(networkId: String, derivationPath: String?): String? {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val card = requireNotNull(appStateHolder.getActualCard()) { "card not found" }
val blockchainNetwork = BlockchainNetwork(blockchain, card)
val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork)
return walletManager?.wallet?.recentTransactions
?.lastOrNull { it.hash?.isNotEmpty() == true }
val walletManager = getActualWalletManager(blockchain, derivationPath)
return walletManager.wallet.recentTransactions
.lastOrNull { it.hash?.isNotEmpty() == true }
?.hash?.let { HEX_PREFIX + it }
}
override suspend fun getCurrentWalletTokensBalance(
networkId: String,
extraTokens: List<Currency>,
derivationPath: String?,
): Map<String, ProxyAmount> {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain)
val walletManager = getActualWalletManager(blockchain, derivationPath)
// workaround for get balance for tokens that doesn't exist in wallet
val extraTokensToLoadBalance = extraTokens
@ -165,9 +152,9 @@ class UserWalletManagerImpl(
return balances
}
override fun getNativeTokenBalance(networkId: String): ProxyAmount? {
override fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? {
val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
val walletManager = getActualWalletManager(blockchain)
val walletManager = getActualWalletManager(blockchain, derivationPath)
return walletManager.wallet.amounts.firstNotNullOfOrNull {
it.takeIf { it.key is AmountType.Coin }
}?.value?.let {
@ -198,9 +185,8 @@ class UserWalletManagerImpl(
appStateHolder.mainStore?.dispatchOnMain(WalletAction.LoadData.Refresh)
}
private fun getActualWalletManager(blockchain: Blockchain): WalletManager {
val card = requireNotNull(appStateHolder.getActualCard()) { "card not found" }
val blockchainNetwork = BlockchainNetwork(blockchain, card)
private fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager {
val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList())
return requireNotNull(appStateHolder.walletState?.getWalletManager(blockchainNetwork)) {
"No wallet manager found"
}

View file

@ -26,7 +26,7 @@ internal class ReferralInteractorImpl(
if (tokensForReferral.isNotEmpty()) {
val currency = tokensConverter.convert(tokensForReferral.first())
deriveOrAddTokens(currency)
val publicAddress = userWalletManager.getWalletAddress(currency.networkId)
val publicAddress = userWalletManager.getWalletAddress(currency.networkId, null)
return repository.startReferral(
walletId = userWalletManager.getWalletId(),
networkId = currency.networkId,
@ -42,8 +42,8 @@ internal class ReferralInteractorImpl(
if (!derivationManager.hasDerivation(currency.networkId)) {
derivationManager.deriveMissingBlockchains(currency)
}
if (!userWalletManager.isTokenAdded(currency)) {
userWalletManager.addToken(currency)
if (!userWalletManager.isTokenAdded(currency, null)) {
userWalletManager.addToken(currency, null)
}
}

View file

@ -11,6 +11,8 @@ import com.tangem.feature.swap.domain.models.ui.TxState
interface SwapInteractor {
fun initDerivationPath(derivationPath: String?)
/**
* Init tokens to swap, load tokens list available to swap for given network
*

View file

@ -39,6 +39,11 @@ internal class SwapInteractorImpl @Inject constructor(
private val cryptoCurrencyConverter = CryptoCurrencyConverter()
private val amountFormatter = AmountFormatter()
private var derivationPath: String? = null
override fun initDerivationPath(derivationPath: String?) {
this.derivationPath = derivationPath
}
override suspend fun initTokensToSwap(initialCurrency: Currency): TokensDataState {
val networkId = initialCurrency.networkId
@ -63,7 +68,7 @@ internal class SwapInteractorImpl @Inject constructor(
.filter {
!loadedOnWalletsMap.contains(it.symbol)
}
val tokensBalance = userWalletManager.getCurrentWalletTokensBalance(networkId, emptyList())
val tokensBalance = userWalletManager.getCurrentWalletTokensBalance(networkId, emptyList(), derivationPath)
.mapValues { SwapAmount(it.value.value, it.value.decimals) }
val appCurrency = userWalletManager.getUserAppCurrency()
val rates = repository.getRates(appCurrency.code, tokensInWallet.map { it.id })
@ -118,11 +123,12 @@ internal class SwapInteractorImpl @Inject constructor(
gasLimit = approveData.gasLimit,
destinationAddress = approveData.approveModel.toAddress,
dataToSign = approveData.approveModel.data,
derivationPath = derivationPath,
)
return when (result) {
is SendTxResult.Success -> {
allowPermissionsHandler.addAddressToInProgress(forTokenContractAddress)
TxState.TxSent(txAddress = userWalletManager.getLastTransactionHash(networkId) ?: "")
TxState.TxSent(txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath) ?: "")
}
SendTxResult.UserCancelledError -> TxState.UserCancelled
is SendTxResult.BlockchainSdkError -> TxState.BlockchainError
@ -149,7 +155,7 @@ internal class SwapInteractorImpl @Inject constructor(
val isAllowedToSpend = checkAllowance(networkId, fromTokenAddress)
if (isAllowedToSpend && allowPermissionsHandler.isAddressAllowanceInProgress(fromTokenAddress)) {
allowPermissionsHandler.removeAddressFromProgress(fromTokenAddress)
transactionManager.updateWalletManager(networkId)
transactionManager.updateWalletManager(networkId, derivationPath)
}
val isBalanceWithoutFeeEnough = isBalanceEnough(fromToken, amount, null)
return if (isAllowedToSpend && isBalanceWithoutFeeEnough) {
@ -192,10 +198,11 @@ internal class SwapInteractorImpl @Inject constructor(
destinationAddress = swapStateData.swapModel.transaction.toWalletAddress,
dataToSign = swapStateData.swapModel.transaction.data,
isSwap = true,
derivationPath = derivationPath,
)
return when (result) {
is SendTxResult.Success -> {
userWalletManager.addToken(cryptoCurrencyConverter.convert(currencyToGet))
userWalletManager.addToken(cryptoCurrencyConverter.convert(currencyToGet), derivationPath)
userWalletManager.refreshWallet()
TxState.TxSent(
fromAmount = amountFormatter.formatSwapAmountToUI(
@ -206,7 +213,7 @@ internal class SwapInteractorImpl @Inject constructor(
swapStateData.swapModel.toTokenAmount,
currencyToGet.symbol,
),
txAddress = userWalletManager.getLastTransactionHash(networkId) ?: "",
txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath) ?: "",
)
}
SendTxResult.UserCancelledError -> TxState.UserCancelled
@ -285,7 +292,7 @@ internal class SwapInteractorImpl @Inject constructor(
val allowance = repository.checkTokensSpendAllowance(
networkId = networkId,
tokenAddress = fromTokenAddress,
walletAddress = userWalletManager.getWalletAddress(networkId),
walletAddress = userWalletManager.getWalletAddress(networkId, derivationPath),
)
return allowance.error == DataError.NoError && allowance.dataModel != ZERO_BALANCE
}
@ -396,6 +403,7 @@ internal class SwapInteractorImpl @Inject constructor(
currencyToSend = cryptoCurrencyConverter.convert(fromToken),
destinationAddress = swapData.transaction.toWalletAddress,
data = swapData.transaction.data,
derivationPath = derivationPath,
)
val feeFiat = getFormattedFiatFee(networkId, feeData.fee.value)
val formattedFee = amountFormatter.formatBigDecimalAmountToUI(
@ -513,6 +521,7 @@ internal class SwapInteractorImpl @Inject constructor(
currencyToSend = userWalletManager.getNativeTokenForNetwork(networkId),
destinationAddress = transactionData.toAddress,
data = transactionData.data,
derivationPath = derivationPath,
)
val feeFiat = getFormattedFiatFee(networkId, feeData.fee.value)
val formattedFee = amountFormatter.formatBigDecimalAmountToUI(
@ -543,6 +552,7 @@ internal class SwapInteractorImpl @Inject constructor(
userWalletManager.getCurrentWalletTokensBalance(
networkId = networkId,
extraTokens = tokensToSync.map { cryptoCurrencyConverter.convert(it) },
derivationPath = derivationPath,
)
cache.cacheBalances(tokensBalance.mapValues { SwapAmount(it.value.value, it.value.decimals) })
}
@ -558,7 +568,7 @@ internal class SwapInteractorImpl @Inject constructor(
}
private fun getWalletAddress(networkId: String): String {
return userWalletManager.getWalletAddress(networkId)
return userWalletManager.getWalletAddress(networkId, derivationPath)
}
private fun getTokenAddress(currency: Currency): String {
@ -581,7 +591,7 @@ internal class SwapInteractorImpl @Inject constructor(
if (fee == null) {
return false
}
val nativeTokenBalance = userWalletManager.getNativeTokenBalance(networkId)
val nativeTokenBalance = userWalletManager.getNativeTokenBalance(networkId, derivationPath)
val percentsToFeeIncrease = BigDecimal.valueOf(INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT)
return when (fromToken) {
is Currency.NativeToken -> {

View file

@ -72,5 +72,6 @@ class SwapFragment : Fragment() {
companion object {
const val CURRENCY_BUNDLE_KEY = "swap_currency"
const val DERIVATION_PATH = "DERIVATION_STYLE"
}
}

View file

@ -52,6 +52,7 @@ internal class SwapViewModel @Inject constructor(
savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY]
?: error("no expected parameter Currency found"),
)
private val derivationPath = savedStateHandle.get<String>(SwapFragment.DERIVATION_PATH)
private val stateBuilder = StateBuilder(
actions = createUiActions(),
@ -78,6 +79,7 @@ internal class SwapViewModel @Inject constructor(
get() = swapRouter.currentScreen
init {
swapInteractor.initDerivationPath(derivationPath)
initTokens(currency)
}

View file

@ -8,6 +8,7 @@ import java.math.BigDecimal
interface TransactionManager {
@Suppress("LongParameterList")
@Throws(IllegalStateException::class)
suspend fun sendApproveTransaction(
networkId: String,
@ -15,6 +16,7 @@ interface TransactionManager {
gasLimit: Int,
destinationAddress: String,
dataToSign: String,
derivationPath: String?,
): SendTxResult
@Suppress("LongParameterList")
@ -28,8 +30,10 @@ interface TransactionManager {
dataToSign: String,
isSwap: Boolean,
currencyToSend: Currency,
derivationPath: String?,
): SendTxResult
@Suppress("LongParameterList")
@Throws(IllegalStateException::class)
suspend fun getFee(
networkId: String,
@ -37,13 +41,14 @@ interface TransactionManager {
currencyToSend: Currency,
destinationAddress: String,
data: String?,
derivationPath: String?,
): ProxyFee
@Throws(IllegalStateException::class)
fun getNativeTokenDecimals(networkId: String): Int
@Throws(IllegalStateException::class)
suspend fun updateWalletManager(networkId: String)
suspend fun updateWalletManager(networkId: String, derivationPath: String?)
fun calculateFee(networkId: String, gasPrice: String, estimatedGas: Int): BigDecimal

View file

@ -29,15 +29,16 @@ interface UserWalletManager {
* @param currency to receive referral payments
*/
@Throws(IllegalStateException::class)
suspend fun isTokenAdded(currency: Currency): Boolean
suspend fun isTokenAdded(currency: Currency, derivationPath: String?): Boolean
/**
* Adds token to wallet if its not
*
* @param currency to add to wallet
* @param derivationPath if null uses default
*/
@Throws(IllegalStateException::class)
suspend fun addToken(currency: Currency)
suspend fun addToken(currency: Currency, derivationPath: String?)
fun refreshWallet()
@ -45,21 +46,27 @@ interface UserWalletManager {
* Returns wallet public address for token
*
* @param networkId for currency
* @param derivationPath if null uses default
*/
@Throws(IllegalStateException::class)
fun getWalletAddress(networkId: String): String
fun getWalletAddress(networkId: String, derivationPath: String?): String
/**
* Return balances from wallet found by networkId
*
* @param networkId
* @param derivationPath if null uses default
* @return map of <Symbol, [ProxyAmount]>
*/
@Throws(IllegalStateException::class)
suspend fun getCurrentWalletTokensBalance(networkId: String, extraTokens: List<Currency>): Map<String, ProxyAmount>
suspend fun getCurrentWalletTokensBalance(
networkId: String,
extraTokens: List<Currency>,
derivationPath: String?,
): Map<String, ProxyAmount>
@Throws(IllegalStateException::class)
fun getNativeTokenBalance(networkId: String): ProxyAmount?
fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount?
/**
* @param networkId
@ -74,5 +81,5 @@ interface UserWalletManager {
fun getUserAppCurrency(): ProxyFiatCurrency
@Throws(IllegalStateException::class)
fun getLastTransactionHash(networkId: String): String?
fun getLastTransactionHash(networkId: String, derivationPath: String?): String?
}