diff --git a/erc831/src/main/kotlin/org/kethereum/erc831/ERC831.kt b/erc831/src/main/kotlin/org/kethereum/erc831/ERC831.kt index c02d6d56..53add00b 100644 --- a/erc831/src/main/kotlin/org/kethereum/erc831/ERC831.kt +++ b/erc831/src/main/kotlin/org/kethereum/erc831/ERC831.kt @@ -1,9 +1,10 @@ package org.kethereum.erc831 -// as defined in http://eips.ethereum.org/EIPS/eip-831 - +/** + * as defined in [EIP-831](https://eips.ethereum.org/EIPS/eip-831) + */ data class ERC831( - var scheme: String? = null, - var prefix: String? = null, - var payload: String? = null + var scheme: String? = null, + var prefix: String? = null, + var payload: String? = null ) \ No newline at end of file diff --git a/erc831/src/main/kotlin/org/kethereum/erc831/ERC831Parser.kt b/erc831/src/main/kotlin/org/kethereum/erc831/ERC831Parser.kt index 95bba399..3c00fa82 100644 --- a/erc831/src/main/kotlin/org/kethereum/erc831/ERC831Parser.kt +++ b/erc831/src/main/kotlin/org/kethereum/erc831/ERC831Parser.kt @@ -11,7 +11,7 @@ private enum class ParseState { PAYLOAD } -fun EthereumURI.toERC831() = ERC831().apply { +fun EthereumURI.toERC831(): ERC831 = ERC831().apply { var currentSegment = "" @@ -39,12 +39,13 @@ fun EthereumURI.toERC831() = ERC831().apply { } } - if (!currentSegment.isBlank()) { + if (currentSegment.isNotBlank()) { stateTransition(PAYLOAD) } } -private fun String.hasPrefix() = contains('-') && (!contains("0x") || indexOf('-') < indexOf("0x")) +private fun String.hasPrefix(): Boolean = + contains('-') && (!contains("0x") || indexOf('-') < indexOf("0x")) -fun parseERC831(url: String) = EthereumURI(url).toERC831() \ No newline at end of file +fun parseERC831(url: String): ERC831 = EthereumURI(url).toERC831() \ No newline at end of file diff --git a/erc961/README.md b/erc961/README.md new file mode 100644 index 00000000..15aa32f1 --- /dev/null +++ b/erc961/README.md @@ -0,0 +1,33 @@ +This module provides and implements methods for working with [EIP-961](https://github.com/ethereum/EIPs/pull/961) + +### Installation +[![](https://jitpack.io/v/komputing/kethereum.svg)](https://jitpack.io/#komputing/kethereum) +```groovy +implementation("com.github.komputing.kethereum:model:$KETHEREUM_VERSION") +implementation("com.github.komputing.kethereum:erc961:$KETHEREUM_VERSION") +``` + +### Usage +#### Generating EIP-961 token URL +```kotlin +val address = Address(0x00AB42) +val chainId = ChainId(4) +val erc20Token = Token("TST", address, chainId) +val eip651Url: String = erc20Token.generateURL() + +println(eip651Url) // ethereum:token_info-0x00AB42@4?symbol=TST +``` + +#### Working with EIP-961 token URL +```kotlin +val exampleUrl = "ethereum:token_info-0x00AB42@4?symbol=TST" + +println(isEthereumTokenURI(exampleUri)) // true +val tokenFromUrl: Token = parseTokenFromEthereumURI(exampleUrl) +println(tokenFromUrl) // Token(symbol="TST", address=0x00AB42, chainId=4, decimals=18) + +// also there are extension methods for EthereumURI class +val uri = EthereumURI(exampleUrl) +println(uri.isTokenURI()) // true +println(uri.parseToken()) // Token(symbol="TST", address=0x00AB42, chainId=4, decimals=18) +``` diff --git a/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Generator.kt b/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Generator.kt index 890c1631..df2916a4 100644 --- a/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Generator.kt +++ b/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Generator.kt @@ -2,13 +2,20 @@ package org.kethereum.erc961 import org.kethereum.model.Token +private const val EIP_961_DEFAULT_DECIMALS = 18 + +/** + * Create URL in [EIP-961](https://github.com/ethereum/EIPs/pull/961) format + * + * e.g. ethereum:token_info-0x00AB42@4?symbol=TST + */ fun Token.generateURL(): String { val params = mutableListOf() params.add("symbol=$symbol") - if (decimals != 18) { + if (decimals != EIP_961_DEFAULT_DECIMALS) { params.add("decimals=$decimals") } @@ -20,5 +27,7 @@ fun Token.generateURL(): String { params.add("type=$it") } - return "ethereum:token_info-$address@${chain.value}?" + params.joinToString("&") + val joinedParams = params.joinToString("&") + + return "ethereum:token_info-$address@${chain.value}?$joinedParams" } \ No newline at end of file diff --git a/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Parser.kt b/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Parser.kt index 6f9bc7d9..d02762df 100644 --- a/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Parser.kt +++ b/erc961/src/main/kotlin/org/kethereum/erc961/ERC961Parser.kt @@ -11,8 +11,11 @@ class InvalidTokenURIException : IllegalArgumentException("invalid token") private const val TOKEN_INFO_PREFIX = "token_info" private const val FULL_URI_PREFIX = "ethereum:$TOKEN_INFO_PREFIX" -fun isEthereumTokenURI(uri: String) = uri.startsWith(FULL_URI_PREFIX) +fun isEthereumTokenURI(uri: String): Boolean = uri.startsWith(FULL_URI_PREFIX) +/** + * @throws InvalidTokenURIException if the URL doesn't implement EIP-961 standard + */ fun parseTokenFromEthereumURI(uri: String): Token { val commonEthereumURI = EthereumURI(uri).parseCommonURI() val queryMap = commonEthereumURI.query.toMap() @@ -23,7 +26,7 @@ fun parseTokenFromEthereumURI(uri: String): Token { return Token( symbol = queryMap["symbol"] ?: "SYM", - address = Address(commonEthereumURI.address ?: ""), + address = Address(commonEthereumURI.address.orEmpty()), chain = commonEthereumURI.chainId ?: ChainId(1), name = queryMap["name"], decimals = queryMap["decimals"]?.toInt() ?: 18, @@ -31,5 +34,5 @@ fun parseTokenFromEthereumURI(uri: String): Token { ) } -fun EthereumURI.parseToken() = parseTokenFromEthereumURI(uri) -fun EthereumURI.isTokenURI() = isEthereumTokenURI(uri) \ No newline at end of file +fun EthereumURI.parseToken(): Token = parseTokenFromEthereumURI(uri) +fun EthereumURI.isTokenURI(): Boolean = isEthereumTokenURI(uri) \ No newline at end of file diff --git a/erc961/src/test/kotlin/org/kethereum/erc961/TheERC961Parser.kt b/erc961/src/test/kotlin/org/kethereum/erc961/TheERC961Parser.kt index b9dd32b4..551c4df3 100644 --- a/erc961/src/test/kotlin/org/kethereum/erc961/TheERC961Parser.kt +++ b/erc961/src/test/kotlin/org/kethereum/erc961/TheERC961Parser.kt @@ -4,56 +4,99 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Test import org.kethereum.model.Address -import org.kethereum.model.ChainDefinition import org.kethereum.model.ChainId +import org.kethereum.model.EthereumURI class TheERC961Parser { @Test fun canParseAddress() { assertThat(parseTokenFromEthereumURI("ethereum:token_info-0x00AB42@4?symbol=TST").address) - .isEqualTo(Address("0x00AB42")) + .isEqualTo(Address("0x00AB42")) + } + + @Test + fun canParseAddressAsExtension() { + val address = EthereumURI("ethereum:token_info-0x00AB42@4?symbol=TST") + assertThat(address.parseToken().address).isEqualTo(Address("0x00AB42")) } @Test fun canParseChain() { assertThat(parseTokenFromEthereumURI("ethereum:token_info-0x00AB42@425?symbol=TST").chain) - .isEqualTo(ChainId(425)) + .isEqualTo(ChainId(425)) + } + + @Test + fun canParseChainAsExtension() { + val address = EthereumURI("ethereum:token_info-0x00AB42@425?symbol=TST") + assertThat(address.parseToken().chain).isEqualTo(ChainId(425)) } @Test fun canParseSymbol() { assertThat(parseTokenFromEthereumURI("ethereum:token_info-0x00AB42@4?symbol=TST").symbol) - .isEqualTo("TST") + .isEqualTo("TST") + } + + @Test + fun canParseSymbolAsExtension() { + val address = EthereumURI("ethereum:token_info-0x00AB42@4?symbol=TST") + assertThat(address.parseToken().symbol).isEqualTo("TST") } @Test fun decimalsDefaultTo18() { assertThat(parseTokenFromEthereumURI("ethereum:token_info-0x00AB42@4").decimals) - .isEqualTo(18) + .isEqualTo(18) + } + + @Test + fun decimalsDefaultTo18AsExtension() { + val address = EthereumURI("ethereum:token_info-0x00AB42@4") + assertThat(address.parseToken().decimals).isEqualTo(18) } @Test fun canParseDecimals() { assertThat(parseTokenFromEthereumURI("ethereum:token_info-0x00AB42@4?decimals=5").decimals) - .isEqualTo(5) + .isEqualTo(5) + } + + @Test + fun canParseDecimalsAsExtension() { + val address = EthereumURI("ethereum:token_info-0x00AB42@4?decimals=5") + assertThat(address.parseToken().decimals).isEqualTo(5) } @Test fun canParseName() { assertThat(parseTokenFromEthereumURI("ethereum:token_info-0x00AB42@4?name=yolo").name) - .isEqualTo("yolo") + .isEqualTo("yolo") + } + + @Test + fun canParseNameAsExtension() { + val address = EthereumURI("ethereum:token_info-0x00AB42@4?name=yolo") + assertThat(address.parseToken().name).isEqualTo("yolo") } @Test fun canParseType() { assertThat(parseTokenFromEthereumURI("ethereum:token_info-0x00AB42@4?type=FOO").type) - .isEqualTo("FOO") + .isEqualTo("FOO") + } + + @Test + fun canParseTypeAsExtension() { + val address = EthereumURI("ethereum:token_info-0x00AB42@4?type=FOO") + assertThat(address.parseToken().type).isEqualTo("FOO") } @Test fun canParseFull() { - val parseTokenFromEthereumURI = parseTokenFromEthereumURI("ethereum:token_info-0x00AB43@425?type=TypTest&name=NameTest&decimals=2&symbol=SymbolTest") + val parseTokenFromEthereumURI = + parseTokenFromEthereumURI("ethereum:token_info-0x00AB43@425?type=TypTest&name=NameTest&decimals=2&symbol=SymbolTest") assertThat(parseTokenFromEthereumURI.address).isEqualTo(Address("0x00AB43")) assertThat(parseTokenFromEthereumURI.chain).isEqualTo(ChainId(425)) assertThat(parseTokenFromEthereumURI.type).isEqualTo("TypTest") @@ -62,12 +105,10 @@ class TheERC961Parser { assertThat(parseTokenFromEthereumURI.decimals).isEqualTo(2) } - @Test fun failsForInvalidScheme() { assertThrows(InvalidTokenURIException::class.java) { parseTokenFromEthereumURI("ethereUUm:token_info") } } - } diff --git a/model/src/main/kotlin/org/kethereum/model/SignedTransaction.kt b/model/src/main/kotlin/org/kethereum/model/SignedTransaction.kt index 2d88dcd7..e9b8baec 100644 --- a/model/src/main/kotlin/org/kethereum/model/SignedTransaction.kt +++ b/model/src/main/kotlin/org/kethereum/model/SignedTransaction.kt @@ -1,6 +1,6 @@ package org.kethereum.model data class SignedTransaction( - var transaction: Transaction, - var signatureData: SignatureData + var transaction: Transaction, + var signatureData: SignatureData ) \ No newline at end of file diff --git a/model/src/main/kotlin/org/kethereum/model/Token.kt b/model/src/main/kotlin/org/kethereum/model/Token.kt index 9a33fe9e..74e6ecfd 100644 --- a/model/src/main/kotlin/org/kethereum/model/Token.kt +++ b/model/src/main/kotlin/org/kethereum/model/Token.kt @@ -1,8 +1,10 @@ package org.kethereum.model -data class Token(val symbol: String, - val address: Address, - val chain: ChainId, - val decimals: Int = 18, - val type: String? = null, - val name: String? = null) \ No newline at end of file +data class Token( + val symbol: String, + val address: Address, + val chain: ChainId, + val decimals: Int = 18, + val type: String? = null, + val name: String? = null +) \ No newline at end of file diff --git a/uri_common/src/main/kotlin/org/kethereum/uri/common/CommonEthereumURIData.kt b/uri_common/src/main/kotlin/org/kethereum/uri/common/CommonEthereumURIData.kt index b89992a2..6cca4b13 100644 --- a/uri_common/src/main/kotlin/org/kethereum/uri/common/CommonEthereumURIData.kt +++ b/uri_common/src/main/kotlin/org/kethereum/uri/common/CommonEthereumURIData.kt @@ -3,11 +3,11 @@ package org.kethereum.uri.common import org.kethereum.model.ChainId data class CommonEthereumURIData( - var valid: Boolean = true, - var scheme: String? = null, - var prefix: String? = null, - var chainId: ChainId? = null, - var address: String? = null, - var function: String? = null, - var query: List> = listOf() + var valid: Boolean = true, + var scheme: String? = null, + var prefix: String? = null, + var chainId: ChainId? = null, + var address: String? = null, + var function: String? = null, + var query: List> = listOf() ) \ No newline at end of file