Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-25 20:04:51 +05:00
parent d614f69161
commit 4aa74c4b70
21 changed files with 397 additions and 70 deletions

View file

@ -188,4 +188,12 @@ internal object TransactionDomainModule {
): SignUseCase {
return SignUseCase(cardSdkConfigRepository, walletManagersFacade)
}
@Provides
@Singleton
fun provideCreateNFTTransferTransactionUseCase(
transactionRepository: TransactionRepository,
): CreateNFTTransferTransactionUseCase {
return CreateNFTTransferTransactionUseCase(transactionRepository)
}
}

View file

@ -0,0 +1,13 @@
package com.tangem.datasource.api.common.adapter
import com.squareup.moshi.FromJson
import com.squareup.moshi.ToJson
import java.math.BigInteger
internal class BigIntegerAdapter {
@FromJson
fun fromJson(value: String) = BigInteger(value)
@ToJson
fun toJson(value: BigInteger) = value.toString()
}

View file

@ -6,10 +6,7 @@ import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchain.nft.models.NFTCollection
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.datasource.api.common.adapter.BigDecimalAdapter
import com.tangem.datasource.api.common.adapter.DateTimeAdapter
import com.tangem.datasource.api.common.adapter.LocalDateAdapter
import com.tangem.datasource.api.common.adapter.addStakeKitEnumFallbackAdapters
import com.tangem.datasource.api.common.adapter.*
import com.tangem.datasource.local.config.providers.models.ProviderModel
import com.tangem.datasource.local.network.entity.NetworkStatusDM
import com.tangem.domain.models.scan.serialization.*
@ -38,6 +35,7 @@ class MoshiModule {
.withDefaultValue(ProviderModel.UnsupportedType),
)
.add(BigDecimalAdapter())
.add(BigIntegerAdapter())
.add(LocalDateAdapter())
.add(DateTimeAdapter())
.add(VisaActivationRemoteState.jsonAdapter)
@ -73,6 +71,7 @@ class MoshiModule {
val adapters = MoshiJsonConverter.getTangemSdkAdapters() +
listOf(
BigDecimalAdapter(),
BigIntegerAdapter(),
WalletDerivedKeysMapAdapter(),
ScanResponseDerivedKeysMapAdapter(),
ByteArrayKeyAdapter(),
@ -100,6 +99,7 @@ class MoshiModule {
adapters = MoshiJsonConverter.getTangemSdkAdapters() +
listOf(
BigDecimalAdapter(),
BigIntegerAdapter(),
WalletDerivedKeysMapAdapter(),
ScanResponseDerivedKeysMapAdapter(),
ByteArrayKeyAdapter(),

View file

@ -4,14 +4,17 @@ import com.tangem.domain.models.StatusSource
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.nft.models.NFTSalePrice
import com.tangem.domain.tokens.model.Network
import com.tangem.utils.converter.Converter
import com.tangem.utils.converter.TwoWayConverter
import com.tangem.blockchain.nft.models.NFTAsset as SdkNFTAsset
object NFTSdkAssetConverter : Converter<Pair<Network, SdkNFTAsset>, NFTAsset> {
object NFTSdkAssetConverter : TwoWayConverter<Pair<Network, SdkNFTAsset>, NFTAsset> {
override fun convert(value: Pair<Network, SdkNFTAsset>): NFTAsset {
val (network, asset) = value
val assetId = NFTSdkAssetIdentifierConverter.convert(asset.identifier)
val collectionId = NFTSdkCollectionIdentifierConverter.convert(asset.collectionIdentifier)
val salePrice = asset.salePrice?.let { NFTSdkAssetSalePriceConverter(assetId).convert(it) }
?: NFTSalePrice.Empty(assetId)
return NFTAsset(
id = assetId,
collectionId = collectionId,
@ -20,13 +23,9 @@ object NFTSdkAssetConverter : Converter<Pair<Network, SdkNFTAsset>, NFTAsset> {
owner = asset.owner,
name = asset.name,
description = asset.description,
salePrice = asset.salePrice?.let {
NFTSalePrice.Value(
assetId = assetId,
value = it.value,
symbol = it.symbol,
)
} ?: NFTSalePrice.Empty(assetId = assetId),
amount = asset.amount,
decimals = asset.decimals,
salePrice = salePrice,
rarity = asset.rarity?.let {
NFTAsset.Rarity(
rank = it.rank,
@ -48,4 +47,42 @@ object NFTSdkAssetConverter : Converter<Pair<Network, SdkNFTAsset>, NFTAsset> {
source = StatusSource.CACHE,
)
}
override fun convertBack(value: NFTAsset): Pair<Network, SdkNFTAsset> {
val assetId = NFTSdkAssetIdentifierConverter.convertBack(value.id)
val collectionId = NFTSdkCollectionIdentifierConverter.convertBack(value.collectionId)
val salePrice = (value.salePrice as? NFTSalePrice.Value)?.let {
NFTSdkAssetSalePriceConverter(value.id).convertBack(it)
}
return value.network to SdkNFTAsset(
identifier = assetId,
collectionIdentifier = collectionId,
blockchainId = value.network.id.value,
contractType = value.contractType,
owner = value.owner,
name = value.name,
description = value.description,
amount = value.amount,
decimals = value.decimals,
salePrice = salePrice,
rarity = value.rarity?.let {
SdkNFTAsset.Rarity(
rank = it.rank,
label = it.label,
)
},
media = value.media?.let {
SdkNFTAsset.Media(
url = it.url,
mimetype = it.mimetype,
)
},
traits = value.traits.map {
SdkNFTAsset.Trait(
name = it.name,
value = it.value,
)
},
)
}
}

View file

@ -9,6 +9,7 @@ object NFTSdkAssetIdentifierConverter : TwoWayConverter<SdkNFTAsset.Identifier,
is SdkNFTAsset.Identifier.EVM -> NFTAsset.Identifier.EVM(
tokenId = value.tokenId,
tokenAddress = value.tokenAddress,
contractType = NFTAsset.Identifier.EVM.ContractType.valueOf(value.contractType.name),
)
is SdkNFTAsset.Identifier.TON -> NFTAsset.Identifier.TON(
tokenAddress = value.tokenAddress,
@ -24,6 +25,7 @@ object NFTSdkAssetIdentifierConverter : TwoWayConverter<SdkNFTAsset.Identifier,
is NFTAsset.Identifier.EVM -> SdkNFTAsset.Identifier.EVM(
tokenId = value.tokenId,
tokenAddress = value.tokenAddress,
contractType = SdkNFTAsset.Identifier.EVM.ContractType.valueOf(value.contractType.name),
)
is NFTAsset.Identifier.TON -> SdkNFTAsset.Identifier.TON(
tokenAddress = value.tokenAddress,

View file

@ -0,0 +1,25 @@
package com.tangem.datasource.local.nft.converter
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.nft.models.NFTSalePrice
import com.tangem.utils.converter.TwoWayConverter
import com.tangem.blockchain.nft.models.NFTAsset.SalePrice as SDKSalePrice
internal class NFTSdkAssetSalePriceConverter(
private val assetId: NFTAsset.Identifier,
) : TwoWayConverter<SDKSalePrice, NFTSalePrice.Value> {
override fun convert(value: SDKSalePrice): NFTSalePrice.Value {
return NFTSalePrice.Value(
assetId = assetId,
value = value.value,
symbol = value.symbol,
)
}
override fun convertBack(value: NFTSalePrice.Value): SDKSalePrice {
return SDKSalePrice(
symbol = value.symbol,
value = value.value,
)
}
}

View file

@ -17,6 +17,7 @@ import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.smartcontract.SmartContractCallData
import com.tangem.blockchain.common.smartcontract.SmartContractCallDataProviderFactory
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.datasource.local.walletmanager.WalletManagersStore
import com.tangem.domain.tokens.model.CryptoCurrency
@ -30,6 +31,7 @@ import timber.log.Timber
import java.math.BigDecimal
import java.math.BigInteger
@Suppress("LargeClass")
internal class DefaultTransactionRepository(
private val walletManagersFacade: WalletManagersFacade,
private val walletManagersStore: WalletManagersStore,
@ -144,6 +146,53 @@ internal class DefaultTransactionRepository(
)
}
override suspend fun createNFTTransferTransaction(
ownerAddress: String,
nftAsset: NFTAsset,
fee: Fee?,
memo: String?,
destinationAddress: String,
userWalletId: UserWalletId,
network: Network,
): TransactionData.Uncompiled = withContext(coroutineDispatcherProvider.io) {
val blockchain = Blockchain.fromId(network.id.value)
val nftTransferCallData = SmartContractCallDataProviderFactory.getNFTTransferCallData(
destinationAddress = destinationAddress,
ownerAddress = ownerAddress,
nftAsset = nftAsset,
blockchain = blockchain,
)
val extras = if (nftTransferCallData != null) {
createTransactionDataExtras(
callData = nftTransferCallData,
network = network,
nonce = null,
gasLimit = null,
)
} else {
null
}
return@withContext createTransaction(
amount = Amount(
value = nftAsset.amount?.toBigDecimal() ?: error("Invalid amount"),
token = Token(
symbol = blockchain.currency,
contractAddress = "",
decimals = nftAsset.decimals ?: error("Invalid decimals"),
),
),
fee = fee,
memo = memo,
destination = destinationAddress,
userWalletId = userWalletId,
network = network,
txExtras = extras,
)
}
override suspend fun validateTransaction(
amount: Amount,
fee: Fee?,

View file

@ -0,0 +1,21 @@
package com.tangem.domain.core.serialization
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.math.BigInteger
object BigIntegerSerializer : KSerializer<BigInteger> {
override val descriptor = PrimitiveSerialDescriptor(serialName = "BigInteger", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: BigInteger) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): BigInteger {
return BigInteger(decoder.decodeString())
}
}

View file

@ -0,0 +1,6 @@
package com.tangem.domain.core.serialization
import kotlinx.serialization.Serializable
import java.math.BigInteger
typealias SerializedBigInteger = @Serializable(with = BigIntegerSerializer::class) BigInteger

View file

@ -1,5 +1,6 @@
package com.tangem.domain.nft.models
import com.tangem.domain.core.serialization.SerializedBigInteger
import com.tangem.domain.models.StatusSource
import com.tangem.domain.tokens.model.Network
import kotlinx.serialization.Serializable
@ -13,6 +14,8 @@ data class NFTAsset(
val owner: String?,
val name: String?,
val description: String?,
val amount: SerializedBigInteger?,
val decimals: Int?,
val salePrice: NFTSalePrice,
val rarity: Rarity?,
val media: Media?,
@ -44,10 +47,17 @@ data class NFTAsset(
@Serializable
data class EVM(
val tokenId: String,
val tokenAddress: String,
val tokenId: SerializedBigInteger,
val contractType: ContractType,
) : Identifier() {
override val stringValue: String = "${tokenAddress}_$tokenId"
override val stringValue: String = "${contractType}_$tokenId"
enum class ContractType {
ERC1155,
ERC721,
Unknown,
}
}
@Serializable

View file

@ -14,6 +14,7 @@ dependencies {
implementation(projects.core.utils)
implementation(projects.core.ui)
implementation(projects.core.datasource)
/** Tangem SDKs */
implementation(tangemDeps.card.core)
@ -22,10 +23,11 @@ dependencies {
}
implementation(tangemDeps.blockchain)
implementation(projects.domain.models)
implementation(projects.domain.legacy)
implementation(projects.libs.blockchainSdk)
implementation(projects.libs.crypto)
implementation(projects.domain.models)
implementation(projects.domain.legacy)
implementation(projects.domain.wallets.models)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)

View file

@ -5,6 +5,7 @@ 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.blockchain.nft.models.NFTAsset
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWalletId
@ -34,6 +35,17 @@ interface TransactionRepository {
network: Network,
): TransactionData.Uncompiled
@Suppress("LongParameterList")
suspend fun createNFTTransferTransaction(
ownerAddress: String,
nftAsset: NFTAsset,
fee: Fee?,
memo: String?,
destinationAddress: String,
userWalletId: UserWalletId,
network: Network,
): TransactionData.Uncompiled
@Suppress("LongParameterList")
suspend fun createApprovalTransaction(
amount: Amount,

View file

@ -34,4 +34,23 @@ class CreateApprovalTransactionUseCase(
fee = fee,
)
}
@Suppress("LongParameterList")
suspend operator fun invoke(
cryptoCurrency: CryptoCurrency.Token,
userWalletId: UserWalletId,
amount: BigDecimal?,
contractAddress: String,
spenderAddress: String,
) = Either.catch {
transactionRepository.createApprovalTransaction(
amount = BigDecimal.ZERO.convertToSdkAmount(cryptoCurrency),
approvalAmount = amount?.convertToSdkAmount(cryptoCurrency),
contractAddress = contractAddress,
spenderAddress = spenderAddress,
userWalletId = userWalletId,
network = cryptoCurrency.network,
fee = null,
)
}
}

View file

@ -0,0 +1,59 @@
package com.tangem.domain.transaction.usecase
import arrow.core.Either
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.nft.models.NFTAsset
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.transaction.TransactionRepository
import com.tangem.domain.wallets.models.UserWalletId
class CreateNFTTransferTransactionUseCase(
private val transactionRepository: TransactionRepository,
) {
/**
* [REDACTED_TODO_COMMENT]
*/
@Suppress("LongParameterList")
suspend operator fun invoke(
ownerAddress: String,
nftAsset: NFTAsset,
fee: Fee,
memo: String?,
destinationAddress: String,
userWalletId: UserWalletId,
network: Network,
) = Either.catch {
transactionRepository.createNFTTransferTransaction(
ownerAddress = ownerAddress,
nftAsset = nftAsset,
destinationAddress = destinationAddress,
fee = fee,
memo = memo,
userWalletId = userWalletId,
network = network,
)
}
/**
* [REDACTED_TODO_COMMENT]
*/
@Suppress("LongParameterList")
suspend operator fun invoke(
ownerAddress: String,
nftAsset: NFTAsset,
memo: String?,
destinationAddress: String,
userWalletId: UserWalletId,
network: Network,
) = Either.catch {
transactionRepository.createNFTTransferTransaction(
ownerAddress = ownerAddress,
nftAsset = nftAsset,
destinationAddress = destinationAddress,
fee = null,
memo = memo,
userWalletId = userWalletId,
network = network,
)
}
}

View file

@ -89,7 +89,7 @@ internal class NFTDetailsUMFactory(
),
NFTAssetUM.BlockItem(
title = resourceReference(R.string.nft_details_token_id),
value = id.tokenId,
value = id.tokenId.toString(),
),
NFTAssetUM.BlockItem(
title = resourceReference(R.string.nft_details_chain),

View file

@ -26,6 +26,7 @@ dependencies {
implementation(projects.core.analytics)
implementation(projects.core.configToggles)
implementation(projects.core.navigation)
implementation(projects.core.datasource)
api(projects.core.pagination)
/** Tangem SDK */

View file

@ -433,6 +433,7 @@ internal class SendConfirmModel @Inject constructor(
).filter { it.second is CommonSendRoute.Confirm }.onEach { (state, _) ->
val amountUM = state.amountUM as? AmountState.Data
val confirmUM = state.confirmUM
val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isSending
params.callback.onResult(
state.copy(
navigationUM = NavigationUM.Content(
@ -462,9 +463,9 @@ internal class SendConfirmModel @Inject constructor(
}
else -> resourceReference(R.string.common_send)
},
iconResId = R.drawable.ic_tangem_24.takeIf { confirmUM is ConfirmUM.Content },
iconResId = R.drawable.ic_tangem_24.takeIf { isReadyToSend },
isEnabled = confirmUM.isPrimaryButtonEnabled,
isHapticClick = confirmUM is ConfirmUM.Content && !confirmUM.isSending,
isHapticClick = isReadyToSend,
onClick = {
when (confirmUM) {
is ConfirmUM.Success -> appRouter.pop()

View file

@ -2,6 +2,7 @@ package com.tangem.features.send.v2.sendnft.confirm.model
import android.os.SystemClock
import arrow.core.getOrElse
import com.tangem.blockchain.common.TransactionData
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
@ -12,9 +13,14 @@ import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
import com.tangem.domain.settings.NeverShowTapHelpUseCase
import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.SendBalanceUpdater
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.impl.R
@ -37,6 +43,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
@ -49,21 +56,32 @@ internal class NFTSendConfirmModel @Inject constructor(
private val appRouter: AppRouter,
private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase,
private val neverShowTapHelpUseCase: NeverShowTapHelpUseCase,
private val createNFTTransferTransactionUseCase: CreateNFTTransferTransactionUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val notificationsUpdateTrigger: NotificationsUpdateTrigger,
private val sendFeeCheckReloadTrigger: SendFeeCheckReloadTrigger,
private val sendFeeCheckReloadListener: SendFeeCheckReloadListener,
private val urlOpener: UrlOpener,
private val shareManager: ShareManager,
private val analyticsEventHandler: AnalyticsEventHandler,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), NFTSendConfirmClickIntents {
private val params: NFTSendConfirmComponent.Params = paramsContainer.require()
private val userWallet = params.userWallet
private val cryptoCurrencyStatus = params.cryptoCurrencyStatus
private val cryptoCurrency = cryptoCurrencyStatus.currency
private val _uiState = MutableStateFlow(params.state)
val uiState = _uiState.asStateFlow()
private val sendBalanceUpdater = sendBalanceUpdaterFactory.create(
cryptoCurrency = cryptoCurrency,
userWallet = userWallet,
)
private val destinationUM
get() = uiState.value.destinationUM as? DestinationUM.Content
private val feeUM
@ -191,34 +209,63 @@ internal class NFTSendConfirmModel @Inject constructor(
}
private fun verifyAndSendTransaction() {
// TODO:
val destination = destinationUM?.addressTextField?.value ?: return
val memo = destinationUM?.memoTextField?.value
val fee = feeSelectorUM?.selectedFee ?: return
val ownerAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value ?: return
val sdkNFTAsset = NFTSdkAssetConverter.convertBack(params.nftAsset)
modelScope.launch {
createNFTTransferTransactionUseCase(
ownerAddress = ownerAddress,
nftAsset = sdkNFTAsset.second,
fee = fee,
memo = memo,
destinationAddress = destination,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
).fold(
ifLeft = { error ->
Timber.e(error)
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
// alertFactory.getGenericErrorState {
// onFailedTxEmailClick(error.localizedMessage.orEmpty())
// }
},
ifRight = { txData ->
sendTransaction(txData)
},
)
}
}
// private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
// val result = sendTransactionUseCase(
// txData = txData,
// userWallet = userWallet,
// network = cryptoCurrency.network,
// )
//
// _uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
//
// result.fold(
// ifLeft = { error ->
// // alertFactory.getSendTransactionErrorState(
// // error = error,
// // popBack = appRouter::pop,
// // onFailedTxEmailClick = ::onFailedTxEmailClick,
// // )
// analyticsEventHandler.send(SendAnalyticEvents.TransactionError(cryptoCurrency.symbol))
// },
// ifRight = {
// updateTransactionStatus(txData)
// sendBalanceUpdater.scheduleUpdates()
// // sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
// },
// )
// }
private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
val result = sendTransactionUseCase(
txData = txData,
userWallet = params.userWallet,
network = params.cryptoCurrencyStatus.currency.network,
)
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
result.fold(
ifLeft = { error ->
Timber.e(error.toString())
// alertFactory.getSendTransactionErrorState(
// error = error,
// popBack = appRouter::pop,
// onFailedTxEmailClick = ::onFailedTxEmailClick,
// )
// analyticsEventHandler.send(SendAnalyticEvents.TransactionError(cryptoCurrency.symbol))
},
ifRight = {
updateTransactionStatus(txData)
sendBalanceUpdater.scheduleUpdates()
// sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
},
)
}
private fun subscribeOnCheckFeeResultUpdates() {
sendFeeCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
@ -232,13 +279,13 @@ internal class NFTSendConfirmModel @Inject constructor(
}.launchIn(modelScope)
}
// private suspend fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
// val txUrl = getExplorerTransactionUrlUseCase(
// userWalletId = userWallet.walletId,
// network = cryptoCurrency.network,
// ).getOrElse { "" }
// _uiState.update(NFTSendConfirmSentStateTransformer(txData, txUrl))
// }
private fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
val txUrl = getExplorerTransactionUrlUseCase(
txHash = txData.hash.orEmpty(),
networkId = cryptoCurrency.network.id,
).getOrElse { "" }
_uiState.update(NFTSendConfirmSentStateTransformer(txData, txUrl))
}
private fun updateConfirmNotifications() {
modelScope.launch {
@ -272,6 +319,7 @@ internal class NFTSendConfirmModel @Inject constructor(
transform = { state, route -> state to route },
).onEach { (state, _) ->
val confirmUM = state.confirmUM
val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isSending
params.callback.onResult(
state.copy(
navigationUM = NavigationUM.Content(
@ -301,9 +349,9 @@ internal class NFTSendConfirmModel @Inject constructor(
}
else -> resourceReference(R.string.common_send)
},
iconResId = R.drawable.ic_tangem_24.takeIf { confirmUM is ConfirmUM.Content },
iconResId = R.drawable.ic_tangem_24.takeIf { isReadyToSend },
isEnabled = confirmUM.isPrimaryButtonEnabled,
isHapticClick = confirmUM is ConfirmUM.Content && !confirmUM.isSending,
isHapticClick = isReadyToSend,
onClick = {
when (confirmUM) {
is ConfirmUM.Success -> appRouter.pop()

View file

@ -3,11 +3,13 @@ package com.tangem.features.send.v2.sendnft.model
import androidx.compose.runtime.Stable
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.left
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.util.cardTypesResolver
@ -17,6 +19,8 @@ import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.NFTSendComponent
@ -52,6 +56,8 @@ internal class NFTSendModel @Inject constructor(
private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val createNFTTransferTransactionUseCase: CreateNFTTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
) : Model(), SendNFTComponentCallback {
val params: NFTSendComponent.Params = paramsContainer.require()
@ -92,20 +98,29 @@ internal class NFTSendModel @Inject constructor(
}
suspend fun loadFee(): Either<GetFeeError, TransactionFee> {
TODO()
// val destinationUM = uiState.value.destinationUM as? DestinationUM.Content ?: error("Invalid destination")
// val ownerAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
// ?: error("Invalid owner address")
// val enteredDestinationAddress = destinationUM.addressTextField.value
// val nftAsset = null // NFTSdkAssetConverter.convertBack(params.nftAsset).second
val destinationUM = uiState.value.destinationUM as? DestinationUM.Content ?: error("Invalid destination")
val ownerAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
?: error("Invalid owner address")
val enteredDestinationAddress = destinationUM.addressTextField.value
val enteredMemo = destinationUM.memoTextField?.value
val nftAsset = NFTSdkAssetConverter.convertBack(params.nftAsset).second
// getNFTTransferFeeUseCase(
// ownerAddress = ownerAddress,
// destinationAddress = enteredDestinationAddress,
// nftAsset = nftAsset,
// userWallet = userWallet,
// cryptoCurrency = cryptoCurrency,
// )
val nftSendTransaction = createNFTTransferTransactionUseCase(
ownerAddress = ownerAddress,
nftAsset = nftAsset,
memo = enteredMemo,
destinationAddress = enteredDestinationAddress,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
).getOrElse {
return GetFeeError.DataError(it).left()
}
return getFeeUseCase(
transactionData = nftSendTransaction,
network = cryptoCurrency.network,
userWallet = userWallet,
)
}
private fun initAppCurrency() {

View file

@ -192,7 +192,6 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
cryptoCurrency = tokenCurrency,
userWalletId = userWallet.walletId,
amount = amount,
fee = null,
contractAddress = tokenCurrency.contractAddress,
spenderAddress = validatorAddress,
).getOrElse {

View file

@ -5,7 +5,7 @@
# https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico
tangemBlockchainSdk = "develop-1042"
tangemBlockchainSdk = "develop-1046"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-455"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^