From 1c5519a70200c609caed790e8767a1c8ef190883 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 22 Dec 2022 16:25:58 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 1 + .../com/tangem/tap/proxy/AppStateHolder.kt | 5 +- .../tap/proxy/TransactionManagerImpl.kt | 133 ++++++++++++++++++ .../com/tangem/tap/proxy/di/ProxyModule.kt | 8 ++ .../com/tangem/datasource/di/NetworkModule.kt | 23 --- .../tangem/datasource/di/OneInchApiFactory.kt | 16 +++ .../tangem/datasource/di/OneInchApisModule.kt | 53 +++++++ .../di/qualifiers/OneInchApiQualifiers.kt | 15 -- .../domain/converter/TokensConverter.kt | 1 + .../tangem/feature/swap/SwapRepositoryImpl.kt | 30 ++-- .../tangem/feature/swap/di/SwapDataModule.kt | 7 +- .../feature/swap/domain/SwapInteractorImpl.kt | 35 ++++- .../feature/swap/domain/SwapRepository.kt | 11 +- .../swap/domain/di/SwapDomainModule.kt | 7 +- .../tangem/lib/crypto/TransactionManager.kt | 17 +++ .../com/tangem/lib/crypto/models/Currency.kt | 20 ++- .../tangem/lib/crypto/models/NativeToken.kt | 8 -- .../lib/crypto/models/NonNativeToken.kt | 10 -- .../models/transactions/SendTxResult.kt | 10 ++ 19 files changed, 327 insertions(+), 83 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/OneInchApiFactory.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/OneInchApisModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/qualifiers/OneInchApiQualifiers.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt delete mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/NativeToken.kt delete mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/NonNativeToken.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 5ef7c985a6..9007fc7694 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -79,6 +79,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac tangemSdk = TangemSdk.init(this, TangemSdkManager.config) tangemSdkManager = TangemSdkManager(tangemSdk, this) appStateHolder.tangemSdkManager = tangemSdkManager + appStateHolder.tangemSdk = tangemSdk backupService = BackupService.init(tangemSdk, this) userWalletsListManager = UserWalletsListManager.provideBiometricImplementation( context = applicationContext, diff --git a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt index 11cfdee61f..4bc04e994e 100644 --- a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt @@ -1,5 +1,6 @@ package com.tangem.tap.proxy +import com.tangem.TangemSdk import com.tangem.domain.common.CardDTO import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.redux.AppState @@ -7,18 +8,20 @@ import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.domain.tokens.UserTokensRepository import com.tangem.tap.features.wallet.redux.WalletState import org.rekotlin.Store +import javax.inject.Inject /** * Holds objects from old modules, that missing in DI graph * Object sets manually to use in new modules and [AppStateHolder] proxies its to DI */ -class AppStateHolder { +class AppStateHolder @Inject constructor(){ var scanResponse: ScanResponse? = null var walletState: WalletState? = null var userTokensRepository: UserTokensRepository? = null var mainStore: Store? = null var tangemSdkManager: TangemSdkManager? = null + var tangemSdk: TangemSdk? = null fun getActualCard(): CardDTO? { return scanResponse?.card diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt new file mode 100644 index 0000000000..9cbeec23f9 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -0,0 +1,133 @@ +package com.tangem.tap.proxy + +import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.tangem.Message +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.BlockchainSdkError +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.TransactionSender +import com.tangem.blockchain.common.TransactionSigner +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.common.core.TangemSdkError +import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.lib.crypto.TransactionManager +import com.tangem.lib.crypto.models.Currency +import com.tangem.lib.crypto.models.NativeToken +import com.tangem.lib.crypto.models.NonNativeToken +import com.tangem.lib.crypto.models.transactions.SendTxResult +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.domain.TangemSigner +import com.tangem.tap.domain.tokens.models.BlockchainNetwork +import com.tangem.tap.store +import com.tangem.tap.tangemSdk +import java.math.BigDecimal + +class TransactionManagerImpl( + private val appStateHolder: AppStateHolder, +) : TransactionManager { + + @Throws(IllegalStateException::class) + override suspend fun sendTransaction( + networkId: String, + amountToSend: BigDecimal, + currencyToSend: Currency, + feeAmount: BigDecimal, + destinationAddress: String, + dataToSign: String, + ): SendTxResult { + val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } + val walletManager = getActualWalletManager(blockchain) + val amount = when (currencyToSend) { + is NativeToken -> { + Amount(value = amountToSend, blockchain = blockchain) + } + is NonNativeToken -> { + Amount( + convertNonNativeToken(currencyToSend), + amountToSend, + ) + } + } + val txData = walletManager.createTransaction( + amount = amount, + fee = Amount(value = feeAmount, blockchain = blockchain), + destination = destinationAddress, + ).copy(hash = dataToSign) + + val signer = transactionSigner(walletManager) + + val sendResult = try { + (walletManager as TransactionSender).send(txData, signer) + } catch (ex: Exception) { + FirebaseCrashlytics.getInstance().recordException(ex) + return SendTxResult.UnknownError(ex) + } + return handleSendResult(sendResult) + } + + private fun handleSendResult(result: SimpleResult): SendTxResult { + when (result) { + is SimpleResult.Success -> { + return SendTxResult.Success + } + is SimpleResult.Failure -> { + val error = (result.error as? BlockchainSdkError) ?: return SendTxResult.UnknownError() + when (error) { + is BlockchainSdkError.WrappedTangemError -> { + val tangemSdkError = (error.tangemError as? TangemSdkError) + ?: return SendTxResult.UnknownError() + if (tangemSdkError is TangemSdkError.UserCancelled) return SendTxResult.UserCancelledError + return SendTxResult.TangemSdkError(tangemSdkError.code, tangemSdkError.cause) + } + else -> { + return SendTxResult.TangemSdkError(error.code, error.cause) + } + } + } + } + } + + private fun transactionSigner(walletManager: WalletManager): TransactionSigner { + val actualCard = requireNotNull(appStateHolder.getActualCard()) { "no card found" } + return TangemSigner( + card = actualCard, + tangemSdk = tangemSdk, + initialMessage = Message("test transaction title", "test description"), + ) { signResponse -> + store.dispatch( + GlobalAction.UpdateWalletSignedHashes( + walletSignedHashes = signResponse.totalSignedHashes, + walletPublicKey = walletManager.wallet.publicKey.seedKey, + remainingSignatures = signResponse.remainingSignatures, + ), + ) + } + } + + private fun getActualWalletManager(blockchain: Blockchain): WalletManager { + val card = appStateHolder.getActualCard() + if (card != null) { + val blockchainNetwork = BlockchainNetwork(blockchain, card) + val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork) + if (walletManager != null) { + return walletManager + } else { + error("no wallet manager found") + } + } else { + error("card not found") + } + } + + private fun convertNonNativeToken(token: NonNativeToken): Token { + return Token( + name = token.name, + symbol = token.symbol, + contractAddress = token.contractAddress, + decimals = token.decimalCount, + id = token.id, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt index 07d3fb1e8d..1a65b1ddd0 100644 --- a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt +++ b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt @@ -2,9 +2,11 @@ package com.tangem.tap.proxy.di import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.lib.crypto.DerivationManager +import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.DerivationManagerImpl +import com.tangem.tap.proxy.TransactionManagerImpl import com.tangem.tap.proxy.UserWalletManagerImpl import dagger.Module import dagger.Provides @@ -31,6 +33,12 @@ class ProxyModule { ) } + @Provides + @Singleton + fun provideTransactionManager(appStateHolder: AppStateHolder): TransactionManager { + return TransactionManagerImpl(appStateHolder) + } + @Provides @Singleton fun provideDerivationManager(appStateHolder: AppStateHolder): DerivationManager { diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt index a79330cfcf..51dabf7253 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt @@ -4,10 +4,8 @@ import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import com.tangem.datasource.api.AuthHeaderInterceptor import com.tangem.datasource.api.common.BigDecimalAdapter -import com.tangem.datasource.api.oneinch.OneInchApi import com.tangem.datasource.api.referral.ReferralApi import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.di.qualifiers.OneInchEthereum import com.tangem.lib.auth.AuthProvider import dagger.Module import dagger.Provides @@ -39,26 +37,6 @@ class NetworkModule { .create(TangemTechApi::class.java) } - @Provides - @Singleton - @OneInchEthereum - fun provideOneInchEthereumApi(moshi: Moshi): OneInchApi { - return Retrofit.Builder() - .addConverterFactory( - MoshiConverterFactory.create(moshi), - ) - .baseUrl(ONE_INCH_ETHER_BASE_URL) - .client( - OkHttpClient.Builder() - .addInterceptor( - HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY), - ) - .build(), - ) - .build() - .create(OneInchApi::class.java) - } - @Provides @Singleton fun provideReferralApi(okHttpClient: OkHttpClient, moshi: Moshi): ReferralApi { @@ -95,6 +73,5 @@ class NetworkModule { private companion object { const val PROD_TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v1/" const val DEV_TANGEM_TECH_BASE_URL = "https://devapi.tangem-tech.com/v1/" - const val ONE_INCH_ETHER_BASE_URL = "https://api.1inch.io/v5.0/1/" } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/OneInchApiFactory.kt b/core/datasource/src/main/java/com/tangem/datasource/di/OneInchApiFactory.kt new file mode 100644 index 0000000000..e9493488d1 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/OneInchApiFactory.kt @@ -0,0 +1,16 @@ +package com.tangem.datasource.di + +import com.tangem.datasource.api.oneinch.OneInchApi + +class OneInchApiFactory { + + private val oneInchApiMap = mutableMapOf() + + fun putApi(networkId: String, api: OneInchApi) { + oneInchApiMap[networkId] = api + } + + fun getApi(networkId: String): OneInchApi { + return oneInchApiMap[networkId] ?: error("no api found for networkId $networkId") + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/OneInchApisModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/OneInchApisModule.kt new file mode 100644 index 0000000000..e23d2849c2 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/OneInchApisModule.kt @@ -0,0 +1,53 @@ +package com.tangem.datasource.di + +import com.squareup.moshi.Moshi +import com.tangem.datasource.api.oneinch.OneInchApi +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class OneInchApisModule { + + @Provides + @Singleton + fun provideOneInchApiFactory(moshi: Moshi): OneInchApiFactory { + val apiFactory = OneInchApiFactory() + apiFactory.putApi(ETH_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_ETH_PATH, moshi)) + apiFactory.putApi(BSC_NETWORK, createOneInchApiWithUrl(ONE_INCH_BASE_URL + ONE_INCH_BSC_PATH, moshi)) + return apiFactory + } + + private fun createOneInchApiWithUrl(url: String, moshi: Moshi): OneInchApi { + return Retrofit.Builder() + .addConverterFactory( + MoshiConverterFactory.create(moshi), + ) + .baseUrl(url) + .client( + OkHttpClient.Builder() + .addInterceptor( + HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY), + ) + .build(), + ) + .build() + .create(OneInchApi::class.java) + } + + companion object { + private const val ONE_INCH_BASE_URL = "https://api.1inch.io/v5.0/" + private const val ONE_INCH_ETH_PATH = "1/" + private const val ONE_INCH_BSC_PATH = "56/" + + private const val ETH_NETWORK = "ethereum" + private const val BSC_NETWORK = "binance-smart-chain" + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/qualifiers/OneInchApiQualifiers.kt b/core/datasource/src/main/java/com/tangem/datasource/di/qualifiers/OneInchApiQualifiers.kt deleted file mode 100644 index f312541fde..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/qualifiers/OneInchApiQualifiers.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.datasource.di.qualifiers - -import javax.inject.Qualifier - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class OneInchEthereum - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class OneInchBinance - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class OneInchOptimism \ No newline at end of file diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt index 2f756567f1..8abe25e73c 100644 --- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt +++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt @@ -5,6 +5,7 @@ import com.tangem.lib.crypto.models.Currency import com.tangem.lib.crypto.models.NativeToken import com.tangem.lib.crypto.models.NonNativeToken import com.tangem.utils.converter.Converter + import javax.inject.Inject class TokensConverter @Inject constructor() : Converter { diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt index d37105e46e..c7d8cd3d5c 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt @@ -4,6 +4,7 @@ import com.tangem.datasource.api.oneinch.OneInchApi import com.tangem.datasource.api.oneinch.OneInchErrorsHandler import com.tangem.datasource.api.oneinch.errors.OneIncResponseException import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.di.OneInchApiFactory import com.tangem.feature.swap.converters.ApproveConverter import com.tangem.feature.swap.converters.QuotesConverter import com.tangem.feature.swap.converters.SwapConverter @@ -21,7 +22,7 @@ import javax.inject.Inject internal class SwapRepositoryImpl @Inject constructor( private val tangemTechApi: TangemTechApi, - private val oneInchApi: OneInchApi, + private val oneInchApiFactory: OneInchApiFactory, private val tokensConverter: TokensConverter, private val quotesConverter: QuotesConverter, private val swapConverter: SwapConverter, @@ -36,12 +37,16 @@ internal class SwapRepositoryImpl @Inject constructor( } } - override suspend fun findBestQuote(fromTokenAddress: String, toTokenAddress: String, amount: String): + override suspend fun findBestQuote( + networkId: String, fromTokenAddress: String, toTokenAddress: String, + amount: + String, + ): AggregatedSwapDataModel { return withContext(coroutineDispatcher.io) { try { val response = oneInchErrorsHandler.handleOneInchResponse( - oneInchApi.quote( + getOneInchApi(networkId).quote( fromTokenAddress = fromTokenAddress, toTokenAddress = toTokenAddress, amount = amount, @@ -54,24 +59,24 @@ internal class SwapRepositoryImpl @Inject constructor( } } - override suspend fun addressForTrust(): String { + override suspend fun addressForTrust(networkId: String): String { return withContext(coroutineDispatcher.io) { - oneInchApi.approveSpender().address + getOneInchApi(networkId).approveSpender().address } } - override suspend fun dataToApprove(tokenAddress: String, amount: String?): ApproveModel { + override suspend fun dataToApprove(networkId: String, tokenAddress: String, amount: String?): ApproveModel { return withContext(coroutineDispatcher.io) { - approveConverter.convert(oneInchApi.approveTransaction(tokenAddress, amount)) + approveConverter.convert(getOneInchApi(networkId).approveTransaction(tokenAddress, amount)) } } - override suspend fun checkTokensSpendAllowance(tokenAddress: String, walletAddress: String): + override suspend fun checkTokensSpendAllowance(networkId: String, tokenAddress: String, walletAddress: String): AggregatedSwapDataModel { return withContext(coroutineDispatcher.io) { try { val response = oneInchErrorsHandler.handleOneInchResponse( - oneInchApi.approveAllowance(tokenAddress, walletAddress) + getOneInchApi(networkId).approveAllowance(tokenAddress, walletAddress), ) AggregatedSwapDataModel(response.allowance) } catch (ex: OneIncResponseException) { @@ -81,6 +86,7 @@ internal class SwapRepositoryImpl @Inject constructor( } override suspend fun prepareSwapTransaction( + networkId: String, fromTokenAddress: String, toTokenAddress: String, amount: String, @@ -90,7 +96,7 @@ internal class SwapRepositoryImpl @Inject constructor( return withContext(coroutineDispatcher.io) { try { val swapResponse = oneInchErrorsHandler.handleOneInchResponse( - oneInchApi.swap( + getOneInchApi(networkId).swap( fromTokenAddress = fromTokenAddress, toTokenAddress = toTokenAddress, amount = amount, @@ -104,4 +110,8 @@ internal class SwapRepositoryImpl @Inject constructor( } } } + + private fun getOneInchApi(networkId: String): OneInchApi { + return oneInchApiFactory.getApi(networkId) + } } \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt index 0fdb016f7b..bb35020942 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/di/SwapDataModule.kt @@ -1,9 +1,8 @@ package com.tangem.feature.swap.di -import com.tangem.datasource.api.oneinch.OneInchApi import com.tangem.datasource.api.oneinch.OneInchErrorsHandler import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.di.qualifiers.OneInchEthereum +import com.tangem.datasource.di.OneInchApiFactory import com.tangem.feature.swap.SwapRepositoryImpl import com.tangem.feature.swap.converters.ApproveConverter import com.tangem.feature.swap.converters.QuotesConverter @@ -25,7 +24,7 @@ class SwapDataModule { @Singleton fun provideSwapRepository( tangemTechApi: TangemTechApi, - @OneInchEthereum oneInchApi: OneInchApi, + oneInchApiFactory: OneInchApiFactory, tokensConverter: TokensConverter, quotesConverter: QuotesConverter, swapConverter: SwapConverter, @@ -35,7 +34,7 @@ class SwapDataModule { ): SwapRepository { return SwapRepositoryImpl( tangemTechApi = tangemTechApi, - oneInchApi = oneInchApi, + oneInchApiFactory = oneInchApiFactory, tokensConverter = tokensConverter, quotesConverter = quotesConverter, swapConverter = swapConverter, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 3a989bb4c6..43862840fb 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -5,10 +5,13 @@ import com.tangem.feature.swap.domain.models.data.Currency import com.tangem.feature.swap.domain.models.data.DataError import com.tangem.feature.swap.domain.models.data.SwapState import com.tangem.feature.swap.domain.models.data.TransactionModel +import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager +import java.math.BigDecimal import javax.inject.Inject internal class SwapInteractorImpl @Inject constructor( + private val transactionManager: TransactionManager, private val userWalletManager: UserWalletManager, private val repository: SwapRepository, private val cache: SwapDataCache, @@ -29,21 +32,40 @@ internal class SwapInteractorImpl @Inject constructor( } override suspend fun givePermissionToSwap(tokenAddress: String) { - val oneInchAddress = repository.addressForTrust() - val transactionData = repository.dataToApprove(tokenAddress) - //todo sign transaction (next tasks) + cache.getNetworkId()?.let { networkId -> + val oneInchAddress = repository.addressForTrust(networkId) + val transactionData = repository.dataToApprove(networkId, tokenAddress) + //todo for test + // transactionManager.sendTransaction( + // networkId = networkId, + // amountToSend = BigDecimal.ZERO, + // currencyToSend = , + // feeAmount = BigDecimal.ZERO, + // destinationAddress = oneInchAddress, + // dataToSign = transactionData.data + // ) + } } override suspend fun findBestQuote(fromTokenAddress: String, toTokenAddress: String, amount: String): SwapState { val networkId = cache.getNetworkId() val isAllowedToSpend = if (networkId.isNullOrEmpty()) { - false + error("no networkId found, please call getTokensToSwap first") } else { val allowance = - repository.checkTokensSpendAllowance(fromTokenAddress, userWalletManager.getWalletAddress(networkId)) + repository.checkTokensSpendAllowance( + networkId = networkId, + tokenAddress = fromTokenAddress, + walletAddress = userWalletManager.getWalletAddress(networkId), + ) allowance.error == DataError.NO_ERROR && allowance.dataModel != "0" } - repository.findBestQuote(fromTokenAddress, toTokenAddress, amount).let { quotes -> + repository.findBestQuote( + networkId = networkId, + fromTokenAddress = fromTokenAddress, + toTokenAddress = toTokenAddress, + amount = amount, + ).let { quotes -> val quoteDataModel = quotes.dataModel if (quoteDataModel != null) { cache.cacheSwapParams(quoteDataModel.copy(isAllowedToSpend = isAllowedToSpend), amount) @@ -60,6 +82,7 @@ internal class SwapInteractorImpl @Inject constructor( val networkId = cache.getNetworkId() if (quoteModel != null && amountToSwap != null && networkId != null) { repository.prepareSwapTransaction( + networkId = networkId, fromTokenAddress = quoteModel.fromTokenAmount, toTokenAddress = quoteModel.toTokenAmount, amount = amountToSwap, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt index 01f534a296..55a973b7e9 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt @@ -11,6 +11,7 @@ interface SwapRepository { suspend fun getExchangeableTokens(networkId: String): List suspend fun findBestQuote( + networkId: String, fromTokenAddress: String, toTokenAddress: String, amount: String, @@ -21,7 +22,7 @@ interface SwapRepository { * * @return address */ - suspend fun addressForTrust(): String + suspend fun addressForTrust(networkId: String): String /** * Generate "data" for calling contract in order to allow 1inch spend funds @@ -30,6 +31,7 @@ interface SwapRepository { * @param amount number of tokens is allowed. By default infinite */ suspend fun dataToApprove( + networkId: String, tokenAddress: String, amount: String? = null, ): ApproveModel @@ -42,9 +44,14 @@ interface SwapRepository { * * @return amount of tokens allowed to spend */ - suspend fun checkTokensSpendAllowance(tokenAddress: String, walletAddress: String): AggregatedSwapDataModel + suspend fun checkTokensSpendAllowance( + networkId: String, + tokenAddress: String, + walletAddress: String, + ): AggregatedSwapDataModel suspend fun prepareSwapTransaction( + networkId: String, fromTokenAddress: String, toTokenAddress: String, amount: String, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt index ef7b8e44c8..eb5d59171e 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt @@ -4,6 +4,7 @@ import com.tangem.feature.swap.domain.SwapInteractor import com.tangem.feature.swap.domain.SwapInteractorImpl import com.tangem.feature.swap.domain.SwapRepository import com.tangem.feature.swap.domain.cache.SwapDataCacheImpl +import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager import dagger.Module import dagger.Provides @@ -18,12 +19,14 @@ class SwapDomainModule { @Provides @Singleton fun provideSwapInteractor( - referralRepository: SwapRepository, + swapRepository: SwapRepository, userWalletManager: UserWalletManager, + transactionManager: TransactionManager, ): SwapInteractor { return SwapInteractorImpl( + transactionManager = transactionManager, userWalletManager = userWalletManager, - repository = referralRepository, + repository = swapRepository, cache = SwapDataCacheImpl(), ) } diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt new file mode 100644 index 0000000000..93a7ab6914 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt @@ -0,0 +1,17 @@ +package com.tangem.lib.crypto + +import com.tangem.lib.crypto.models.Currency +import com.tangem.lib.crypto.models.transactions.SendTxResult +import java.math.BigDecimal + +interface TransactionManager { + + suspend fun sendTransaction( + networkId: String, + amountToSend: BigDecimal, + currencyToSend: Currency, + feeAmount: BigDecimal, + destinationAddress: String, + dataToSign: String, + ): SendTxResult +} \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/Currency.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/Currency.kt index 82afac8e25..cd49b303ef 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/Currency.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/Currency.kt @@ -4,9 +4,25 @@ package com.tangem.lib.crypto.models * Currency data class that can be native blockchain Token * or custom Token (used in this lib to replace and divide logic Currency from app module) */ -abstract class Currency( +sealed class Currency( open val id: String, open val name: String, open val symbol: String, open val networkId: String, -) \ No newline at end of file +) + +data class NativeToken( + override val id: String, + override val name: String, + override val symbol: String, + override val networkId: String, +) : Currency(id, name, symbol, networkId) + +class NonNativeToken( + override val id: String, + override val name: String, + override val symbol: String, + override val networkId: String, + val contractAddress: String, + val decimalCount: Int, +) : Currency(id, name, symbol, networkId) \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/NativeToken.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/NativeToken.kt deleted file mode 100644 index f8ab7464d6..0000000000 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/NativeToken.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.lib.crypto.models - -data class NativeToken( - override val id: String, - override val name: String, - override val symbol: String, - override val networkId: String, -) : Currency(id, name, symbol, networkId) \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/NonNativeToken.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/NonNativeToken.kt deleted file mode 100644 index a2d812cd1b..0000000000 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/NonNativeToken.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.lib.crypto.models - -class NonNativeToken( - override val id: String, - override val name: String, - override val symbol: String, - override val networkId: String, - val contractAddress: String, - val decimalCount: Int, -) : Currency(id, name, symbol, networkId) \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt new file mode 100644 index 0000000000..25655c4119 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt @@ -0,0 +1,10 @@ +package com.tangem.lib.crypto.models.transactions + +sealed interface SendTxResult { + + object Success : SendTxResult + object UserCancelledError : SendTxResult + data class TangemSdkError(val code: Int, val cause: Throwable?) : SendTxResult + data class BlockchainSdkError(val code: Int, val cause: Throwable?) : SendTxResult + data class UnknownError(val ex: Exception? = null) : SendTxResult +} \ No newline at end of file