Updated on 2026-08-14
This commit is contained in:
parent
a8e621c2c4
commit
d1883231ed
6 changed files with 75 additions and 29 deletions
|
|
@ -21,6 +21,7 @@ import com.tangem.domain.walletconnect.usecase.method.BlockAidTransactionCheck
|
|||
import com.tangem.domain.walletconnect.usecase.method.SignRequirements
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcSignState
|
||||
import com.tangem.domain.walletconnect.usecase.method.WcTransactionUseCase
|
||||
import com.tangem.lib.crypto.BlockchainUtils.SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -107,7 +108,7 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun isLargeHash(hash: ByteArray): Boolean {
|
||||
return hash.size > LARGE_HASH_SIZE
|
||||
return hash.size > SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES
|
||||
}
|
||||
|
||||
private fun getFormattedHash(hash: ByteArray): ByteArray {
|
||||
|
|
@ -124,10 +125,6 @@ internal class WcSolanaSignTransactionUseCase @AssistedInject constructor(
|
|||
return isLargeHash(data)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val LARGE_HASH_SIZE = 930 // bytes
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ sealed class ExpressDataError {
|
|||
|
||||
abstract val code: Int
|
||||
|
||||
open val message: String? = null
|
||||
|
||||
data class BadRequest(override val code: Int) : ExpressDataError()
|
||||
|
||||
data class SwapsAreUnavailableNowError(override val code: Int) : ExpressDataError()
|
||||
|
|
@ -57,4 +59,9 @@ sealed class ExpressDataError {
|
|||
data object UnknownError : ExpressDataError() {
|
||||
override val code: Int = -1
|
||||
}
|
||||
|
||||
data object TooLargeSolanaTransactionError : ExpressDataError() {
|
||||
override val code: Int = -2
|
||||
override val message: String = "tooLargeSolanaTransaction"
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.swap.domain
|
|||
import android.util.Base64
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.blockchains.solana.SolanaTransactionHelper
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
|
|
@ -44,6 +45,7 @@ import com.tangem.feature.swap.domain.models.SwapAmount
|
|||
import com.tangem.feature.swap.domain.models.domain.*
|
||||
import com.tangem.feature.swap.domain.models.toStringWithRightOffset
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
import com.tangem.lib.crypto.BlockchainUtils.SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES
|
||||
import com.tangem.lib.crypto.UserWalletManager
|
||||
import com.tangem.lib.crypto.models.ProxyAmount
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -1325,10 +1327,23 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
val otherNativeFee = transaction.otherNativeFeeWei
|
||||
?.movePointLeft(nativeCoinDecimals)
|
||||
?: BigDecimal.ZERO
|
||||
|
||||
val txFeeState = if (isSolana(networkId)) {
|
||||
val transactionBytes = Base64.decode(transaction.txData, Base64.NO_WRAP)
|
||||
|
||||
val formattedHash = getFormattedHash(transactionBytes)
|
||||
|
||||
if (formattedHash.size > SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES) {
|
||||
return produceDexSwapDataError(
|
||||
error = ExpressDataError.TooLargeSolanaTransactionError,
|
||||
fromToken = fromToken,
|
||||
amount = amount,
|
||||
)
|
||||
}
|
||||
|
||||
getFeeDataForSolanaDexSwap(
|
||||
network = fromToken.currency.network,
|
||||
transaction = transaction,
|
||||
transactionBytes = transactionBytes,
|
||||
)
|
||||
.toTxFeeState(fromToken.currency, otherNativeFee)
|
||||
} else {
|
||||
|
|
@ -1387,22 +1402,34 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
)
|
||||
},
|
||||
ifLeft = { error ->
|
||||
val rates = getQuotes(fromToken.currency.id)
|
||||
val fromTokenSwapInfo = TokenSwapInfo(
|
||||
tokenAmount = amount,
|
||||
amountFiat = rates[fromToken.currency.id]?.multiply(amount.value)
|
||||
?: BigDecimal.ZERO,
|
||||
cryptoCurrencyStatus = fromToken,
|
||||
)
|
||||
SwapState.SwapError(
|
||||
fromTokenSwapInfo,
|
||||
error,
|
||||
IncludeFeeInAmount.Excluded,
|
||||
produceDexSwapDataError(
|
||||
error = error,
|
||||
fromToken = fromToken,
|
||||
amount = amount,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun produceDexSwapDataError(
|
||||
error: ExpressDataError,
|
||||
fromToken: CryptoCurrencyStatus,
|
||||
amount: SwapAmount,
|
||||
): SwapState.SwapError {
|
||||
val rates = getQuotes(fromToken.currency.id)
|
||||
val fromTokenSwapInfo = TokenSwapInfo(
|
||||
tokenAmount = amount,
|
||||
amountFiat = rates[fromToken.currency.id]?.multiply(amount.value)
|
||||
?: BigDecimal.ZERO,
|
||||
cryptoCurrencyStatus = fromToken,
|
||||
)
|
||||
return SwapState.SwapError(
|
||||
fromTokenSwapInfo,
|
||||
error,
|
||||
IncludeFeeInAmount.Excluded,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getFeeDataForDexSwap(
|
||||
network: Network,
|
||||
transaction: ExpressTransactionModel.DEX,
|
||||
|
|
@ -1446,13 +1473,9 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getFeeDataForSolanaDexSwap(
|
||||
network: Network,
|
||||
transaction: ExpressTransactionModel.DEX,
|
||||
): TransactionFee {
|
||||
val txData = transaction.txData
|
||||
private suspend fun getFeeDataForSolanaDexSwap(network: Network, transactionBytes: ByteArray): TransactionFee {
|
||||
val transactionData = TransactionData.Compiled(
|
||||
value = TransactionData.Compiled.Data.Bytes(Base64.decode(txData, Base64.NO_WRAP)),
|
||||
value = TransactionData.Compiled.Data.Bytes(transactionBytes),
|
||||
)
|
||||
|
||||
return getFeeUseCase(
|
||||
|
|
@ -2018,6 +2041,16 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
|||
return networkId == Blockchain.Solana.toNetworkId()
|
||||
}
|
||||
|
||||
// TODO create usecase [REDACTED_TASK_KEY]
|
||||
private fun getFormattedHash(hash: ByteArray): ByteArray {
|
||||
return try {
|
||||
SolanaTransactionHelper.removeSignaturesPlaceholders(hash)
|
||||
} catch (e: Exception) {
|
||||
Timber.e("Failed to format the hash: ${e.message}")
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INCREASE_GAS_LIMIT_FOR_DEX = 112 // 12%
|
||||
private const val INCREASE_GAS_LIMIT_FOR_SEND = 105 // 5%
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@ package com.tangem.feature.swap.analytics
|
|||
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_MESSAGE
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_TOKEN
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.SEND_TOKEN
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.feature.swap.domain.models.ui.FeeType
|
||||
|
||||
|
|
@ -108,14 +113,16 @@ sealed class SwapEvents(
|
|||
val receiveToken: String,
|
||||
val provider: SwapProvider,
|
||||
val errorCode: Int,
|
||||
val errorMessage: String?,
|
||||
) : SwapEvents(
|
||||
event = "Notice - Express Error",
|
||||
params = mapOf(
|
||||
"Send Token" to sendToken,
|
||||
"Receive Token" to receiveToken,
|
||||
"Provider" to provider.name,
|
||||
"Error Code" to errorCode.toString(),
|
||||
),
|
||||
params = buildMap {
|
||||
put(SEND_TOKEN, sendToken)
|
||||
put(RECEIVE_TOKEN, receiveToken)
|
||||
put(PROVIDER, provider.name)
|
||||
put(ERROR_CODE, errorCode.toString())
|
||||
errorMessage?.let { put(ERROR_MESSAGE, it) }
|
||||
},
|
||||
)
|
||||
// TODO parameters
|
||||
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ internal class SwapModel @Inject constructor(
|
|||
receiveToken = receiveToken ?: "",
|
||||
provider = provider,
|
||||
errorCode = error.code,
|
||||
errorMessage = error.message,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.math.BigDecimal
|
|||
object BlockchainUtils {
|
||||
|
||||
private const val XRP_X_ADDRESS = 'X'
|
||||
const val SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES = 930
|
||||
|
||||
/** Decodes XRP Blockchain address */
|
||||
fun decodeRippleXAddress(xAddress: String, blockchainId: String): XrpTaggedAddress? {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue