From a08ac8470d59272a4f6bf9e43daa9fe398555720 Mon Sep 17 00:00:00 2001 From: Antlion12 Date: Tue, 16 Jul 2024 08:31:35 -0700 Subject: [PATCH 1/7] Factor out offset-computation and parameter-extraction from decodeDynamicStructElementsFromInnerTypes(). Signed-off-by: Antlion12 --- .../main/java/org/web3j/abi/TypeDecoder.java | 103 ++++++++++++------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/abi/src/main/java/org/web3j/abi/TypeDecoder.java b/abi/src/main/java/org/web3j/abi/TypeDecoder.java index f3237d52f..b30911bcf 100644 --- a/abi/src/main/java/org/web3j/abi/TypeDecoder.java +++ b/abi/src/main/java/org/web3j/abi/TypeDecoder.java @@ -484,33 +484,44 @@ public static T decodeDynamicStruct( return decodeDynamicStructElements(input, offset, typeReference, function); } - @SuppressWarnings("unchecked") - private static T decodeDynamicStructElementsFromInnerTypes( - final String input, - final int offset, - final TypeReference typeReference, - final BiFunction, String, T> consumer) - throws ClassNotFoundException { - final Class classType = typeReference.getClassType(); - final List> innerTypes = typeReference.getInnerTypes(); - final int length = innerTypes.size(); - final Map parameters = new HashMap<>(); - int staticOffset = 0; - final List parameterOffsets = new ArrayList<>(); + private static class ParameterOffsetTracker { + public final Map parameters; + public final List parameterOffsets; + public int staticOffset; + public int dynamicParametersToProcess; + + ParameterOffsetTracker( + final Map parametersIn, + final List parameterOffsetsIn, + int staticOffsetIn, + int dynamicParametersToProcessIn) { + this.parameters = parametersIn; + this.parameterOffsets = parameterOffsetsIn; + this.staticOffset = staticOffsetIn; + this.dynamicParametersToProcess = dynamicParametersToProcess; + } + } - int dynamicParametersToProcess = 0; - for (int i = 0; i < length; ++i) { + private static + ParameterOffsetTracker getDynamicOffsetsAndNonDynamicParameters( + final String input, final int offset, final TypeReference typeReference) + throws ClassNotFoundException { + ParameterOffsetTracker tracker = + new ParameterOffsetTracker(new HashMap<>(), new ArrayList<>(), 0, 0); + + final List> innerTypes = typeReference.getInnerTypes(); + for (int i = 0; i < innerTypes.size(); ++i) { final Class declaredField = (Class) innerTypes.get(i).getClassType(); final T value; - final int beginIndex = offset + staticOffset; + final int beginIndex = offset + tracker.staticOffset; if (isDynamic(declaredField)) { final int parameterOffset = decodeDynamicStructDynamicParameterOffset( input.substring(beginIndex, beginIndex + 64)) + offset; - parameterOffsets.add(parameterOffset); - staticOffset += 64; - dynamicParametersToProcess += 1; + tracker.parameterOffsets.add(parameterOffset); + tracker.staticOffset += 64; + tracker.dynamicParametersToProcess += 1; } else { if (StaticStruct.class.isAssignableFrom(declaredField)) { value = @@ -518,49 +529,73 @@ private static T decodeDynamicStructElementsFromInnerTypes( input.substring(beginIndex), 0, TypeReference.create(declaredField)); - staticOffset += + tracker.staticOffset += staticStructNestedPublicFieldsFlatList((Class) declaredField) .size() * MAX_BYTE_LENGTH_FOR_HEX_STRING; } else { value = decode(input.substring(beginIndex), 0, declaredField); - staticOffset += value.bytes32PaddedLength() * 2; + tracker.staticOffset += value.bytes32PaddedLength() * 2; } - parameters.put(i, value); + tracker.parameters.put(i, value); } } + + return tracker; + } + + private static List getDynamicParametersWithTracker( + final String input, + final TypeReference typeReference, + final ParameterOffsetTracker tracker) + throws ClassNotFoundException { + + final List> innerTypes = typeReference.getInnerTypes(); int dynamicParametersProcessed = 0; - for (int i = 0; i < length; ++i) { + for (int i = 0; i < innerTypes.size(); ++i) { final TypeReference parameterTypeReference = (TypeReference) innerTypes.get(i); final Class declaredField = parameterTypeReference.getClassType(); if (isDynamic(declaredField)) { final boolean isLastParameterInStruct = - dynamicParametersProcessed == (dynamicParametersToProcess - 1); + dynamicParametersProcessed == (tracker.dynamicParametersToProcess - 1); final int parameterLength = isLastParameterInStruct - ? input.length() - parameterOffsets.get(dynamicParametersProcessed) - : parameterOffsets.get(dynamicParametersProcessed + 1) - - parameterOffsets.get(dynamicParametersProcessed); + ? input.length() + - tracker.parameterOffsets.get(dynamicParametersProcessed) + : tracker.parameterOffsets.get(dynamicParametersProcessed + 1) + - tracker.parameterOffsets.get(dynamicParametersProcessed); - parameters.put( + tracker.parameters.put( i, decodeDynamicParameterFromStructWithTypeReference( input, - parameterOffsets.get(dynamicParametersProcessed), + tracker.parameterOffsets.get(dynamicParametersProcessed), parameterLength, parameterTypeReference)); dynamicParametersProcessed++; } } - String typeName = getSimpleTypeName(classType); - final List elements = new ArrayList<>(); - for (int i = 0; i < length; ++i) { - elements.add(parameters.get(i)); + for (int i = 0; i < innerTypes.size(); ++i) { + elements.add(tracker.parameters.get(i)); } - return consumer.apply(elements, typeName); + return elements; + } + + @SuppressWarnings("unchecked") + private static T decodeDynamicStructElementsFromInnerTypes( + final String input, + final int offset, + final TypeReference typeReference, + final BiFunction, String, T> consumer) + throws ClassNotFoundException { + ParameterOffsetTracker tracker = + getDynamicOffsetsAndNonDynamicParameters(input, offset, typeReference); + final List parameters = getDynamicParametersWithTracker(input, typeReference, tracker); + String typeName = getSimpleTypeName(typeReference.getClassType()); + return consumer.apply(parameters, typeName); } @SuppressWarnings("unchecked") From ebc14952da07ea6f000ff07efa55e91ea8b17eb8 Mon Sep 17 00:00:00 2001 From: gtebrean <99179176+gtebrean@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:04:51 +0300 Subject: [PATCH 2/7] move test Signed-off-by: gtebrean <99179176+gtebrean@users.noreply.github.com> --- .../web3j/abi/DefaultFunctionEncoderTest.java | 43 ------- .../web3j/abi/FunctionReturnDecoderTest.java | 106 ++++++++++++++---- 2 files changed, 86 insertions(+), 63 deletions(-) diff --git a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java index 456864d83..66a0c5296 100644 --- a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java +++ b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java @@ -59,49 +59,6 @@ public void testBuildMethodSignatureWithStructWithArray() { BigInteger.ONE, Collections.emptyList())))); } - @Test - public void testDynamicStructFix() throws ClassNotFoundException { - // Return data from 'testInputAndOutput' function of this contract - // https://sepolia.etherscan.io/address/0x009C10396226ECFE3E39b3f1AEFa072E37578e30#readContract - String returnedData = - "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000b76616c75656265666f72650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063078313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a76616c7565616674657200000000000000000000000000000000000000000000"; - - List> MyStruct2Types = new ArrayList<>(); - List> MyStructTypes = new ArrayList<>(); - List> MyParameters = new ArrayList<>(); - - MyStruct2Types.add(TypeReference.makeTypeReference("string")); - MyStruct2Types.add(TypeReference.makeTypeReference("string")); - - MyStructTypes.add(TypeReference.makeTypeReference("uint256")); - MyStructTypes.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(new TypeReference(false, MyStruct2Types) {}); - - MyParameters.add(TypeReference.makeTypeReference("string")); - MyParameters.add(new TypeReference(false, MyStructTypes) {}); - - MyParameters.add(TypeReference.makeTypeReference("string")); - - List decodedData = - FunctionReturnDecoder.decode(returnedData, Utils.convert(MyParameters)); - - assertEquals(decodedData.get(0).getValue(), "valuebefore"); - - List structData = ((DynamicStruct) decodedData.get(1)).getValue(); - - assertEquals(structData.get(0).getValue(), BigInteger.valueOf(1)); - assertEquals(structData.get(1).getValue(), "2"); - assertEquals(structData.get(2).getValue(), "3"); - - List innerStructData = ((DynamicStruct) structData.get(3)).getValue(); - - assertEquals(innerStructData.get(0).getValue(), "1234"); - assertEquals(innerStructData.get(1).getValue(), "0x1234"); - - assertEquals(decodedData.get(2).getValue(), "valueafter"); - } - @Test public void testArrayOfDynamicStruct() throws ClassNotFoundException { // The full event signature is diff --git a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java index 9cf79926e..8795d95fd 100644 --- a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java +++ b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java @@ -22,6 +22,7 @@ import org.web3j.abi.datatypes.DynamicArray; import org.web3j.abi.datatypes.DynamicBytes; +import org.web3j.abi.datatypes.DynamicStruct; import org.web3j.abi.datatypes.Function; import org.web3j.abi.datatypes.StaticArray; import org.web3j.abi.datatypes.Type; @@ -46,7 +47,8 @@ public void testSimpleFunctionDecode() { new Function( "test", Collections.emptyList(), - Collections.singletonList(new TypeReference() {})); + Collections.singletonList(new TypeReference() { + })); assertEquals( FunctionReturnDecoder.decode( @@ -61,7 +63,8 @@ public void testSimpleFunctionStringResultDecode() { new Function( "simple", Arrays.asList(), - Collections.singletonList(new TypeReference() {})); + Collections.singletonList(new TypeReference() { + })); List utf8Strings = FunctionReturnDecoder.decode( @@ -79,7 +82,8 @@ public void testFunctionEmptyStringResultDecode() { new Function( "test", Collections.emptyList(), - Collections.singletonList(new TypeReference() {})); + Collections.singletonList(new TypeReference() { + })); List utf8Strings = FunctionReturnDecoder.decode( @@ -96,7 +100,9 @@ public void testMultipleResultFunctionDecode() { new Function( "test", Collections.emptyList(), - Arrays.asList(new TypeReference() {}, new TypeReference() {})); + Arrays.asList(new TypeReference() { + }, new TypeReference() { + })); assertEquals( FunctionReturnDecoder.decode( @@ -113,10 +119,14 @@ public void testDecodeMultipleStringValues() { "function", Collections.emptyList(), Arrays.asList( - new TypeReference() {}, - new TypeReference() {}, - new TypeReference() {}, - new TypeReference() {})); + new TypeReference() { + }, + new TypeReference() { + }, + new TypeReference() { + }, + new TypeReference() { + })); assertEquals( FunctionReturnDecoder.decode( @@ -144,8 +154,10 @@ public void testDecodeStaticArrayValue() { List> outputParameters = new ArrayList<>(1); outputParameters.add( (TypeReference) - new TypeReference.StaticArrayTypeReference>(2) {}); - outputParameters.add((TypeReference) new TypeReference() {}); + new TypeReference.StaticArrayTypeReference>(2) { + }); + outputParameters.add((TypeReference) new TypeReference() { + }); List decoded = FunctionReturnDecoder.decode( @@ -177,7 +189,8 @@ public void testEmptyResultFunctionDecode() { new Function( "test", Collections.emptyList(), - Collections.singletonList(new TypeReference() {})); + Collections.singletonList(new TypeReference() { + })); assertEquals( FunctionReturnDecoder.decode("0x", function.getOutputParameters()), @@ -190,7 +203,8 @@ public void testDecodeIndexedUint256Value() { String encoded = TypeEncoder.encodeNumeric(value); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(encoded, new TypeReference() {}), + FunctionReturnDecoder.decodeIndexedValue(encoded, new TypeReference() { + }), (value)); } @@ -201,7 +215,8 @@ public void testDecodeIndexedStringValue() { String hash = Hash.sha3(encoded); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(hash, new TypeReference() {}), + FunctionReturnDecoder.decodeIndexedValue(hash, new TypeReference() { + }), (new Bytes32(Numeric.hexStringToByteArray(hash)))); } @@ -211,7 +226,8 @@ public void testDecodeIndexedBytes32Value() { byte[] rawInputBytes = Numeric.hexStringToByteArray(rawInput); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() {}), + FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() { + }), (new Bytes32(rawInputBytes))); } @@ -221,19 +237,21 @@ public void testDecodeIndexedBytes16Value() { byte[] rawInputBytes = Numeric.hexStringToByteArray(rawInput.substring(0, 34)); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() {}), + FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() { + }), (new Bytes16(rawInputBytes))); } @Test public void testDecodeIndexedDynamicBytesValue() { - DynamicBytes bytes = new DynamicBytes(new byte[] {1, 2, 3, 4, 5}); + DynamicBytes bytes = new DynamicBytes(new byte[]{1, 2, 3, 4, 5}); String encoded = TypeEncoder.encodeDynamicBytes(bytes); String hash = Hash.sha3(encoded); assertEquals( FunctionReturnDecoder.decodeIndexedValue( - hash, new TypeReference() {}), + hash, new TypeReference() { + }), (new Bytes32(Numeric.hexStringToByteArray(hash)))); } @@ -247,7 +265,8 @@ public void testDecodeIndexedDynamicArrayValue() { assertEquals( FunctionReturnDecoder.decodeIndexedValue( - hash, new TypeReference() {}), + hash, new TypeReference() { + }), (new Bytes32(Numeric.hexStringToByteArray(hash)))); } @@ -1262,8 +1281,10 @@ public void testDecodeTupleOfStaticArrays() { List outputParameters = new ArrayList>(); outputParameters.addAll( Arrays.asList( - new TypeReference>() {}, - new TypeReference>() {})); + new TypeReference>() { + }, + new TypeReference>() { + })); // tuple of (strings string[4]{"", "", "", ""}, ints int[4]{0, 0, 0, 0}) String rawInput = @@ -1316,4 +1337,49 @@ public void testDecodeDynamicStructWithStaticStruct() { new AbiV2TestFixture.Qux( new AbiV2TestFixture.Bar(BigInteger.ONE, BigInteger.TEN), "data"))); } + + @Test + public void testDynamicStructFix() throws ClassNotFoundException { + // Return data from 'testInputAndOutput' function of this contract + // https://sepolia.etherscan.io/address/0x009C10396226ECFE3E39b3f1AEFa072E37578e30#readContract + String returnedData = + "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000b76616c75656265666f72650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063078313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a76616c7565616674657200000000000000000000000000000000000000000000"; + + List> MyStruct2Types = new ArrayList<>(); + List> MyStructTypes = new ArrayList<>(); + List> MyParameters = new ArrayList<>(); + + MyStruct2Types.add(TypeReference.makeTypeReference("string")); + MyStruct2Types.add(TypeReference.makeTypeReference("string")); + + MyStructTypes.add(TypeReference.makeTypeReference("uint256")); + MyStructTypes.add(TypeReference.makeTypeReference("string")); + MyStructTypes.add(TypeReference.makeTypeReference("string")); + MyStructTypes.add(new TypeReference(false, MyStruct2Types) { + }); + + MyParameters.add(TypeReference.makeTypeReference("string")); + MyParameters.add(new TypeReference(false, MyStructTypes) { + }); + + MyParameters.add(TypeReference.makeTypeReference("string")); + + List decodedData = + FunctionReturnDecoder.decode(returnedData, Utils.convert(MyParameters)); + + assertEquals(decodedData.get(0).getValue(), "valuebefore"); + + List structData = ((DynamicStruct) decodedData.get(1)).getValue(); + + assertEquals(structData.get(0).getValue(), BigInteger.valueOf(1)); + assertEquals(structData.get(1).getValue(), "2"); + assertEquals(structData.get(2).getValue(), "3"); + + List innerStructData = ((DynamicStruct) structData.get(3)).getValue(); + + assertEquals(innerStructData.get(0).getValue(), "1234"); + assertEquals(innerStructData.get(1).getValue(), "0x1234"); + + assertEquals(decodedData.get(2).getValue(), "valueafter"); + } } From 17c121183e8f2f42378d07a513d8ffd5577d544c Mon Sep 17 00:00:00 2001 From: gtebrean <99179176+gtebrean@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:07:41 +0300 Subject: [PATCH 3/7] spotless Signed-off-by: gtebrean <99179176+gtebrean@users.noreply.github.com> --- .../web3j/abi/FunctionReturnDecoderTest.java | 66 +++++++------------ 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java index 8795d95fd..ac04ae63f 100644 --- a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java +++ b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java @@ -47,8 +47,7 @@ public void testSimpleFunctionDecode() { new Function( "test", Collections.emptyList(), - Collections.singletonList(new TypeReference() { - })); + Collections.singletonList(new TypeReference() {})); assertEquals( FunctionReturnDecoder.decode( @@ -63,8 +62,7 @@ public void testSimpleFunctionStringResultDecode() { new Function( "simple", Arrays.asList(), - Collections.singletonList(new TypeReference() { - })); + Collections.singletonList(new TypeReference() {})); List utf8Strings = FunctionReturnDecoder.decode( @@ -82,8 +80,7 @@ public void testFunctionEmptyStringResultDecode() { new Function( "test", Collections.emptyList(), - Collections.singletonList(new TypeReference() { - })); + Collections.singletonList(new TypeReference() {})); List utf8Strings = FunctionReturnDecoder.decode( @@ -100,9 +97,7 @@ public void testMultipleResultFunctionDecode() { new Function( "test", Collections.emptyList(), - Arrays.asList(new TypeReference() { - }, new TypeReference() { - })); + Arrays.asList(new TypeReference() {}, new TypeReference() {})); assertEquals( FunctionReturnDecoder.decode( @@ -119,14 +114,10 @@ public void testDecodeMultipleStringValues() { "function", Collections.emptyList(), Arrays.asList( - new TypeReference() { - }, - new TypeReference() { - }, - new TypeReference() { - }, - new TypeReference() { - })); + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {}, + new TypeReference() {})); assertEquals( FunctionReturnDecoder.decode( @@ -154,10 +145,8 @@ public void testDecodeStaticArrayValue() { List> outputParameters = new ArrayList<>(1); outputParameters.add( (TypeReference) - new TypeReference.StaticArrayTypeReference>(2) { - }); - outputParameters.add((TypeReference) new TypeReference() { - }); + new TypeReference.StaticArrayTypeReference>(2) {}); + outputParameters.add((TypeReference) new TypeReference() {}); List decoded = FunctionReturnDecoder.decode( @@ -189,8 +178,7 @@ public void testEmptyResultFunctionDecode() { new Function( "test", Collections.emptyList(), - Collections.singletonList(new TypeReference() { - })); + Collections.singletonList(new TypeReference() {})); assertEquals( FunctionReturnDecoder.decode("0x", function.getOutputParameters()), @@ -203,8 +191,7 @@ public void testDecodeIndexedUint256Value() { String encoded = TypeEncoder.encodeNumeric(value); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(encoded, new TypeReference() { - }), + FunctionReturnDecoder.decodeIndexedValue(encoded, new TypeReference() {}), (value)); } @@ -215,8 +202,7 @@ public void testDecodeIndexedStringValue() { String hash = Hash.sha3(encoded); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(hash, new TypeReference() { - }), + FunctionReturnDecoder.decodeIndexedValue(hash, new TypeReference() {}), (new Bytes32(Numeric.hexStringToByteArray(hash)))); } @@ -226,8 +212,7 @@ public void testDecodeIndexedBytes32Value() { byte[] rawInputBytes = Numeric.hexStringToByteArray(rawInput); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() { - }), + FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() {}), (new Bytes32(rawInputBytes))); } @@ -237,21 +222,19 @@ public void testDecodeIndexedBytes16Value() { byte[] rawInputBytes = Numeric.hexStringToByteArray(rawInput.substring(0, 34)); assertEquals( - FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() { - }), + FunctionReturnDecoder.decodeIndexedValue(rawInput, new TypeReference() {}), (new Bytes16(rawInputBytes))); } @Test public void testDecodeIndexedDynamicBytesValue() { - DynamicBytes bytes = new DynamicBytes(new byte[]{1, 2, 3, 4, 5}); + DynamicBytes bytes = new DynamicBytes(new byte[] {1, 2, 3, 4, 5}); String encoded = TypeEncoder.encodeDynamicBytes(bytes); String hash = Hash.sha3(encoded); assertEquals( FunctionReturnDecoder.decodeIndexedValue( - hash, new TypeReference() { - }), + hash, new TypeReference() {}), (new Bytes32(Numeric.hexStringToByteArray(hash)))); } @@ -265,8 +248,7 @@ public void testDecodeIndexedDynamicArrayValue() { assertEquals( FunctionReturnDecoder.decodeIndexedValue( - hash, new TypeReference() { - }), + hash, new TypeReference() {}), (new Bytes32(Numeric.hexStringToByteArray(hash)))); } @@ -1281,10 +1263,8 @@ public void testDecodeTupleOfStaticArrays() { List outputParameters = new ArrayList>(); outputParameters.addAll( Arrays.asList( - new TypeReference>() { - }, - new TypeReference>() { - })); + new TypeReference>() {}, + new TypeReference>() {})); // tuple of (strings string[4]{"", "", "", ""}, ints int[4]{0, 0, 0, 0}) String rawInput = @@ -1355,12 +1335,10 @@ public void testDynamicStructFix() throws ClassNotFoundException { MyStructTypes.add(TypeReference.makeTypeReference("uint256")); MyStructTypes.add(TypeReference.makeTypeReference("string")); MyStructTypes.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(new TypeReference(false, MyStruct2Types) { - }); + MyStructTypes.add(new TypeReference(false, MyStruct2Types) {}); MyParameters.add(TypeReference.makeTypeReference("string")); - MyParameters.add(new TypeReference(false, MyStructTypes) { - }); + MyParameters.add(new TypeReference(false, MyStructTypes) {}); MyParameters.add(TypeReference.makeTypeReference("string")); From 9a36a217048ea00ada4122eda223d5544aeb5a3c Mon Sep 17 00:00:00 2001 From: gtebrean <99179176+gtebrean@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:01:11 +0300 Subject: [PATCH 4/7] fix naming Signed-off-by: gtebrean <99179176+gtebrean@users.noreply.github.com> --- .../web3j/abi/DefaultFunctionEncoderTest.java | 2 +- .../web3j/abi/FunctionReturnDecoderTest.java | 45 +++++++++++++------ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java index 66a0c5296..487d8b8e6 100644 --- a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java +++ b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java @@ -60,7 +60,7 @@ public void testBuildMethodSignatureWithStructWithArray() { } @Test - public void testArrayOfDynamicStruct() throws ClassNotFoundException { + public void testBuildEventOfArrayOfDynamicStruct() throws ClassNotFoundException { // The full event signature is // // Stamp3(uint256 indexed stampId, address indexed caller, bool odd, diff --git a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java index ac04ae63f..a45d43921 100644 --- a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java +++ b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java @@ -1319,31 +1319,48 @@ public void testDecodeDynamicStructWithStaticStruct() { } @Test - public void testDynamicStructFix() throws ClassNotFoundException { + public void testDynamicStructWithAdditionalParametersReturn() throws ClassNotFoundException { // Return data from 'testInputAndOutput' function of this contract // https://sepolia.etherscan.io/address/0x009C10396226ECFE3E39b3f1AEFa072E37578e30#readContract + // struct MyStruct { + // uint256 value1; + // string value2; + // string value3; + // MyStruct2 value4; + // } + // + // struct MyStruct2 { + // string value1; + // string value2; + // } + // function testInputAndOutput(MyStruct memory struc) external pure + // returns(string memory valueBefore, MyStruct memory, string memory valueAfter) { + // + // return ("valuebefore", mystruc, "valueafter"); + // + // } String returnedData = "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000b76616c75656265666f72650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063078313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a76616c7565616674657200000000000000000000000000000000000000000000"; - List> MyStruct2Types = new ArrayList<>(); - List> MyStructTypes = new ArrayList<>(); - List> MyParameters = new ArrayList<>(); + List> myStruct2Types = new ArrayList<>(); + List> myStructTypes = new ArrayList<>(); + List> myParameters = new ArrayList<>(); - MyStruct2Types.add(TypeReference.makeTypeReference("string")); - MyStruct2Types.add(TypeReference.makeTypeReference("string")); + myStruct2Types.add(TypeReference.makeTypeReference("string")); + myStruct2Types.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(TypeReference.makeTypeReference("uint256")); - MyStructTypes.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(new TypeReference(false, MyStruct2Types) {}); + myStructTypes.add(TypeReference.makeTypeReference("uint256")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(new TypeReference(false, myStruct2Types) {}); - MyParameters.add(TypeReference.makeTypeReference("string")); - MyParameters.add(new TypeReference(false, MyStructTypes) {}); + myParameters.add(TypeReference.makeTypeReference("string")); + myParameters.add(new TypeReference(false, myStructTypes) {}); - MyParameters.add(TypeReference.makeTypeReference("string")); + myParameters.add(TypeReference.makeTypeReference("string")); List decodedData = - FunctionReturnDecoder.decode(returnedData, Utils.convert(MyParameters)); + FunctionReturnDecoder.decode(returnedData, Utils.convert(myParameters)); assertEquals(decodedData.get(0).getValue(), "valuebefore"); From 237e229660a15606d3651c798bef8577e0f46416 Mon Sep 17 00:00:00 2001 From: gtebrean <99179176+gtebrean@users.noreply.github.com> Date: Sun, 4 Aug 2024 19:07:15 +0300 Subject: [PATCH 5/7] add failing DynamicStructOfStaticStruct --- .../web3j/abi/DefaultFunctionEncoderTest.java | 110 ++------------- .../web3j/abi/FunctionReturnDecoderTest.java | 128 +++++++++++++++++- 2 files changed, 139 insertions(+), 99 deletions(-) diff --git a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java index 487d8b8e6..d5abf6edc 100644 --- a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java +++ b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java @@ -14,11 +14,22 @@ import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import org.junit.jupiter.api.Test; -import org.web3j.abi.datatypes.*; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Bool; +import org.web3j.abi.datatypes.DynamicArray; +import org.web3j.abi.datatypes.DynamicBytes; +import org.web3j.abi.datatypes.DynamicStruct; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.StaticStruct; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.Uint; +import org.web3j.abi.datatypes.Utf8String; import org.web3j.abi.datatypes.generated.Bytes10; import org.web3j.abi.datatypes.generated.Uint256; import org.web3j.abi.datatypes.generated.Uint32; @@ -59,101 +70,6 @@ public void testBuildMethodSignatureWithStructWithArray() { BigInteger.ONE, Collections.emptyList())))); } - @Test - public void testBuildEventOfArrayOfDynamicStruct() throws ClassNotFoundException { - // The full event signature is - // - // Stamp3(uint256 indexed stampId, address indexed caller, bool odd, - // (uint256,bool,string) topMessage, (uint256,bool,string)[] messages), - // - // but we are only decoding the non-indexed data portion of it represented by - // 'bool odd, (uint256,bool,string) topMessage, (uint256,bool,string)[] messages'. - // - // Transaction: - // https://testnet.treasurescan.io/tx/0x041e53e7571283d462df99a95b2c21324279657f26a3adef907095d2d9c5ed85?tab=logs - // Contract: - // https://testnet.treasurescan.io/address/0x5167E9A422aCEd95C2D0b62bF05a7847a9a942B2 - String data = - "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000"; - - TypeReference tupleTr = - new TypeReference( - false, - Arrays.asList( - TypeReference.makeTypeReference("uint256"), - TypeReference.makeTypeReference("bool"), - TypeReference.makeTypeReference("string"))) {}; - - // Decode data according to the above signature for the non-indexed parameters. - List decodedData = - FunctionReturnDecoder.decode( - data, - Utils.convert( - Arrays.asList( - // bool odd - TypeReference.makeTypeReference("bool"), - - // (uint256,bool,string) - tupleTr, - - // (uint256,bool,string)[] - new TypeReference(false) { - @Override - public TypeReference getSubTypeReference() { - return tupleTr; - } - - @Override - public java.lang.reflect.Type getType() { - return new java.lang.reflect.ParameterizedType() { - @Override - public java.lang.reflect.Type[] - getActualTypeArguments() { - return new java.lang.reflect.Type[] { - tupleTr.getType() - }; - } - - @Override - public java.lang.reflect.Type getRawType() { - return DynamicArray.class; - } - - @Override - public java.lang.reflect.Type getOwnerType() { - return Class.class; - } - }; - } - }))); - - assertEquals(decodedData.get(0).getValue(), false); - - List tupleData = ((DynamicStruct) decodedData.get(1)).getValue(); - - assertEquals(tupleData.get(0).getValue(), BigInteger.valueOf(20)); - assertEquals(tupleData.get(1).getValue(), false); - assertEquals(tupleData.get(2).getValue(), "hello"); - - List tupleArrayData = - ((DynamicArray) decodedData.get(2)).getValue(); - - List tupleArrayEntry0 = tupleArrayData.get(0).getValue(); - assertEquals(tupleArrayEntry0.get(0).getValue(), BigInteger.valueOf(21)); - assertEquals(tupleArrayEntry0.get(1).getValue(), true); - assertEquals(tupleArrayEntry0.get(2).getValue(), "gm"); - - List tupleArrayEntry1 = tupleArrayData.get(1).getValue(); - assertEquals(tupleArrayEntry1.get(0).getValue(), BigInteger.valueOf(22)); - assertEquals(tupleArrayEntry1.get(1).getValue(), false); - assertEquals(tupleArrayEntry1.get(2).getValue(), "gm"); - - List tupleArrayEntry2 = tupleArrayData.get(2).getValue(); - assertEquals(tupleArrayEntry2.get(0).getValue(), BigInteger.valueOf(23)); - assertEquals(tupleArrayEntry2.get(1).getValue(), true); - assertEquals(tupleArrayEntry2.get(2).getValue(), "gm"); - } - @Test public void testBuildMessageSignatureWithComplexTuple() { AbiV2TestFixture.Nazz nazz = diff --git a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java index a45d43921..a747881d9 100644 --- a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java +++ b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java @@ -25,6 +25,7 @@ import org.web3j.abi.datatypes.DynamicStruct; import org.web3j.abi.datatypes.Function; import org.web3j.abi.datatypes.StaticArray; +import org.web3j.abi.datatypes.StaticStruct; import org.web3j.abi.datatypes.Type; import org.web3j.abi.datatypes.Uint; import org.web3j.abi.datatypes.Utf8String; @@ -1319,10 +1320,11 @@ public void testDecodeDynamicStructWithStaticStruct() { } @Test - public void testDynamicStructWithAdditionalParametersReturn() throws ClassNotFoundException { + public void testDynamicStructOfDynamicStructWithAdditionalParametersReturn() + throws ClassNotFoundException { // Return data from 'testInputAndOutput' function of this contract // https://sepolia.etherscan.io/address/0x009C10396226ECFE3E39b3f1AEFa072E37578e30#readContract - // struct MyStruct { + // struct MyStruct {s // uint256 value1; // string value2; // string value3; @@ -1377,4 +1379,126 @@ public void testDynamicStructWithAdditionalParametersReturn() throws ClassNotFou assertEquals(decodedData.get(2).getValue(), "valueafter"); } + + @Test + public void testDynamicStructOfStaticStructReturn() throws ClassNotFoundException { + String returnedData = + "0xf535d95e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004746573740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000"; + + List> myStruct2Types = new ArrayList<>(); + List> myStructTypes = new ArrayList<>(); + + myStruct2Types.add(TypeReference.makeTypeReference("uint256")); + + myStructTypes.add(TypeReference.makeTypeReference("uint256")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(new TypeReference(false, myStruct2Types) {}); + + List decodedData = + FunctionReturnDecoder.decode(returnedData, Utils.convert(myStructTypes)); + + assertEquals(decodedData.get(0).getValue(), BigInteger.valueOf(1)); + assertEquals(decodedData.get(1).getValue(), "test"); + assertEquals(decodedData.get(2).getValue(), "test"); + + List innerStructData = ((StaticStruct) decodedData.get(3)).getValue(); + + assertEquals(innerStructData.get(0).getValue(), BigInteger.valueOf(1)); + } + + @Test + public void testBuildEventOfArrayOfDynamicStruct() throws ClassNotFoundException { + // The full event signature is + // + // Stamp3(uint256 indexed stampId, address indexed caller, bool odd, + // (uint256,bool,string) topMessage, (uint256,bool,string)[] messages), + // + // but we are only decoding the non-indexed data portion of it represented by + // 'bool odd, (uint256,bool,string) topMessage, (uint256,bool,string)[] messages'. + // + // Transaction: + // https://testnet.treasurescan.io/tx/0x041e53e7571283d462df99a95b2c21324279657f26a3adef907095d2d9c5ed85?tab=logs + // Contract: + // https://testnet.treasurescan.io/address/0x5167E9A422aCEd95C2D0b62bF05a7847a9a942B2 + String data = + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000"; + + TypeReference tupleTr = + new TypeReference( + false, + Arrays.asList( + TypeReference.makeTypeReference("uint256"), + TypeReference.makeTypeReference("bool"), + TypeReference.makeTypeReference("string"))) {}; + + // Decode data according to the above signature for the non-indexed parameters. + List decodedData = + FunctionReturnDecoder.decode( + data, + Utils.convert( + Arrays.asList( + // bool odd + TypeReference.makeTypeReference("bool"), + + // (uint256,bool,string) + tupleTr, + + // (uint256,bool,string)[] + new TypeReference(false) { + @Override + public TypeReference getSubTypeReference() { + return tupleTr; + } + + @Override + public java.lang.reflect.Type getType() { + return new java.lang.reflect.ParameterizedType() { + @Override + public java.lang.reflect.Type[] + getActualTypeArguments() { + return new java.lang.reflect.Type[] { + tupleTr.getType() + }; + } + + @Override + public java.lang.reflect.Type getRawType() { + return DynamicArray.class; + } + + @Override + public java.lang.reflect.Type getOwnerType() { + return Class.class; + } + }; + } + }))); + + assertEquals(decodedData.get(0).getValue(), false); + + List tupleData = ((DynamicStruct) decodedData.get(1)).getValue(); + + assertEquals(tupleData.get(0).getValue(), BigInteger.valueOf(20)); + assertEquals(tupleData.get(1).getValue(), false); + assertEquals(tupleData.get(2).getValue(), "hello"); + + List tupleArrayData = + ((DynamicArray) decodedData.get(2)).getValue(); + + List tupleArrayEntry0 = tupleArrayData.get(0).getValue(); + assertEquals(tupleArrayEntry0.get(0).getValue(), BigInteger.valueOf(21)); + assertEquals(tupleArrayEntry0.get(1).getValue(), true); + assertEquals(tupleArrayEntry0.get(2).getValue(), "gm"); + + List tupleArrayEntry1 = tupleArrayData.get(1).getValue(); + assertEquals(tupleArrayEntry1.get(0).getValue(), BigInteger.valueOf(22)); + assertEquals(tupleArrayEntry1.get(1).getValue(), false); + assertEquals(tupleArrayEntry1.get(2).getValue(), "gm"); + + List tupleArrayEntry2 = tupleArrayData.get(2).getValue(); + assertEquals(tupleArrayEntry2.get(0).getValue(), BigInteger.valueOf(23)); + assertEquals(tupleArrayEntry2.get(1).getValue(), true); + assertEquals(tupleArrayEntry2.get(2).getValue(), "gm"); + } } From 5faf0f931258129ab845f90091a51724e462a03d Mon Sep 17 00:00:00 2001 From: gtebrean <99179176+gtebrean@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:01:11 +0300 Subject: [PATCH 6/7] fix naming Signed-off-by: gtebrean <99179176+gtebrean@users.noreply.github.com> --- .../web3j/abi/DefaultFunctionEncoderTest.java | 2 +- .../web3j/abi/FunctionReturnDecoderTest.java | 45 +++++++++++++------ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java index 66a0c5296..487d8b8e6 100644 --- a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java +++ b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java @@ -60,7 +60,7 @@ public void testBuildMethodSignatureWithStructWithArray() { } @Test - public void testArrayOfDynamicStruct() throws ClassNotFoundException { + public void testBuildEventOfArrayOfDynamicStruct() throws ClassNotFoundException { // The full event signature is // // Stamp3(uint256 indexed stampId, address indexed caller, bool odd, diff --git a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java index ac04ae63f..a45d43921 100644 --- a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java +++ b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java @@ -1319,31 +1319,48 @@ public void testDecodeDynamicStructWithStaticStruct() { } @Test - public void testDynamicStructFix() throws ClassNotFoundException { + public void testDynamicStructWithAdditionalParametersReturn() throws ClassNotFoundException { // Return data from 'testInputAndOutput' function of this contract // https://sepolia.etherscan.io/address/0x009C10396226ECFE3E39b3f1AEFa072E37578e30#readContract + // struct MyStruct { + // uint256 value1; + // string value2; + // string value3; + // MyStruct2 value4; + // } + // + // struct MyStruct2 { + // string value1; + // string value2; + // } + // function testInputAndOutput(MyStruct memory struc) external pure + // returns(string memory valueBefore, MyStruct memory, string memory valueAfter) { + // + // return ("valuebefore", mystruc, "valueafter"); + // + // } String returnedData = "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000b76616c75656265666f72650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063078313233340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a76616c7565616674657200000000000000000000000000000000000000000000"; - List> MyStruct2Types = new ArrayList<>(); - List> MyStructTypes = new ArrayList<>(); - List> MyParameters = new ArrayList<>(); + List> myStruct2Types = new ArrayList<>(); + List> myStructTypes = new ArrayList<>(); + List> myParameters = new ArrayList<>(); - MyStruct2Types.add(TypeReference.makeTypeReference("string")); - MyStruct2Types.add(TypeReference.makeTypeReference("string")); + myStruct2Types.add(TypeReference.makeTypeReference("string")); + myStruct2Types.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(TypeReference.makeTypeReference("uint256")); - MyStructTypes.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(TypeReference.makeTypeReference("string")); - MyStructTypes.add(new TypeReference(false, MyStruct2Types) {}); + myStructTypes.add(TypeReference.makeTypeReference("uint256")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(new TypeReference(false, myStruct2Types) {}); - MyParameters.add(TypeReference.makeTypeReference("string")); - MyParameters.add(new TypeReference(false, MyStructTypes) {}); + myParameters.add(TypeReference.makeTypeReference("string")); + myParameters.add(new TypeReference(false, myStructTypes) {}); - MyParameters.add(TypeReference.makeTypeReference("string")); + myParameters.add(TypeReference.makeTypeReference("string")); List decodedData = - FunctionReturnDecoder.decode(returnedData, Utils.convert(MyParameters)); + FunctionReturnDecoder.decode(returnedData, Utils.convert(myParameters)); assertEquals(decodedData.get(0).getValue(), "valuebefore"); From 9e7730d4be58771325cd6be9870f41368ac892ec Mon Sep 17 00:00:00 2001 From: gtebrean <99179176+gtebrean@users.noreply.github.com> Date: Sun, 4 Aug 2024 19:07:15 +0300 Subject: [PATCH 7/7] add failing DynamicStructOfStaticStruct Signed-off-by: gtebrean <99179176+gtebrean@users.noreply.github.com> --- .../web3j/abi/DefaultFunctionEncoderTest.java | 110 ++------------- .../web3j/abi/FunctionReturnDecoderTest.java | 128 +++++++++++++++++- 2 files changed, 139 insertions(+), 99 deletions(-) diff --git a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java index 487d8b8e6..d5abf6edc 100644 --- a/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java +++ b/abi/src/test/java/org/web3j/abi/DefaultFunctionEncoderTest.java @@ -14,11 +14,22 @@ import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import org.junit.jupiter.api.Test; -import org.web3j.abi.datatypes.*; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Bool; +import org.web3j.abi.datatypes.DynamicArray; +import org.web3j.abi.datatypes.DynamicBytes; +import org.web3j.abi.datatypes.DynamicStruct; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.StaticStruct; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.Uint; +import org.web3j.abi.datatypes.Utf8String; import org.web3j.abi.datatypes.generated.Bytes10; import org.web3j.abi.datatypes.generated.Uint256; import org.web3j.abi.datatypes.generated.Uint32; @@ -59,101 +70,6 @@ public void testBuildMethodSignatureWithStructWithArray() { BigInteger.ONE, Collections.emptyList())))); } - @Test - public void testBuildEventOfArrayOfDynamicStruct() throws ClassNotFoundException { - // The full event signature is - // - // Stamp3(uint256 indexed stampId, address indexed caller, bool odd, - // (uint256,bool,string) topMessage, (uint256,bool,string)[] messages), - // - // but we are only decoding the non-indexed data portion of it represented by - // 'bool odd, (uint256,bool,string) topMessage, (uint256,bool,string)[] messages'. - // - // Transaction: - // https://testnet.treasurescan.io/tx/0x041e53e7571283d462df99a95b2c21324279657f26a3adef907095d2d9c5ed85?tab=logs - // Contract: - // https://testnet.treasurescan.io/address/0x5167E9A422aCEd95C2D0b62bF05a7847a9a942B2 - String data = - "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000"; - - TypeReference tupleTr = - new TypeReference( - false, - Arrays.asList( - TypeReference.makeTypeReference("uint256"), - TypeReference.makeTypeReference("bool"), - TypeReference.makeTypeReference("string"))) {}; - - // Decode data according to the above signature for the non-indexed parameters. - List decodedData = - FunctionReturnDecoder.decode( - data, - Utils.convert( - Arrays.asList( - // bool odd - TypeReference.makeTypeReference("bool"), - - // (uint256,bool,string) - tupleTr, - - // (uint256,bool,string)[] - new TypeReference(false) { - @Override - public TypeReference getSubTypeReference() { - return tupleTr; - } - - @Override - public java.lang.reflect.Type getType() { - return new java.lang.reflect.ParameterizedType() { - @Override - public java.lang.reflect.Type[] - getActualTypeArguments() { - return new java.lang.reflect.Type[] { - tupleTr.getType() - }; - } - - @Override - public java.lang.reflect.Type getRawType() { - return DynamicArray.class; - } - - @Override - public java.lang.reflect.Type getOwnerType() { - return Class.class; - } - }; - } - }))); - - assertEquals(decodedData.get(0).getValue(), false); - - List tupleData = ((DynamicStruct) decodedData.get(1)).getValue(); - - assertEquals(tupleData.get(0).getValue(), BigInteger.valueOf(20)); - assertEquals(tupleData.get(1).getValue(), false); - assertEquals(tupleData.get(2).getValue(), "hello"); - - List tupleArrayData = - ((DynamicArray) decodedData.get(2)).getValue(); - - List tupleArrayEntry0 = tupleArrayData.get(0).getValue(); - assertEquals(tupleArrayEntry0.get(0).getValue(), BigInteger.valueOf(21)); - assertEquals(tupleArrayEntry0.get(1).getValue(), true); - assertEquals(tupleArrayEntry0.get(2).getValue(), "gm"); - - List tupleArrayEntry1 = tupleArrayData.get(1).getValue(); - assertEquals(tupleArrayEntry1.get(0).getValue(), BigInteger.valueOf(22)); - assertEquals(tupleArrayEntry1.get(1).getValue(), false); - assertEquals(tupleArrayEntry1.get(2).getValue(), "gm"); - - List tupleArrayEntry2 = tupleArrayData.get(2).getValue(); - assertEquals(tupleArrayEntry2.get(0).getValue(), BigInteger.valueOf(23)); - assertEquals(tupleArrayEntry2.get(1).getValue(), true); - assertEquals(tupleArrayEntry2.get(2).getValue(), "gm"); - } - @Test public void testBuildMessageSignatureWithComplexTuple() { AbiV2TestFixture.Nazz nazz = diff --git a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java index a45d43921..a747881d9 100644 --- a/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java +++ b/abi/src/test/java/org/web3j/abi/FunctionReturnDecoderTest.java @@ -25,6 +25,7 @@ import org.web3j.abi.datatypes.DynamicStruct; import org.web3j.abi.datatypes.Function; import org.web3j.abi.datatypes.StaticArray; +import org.web3j.abi.datatypes.StaticStruct; import org.web3j.abi.datatypes.Type; import org.web3j.abi.datatypes.Uint; import org.web3j.abi.datatypes.Utf8String; @@ -1319,10 +1320,11 @@ public void testDecodeDynamicStructWithStaticStruct() { } @Test - public void testDynamicStructWithAdditionalParametersReturn() throws ClassNotFoundException { + public void testDynamicStructOfDynamicStructWithAdditionalParametersReturn() + throws ClassNotFoundException { // Return data from 'testInputAndOutput' function of this contract // https://sepolia.etherscan.io/address/0x009C10396226ECFE3E39b3f1AEFa072E37578e30#readContract - // struct MyStruct { + // struct MyStruct {s // uint256 value1; // string value2; // string value3; @@ -1377,4 +1379,126 @@ public void testDynamicStructWithAdditionalParametersReturn() throws ClassNotFou assertEquals(decodedData.get(2).getValue(), "valueafter"); } + + @Test + public void testDynamicStructOfStaticStructReturn() throws ClassNotFoundException { + String returnedData = + "0xf535d95e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004746573740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000"; + + List> myStruct2Types = new ArrayList<>(); + List> myStructTypes = new ArrayList<>(); + + myStruct2Types.add(TypeReference.makeTypeReference("uint256")); + + myStructTypes.add(TypeReference.makeTypeReference("uint256")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(TypeReference.makeTypeReference("string")); + myStructTypes.add(new TypeReference(false, myStruct2Types) {}); + + List decodedData = + FunctionReturnDecoder.decode(returnedData, Utils.convert(myStructTypes)); + + assertEquals(decodedData.get(0).getValue(), BigInteger.valueOf(1)); + assertEquals(decodedData.get(1).getValue(), "test"); + assertEquals(decodedData.get(2).getValue(), "test"); + + List innerStructData = ((StaticStruct) decodedData.get(3)).getValue(); + + assertEquals(innerStructData.get(0).getValue(), BigInteger.valueOf(1)); + } + + @Test + public void testBuildEventOfArrayOfDynamicStruct() throws ClassNotFoundException { + // The full event signature is + // + // Stamp3(uint256 indexed stampId, address indexed caller, bool odd, + // (uint256,bool,string) topMessage, (uint256,bool,string)[] messages), + // + // but we are only decoding the non-indexed data portion of it represented by + // 'bool odd, (uint256,bool,string) topMessage, (uint256,bool,string)[] messages'. + // + // Transaction: + // https://testnet.treasurescan.io/tx/0x041e53e7571283d462df99a95b2c21324279657f26a3adef907095d2d9c5ed85?tab=logs + // Contract: + // https://testnet.treasurescan.io/address/0x5167E9A422aCEd95C2D0b62bF05a7847a9a942B2 + String data = + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002676d000000000000000000000000000000000000000000000000000000000000"; + + TypeReference tupleTr = + new TypeReference( + false, + Arrays.asList( + TypeReference.makeTypeReference("uint256"), + TypeReference.makeTypeReference("bool"), + TypeReference.makeTypeReference("string"))) {}; + + // Decode data according to the above signature for the non-indexed parameters. + List decodedData = + FunctionReturnDecoder.decode( + data, + Utils.convert( + Arrays.asList( + // bool odd + TypeReference.makeTypeReference("bool"), + + // (uint256,bool,string) + tupleTr, + + // (uint256,bool,string)[] + new TypeReference(false) { + @Override + public TypeReference getSubTypeReference() { + return tupleTr; + } + + @Override + public java.lang.reflect.Type getType() { + return new java.lang.reflect.ParameterizedType() { + @Override + public java.lang.reflect.Type[] + getActualTypeArguments() { + return new java.lang.reflect.Type[] { + tupleTr.getType() + }; + } + + @Override + public java.lang.reflect.Type getRawType() { + return DynamicArray.class; + } + + @Override + public java.lang.reflect.Type getOwnerType() { + return Class.class; + } + }; + } + }))); + + assertEquals(decodedData.get(0).getValue(), false); + + List tupleData = ((DynamicStruct) decodedData.get(1)).getValue(); + + assertEquals(tupleData.get(0).getValue(), BigInteger.valueOf(20)); + assertEquals(tupleData.get(1).getValue(), false); + assertEquals(tupleData.get(2).getValue(), "hello"); + + List tupleArrayData = + ((DynamicArray) decodedData.get(2)).getValue(); + + List tupleArrayEntry0 = tupleArrayData.get(0).getValue(); + assertEquals(tupleArrayEntry0.get(0).getValue(), BigInteger.valueOf(21)); + assertEquals(tupleArrayEntry0.get(1).getValue(), true); + assertEquals(tupleArrayEntry0.get(2).getValue(), "gm"); + + List tupleArrayEntry1 = tupleArrayData.get(1).getValue(); + assertEquals(tupleArrayEntry1.get(0).getValue(), BigInteger.valueOf(22)); + assertEquals(tupleArrayEntry1.get(1).getValue(), false); + assertEquals(tupleArrayEntry1.get(2).getValue(), "gm"); + + List tupleArrayEntry2 = tupleArrayData.get(2).getValue(); + assertEquals(tupleArrayEntry2.get(0).getValue(), BigInteger.valueOf(23)); + assertEquals(tupleArrayEntry2.get(1).getValue(), true); + assertEquals(tupleArrayEntry2.get(2).getValue(), "gm"); + } }