Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/org/arkecosystem/crypto/utils/AbiBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public AbiBase(String abiFilePath) throws IOException {
this.abi = (List<Map<String, Object>>) abiJson.get("abi");
}

protected String[] getArrayComponents(String type) {
protected static String[] getArrayComponents(String type) {
Pattern pattern = Pattern.compile("^(.*)\\[(\\d*)\\]$");
Matcher matcher = pattern.matcher(type);
if (matcher.find()) {
Expand Down
20 changes: 10 additions & 10 deletions src/main/java/org/arkecosystem/crypto/utils/AbiDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private List<Object> decodeAbiParameters(List<Map<String, Object>> params, Strin
return values;
}

private Object[] decodeParameter(byte[] bytes, int offset, Map<String, Object> param)
private static Object[] decodeParameter(byte[] bytes, int offset, Map<String, Object> param)
throws Exception {
String type = (String) param.get("type");
String[] arrayComponents = getArrayComponents(type);
Expand Down Expand Up @@ -108,7 +108,7 @@ private Object[] decodeParameter(byte[] bytes, int offset, Map<String, Object> p
}
}

private Object[] decodeAddress(byte[] bytes, int offset) {
public static Object[] decodeAddress(byte[] bytes, int offset) {
byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32);
byte[] addressBytes = Arrays.copyOfRange(data, 12, 32);
String address = "0x" + Numeric.toHexStringNoPrefix(addressBytes);
Expand All @@ -117,14 +117,14 @@ private Object[] decodeAddress(byte[] bytes, int offset) {
return new Object[] {address, 32};
}

private Object[] decodeBool(byte[] bytes, int offset) {
public static Object[] decodeBool(byte[] bytes, int offset) {
byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32);
boolean value = new BigInteger(data).compareTo(BigInteger.ZERO) != 0;

return new Object[] {value, 32};
}

private Object[] decodeNumber(byte[] bytes, int offset, int bits, boolean signed) {
public static Object[] decodeNumber(byte[] bytes, int offset, int bits, boolean signed) {
byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32);
BigInteger value = new BigInteger(1, data);
if (signed && value.testBit(bits - 1)) {
Expand All @@ -134,7 +134,7 @@ private Object[] decodeNumber(byte[] bytes, int offset, int bits, boolean signed
return new Object[] {value.toString(), 32};
}

private Object[] decodeString(byte[] bytes, int offset) {
public static Object[] decodeString(byte[] bytes, int offset) {
int dataOffset = readUInt(bytes, offset).intValue();
int stringOffset = offset + dataOffset;
int length = readUInt(bytes, stringOffset).intValue();
Expand All @@ -145,7 +145,7 @@ private Object[] decodeString(byte[] bytes, int offset) {
return new Object[] {value, 32};
}

private Object[] decodeDynamicBytes(byte[] bytes, int offset) {
public static Object[] decodeDynamicBytes(byte[] bytes, int offset) {
int dataOffset = readUInt(bytes, offset).intValue();
int bytesOffset = offset + dataOffset;
int length = readUInt(bytes, bytesOffset).intValue();
Expand All @@ -155,14 +155,14 @@ private Object[] decodeDynamicBytes(byte[] bytes, int offset) {
return new Object[] {value, 32};
}

private Object[] decodeFixedBytes(byte[] bytes, int offset, int size) {
public static Object[] decodeFixedBytes(byte[] bytes, int offset, int size) {
byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32);
String value = "0x" + Numeric.toHexStringNoPrefix(Arrays.copyOfRange(data, 0, size));

return new Object[] {value, 32};
}

private Object[] decodeArray(
public static Object[] decodeArray(
byte[] bytes, int offset, Map<String, Object> param, Integer length) throws Exception {
String baseType = (String) param.get("type");
Map<String, Object> elementType = new HashMap<>(param);
Expand Down Expand Up @@ -192,7 +192,7 @@ private Object[] decodeArray(
return new Object[] {values, 32};
}

private Object[] decodeTuple(byte[] bytes, int offset, Map<String, Object> param)
public static Object[] decodeTuple(byte[] bytes, int offset, Map<String, Object> param)
throws Exception {
List<Map<String, Object>> components = (List<Map<String, Object>>) param.get("components");
Map<String, Object> values = new LinkedHashMap<>();
Expand All @@ -210,7 +210,7 @@ private Object[] decodeTuple(byte[] bytes, int offset, Map<String, Object> param
return new Object[] {values, 32};
}

private BigInteger readUInt(byte[] bytes, int offset) {
public static BigInteger readUInt(byte[] bytes, int offset) {
byte[] data = Arrays.copyOfRange(bytes, offset, offset + 32);
return new BigInteger(1, data);
}
Expand Down
159 changes: 159 additions & 0 deletions src/test/java/org/arkecosystem/crypto/utils/AbiDecoderTest.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.arkecosystem.crypto.utils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.web3j.utils.Numeric;

class AbiDecoderTest {

Expand All @@ -18,6 +21,162 @@ void setUp() throws Exception {
decoder = new AbiDecoder();
}

@Test
void it_should_expose_decode_address_as_static() {
byte[] bytes =
Numeric.hexStringToByteArray(
"000000000000000000000000512f366d524157bcf734546eb29a6d687b762255");
Object[] result = AbiDecoder.decodeAddress(bytes, 0);

assertEquals("0x512F366D524157BcF734546eB29a6d687B762255", result[0]);
assertEquals(32, result[1]);
}

@Test
void it_should_expose_decode_bool_as_static() {
byte[] truthy =
Numeric.hexStringToByteArray(
"0000000000000000000000000000000000000000000000000000000000000001");
byte[] falsy =
Numeric.hexStringToByteArray(
"0000000000000000000000000000000000000000000000000000000000000000");

assertEquals(true, AbiDecoder.decodeBool(truthy, 0)[0]);
assertEquals(false, AbiDecoder.decodeBool(falsy, 0)[0]);
}

@Test
void it_should_expose_decode_number_as_static() {
byte[] bytes =
Numeric.hexStringToByteArray(
"00000000000000000000000000000000000000000000000000000000000000ff");

assertEquals("255", AbiDecoder.decodeNumber(bytes, 0, 256, false)[0]);
}

@Test
void it_should_expose_read_uint_as_static() {
byte[] bytes =
Numeric.hexStringToByteArray(
"0000000000000000000000000000000000000000000000000000000000000020");

assertEquals(BigInteger.valueOf(32), AbiDecoder.readUInt(bytes, 0));
}

@Test
void it_should_expose_decode_signed_negative_number_as_static() {
byte[] bytes =
Numeric.hexStringToByteArray(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

assertEquals("-1", AbiDecoder.decodeNumber(bytes, 0, 256, true)[0]);
}

@Test
void it_should_expose_decode_string_as_static() {
byte[] bytes =
Numeric.hexStringToByteArray(
"0000000000000000000000000000000000000000000000000000000000000020"
+ "000000000000000000000000000000000000000000000000000000000000000b"
+ "68656c6c6f20776f726c64000000000000000000000000000000000000000000");

Object[] result = AbiDecoder.decodeString(bytes, 0);

assertEquals("hello world", result[0]);
assertEquals(32, result[1]);
}

@Test
void it_should_expose_decode_dynamic_bytes_as_static() {
byte[] bytes =
Numeric.hexStringToByteArray(
"0000000000000000000000000000000000000000000000000000000000000020"
+ "0000000000000000000000000000000000000000000000000000000000000004"
+ "deadbeef00000000000000000000000000000000000000000000000000000000");

Object[] result = AbiDecoder.decodeDynamicBytes(bytes, 0);

assertEquals("0xdeadbeef", result[0]);
assertEquals(32, result[1]);
}

@Test
void it_should_expose_decode_fixed_bytes_as_static() {
byte[] bytes =
Numeric.hexStringToByteArray(
"deadbeef00000000000000000000000000000000000000000000000000000000");

Object[] result = AbiDecoder.decodeFixedBytes(bytes, 0, 4);

assertEquals("0xdeadbeef", result[0]);
assertEquals(32, result[1]);
}

@Test
void it_should_expose_decode_fixed_array_as_static() throws Exception {
byte[] bytes =
Numeric.hexStringToByteArray(
"0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000002"
+ "0000000000000000000000000000000000000000000000000000000000000003");
Map<String, Object> param = new HashMap<>();
param.put("type", "uint256");

Object[] result = AbiDecoder.decodeArray(bytes, 0, param, 3);

assertEquals(Arrays.asList("1", "2", "3"), result[0]);
}

@Test
void it_should_expose_decode_dynamic_array_as_static() throws Exception {
byte[] bytes =
Numeric.hexStringToByteArray(
"0000000000000000000000000000000000000000000000000000000000000020"
+ "0000000000000000000000000000000000000000000000000000000000000003"
+ "000000000000000000000000000000000000000000000000000000000000000a"
+ "0000000000000000000000000000000000000000000000000000000000000014"
+ "000000000000000000000000000000000000000000000000000000000000001e");
Map<String, Object> param = new HashMap<>();
param.put("type", "uint256");

Object[] result = AbiDecoder.decodeArray(bytes, 0, param, null);

assertEquals(Arrays.asList("10", "20", "30"), result[0]);
}

@Test
void it_should_expose_decode_static_tuple_as_static() throws Exception {
byte[] bytes =
Numeric.hexStringToByteArray(
"000000000000000000000000000000000000000000000000000000000000002a"
+ "000000000000000000000000512f366d524157bcf734546eb29a6d687b762255");
Map<String, Object> uintComponent = new HashMap<>();
uintComponent.put("type", "uint256");
uintComponent.put("name", "amount");
Map<String, Object> addrComponent = new HashMap<>();
addrComponent.put("type", "address");
addrComponent.put("name", "recipient");
Map<String, Object> param = new HashMap<>();
param.put("components", Arrays.asList(uintComponent, addrComponent));

Object[] result = AbiDecoder.decodeTuple(bytes, 0, param);

Map<String, Object> tuple = (Map<String, Object>) result[0];
assertEquals("42", tuple.get("amount"));
assertEquals("0x512F366D524157BcF734546eB29a6d687B762255", tuple.get("recipient"));
}

@Test
void it_should_throw_when_function_selector_is_not_found() {
String unknownSelector = "deadbeef";

Exception thrown =
assertThrows(
Exception.class, () -> decoder.decodeFunctionData("0x" + unknownSelector));

assertEquals("Function selector not found in ABI: " + unknownSelector, thrown.getMessage());
}

@Test
void it_should_decode_vote_payload() throws Exception {
String functionName = "vote";
Expand Down
Loading