Skip to content

Commit

Permalink
Initial implementation of 1inch kit
Browse files Browse the repository at this point in the history
  • Loading branch information
omurovch committed Jun 15, 2021
1 parent a426a61 commit 769b148
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 4 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ dependencies {
implementation project(':ethereumkit')
implementation project(':erc20kit')
implementation project(':uniswapkit')
implementation project(':oneinchkit')
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
package io.horizontalsystems.ethereumkit.models

import io.horizontalsystems.ethereumkit.core.toHexString
import java.math.BigInteger
import java.util.*

data class TransactionData(
val to: Address,
val value: BigInteger,
val input: ByteArray
)
) {
override fun equals(other: Any?): Boolean {
return when {
this === other -> true
other is TransactionData -> to == other.to && value == other.value && input.contentEquals(other.input)
else -> false
}
}

override fun hashCode(): Int {
return Objects.hash(to, value, input)
}

override fun toString(): String {
return "TransactionData {to: ${to.hex}, value: $value, input: ${input.toHexString()}}"
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import java.lang.reflect.Type
import java.math.BigInteger
import java.util.*

class BigIntegerTypeAdapter : TypeAdapter<BigInteger?>() {
class BigIntegerTypeAdapter(private val isHex: Boolean = true) : TypeAdapter<BigInteger?>() {
override fun write(writer: JsonWriter, value: BigInteger?) {
if (value == null) {
writer.nullValue()
} else {
writer.value(value.toHexString())
val stringValue = if (isHex) value.toHexString() else value.toString()
writer.value(stringValue)
}
}

Expand All @@ -28,7 +29,8 @@ class BigIntegerTypeAdapter : TypeAdapter<BigInteger?>() {
reader.nextNull()
return null
}
return reader.nextString().hexStringToBigIntegerOrNull()
val stringValue = reader.nextString()
return if (isHex) stringValue.hexStringToBigIntegerOrNull() else BigInteger(stringValue)
}
}

Expand Down
12 changes: 12 additions & 0 deletions oneinchkit/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,16 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'

implementation 'com.squareup.retrofit2:adapter-rxjava2:2.8.1'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6'

api project(':ethereumkit')
}
143 changes: 143 additions & 0 deletions oneinchkit/src/main/java/io/horizontalsystems/oneinchkit/OneInchKit.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package io.horizontalsystems.oneinchkit

import com.google.gson.annotations.SerializedName
import io.horizontalsystems.ethereumkit.core.EthereumKit
import io.horizontalsystems.ethereumkit.core.EthereumKit.NetworkType
import io.horizontalsystems.ethereumkit.core.toHexString
import io.horizontalsystems.ethereumkit.models.Address
import io.reactivex.Single
import java.math.BigInteger
import java.util.*

class OneInchKit(
private val evmKit: EthereumKit,
private val service: OneInchService
) {

val smartContractAddress: Address = when (evmKit.networkType) {
NetworkType.EthMainNet -> Address("0x11111112542d85b3ef69ae05771c2dccff4faa26")
NetworkType.BscMainNet -> Address("0x11111112542d85b3ef69ae05771c2dccff4faa26")
else -> throw IllegalArgumentException("Invalid NetworkType: $evmKit.networkType")
}

fun getApproveCallDataAsync(tokenAddress: Address, amount: BigInteger): Single<ApproveCallData> {
return service.getApproveCallDataAsync(tokenAddress, amount)
}

fun getQuoteAsync(
fromToken: Address,
toToken: Address,
amount: BigInteger,
protocols: List<String>? = null,
gasPrice: Long? = null,
complexityLevel: Int? = null,
connectorTokens: List<String>? = null,
gasLimit: Long? = null,
mainRouteParts: Int? = null,
parts: Int? = null
): Single<Quote> {
return service.getQuoteAsync(fromToken, toToken, amount, protocols, gasPrice, complexityLevel, connectorTokens, gasLimit, mainRouteParts, parts)
}

fun getSwapAsync(
fromToken: Address,
toToken: Address,
amount: BigInteger,
slippagePercentage: Float,
protocols: List<String>? = null,
recipient: Address? = null,
gasPrice: Long? = null,
burnChi: Boolean = false,
complexityLevel: Int? = null,
connectorTokens: List<String>? = null,
allowPartialFill: Boolean = false,
gasLimit: Long? = null,
parts: Int? = null,
mainRouteParts: Int? = null
): Single<Swap> {
return service.getSwapAsync(fromToken, toToken, amount, evmKit.receiveAddress, slippagePercentage, protocols, recipient, gasPrice, burnChi, complexityLevel, connectorTokens, allowPartialFill, gasLimit, parts, mainRouteParts)
}

companion object {

fun getInstance(evmKit: EthereumKit): OneInchKit {
val service = OneInchService(evmKit.networkType)
return OneInchKit(evmKit, service)
}

}

}

data class Token(
val symbol: String,
val name: String,
val decimals: Int,
val address: String,
val logoURI: String
)

data class Quote(
val fromToken: Token,
val toToken: Token,
val fromTokenAmount: BigInteger,
val toTokenAmount: BigInteger,
@SerializedName("protocols") val route: List<Any>,
val estimatedGas: Long
) {
override fun toString(): String {
return "Quote {fromToken: ${fromToken.name}, toToken: ${toToken.name}, fromTokenAmount: $fromTokenAmount, toTokenAmount: $toTokenAmount}"
}
}

data class SwapTransaction(
val from: Address,
val to: Address,
val data: ByteArray,
val value: BigInteger,
val gasPrice: Long,
@SerializedName("gas") val gasLimit: Long
) {
override fun toString(): String {
return "SwapTransaction {\nfrom: ${from.hex}, \nto: ${to.hex}, \ndata: ${data.toHexString()}, \nvalue: $value, \ngasPrice: $gasPrice, \ngasLimit: $gasLimit\n}"
}
}

data class Swap(
val fromToken: Token,
val toToken: Token,
val fromTokenAmount: BigInteger,
val toTokenAmount: BigInteger,
@SerializedName("protocols") val route: List<Any>,
@SerializedName("tx") val transaction: SwapTransaction
) {
override fun toString(): String {
return "Swap {\nfromToken: ${fromToken.name}, \ntoToken: ${toToken.name}, \nfromTokenAmount: $fromTokenAmount, \ntoTokenAmount: $toTokenAmount, \ntx: $transaction\n}"
}
}

data class ApproveCallData(
val data: ByteArray,
val gasPrice: Long,
val to: Address,
val value: BigInteger
) {
override fun equals(other: Any?): Boolean {
return when {
this === other -> true
other is ApproveCallData -> to == other.to && value == other.value && data.contentEquals(other.data)
else -> false
}
}

override fun hashCode(): Int {
return Objects.hash(to, value, data)
}

override fun toString(): String {
return "ApproveCallData {\nto: ${to.hex}, \nvalue: $value, \ndata: ${data.toHexString()}\n}"
}
}

data class Spender(val address: Address)

Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package io.horizontalsystems.oneinchkit

import com.google.gson.GsonBuilder
import io.horizontalsystems.ethereumkit.core.EthereumKit.NetworkType
import io.horizontalsystems.ethereumkit.models.Address
import io.horizontalsystems.ethereumkit.network.*
import io.reactivex.Single
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
import java.math.BigInteger
import java.util.logging.Logger

class OneInchService(
networkType: NetworkType
) {
private val logger = Logger.getLogger("OneInchService")
private val url = "https://api.1inch.exchange/v3.0/${networkType.getNetwork().id}/"
private val service: OneInchServiceApi

init {
val loggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
logger.info(message)
}
}).setLevel(HttpLoggingInterceptor.Level.BODY)

val httpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)

val gson = GsonBuilder()
.setLenient()
.registerTypeAdapter(BigInteger::class.java, BigIntegerTypeAdapter(isHex = false))
.registerTypeAdapter(Long::class.java, LongTypeAdapter())
.registerTypeAdapter(Int::class.java, IntTypeAdapter())
.registerTypeAdapter(ByteArray::class.java, ByteArrayTypeAdapter())
.registerTypeAdapter(Address::class.java, AddressTypeAdapter())
.create()

val retrofit = Retrofit.Builder()
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient.build())
.build()

service = retrofit.create(OneInchServiceApi::class.java)
}

fun getApproveCallDataAsync(tokenAddress: Address, amount: BigInteger): Single<ApproveCallData> {
return service.getApproveCallData(tokenAddress.hex, amount)
}

fun getQuoteAsync(
fromToken: Address,
toToken: Address,
amount: BigInteger,
protocols: List<String>? = null,
gasPrice: Long? = null,
complexityLevel: Int? = null,
connectorTokens: List<String>? = null,
gasLimit: Long? = null,
mainRouteParts: Int? = null,
parts: Int? = null
): Single<Quote> {
return service.getQuote(fromToken.hex, toToken.hex, amount, protocols?.joinToString(","), gasPrice, complexityLevel, connectorTokens?.joinToString(","), gasLimit, parts, mainRouteParts)
}

fun getSwapAsync(
fromTokenAddress: Address,
toTokenAddress: Address,
amount: BigInteger,
fromAddress: Address,
slippagePercentage: Float,
protocols: List<String>? = null,
recipient: Address? = null,
gasPrice: Long? = null,
burnChi: Boolean? = null,
complexityLevel: Int? = null,
connectorTokens: List<String>? = null,
allowPartialFill: Boolean? = null,
gasLimit: Long? = null,
parts: Int? = null,
mainRouteParts: Int? = null
): Single<Swap> {
return service.getSwap(fromTokenAddress.hex, toTokenAddress.hex, amount, fromAddress.hex, slippagePercentage, protocols?.joinToString(","), recipient?.hex, gasPrice, burnChi, complexityLevel, connectorTokens?.joinToString(","), allowPartialFill, gasLimit, parts, mainRouteParts)
}

private interface OneInchServiceApi {

@GET("approve/calldata")
fun getApproveCallData(
@Query("tokenAddress") tokenAddress: String,
@Query("amount") amount: BigInteger? = null,
@Query("infinity") infinity: Boolean? = null
): Single<ApproveCallData>

@GET("approve/spender")
fun getApproveSpender(): Single<Spender>

@GET("quote")
fun getQuote(
@Query("fromTokenAddress") fromTokenAddress: String,
@Query("toTokenAddress") toTokenAddress: String,
@Query("amount") amount: BigInteger,
@Query("protocols") protocols: String? = null,
@Query("gasPrice") gasPrice: Long? = null,
@Query("complexityLevel") complexityLevel: Int? = null,
@Query("connectorTokens") connectorTokens: String? = null,
@Query("gasLimit") gasLimit: Long? = null,
@Query("parts") parts: Int? = null,
@Query("mainRouteParts") mainRouteParts: Int? = null
): Single<Quote>


@GET("swap")
fun getSwap(
@Query("fromTokenAddress") fromTokenAddress: String,
@Query("toTokenAddress") toTokenAddress: String,
@Query("amount") amount: BigInteger,
@Query("fromAddress") fromAddress: String,
@Query("slippage") slippagePercentage: Float,
@Query("protocols") protocols: String? = null,
@Query("destReceiver") recipient: String? = null,
@Query("gasPrice") gasPrice: Long? = null,
@Query("burnChi") burnChi: Boolean? = null,
@Query("complexityLevel ") complexityLevel: Int? = null,
@Query("connectorTokens") connectorTokens: String? = null,
@Query("allowPartialFill") allowPartialFill: Boolean? = null,
@Query("gasLimit") gasLimit: Long? = null,
@Query("parts") parts: Int? = null,
@Query("mainRouteParts") mainRouteParts: Int? = null
): Single<Swap>


}

}

0 comments on commit 769b148

Please sign in to comment.