Skip to content

Commit

Permalink
Merge pull request #71 from icerockdev/develop
Browse files Browse the repository at this point in the history
Release 0.11.0
  • Loading branch information
anton6tak authored Oct 9, 2021
2 parents dc73ddd + 935b80f commit 1db3fe5
Show file tree
Hide file tree
Showing 19 changed files with 267 additions and 47 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ allprojects {
project build.gradle
```groovy
dependencies {
commonMainApi("dev.icerock.moko:web3:0.10.2")
commonMainApi("dev.icerock.moko:web3:0.11.0")
}
```

Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ kbignumVersion = "2.2.0"
klockVersion = "2.2.2"
ktorClientVersion = "1.6.1"
mokoTestVersion = "0.4.0"
mokoWeb3Version = "0.10.2"
mokoWeb3Version = "0.11.0"
multidexVersion = "2.0.1"

[libraries]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Web3SocketTest {
)
}

@Test
// @Test
fun `test web socket flow`() {
runBlocking {
web3Socket.subscribeWebSocketWithFilter(SubscriptionParam.Logs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@

package dev.icerock.moko.web3

inline class BlockHash(val value: String)
import dev.icerock.moko.web3.hex.Hex32String

class BlockHash(val value: String) : Hex32String by Hex32String(value) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is BlockHash && withoutPrefix == other.withoutPrefix
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@

package dev.icerock.moko.web3

import kotlin.jvm.JvmInline
import dev.icerock.moko.web3.crypto.createChecksummedAddress
import dev.icerock.moko.web3.hex.HexString

@JvmInline
value class ContractAddress(override val value: String) : EthereumAddress
interface ContractAddress : EthereumAddress {
companion object : HexString.Factory<ContractAddress> {
override fun createInstance(value: String): ContractAddress = ContractAddress(value)
}
}

fun ContractAddress(value: String): ContractAddress = _ContractAddress(value)

fun ContractAddress.toChecksummedAddress(): ContractAddress =
createChecksummedAddress(sourceAddress = this, factoryTypeclass = ContractAddress)

@Suppress("ClassName")
private class _ContractAddress(value: String) : EthereumAddress by EthereumAddress(value), ContractAddress {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is _ContractAddress && withoutPrefix == other.withoutPrefix
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

@file:Suppress("ClassName")

package dev.icerock.moko.web3

inline class Erc20TokenAddress(override val value: String) : EthereumAddress {
val contract: ContractAddress
get() = ContractAddress(value)
}
interface Erc20TokenAddress : ContractAddress

private class _Erc20TokenAddress(value: String) : Erc20TokenAddress, ContractAddress by ContractAddress(value)

fun Erc20TokenAddress(value: String): Erc20TokenAddress = _Erc20TokenAddress(value)
40 changes: 26 additions & 14 deletions web3/src/commonMain/kotlin/dev.icerock.moko.web3/EthereumAddress.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,33 @@

package dev.icerock.moko.web3

import com.soywiz.kbignum.BigInt
import com.soywiz.kbignum.bi
import dev.icerock.moko.web3.crypto.toChecksummedAddress
import dev.icerock.moko.web3.crypto.createChecksummedAddress
import dev.icerock.moko.web3.hex.Hex20String
import dev.icerock.moko.web3.hex.Hex32String
import dev.icerock.moko.web3.hex.Hex8String
import dev.icerock.moko.web3.hex.HexString

interface EthereumAddress {
val value: String
interface EthereumAddress : Hex32String {
companion object : HexString.Factory<EthereumAddress> {
override fun createInstance(value: String): EthereumAddress = EthereumAddress(value)
}
}

val bigInt: BigInt get() = value.removePrefix("0x").bi(16)
@Suppress("ClassName")
private class _EthereumAddress(value: String) : Hex20String by Hex20String(value), EthereumAddress {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is EthereumAddress && withoutPrefix == other.withoutPrefix
}

val checksummed: ContractAddress get() = ContractAddress(value = value.toChecksummedAddress())
val isChecksummed: Boolean get() = this == checksummed
fun EthereumAddress(value: String): EthereumAddress = _EthereumAddress(value)

val isValid get(): Boolean {
return value.uppercase() == value
|| value.lowercase() == value
|| isChecksummed
}
}
fun EthereumAddress.toChecksummedAddress(): EthereumAddress =
createChecksummedAddress(sourceAddress = this, factoryTypeclass = EthereumAddress)

val EthereumAddress.isChecksummed: Boolean get() = withoutPrefix == toChecksummedAddress().withoutPrefix

val EthereumAddress.isValid: Boolean get() =
withoutPrefix.uppercase() == withoutPrefix
|| withoutPrefix.lowercase() == withoutPrefix
|| isChecksummed
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

package dev.icerock.moko.web3

import dev.icerock.moko.web3.hex.Hex32String
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline

@JvmInline
@Serializable
value class TransactionHash(val value: String)
class TransactionHash(private val value: String) : Hex32String by Hex32String(value) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is TransactionHash && other.withoutPrefix == withoutPrefix
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@

package dev.icerock.moko.web3

inline class WalletAddress(override val value: String) : EthereumAddress
import dev.icerock.moko.web3.hex.Hex8String

class WalletAddress(value: String) : EthereumAddress by EthereumAddress(value) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is WalletAddress && withoutPrefix == other.withoutPrefix
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ class SmartContract(
): ContractRPC {
val callData: String = createCallData(method, params)
return ContractRPC(
to = contractAddress.value,
from = from?.value,
to = contractAddress.prefixed,
from = from?.prefixed,
data = callData,
value = value
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@

package dev.icerock.moko.web3.crypto

import dev.icerock.moko.web3.EthereumAddress
import dev.icerock.moko.web3.hex.HexString
import dev.icerock.moko.web3.toChecksummedAddress

/**
* Explanation: https://coincodex.com/article/2078/ethereum-address-checksum-explained/
* Algorithm: https://ethereum.stackexchange.com/questions/1374/how-can-i-check-if-an-ethereum-address-is-valid,
* https://github.com/ethers-io/ethers.js/blob/ce8f1e4015c0f27bf178238770b1325136e3351a/packages/address/src.ts/index.ts#L12
*/
internal fun String.toChecksummedAddress(): String {
val addressValue = this
fun <T : EthereumAddress> createChecksummedAddress(
sourceAddress: T,
factoryTypeclass: HexString.Factory<T>
): T = with(sourceAddress) {
val hashed = withoutPrefix
.lowercase()
.removePrefix(prefix = "0x")

val hashed = addressValue
.keccakHash
.asHexInts

val result = addressValue
val result = withoutPrefix
.mapIndexed { i, char -> char.takeIf { hashed[i] < 8 } ?: char.uppercase() }
.joinToString(separator = "")

return "0x$result"
return factoryTypeclass.createInstance(result)
}

private val String.keccakHash get(): ByteArray = this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.soywiz.kbignum.BigInt
import dev.icerock.moko.web3.BlockHash
import dev.icerock.moko.web3.ContractAddress
import dev.icerock.moko.web3.TransactionHash
import dev.icerock.moko.web3.hex.Hex32String
import dev.icerock.moko.web3.serializer.BigIntSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
Expand Down Expand Up @@ -39,4 +40,11 @@ class LogEvent(

@Transient
val txHash: TransactionHash = TransactionHash(_transactionHash)

fun <T> deserializeData(dataDeserializer: DataDeserializer<T>): T =
dataDeserializer.deserialize(data.chunked(size = 32 * 2).map(::Hex32String))

fun interface DataDeserializer<T> {
fun deserialize(source: List<Hex32String>): T
}
}
111 changes: 111 additions & 0 deletions web3/src/commonMain/kotlin/dev.icerock.moko.web3/hex/Constructors.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

@file:Suppress("ClassName")

package dev.icerock.moko.web3.hex

/**
* @param strict if true then there are additional check for odd
*/
fun HexString(value: String, strict: Boolean = true): HexString = HexStringValueClass(value).apply {
if (strict) {
require(withoutPrefix.length % 2 == 0) { "Hex string should have an odd length" }
}
}

/**
* @param size is byte-equivalent size for string
*/
fun HexString(value: String, size: Int): HexString = HexString(value, strict = true)
.apply {
val hexSize = withoutPrefix.length / 2
require(hexSize == size) { "Hex string should have an $size bytes size, but was $hexSize" }
}

// Consider inheriting this interfaces only using the factory function for safety

interface Hex8String : HexString {
companion object : HexString.Factory<Hex8String> {
override fun createInstance(value: String): Hex8String = Hex8String(value)
}
}
private class _Hex8String(value: String) : Hex8String, HexString by HexString(value, size = 8) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is Hex8String && withoutPrefix == other.withoutPrefix
}
fun Hex8String(value: String): Hex8String = _Hex8String(value)

interface Hex16String : HexString {
companion object : HexString.Factory<Hex16String> {
override fun createInstance(value: String): Hex16String = Hex16String(value)
}
}
private class _Hex16String(value: String) : Hex16String, HexString by HexString(value, size = 16) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is Hex16String && withoutPrefix == other.withoutPrefix
}
fun Hex16String(value: String): Hex16String = _Hex16String(value)

interface Hex20String : HexString {
companion object : HexString.Factory<Hex20String> {
override fun createInstance(value: String): Hex20String = Hex20String(value)
}
}
private class _Hex20String(value: String) : Hex20String, HexString by HexString(value, size = 20) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is Hex20String && withoutPrefix == other.withoutPrefix
}
fun Hex20String(value: String): Hex20String = _Hex20String(value)

interface Hex32String : HexString {
companion object : HexString.Factory<Hex32String> {
override fun createInstance(value: String): Hex32String = Hex32String(value)
}
}
private class _Hex32String(value: String) : Hex32String, HexString by HexString(value, size = 32) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is Hex32String && withoutPrefix == other.withoutPrefix
}
fun Hex32String(value: String): Hex32String = _Hex32String(value)

interface Hex64String : HexString {
companion object : HexString.Factory<Hex64String> {
override fun createInstance(value: String): Hex64String = Hex64String(value)
}
}
private class _Hex64String(value: String) : Hex64String, HexString by HexString(value, size = 64) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is Hex64String && withoutPrefix == other.withoutPrefix
}
fun Hex64String(value: String): Hex64String = _Hex64String(value)

interface Hex128String : HexString {
companion object : HexString.Factory<Hex128String> {
override fun createInstance(value: String): Hex128String = Hex128String(value)
}
}
private class _Hex128String(value: String) : Hex128String, HexString by HexString(value, size = 128) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is Hex128String && withoutPrefix == other.withoutPrefix
}
fun Hex128String(value: String): Hex128String = _Hex128String(value)

interface Hex256String : HexString {
companion object : HexString.Factory<Hex256String> {
override fun createInstance(value: String): Hex256String = Hex256String(value)
}
}
private class _Hex256String(value: String) : Hex256String, HexString by HexString(value, size = 256) {
override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is Hex256String && withoutPrefix == other.withoutPrefix
}
fun Hex256String(value: String): Hex256String = _Hex256String(value)
21 changes: 21 additions & 0 deletions web3/src/commonMain/kotlin/dev.icerock.moko.web3/hex/HexString.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.web3.hex

import com.soywiz.kbignum.BigInt

interface HexString {
val withoutPrefix: String
val prefixed: String
val bigInt: BigInt

interface Factory<T> {
fun createInstance(value: String): T
}

companion object : Factory<HexString> {
override fun createInstance(value: String): HexString = HexString(value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.web3.hex

import com.soywiz.kbignum.BigInt
import com.soywiz.kbignum.bi
import kotlin.jvm.JvmInline

internal class HexStringValueClass(private val value: String): HexString {
init {
require(withoutPrefix.matches(Regex("[0-9a-fA-F]+")))
}

override val withoutPrefix: String get() = value.removePrefix(HEX_PREFIX)
override val prefixed: String get() = "$HEX_PREFIX$withoutPrefix"
override val bigInt: BigInt get() = value.bi(RADIX)

override fun toString() = withoutPrefix
override fun hashCode(): Int = withoutPrefix.hashCode()
override fun equals(other: Any?): Boolean = other is HexStringValueClass && other.withoutPrefix == withoutPrefix

companion object {
const val HEX_PREFIX = "0x"
const val RADIX = 16
}
}
Loading

0 comments on commit 1db3fe5

Please sign in to comment.