Updated on 2026-08-14
This commit is contained in:
parent
4ad958be89
commit
e0fb0e3587
7 changed files with 118 additions and 3 deletions
|
|
@ -172,4 +172,13 @@ interface TangemTechApi {
|
||||||
@Header("Cache-Control") cacheControl: String = "max-age=600",
|
@Header("Cache-Control") cacheControl: String = "max-age=600",
|
||||||
): ApiResponse<PromoBannerResponse>
|
): ApiResponse<PromoBannerResponse>
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores transaction hash in cache to prevent duplicate push
|
||||||
|
* notifications for yield operations (deposit, withdraw, send).
|
||||||
|
* Used when yield operations generate intermediate transactions
|
||||||
|
* that should not trigger notifications.
|
||||||
|
*/
|
||||||
|
@POST("v1/transaction-events")
|
||||||
|
suspend fun transactionEvents(@Body name: TransactionEventBody): ApiResponse<Unit>
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.tangem.datasource.api.tangemTech.models
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class TransactionEventBody(
|
||||||
|
@Json(name = "transactionId") val transactionId: String,
|
||||||
|
@Json(name = "operationType") val operationType: OperationType,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = false)
|
||||||
|
enum class OperationType {
|
||||||
|
|
||||||
|
@Json(name = "YIELD_DEPOSIT")
|
||||||
|
YIELD_DEPOSIT,
|
||||||
|
|
||||||
|
@Json(name = "YIELD_WITHDRAW")
|
||||||
|
YIELD_WITHDRAW,
|
||||||
|
|
||||||
|
@Json(name = "YIELD_SEND")
|
||||||
|
YIELD_SEND,
|
||||||
|
}
|
||||||
|
|
@ -20,13 +20,19 @@ import com.tangem.blockchain.common.transaction.Fee
|
||||||
import com.tangem.blockchain.nft.models.NFTAsset
|
import com.tangem.blockchain.nft.models.NFTAsset
|
||||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||||
|
import com.tangem.datasource.api.common.response.fold
|
||||||
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.OperationType
|
||||||
|
import com.tangem.datasource.api.tangemTech.models.TransactionEventBody
|
||||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.transaction.TransactionRepository
|
import com.tangem.domain.transaction.TransactionRepository
|
||||||
|
import com.tangem.domain.transaction.models.EventTransactionTypeDto
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import com.tangem.utils.coroutines.runCatching
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
@ -34,6 +40,7 @@ import java.math.BigInteger
|
||||||
|
|
||||||
@Suppress("LargeClass")
|
@Suppress("LargeClass")
|
||||||
internal class DefaultTransactionRepository(
|
internal class DefaultTransactionRepository(
|
||||||
|
private val tangemTechApi: TangemTechApi,
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val walletManagersFacade: WalletManagersFacade,
|
||||||
private val walletManagersStore: WalletManagersStore,
|
private val walletManagersStore: WalletManagersStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
|
@ -415,6 +422,31 @@ internal class DefaultTransactionRepository(
|
||||||
preparer.prepareAndSignMultiple(transactionData, signer)
|
preparer.prepareAndSignMultiple(transactionData, signer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun sendTransactionHash(hash: String, transactionType: EventTransactionTypeDto) {
|
||||||
|
runCatching(dispatchers.io) {
|
||||||
|
val operationType = when (transactionType) {
|
||||||
|
EventTransactionTypeDto.DEPOSIT -> OperationType.YIELD_DEPOSIT
|
||||||
|
EventTransactionTypeDto.WITHDRAW -> OperationType.YIELD_WITHDRAW
|
||||||
|
EventTransactionTypeDto.SEND -> OperationType.YIELD_SEND
|
||||||
|
}
|
||||||
|
val body = TransactionEventBody(
|
||||||
|
operationType = operationType,
|
||||||
|
transactionId = hash,
|
||||||
|
)
|
||||||
|
val response = tangemTechApi.transactionEvents(body)
|
||||||
|
response.fold(
|
||||||
|
onSuccess = {
|
||||||
|
Timber.d("Successfully sent yield supply transaction hash: $hash")
|
||||||
|
},
|
||||||
|
onError = { error ->
|
||||||
|
Timber.e(error, "Failed to send yield supply transaction hash: $hash")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}.onFailure { error ->
|
||||||
|
Timber.e(error, "Failed to send yield supply transaction hash: $hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun getPreparer(network: Network, userWalletId: UserWalletId): TransactionPreparer {
|
private suspend fun getPreparer(network: Network, userWalletId: UserWalletId): TransactionPreparer {
|
||||||
val blockchain = network.toBlockchain()
|
val blockchain = network.toBlockchain()
|
||||||
val walletManager = walletManagersFacade.getOrCreateWalletManager(
|
val walletManager = walletManagersFacade.getOrCreateWalletManager(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.tangem.data.transaction.DefaultFeeRepository
|
||||||
import com.tangem.data.transaction.DefaultTransactionRepository
|
import com.tangem.data.transaction.DefaultTransactionRepository
|
||||||
import com.tangem.data.transaction.DefaultWalletAddressServiceRepository
|
import com.tangem.data.transaction.DefaultWalletAddressServiceRepository
|
||||||
import com.tangem.data.transaction.error.DefaultFeeErrorResolver
|
import com.tangem.data.transaction.error.DefaultFeeErrorResolver
|
||||||
|
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
import com.tangem.domain.transaction.FeeRepository
|
import com.tangem.domain.transaction.FeeRepository
|
||||||
|
|
@ -25,11 +26,13 @@ internal object TransactionDataModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun providesTransactionRepository(
|
fun providesTransactionRepository(
|
||||||
|
tangemTechApi: TangemTechApi,
|
||||||
walletManagersFacade: WalletManagersFacade,
|
walletManagersFacade: WalletManagersFacade,
|
||||||
walletManagersStore: WalletManagersStore,
|
walletManagersStore: WalletManagersStore,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): TransactionRepository {
|
): TransactionRepository {
|
||||||
return DefaultTransactionRepository(
|
return DefaultTransactionRepository(
|
||||||
|
tangemTechApi = tangemTechApi,
|
||||||
walletManagersFacade = walletManagersFacade,
|
walletManagersFacade = walletManagersFacade,
|
||||||
walletManagersStore = walletManagersStore,
|
walletManagersStore = walletManagersStore,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.tangem.domain.transaction.models
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO representing the type of a transaction event on tangem backend to send
|
||||||
|
* info about transaction happens and its hash
|
||||||
|
*/
|
||||||
|
enum class EventTransactionTypeDto {
|
||||||
|
DEPOSIT,
|
||||||
|
WITHDRAW,
|
||||||
|
SEND,
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.blockchain.nft.models.NFTAsset
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.transaction.models.EventTransactionTypeDto
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
|
|
||||||
|
|
@ -123,4 +124,6 @@ interface TransactionRepository {
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
network: Network,
|
network: Network,
|
||||||
): com.tangem.blockchain.extensions.Result<List<ByteArray>>
|
): com.tangem.blockchain.extensions.Result<List<ByteArray>>
|
||||||
|
|
||||||
|
suspend fun sendTransactionHash(hash: String, transactionType: EventTransactionTypeDto)
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.domain.transaction.usecase
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import arrow.core.left
|
import arrow.core.left
|
||||||
import arrow.core.right
|
import arrow.core.right
|
||||||
|
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
|
||||||
import com.tangem.blockchain.common.BlockchainSdkError
|
import com.tangem.blockchain.common.BlockchainSdkError
|
||||||
import com.tangem.blockchain.common.TransactionData
|
import com.tangem.blockchain.common.TransactionData
|
||||||
import com.tangem.blockchain.common.TransactionSender
|
import com.tangem.blockchain.common.TransactionSender
|
||||||
|
|
@ -10,21 +11,25 @@ import com.tangem.blockchain.common.TransactionSigner
|
||||||
import com.tangem.blockchain.common.transaction.TransactionsSendResult
|
import com.tangem.blockchain.common.transaction.TransactionsSendResult
|
||||||
import com.tangem.blockchain.extensions.Result
|
import com.tangem.blockchain.extensions.Result
|
||||||
import com.tangem.blockchain.network.ResultChecker
|
import com.tangem.blockchain.network.ResultChecker
|
||||||
|
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplyEnterCallData
|
||||||
|
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplyExitCallData
|
||||||
|
import com.tangem.blockchain.yieldsupply.providers.ethereum.yield.EthereumYieldSupplySendCallData
|
||||||
import com.tangem.core.ui.format.bigdecimal.format
|
import com.tangem.core.ui.format.bigdecimal.format
|
||||||
import com.tangem.core.ui.format.bigdecimal.simple
|
import com.tangem.core.ui.format.bigdecimal.simple
|
||||||
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
|
import com.tangem.domain.card.common.TapWorkarounds.isStart2Coin
|
||||||
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
|
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
|
||||||
import com.tangem.domain.card.models.TwinKey
|
import com.tangem.domain.card.models.TwinKey
|
||||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||||
import com.tangem.domain.demo.models.DemoConfig
|
|
||||||
import com.tangem.domain.demo.DemoTransactionSender
|
import com.tangem.domain.demo.DemoTransactionSender
|
||||||
|
import com.tangem.domain.demo.models.DemoConfig
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||||
import com.tangem.domain.transaction.TransactionRepository
|
import com.tangem.domain.transaction.TransactionRepository
|
||||||
import com.tangem.domain.transaction.error.SendTransactionError
|
import com.tangem.domain.transaction.error.SendTransactionError
|
||||||
import com.tangem.domain.transaction.error.parseWrappedError
|
import com.tangem.domain.transaction.error.parseWrappedError
|
||||||
|
import com.tangem.domain.transaction.models.EventTransactionTypeDto
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
|
||||||
|
|
||||||
class SendTransactionUseCase(
|
class SendTransactionUseCase(
|
||||||
private val demoConfig: DemoConfig,
|
private val demoConfig: DemoConfig,
|
||||||
|
|
@ -100,7 +105,10 @@ class SendTransactionUseCase(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.fold(
|
.fold(
|
||||||
ifRight = { result -> result.hashes.right() },
|
ifRight = { result ->
|
||||||
|
processSentTransactionsHashes(txsData, result.hashes)
|
||||||
|
result.hashes.right()
|
||||||
|
},
|
||||||
ifLeft = { it.left() },
|
ifLeft = { it.left() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -114,6 +122,32 @@ class SendTransactionUseCase(
|
||||||
.map { it.first() }
|
.map { it.first() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun processSentTransactionsHashes(transactions: List<TransactionData>, hashes: List<String>) {
|
||||||
|
transactions.forEachIndexed { ind, tx ->
|
||||||
|
sendHashToBackendIfNeeded(tx, hashes[ind])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends tx hash to backend for specific transaction types
|
||||||
|
*/
|
||||||
|
private suspend fun sendHashToBackendIfNeeded(transaction: TransactionData, txHash: String) {
|
||||||
|
(transaction as? TransactionData.Uncompiled)?.let {
|
||||||
|
val extras = it.extras
|
||||||
|
when (extras) {
|
||||||
|
is EthereumTransactionExtras -> {
|
||||||
|
val txType = when (extras.callData) {
|
||||||
|
is EthereumYieldSupplyEnterCallData -> EventTransactionTypeDto.DEPOSIT
|
||||||
|
is EthereumYieldSupplySendCallData -> EventTransactionTypeDto.SEND
|
||||||
|
is EthereumYieldSupplyExitCallData -> EventTransactionTypeDto.WITHDRAW
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
transactionRepository.sendTransactionHash(txHash, txType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun sendDemo(
|
private suspend fun sendDemo(
|
||||||
userWallet: UserWallet,
|
userWallet: UserWallet,
|
||||||
network: Network,
|
network: Network,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue