Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-25 14:41:36 +03:00
commit b3557dd0e4
1030 changed files with 32207 additions and 10702 deletions

View file

@ -1,10 +1,21 @@
package com.tangem.domain.transaction
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
interface FeeRepository {
/** Returns if fee is approximate for current [networkId] */
fun isFeeApproximate(networkId: Network.ID, amountType: AmountType): Boolean
/** Returns fee calculated for the transaction [transactionData] */
suspend fun calculateFee(
userWallet: UserWallet,
cryptoCurrency: CryptoCurrency,
transactionData: TransactionData,
): TransactionFee
}

View file

@ -0,0 +1,6 @@
package com.tangem.domain.transaction.error
interface FeeErrorResolver {
fun resolve(throwable: Throwable): GetFeeError
}

View file

@ -12,8 +12,6 @@ import com.tangem.domain.networks.single.SingleNetworkStatusProducer
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.transaction.error.AssociateAssetError
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.isNullOrZero
@ -22,10 +20,8 @@ import kotlinx.coroutines.flow.firstOrNull
class AssociateAssetUseCase(
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val walletManagersFacade: WalletManagersFacade,
private val currenciesRepository: CurrenciesRepository,
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier,
private val tokensFeatureToggles: TokensFeatureToggles,
) {
suspend operator fun invoke(
@ -33,25 +29,19 @@ class AssociateAssetUseCase(
currency: CryptoCurrency,
): Either<AssociateAssetError, Unit> {
return either {
val networkCoin = if (tokensFeatureToggles.isWalletBalanceFetcherEnabled) {
multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId),
)
?.firstOrNull {
val network = currency.network
it.network.id == network.id && it.network.derivationPath == network.derivationPath
}
?: error("Unable to create network coin for currencyID: ${currency.id}")
} else {
currenciesRepository.getNetworkCoin(
userWalletId = userWalletId,
networkId = currency.network.id,
derivationPath = currency.network.derivationPath,
)
}
val networkCoin = multiWalletCryptoCurrenciesSupplier.getSyncOrNull(
params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId),
)
?.firstOrNull {
val network = currency.network
it.network.id == network.id && it.network.derivationPath == network.derivationPath
}
?: error("Unable to create network coin for currencyID: ${currency.id}")
if (isBalanceZero(userWalletId, networkCoin)) {
raise(AssociateAssetError.NotEnoughBalance(networkCoin))
}
val signer = cardSdkConfigRepository.getCommonSigner(
cardId = null,
twinKey = null, // use null here because no assets support for Twin cards

View file

@ -5,14 +5,14 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.models.wallet.UserWalletId
/**
* Gets UTXO consolidation availability
* Gets self send availability
*/
class IsUtxoConsolidationAvailableUseCase(
class IsSelfSendAvailableUseCase(
private val walletManagersFacade: WalletManagersFacade,
) {
suspend fun invokeSync(userWalletId: UserWalletId, network: Network) =
walletManagersFacade.checkUtxoConsolidationAvailability(
walletManagersFacade.checkSelfSendAvailability(
userWalletId = userWalletId,
network = network,
)

View file

@ -11,13 +11,13 @@ import com.tangem.domain.card.models.TwinKey
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.transaction.error.SendTransactionError
class PrepareAndSignUseCase(
private val transactionRepository: TransactionRepository,
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val getHotTransactionSigner: (UserWallet.Hot) -> TransactionSigner,
) {
suspend operator fun invoke(
@ -57,7 +57,13 @@ class PrepareAndSignUseCase(
}
private fun createSigner(userWallet: UserWallet): TransactionSigner {
userWallet.requireColdWallet() // TODO [REDACTED_TASK_KEY]
return when (userWallet) {
is UserWallet.Hot -> getHotTransactionSigner(userWallet)
is UserWallet.Cold -> createColdSigner(userWallet)
}
}
private fun createColdSigner(userWallet: UserWallet.Cold): TransactionSigner {
val card = userWallet.scanResponse.card
val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins

View file

@ -0,0 +1,55 @@
package com.tangem.domain.transaction.usecase
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.tangem.blockchain.blockchains.solana.SolanaWalletManager
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.card.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.card.models.TwinKey
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.walletmanager.WalletManagersFacade
/**
* Use case for sending large Solana transactions that cannot be signed directly by the card.
* It handles the transaction creating alt tables and sending the transaction through the Solana network.
*
* @property cardSdkConfigRepository Repository to access card SDK configurations and signers.
* @property walletManagersFacade Facade to manage and retrieve wallet managers.
*/
class SendLargeSolanaTransactionUseCase(
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val walletManagersFacade: WalletManagersFacade,
) {
suspend operator fun invoke(
userWallet: UserWallet.Cold,
network: Network,
txHash: ByteArray,
): Either<SendTransactionError, Unit> {
val card = userWallet.scanResponse.card
val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins
val signer = cardSdkConfigRepository.getCommonSigner(
cardId = card.cardId.takeIf { isCardNotBackedUp },
twinKey = TwinKey.getOrNull(scanResponse = userWallet.scanResponse),
)
val walletManager = walletManagersFacade
.getOrCreateWalletManager(userWallet.walletId, network)
?: error("WalletManager is null")
if (walletManager !is SolanaWalletManager) return SendTransactionError.UnknownError().left()
val result = walletManager.handleLargeLegacyTransaction(signer, txHash)
return when (result) {
is Result.Failure -> SendTransactionError.BlockchainSdkError(
code = result.error.code,
message = result.error.customMessage,
).left()
is Result.Success -> Unit.right()
}
}
}

View file

@ -56,12 +56,11 @@ class ValidateWalletAddressUseCase(
isCurrentAddress: (String) -> Boolean,
): AddressValidationResult {
val decodedXAddress = BlockchainUtils.decodeRippleXAddress(address, network.rawId)
val isUtxoConsolidationAvailable =
walletManagersFacade.checkUtxoConsolidationAvailability(userWalletId, network)
val isSelfSendAvailable = walletManagersFacade.checkSelfSendAvailability(userWalletId, network)
val addressToValidate = decodedXAddress?.address ?: address
val current = isCurrentAddress(addressToValidate)
val isForbidSelfSend = current && !isUtxoConsolidationAvailable
val isForbidSelfSend = current && !isSelfSendAvailable
val isValidAddress = walletAddressServiceRepository.validateAddress(userWalletId, network, addressToValidate)
return when {