diff --git a/bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt b/bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt index 6d70652..53a7d2b 100644 --- a/bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt +++ b/bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt @@ -19,12 +19,23 @@ class AbiParser { fun hash(): String } - internal class SimpleTypeHolder(private val className: ClassName, private val dynamic: Boolean) : TypeHolder { + internal class SimpleTypeHolder private constructor(private val className: ClassName, private val dynamic: Boolean) : TypeHolder { override fun toTypeName() = className override fun isDynamic() = dynamic override fun hash() = generateHash(listOf(className.toString())) + + companion object { + /** + * Create a SimpleTypeHolder for a given type if possible. + * If the given type is not a simple type (e.g. Tuple) null will be returned. + */ + fun forType(type: String): SimpleTypeHolder? { + val baseType = Solidity.types[checkType(type)] ?: return null + return SimpleTypeHolder(ClassName.bestGuess(baseType), SolidityBase.dynamicTypes.contains(type.toLowerCase())) + } + } } internal abstract class CollectionTypeHolder(val listType: ClassName, val itemType: TypeHolder) : TypeHolder { @@ -165,7 +176,7 @@ class AbiParser { throw IllegalArgumentException("Invalid parameter definition: ${parameter.type}!") } val arrayType = matcher.group(1) - val baseType = generateElementaryType(arrayType) ?: generateTuple(arrayType, parameter, context) ?: throw IllegalArgumentException("Unknown parameter ${parameter.type}!") + val baseType = SimpleTypeHolder.forType(arrayType) ?: generateTuple(arrayType, parameter, context) ?: throw IllegalArgumentException("Unknown parameter ${parameter.type}!") val arrayDef = matcher.group(2) if (arrayType.length < parameter.type.length && arrayDef.isNullOrBlank()) { throw IllegalArgumentException("Invalid parameter definition: ${parameter.type}!") @@ -173,11 +184,6 @@ class AbiParser { return parseArrayDefinition(arrayDef, baseType, context) } - private fun generateElementaryType(type: String): TypeHolder? { - val baseType = Solidity.types[checkType(type)] ?: return null - return SimpleTypeHolder(ClassName.bestGuess(baseType), isSolidityDynamicType(type)) - } - private fun generateTuple(type: String, parameters: ParameterJson, context: GeneratorContext): TypeHolder? { if (type != TUPLE_TYPE_NAME || parameters.components == null) { return null @@ -405,14 +411,6 @@ class AbiParser { return Pair("%T.DECODER", types) } - private fun isSolidityDynamicType(type: String) = isSolidityBytesType(type) || isSolidityStringType(type) || isSolidityDynamicArray(type) - - private fun isSolidityBytesType(type: String) = type.contains("bytes") - - private fun isSolidityStringType(type: String) = type.contains("string") - - private fun isSolidityDynamicArray(type: String) = type.contains("[]") - private fun isSolidityDynamicType(type: TypeHolder) = type.isDynamic() private fun isSolidityArray(type: TypeHolder) = type is CollectionTypeHolder diff --git a/bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt b/bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt index 4fb3aa1..4f34497 100644 --- a/bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt +++ b/bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt @@ -5,8 +5,7 @@ import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.asTypeName import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue +import org.junit.Assert.* import org.junit.Test import pm.gnosis.model.AbiRoot import pm.gnosis.model.ParameterJson @@ -57,6 +56,27 @@ class AbiParserTest { AbiParser.mapType(testParameter("gnosis[1][]"), testContext()) } + @Test() + fun testSimpleTypeHolder() { + assertEquals("Unknown type should return null", AbiParser.SimpleTypeHolder.forType("unknown"), null) + assertEquals("Tuple type should return null", AbiParser.SimpleTypeHolder.forType("tuple"), null) + + val bytes32 = AbiParser.SimpleTypeHolder.forType("bytes32")!! + assertFalse("bytes32 should be static", bytes32.isDynamic()) + + val uint = AbiParser.SimpleTypeHolder.forType("uint")!! + assertFalse("uint should be static", uint.isDynamic()) + + val int = AbiParser.SimpleTypeHolder.forType("int")!! + assertFalse("int should be static", int.isDynamic()) + + val bytes = AbiParser.SimpleTypeHolder.forType("bytes")!! + assertTrue("bytes should be dynamic", bytes.isDynamic()) + + val string = AbiParser.SimpleTypeHolder.forType("string")!! + assertTrue("string should be dynamic", string.isDynamic()) + } + @Test() fun testParseAliasTypes() { val uintType = AbiParser.mapType(testParameter("uint"), testContext()) @@ -160,6 +180,36 @@ class AbiParserTest { assertEquals(Solidity.String::class.asTypeName(), g1Type.toTypeName()) } + @Test() + fun testParseBytesArray() { + val type = AbiParser.mapType(testParameter("bytes[5]"), testContext()) + assertType(type, AbiParser.ArrayTypeHolder::class) + val pType = type as AbiParser.ArrayTypeHolder + assertEquals(5, pType.capacity) + assertTrue(pType.isDynamic()) + assertEquals(ClassName("com.example.arrays", "Array5"), pType.listType) + + // First generic type + val g1Type = pType.itemType + assertEquals(Solidity.Bytes::class.asTypeName(), g1Type.toTypeName()) + assertTrue(g1Type.isDynamic()) + } + + @Test() + fun testParseBytesXArray() { + val type = AbiParser.mapType(testParameter("bytes32[5]"), testContext()) + assertType(type, AbiParser.ArrayTypeHolder::class) + val pType = type as AbiParser.ArrayTypeHolder + assertEquals(5, pType.capacity) + assertFalse(pType.isDynamic()) + assertEquals(ClassName("com.example.arrays", "Array5"), pType.listType) + + // First generic type + val g1Type = pType.itemType + assertEquals(Solidity.Bytes32::class.asTypeName(), g1Type.toTypeName()) + assertFalse(g1Type.isDynamic()) + } + @Test fun testDecodeFunctionArguments() { /* diff --git a/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/SolidityBase.kt b/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/SolidityBase.kt index b66d94e..bde5bb8 100644 --- a/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/SolidityBase.kt +++ b/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/SolidityBase.kt @@ -14,6 +14,8 @@ object SolidityBase { const val BYTES_PAD = 32 const val PADDED_HEX_LENGTH = BYTES_PAD * 2 + val dynamicTypes: List = listOf("bytes", "string") + interface Type { fun encode(): String }