Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-06 15:13:06 +01:00
parent 1d8522dca6
commit 0039672af9
19 changed files with 146 additions and 90 deletions

View file

@ -192,17 +192,17 @@ class WalletConnectSdkHelper {
),
)
return when (result) {
SimpleResult.Success -> {
is Result.Success -> {
val sentFrom = CoreAnalyticsParam.TxSentFrom.WalletConnect
Analytics.send(Basic.TransactionSent(sentFrom = sentFrom, memoType = MemoType.Null))
val hash = data.walletManager.wallet.recentTransactions.last().hash
if (hash?.startsWith(HEX_PREFIX) == true) {
val hash = result.data.hash
if (hash.startsWith(HEX_PREFIX)) {
hash
} else {
HEX_PREFIX + hash
}
}
is SimpleResult.Failure -> {
is Result.Failure -> {
Timber.e(result.error as BlockchainSdkError)
null
}

View file

@ -11,7 +11,7 @@ import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder.XrpTransactionExtras
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchainsdk.utils.minimalAmount
import com.tangem.common.core.TangemSdkError
import com.tangem.core.analytics.Analytics
@ -246,7 +246,7 @@ private fun sendTransaction(
tangemSdk.config.linkedTerminal = linkedTerminalState
when (sendResult) {
is SimpleResult.Success -> {
is Result.Success -> {
dispatch(SendAction.SendSuccess)
if (externalTransactionData != null) {
@ -273,7 +273,7 @@ private fun sendTransaction(
dispatch(NavigationAction.PopBackTo())
}
}
is SimpleResult.Failure -> {
is Result.Failure -> {
updateFeedbackManagerInfo(
sendResult = sendResult.error,
walletManager = walletManager,

View file

@ -17,8 +17,8 @@ import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.common.transaction.TransactionSendResult
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.externallinkprovider.TxExploreState
import com.tangem.blockchain.network.ResultChecker
import com.tangem.blockchainsdk.utils.fromNetworkId
@ -363,12 +363,12 @@ class TransactionManagerImpl(
}
}
private fun handleSendResult(result: SimpleResult): SendTxResult {
private fun handleSendResult(result: Result<TransactionSendResult>): SendTxResult {
when (result) {
is SimpleResult.Success -> {
is Result.Success -> {
return SendTxResult.Success
}
is SimpleResult.Failure -> {
is Result.Failure -> {
if (ResultChecker.isNetworkError(result)) return SendTxResult.NetworkError(result.error)
val error = result.error as? BlockchainSdkError ?: return SendTxResult.UnknownError()
when (error) {

View file

@ -2,6 +2,7 @@ package com.tangem.datasource.api.express
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.express.models.request.AssetsRequestBody
import com.tangem.datasource.api.express.models.request.ExchangeSentRequestBody
import com.tangem.datasource.api.express.models.request.PairsRequestBody
import com.tangem.datasource.api.express.models.response.*
import retrofit2.http.Body
@ -57,4 +58,7 @@ interface TangemExpressApi {
@GET("exchange-status")
suspend fun getExchangeStatus(@Query("txId") txId: String): ApiResponse<ExchangeStatusResponse>
@POST("exchange-sent")
suspend fun exchangeSent(@Body body: ExchangeSentRequestBody): ApiResponse<ExchangeSentResponseBody>
}

View file

@ -0,0 +1,18 @@
package com.tangem.datasource.api.express.models.request
import com.squareup.moshi.Json
data class ExchangeSentRequestBody(
@Json(name = "txId")
val txId: String,
@Json(name = "fromNetwork")
val fromNetwork: String,
@Json(name = "fromAddress")
val fromAddress: String,
@Json(name = "payinAddress")
val payinAddress: String,
@Json(name = "payinExtraId")
val payinExtraId: String?,
@Json(name = "txHash")
val txHash: String,
)

View file

@ -0,0 +1,10 @@
package com.tangem.datasource.api.express.models.response
import com.squareup.moshi.Json
data class ExchangeSentResponseBody(
@Json(name = "txId")
val txId: String,
@Json(name = "status")
val status: String,
)

View file

@ -10,8 +10,8 @@ data class ExchangeStatusResponse(
@Json(name = "externalTxId")
val externalTxId: String,
@Json(name = "externalTxStatus")
val externalStatus: ExchangeStatus,
@Json(name = "status")
val status: ExchangeStatus,
@Json(name = "externalTxUrl")
val externalTxUrl: String,

View file

@ -3,8 +3,8 @@ package com.tangem.domain.demo
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.common.transaction.TransactionSendResult
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.CompletionResult
import kotlin.random.Random
@ -25,14 +25,17 @@ class DemoTransactionSender(private val walletManager: WalletManager) : Transact
return getFee(amount, walletManager.wallet.address)
}
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
override suspend fun send(
transactionData: TransactionData,
signer: TransactionSigner,
): Result<TransactionSendResult> {
val signerResponse = signer.sign(
hash = getDataToSign(),
publicKey = walletManager.wallet.publicKey,
)
return when (signerResponse) {
is CompletionResult.Success -> SimpleResult.Failure(Exception(ID).toBlockchainSdkError())
is CompletionResult.Failure -> SimpleResult.fromTangemSdkError(signerResponse.error)
is CompletionResult.Success -> Result.Failure(Exception(ID).toBlockchainSdkError())
is CompletionResult.Failure -> Result.fromTangemSdkError(signerResponse.error)
}
}

View file

@ -514,20 +514,6 @@ class DefaultWalletManagersFacade(
return walletManager?.createTransaction(amount, fee, destination)
}
@Deprecated("Will be removed in future")
override suspend fun sendTransaction(
txData: TransactionData,
signer: CommonSigner,
userWalletId: UserWalletId,
network: Network,
): SimpleResult {
val walletManager = getOrCreateWalletManager(
userWalletId = userWalletId,
network = network,
)
return (walletManager as TransactionSender).send(txData, signer)
}
override suspend fun getRecentTransactions(
userWalletId: UserWalletId,
currency: CryptoCurrency,

View file

@ -212,22 +212,6 @@ interface WalletManagersFacade {
network: Network,
): TransactionData?
/**
* Sends transaction
*
* @param txData transaction data
* @param signer card signer
* @param userWalletId selected wallet id
* @param network network of currency
*/
@Deprecated("Will be removed in future")
suspend fun sendTransaction(
txData: TransactionData,
signer: CommonSigner,
userWalletId: UserWalletId,
network: Network,
): SimpleResult
/** Get recent transactions of [userWalletId] for [currency] */
suspend fun getRecentTransactions(userWalletId: UserWalletId, currency: CryptoCurrency): List<TxHistoryItem>

View file

@ -1,11 +1,8 @@
package com.tangem.domain.transaction
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.CommonSigner
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionExtras
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.common.transaction.TransactionSendResult
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWalletId
@ -42,5 +39,5 @@ interface TransactionRepository {
signer: CommonSigner,
userWalletId: UserWalletId,
network: Network,
): SimpleResult
): com.tangem.blockchain.extensions.Result<TransactionSendResult>
}

View file

@ -6,7 +6,8 @@ import arrow.core.right
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.common.transaction.TransactionSendResult
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.network.ResultChecker
import com.tangem.common.core.TangemSdkError
import com.tangem.core.ui.extensions.resourceReference
@ -35,7 +36,7 @@ class SendTransactionUseCase(
txData: TransactionData,
userWallet: UserWallet,
network: Network,
): Either<SendTransactionError?, Boolean> {
): Either<SendTransactionError?, String> {
val signer = cardSdkConfigRepository.getCommonSigner(cardId = null)
val linkedTerminal = cardSdkConfigRepository.isLinkedTerminal()
@ -51,12 +52,16 @@ class SendTransactionUseCase(
signer = signer,
)
} else {
transactionRepository.sendTransaction(
val sendResult = transactionRepository.sendTransaction(
txData = txData,
signer = signer,
userWalletId = userWallet.walletId,
network = network,
).right()
)
when (sendResult) {
is Result.Failure -> handleError(sendResult).left()
is Result.Success -> sendResult.data.right()
}
}
} catch (ex: Exception) {
cardSdkConfigRepository.setLinkedTerminal(linkedTerminal)
@ -65,12 +70,7 @@ class SendTransactionUseCase(
cardSdkConfigRepository.setLinkedTerminal(linkedTerminal)
return sendResult.fold(
ifRight = { result ->
when (result) {
is SimpleResult.Success -> true.right()
is SimpleResult.Failure -> handleError(result).left()
}
},
ifRight = { result -> result.hash.right() },
ifLeft = { it.left() },
)
}
@ -80,7 +80,7 @@ class SendTransactionUseCase(
network: Network,
transactionData: TransactionData,
signer: TransactionSigner,
): Either<SendTransactionError, SimpleResult> {
): Either<SendTransactionError, TransactionSendResult> {
val demoTransactionSender = DemoTransactionSender(
walletManagersFacade
.getOrCreateWalletManager(userWallet.walletId, network)
@ -89,14 +89,14 @@ class SendTransactionUseCase(
val result = demoTransactionSender.send(transactionData = transactionData, signer = signer)
return if (result is SimpleResult.Failure && result.error.customMessage.contains(DemoTransactionSender.ID)) {
return if (result is Result.Failure && result.error.customMessage.contains(DemoTransactionSender.ID)) {
SendTransactionError.DemoCardError.left()
} else {
result.right()
TransactionSendResult("hash").right()
}
}
private fun handleError(result: SimpleResult.Failure): SendTransactionError {
private fun handleError(result: Result.Failure): SendTransactionError {
if (ResultChecker.isNetworkError(result)) {
return SendTransactionError.NetworkError(
code = result.error.message,

View file

@ -14,6 +14,7 @@ import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.express.TangemExpressApi
import com.tangem.datasource.api.express.models.request.ExchangeSentRequestBody
import com.tangem.datasource.api.express.models.request.PairsRequestBody
import com.tangem.datasource.api.express.models.response.ExchangeDataResponseWithTxDetails
import com.tangem.datasource.api.express.models.response.SwapPair
@ -303,6 +304,31 @@ internal class DefaultSwapRepository @Inject constructor(
}
}
override suspend fun exchangeSent(
txId: String,
fromNetwork: String,
fromAddress: String,
payInAddress: String,
txHash: String,
payInExtraId: String?,
): Either<DataError, Unit> = withContext(coroutineDispatcher.io) {
try {
tangemExpressApi.exchangeSent(
ExchangeSentRequestBody(
txId = txId,
fromNetwork = fromNetwork,
fromAddress = fromAddress,
payinAddress = payInAddress,
payinExtraId = payInExtraId,
txHash = txHash,
),
).getOrThrow()
Unit.right()
} catch (ex: Exception) {
getDataError(ex).left()
}
}
private fun parseTxDetails(txDetailsJson: String): TxDetails? {
return try {
txDetailsMoshiAdapter.fromJson(txDetailsJson)

View file

@ -10,7 +10,7 @@ internal class ExchangeStatusConverter : Converter<ExchangeStatusResponse, Excha
return ExchangeStatusModel(
providerId = value.providerId,
status = ExchangeStatus.values().firstOrNull {
it.name.lowercase() == value.externalStatus.name.lowercase()
it.name.lowercase() == value.status.name.lowercase()
},
txId = value.externalTxId,
txExternalUrl = value.externalTxUrl,

View file

@ -31,6 +31,7 @@ internal class ExpressDataConverter : Converter<ExchangeDataResponseWithTxDetail
txTo = transactionDto.txTo,
txFrom = requireNotNull(transactionDto.txFrom),
txData = requireNotNull(transactionDto.txData),
txExtraId = transactionDto.txExtraId,
)
} else {
ExpressTransactionModel.CEX(

View file

@ -70,5 +70,16 @@ interface SwapRepository {
refundExtraId: String? = null, // for cex only
): Either<DataError, SwapDataModel>
// TODO: Add target error handling, remove either ([REDACTED_JIRA])
@Suppress("LongParameterList")
suspend fun exchangeSent(
txId: String,
fromNetwork: String,
fromAddress: String,
payInAddress: String,
txHash: String,
payInExtraId: String?,
): Either<DataError, Unit>
fun getNativeTokenForNetwork(networkId: String): CryptoCurrency
}

View file

@ -8,12 +8,14 @@ sealed class ExpressTransactionModel {
abstract val toAmount: SwapAmount
abstract val txId: String
abstract val txTo: String
abstract val txExtraId: String?
data class DEX(
override val fromAmount: SwapAmount,
override val toAmount: SwapAmount,
override val txId: String,
override val txTo: String,
override val txExtraId: String?,
val txFrom: String,
val txData: String,
) : ExpressTransactionModel()
@ -23,9 +25,9 @@ sealed class ExpressTransactionModel {
override val toAmount: SwapAmount,
override val txId: String,
override val txTo: String,
override val txExtraId: String?,
val externalTxId: String,
val externalTxUrl: String,
val txExtraIdName: String?,
val txExtraId: String?,
) : ExpressTransactionModel()
}

View file

@ -565,8 +565,8 @@ internal class SwapInteractorImpl @Inject constructor(
onSwapDex(
networkId = currencyToSend.currency.network.backendId,
swapData = requireNotNull(swapData),
currencyToSend = currencyToSend.currency,
currencyToGet = currencyToGet.currency,
currencyToSendStatus = currencyToSend,
currencyToGetStatus = currencyToGet,
amountToSwap = amountToSwap,
fee = fee,
userWalletId = requireNotNull(getSelectedWallet()).walletId,
@ -616,26 +616,26 @@ internal class SwapInteractorImpl @Inject constructor(
private suspend fun onSwapDex(
networkId: String,
swapData: SwapDataModel,
currencyToSend: CryptoCurrency,
currencyToGet: CryptoCurrency,
currencyToSendStatus: CryptoCurrencyStatus,
currencyToGetStatus: CryptoCurrencyStatus,
amountToSwap: String,
fee: TxFee,
userWalletId: UserWalletId,
): SwapTransactionState {
val amountDecimal = requireNotNull(toBigDecimalOrNull(amountToSwap)) { "wrong amount format" }
val amount = SwapAmount(amountDecimal, currencyToSend.decimals)
val derivationPath = currencyToSend.network.derivationPath.value
val amount = SwapAmount(amountDecimal, currencyToSendStatus.currency.decimals)
val derivationPath = currencyToSendStatus.currency.network.derivationPath.value
val dataToSign = (swapData.transaction as ExpressTransactionModel.DEX).txData
val txData = createTransactionUseCase(
amount = amount.value.convertToAmount(currencyToSend),
amount = amount.value.convertToAmount(currencyToSendStatus.currency),
fee = getFeeForTransaction(
fee = fee,
blockchain = Blockchain.fromId(currencyToSend.network.id.value),
blockchain = Blockchain.fromId(currencyToSendStatus.currency.network.id.value),
),
memo = null,
destination = swapData.transaction.txTo,
userWalletId = userWalletId,
network = currencyToSend.network,
network = currencyToSendStatus.currency.network,
txExtras = createDexTxExtras(fee.gasLimit, dataToSign),
hash = dataToSign,
isSwap = true,
@ -647,20 +647,28 @@ internal class SwapInteractorImpl @Inject constructor(
val result = sendTransactionUseCase(
txData = txData,
userWallet = requireNotNull(getSelectedWallet()),
network = currencyToSend.network,
network = currencyToSendStatus.currency.network,
)
return result.fold(
ifRight = {
storeLastCryptoCurrencyId(currencyToGet)
ifRight = { txHash ->
repository.exchangeSent(
txId = swapData.transaction.txId,
fromNetwork = currencyToSendStatus.currency.network.backendId,
fromAddress = currencyToSendStatus.value.networkAddress?.defaultAddress?.value.orEmpty(),
payInAddress = txData.destinationAddress,
txHash = txHash,
payInExtraId = swapData.transaction.txExtraId,
)
storeLastCryptoCurrencyId(currencyToGetStatus.currency)
SwapTransactionState.TxSent(
fromAmount = amountFormatter.formatSwapAmountToUI(
amount,
currencyToSend.symbol,
currencyToSendStatus.currency.symbol,
),
fromAmountValue = amount.value,
toAmount = amountFormatter.formatSwapAmountToUI(
swapData.toTokenAmount,
currencyToGet.symbol,
currencyToGetStatus.currency.symbol,
),
toAmountValue = swapData.toTokenAmount.value,
txHash = userWalletManager.getLastTransactionHash(networkId, derivationPath).orEmpty(),
@ -746,8 +754,6 @@ internal class SwapInteractorImpl @Inject constructor(
network = currencyToSend.currency.network,
)
val txCexModel = exchangeData.transaction as? ExpressTransactionModel.CEX
val derivationPath = currencyToSend.currency.network.derivationPath.value
return result.fold(
ifLeft = {
@ -760,9 +766,17 @@ internal class SwapInteractorImpl @Inject constructor(
else -> SwapTransactionState.UnknownError
}
},
ifRight = {
ifRight = { txHash ->
repository.exchangeSent(
txId = exchangeDataCex.txId,
fromNetwork = currencyToSend.currency.network.backendId,
fromAddress = currencyToSend.value.networkAddress?.defaultAddress?.value.orEmpty(),
payInAddress = txData.destinationAddress,
txHash = txHash,
payInExtraId = exchangeDataCex.txExtraId,
)
val timestamp = System.currentTimeMillis()
val txExternalUrl = txCexModel?.externalTxUrl
val txExternalUrl = exchangeDataCex.externalTxUrl
storeSwapTransaction(
currencyToSend = currencyToSend,
currencyToGet = currencyToGet,
@ -770,8 +784,8 @@ internal class SwapInteractorImpl @Inject constructor(
swapProvider = swapProvider,
swapDataModel = exchangeData,
timestamp = timestamp,
txExternalUrl = txExternalUrl.orEmpty(),
txExternalId = txCexModel?.externalTxId.orEmpty(),
txExternalUrl = txExternalUrl,
txExternalId = exchangeDataCex.externalTxId,
)
storeLastCryptoCurrencyId(currencyToGet.currency)
SwapTransactionState.TxSent(

View file

@ -87,7 +87,7 @@ markdown = "0.7.2"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "release-app_5.12-656"
tangemBlockchainSdk = "release-app_5.12-658"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_5.12-364"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^