Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-28 14:22:23 +05:00
parent 8adc79a8c3
commit f4bb2bbc2d
21 changed files with 327 additions and 236 deletions

View file

@ -1,12 +1,12 @@
package com.tangem.domain.transaction
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.smartcontract.SmartContractCallData
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionSendResult
import com.tangem.blockchain.common.transaction.TransactionsSendResult
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.models.TransactionType
import com.tangem.domain.wallets.models.UserWalletId
import java.math.BigDecimal
import java.math.BigInteger
@ -22,7 +22,16 @@ interface TransactionRepository {
userWalletId: UserWalletId,
network: Network,
txExtras: TransactionExtras?,
hash: String?,
): TransactionData.Uncompiled
@Suppress("LongParameterList")
suspend fun createTransferTransaction(
amount: Amount,
fee: Fee,
memo: String?,
destination: String,
userWalletId: UserWalletId,
network: Network,
): TransactionData.Uncompiled
@Suppress("LongParameterList")
@ -34,7 +43,6 @@ interface TransactionRepository {
spenderAddress: String,
userWalletId: UserWalletId,
network: Network,
hash: String?,
): TransactionData.Uncompiled
@Suppress("LongParameterList")
@ -45,9 +53,6 @@ interface TransactionRepository {
destination: String,
userWalletId: UserWalletId,
network: Network,
isSwap: Boolean = false,
txExtras: TransactionExtras?,
hash: String? = null,
): Result<Unit>
suspend fun sendTransaction(
@ -66,9 +71,8 @@ interface TransactionRepository {
): com.tangem.blockchain.extensions.Result<TransactionsSendResult>
fun createTransactionDataExtras(
data: String,
callData: SmartContractCallData,
network: Network,
transactionType: TransactionType,
nonce: BigInteger?,
gasLimit: BigInteger?,
): TransactionExtras

View file

@ -8,6 +8,9 @@ import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.UserWalletId
import java.math.BigDecimal
/**
* Use case to create and get approval transaction
*/
class CreateApprovalTransactionUseCase(
private val transactionRepository: TransactionRepository,
) {
@ -20,7 +23,6 @@ class CreateApprovalTransactionUseCase(
fee: Fee,
contractAddress: String,
spenderAddress: String,
hash: String? = null,
) = Either.catch {
transactionRepository.createApprovalTransaction(
amount = BigDecimal.ZERO.convertToSdkAmount(cryptoCurrency),
@ -30,7 +32,6 @@ class CreateApprovalTransactionUseCase(
userWalletId = userWalletId,
network = cryptoCurrency.network,
fee = fee,
hash = hash,
)
}
}

View file

@ -1,30 +1,25 @@
package com.tangem.domain.transaction.usecase
import arrow.core.Either
import com.tangem.blockchain.common.smartcontract.CompiledSmartContractCallData
import com.tangem.common.extensions.hexToBytes
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.models.TransactionType
import java.math.BigInteger
class CreateTransactionDataExtrasUseCase(
private val transactionRepository: TransactionRepository,
) {
operator fun invoke(
data: String,
network: Network,
transactionType: TransactionType,
gasLimit: BigInteger? = null,
nonce: BigInteger? = null,
) = Either.catch {
requireNotNull(
transactionRepository.createTransactionDataExtras(
data = data,
network = network,
transactionType = transactionType,
nonce = nonce,
gasLimit = gasLimit,
),
) { "Failed to create transaction" }
}
operator fun invoke(data: String, network: Network, gasLimit: BigInteger? = null, nonce: BigInteger? = null) =
Either.catch {
requireNotNull(
transactionRepository.createTransactionDataExtras(
callData = CompiledSmartContractCallData(data.hexToBytes()),
network = network,
nonce = nonce,
gasLimit = gasLimit,
),
) { "Failed to create transaction" }
}
}

View file

@ -8,6 +8,12 @@ import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.wallets.models.UserWalletId
/**
* Use case to create and get transaction
*
* !!!IMPORTANT
* Use when transaction data is already compiled by external service or provider
*/
class CreateTransactionUseCase(
private val transactionRepository: TransactionRepository,
) {
@ -24,7 +30,6 @@ class CreateTransactionUseCase(
userWalletId: UserWalletId,
network: Network,
txExtras: TransactionExtras? = null,
hash: String? = null,
) = Either.catch {
transactionRepository.createTransaction(
amount = amount,
@ -34,7 +39,6 @@ class CreateTransactionUseCase(
userWalletId = userWalletId,
network = network,
txExtras = txExtras,
hash = hash,
)
}
}

View file

@ -0,0 +1,41 @@
package com.tangem.domain.transaction.usecase
import arrow.core.Either
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.wallets.models.UserWalletId
/**
* Use case to create and get transfer transaction
*
* !!!IMPORTANT
* Use when transaction data is compiled by us using BlockchainSDK methods
*/
class CreateTransferTransactionUseCase(
private val transactionRepository: TransactionRepository,
) {
/**
* [REDACTED_TODO_COMMENT]
*/
@Suppress("LongParameterList")
suspend operator fun invoke(
amount: Amount,
fee: Fee,
memo: String?,
destination: String,
userWalletId: UserWalletId,
network: Network,
) = Either.catch {
transactionRepository.createTransferTransaction(
amount = amount,
fee = fee,
memo = memo,
destination = destination,
userWalletId = userWalletId,
network = network,
)
}
}

View file

@ -17,6 +17,9 @@ import java.math.BigDecimal
/**
* Use case to get transaction fee
*
* !!!IMPORTANT!!!
* Use when transaction data is already compiled by external service or provider
*/
class GetFeeUseCase(
private val walletManagersFacade: WalletManagersFacade,

View file

@ -0,0 +1,102 @@
package com.tangem.domain.transaction.usecase
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.smartcontract.SmartContractCallDataProviderFactory
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.demo.DemoTransactionSender
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.error.mapToFeeError
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import java.math.BigDecimal
/**
* Use case to get transfer transaction fee
*
* !!!IMPORTANT
* Use when transaction data is compiled by us using BlockchainSDK methods
*/
class GetTransferFeeUseCase(
private val walletManagersFacade: WalletManagersFacade,
private val demoConfig: DemoConfig,
) {
suspend operator fun invoke(
amount: BigDecimal,
destination: String,
userWallet: UserWallet,
cryptoCurrency: CryptoCurrency,
) = either {
catch(
block = {
val amountData = convertCryptoCurrencyToAmount(cryptoCurrency, amount)
val result = if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) {
demoTransactionSender(userWallet, cryptoCurrency).getFee(
amount = amountData,
destination = destination,
)
} else {
val walletManager = walletManagersFacade.getOrCreateWalletManager(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
)
val smartContractCallData = if (amountData.type is AmountType.Token) {
SmartContractCallDataProviderFactory.getTokenTransferCallData(
amount = amountData,
destinationAddress = destination,
blockchain = Blockchain.fromId(cryptoCurrency.network.id.value),
)
} else {
null
}
(walletManager as? TransactionSender)?.getFee(
amount = amountData,
destination = destination,
callData = smartContractCallData,
) ?: error("Fee is null")
}
val maybeFee = when (result) {
is Result.Success -> result.data
is Result.Failure -> raise(result.mapToFeeError())
}
maybeFee
},
catch = {
raise(GetFeeError.DataError(it))
},
)
}
private suspend fun demoTransactionSender(
userWallet: UserWallet,
cryptoCurrency: CryptoCurrency,
): DemoTransactionSender {
return DemoTransactionSender(
walletManagersFacade
.getOrCreateWalletManager(userWallet.walletId, cryptoCurrency.network)
?: error("WalletManager is null"),
)
}
private fun convertCryptoCurrencyToAmount(cryptoCurrency: CryptoCurrency, amount: BigDecimal) = Amount(
currencySymbol = cryptoCurrency.symbol,
value = amount,
decimals = cryptoCurrency.decimals,
type = when (cryptoCurrency) {
is CryptoCurrency.Coin -> AmountType.Coin
is CryptoCurrency.Token -> AmountType.Token(
token = Token(
symbol = cryptoCurrency.symbol,
contractAddress = cryptoCurrency.contractAddress,
decimals = cryptoCurrency.decimals,
),
)
},
)
}

View file

@ -21,8 +21,6 @@ class ValidateTransactionUseCase(
destination: String,
userWalletId: UserWalletId,
network: Network,
isSwap: Boolean = false,
hash: String? = null,
): Either<Throwable, Unit> {
return transactionRepository.validateTransaction(
amount = amount,
@ -31,10 +29,6 @@ class ValidateTransactionUseCase(
destination = destination,
userWalletId = userWalletId,
network = network,
isSwap = isSwap,
txExtras = null,
hash = hash,
)
.fold(onSuccess = { Unit.right() }, onFailure = { it.left() })
).fold(onSuccess = { Unit.right() }, onFailure = { it.left() })
}
}