Skip to content

Commit

Permalink
Fix for SolidityType uint[] not matching array types.
Browse files Browse the repository at this point in the history
Add Javadocs and new tests for SolidityType factories

Enhanced the `SolidityType` class with Javadocs for improved code documentation. Added unit tests, including a new test for `uint256[]` matching and decoding of dynamic arrays. Additionally, included an ERC20 ABI JSON file for testing purposes.
  • Loading branch information
wkennedy committed Aug 4, 2024
1 parent ab2f598 commit 762a52b
Show file tree
Hide file tree
Showing 4 changed files with 353 additions and 5 deletions.
38 changes: 35 additions & 3 deletions src/main/java/com/github/wkennedy/abi/SolidityType.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public abstract class SolidityType {
private final static int Int32Size = 32;
protected String name;

/**
* Constructs a SolidityType with the specified name.
*
* @param name the name of the SolidityType
*/
public SolidityType(String name) {
this.name = name;
}
Expand All @@ -42,12 +47,20 @@ public String getCanonicalName() {
return getName();
}

/**
* TypeFactory is an abstract class that represents a factory for creating SolidityTypes.
* It provides methods for getting a SolidityType based on a typeName and checking if a typeName matches any of the factory's types.
*/
public static abstract class TypeFactory {
public abstract SolidityType getType(String typeName);

public abstract boolean matches(String typeName);
}

/**
* BoolTypeFactory is a factory for creating BoolType objects and matching the input string with the "bool" type.
* It extends the TypeFactory class.
*/
public static class BoolTypeFactory extends TypeFactory {
@Override
public SolidityType getType(String typeName) {
Expand All @@ -60,6 +73,9 @@ public boolean matches(String typeName) {
}
}

/**
* IntTypeFactory is a subclass of TypeFactory that provides methods for creating IntType objects and checking if a typeName matches any of the factory's types.
*/
public static class IntTypeFactory extends TypeFactory {
@Override
public SolidityType getType(String typeName) {
Expand All @@ -72,6 +88,11 @@ public boolean matches(String typeName) {
}
}

/**
* ArrayTypeFactory is a concrete implementation of the TypeFactory abstract class.
* It is responsible for creating ArrayType instances based on the given typeName
* and checking if a typeName matches any of the factory's types.
*/
public static class ArrayTypeFactory extends TypeFactory {
@Override
public SolidityType getType(String typeName) {
Expand All @@ -84,6 +105,10 @@ public boolean matches(String typeName) {
}
}

/**
* UnsignedIntTypeFactory is a factory class that creates UnsignedIntType objects.
* It extends the abstract class TypeFactory.
*/
public static class UnsignedIntTypeFactory extends TypeFactory {
@Override
public SolidityType getType(String typeName) {
Expand All @@ -92,10 +117,13 @@ public SolidityType getType(String typeName) {

@Override
public boolean matches(String typeName) {
return typeName.startsWith("uint") || typeName.startsWith("wad") || typeName.startsWith("ray");
return (typeName.startsWith("uint") || typeName.startsWith("wad") || typeName.startsWith("ray")) && !typeName.endsWith("]");
}
}

/**
* AddressTypeFactory is a factory class for creating SolidityType instances of address type.
*/
public static class AddressTypeFactory extends TypeFactory {
@Override
public SolidityType getType(String typeName) {
Expand All @@ -108,6 +136,10 @@ public boolean matches(String typeName) {
}
}

/**
* StringTypeFactory is a concrete implementation of the TypeFactory abstract class.
* It represents a factory for creating StringType instances.
*/
public static class StringTypeFactory extends TypeFactory {
@Override
public SolidityType getType(String typeName) {
Expand Down Expand Up @@ -169,8 +201,8 @@ public boolean matches(String typeName) {
}

private static final List<TypeFactory> typeFactories = Arrays.asList(
new BoolTypeFactory(), new IntTypeFactory(), new StringTypeFactory(), new Bytes32TypeFactory(), new BytesTypeFactory(),
new FunctionTypeFactory(), new AddressTypeFactory(), new UnsignedIntTypeFactory(), new ArrayTypeFactory(), new TupleTypeFactory()
new ArrayTypeFactory(), new BoolTypeFactory(), new IntTypeFactory(), new StringTypeFactory(), new Bytes32TypeFactory(), new BytesTypeFactory(),
new FunctionTypeFactory(), new AddressTypeFactory(), new UnsignedIntTypeFactory(), new TupleTypeFactory()
);

@JsonCreator
Expand Down
8 changes: 8 additions & 0 deletions src/test/java/com/github/wkennedy/abi/SolidityTypeTest.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.github.wkennedy.abi;

import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -153,4 +154,11 @@ public void testDecodeBytesTypeInvalidInput() {
byte[] encoded = new byte[0];
assertThrows(RuntimeException.class, () -> bytesType.decode(encoded, 100));
}

@Test
public void testUintArrayMatches() {
SolidityType type = SolidityType.getType("uint256[]");
assertTrue(type.isDynamicType());
}

}
17 changes: 15 additions & 2 deletions src/test/java/com/github/wkennedy/abi/entry/AbiFunctionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ void decodeResult_withEmptyByteArgument_shouldReturnEmptyList() {

@Test
public void testUniswapFunctionDecode() throws IOException, DecoderException {
String abiJson = new String(Files.readAllBytes(Paths.get("./src/test/resources/uniswap_abi.json")));
String abiJson = new String(Files.readAllBytes(Paths.get("./src/test/resources/erc20_abi.json")));
Decoder decoder = new Decoder();
decoder.addAbi("0x731847de5b19b26039f283826ae5218ac7e070ed1b7fff689c2253a3035d8bd6", abiJson);
Predicate<AbiFunction> exists = fn -> fn.name.equals("getAmountsOut");
Predicate<AbiFunction> exists = fn -> fn.name.equals("decimals");
Optional<AbiFunction> function = decoder.getAbis().get("0x731847de5b19b26039f283826ae5218ac7e070ed1b7fff689c2253a3035d8bd6").findFunction(exists);
byte[] bytes = org.apache.commons.codec.binary.Hex.decodeHex("0x0000000000000000000000000000000000000000000000000000000000000012".replace(HEX_PREFIX, "").toUpperCase());
List<?> decode = function.get().decodeResult(bytes);
Expand Down Expand Up @@ -113,4 +113,17 @@ void encode_withEmptyArgs_shouldReturnEmptyBytes() {
assertNotNull(result);
assertEquals(4, result.length);
}

@Test
void decode_functionResult_DynamicArray() {
String functionResult = "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000001e3503a1bb2d22";
AbiFunction testedAbiFunction = new AbiFunction(
true, "test", Collections.emptyList(), Collections.singletonList(new AbiParam(false, "param", new SolidityType.DynamicArrayType("uint256[]"))), true);
List<?> objects = testedAbiFunction.decodeResult(functionResult);
assertNotNull(objects);
assertEquals(1, objects.size());
Object[] amounts = (Object[]) objects.getFirst();
assertEquals(BigInteger.valueOf(100000000000000000L), amounts[0]);
assertEquals(BigInteger.valueOf(8502539015892258L), amounts[1]);
}
}
Loading

0 comments on commit 762a52b

Please sign in to comment.