Updated on 2026-08-14
This commit is contained in:
commit
1736106734
34 changed files with 425 additions and 76 deletions
|
|
@ -11,6 +11,7 @@ import com.tangem.blockchain.common.BlockchainSdkConfig
|
|||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.datasource.api.common.BigDecimalAdapter
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.domain.DomainLayer
|
||||
import com.tangem.domain.common.LogConfig
|
||||
|
|
@ -30,7 +31,6 @@ import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
|
|||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.images.createCoilImageLoader
|
||||
import com.tangem.tap.common.log.TangemLogCollector
|
||||
import com.tangem.tap.common.moshi.BigDecimalAdapter
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.appReducer
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? {
|
|||
|
||||
fun FragmentActivity.addOnBackPressedDispatcher(
|
||||
isEnabled: Boolean = true,
|
||||
onBackPressed: VoidCallback
|
||||
onBackPressed: VoidCallback,
|
||||
): OnBackPressedCallback = (object : OnBackPressedCallback(isEnabled) {
|
||||
override fun handleOnBackPressed() {
|
||||
onBackPressed()
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ class UserWalletManagerImpl(
|
|||
mainStore.dispatch(action)
|
||||
}
|
||||
|
||||
override fun getWalletAddress(currency: Currency): String {
|
||||
val blockchain = Blockchain.fromNetworkId(currency.networkId)
|
||||
override fun getWalletAddress(networkId: String): String {
|
||||
val blockchain = Blockchain.fromNetworkId(networkId)
|
||||
val card = appStateHolder.getActualCard()
|
||||
if (blockchain != null && card != null) {
|
||||
val blockchainNetwork = BlockchainNetwork(blockchain, card)
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ dependencies {
|
|||
implementation(Library.okHttp)
|
||||
implementation(Library.okHttpLogging)
|
||||
|
||||
// kapt("com.squareup.moshi:moshi-kotlin-codegen:1.14.0")
|
||||
|
||||
/** Time */
|
||||
implementation(Library.jodatime)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.common.moshi
|
||||
package com.tangem.datasource.api.common
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.ToJson
|
||||
|
|
@ -114,6 +114,7 @@ interface OneInchApi {
|
|||
*
|
||||
* @return [QuoteResponse]
|
||||
*/
|
||||
@GET("quote")
|
||||
suspend fun quote(
|
||||
@Query("fromTokenAddress") fromTokenAddress: String,
|
||||
@Query("toTokenAddress") toTokenAddress: String,
|
||||
|
|
@ -174,6 +175,7 @@ interface OneInchApi {
|
|||
*
|
||||
* @return [SwapResponse]
|
||||
*/
|
||||
@GET("swap")
|
||||
suspend fun swap(
|
||||
@Query("fromTokenAddress") fromTokenAddress: String,
|
||||
@Query("toTokenAddress") toTokenAddress: String,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.datasource.di
|
|||
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
|
||||
|
|
@ -14,6 +15,7 @@ import dagger.hilt.InstallIn
|
|||
import dagger.hilt.components.SingletonComponent
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Converter
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import javax.inject.Singleton
|
||||
|
|
@ -24,11 +26,12 @@ class NetworkModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideTangemTechApi(okHttpClient: OkHttpClient): TangemTechApi {
|
||||
fun provideTangemTechApi(
|
||||
okHttpClient: OkHttpClient,
|
||||
converter: Converter.Factory,
|
||||
): TangemTechApi {
|
||||
return Retrofit.Builder()
|
||||
.addConverterFactory(
|
||||
MoshiConverterFactory.create(),
|
||||
)
|
||||
.addConverterFactory(converter)
|
||||
.baseUrl(DEV_TANGEM_TECH_BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
|
|
@ -38,28 +41,26 @@ class NetworkModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
@OneInchEthereum
|
||||
fun provideOneInchEthereumApi(): OneInchApi {
|
||||
fun provideOneInchEthereumApi(converter: Converter.Factory): OneInchApi {
|
||||
return Retrofit.Builder()
|
||||
.addConverterFactory(
|
||||
MoshiConverterFactory.create(),
|
||||
)
|
||||
.addConverterFactory(converter)
|
||||
.baseUrl(ONE_INCH_ETHER_BASE_URL)
|
||||
.client(OkHttpClient())
|
||||
.client(
|
||||
OkHttpClient.Builder()
|
||||
.addInterceptor(
|
||||
HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.build()
|
||||
.create(OneInchApi::class.java)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideReferralApi(okHttpClient: OkHttpClient): ReferralApi {
|
||||
fun provideReferralApi(okHttpClient: OkHttpClient, converter: Converter.Factory): ReferralApi {
|
||||
return Retrofit.Builder()
|
||||
.addConverterFactory(
|
||||
MoshiConverterFactory.create(
|
||||
Moshi.Builder()
|
||||
.addLast(KotlinJsonAdapterFactory())
|
||||
.build(),
|
||||
),
|
||||
)
|
||||
.addConverterFactory(converter)
|
||||
.baseUrl(DEV_TANGEM_TECH_BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
|
|
@ -77,9 +78,20 @@ class NetworkModule {
|
|||
.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideMoshiConverter(): Converter.Factory {
|
||||
return MoshiConverterFactory.create(
|
||||
Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.add(BigDecimalAdapter())
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
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 = ""
|
||||
const val ONE_INCH_ETHER_BASE_URL = "https://api.1inch.io/v5.0/1/"
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,9 @@ import com.tangem.feature.referral.domain.ReferralRepository
|
|||
import com.tangem.feature.referral.domain.models.ReferralData
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class ReferralRepositoryImpl(
|
||||
internal class ReferralRepositoryImpl @Inject constructor(
|
||||
private val referralApi: ReferralApi,
|
||||
private val referralConverter: ReferralConverter,
|
||||
private val coroutineDispatcher: CoroutineDispatcherProvider,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ internal class ReferralInteractorImpl(
|
|||
if (tokensForReferral.isNotEmpty()) {
|
||||
val currency = tokensConverter.convert(tokensForReferral.first())
|
||||
deriveOrAddTokens(currency)
|
||||
val publicAddress = userWalletManager.getWalletAddress(currency)
|
||||
val publicAddress = userWalletManager.getWalletAddress(currency.networkId)
|
||||
return repository.startReferral(
|
||||
walletId = userWalletManager.getWalletId(),
|
||||
networkId = currency.networkId,
|
||||
|
|
|
|||
|
|
@ -7,14 +7,15 @@ import com.tangem.feature.swap.converters.QuotesConverter
|
|||
import com.tangem.feature.swap.converters.SwapConverter
|
||||
import com.tangem.feature.swap.converters.TokensConverter
|
||||
import com.tangem.feature.swap.domain.SwapRepository
|
||||
import com.tangem.feature.swap.domain.models.ApproveModel
|
||||
import com.tangem.feature.swap.domain.models.Currency
|
||||
import com.tangem.feature.swap.domain.models.QuoteModel
|
||||
import com.tangem.feature.swap.domain.models.data.ApproveModel
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
import com.tangem.feature.swap.domain.models.data.SwapDataModel
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
class SwapRepositoryImpl @Inject constructor(
|
||||
internal class SwapRepositoryImpl @Inject constructor(
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val oneInchApi: OneInchApi,
|
||||
private val tokensConverter: TokensConverter,
|
||||
|
|
@ -64,16 +65,16 @@ class SwapRepositoryImpl @Inject constructor(
|
|||
fromTokenAddress: String,
|
||||
toTokenAddress: String,
|
||||
amount: String,
|
||||
fromAddress: String,
|
||||
fromWalletAddress: String,
|
||||
slippage: Int,
|
||||
) {
|
||||
): SwapDataModel {
|
||||
return withContext(coroutineDispatcher.io) {
|
||||
swapConverter.convert(
|
||||
oneInchApi.swap(
|
||||
fromTokenAddress = fromTokenAddress,
|
||||
toTokenAddress = toTokenAddress,
|
||||
amount = amount,
|
||||
fromAddress = fromAddress,
|
||||
fromAddress = fromWalletAddress,
|
||||
slippage = slippage,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.feature.swap.converters
|
||||
|
||||
import com.tangem.datasource.api.oneinch.models.ApproveCalldataResponse
|
||||
import com.tangem.feature.swap.domain.models.ApproveModel
|
||||
import com.tangem.feature.swap.domain.models.data.ApproveModel
|
||||
import com.tangem.utils.converter.Converter
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package com.tangem.feature.swap.converters
|
||||
|
||||
import com.tangem.datasource.api.oneinch.models.QuoteResponse
|
||||
import com.tangem.feature.swap.domain.models.QuoteModel
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
import com.tangem.utils.converter.Converter
|
||||
import javax.inject.Inject
|
||||
|
||||
class QuotesConverter : Converter<QuoteResponse, QuoteModel> {
|
||||
class QuotesConverter @Inject constructor(): Converter<QuoteResponse, QuoteModel> {
|
||||
|
||||
override fun convert(value: QuoteResponse): QuoteModel {
|
||||
return QuoteModel(
|
||||
|
|
|
|||
|
|
@ -1,24 +1,32 @@
|
|||
package com.tangem.feature.swap.converters
|
||||
|
||||
import com.tangem.datasource.api.oneinch.models.SwapResponse
|
||||
import com.tangem.feature.swap.domain.models.SwapTransactionModel
|
||||
import com.tangem.datasource.api.oneinch.models.TransactionDto
|
||||
import com.tangem.feature.swap.domain.models.data.SwapDataModel
|
||||
import com.tangem.feature.swap.domain.models.data.TransactionModel
|
||||
import com.tangem.utils.converter.Converter
|
||||
import javax.inject.Inject
|
||||
|
||||
class SwapConverter @Inject constructor() : Converter<SwapResponse, SwapTransactionModel> {
|
||||
class SwapConverter @Inject constructor() : Converter<SwapResponse, SwapDataModel> {
|
||||
|
||||
override fun convert(value: SwapResponse): SwapTransactionModel {
|
||||
return SwapTransactionModel(
|
||||
override fun convert(value: SwapResponse): SwapDataModel {
|
||||
return SwapDataModel(
|
||||
fromTokenAddress = value.fromToken.address,
|
||||
toTokenAddress = value.toToken.address,
|
||||
toTokenAmount = value.fromTokenAmount,
|
||||
fromTokenAmount = value.toTokenAmount,
|
||||
fromWalletAddress = value.transaction.fromAddress,
|
||||
toWalletAddress = value.transaction.toAddress,
|
||||
data = value.transaction.data,
|
||||
value = value.transaction.value,
|
||||
gasPrice = value.transaction.gasPrice,
|
||||
gas = value.transaction.gas,
|
||||
transaction = convertTransaction(value.transaction),
|
||||
)
|
||||
}
|
||||
|
||||
private fun convertTransaction(transactionDto: TransactionDto): TransactionModel {
|
||||
return TransactionModel(
|
||||
fromWalletAddress = transactionDto.fromAddress,
|
||||
toWalletAddress = transactionDto.toAddress,
|
||||
data = transactionDto.data,
|
||||
value = transactionDto.value,
|
||||
gasPrice = transactionDto.gasPrice,
|
||||
gas = transactionDto.gas,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.feature.swap.converters
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import com.tangem.feature.swap.domain.models.Currency
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.utils.converter.Converter
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ class TokensConverter @Inject constructor() : Converter<CoinsResponse.Coin, Curr
|
|||
networkId = network.networkId,
|
||||
contractAddress = network.contractAddress!!,
|
||||
decimalCount = network.decimalCount!!.intValueExact(),
|
||||
logoUrl = "",//todo add logo
|
||||
logoUrl = getSmallIconUrl(value.id),
|
||||
)
|
||||
} else {
|
||||
Currency.NativeToken(
|
||||
|
|
@ -25,8 +25,17 @@ class TokensConverter @Inject constructor() : Converter<CoinsResponse.Coin, Curr
|
|||
name = value.name,
|
||||
symbol = value.symbol,
|
||||
networkId = network.networkId,
|
||||
logoUrl = "", //todo add logo
|
||||
logoUrl = getSmallIconUrl(value.id),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSmallIconUrl(coin: String): String {
|
||||
return "$DEFAULT_IMAGE_HOST$SMALL_ICON_PATH/$coin.png"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/"
|
||||
private const val SMALL_ICON_PATH = "small"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.feature.swap.di
|
||||
|
||||
import com.tangem.datasource.api.oneinch.OneInchApi
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.di.qualifiers.OneInchEthereum
|
||||
import com.tangem.feature.swap.SwapRepositoryImpl
|
||||
import com.tangem.feature.swap.converters.ApproveConverter
|
||||
import com.tangem.feature.swap.converters.QuotesConverter
|
||||
import com.tangem.feature.swap.converters.SwapConverter
|
||||
import com.tangem.feature.swap.converters.TokensConverter
|
||||
import com.tangem.feature.swap.domain.SwapRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class SwapDataModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSwapRepository(
|
||||
tangemTechApi: TangemTechApi,
|
||||
@OneInchEthereum oneInchApi: OneInchApi,
|
||||
tokensConverter: TokensConverter,
|
||||
quotesConverter: QuotesConverter,
|
||||
swapConverter: SwapConverter,
|
||||
approveConverter: ApproveConverter,
|
||||
coroutineDispatcher: CoroutineDispatcherProvider,
|
||||
): SwapRepository {
|
||||
return SwapRepositoryImpl(
|
||||
tangemTechApi = tangemTechApi,
|
||||
oneInchApi = oneInchApi,
|
||||
tokensConverter = tokensConverter,
|
||||
quotesConverter = quotesConverter,
|
||||
swapConverter = swapConverter,
|
||||
approveConverter = approveConverter,
|
||||
coroutineDispatcher = coroutineDispatcher,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,18 @@
|
|||
plugins {
|
||||
id("java-library")
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
kotlin("kapt")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/** Libs */
|
||||
implementation(project(":libs:crypto"))
|
||||
implementation(project(":core:utils"))
|
||||
|
||||
/** DI */
|
||||
implementation(Library.hiltCore)
|
||||
kapt(Library.hiltKapt)
|
||||
}
|
||||
|
||||
java {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,20 @@
|
|||
package com.tangem.feature.swap.domain
|
||||
|
||||
import com.tangem.feature.swap.domain.models.SwapResultModel
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
|
||||
interface SwapInteractor {
|
||||
|
||||
suspend fun getTokensToSwap(networkId: String): List<Currency>
|
||||
|
||||
suspend fun getTokenBalance(tokenId: String): String
|
||||
|
||||
suspend fun findBestQuote(
|
||||
fromTokenAddress: String,
|
||||
toTokenAddress: String,
|
||||
amount: String,
|
||||
): QuoteModel
|
||||
|
||||
suspend fun onSwap(): SwapResultModel
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.tangem.feature.swap.domain
|
||||
|
||||
import com.tangem.feature.swap.domain.cache.SwapDataCache
|
||||
import com.tangem.feature.swap.domain.models.SwapResultModel
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
import com.tangem.feature.swap.domain.models.data.TransactionModel
|
||||
import com.tangem.lib.crypto.UserWalletManager
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SwapInteractorImpl @Inject constructor(
|
||||
private val userWalletManager: UserWalletManager,
|
||||
private val repository: SwapRepository,
|
||||
private val cache: SwapDataCache,
|
||||
) : SwapInteractor {
|
||||
|
||||
override suspend fun getTokensToSwap(networkId: String): List<Currency> {
|
||||
val availableTokens = cache.getAvailableTokens(networkId)
|
||||
return availableTokens.ifEmpty {
|
||||
val tokens = repository.getExchangeableTokens(networkId)
|
||||
cache.cacheAvailableToSwapTokens(networkId, tokens)
|
||||
cache.cacheNetworkId(networkId)
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTokenBalance(tokenId: String): String {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun findBestQuote(fromTokenAddress: String, toTokenAddress: String, amount: String): QuoteModel {
|
||||
val networkId = cache.getNetworkId()
|
||||
val isAllowedToSpend = if (networkId.isNullOrEmpty()) {
|
||||
false
|
||||
} else {
|
||||
repository.checkTokensSpendAllowance(fromTokenAddress, userWalletManager.getWalletAddress(networkId)) != "0"
|
||||
}
|
||||
repository.findBestQuote(fromTokenAddress, toTokenAddress, amount).let { quotes ->
|
||||
cache.cacheSwapParams(quotes.copy(isAllowedToSpend = isAllowedToSpend), amount)
|
||||
return quotes
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun onSwap(): SwapResultModel {
|
||||
val quoteModel = cache.getLastQuote()
|
||||
val amountToSwap = cache.getAmountToSwap()
|
||||
val networkId = cache.getNetworkId()
|
||||
if (quoteModel != null && amountToSwap != null && networkId != null) {
|
||||
val swapData = repository.prepareSwapTransaction(
|
||||
fromTokenAddress = quoteModel.fromTokenAmount,
|
||||
toTokenAddress = quoteModel.toTokenAmount,
|
||||
amount = amountToSwap,
|
||||
slippage = DEFAULT_SLIPPAGE,
|
||||
fromWalletAddress = getWalletAddress(networkId),
|
||||
)
|
||||
signTransactionData(swapData.transaction) //todo implement
|
||||
return SwapResultModel.SwapSuccess(
|
||||
quoteModel.fromTokenAmount,
|
||||
quoteModel.toTokenAmount,
|
||||
)
|
||||
}
|
||||
return SwapResultModel.SwapError(
|
||||
0,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
private fun getWalletAddress(networkId: String): String {
|
||||
return userWalletManager.getWalletAddress(networkId)
|
||||
}
|
||||
|
||||
//todo implement after merge referral
|
||||
private fun signTransactionData(transaction: TransactionModel) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_SLIPPAGE = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.feature.swap.domain
|
||||
|
||||
import com.tangem.feature.swap.domain.models.ApproveModel
|
||||
import com.tangem.feature.swap.domain.models.Currency
|
||||
import com.tangem.feature.swap.domain.models.QuoteModel
|
||||
import com.tangem.feature.swap.domain.models.data.ApproveModel
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
import com.tangem.feature.swap.domain.models.data.SwapDataModel
|
||||
|
||||
interface SwapRepository {
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ interface SwapRepository {
|
|||
fromTokenAddress: String,
|
||||
toTokenAddress: String,
|
||||
amount: String,
|
||||
fromAddress: String,
|
||||
fromWalletAddress: String,
|
||||
slippage: Int,
|
||||
)
|
||||
): SwapDataModel
|
||||
}
|
||||
15
features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt
vendored
Normal file
15
features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCache.kt
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.feature.swap.domain.cache
|
||||
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
|
||||
interface SwapDataCache {
|
||||
|
||||
fun cacheNetworkId(networkId: String)
|
||||
fun cacheSwapParams(quoteModel: QuoteModel, amount: String)
|
||||
fun cacheAvailableToSwapTokens(networkId: String, tokens: List<Currency>)
|
||||
fun getAvailableTokens(networkId: String): List<Currency>
|
||||
fun getLastQuote(): QuoteModel?
|
||||
fun getAmountToSwap(): String?
|
||||
fun getNetworkId(): String?
|
||||
}
|
||||
39
features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt
vendored
Normal file
39
features/swap/domain/src/main/java/com/tangem/feature/swap/domain/cache/SwapDataCacheImpl.kt
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.feature.swap.domain.cache
|
||||
|
||||
import com.tangem.feature.swap.domain.models.SwapDataHolder
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
|
||||
class SwapDataCacheImpl : SwapDataCache {
|
||||
|
||||
private var lastDataForSwap: SwapDataHolder = SwapDataHolder()
|
||||
private val availableTokensForNetwork: MutableMap<String, List<Currency>> = mutableMapOf()
|
||||
|
||||
override fun cacheSwapParams(quoteModel: QuoteModel, amount: String) {
|
||||
lastDataForSwap = lastDataForSwap.copy(quoteModel = quoteModel, amountToSwap = amount)
|
||||
}
|
||||
|
||||
override fun cacheAvailableToSwapTokens(networkId: String, tokens: List<Currency>) {
|
||||
availableTokensForNetwork[networkId] = tokens
|
||||
}
|
||||
|
||||
override fun cacheNetworkId(networkId: String) {
|
||||
lastDataForSwap = lastDataForSwap.copy(networkId = networkId)
|
||||
}
|
||||
|
||||
override fun getNetworkId(): String? {
|
||||
return lastDataForSwap.networkId
|
||||
}
|
||||
|
||||
override fun getAvailableTokens(networkId: String): List<Currency> {
|
||||
return availableTokensForNetwork.getOrElse(networkId) { emptyList() }
|
||||
}
|
||||
|
||||
override fun getLastQuote(): QuoteModel? {
|
||||
return lastDataForSwap.quoteModel
|
||||
}
|
||||
|
||||
override fun getAmountToSwap(): String? {
|
||||
return lastDataForSwap.amountToSwap
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.feature.swap.domain.di
|
||||
|
||||
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.UserWalletManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class SwapDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSwapInteractor(
|
||||
referralRepository: SwapRepository,
|
||||
userWalletManager: UserWalletManager,
|
||||
): SwapInteractor {
|
||||
return SwapInteractorImpl(
|
||||
userWalletManager = userWalletManager,
|
||||
repository = referralRepository,
|
||||
cache = SwapDataCacheImpl(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package com.tangem.feature.swap.domain.models
|
||||
|
||||
data class QuoteModel(
|
||||
val fromTokenAmount: String,
|
||||
val toTokenAmount: String,
|
||||
val estimatedGas: Int
|
||||
)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.feature.swap.domain.models
|
||||
|
||||
import com.tangem.feature.swap.domain.models.data.QuoteModel
|
||||
|
||||
data class SwapDataHolder(
|
||||
val quoteModel: QuoteModel? = null,
|
||||
val amountToSwap: String? = null,
|
||||
val networkId: String? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.feature.swap.domain.models
|
||||
|
||||
sealed interface SwapResultModel {
|
||||
|
||||
data class SwapSuccess(
|
||||
val fromTokenAmount: String,
|
||||
val toTokenAmount: String,
|
||||
) : SwapResultModel
|
||||
|
||||
data class SwapError(
|
||||
val errorCode: Int,
|
||||
val errorMessage: String,
|
||||
) : SwapResultModel
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.swap.domain.models
|
||||
package com.tangem.feature.swap.domain.models.data
|
||||
|
||||
/**
|
||||
* Approve model
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.swap.domain.models
|
||||
package com.tangem.feature.swap.domain.models.data
|
||||
|
||||
sealed class Currency(
|
||||
open val id: String,
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.feature.swap.domain.models.data
|
||||
|
||||
data class QuoteModel(
|
||||
val fromTokenAmount: String,
|
||||
val toTokenAmount: String,
|
||||
val estimatedGas: Int,
|
||||
val isAllowedToSpend: Boolean = false,
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.feature.swap.domain.models.data
|
||||
|
||||
/**
|
||||
* Swap transaction model
|
||||
*
|
||||
* @property fromTokenAddress token from which want to convert
|
||||
* @property toTokenAddress token to want to convert
|
||||
* @property toTokenAmount amount "target" token
|
||||
* @property fromTokenAmount amount "initial" token
|
||||
*/
|
||||
data class SwapDataModel(
|
||||
val fromTokenAddress: String,
|
||||
val toTokenAddress: String,
|
||||
val toTokenAmount: String,
|
||||
val fromTokenAmount: String,
|
||||
val transaction: TransactionModel
|
||||
)
|
||||
|
|
@ -1,25 +1,16 @@
|
|||
package com.tangem.feature.swap.domain.models
|
||||
package com.tangem.feature.swap.domain.models.data
|
||||
|
||||
/**
|
||||
* Swap transaction model
|
||||
* Transaction model
|
||||
*
|
||||
* @property fromTokenAddress token from which want to convert
|
||||
* @property toTokenAddress token to want to convert
|
||||
* @property toTokenAmount amount "target" token
|
||||
* @property fromTokenAmount amount "initial" token
|
||||
* @property fromWalletAddress wallet from address (transactions will be sent from this address)
|
||||
* @property toWalletAddress transactions will be sent to our(1inch) contract address
|
||||
* @property data The encoded data to call the approve method on the swapped token contract
|
||||
* @property value Native token value in WEI (for approve is always 0)
|
||||
* @property gasPrice maximum amount of gas for a swap default: 11500000; max: 11500000
|
||||
* @property gas estimated amount of the gas limit, increase this value by 25%
|
||||
* @constructor Create empty Swap transaction model
|
||||
*/
|
||||
data class SwapTransactionModel(
|
||||
val fromTokenAddress: String,
|
||||
val toTokenAddress: String,
|
||||
val toTokenAmount: String,
|
||||
val fromTokenAmount: String,
|
||||
data class TransactionModel(
|
||||
val fromWalletAddress: String,
|
||||
val toWalletAddress: String,
|
||||
val data: String,
|
||||
|
|
@ -49,6 +49,7 @@ dependencies {
|
|||
|
||||
/** Domain */
|
||||
implementation(project(":features:swap:domain"))
|
||||
implementation(project(":features:swap:data"))//todo remove, only for test
|
||||
|
||||
/** Other libraries */
|
||||
implementation(Library.composeShimmer)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.feature.swap.presentation
|
||||
|
||||
import android.os.Bundle
|
||||
import android.transition.TransitionInflater
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.feature.swap.viewmodels.SwapViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SwapFragment : Fragment() {
|
||||
|
||||
private val viewModel by viewModels<SwapViewModel>()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, true) }
|
||||
viewModel
|
||||
return ComposeView(inflater.context).apply {
|
||||
setContent {
|
||||
//todo add compose screen here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.feature.swap.viewmodels
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
internal class SwapViewModel @Inject constructor(
|
||||
private val referralInteractor: SwapInteractor,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : ViewModel() {
|
||||
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ interface UserWalletManager {
|
|||
/**
|
||||
* Returns wallet public address for token
|
||||
*
|
||||
* @param currency for which find address
|
||||
* @param networkId for currency
|
||||
*/
|
||||
fun getWalletAddress(currency: Currency): String
|
||||
fun getWalletAddress(networkId: String): String
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue