Week19- 11.14

Day1 Java-Solidity-类型映射

基本类型

The basic types in solidity such as the uint256, bytes, byte32[] can be shown in the following.

Solidity Type web3j.abi type conflux-Java-sdk Type Example
uintxxx Uintxxx BigInteger new Uint256(new BigInteger("111"))
address Address Address(cfx:xxxx) new org.web3j.abi.Address(new Address("xxxxx"))
bool Bool Boolean new Bool(true)
string Utf8String String new Utf8String("heyman")
bytesxx Bytesxx byte[] new Bytes32("111".getBytes())
bytes DynamicBytes byte[] new DynamicBytes("111".getBytes())
Static Array (address[2]) StaticArray2 new StaticArray2<>(org.web3j.abi.datatypes.Address.class, xxx)
Dynamic Array (address[]) DynamicArray new DynamicArray<>(org.web3j.abi.datatypes.Address.class, xxx)

结构体

以下举了一个例子

struct Foo {
    Address[] addresses;
}
    public static class BasicAddressesStruct extends DynamicStruct {
        public List<org.web3j.abi.datatypes.Address> addr;

        public BasicAddressesStruct(List<org.web3j.abi.datatypes.Address> addr) {
            super(new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.Address>(addr));
            this.addr = addr;
        }

        public BasicAddressesStruct(DynamicArray<org.web3j.abi.datatypes.Address> addr) {
            super(addr);
            this.addr = addr.getValue();
        }
    }

需要注意的是,若结构体中包含动态类型,如bytes[], string等,创建的类需要继承DynamicStruct,否则,需要继承StaticStruct

其余结构其例子

Day2 Decode响应

当我们查询合约时,返回的是一串hexstring。 本节开始将告诉大家如何将这一串hexstring转为相应的类型

单个基本类型

contractCall.callAndGet(Utf8String.class,"text", node, key)

多个基本类型

以java-sdk的posRegister的内置合约的getVotes方法为例

用一个TupleDecoder去对hexstring进行decode

        BigInteger[] res = new BigInteger[2]; 
        
        String rawData = this.call("getVotes", new Bytes32(Numeric.hexStringToByteArray(identifier))).sendAndGet();
        rawData = Numeric.cleanHexPrefix(rawData);    
        TupleDecoder decoder = new TupleDecoder(rawData);
        res[0] = decoder.nextUint256();
        res[1] = decoder.nextUint256();

decoder还支持address和bytes之间的decode,具体可见

或者通过web3j的FunctionReturnDecoder进行decode

        String rawData = this.call("getVotes", new Bytes32(Numeric.hexStringToByteArray(identifier))).sendAndGet();

	List outputParameters = new ArrayList<TypeReference<Type>>();
	outputParameters.add(new TypeReference<Uint256>() {});
	outputParameters.add(new TypeReference<Uint256>() {});

	List<Type> list = FunctionReturnDecoder.decode(rawData, outputParameters);
 

Day3 结构体decode响应

结构体的解析方法跟多个基本类型的第二种方法一致,不同的是需要自定义一个结构体类。 以下举个例子

struct price{
   Uint256 base
   Uint256 premium
}
    public static class Price extends StaticStruct {
        public BigInteger base;
        public BigInteger premium;

        public Price(Uint256 base, Uint256 premium) {
            super(base,premium);
            this.base = base.getValue();
            this.premium = premium.getValue();
        }
    }

需要注意的是,由于该price结构体内部的成员均为Uint256,为定长类型,所以该结构体类继承的是StaticStruct。若其中包含不定长类型,则需要继承DynamicStruct

static void decodeStruct() {
  String structData = "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000008f03f1a3f10c05e7cccf75c1fd10168e06659be7000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000";
  TypeReference ts = new TypeReference<Price>() {};
  List<Type> list = DefaultFunctionReturnDecoder.decode(structData, Arrays.asList(ts));
  System.out.println(((Price)list.get(0)).base);
}

Day4 结构体参数调用问题反馈

Web3j对结构体中嵌套数组的类型支持有问题,当结构体中包含了DynamicArray类型或是StaticArray类型时,用以下语句去调用合约会报错

//struct BasicAddresses{
//	address[] addr;
//}

String hash = account.call(opt, contract_address, "write", basicAddresses);

通过测试,我们发现,该问题主要是由MethodSignature不同导致的,该signature的不同会导致methodid的不同,最后会导致encode产生的rawdata不用。

为了解决该问题,本教程提供了一个折衷的办法,大致的思路就是自己手动去生成一个methodid。由拼接思路可得,我们提供一个正确的methodid,再与其他的参数进行拼接,得到正确的rawdata。

具体的为重写web3j的DefaultFunctionEncoder.java中的encodeFunction:

    @Override
    public String encodeFunction(final Function function, String signature) {
      String methodId = buildMethodId(signature);
        final StringBuilder result = new StringBuilder(methodId);

        return encodeParameters(parameters, result);
    }
	// methodSignature的格式如下:方法名(参数)。其中,参数类型为结构体的表示为(xxx)。如:writeBasicAddressesStruct((address[]))

    public static String buildMethodId(final String methodSignature) {
        final byte[] input = methodSignature.getBytes();
        final byte[] hash = Hash.sha3(input);
        return Numeric.toHexString(hash).substring(0, 10);
    }

其中,Function可以由以下例子进行构造:

        Function f = new Function(
                "writeBasicAddressesStruct",
                Arrays.asList(struct),
                Collections.emptyList()
        );

再得到rawdata后就可以通过以下例子进行调用:

String hash = acc.callWithData(new Address(addr), t);

Day5 web3j合约交互问题补充

因为web3j中存在的问题,目前web3j中的codegen会产生错误的代码以及用getlog的方式去获取日志会报 java.lang.UnsupportedOperationException: Array types must be wrapped in a TypeReference的错误

具体的问题描述可以查看https://github.com/web3j/web3j/issues/1726

该问题也是源于对数组的支持不太好,需要等待web3j修复该bug


Revision #14
Created 14 November 2022 09:01:01 by Xianqi
Updated 18 November 2022 12:45:49 by Xianqi