Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle ABI V2 nested array case #1726

Closed
rach-id opened this issue Jun 12, 2022 · 32 comments
Closed

Handle ABI V2 nested array case #1726

rach-id opened this issue Jun 12, 2022 · 32 comments
Labels
bug A bug in behaviour or functionality

Comments

@rach-id
Copy link
Contributor

rach-id commented Jun 12, 2022

As discussed by @874341642 in #1321 (comment), the following ABI:

{
  "outputs": [
    {
      "components": [
        {
          "internalType": "uint256[]",
          "name": "blocks",
          "type": "uint256[]"
        }
      ],
      "internalType": "struct Mining.PlayerWork",
      "name": "",
      "type": "tuple"
    }
  ]
}

Returns the following exception:

public PlayerWork(DynamicArray< Uint256 > blocks) {
super(blocks);
}
Error info
java.lang.UnsupportedOperationException: Array types must be wrapped in a TypeReference
@rach-id rach-id added the bug A bug in behaviour or functionality label Jun 12, 2022
@rach-id rach-id assigned rach-id and unassigned rach-id Jun 12, 2022
@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

The first step to solve this would be to add this ABI definition to the AbiV2TestFixture.java file, such as:
https://github.com/web3j/web3j/blob/a9cc039ad3da656049e8b7bc004656ba1113b40d/abi/src/test/java/org/web3j/abi/AbiV2TestFixture.java#L183-L228

This would add the above to the test cases we can work with.
Then, add type tests under: web3j/abi/src/test/java/org/web3j/abi.
You can be inspired by this PR: #1321.

After finishing with this, and having encoding decoding tests working. Move on to the codegen module. And, following the same above way, add the tests and see where the problem is happening.

This PR should be your reference for changes #1321 as it is doing almost the same thing to implement support for struct arrays support.

@874341642
Copy link

874341642 commented Jun 12, 2022

{
	"inputs": [{
		"internalType": "uint256",
		"name": "tokenId",
		"type": "uint256"
	}],
	"name": "getPlayerWork",
	"outputs": [{
		"components": [{
			"internalType": "address",
			"name": "workType",
			"type": "address"
		}, {
			"internalType": "address",
			"name": "owner",
			"type": "address"
		}, {
			"internalType": "uint256[]",
			"name": "blocks",
			"type": "uint256[]"
		}],
		"internalType": "struct Mining.PlayerWork",
		"name": "",
		"type": "tuple"
	}],
	"stateMutability": "view",
	"type": "function"
}

I still don't understand what you mean, how to write this output parameter?

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

If I understand right, you will write your own Foo, call it something I don't know, that has the same ABI definition as the one above and test against it.

@874341642
Copy link

874341642 commented Jun 12, 2022

If I understand right, you will write your own Foo, call it something I don't know, that has the same ABI definition as the one above and test against it.

  public static class PlayerWork extends DynamicStruct {
        public String workType;

        public String careerAddr;

        public BigInteger startTime;

        public BigInteger endTime;

        public String owner;

        public List<BigInteger> blocks;

        public BigInteger beginTime;

        public PlayerWork(String workType, String careerAddr, BigInteger startTime, BigInteger endTime, String owner, List<BigInteger> blocks, BigInteger beginTime) {
            super(new org.web3j.abi.datatypes.Address(160, workType), 
                    new org.web3j.abi.datatypes.Address(160, careerAddr), 
                    new org.web3j.abi.datatypes.generated.Uint256(startTime), 
                    new org.web3j.abi.datatypes.generated.Uint256(endTime), 
                    new org.web3j.abi.datatypes.Address(160, owner), 
                    new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Uint256>(
                            org.web3j.abi.datatypes.generated.Uint256.class,
                            org.web3j.abi.Utils.typeMap(blocks, org.web3j.abi.datatypes.generated.Uint256.class)), 
                    new org.web3j.abi.datatypes.generated.Uint256(beginTime));
            this.workType = workType;
            this.careerAddr = careerAddr;
            this.startTime = startTime;
            this.endTime = endTime;
            this.owner = owner;
            this.blocks = blocks;
            this.beginTime = beginTime;
        }

        public PlayerWork(Address workType, Address careerAddr, Uint256 startTime, Uint256 endTime, Address owner, DynamicArray<Uint256> blocks, Uint256 beginTime) {
            super(workType, careerAddr, startTime, endTime, owner, blocks, beginTime);
            this.workType = workType.getValue();
            this.careerAddr = careerAddr.getValue();
            this.startTime = startTime.getValue();
            this.endTime = endTime.getValue();
            this.owner = owner.getValue();
            this.blocks = blocks.getValue().stream().map(v -> v.getValue()).collect(Collectors.toList());
            this.beginTime = beginTime.getValue();
        }
    }

This will throw an exception

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

How did you generate this?

@874341642
Copy link

874341642 commented Jun 12, 2022

How did you generate this?

 public static void generateClass(String abiFile,String generateFile){
	        String[] args = Arrays.asList(
	                "-a",abiFile,	             
	                "-p","cn.contract",
	                "-o",generateFile
	        ).toArray(new String[0]);
	        Stream.of(args).forEach(System.out::println);
	        SolidityFunctionWrapperGenerator.main(args);
	    }

@874341642
Copy link

How did you generate this?

org.web3j codegen 4.9.2

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

And what do you run to get the exception?

@874341642
Copy link

image

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

Thanks, but what are you running here?

@874341642
Copy link

image

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

Can you provide a command or some code that throw this exception?

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

Also, you can use permalink to share lines of code. And, code blocks for code blocks for a more readable discussion.

@874341642
Copy link

Also, you can use permalink to share lines of code. And, code blocks for code blocks for a more readable discussion.

As long as the tuple contains an array of output parameters, an exception will occur.

@874341642
Copy link

874341642 commented Jun 12, 2022

[{
	"inputs": [{
		"internalType": "uint256",
		"name": "tokenId",
		"type": "uint256"
	}],
	"name": "getPlayerWork",
	"outputs": [{
		"components": [{
			"internalType": "address",
			"name": "workType",
			"type": "address"
		}, {
			"internalType": "address",
			"name": "careerAddr",
			"type": "address"
		}, {
			"internalType": "uint256",
			"name": "startTime",
			"type": "uint256"
		}, {
			"internalType": "uint256",
			"name": "endTime",
			"type": "uint256"
		}, {
			"internalType": "address",
			"name": "owner",
			"type": "address"
		}, {
			"internalType": "uint256[]",
			"name": "blocks",
			"type": "uint256[]"
		}, {
			"internalType": "uint256",
			"name": "beginTime",
			"type": "uint256"
		}],
		"internalType": "struct Mining.PlayerWork",
		"name": "",
		"type": "tuple"
	}],
	"stateMutability": "view",
	"type": "function"
}]

This is the abi file and the generated java file is wrong.

@874341642
Copy link

874341642 commented Jun 12, 2022

package cn.contract;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.DynamicArray;
import org.web3j.abi.datatypes.DynamicStruct;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.RemoteFunctionCall;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;

/**
 * <p>Auto generated code.
 * <p><strong>Do not modify!</strong>
 * <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
 * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the 
 * <a href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
 *
 * <p>Generated with web3j version 4.9.2.
 */
@SuppressWarnings("rawtypes")
public class Market extends Contract {
    public static final String BINARY = "Bin file was not provided";

    public static final String FUNC_GETPLAYERWORK = "getPlayerWork";

    @Deprecated
    protected Market(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    protected Market(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
        super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
    }

    @Deprecated
    protected Market(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }

    protected Market(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
        super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
    }

    public RemoteFunctionCall<PlayerWork> getPlayerWork(BigInteger tokenId) {
        final Function function = new Function(FUNC_GETPLAYERWORK, 
                Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(tokenId)), 
                Arrays.<TypeReference<?>>asList(new TypeReference<PlayerWork>() {}));
        return executeRemoteCallSingleValueReturn(function, PlayerWork.class);
    }

    @Deprecated
    public static Market load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        return new Market(contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    @Deprecated
    public static Market load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        return new Market(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }

    public static Market load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
        return new Market(contractAddress, web3j, credentials, contractGasProvider);
    }

    public static Market load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
        return new Market(contractAddress, web3j, transactionManager, contractGasProvider);
    }

    public static class PlayerWork extends DynamicStruct {
        public String workType;

        public String careerAddr;

        public BigInteger startTime;

        public BigInteger endTime;

        public String owner;

        public List<BigInteger> blocks;

        public BigInteger beginTime;

        public PlayerWork(String workType, String careerAddr, BigInteger startTime, BigInteger endTime, String owner, List<BigInteger> blocks, BigInteger beginTime) {
            super(new org.web3j.abi.datatypes.Address(160, workType), 
                    new org.web3j.abi.datatypes.Address(160, careerAddr), 
                    new org.web3j.abi.datatypes.generated.Uint256(startTime), 
                    new org.web3j.abi.datatypes.generated.Uint256(endTime), 
                    new org.web3j.abi.datatypes.Address(160, owner), 
                    new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Uint256>(
                            org.web3j.abi.datatypes.generated.Uint256.class,
                            org.web3j.abi.Utils.typeMap(blocks, org.web3j.abi.datatypes.generated.Uint256.class)), 
                    new org.web3j.abi.datatypes.generated.Uint256(beginTime));
            this.workType = workType;
            this.careerAddr = careerAddr;
            this.startTime = startTime;
            this.endTime = endTime;
            this.owner = owner;
            this.blocks = blocks;
            this.beginTime = beginTime;
        }

        public PlayerWork(Address workType, Address careerAddr, Uint256 startTime, Uint256 endTime, Address owner, DynamicArray<Uint256> blocks, Uint256 beginTime) {
            super(workType, careerAddr, startTime, endTime, owner, blocks, beginTime);
            this.workType = workType.getValue();
            this.careerAddr = careerAddr.getValue();
            this.startTime = startTime.getValue();
            this.endTime = endTime.getValue();
            this.owner = owner.getValue();
            this.blocks = blocks.getValue().stream().map(v -> v.getValue()).collect(Collectors.toList());
            this.beginTime = beginTime.getValue();
        }
    }
}

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

I agree, but we need to find a way to reproduce this to add tests for it and know what we need to implement.
Also, write a more refined issue following the templates.

Then, we can work on a fix and decide whether this is a bug or a missing feature.

@874341642
Copy link

I agree, but we need to find a way to reproduce this to add tests for it and know what we need to implement. Also, write a more refined issue following the templates.

Then, we can work on a fix and decide whether this is a bug or a missing feature.

Can you guys generate a java file based on the abi file I provided?

@rach-id
Copy link
Contributor Author

rach-id commented Jun 12, 2022

Can you try generating it, please? And let us know the exact steps to see that exception?

@874341642
Copy link

874341642 commented Jun 12, 2022

public BigInteger getPlayerWork(BigInteger tokenId) {
String functionName = "getPlayerWork";
List inputParameters = Arrays.asList(new Uint256(tokenId));
List<TypeReference<?>> outputParameters = Arrays.asList(new TypeReference< PlayerWork >() {
});
Function function = new Function(functionName, inputParameters, outputParameters);
String data = FunctionEncoder.encode(function);
Transaction transaction = Transaction.createEthCallTransaction(BaseConst.myAddress, BaseConst.miningv3, data);
try {
EthCall ethCall = web3jUtil.getWeb3j().ethCall(transaction, DefaultBlockParameterName.LATEST).send();
List results = FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters());
if (results != null && results.size() > 0) {
List result = (List) results.get(0).getValue();
if (result != null && result.size() == 7) {
return new BigInteger(result.get(6).getValue().toString());
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}

@874341642
Copy link

public static class PlayerWork extends DynamicStruct {
    public String workType;

    public String careerAddr;

    public BigInteger startTime;

    public BigInteger endTime;

    public String owner;

    public List<BigInteger> blocks;

    public BigInteger beginTime;

    public PlayerWork(String workType, String careerAddr, BigInteger startTime, BigInteger endTime, String owner, List<BigInteger> blocks, BigInteger beginTime) {
        super(new org.web3j.abi.datatypes.Address(160, workType), 
                new org.web3j.abi.datatypes.Address(160, careerAddr), 
                new org.web3j.abi.datatypes.generated.Uint256(startTime), 
                new org.web3j.abi.datatypes.generated.Uint256(endTime), 
                new org.web3j.abi.datatypes.Address(160, owner), 
                new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Uint256>(
                        org.web3j.abi.datatypes.generated.Uint256.class,
                        org.web3j.abi.Utils.typeMap(blocks, org.web3j.abi.datatypes.generated.Uint256.class)), 
                new org.web3j.abi.datatypes.generated.Uint256(beginTime));
        this.workType = workType;
        this.careerAddr = careerAddr;
        this.startTime = startTime;
        this.endTime = endTime;
        this.owner = owner;
        this.blocks = blocks;
        this.beginTime = beginTime;
    }

    public PlayerWork(Address workType, Address careerAddr, Uint256 startTime, Uint256 endTime, Address owner, DynamicArray<Uint256> blocks, Uint256 beginTime) {
        super(workType, careerAddr, startTime, endTime, owner, blocks, beginTime);
        this.workType = workType.getValue();
        this.careerAddr = careerAddr.getValue();
        this.startTime = startTime.getValue();
        this.endTime = endTime.getValue();
        this.owner = owner.getValue();
        this.blocks = blocks.getValue().stream().map(v -> v.getValue()).collect(Collectors.toList());
        this.beginTime = beginTime.getValue();
    }
}

}

@874341642
Copy link

Write these two classes to run.

@huangshun666
Copy link

使用 web3j1.4.1 生成java文件,支持Tuple类型

@mohamedelshami mohamedelshami pinned this issue Oct 21, 2022
@YangXianqi
Copy link

When the bug can be fixed?

@metaglobal
Copy link

metaglobal commented Feb 3, 2023

[
    {
        "inputs":[
            {
                "internalType":"address",
                "name":"account",
                "type":"address"
            }
        ],
        "name":"getStakingData",
        "outputs":[
            {
                "components":[
                    {
                        "internalType":"uint256",
                        "name":"totalStaked",
                        "type":"uint256"
                    },
                    {
                        "internalType":"uint256",
                        "name":"veTokenTotalSupply",
                        "type":"uint256"
                    },
                    {
                        "internalType":"uint256",
                        "name":"accountVeTokenBalance",
                        "type":"uint256"
                    },
                    {
                        "internalType":"uint256",
                        "name":"accountWithdrawableRewards",
                        "type":"uint256"
                    },
                    {
                        "internalType":"uint256",
                        "name":"accountWithdrawnRewards",
                        "type":"uint256"
                    },
                    {
                        "internalType":"uint256",
                        "name":"accountDepositTokenBalance",
                        "type":"uint256"
                    },
                    {
                        "internalType":"uint256",
                        "name":"accountDepositTokenAllowance",
                        "type":"uint256"
                    },
                    {
                        "components":[
                            {
                                "internalType":"uint256",
                                "name":"amount",
                                "type":"uint256"
                            },
                            {
                                "internalType":"uint32",
                                "name":"lockedAt",
                                "type":"uint32"
                            },
                            {
                                "internalType":"uint32",
                                "name":"lockDuration",
                                "type":"uint32"
                            }
                        ],
                        "internalType":"struct SharesTimeLock.Lock[]",
                        "name":"accountLocks",
                        "type":"tuple[]"
                    }
                ],
                "internalType":"struct SharesTimeLock.StakingData",
                "name":"data",
                "type":"tuple"
            }
        ],
        "stateMutability":"view",
        "type":"function"
    }
]

This complex structure also cannot be parsed

@wangweiai
Copy link

When the bug can be fixed?

@georgelombardi97
Copy link

Is there any fix for this? Hotfix or dirty way around it?

@NickSneo
Copy link
Contributor

@874341642 @metaglobal Can you provide the Solidity code for the respective abi provided?
We are looking into this issue

@NickSneo
Copy link
Contributor

NickSneo commented May 29, 2023

{
	"inputs": [{
		"internalType": "uint256",
		"name": "tokenId",
		"type": "uint256"
	}],
	"name": "getPlayerWork",
	"outputs": [{
		"components": [{
			"internalType": "address",
			"name": "workType",
			"type": "address"
		}, {
			"internalType": "address",
			"name": "owner",
			"type": "address"
		}, {
			"internalType": "uint256[]",
			"name": "blocks",
			"type": "uint256[]"
		}],
		"internalType": "struct Mining.PlayerWork",
		"name": "",
		"type": "tuple"
	}],
	"stateMutability": "view",
	"type": "function"
}

I still don't understand what you mean, how to write this output parameter?

I tried to create a solidity code in respect to your provided abi -

pragma solidity ^0.8.0;

contract Random {
    struct PlayerWork {
        address workType;
        address owner;
        uint256[] blocks;
    }

    function getPlayerWork(uint256 tokenId) public view returns (PlayerWork[] memory) {
        PlayerWork[] memory works = new PlayerWork[](1);
        
        works[0].workType = address(0x123);
        works[0].owner = address(0x456);
        works[0].blocks = new uint256[](3);
        works[0].blocks[0] = 1;
        works[0].blocks[1] = 2;
        works[0].blocks[2] = 3;

        return works;
    }
}

Then using web3j only I compiled and generated java wrappers.

The abi generated (look at the difference, instead of tuple this one has tuple[]) -

{
    "inputs": [
        {
            "internalType": "uint256",
            "name": "tokenId",
            "type": "uint256"
        }
    ],
    "name": "getPlayerWork",
    "outputs": [
        {
            "components": [
                {
                    "internalType": "address",
                    "name": "workType",
                    "type": "address"
                },
                {
                    "internalType": "address",
                    "name": "owner",
                    "type": "address"
                },
                {
                    "internalType": "uint256[]",
                    "name": "blocks",
                    "type": "uint256[]"
                }
            ],
            "internalType": "struct Random.PlayerWork[]",
            "name": "",
            "type": "tuple[]"
        }
    ],
    "stateMutability": "view",
    "type": "function"
}

Seems it is working fine for me, can you please check?

@NickSneo
Copy link
Contributor

NickSneo commented Jun 6, 2023

Closing this ticket as everything seems working fine with latest web3j 4.10.0, if someone faces any issue kindly reopen so we can discuss.

@NickSneo NickSneo closed this as completed Jun 6, 2023
@tantma
Copy link

tantma commented Jun 24, 2023

Thanks @NickSneo

I had a similar situation when the result returned with address[]

Solidity Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract PeopleContract {
    enum Gender {Male, Female}

    struct Person {
        uint256 id;
        string name;
        Gender gender;
        address[] acquaintances; //
    }
    
    Person[] public people;

    function addPerson(uint256 id, string memory name, Gender gender, address[] memory acquaintances) public {
        Person memory newPerson = Person(id, name, gender, acquaintances);
        people.push(newPerson);
    }

    function getPersonById(uint256 id) public view returns (Person memory) {
        for (uint256 i = 0; i < people.length; i++) {
            if (people[i].id == id) {
                return people[i];
            }
        }
        revert("Person not found");
    }

    function getAllPeople() public view returns (Person[] memory) {
        return people;
    }
}

java code

package com.novatti.ethereum;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.DynamicArray;
import org.web3j.abi.datatypes.DynamicStruct;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint8;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.RemoteFunctionCall;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tuples.generated.Tuple3;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;

/**
 * <p>Auto generated code.
 * <p><strong>Do not modify!</strong>
 * <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
 * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the 
 * <a href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
 *
 * <p>Generated with web3j version 1.4.2.
 */
@SuppressWarnings("rawtypes")
public class People extends Contract {
    public static final String BINARY = "";

    public static final String FUNC_ADDPERSON = "addPerson";

    public static final String FUNC_GETALLPEOPLE = "getAllPeople";

    public static final String FUNC_GETPERSONBYID = "getPersonById";

    public static final String FUNC_PEOPLE = "people";

    @Deprecated
    protected People(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    protected People(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
        super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
    }

    @Deprecated
    protected People(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }

    protected People(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
        super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
    }

    public RemoteFunctionCall<TransactionReceipt> addPerson(BigInteger id, String name, BigInteger gender, List<String> acquaintances) {
        final Function function = new Function(
                FUNC_ADDPERSON, 
                Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(id), 
                new org.web3j.abi.datatypes.Utf8String(name), 
                new org.web3j.abi.datatypes.generated.Uint8(gender), 
                new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.Address>(
                        org.web3j.abi.datatypes.Address.class,
                        org.web3j.abi.Utils.typeMap(acquaintances, org.web3j.abi.datatypes.Address.class))), 
                Collections.<TypeReference<?>>emptyList());
        return executeRemoteCallTransaction(function);
    }

    public RemoteFunctionCall<List> getAllPeople() {
        final Function function = new Function(FUNC_GETALLPEOPLE, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<Person>>() {}));
        return new RemoteFunctionCall<List>(function,
                new Callable<List>() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public List call() throws Exception {
                        List<Type> result = (List<Type>) executeCallSingleValueReturn(function, List.class);
                        return convertToNative(result);
                    }
                });
    }

    public RemoteFunctionCall<Person> getPersonById(BigInteger id) {
        final Function function = new Function(FUNC_GETPERSONBYID, 
                Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(id)), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Person>() {}));
        return executeRemoteCallSingleValueReturn(function, Person.class);
    }

    public RemoteFunctionCall<Tuple3<BigInteger, String, BigInteger>> people(BigInteger param0) {
        final Function function = new Function(FUNC_PEOPLE, 
                Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(param0)), 
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}, new TypeReference<Utf8String>() {}, new TypeReference<Uint8>() {}));
        return new RemoteFunctionCall<Tuple3<BigInteger, String, BigInteger>>(function,
                new Callable<Tuple3<BigInteger, String, BigInteger>>() {
                    @Override
                    public Tuple3<BigInteger, String, BigInteger> call() throws Exception {
                        List<Type> results = executeCallMultipleValueReturn(function);
                        return new Tuple3<BigInteger, String, BigInteger>(
                                (BigInteger) results.get(0).getValue(), 
                                (String) results.get(1).getValue(), 
                                (BigInteger) results.get(2).getValue());
                    }
                });
    }

    @Deprecated
    public static People load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        return new People(contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    @Deprecated
    public static People load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        return new People(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }

    public static People load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
        return new People(contractAddress, web3j, credentials, contractGasProvider);
    }

    public static People load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
        return new People(contractAddress, web3j, transactionManager, contractGasProvider);
    }

    public static RemoteCall<People> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
        return deployRemoteCall(People.class, web3j, credentials, contractGasProvider, BINARY, "");
    }

    @Deprecated
    public static RemoteCall<People> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        return deployRemoteCall(People.class, web3j, credentials, gasPrice, gasLimit, BINARY, "");
    }

    public static RemoteCall<People> deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
        return deployRemoteCall(People.class, web3j, transactionManager, contractGasProvider, BINARY, "");
    }

    @Deprecated
    public static RemoteCall<People> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        return deployRemoteCall(People.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, "");
    }

    public static class Person extends DynamicStruct {
        public BigInteger id;

        public String name;

        public BigInteger gender;

        public List<String> acquaintances;

        public Person(BigInteger id, String name, BigInteger gender, List<String> acquaintances) {
            super(new org.web3j.abi.datatypes.generated.Uint256(id), 
                    new org.web3j.abi.datatypes.Utf8String(name), 
                    new org.web3j.abi.datatypes.generated.Uint8(gender), 
                    new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.Address>(
                            org.web3j.abi.datatypes.Address.class,
                            org.web3j.abi.Utils.typeMap(acquaintances, org.web3j.abi.datatypes.Address.class)));
            this.id = id;
            this.name = name;
            this.gender = gender;
            this.acquaintances = acquaintances;
        }

        public Person(Uint256 id, Utf8String name, Uint8 gender, DynamicArray<Address> acquaintances) {
            super(id, name, gender, acquaintances);
            this.id = id.getValue();
            this.name = name.getValue();
            this.gender = gender.getValue();
            this.acquaintances = acquaintances.getValue().stream().map(v -> v.getValue()).collect(Collectors.toList());
        }
    }
}

the error i got

10:33:29.064 [main] DEBUG org.web3j.protocol.http.HttpService -- {"jsonrpc":"2.0","id":0,"result":"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000374616e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000aab484cf3c7d5da91afdafef88928c10077b4010"}
10:33:29.064 [main] DEBUG org.web3j.protocol.http.HttpService -- <-- END HTTP (614-byte body)

java.lang.NullPointerException: Cannot invoke "java.lang.reflect.Type.getTypeName()" because "type" is null

	at org.web3j.abi.Utils.getTypeName(Utils.java:286)
	at org.web3j.abi.TypeReference.getClassType(TypeReference.java:94)
	at org.web3j.abi.Utils.getParameterizedTypeFromArray(Utils.java:182)
	at org.web3j.abi.TypeDecoder.decodeArrayElements(TypeDecoder.java:638)
	at org.web3j.abi.TypeDecoder.decodeDynamicArray(TypeDecoder.java:442)
	at org.web3j.abi.TypeDecoder.decodeDynamicParameterFromStruct(TypeDecoder.java:569)
	at org.web3j.abi.TypeDecoder.decodeDynamicStructElements(TypeDecoder.java:522)
	at org.web3j.abi.TypeDecoder.decodeDynamicStruct(TypeDecoder.java:458)
	at org.web3j.abi.DefaultFunctionReturnDecoder.build(DefaultFunctionReturnDecoder.java:94)
	at org.web3j.abi.DefaultFunctionReturnDecoder.decodeFunctionResult(DefaultFunctionReturnDecoder.java:52)
	at org.web3j.abi.FunctionReturnDecoder.decode(FunctionReturnDecoder.java:57)
	at org.web3j.tx.Contract.executeCall(Contract.java:313)
	at org.web3j.tx.Contract.executeCallSingleValueReturn(Contract.java:324)
	at org.web3j.tx.Contract.executeCallSingleValueReturn(Contract.java:335)
	at org.web3j.tx.Contract.lambda$executeRemoteCallSingleValueReturn$1(Contract.java:457)
	at org.web3j.protocol.core.RemoteCall.send(RemoteCall.java:42)

@lzgh0102
Copy link
Contributor

Add an annotation on the dynamicArray parameter in the constructor may helps:

public Person(Uint256 id, Utf8String name, Uint8 gender, @Parameterized(type = Address.class) DynamicArray<Address> acquaintances) {
            super(id, name, gender, acquaintances);
            this.id = id.getValue();
            this.name = name.getValue();
            this.gender = gender.getValue();
            this.acquaintances = acquaintances.getValue().stream().map(v -> v.getValue()).collect(Collectors.toList());
        }

@parameterized(type = Address.class)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A bug in behaviour or functionality
Projects
None yet
Development

No branches or pull requests

10 participants