Updated on 2026-08-14
This commit is contained in:
parent
d614f69161
commit
4aa74c4b70
21 changed files with 397 additions and 70 deletions
|
|
@ -188,4 +188,12 @@ internal object TransactionDomainModule {
|
|||
): SignUseCase {
|
||||
return SignUseCase(cardSdkConfigRepository, walletManagersFacade)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCreateNFTTransferTransactionUseCase(
|
||||
transactionRepository: TransactionRepository,
|
||||
): CreateNFTTransferTransactionUseCase {
|
||||
return CreateNFTTransferTransactionUseCase(transactionRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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?,
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -192,7 +192,6 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
|
|||
cryptoCurrency = tokenCurrency,
|
||||
userWalletId = userWallet.walletId,
|
||||
amount = amount,
|
||||
fee = null,
|
||||
contractAddress = tokenCurrency.contractAddress,
|
||||
spenderAddress = validatorAddress,
|
||||
).getOrElse {
|
||||
|
|
|
|||
|
|
@ -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 ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue