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 5a9c13d..2cce6ec 100644 --- a/bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt +++ b/bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt @@ -6,11 +6,13 @@ import com.squareup.moshi.Moshi import pm.gnosis.model.* import pm.gnosis.utils.generateSolidityMethodId import java.io.File +import java.math.BigInteger object AbiParser { internal const val DECODER_FUN_ARG_NAME = "data" internal const val DECODER_VAR_PARTITIONS_NAME = "source" internal const val DECODER_VAR_ARG_PREFIX = "arg" //arg0, arg1... + internal const val DECODER_VAR_ARG_OFFSET_SUFFIX = "Offset" internal const val INDENTATION = " " internal lateinit var context: GeneratorContext @@ -33,9 +35,9 @@ object AbiParser { class ArraysMap(basePackageName: String, private val map: MutableMap = HashMap()) { - private val arraysPackageName = basePackageName + ".arrays" + private val arraysPackageName = "$basePackageName.arrays" - fun get(capacity: Int) = map.getOrPut(capacity, { ClassName(arraysPackageName, "Array" + capacity) }) + fun get(capacity: Int) = map.getOrPut(capacity) { ClassName(arraysPackageName, "Array" + capacity) } fun generate(output: File) { map.forEach { @@ -44,7 +46,7 @@ object AbiParser { val kotlinFile = FileSpec.builder(arraysPackageName, arrayType.simpleName()) - val typeVariable = TypeVariableName.Companion.invoke("T", SolidityBase.Type::class) + val typeVariable = TypeVariableName.invoke("T", SolidityBase.Type::class) val itemsType = ParameterizedTypeName.get(List::class.asClassName(), typeVariable) val parameterizedClassType = ParameterizedTypeName.get(arrayType, typeVariable) @@ -138,9 +140,7 @@ object AbiParser { //Generate decodings val decodeBlockBuilder = CodeBlock.builder() - val locationArgs = ArrayList>() - generateStaticArgDecoding(typeHolder, decodeBlockBuilder, locationArgs) - generateDynamicArgDecoding(locationArgs, decodeBlockBuilder) + generateStaticArgDecoding(typeHolder, decodeBlockBuilder) decodeBlockBuilder.addStatement("return %1N(${(0 until typeHolder.entries.size).joinToString(", ") { "arg$it" }})", typeHolder.name) builder @@ -155,9 +155,9 @@ object AbiParser { }.toList() - private fun generateStaticArgDecoding(typeHolder: TupleTypeHolder, function: CodeBlock.Builder, locationArgs: MutableCollection>) { + private fun generateStaticArgDecoding(typeHolder: TupleTypeHolder, function: CodeBlock.Builder) { typeHolder.entries.forEachIndexed { index, info -> - addDecoderStatementForType(function, locationArgs, index, info.second) + addDecoderStatementForType(function, index, info.second) } } @@ -205,10 +205,7 @@ object AbiParser { internal fun generateParameterDecoderCode(parameters: List): CodeBlock { val codeBlock = CodeBlock.builder() - //Generate decodings - val locationArgs = ArrayList>() - generateStaticArgDecoding(parameters, codeBlock, locationArgs) - generateDynamicArgDecoding(locationArgs, codeBlock) + generateStaticArgDecoding(parameters, codeBlock) return codeBlock.build() } @@ -227,37 +224,39 @@ object AbiParser { return returnContainerBuilder.primaryConstructor(returnContainerConstructor.build()).build() } - private fun generateStaticArgDecoding(parameters: List, function: CodeBlock.Builder, locationArgs: MutableCollection>) { + private fun generateStaticArgDecoding(parameters: List, function: CodeBlock.Builder) { parameters.forEachIndexed { index, outputJson -> val className = mapType(outputJson, context) - addDecoderStatementForType(function, locationArgs, index, className) + addDecoderStatementForType(function, index, className) } } - private fun addDecoderStatementForType(function: CodeBlock.Builder, locationArgs: MutableCollection>, index: Int, className: TypeHolder) { - when { - isSolidityDynamicType(className) -> { - locationArgs.add("$index" to className) - function.addStatement("$DECODER_VAR_PARTITIONS_NAME.consume()") - } - isSolidityArray(className) -> { - val format = buildArrayDecoder(className, forceStatic = true) - function.addStatement("val $DECODER_VAR_ARG_PREFIX$index = ${format.first}.decode(%L)", *format.second.toTypedArray(), DECODER_VAR_PARTITIONS_NAME) - } - else -> function.addStatement("val $DECODER_VAR_ARG_PREFIX$index = %1T.DECODER.decode($DECODER_VAR_PARTITIONS_NAME)", className.toTypeName()) + private fun addDecoderStatementForType(function: CodeBlock.Builder, index: Int, className: TypeHolder) { + val dynamicValName = "$DECODER_VAR_ARG_PREFIX$index" + val source = if (isSolidityDynamicType(className)) { + val dynamicValOffsetName = "$dynamicValName$DECODER_VAR_ARG_OFFSET_SUFFIX" + function.addStatement( + "val $dynamicValOffsetName = %T(%L.consume(), 16).intValueExact()", + BigInteger::class.asClassName(), DECODER_VAR_PARTITIONS_NAME + ) + "%L.subData(%L)" to mutableListOf(DECODER_VAR_PARTITIONS_NAME, dynamicValOffsetName) + } else { + DECODER_VAR_PARTITIONS_NAME to mutableListOf() } - } - private fun generateDynamicArgDecoding(locationArgs: List>, function: CodeBlock.Builder) { - locationArgs.forEach { (argIndex, type) -> - val dynamicValName = "$DECODER_VAR_ARG_PREFIX$argIndex" - - if (isSolidityArray(type)) { - val format = buildArrayDecoder(type) - function.addStatement("val $dynamicValName = ${format.first}.decode(%L)", *format.second.toTypedArray(), DECODER_VAR_PARTITIONS_NAME) - } else { - function.addStatement("val $dynamicValName = %1T.DECODER.decode(%2L)", type.toTypeName(), DECODER_VAR_PARTITIONS_NAME) - } + if (isSolidityArray(className)) { + val format = buildArrayDecoder(className) + function + .addStatement( + "val $dynamicValName = ${format.first}.decode(${source.first})", + *format.second.toTypedArray(), *source.second.toTypedArray() + ) + } else { + function + .addStatement( + "val $dynamicValName = %T.DECODER.decode(${source.first})", + className.toTypeName(), *source.second.toTypedArray() + ) } } diff --git a/bivrost-abi-parser/src/main/kotlin/pm/gnosis/EventParser.kt b/bivrost-abi-parser/src/main/kotlin/pm/gnosis/EventParser.kt index f595b99..458f32e 100644 --- a/bivrost-abi-parser/src/main/kotlin/pm/gnosis/EventParser.kt +++ b/bivrost-abi-parser/src/main/kotlin/pm/gnosis/EventParser.kt @@ -14,7 +14,6 @@ internal object EventParser { private const val EVENT_ARGUMENTS_CLASS_NAME = "Arguments" private const val DECODE_FUN_NAME = "decode" private const val TOPIC_ARG_NAME = "topics" - private const val TOPICS_PARTITION_DATA_NAME = "topicsSource" internal fun generateEventObjects(): TypeSpec? { val eventsObject = TypeSpec.objectBuilder(ROOT_OBJECT_NAME) @@ -85,17 +84,19 @@ internal object EventParser { private fun generateTopicsDecoderCodeBlock(indexedParameters: List, isAnonymous: Boolean): CodeBlock { val codeBlock = CodeBlock.builder() codeBlock.addStatement("// Decode topics") - codeBlock.addStatement("val $TOPICS_PARTITION_DATA_NAME = %1T($TOPIC_ARG_NAME)", SolidityBase.PartitionData::class) if (!isAnonymous) { - codeBlock.addStatement("if ($TOPICS_PARTITION_DATA_NAME.consume() != $EVENT_ID_PROPERTY_NAME) throw %1T(\"topics[0] does not match event id\")", IllegalArgumentException::class) + codeBlock.addStatement("if ($TOPIC_ARG_NAME.first() != $EVENT_ID_PROPERTY_NAME) throw %1T(\"topics[0] does not match event id\")", IllegalArgumentException::class) } indexedParameters.forEachIndexed { index, topic -> val typeHolder = mapType(topic, context) if (typeHolder.isHashTopic(topic)) { - codeBlock.addStatement("val t${index + 1} = $TOPICS_PARTITION_DATA_NAME.consume()") + codeBlock.addStatement("val t${index + 1} = $TOPIC_ARG_NAME[${index + 1}]") } else { - codeBlock.addStatement("val t${index + 1} = %1T.DECODER.decode($TOPICS_PARTITION_DATA_NAME)", typeHolder.toTypeName()) + val sourceName = "source${index + 1}" + codeBlock + .addStatement("val $sourceName = %1T.of($TOPIC_ARG_NAME[${index + 1}])", SolidityBase.PartitionData::class) + .addStatement("val t${index + 1} = %1T.DECODER.decode(%2L)", typeHolder.toTypeName(), sourceName) } } 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 c0daee8..34de486 100644 --- a/bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt +++ b/bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt @@ -231,8 +231,11 @@ class AbiParserTest { Solidity.UInt256.DECODER.decode(testData).value, BigInteger("123", 16)) - // Consume location of dynamic uint32 array (we don't need it) - testData.consume() + // Decode uint32[] + val uint32Offset = BigInteger(testData.consume(), 16).intValueExact() + assertEquals( + SolidityBase.Vector.Decoder(Solidity.UInt32.DECODER).decode(testData.subData(uint32Offset)).items, + listOf(Solidity.UInt32(BigInteger("456", 16)), Solidity.UInt32(BigInteger("789", 16)))) // Decode bytes10 Assert.assertArrayEquals( @@ -240,17 +243,10 @@ class AbiParserTest { "1234567890".toByteArray()) // Consume location of bytes (we don't need it) - testData.consume() - - // Decode dynamic uint32 array - assertEquals( - SolidityBase.Vector.Decoder(Solidity.UInt32.DECODER).decode(testData).items, - listOf(Solidity.UInt32(BigInteger("456", 16)), Solidity.UInt32(BigInteger("789", 16)))) - - // Decode bytes + val bytesOffset = BigInteger(testData.consume(), 16).intValueExact() Assert.assertArrayEquals( - Solidity.Bytes.DECODER.decode(testData).items, - "Hello, world!".toByteArray()) + Solidity.Bytes.DECODER.decode(testData.subData(bytesOffset)).items, + "Hello, world!".toByteArray()) } @Test diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/0_empty_contract/abis/empty_contract.json b/bivrost-abi-parser/src/test/resources/automatic_tests/00_empty_contract/abis/empty_contract.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/0_empty_contract/abis/empty_contract.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/00_empty_contract/abis/empty_contract.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/0_empty_contract/expected/EmptyContract.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/00_empty_contract/expected/EmptyContract.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/0_empty_contract/expected/EmptyContract.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/00_empty_contract/expected/EmptyContract.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/1_simple_function/abis/abi1.json b/bivrost-abi-parser/src/test/resources/automatic_tests/01_simple_function/abis/abi1.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/1_simple_function/abis/abi1.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/01_simple_function/abis/abi1.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/1_simple_function/expected/Abi1.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/01_simple_function/expected/Abi1.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/1_simple_function/expected/Abi1.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/01_simple_function/expected/Abi1.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/2_function_no_type/abis/abi2.json b/bivrost-abi-parser/src/test/resources/automatic_tests/02_function_no_type/abis/abi2.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/2_function_no_type/abis/abi2.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/02_function_no_type/abis/abi2.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/2_function_no_type/expected/Abi2.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/02_function_no_type/expected/Abi2.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/2_function_no_type/expected/Abi2.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/02_function_no_type/expected/Abi2.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/3_no_inputs_and_outputs/abis/abi3.json b/bivrost-abi-parser/src/test/resources/automatic_tests/03_no_inputs_and_outputs/abis/abi3.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/3_no_inputs_and_outputs/abis/abi3.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/03_no_inputs_and_outputs/abis/abi3.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/3_no_inputs_and_outputs/expected/Abi3.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/03_no_inputs_and_outputs/expected/Abi3.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/3_no_inputs_and_outputs/expected/Abi3.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/03_no_inputs_and_outputs/expected/Abi3.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/4_unnamed_function/abis/abi4.json b/bivrost-abi-parser/src/test/resources/automatic_tests/04_unnamed_function/abis/abi4.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/4_unnamed_function/abis/abi4.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/04_unnamed_function/abis/abi4.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/4_unnamed_function/expected/Abi4.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/04_unnamed_function/expected/Abi4.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/4_unnamed_function/expected/Abi4.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/04_unnamed_function/expected/Abi4.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/5_only_name/abis/abi5.json b/bivrost-abi-parser/src/test/resources/automatic_tests/05_only_name/abis/abi5.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/5_only_name/abis/abi5.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/05_only_name/abis/abi5.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/5_only_name/expected/Abi5.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/05_only_name/expected/Abi5.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/5_only_name/expected/Abi5.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/05_only_name/expected/Abi5.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/6_function_input/abis/abi6.json b/bivrost-abi-parser/src/test/resources/automatic_tests/06_function_input/abis/abi6.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/6_function_input/abis/abi6.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/06_function_input/abis/abi6.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/6_function_input/expected/Abi6.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/06_function_input/expected/Abi6.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/6_function_input/expected/Abi6.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/06_function_input/expected/Abi6.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/7_function_input_output/abis/abi7.json b/bivrost-abi-parser/src/test/resources/automatic_tests/07_function_input_output/abis/abi7.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/7_function_input_output/abis/abi7.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/07_function_input_output/abis/abi7.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/7_function_input_output/expected/Abi7.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/07_function_input_output/expected/Abi7.kt similarity index 82% rename from bivrost-abi-parser/src/test/resources/automatic_tests/7_function_input_output/expected/Abi7.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/07_function_input_output/expected/Abi7.kt index c5ec41f..e5ca89e 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/7_function_input_output/expected/Abi7.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/07_function_input_output/expected/Abi7.kt @@ -1,5 +1,6 @@ package expected +import java.math.BigInteger import kotlin.String import pm.gnosis.model.Solidity import pm.gnosis.model.SolidityBase @@ -14,8 +15,8 @@ class Abi7 { val source = SolidityBase.PartitionData.of(data) // Add decoders - source.consume() - val arg0 = Solidity.Bytes.DECODER.decode(source) + val arg0Offset = BigInteger(source.consume(), 16).intValueExact() + val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) return Return(arg0) } diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/8_unnamed_parameters/abis/abi8.json b/bivrost-abi-parser/src/test/resources/automatic_tests/08_unnamed_parameters/abis/abi8.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/8_unnamed_parameters/abis/abi8.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/08_unnamed_parameters/abis/abi8.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/8_unnamed_parameters/expected/Abi8.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/08_unnamed_parameters/expected/Abi8.kt similarity index 82% rename from bivrost-abi-parser/src/test/resources/automatic_tests/8_unnamed_parameters/expected/Abi8.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/08_unnamed_parameters/expected/Abi8.kt index e6f1c45..96588bd 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/8_unnamed_parameters/expected/Abi8.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/08_unnamed_parameters/expected/Abi8.kt @@ -1,5 +1,6 @@ package expected +import java.math.BigInteger import kotlin.String import pm.gnosis.model.Solidity import pm.gnosis.model.SolidityBase @@ -14,8 +15,8 @@ class Abi8 { val source = SolidityBase.PartitionData.of(data) // Add decoders - source.consume() - val arg0 = Solidity.Bytes.DECODER.decode(source) + val arg0Offset = BigInteger(source.consume(), 16).intValueExact() + val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) return Return(arg0) } diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/abis/abi9.json b/bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/abis/abi9.json similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/abis/abi9.json rename to bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/abis/abi9.json diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/expected/Abi9.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/Abi9.kt similarity index 85% rename from bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/expected/Abi9.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/Abi9.kt index 03ea677..870da20 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/expected/Abi9.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/Abi9.kt @@ -2,6 +2,7 @@ package expected import expected.arrays.Array5 import expected.arrays.Array7 +import java.math.BigInteger import kotlin.Boolean import kotlin.String import pm.gnosis.model.Solidity @@ -18,8 +19,8 @@ class Abi9 { // Add decoders val arg0 = TupleB.DECODER.decode(source) - source.consume() - val arg1 = SolidityBase.Vector.Decoder(TupleB.DECODER).decode(source) + val arg1Offset = BigInteger(source.consume(), 16).intValueExact() + val arg1 = SolidityBase.Vector.Decoder(TupleB.DECODER).decode(source.subData(arg1Offset)) return Return(arg0, arg1) } @@ -28,10 +29,10 @@ class Abi9 { val source = SolidityBase.PartitionData.of(data) // Add decoders - source.consume() - source.consume() - val arg0 = SolidityBase.Vector.Decoder(TupleA.DECODER).decode(source) - val arg1 = SolidityBase.Vector.Decoder(SolidityBase.Vector.Decoder(Array7.Decoder(Array5.Decoder(Solidity.UInt256.DECODER)))).decode(source) + val arg0Offset = BigInteger(source.consume(), 16).intValueExact() + val arg0 = SolidityBase.Vector.Decoder(TupleA.DECODER).decode(source.subData(arg0Offset)) + val arg1Offset = BigInteger(source.consume(), 16).intValueExact() + val arg1 = SolidityBase.Vector.Decoder(SolidityBase.Vector.Decoder(Array7.Decoder(Array5.Decoder(Solidity.UInt256.DECODER)))).decode(source.subData(arg1Offset)) return Arguments(arg0, arg1) } @@ -53,8 +54,8 @@ class Abi9 { override fun decode(source: SolidityBase.PartitionData): TupleA { val arg0 = Solidity.UInt256.DECODER.decode(source) val arg1 = Solidity.UInt256.DECODER.decode(source) - source.consume() - val arg2 = SolidityBase.Vector.Decoder(SolidityBase.Vector.Decoder(Array7.Decoder(Array5.Decoder(Solidity.UInt256.DECODER)))).decode(source) + val arg2Offset = BigInteger(source.consume(), 16).intValueExact() + val arg2 = SolidityBase.Vector.Decoder(SolidityBase.Vector.Decoder(Array7.Decoder(Array5.Decoder(Solidity.UInt256.DECODER)))).decode(source.subData(arg2Offset)) return TupleA(arg0, arg1, arg2) } } diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/expected/arrays/Array5.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/arrays/Array5.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/expected/arrays/Array5.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/arrays/Array5.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/expected/arrays/Array7.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/arrays/Array7.kt similarity index 100% rename from bivrost-abi-parser/src/test/resources/automatic_tests/9_complex_arrays/expected/arrays/Array7.kt rename to bivrost-abi-parser/src/test/resources/automatic_tests/09_complex_arrays/expected/arrays/Array7.kt diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/11_event_static_indexed/expected/Abi11.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/11_event_static_indexed/expected/Abi11.kt index b81a133..333efa4 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/11_event_static_indexed/expected/Abi11.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/11_event_static_indexed/expected/Abi11.kt @@ -13,9 +13,9 @@ class Abi11 { fun decode(topics: List): Arguments { // Decode topics - val topicsSource = SolidityBase.PartitionData(topics) - if (topicsSource.consume() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") - val t1 = Solidity.UInt256.DECODER.decode(topicsSource) + if (topics.first() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") + val source1 = SolidityBase.PartitionData.of(topics[1]) + val t1 = Solidity.UInt256.DECODER.decode(source1) return Arguments(t1) } diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/12_event_static_not_indexed/expected/Abi12.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/12_event_static_not_indexed/expected/Abi12.kt index de19a65..94602c1 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/12_event_static_not_indexed/expected/Abi12.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/12_event_static_not_indexed/expected/Abi12.kt @@ -13,8 +13,7 @@ class Abi12 { fun decode(topics: List, data: String): Arguments { // Decode topics - val topicsSource = SolidityBase.PartitionData(topics) - if (topicsSource.consume() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") + if (topics.first() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") // Decode data val source = SolidityBase.PartitionData.of(data) diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/13_simple_event_dynamic_indexed/expected/Abi13.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/13_simple_event_dynamic_indexed/expected/Abi13.kt index 8379dbb..0fe7ce7 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/13_simple_event_dynamic_indexed/expected/Abi13.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/13_simple_event_dynamic_indexed/expected/Abi13.kt @@ -14,11 +14,10 @@ class Abi13 { fun decode(topics: List): Arguments { // Decode topics - val topicsSource = SolidityBase.PartitionData(topics) - if (topicsSource.consume() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") - val t1 = topicsSource.consume() - val t2 = topicsSource.consume() - val t3 = topicsSource.consume() + if (topics.first() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") + val t1 = topics[1] + val t2 = topics[2] + val t3 = topics[3] return Arguments(t1, t2, t3) } diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/14_simple_event_dynamic_not_indexed/expected/Abi14.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/14_simple_event_dynamic_not_indexed/expected/Abi14.kt index 0e306a5..4ac9401 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/14_simple_event_dynamic_not_indexed/expected/Abi14.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/14_simple_event_dynamic_not_indexed/expected/Abi14.kt @@ -1,6 +1,7 @@ package expected import java.lang.IllegalArgumentException +import java.math.BigInteger import kotlin.Boolean import kotlin.String import kotlin.collections.List @@ -14,16 +15,15 @@ class Abi14 { fun decode(topics: List, data: String): Arguments { // Decode topics - val topicsSource = SolidityBase.PartitionData(topics) - if (topicsSource.consume() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") + if (topics.first() != EVENT_ID) throw IllegalArgumentException("topics[0] does not match event id") // Decode data val source = SolidityBase.PartitionData.of(data) - source.consume() - source.consume() + val arg0Offset = BigInteger(source.consume(), 16).intValueExact() + val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) + val arg1Offset = BigInteger(source.consume(), 16).intValueExact() + val arg1 = Solidity.String.DECODER.decode(source.subData(arg1Offset)) val arg2 = TupleA.DECODER.decode(source) - val arg0 = Solidity.Bytes.DECODER.decode(source) - val arg1 = Solidity.String.DECODER.decode(source) return Arguments(arg0, arg1, arg2) } diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/15_anonymous_event/expected/Abi15.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/15_anonymous_event/expected/Abi15.kt index 81fbed9..1e8490f 100644 --- a/bivrost-abi-parser/src/test/resources/automatic_tests/15_anonymous_event/expected/Abi15.kt +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/15_anonymous_event/expected/Abi15.kt @@ -12,8 +12,8 @@ class Abi15 { fun decode(topics: List): Arguments { // Decode topics - val topicsSource = SolidityBase.PartitionData(topics) - val t1 = Solidity.UInt256.DECODER.decode(topicsSource) + val source1 = SolidityBase.PartitionData.of(topics[1]) + val t1 = Solidity.UInt256.DECODER.decode(source1) return Arguments(t1) } diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/abis/abi16.json b/bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/abis/abi16.json new file mode 100644 index 0000000..96d4d7d --- /dev/null +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/abis/abi16.json @@ -0,0 +1,28 @@ +{ + "contractName": "Abi16", + "abi": [ + { + "constant": true, + "inputs": [ + { + "name": "c", + "type": "tuple", + "components": [ + { + "name": "bytesVar", + "type": "bytes" + }, + { + "name": "stringVar", + "type": "string" + } + ] + } + ], + "name": "malformed", + "outputs": [], + "payable": false, + "type": "function" + } + ] +} diff --git a/bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/expected/Abi16.kt b/bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/expected/Abi16.kt new file mode 100644 index 0000000..131a1e6 --- /dev/null +++ b/bivrost-abi-parser/src/test/resources/automatic_tests/16_malformed_bytes_encoding/expected/Abi16.kt @@ -0,0 +1,45 @@ +package expected + +import java.math.BigInteger +import kotlin.Boolean +import kotlin.String +import pm.gnosis.model.Solidity +import pm.gnosis.model.SolidityBase + +class Abi16 { + object Malformed { + const val METHOD_ID: String = "a76411a9" + + fun encode(c: TupleA): String = "0x" + METHOD_ID + pm.gnosis.model.SolidityBase.encodeFunctionArguments(c) + + fun decodeArguments(data: String): Arguments { + val source = SolidityBase.PartitionData.of(data) + + // Add decoders + val arg0Offset = BigInteger(source.consume(), 16).intValueExact() + val arg0 = TupleA.DECODER.decode(source.subData(arg0Offset)) + + return Arguments(arg0) + } + + data class Arguments(val c: TupleA) + } + + data class TupleA(val bytesvar: Solidity.Bytes, val stringvar: Solidity.String) : SolidityBase.DynamicType { + override fun encode(): String = SolidityBase.encodeFunctionArguments(bytesvar, stringvar) + + class Decoder : SolidityBase.TypeDecoder { + override fun isDynamic(): Boolean = true + override fun decode(source: SolidityBase.PartitionData): TupleA { + val arg0Offset = BigInteger(source.consume(), 16).intValueExact() + val arg0 = Solidity.Bytes.DECODER.decode(source.subData(arg0Offset)) + val arg1Offset = BigInteger(source.consume(), 16).intValueExact() + val arg1 = Solidity.String.DECODER.decode(source.subData(arg1Offset)) + return TupleA(arg0, arg1) + } + } + companion object { + val DECODER: Decoder = Decoder() + } + } +} diff --git a/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/Solidity.kt b/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/Solidity.kt index 6ea1872..a589910 100644 --- a/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/Solidity.kt +++ b/bivrost-solidity-types/src/main/kotlin/pm/gnosis/model/Solidity.kt @@ -7,6 +7,7 @@ import kotlin.ByteArray import kotlin.collections.Map import pm.gnosis.utils.padEndMultiple import pm.gnosis.utils.toHex +import java.util.* /** * Generated code. Do not modify @@ -122,679 +123,679 @@ object Solidity { data class UInt8(val value: BigInteger) : SolidityBase.UIntBase(value, 8) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt8(it) }) + SolidityBase.UIntBase.Decoder { UInt8(it) } } } data class UInt16(val value: BigInteger) : SolidityBase.UIntBase(value, 16) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt16(it) }) + SolidityBase.UIntBase.Decoder { UInt16(it) } } } data class UInt24(val value: BigInteger) : SolidityBase.UIntBase(value, 24) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt24(it) }) + SolidityBase.UIntBase.Decoder { UInt24(it) } } } data class UInt32(val value: BigInteger) : SolidityBase.UIntBase(value, 32) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt32(it) }) + SolidityBase.UIntBase.Decoder { UInt32(it) } } } data class UInt40(val value: BigInteger) : SolidityBase.UIntBase(value, 40) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt40(it) }) + SolidityBase.UIntBase.Decoder { UInt40(it) } } } data class UInt48(val value: BigInteger) : SolidityBase.UIntBase(value, 48) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt48(it) }) + SolidityBase.UIntBase.Decoder { UInt48(it) } } } data class UInt56(val value: BigInteger) : SolidityBase.UIntBase(value, 56) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt56(it) }) + SolidityBase.UIntBase.Decoder { UInt56(it) } } } data class UInt64(val value: BigInteger) : SolidityBase.UIntBase(value, 64) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt64(it) }) + SolidityBase.UIntBase.Decoder { UInt64(it) } } } data class UInt72(val value: BigInteger) : SolidityBase.UIntBase(value, 72) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt72(it) }) + SolidityBase.UIntBase.Decoder { UInt72(it) } } } data class UInt80(val value: BigInteger) : SolidityBase.UIntBase(value, 80) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt80(it) }) + SolidityBase.UIntBase.Decoder { UInt80(it) } } } data class UInt88(val value: BigInteger) : SolidityBase.UIntBase(value, 88) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt88(it) }) + SolidityBase.UIntBase.Decoder { UInt88(it) } } } data class UInt96(val value: BigInteger) : SolidityBase.UIntBase(value, 96) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt96(it) }) + SolidityBase.UIntBase.Decoder { UInt96(it) } } } data class UInt104(val value: BigInteger) : SolidityBase.UIntBase(value, 104) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt104(it) }) + SolidityBase.UIntBase.Decoder { UInt104(it) } } } data class UInt112(val value: BigInteger) : SolidityBase.UIntBase(value, 112) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt112(it) }) + SolidityBase.UIntBase.Decoder { UInt112(it) } } } data class UInt120(val value: BigInteger) : SolidityBase.UIntBase(value, 120) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt120(it) }) + SolidityBase.UIntBase.Decoder { UInt120(it) } } } data class UInt128(val value: BigInteger) : SolidityBase.UIntBase(value, 128) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt128(it) }) + SolidityBase.UIntBase.Decoder { UInt128(it) } } } data class UInt136(val value: BigInteger) : SolidityBase.UIntBase(value, 136) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt136(it) }) + SolidityBase.UIntBase.Decoder { UInt136(it) } } } data class UInt144(val value: BigInteger) : SolidityBase.UIntBase(value, 144) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt144(it) }) + SolidityBase.UIntBase.Decoder { UInt144(it) } } } data class UInt152(val value: BigInteger) : SolidityBase.UIntBase(value, 152) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt152(it) }) + SolidityBase.UIntBase.Decoder { UInt152(it) } } } data class UInt160(val value: BigInteger) : SolidityBase.UIntBase(value, 160) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt160(it) }) + SolidityBase.UIntBase.Decoder { UInt160(it) } } } data class UInt168(val value: BigInteger) : SolidityBase.UIntBase(value, 168) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt168(it) }) + SolidityBase.UIntBase.Decoder { UInt168(it) } } } data class UInt176(val value: BigInteger) : SolidityBase.UIntBase(value, 176) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt176(it) }) + SolidityBase.UIntBase.Decoder { UInt176(it) } } } data class UInt184(val value: BigInteger) : SolidityBase.UIntBase(value, 184) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt184(it) }) + SolidityBase.UIntBase.Decoder { UInt184(it) } } } data class UInt192(val value: BigInteger) : SolidityBase.UIntBase(value, 192) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt192(it) }) + SolidityBase.UIntBase.Decoder { UInt192(it) } } } data class UInt200(val value: BigInteger) : SolidityBase.UIntBase(value, 200) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt200(it) }) + SolidityBase.UIntBase.Decoder { UInt200(it) } } } data class UInt208(val value: BigInteger) : SolidityBase.UIntBase(value, 208) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt208(it) }) + SolidityBase.UIntBase.Decoder { UInt208(it) } } } data class UInt216(val value: BigInteger) : SolidityBase.UIntBase(value, 216) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt216(it) }) + SolidityBase.UIntBase.Decoder { UInt216(it) } } } data class UInt224(val value: BigInteger) : SolidityBase.UIntBase(value, 224) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt224(it) }) + SolidityBase.UIntBase.Decoder { UInt224(it) } } } data class UInt232(val value: BigInteger) : SolidityBase.UIntBase(value, 232) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt232(it) }) + SolidityBase.UIntBase.Decoder { UInt232(it) } } } data class UInt240(val value: BigInteger) : SolidityBase.UIntBase(value, 240) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt240(it) }) + SolidityBase.UIntBase.Decoder { UInt240(it) } } } data class UInt248(val value: BigInteger) : SolidityBase.UIntBase(value, 248) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt248(it) }) + SolidityBase.UIntBase.Decoder { UInt248(it) } } } data class UInt256(val value: BigInteger) : SolidityBase.UIntBase(value, 256) { companion object { val DECODER: SolidityBase.UIntBase.Decoder = - SolidityBase.UIntBase.Decoder({ UInt256(it) }) + SolidityBase.UIntBase.Decoder { UInt256(it) } } } data class Int8(val value: BigInteger) : SolidityBase.IntBase(value, 8) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int8(it) }) + SolidityBase.IntBase.Decoder { Int8(it) } } } data class Int16(val value: BigInteger) : SolidityBase.IntBase(value, 16) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int16(it) }) + SolidityBase.IntBase.Decoder { Int16(it) } } } data class Int24(val value: BigInteger) : SolidityBase.IntBase(value, 24) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int24(it) }) + SolidityBase.IntBase.Decoder { Int24(it) } } } data class Int32(val value: BigInteger) : SolidityBase.IntBase(value, 32) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int32(it) }) + SolidityBase.IntBase.Decoder { Int32(it) } } } data class Int40(val value: BigInteger) : SolidityBase.IntBase(value, 40) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int40(it) }) + SolidityBase.IntBase.Decoder { Int40(it) } } } data class Int48(val value: BigInteger) : SolidityBase.IntBase(value, 48) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int48(it) }) + SolidityBase.IntBase.Decoder { Int48(it) } } } data class Int56(val value: BigInteger) : SolidityBase.IntBase(value, 56) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int56(it) }) + SolidityBase.IntBase.Decoder { Int56(it) } } } data class Int64(val value: BigInteger) : SolidityBase.IntBase(value, 64) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int64(it) }) + SolidityBase.IntBase.Decoder { Int64(it) } } } data class Int72(val value: BigInteger) : SolidityBase.IntBase(value, 72) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int72(it) }) + SolidityBase.IntBase.Decoder { Int72(it) } } } data class Int80(val value: BigInteger) : SolidityBase.IntBase(value, 80) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int80(it) }) + SolidityBase.IntBase.Decoder { Int80(it) } } } data class Int88(val value: BigInteger) : SolidityBase.IntBase(value, 88) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int88(it) }) + SolidityBase.IntBase.Decoder { Int88(it) } } } data class Int96(val value: BigInteger) : SolidityBase.IntBase(value, 96) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int96(it) }) + SolidityBase.IntBase.Decoder { Int96(it) } } } data class Int104(val value: BigInteger) : SolidityBase.IntBase(value, 104) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int104(it) }) + SolidityBase.IntBase.Decoder { Int104(it) } } } data class Int112(val value: BigInteger) : SolidityBase.IntBase(value, 112) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int112(it) }) + SolidityBase.IntBase.Decoder { Int112(it) } } } data class Int120(val value: BigInteger) : SolidityBase.IntBase(value, 120) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int120(it) }) + SolidityBase.IntBase.Decoder { Int120(it) } } } data class Int128(val value: BigInteger) : SolidityBase.IntBase(value, 128) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int128(it) }) + SolidityBase.IntBase.Decoder { Int128(it) } } } data class Int136(val value: BigInteger) : SolidityBase.IntBase(value, 136) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int136(it) }) + SolidityBase.IntBase.Decoder { Int136(it) } } } data class Int144(val value: BigInteger) : SolidityBase.IntBase(value, 144) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int144(it) }) + SolidityBase.IntBase.Decoder { Int144(it) } } } data class Int152(val value: BigInteger) : SolidityBase.IntBase(value, 152) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int152(it) }) + SolidityBase.IntBase.Decoder { Int152(it) } } } data class Int160(val value: BigInteger) : SolidityBase.IntBase(value, 160) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int160(it) }) + SolidityBase.IntBase.Decoder { Int160(it) } } } data class Int168(val value: BigInteger) : SolidityBase.IntBase(value, 168) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int168(it) }) + SolidityBase.IntBase.Decoder { Int168(it) } } } data class Int176(val value: BigInteger) : SolidityBase.IntBase(value, 176) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int176(it) }) + SolidityBase.IntBase.Decoder { Int176(it) } } } data class Int184(val value: BigInteger) : SolidityBase.IntBase(value, 184) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int184(it) }) + SolidityBase.IntBase.Decoder { Int184(it) } } } data class Int192(val value: BigInteger) : SolidityBase.IntBase(value, 192) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int192(it) }) + SolidityBase.IntBase.Decoder { Int192(it) } } } data class Int200(val value: BigInteger) : SolidityBase.IntBase(value, 200) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int200(it) }) + SolidityBase.IntBase.Decoder { Int200(it) } } } data class Int208(val value: BigInteger) : SolidityBase.IntBase(value, 208) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int208(it) }) + SolidityBase.IntBase.Decoder { Int208(it) } } } data class Int216(val value: BigInteger) : SolidityBase.IntBase(value, 216) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int216(it) }) + SolidityBase.IntBase.Decoder { Int216(it) } } } data class Int224(val value: BigInteger) : SolidityBase.IntBase(value, 224) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int224(it) }) + SolidityBase.IntBase.Decoder { Int224(it) } } } data class Int232(val value: BigInteger) : SolidityBase.IntBase(value, 232) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int232(it) }) + SolidityBase.IntBase.Decoder { Int232(it) } } } data class Int240(val value: BigInteger) : SolidityBase.IntBase(value, 240) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int240(it) }) + SolidityBase.IntBase.Decoder { Int240(it) } } } data class Int248(val value: BigInteger) : SolidityBase.IntBase(value, 248) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int248(it) }) + SolidityBase.IntBase.Decoder { Int248(it) } } } data class Int256(val value: BigInteger) : SolidityBase.IntBase(value, 256) { companion object { val DECODER: SolidityBase.IntBase.Decoder = - SolidityBase.IntBase.Decoder({ Int256(it) }) + SolidityBase.IntBase.Decoder { Int256(it) } } } class Bytes1(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 1) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes1(it) }, 1) + SolidityBase.StaticBytes.Decoder({ Bytes1(it) }, 1) } } class Bytes2(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 2) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes2(it) }, 2) + SolidityBase.StaticBytes.Decoder({ Bytes2(it) }, 2) } } class Bytes3(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 3) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes3(it) }, 3) + SolidityBase.StaticBytes.Decoder({ Bytes3(it) }, 3) } } class Bytes4(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 4) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes4(it) }, 4) + SolidityBase.StaticBytes.Decoder({ Bytes4(it) }, 4) } } class Bytes5(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 5) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes5(it) }, 5) + SolidityBase.StaticBytes.Decoder({ Bytes5(it) }, 5) } } class Bytes6(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 6) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes6(it) }, 6) + SolidityBase.StaticBytes.Decoder({ Bytes6(it) }, 6) } } class Bytes7(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 7) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes7(it) }, 7) + SolidityBase.StaticBytes.Decoder({ Bytes7(it) }, 7) } } class Bytes8(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 8) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes8(it) }, 8) + SolidityBase.StaticBytes.Decoder({ Bytes8(it) }, 8) } } class Bytes9(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 9) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes9(it) }, 9) + SolidityBase.StaticBytes.Decoder({ Bytes9(it) }, 9) } } class Bytes10(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 10) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes10(it) }, 10) + SolidityBase.StaticBytes.Decoder({ Bytes10(it) }, 10) } } class Bytes11(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 11) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes11(it) }, 11) + SolidityBase.StaticBytes.Decoder({ Bytes11(it) }, 11) } } class Bytes12(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 12) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes12(it) }, 12) + SolidityBase.StaticBytes.Decoder({ Bytes12(it) }, 12) } } class Bytes13(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 13) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes13(it) }, 13) + SolidityBase.StaticBytes.Decoder({ Bytes13(it) }, 13) } } class Bytes14(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 14) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes14(it) }, 14) + SolidityBase.StaticBytes.Decoder({ Bytes14(it) }, 14) } } class Bytes15(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 15) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes15(it) }, 15) + SolidityBase.StaticBytes.Decoder({ Bytes15(it) }, 15) } } class Bytes16(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 16) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes16(it) }, 16) + SolidityBase.StaticBytes.Decoder({ Bytes16(it) }, 16) } } class Bytes17(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 17) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes17(it) }, 17) + SolidityBase.StaticBytes.Decoder({ Bytes17(it) }, 17) } } class Bytes18(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 18) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes18(it) }, 18) + SolidityBase.StaticBytes.Decoder({ Bytes18(it) }, 18) } } class Bytes19(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 19) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes19(it) }, 19) + SolidityBase.StaticBytes.Decoder({ Bytes19(it) }, 19) } } class Bytes20(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 20) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes20(it) }, 20) + SolidityBase.StaticBytes.Decoder({ Bytes20(it) }, 20) } } class Bytes21(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 21) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes21(it) }, 21) + SolidityBase.StaticBytes.Decoder({ Bytes21(it) }, 21) } } class Bytes22(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 22) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes22(it) }, 22) + SolidityBase.StaticBytes.Decoder({ Bytes22(it) }, 22) } } class Bytes23(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 23) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes23(it) }, 23) + SolidityBase.StaticBytes.Decoder({ Bytes23(it) }, 23) } } class Bytes24(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 24) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes24(it) }, 24) + SolidityBase.StaticBytes.Decoder({ Bytes24(it) }, 24) } } class Bytes25(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 25) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes25(it) }, 25) + SolidityBase.StaticBytes.Decoder({ Bytes25(it) }, 25) } } class Bytes26(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 26) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes26(it) }, 26) + SolidityBase.StaticBytes.Decoder({ Bytes26(it) }, 26) } } class Bytes27(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 27) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes27(it) }, 27) + SolidityBase.StaticBytes.Decoder({ Bytes27(it) }, 27) } } class Bytes28(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 28) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes28(it) }, 28) + SolidityBase.StaticBytes.Decoder({ Bytes28(it) }, 28) } } class Bytes29(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 29) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes29(it) }, 29) + SolidityBase.StaticBytes.Decoder({ Bytes29(it) }, 29) } } class Bytes30(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 30) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes30(it) }, 30) + SolidityBase.StaticBytes.Decoder({ Bytes30(it) }, 30) } } class Bytes31(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 31) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes31(it) }, 31) + SolidityBase.StaticBytes.Decoder({ Bytes31(it) }, 31) } } class Bytes32(val bytes: ByteArray) : SolidityBase.StaticBytes(bytes, 32) { companion object { val DECODER: SolidityBase.StaticBytes.Decoder = - SolidityBase.StaticBytes.Decoder({ Bytes32(it) }, 32) + SolidityBase.StaticBytes.Decoder({ Bytes32(it) }, 32) } } data class Address(val value: BigInteger) : SolidityBase.UIntBase(value, 160) { companion object { val DECODER: SolidityBase.UIntBase.Decoder
= - SolidityBase.UIntBase.Decoder
({ Address(it) }) + SolidityBase.UIntBase.Decoder { Address(it) } } } @@ -824,6 +825,14 @@ object Solidity { return SolidityBase.DynamicType.Parts(length, contents) } + override fun equals(other: Any?): Boolean { + return (other as? Bytes)?.run { items.contentEquals(other.items) } ?: super.equals(other) + } + + override fun hashCode(): Int { + return Arrays.hashCode(items) + } + class Decoder : SolidityBase.TypeDecoder { override fun isDynamic(): Boolean = true override fun decode(source: SolidityBase.PartitionData): Bytes = Bytes(SolidityBase.decodeBytes(source)) 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 9942075..a3b1f9b 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 @@ -146,9 +146,7 @@ object SolidityBase { other as StaticBytes - if (byteArray != other.byteArray) return false - - return true + return byteArray.contentEquals(other.byteArray) } override fun hashCode(): Int { @@ -166,20 +164,27 @@ object SolidityBase { } } - class PartitionData(private val partitions: List) { - var index: Int = 0 + class PartitionData(data: String, private val offset: Int = 0) { + private val cleanData = data.removePrefix("0x") + private var index: Int = 0 fun consume(): String { - return partitions[index++].removePrefix("0x") + return cleanData.substring(offset + index * 64, offset + (index + 1) * 64).apply { + index++ + } } fun reset() { index = 0 } + fun subData() = PartitionData(cleanData, offset + index * 64) + + fun subData(bytesOffset: Int) = PartitionData(cleanData, offset + bytesOffset * 2) + companion object { fun of(data: String): PartitionData { - return PartitionData(partitionData(data)) + return PartitionData(data) } } } @@ -192,24 +197,16 @@ object SolidityBase { } fun decodeList(source: PartitionData, capacity: Int, itemDecoder: TypeDecoder): List { - val decodeParams = ArrayList() - val dynamicDecoders = LinkedList>() - - for (i in 0 until capacity) { + return (0 until capacity).map { if (itemDecoder.isDynamic()) { - // We cannot decode it right away, remember the decoder for later - decodeParams.add(null) - dynamicDecoders.push(itemDecoder) - // consume location of dynamic value - source.consume() + // Get offset + val offset = BigInteger(source.consume(), 16).intValueExact() + // Decode dynamic data at offset + itemDecoder.decode(source.subData(offset)) } else { - decodeParams += itemDecoder.decode(source) + itemDecoder.decode(source) } } - - return decodeParams.map { - it ?: dynamicDecoders.removeFirst().decode(source) - } } abstract class Array(items: List, val capacity: Int) : Collection(checkCapacity(items, capacity)) { @@ -225,12 +222,7 @@ object SolidityBase { if (capacity == 0) { return false } - items.forEach { - if (isDynamic(it)) { - return true - } - } - return false + return items.any { isDynamic(it) } } companion object { @@ -268,7 +260,7 @@ object SolidityBase { override fun decode(source: PartitionData): Vector { val capacity = decodeUInt(source.consume()).toInt() - return Vector(decodeList(source, capacity, itemDecoder)) + return Vector(decodeList(source.subData(), capacity, itemDecoder)) } } } @@ -317,19 +309,6 @@ object SolidityBase { return (type as? Collection<*>)?.isDynamic() ?: false || (type is Vector<*>) } - @Suppress("MemberVisibilityCanPrivate") - fun partitionData(data: String): List { - var noPrefix = data.removePrefix("0x") - if (noPrefix.length.rem(PADDED_HEX_LENGTH) != 0) throw IllegalArgumentException("Data is not a multiple of $PADDED_HEX_LENGTH") - val properties = arrayListOf() - - while (noPrefix.length >= PADDED_HEX_LENGTH) { - properties.add(noPrefix.subSequence(0, PADDED_HEX_LENGTH).toString()) - noPrefix = noPrefix.removeRange(0 until PADDED_HEX_LENGTH) - } - return properties - } - fun decodeUInt(data: String): BigInteger { return BigInteger(data, 16) } @@ -376,11 +355,11 @@ object SolidityBase { fun decodeString(source: PartitionData) = decodeBytes(source).toString(Charset.forName("UTF-8")) + @Deprecated("Deprecated for decodeList") fun decodeArray(data: String, itemDecoder: (String) -> T): List { - val params = partitionData(data) - val contentSize = BigDecimal(BigInteger(params[0])).intValueExact() - if (contentSize != params.size - 1) throw IllegalArgumentException("Number of items is different from the actual array size") + val params = PartitionData.of(data) + val contentSize = BigInteger(params.consume()).intValueExact() if (contentSize == 0) return emptyList() - return (1 until params.size).map { itemDecoder.invoke(params[it]) }.toList() + return (0 until contentSize).map { itemDecoder(params.consume()) } } } diff --git a/bivrost-solidity-types/src/test/kotlin/pm/gnosis/model/SolidityBaseTest.kt b/bivrost-solidity-types/src/test/kotlin/pm/gnosis/model/SolidityBaseTest.kt index 1627635..a8290e6 100644 --- a/bivrost-solidity-types/src/test/kotlin/pm/gnosis/model/SolidityBaseTest.kt +++ b/bivrost-solidity-types/src/test/kotlin/pm/gnosis/model/SolidityBaseTest.kt @@ -52,6 +52,24 @@ class SolidityBaseTest { SolidityBase.decodeUInt("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) } + @Test + fun testUIntDecodingWithPartitionData() { + assertEquals( + Solidity.UInt32(BigInteger.ZERO), + Solidity.UInt32.DECODER.decode(SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000000")) + ) + + assertEquals( + Solidity.UInt8(BigInteger.ONE), + Solidity.UInt8.DECODER.decode(SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000001")) + ) + + assertEquals( + Solidity.UInt256(BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639935")), + Solidity.UInt256.DECODER.decode(SolidityBase.PartitionData.of("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + ) + } + @Test fun testBoolEncoding() { assertEquals("0000000000000000000000000000000000000000000000000000000000000000", @@ -70,6 +88,19 @@ class SolidityBaseTest { SolidityBase.decodeBool("0000000000000000000000000000000000000000000000000000000000000001")) } + @Test + fun testBoolDecodingWithPartitionData() { + assertEquals( + Solidity.Bool(false), + Solidity.Bool.DECODER.decode(SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000000")) + ) + + assertEquals( + Solidity.Bool(true), + Solidity.Bool.DECODER.decode(SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000001")) + ) + } + @Test fun testIntEncoding() { assertEquals("0000000000000000000000000000000000000000000000000000000000000000", @@ -130,6 +161,34 @@ class SolidityBaseTest { SolidityBase.decodeInt("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80")) } + @Test + fun testIntDecodingWithPartitionData() { + assertEquals( + Solidity.Int32(BigInteger.ZERO), + Solidity.Int32.DECODER.decode(SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000000")) + ) + + assertEquals( + Solidity.Int8(BigInteger.ONE), + Solidity.Int8.DECODER.decode(SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000001")) + ) + + assertEquals( + Solidity.Int256(BigInteger.valueOf(-1)), + Solidity.Int256.DECODER.decode(SolidityBase.PartitionData.of("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) + ) + + assertEquals( + Solidity.Int256(BigInteger.valueOf(127)), + Solidity.Int256.DECODER.decode(SolidityBase.PartitionData.of("000000000000000000000000000000000000000000000000000000000000007f")) + ) + + assertEquals( + Solidity.Int256(BigInteger.valueOf(-128)), + Solidity.Int256.DECODER.decode(SolidityBase.PartitionData.of("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80")) + ) + } + @Test fun testStaticBytesEncoding() { assertEquals("0000000000000000000000000000000000000000000000000000000000000000", @@ -154,17 +213,39 @@ class SolidityBaseTest { assertArrayEquals(byteArrayOf(0, 1, 2), SolidityBase.decodeStaticBytes("0001020000000000000000000000000000000000000000000000000000000000", 3)) assertArrayEquals("dave".toByteArray(), SolidityBase.decodeStaticBytes("6461766500000000000000000000000000000000000000000000000000000000", 4)) + } + @Test + fun testStaticBytesDecodingWithPartitionData() { + assertEquals( + Solidity.Bytes1(byteArrayOf(0)), + Solidity.Bytes1.DECODER.decode(SolidityBase.PartitionData.of("0000000000000000000000000000000000000000000000000000000000000000")) + ) + + assertEquals( + Solidity.Bytes2(byteArrayOf(0, 1)), + Solidity.Bytes2.DECODER.decode(SolidityBase.PartitionData.of("0001000000000000000000000000000000000000000000000000000000000000")) + ) + + assertEquals( + Solidity.Bytes3(byteArrayOf(0, 1, 2)), + Solidity.Bytes3.DECODER.decode(SolidityBase.PartitionData.of("0001020000000000000000000000000000000000000000000000000000000000")) + ) + + assertEquals( + Solidity.Bytes4("dave".toByteArray()), + Solidity.Bytes4.DECODER.decode(SolidityBase.PartitionData.of("6461766500000000000000000000000000000000000000000000000000000000")) + ) } @Test fun testStaticBytesRange() { - (1..32).forEach { - val bytes = ByteArray(it, { it.toByte() }) - val constructor = Class.forName(formatClassName(Solidity.types["bytes$it"]!!)).constructors[0] + (1..32).forEach { bytesSize -> + val bytes = ByteArray(bytesSize) { it.toByte() } + val constructor = Class.forName(formatClassName(Solidity.types["bytes$bytesSize"]!!)).constructors[0] constructor.newInstance(bytes) try { - val oversizedBytes = ByteArray(it + 1, { it.toByte() }) + val oversizedBytes = ByteArray(bytesSize + 1) { it.toByte() } constructor.newInstance(oversizedBytes) fail("Expected IllegalArgumentException") } catch (e: InvocationTargetException) { @@ -233,12 +314,12 @@ class SolidityBaseTest { @Test fun testMultiTypeDecoding() { - val testData = "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000321000000000000000000000000000000000000000000000000000000000000076500000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789" + val testData = "00000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000003210000000000000000000000000000000000000000000000000000000000000765" val source = SolidityBase.PartitionData.of(testData) + assertEquals(listOf(Solidity.UInt256(BigInteger("456", 16)), Solidity.UInt256(BigInteger("789", 16))), + TestArray.Decoder(Solidity.UInt256.DECODER, 2).decode(source).items) assertEquals(listOf(Solidity.UInt256(BigInteger("321", 16)), Solidity.UInt256(BigInteger("765", 16))), SolidityBase.Vector.Decoder(Solidity.UInt256.DECODER).decode(source).items) - assertEquals(listOf(Solidity.UInt256(BigInteger("456", 16)), Solidity.UInt256(BigInteger("789", 16))), - TestArray.Decoder(Solidity.UInt256.DECODER, 2).decode(source).items) } @Test @@ -308,6 +389,56 @@ class SolidityBaseTest { } } + @Test + fun testDecodeEncodeDynamicTuple() { + val uint32s = listOf( + Solidity.UInt32(BigInteger("456", 16)), + Solidity.UInt32(BigInteger("789", 16)) + ) + val items = listOf( + Solidity.UInt256(BigInteger("123", 16)), + SolidityBase.Vector(uint32s), + Solidity.Bytes10("1234567890".toByteArray()), + Solidity.Bytes("Hello, world!".toByteArray()) + ) + val encoded = SolidityBase.encodeTuple(items) + assertEquals("Encoded string not correct!", ENCODED_SOLIDITY_EXAMPLE_TUPLE, encoded) + + val source = SolidityBase.PartitionData.of(encoded) + assertEquals(Solidity.UInt256.DECODER.decode(source), items[0]) + val offsetUint32s = BigInteger(source.consume(), 16).intValueExact() + assertEquals(SolidityBase.Vector.Decoder(Solidity.UInt32.DECODER).decode(source.subData(offsetUint32s)), items[1]) + assertEquals(Solidity.Bytes10.DECODER.decode(source), items[2]) + val offsetBytes = BigInteger(source.consume(), 16).intValueExact() + assertEquals(Solidity.Bytes.DECODER.decode(source.subData(offsetBytes)), items[3]) + } + + @Test + fun testDecodeEncodeDynamicTupleNested() { + val uints1 = SolidityBase.Vector(listOf( + Solidity.UInt256(BigInteger("1", 16)), + Solidity.UInt256(BigInteger("2", 16)) + )) + val uints2 = SolidityBase.Vector(listOf( + Solidity.UInt256(BigInteger("3", 16)) + )) + val uintsArrays = SolidityBase.Vector(listOf(uints1, uints2)) + val strings = SolidityBase.Vector(listOf("one", "two", "three").map { Solidity.String(it) }) + val items = listOf(uintsArrays, strings) + val encoded = SolidityBase.encodeTuple(items) + assertEquals("Encoded string not correct!", ENCODED_SOLIDITY_EXAMPLE_TUPLE_NESTED, encoded) + + val source = SolidityBase.PartitionData.of(encoded) + + val offsetUints = BigInteger(source.consume(), 16).intValueExact() + assertEquals(SolidityBase.Vector.Decoder(SolidityBase.Vector.Decoder(Solidity.UInt256.DECODER)) + .decode(source.subData(offsetUints)), items[0]) + + val offsetStrings = BigInteger(source.consume(), 16).intValueExact() + assertEquals(SolidityBase.Vector.Decoder(Solidity.String.DECODER) + .decode(source.subData(offsetStrings)), items[1]) + } + @Test fun testDecodeEncodeStaticStringArray() { val items = listOf(Solidity.String("Hi"), Solidity.String("I"), Solidity.String("want"), @@ -327,12 +458,29 @@ class SolidityBaseTest { Solidity.String("Hello, world!").encode()) } - @Test(expected = IllegalArgumentException::class) + @Test(expected = Exception::class) fun testMalformedArraySizeDecoding() { assertEquals(listOf(BigInteger("456", 16), BigInteger("789", 16)), SolidityBase.decodeArray("000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789", SolidityBase::decodeUInt)) } + // Web3py and Web3js add an 0-bytes body to empty arrays, we should still correctly decode the data + @Test + fun testMalformedBytes() { + val source = SolidityBase.PartitionData.of(ENCODED_MALFORMED_BYTES_TUPLE) + // This would be the code generated for a tuple (bytes, string) + val bytesOffset = BigInteger(source.consume(), 16).intValueExact() + assertEquals( + Solidity.Bytes(byteArrayOf()), + Solidity.Bytes.DECODER.decode(source.subData(bytesOffset)) + ) + val stringOffset = BigInteger(source.consume(), 16).intValueExact() + assertEquals( + Solidity.String("Broken"), + Solidity.String.DECODER.decode(source.subData(stringOffset)) + ) + } + private fun formatClassName(clazz: String): String { val index = clazz.lastIndexOf(".") return clazz.replaceRange(index, index + 1, "$") @@ -347,6 +495,23 @@ class SolidityBaseTest { } companion object { + + const val ENCODED_MALFORMED_BYTES_TUPLE = "" + + // Location of first empty bytes + "0000000000000000000000000000000000000000000000000000000000000040" + + // Location of String "Broken" + "0000000000000000000000000000000000000000000000000000000000000080" + + + // Length of empty bytes + "0000000000000000000000000000000000000000000000000000000000000000" + + // Empty body of empty bytes + "0000000000000000000000000000000000000000000000000000000000000000" + + + // Length of "Broken" + "0000000000000000000000000000000000000000000000000000000000000006" + + // Byte string of "Broken" + "42726f6b656e0000000000000000000000000000000000000000000000000000" + // Encoded string of ["Hi", "I", "want", "to", "learn", "Solidity"] const val ENCODED_STATIC_STRING_ARRAY = "" + // Location of String "Hi" @@ -396,5 +561,39 @@ class SolidityBaseTest { // Array length "0000000000000000000000000000000000000000000000000000000000000006" + ENCODED_STATIC_STRING_ARRAY + + const val ENCODED_SOLIDITY_EXAMPLE_TUPLE = "" + + "0000000000000000000000000000000000000000000000000000000000000123" + + "0000000000000000000000000000000000000000000000000000000000000080" + + "3132333435363738393000000000000000000000000000000000000000000000" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000456" + + "0000000000000000000000000000000000000000000000000000000000000789" + + "000000000000000000000000000000000000000000000000000000000000000d" + + "48656c6c6f2c20776f726c642100000000000000000000000000000000000000" + + const val ENCODED_SOLIDITY_EXAMPLE_TUPLE_NESTED = "" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "0000000000000000000000000000000000000000000000000000000000000140" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000040" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000002" + + "0000000000000000000000000000000000000000000000000000000000000001" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "0000000000000000000000000000000000000000000000000000000000000060" + + "00000000000000000000000000000000000000000000000000000000000000a0" + + "00000000000000000000000000000000000000000000000000000000000000e0" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "6f6e650000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000003" + + "74776f0000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000005" + + "7468726565000000000000000000000000000000000000000000000000000000" + } }