Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
vite.utils.abi.Abi Maven / Gradle / Ivy
package vite.utils.abi;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.collections4.Predicate;
import vite.utils.ByteUtil;
import vite.utils.HashUtil;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.fasterxml.jackson.annotation.JsonInclude.Include;
import static java.lang.String.format;
import static org.apache.commons.lang3.ArrayUtils.subarray;
import static org.apache.commons.lang3.StringUtils.join;
import static org.apache.commons.lang3.StringUtils.stripEnd;
public class Abi extends ArrayList {
private final static ObjectMapper DEFAULT_MAPPER = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
public static Abi fromJson(String json) {
try {
return DEFAULT_MAPPER.readValue(json, Abi.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String toJson() {
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public byte[] encodeFunction(String name, Object... args) {
Function f = findFunctionByName(name);
if (f == null) {
throw new RuntimeException("function not exist");
}
return f.encode(args);
}
public byte[] encodeConstructor(Object... args) {
Constructor c = find(Constructor.class, Entry.Type.constructor, object -> true);
if (c != null) {
return c.encode(args);
}
return null;
}
public byte[] encodeOffchain(String name, Object... args) {
Offchain f = findOffchainByName(name);
if (f == null) {
throw new RuntimeException("offchain not exist");
}
return f.encode(args);
}
public List> decodeConstructor(byte[] encoded) {
Constructor c = find(Constructor.class, Entry.Type.constructor, object -> true);
if (c != null) {
return Entry.Param.decodeList(c.inputs, encoded);
}
return null;
}
public List> decodeFunction(byte[] encoded) {
Predicate p = (v1) -> {
return Arrays.equals(v1.encodeSignature(), Abi.Function.extractSignature(encoded));
};
Abi.Function f = find(Function.class, Abi.Entry.Type.function, p);
if (f != null) {
return f.decode(encoded);
} else {
return null;
}
}
public List> decodeOffchainOutput(String name, byte[] encoded) {
Predicate p = (v1) -> {
return v1.name.equals(name);
};
Abi.Offchain f = find(Offchain.class, Abi.Entry.Type.offchain, p);
if (f != null) {
return f.decodeOutput(encoded);
} else {
return null;
}
}
public List> decode(byte[] data, byte[][] topics) {
if (topics == null || topics.length == 0) {
return null;
}
Predicate p = (v1) -> {
return Arrays.equals(v1.encodeSignature(), topics[0]);
};
Abi.Event e = find(Event.class, Abi.Entry.Type.event, p);
if (e != null) {
List result = new ArrayList<>(e.inputs.size());
byte[][] argTopics = e.anonymous ? topics : subarray(topics, 1, topics.length);
List> indexed = Entry.Param.decodeList(e.filteredInputs(true), ByteUtil.merge(argTopics));
List> notIndexed = Entry.Param.decodeList(e.filteredInputs(false), data);
for (Entry.Param input : e.inputs) {
result.add(input.indexed ? indexed.remove(0) : notIndexed.remove(0));
}
return result;
} else {
return null;
}
}
private T find(Class resultClass, final Abi.Entry.Type type, final Predicate searchPredicate) {
return (T) CollectionUtils.find(this, entry -> entry.type == type && searchPredicate.evaluate((T) entry));
}
public Function findFunctionByData(byte[] encoded) {
Predicate p = (v1) -> {
return Arrays.equals(v1.encodeSignature(), Abi.Function.extractSignature(encoded));
};
Abi.Function f = find(Function.class, Abi.Entry.Type.function, p);
if (f != null) {
return f;
} else {
return null;
}
}
public Function findFunctionByName(String name) {
Predicate p = (v1) -> {
return v1.name.equals(name);
};
Abi.Function f = find(Function.class, Abi.Entry.Type.function, p);
if (f != null) {
return f;
} else {
return null;
}
}
public Offchain findOffchainByName(String name) {
Predicate p = (v1) -> {
return v1.name.equals(name);
};
Abi.Offchain f = find(Offchain.class, Abi.Entry.Type.offchain, p);
if (f != null) {
return f;
} else {
return null;
}
}
@Override
public String toString() {
return toJson();
}
@JsonInclude(Include.NON_NULL)
public static abstract class Entry {
private HashUtil hashUtil;
public enum Type {
constructor,
function,
event,
fallback,
offchain
}
@JsonInclude(Include.NON_NULL)
public static class Param {
public Boolean indexed;
public String name;
public SolidityType type;
public static List> decodeList(List params, byte[] encoded) {
List result = new ArrayList<>(params.size());
int offset = 0;
for (Param param : params) {
Object decoded = param.type.isDynamicType()
? param.type.decode(encoded, SolidityType.IntType.decodeInt(encoded, offset).intValue())
: param.type.decode(encoded, offset);
result.add(decoded);
offset += param.type.getFixedSize();
}
return result;
}
@Override
public String toString() {
return format("%s%s%s", type.getCanonicalName(), (indexed != null && indexed) ? " indexed " : " ", name);
}
}
public final Boolean anonymous;
public final String name;
public final List inputs;
public final List outputs;
public final Type type;
public final Boolean payable;
public Entry(Boolean anonymous, String name, List inputs, List outputs, Type type, Boolean payable) {
this.anonymous = anonymous;
this.name = name;
this.inputs = inputs;
this.outputs = outputs;
this.type = type;
this.payable = payable;
}
public String formatSignature() {
StringBuilder paramsTypes = new StringBuilder();
for (Entry.Param param : inputs) {
paramsTypes.append(param.type.getCanonicalName()).append(",");
}
return format("%s(%s)", name, stripEnd(paramsTypes.toString(), ","));
}
public byte[] fingerprintSignature() {
return hashUtil.Hash256(formatSignature().getBytes());
}
public byte[] encodeSignature() {
return fingerprintSignature();
}
@JsonCreator
public static Entry create(@JsonProperty("anonymous") boolean anonymous,
@JsonProperty("name") String name,
@JsonProperty("inputs") List inputs,
@JsonProperty("outputs") List outputs,
@JsonProperty("type") Type type,
@JsonProperty(value = "payable", required = false, defaultValue = "false") Boolean payable) {
Entry result = null;
switch (type) {
case constructor:
result = new Constructor(inputs);
break;
case function:
case fallback:
result = new Function(name, inputs, payable);
break;
case event:
result = new Event(anonymous, name, inputs);
break;
case offchain:
result = new Offchain(name, inputs, outputs, payable);
break;
}
return result;
}
public byte[] encodeArguments(Object... args) {
if (args.length > inputs.size())
throw new RuntimeException("Too many arguments: " + args.length + " > " + inputs.size());
int staticSize = 0;
int dynamicCnt = 0;
// calculating static size and number of dynamic params
for (int i = 0; i < args.length; i++) {
SolidityType type = inputs.get(i).type;
if (type.isDynamicType()) {
dynamicCnt++;
}
staticSize += type.getFixedSize();
}
byte[][] bb = new byte[args.length + dynamicCnt][];
for (int curDynamicPtr = staticSize, curDynamicCnt = 0, i = 0; i < args.length; i++) {
SolidityType type = inputs.get(i).type;
if (type.isDynamicType()) {
byte[] dynBB = type.encode(args[i]);
bb[i] = SolidityType.IntType.encodeInt(curDynamicPtr);
bb[args.length + curDynamicCnt] = dynBB;
curDynamicCnt++;
curDynamicPtr += dynBB.length;
} else {
bb[i] = type.encode(args[i]);
}
}
return ByteUtil.merge(bb);
}
}
public static class Constructor extends Entry {
public Constructor(List inputs) {
super(null, "", inputs, null, Type.constructor, false);
}
public byte[] encode(Object... args) {
return encodeArguments(args);
}
public List> decode(byte[] encoded) {
return Param.decodeList(inputs, encoded);
}
public String formatSignature(String contractName) {
return format("function %s(%s)", contractName, join(inputs, ", "));
}
@Override
public String toString() {
return format("constructor (%s)", join(inputs, ", "));
}
}
public static class Offchain extends Entry {
private static final int ENCODED_SIGN_LENGTH = 4;
public Offchain(String name, List inputs, List outputs, Boolean payable) {
super(null, name, inputs, outputs, Type.offchain, payable);
}
public List> decodeOutput(byte[] encoded) {
return Param.decodeList(outputs, encoded);
}
public List> decode(byte[] encoded) {
return Param.decodeList(inputs, subarray(encoded, ENCODED_SIGN_LENGTH, encoded.length));
}
public byte[] encode(Object... args) {
return ByteUtil.merge(encodeSignature(), encodeArguments(args));
}
@Override
public byte[] encodeSignature() {
return extractSignature(super.encodeSignature());
}
public static byte[] extractSignature(byte[] data) {
return subarray(data, 0, ENCODED_SIGN_LENGTH);
}
@Override
public String toString() {
return format("getter %s(%s)", name, join(inputs, ", "));
}
}
public static class Function extends Entry {
private static final int ENCODED_SIGN_LENGTH = 4;
public Function(String name, List inputs, Boolean payable) {
super(null, name, inputs, null, Type.function, payable);
}
public List> decode(byte[] encoded) {
return Param.decodeList(inputs, subarray(encoded, ENCODED_SIGN_LENGTH, encoded.length));
}
public byte[] encode(Object... args) {
return ByteUtil.merge(encodeSignature(), encodeArguments(args));
}
@Override
public byte[] encodeSignature() {
return extractSignature(super.encodeSignature());
}
public static byte[] extractSignature(byte[] data) {
return subarray(data, 0, ENCODED_SIGN_LENGTH);
}
@Override
public String toString() {
return format("onMessage %s(%s)", name, join(inputs, ", "));
}
}
public static class Event extends Entry {
public Event(boolean anonymous, String name, List inputs) {
super(anonymous, name, inputs, null, Type.event, false);
}
public List> decode(byte[] data, byte[][] topics) {
List result = new ArrayList<>(inputs.size());
byte[][] argTopics = anonymous ? topics : subarray(topics, 1, topics.length);
List> indexed = Param.decodeList(filteredInputs(true), ByteUtil.merge(argTopics));
List> notIndexed = Param.decodeList(filteredInputs(false), data);
for (Param input : inputs) {
result.add(input.indexed ? indexed.remove(0) : notIndexed.remove(0));
}
return result;
}
private List filteredInputs(final boolean indexed) {
return ListUtils.select(inputs, param -> param.indexed == indexed);
}
@Override
public String toString() {
return format("event %s(%s)", name, join(inputs, ", "));
}
public static void main(String[] args) {
String contractAbi = "[\n" +
"\t{\"inputs\":[{\"name\":\"i\",\"type\":\"bool[]\"},{\"name\":\"s\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}," +
"\t{\"type\":\"offchain\",\"name\":\"GetData\",\"inputs\":[{\"name\":\"tokenId\",\"type\":\"tokenId\"},{\"name\":\"tokenName\",\"type\":\"string\"},{\"name\":\"tokenSymbol\",\"type\":\"string\"},{\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"name\":\"decimals\",\"type\":\"uint8\"}],\"outputs\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"index\",\"type\":\"uint16\"}]},\n" +
"\t{\"type\":\"function\",\"name\":\"Mintage\",\"inputs\":[{\"name\":\"tokenId\",\"type\":\"tokenId\"},{\"name\":\"tokenName\",\"type\":\"string\"},{\"name\":\"tokenSymbol\",\"type\":\"string\"},{\"name\":\"totalSupply\",\"type\":\"uint256\"},{\"name\":\"decimals\",\"type\":\"uint8\"}]},\n" +
"\t{\"type\":\"function\",\"name\":\"CancelPledge\",\"inputs\":[{\"name\":\"tokenId\",\"type\":\"tokenId\"}]}\n" +
"]";
Abi abi = Abi.fromJson(contractAbi);
byte[] targetData = ByteUtil.hexStringToBytes("46d0ce8b000000000000000000000000000000000000000000005649544520544f4b454e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a5669746520546f6b656e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045649544500000000000000000000000000000000000000000000000000000000");
byte[] encodedData = abi.encodeFunction("Mintage", "tti_5649544520544f4b454e6e40", "Vite Token", "VITE", BigInteger.valueOf(100l), BigInteger.valueOf(2l));
if (!Arrays.equals(encodedData, targetData)) {
System.err.println("encode failed");
System.err.println(ByteUtil.toHexString(targetData));
System.err.println(ByteUtil.toHexString(encodedData));
}
byte[] targetOffchainData = ByteUtil.hexStringToBytes("2e4553f4000000000000000000000000000000000000000000005649544520544f4b454e00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a5669746520546f6b656e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045649544500000000000000000000000000000000000000000000000000000000");
byte[] encodedOffchainData = abi.encodeOffchain("GetData", "tti_5649544520544f4b454e6e40", "Vite Token", "VITE", BigInteger.valueOf(100l), BigInteger.valueOf(2l));
if (!Arrays.equals(encodedOffchainData, targetOffchainData)) {
System.err.println("encode offchain failed");
System.err.println(ByteUtil.toHexString(targetOffchainData));
System.err.println(ByteUtil.toHexString(encodedOffchainData));
}
List> decodedOffchainOutput = abi.decodeOffchainOutput("GetData", ByteUtil.hexStringToBytes("0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000036162630000000000000000000000000000000000000000000000000000000000"));
if (decodedOffchainOutput.size() != 2 || !"abc".equals(decodedOffchainOutput.get(0)) || !new BigInteger("10").equals(decodedOffchainOutput.get(1))) {
System.err.println("decode offchain output failed");
System.err.println(JSON.toJSONString(decodedOffchainOutput));
}
byte[] encodedContractParams = abi.encodeConstructor("[true,false]", "abc");
byte[] targetEncodedContractParams = ByteUtil.hexStringToBytes("000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036162630000000000000000000000000000000000000000000000000000000000");
if (!Arrays.equals(encodedContractParams, targetEncodedContractParams)) {
System.err.println("encode constructor params failed");
System.err.println(ByteUtil.toHexString(encodedContractParams));
System.err.println(ByteUtil.toHexString(targetEncodedContractParams));
}
}
}
}