Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix error handling for 1Inch swap quote responses #1366

Merged
merged 4 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 40 additions & 18 deletions data/src/main/kotlin/com/vultisig/wallet/data/api/OneInchApi.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package com.vultisig.wallet.data.api

import com.vultisig.wallet.data.api.models.OneInchSwapQuoteDeserialized
import com.vultisig.wallet.data.api.models.OneInchSwapQuoteJson
import com.vultisig.wallet.data.api.models.OneInchTokenJson
import com.vultisig.wallet.data.api.models.OneInchTokensJson
import com.vultisig.wallet.data.api.models.THORChainSwapQuoteDeserialized
import com.vultisig.wallet.data.api.models.THORChainSwapQuoteError
import com.vultisig.wallet.data.models.Chain
import com.vultisig.wallet.data.models.oneInchChainId
import com.vultisig.wallet.data.utils.OneInchSwapQuoteResponseJsonSerializer
import com.vultisig.wallet.data.utils.ThorChainSwapQuoteResponseJsonSerializer
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.http.isSuccess
import kotlinx.serialization.json.Json
import java.math.BigInteger
import javax.inject.Inject

Expand All @@ -21,7 +28,7 @@ interface OneInchApi {
srcAddress: String,
amount: String,
isAffiliate: Boolean,
): OneInchSwapQuoteJson
): OneInchSwapQuoteDeserialized

suspend fun getTokens(
chain: Chain,
Expand All @@ -41,6 +48,8 @@ interface OneInchApi {

class OneInchApiImpl @Inject constructor(
private val httpClient: HttpClient,
private val oneInchSwapQuoteResponseJsonSerializer: OneInchSwapQuoteResponseJsonSerializer,
private val json: Json,
) : OneInchApi {

override suspend fun getSwapQuote(
Expand All @@ -50,24 +59,37 @@ class OneInchApiImpl @Inject constructor(
srcAddress: String,
amount: String,
isAffiliate: Boolean,
): OneInchSwapQuoteJson =
httpClient.get("https://api.vultisig.com/1inch/swap/v6.0/${chain.oneInchChainId()}/swap") {
parameter(
"src",
srcTokenContractAddress.takeIf { it.isNotEmpty() } ?: ONEINCH_NULL_ADDRESS)
parameter(
"dst",
dstTokenContractAddress.takeIf { it.isNotEmpty() } ?: ONEINCH_NULL_ADDRESS)
parameter("amount", amount)
parameter("from", srcAddress)
parameter("slippage", "0.5")
parameter("disableEstimate", true)
parameter("includeGas", true)
if (isAffiliate) {
parameter("referrer", ONEINCH_REFERRER_ADDRESS)
parameter("fee", ONEINCH_REFERRER_FEE)
): OneInchSwapQuoteDeserialized {
try {
val response =
httpClient.get("https://api.vultisig.com/1inch/swap/v6.0/${chain.oneInchChainId()}/swap") {
parameter(
"src",
srcTokenContractAddress.takeIf { it.isNotEmpty() } ?: ONEINCH_NULL_ADDRESS)
parameter(
"dst",
dstTokenContractAddress.takeIf { it.isNotEmpty() } ?: ONEINCH_NULL_ADDRESS)
parameter("amount", amount)
parameter("from", srcAddress)
parameter("slippage", "0.5")
parameter("disableEstimate", true)
parameter("includeGas", true)
if (isAffiliate) {
parameter("referrer", ONEINCH_REFERRER_ADDRESS)
parameter("fee", ONEINCH_REFERRER_FEE)
}
}
if (!response.status.isSuccess()) {
return OneInchSwapQuoteDeserialized.Error(error = response.status.description)
}
}.body()
return json.decodeFromString(
oneInchSwapQuoteResponseJsonSerializer,
response.body<String>()
)
} catch (e: Exception) {
return OneInchSwapQuoteDeserialized.Error(error = e.message ?: "Unknown error")
}
}


override suspend fun getTokens(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ package com.vultisig.wallet.data.api.models
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

sealed class OneInchSwapQuoteDeserialized {
data class Result(val data: OneInchSwapQuoteJson) : OneInchSwapQuoteDeserialized()
data class Error(val error: String) : OneInchSwapQuoteDeserialized()
}


@Serializable
data class OneInchSwapQuoteJson(
@SerialName("dstAmount")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.vultisig.wallet.data.api.MayaChainApi
import com.vultisig.wallet.data.api.OneInchApi
import com.vultisig.wallet.data.api.ThorChainApi
import com.vultisig.wallet.data.api.errors.SwapException
import com.vultisig.wallet.data.api.models.OneInchSwapQuoteDeserialized
import com.vultisig.wallet.data.api.models.OneInchSwapQuoteJson
import com.vultisig.wallet.data.api.models.OneInchSwapTxJson
import com.vultisig.wallet.data.api.models.THORChainSwapQuoteDeserialized
Expand Down Expand Up @@ -79,9 +80,13 @@ internal class SwapQuoteRepositoryImpl @Inject constructor(
amount = tokenValue.value.toString(),
isAffiliate = isAffiliate,
)

oneInchQuote.error?.let { throw SwapException.handleSwapException(it) }
return oneInchQuote
when (oneInchQuote) {
is OneInchSwapQuoteDeserialized.Error -> throw SwapException.handleSwapException(oneInchQuote.error)
is OneInchSwapQuoteDeserialized.Result -> {
oneInchQuote.data.error?.let { throw SwapException.handleSwapException(it) }
return oneInchQuote.data
}
}
}

override suspend fun getMayaSwapQuote(
Expand Down
27 changes: 27 additions & 0 deletions data/src/main/kotlin/com/vultisig/wallet/data/utils/Serializer.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.vultisig.wallet.data.utils

import com.vultisig.wallet.data.api.models.KeysignResponseSerializable
import com.vultisig.wallet.data.api.models.OneInchSwapQuoteDeserialized
import com.vultisig.wallet.data.api.models.OneInchSwapQuoteJson
import com.vultisig.wallet.data.api.models.SplTokenJson
import com.vultisig.wallet.data.api.models.SplTokenResponseJson
import com.vultisig.wallet.data.api.models.THORChainSwapQuote
Expand All @@ -23,6 +25,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import java.math.BigDecimal
import java.math.BigInteger
import javax.inject.Inject
Expand Down Expand Up @@ -117,6 +120,30 @@ class ThorChainSwapQuoteResponseJsonSerializerImpl @Inject constructor(private v
}
}


interface OneInchSwapQuoteResponseJsonSerializer : DefaultSerializer<OneInchSwapQuoteDeserialized>

class OneInchSwapQuoteResponseJsonSerializerImpl @Inject constructor(private val json: Json) :
OneInchSwapQuoteResponseJsonSerializer {
override val descriptor: SerialDescriptor =
buildClassSerialDescriptor("OnceInchSwapQouteResponseJsonSerializer")

override fun deserialize(decoder: Decoder): OneInchSwapQuoteDeserialized {
val input = decoder as JsonDecoder
val jsonObject = input.decodeJsonElement().jsonObject

return if (jsonObject.containsKey("dstAmount")) {
OneInchSwapQuoteDeserialized.Result(
json.decodeFromJsonElement<OneInchSwapQuoteJson>(jsonObject)
)
} else {
OneInchSwapQuoteDeserialized.Error(
json.decodeFromJsonElement<String>(jsonObject)
)
}
}
}

interface KeysignResponseSerializer : DefaultSerializer<tss.KeysignResponse>

class KeysignResponseSerializerImpl @Inject constructor() : KeysignResponseSerializer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton


@Module
Expand Down Expand Up @@ -39,4 +40,10 @@ internal interface SerializerModule {
fun bindCosmosThorChainResponseSerializer(
impl: CosmosThorChainResponseSerializerImpl,
): CosmosThorChainResponseSerializer

@Binds
@Singleton
fun bindOneInchSwapQuoteResponseJsonSerializer(
impl: OneInchSwapQuoteResponseJsonSerializerImpl,
): OneInchSwapQuoteResponseJsonSerializer
}