Last active
October 13, 2020 09:35
-
-
Save Legion1900/58c432faf798ee1e8a96e1e9101569bd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Code of this class belongs to https://github.com/horizontalsystems/ethereum-kit-android library; | |
* Modified by Sapien Wallet team on 10.12.2020 | |
* */ | |
import com.google.gson.Gson | |
import com.google.gson.reflect.TypeToken | |
import io.horizontalsystems.ethereumkit.api.models.ApiError | |
import io.horizontalsystems.ethereumkit.core.EthereumKit | |
import io.horizontalsystems.ethereumkit.core.removeLeadingZeros | |
import io.horizontalsystems.ethereumkit.core.stripHexPrefix | |
import io.horizontalsystems.ethereumkit.core.toHexString | |
import io.horizontalsystems.ethereumkit.models.Address | |
import io.horizontalsystems.ethereumkit.models.Block | |
import io.horizontalsystems.ethereumkit.models.EthereumLog | |
import io.horizontalsystems.ethereumkit.models.TransactionStatus | |
import io.reactivex.Single | |
import retrofit2.http.Body | |
import retrofit2.http.POST | |
import retrofit2.http.Path | |
import java.math.BigInteger | |
import javax.inject.Inject | |
import javax.inject.Singleton | |
@Singleton | |
class InfuraService @Inject constructor( | |
private val service: InfuraServiceAPI, | |
private val infuraCredentials: EthereumKit.InfuraCredentials | |
) { | |
fun getLastBlockHeight(): Single<Long> { | |
val request = Request("eth_blockNumber") | |
return service.makeRequestForBigInteger(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it).map { blockNumber -> | |
blockNumber.toLong() | |
} | |
} | |
} | |
fun getTransactionCount(address: Address): Single<Long> { | |
val request = Request("eth_getTransactionCount", listOf(address.hex, "pending")) | |
return service.makeRequestForBigInteger(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it).map { txCount -> | |
txCount.toLong() | |
} | |
} | |
} | |
fun getBalance(address: Address): Single<BigInteger> { | |
val request = Request("eth_getBalance", listOf(address.hex, "latest")) | |
return service.makeRequestForBigInteger(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it) | |
} | |
} | |
fun send(signedTransaction: ByteArray): Single<Unit> { | |
val request = Request("eth_sendRawTransaction", listOf(signedTransaction.toHexString())) | |
return service.makeRequestForString(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it) | |
}.map { Unit } | |
} | |
fun getLogs( | |
address: Address?, fromBlock: Long?, toBlock: Long?, | |
topics: List<ByteArray?> | |
): Single<List<EthereumLog>> { | |
val fromBlockStr = fromBlock?.toBigInteger()?.toString(16)?.let { "0x$it" } ?: "earliest" | |
val toBlockStr = toBlock?.toBigInteger()?.toString(16)?.let { "0x$it" } ?: "latest" | |
val params: MutableMap<String, Any> = mutableMapOf( | |
"fromBlock" to fromBlockStr, | |
"toBlock" to toBlockStr, | |
"topics" to topics.map { it?.toHexString() }) | |
address?.let { | |
params["address"] = address.hex | |
} | |
val request = Request("eth_getLogs", listOf(params)) | |
return service.makeRequestForLogs(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it) | |
} | |
} | |
fun getStorageAt(contractAddress: Address, position: String, blockNumber: Long?): Single<String> { | |
val request = Request("eth_getStorageAt", listOf(contractAddress.hex, position, "latest")) | |
return service.makeRequestForString(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it) | |
} | |
} | |
fun transactionReceiptStatus(transactionHash: ByteArray): Single<TransactionStatus> { | |
val request = Request("eth_getTransactionReceipt", listOf(transactionHash.toHexString())) | |
return service.makeRequestForString(infuraCredentials.projectId, request) | |
.flatMap { | |
if (it.error != null) | |
Single.just(TransactionStatus.FAILED) | |
else { | |
it.result?.let { result -> | |
val txStatusMap: Map<String, String> = | |
Gson().fromJson(result, object : TypeToken<Map<String, Any>>() {}.type) | |
txStatusMap["status"]?.let { statusStr -> | |
val success = Integer.parseInt(statusStr.stripHexPrefix(), 16) | |
if (success == 0) | |
Single.just(TransactionStatus.SUCCESS) | |
else | |
Single.just(TransactionStatus.FAILED) | |
} | |
} ?: Single.just(TransactionStatus.NOTFOUND) | |
} | |
} | |
} | |
fun transactionExist(transactionHash: ByteArray): Single<Boolean> { | |
val request = Request("eth_getTransactionByHash", listOf(transactionHash.toHexString())) | |
return service.makeRequestForString(infuraCredentials.projectId, request).flatMap { | |
if (it.error != null || it.result == null) | |
Single.just(false) | |
else | |
Single.just(true) | |
} | |
} | |
fun estimateGas( | |
from: Address?, | |
to: Address, | |
value: BigInteger?, | |
gasLimit: Long?, | |
gasPrice: Long?, | |
data: String? | |
): Single<String> { | |
val params: MutableMap<String, String> = mutableMapOf("to" to to.hex) | |
from?.let { params.put("from", from.hex) } | |
gasLimit?.let { params.put("gas", "0x${gasLimit.toString(16).removeLeadingZeros()}") } | |
gasPrice?.let { params.put("gasPrice", "0x${gasPrice.toString(16).removeLeadingZeros()}") } | |
value?.let { params.put("value", "0x${value.toString(16).removeLeadingZeros()}") } | |
data?.let { params.put("data", data) } | |
val request = Request("eth_estimateGas", listOf(params)) | |
return service.makeRequestForString(infuraCredentials.projectId, request).flatMap { | |
returnResultOrParsedError(it) | |
} | |
} | |
fun getBlockByNumber(blockNumber: Long): Single<Block> { | |
val request = Request("eth_getBlockByNumber", listOf("0x${blockNumber.toString(16)}", false)) | |
return service.makeRequestForBlock(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it) | |
} | |
} | |
fun call(contractAddress: Address, data: ByteArray, blockNumber: Long?): Single<String> { | |
val request = Request( | |
"eth_call", | |
listOf( | |
mapOf("to" to contractAddress.hex, "data" to data.toHexString()), | |
"latest" | |
) | |
) | |
return service.makeRequestForString(infuraCredentials.projectId, request).flatMap { | |
returnResultOrError(it) | |
} | |
} | |
private fun <T> parseInfuraError(errorResponse: Response<T>): Exception { | |
if (errorResponse.error != null) { | |
return ApiError.InfuraError(errorResponse.error.code, errorResponse.error.message) | |
} | |
return ApiError.InvalidData | |
} | |
private fun <T> returnResultOrParsedError(response: Response<T>): Single<T> { | |
return if (response.error != null) { | |
Single.error(parseInfuraError(response)) | |
} else { | |
Single.just(response.result) | |
} | |
} | |
private fun <T> returnResultOrError(response: Response<T>): Single<T> { | |
return if (response.error != null) { | |
Single.error(Exception(response.error.message)) | |
} else { | |
Single.just(response.result) | |
} | |
} | |
class Request(val method: String, val params: List<Any> = listOf(), val id: Long = 1, val jsonrpc: String = "2.0") | |
class Error(val code: Int, val message: String) | |
class Response<T>(val result: T?, val error: Error?, val id: Long, val jsonrpc: String) | |
interface InfuraServiceAPI { | |
@POST("{projectId}") | |
fun makeRequestForBigInteger( | |
@Path("projectId") apiKey: String, | |
@Body request: Request | |
): Single<Response<BigInteger>> | |
@POST("{projectId}") | |
fun makeRequestForString( | |
@Path("projectId") apiKey: String, | |
@Body request: Request | |
): Single<Response<String>> | |
@POST("{projectId}") | |
fun makeRequestForLogs( | |
@Path("projectId") apiKey: String, | |
@Body request: Request | |
): Single<Response<List<EthereumLog>>> | |
@POST("{projectId}") | |
fun makeRequestForBlock( | |
@Path("projectId") apiKey: String, | |
@Body request: Request | |
): Single<Response<Block>> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment