Updated on 2026-08-14
This commit is contained in:
parent
9cf90df510
commit
1c5519a702
19 changed files with 327 additions and 83 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<AppState>? = null
|
||||
var tangemSdkManager: TangemSdkManager? = null
|
||||
var tangemSdk: TangemSdk? = null
|
||||
|
||||
fun getActualCard(): CardDTO? {
|
||||
return scanResponse?.card
|
||||
|
|
|
|||
133
app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt
Normal file
133
app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt
Normal file
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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/"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.datasource.di
|
||||
|
||||
import com.tangem.datasource.api.oneinch.OneInchApi
|
||||
|
||||
class OneInchApiFactory {
|
||||
|
||||
private val oneInchApiMap = mutableMapOf<String, OneInchApi>()
|
||||
|
||||
fun putApi(networkId: String, api: OneInchApi) {
|
||||
oneInchApiMap[networkId] = api
|
||||
}
|
||||
|
||||
fun getApi(networkId: String): OneInchApi {
|
||||
return oneInchApiMap[networkId] ?: error("no api found for networkId $networkId")
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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<TokenData, Currency> {
|
||||
|
|
|
|||
|
|
@ -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<QuoteModel> {
|
||||
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<String> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ interface SwapRepository {
|
|||
suspend fun getExchangeableTokens(networkId: String): List<Currency>
|
||||
|
||||
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<String>
|
||||
suspend fun checkTokensSpendAllowance(
|
||||
networkId: String,
|
||||
tokenAddress: String,
|
||||
walletAddress: String,
|
||||
): AggregatedSwapDataModel<String>
|
||||
|
||||
suspend fun prepareSwapTransaction(
|
||||
networkId: String,
|
||||
fromTokenAddress: String,
|
||||
toTokenAddress: String,
|
||||
amount: String,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
|
||||
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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue