hyperledger-web3j / web3j

Lightweight Java and Android library for integration with Ethereum clients
https://www.web3labs.com/web3j-sdk
Other
5.11k stars 1.68k forks source link

web3j generate solidity for structs #1422

Closed aleksvujic closed 2 years ago

aleksvujic commented 3 years ago

I have problems with generating contract wrapper code when smart contract uses ABIEncoderV2 and structs. I am using web3j CLI v1.4.1. I have the following demo smart contract:

// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

contract MyContract {

    struct MyStruct {
        address creator;
        string name;
    }

    constructor() public {

    }

    function getMethods() public pure returns (MyStruct[] memory methods) {
        return new MyStruct[](2);
    }
}

When I compile it with Solidity 0.6.12+commit.27d51765, it generates the following ABI:

[
    {
        "inputs": [],
        "stateMutability": "nonpayable",
        "type": "constructor"
    },
    {
        "inputs": [],
        "name": "getMethods",
        "outputs": [
            {
                "components": [
                    {
                        "internalType": "address",
                        "name": "creator",
                        "type": "address"
                    },
                    {
                        "internalType": "string",
                        "name": "name",
                        "type": "string"
                    }
                ],
                "internalType": "struct MyContract.MyStruct[]",
                "name": "methods",
                "type": "tuple[]"
            }
        ],
        "stateMutability": "pure",
        "type": "function"
    }
]

When I run web3j generate solidity -a MyContract.json -o ".\org\company" -p mypackage it generates the following Java file:

package mypackage;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.DynamicArray;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
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 1.4.1.
 */
@SuppressWarnings("rawtypes")
public class MyContract extends Contract {
    public static final String BINARY = "Bin file was not provided";

    public static final String FUNC_TESTFUNCTION = "testFunction";

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

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

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

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

    public RemoteFunctionCall<List> testFunction() {
        final Function function = new Function(FUNC_TESTFUNCTION, 
                Arrays.<Type>asList(), 
                Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<MyStruct>>() {})); // <------- ERROR HERE
        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);
                    }
                });
    }

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

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

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

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

However, it reports an error that MyStruct can't be resolved to a type. I read here that it should support structs. Quote:

From Web3j version 4.6.x onwards, the generator supports ABIv2 for contract compiled with Solidity compiler version 0.6.x. This means that you can have structs as input/output/event parameters in your smart contract.

Why is it not working?

YuboLong commented 3 years ago

I encountered same issue

aleksvujic commented 3 years ago

@YuboLong Did you manage to solve it?

qyzxcswbll commented 3 years ago

I encountered same issue

qyzxcswbll commented 3 years ago

I encountered same issue

use 4.84 solve

WorldDogs commented 3 years ago

I encountered same issue

use 4.84 solve

But the CLI does not have this version.

sergio11 commented 3 years ago

Hello @aleksvujic , I have been researching on this topic, it seems that the latest version of web3j has support for struct to some extent, that is, it is still not possible to return lists of structures as you have in the example that you show.

Furthermore, structures with addresses are not supported at this time either.

https://github.com/web3j/web3j/issues/1361#issuecomment-835760015

MDSADABWASIM commented 3 years ago

I encountered same issue

use 4.84 solve

But the CLI does not have this version.

He's talking about web3j version

rafiulahad commented 2 years ago

I have five structs used in different public functions in my solidity program. None of them contains address. web3j created static Java classes for just two of them. So I am getting errors for the other three. I could not figure out the pattern of usage of these structs in the solidity program for which web3j failed to generate the java classes. Is this a known issue? If needed I may be able to create a reproducible test case for it.

mudebbela commented 2 years ago

1 year Later. I am also getting this issue

I have 2 structs in my code.

`struct EmpExceStruct{ address addr; string name; string age; string sex; uint timestamp; uint calories; string exerciseType; string extra; } struct Employee { bool created; string name; string age; string sex; uint256 exerciseCount; mapping(uint => Exercise) et; uint etSize; // TODO connect to address of user

}

`

after generaing them with web3j, both structs have been brought in as Classes that do not exist. heres one of the functions that have been created in Java

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

`

here the exact script i used to generate the abi and bin. its includes the solc version since I use solc-select.

~/.solc-select/artifacts/solc-0.8.13 Health.sol --bin --abi --optimize -o ./generated/

EDIT: sorry its formatted so bad, I thought the add code button will format it nicely for me. Ah well. let me know if you need more information or indeed better formatted code. Im just a grad school student trying his best to finish a project he doesnt necessarily enjoy working on, lol.

mudebbela commented 2 years ago

Okay I found a workaround That im only now implementing. I think it has to do with how solidity never used to be able to return structs. Im gonna use the workaround found in this video and I am assuming it will solve the issue. Essentially it seems the wrapper generator isnt hype to the newer solidity functionality of allowing the return of structs. I hope they sort that out soon but whatever.

https://www.youtube.com/watch?v=QGjshWJjPPI

I figured this is the issue as the only problems im getting have to do with getters but not setters

andrii-kl commented 2 years ago

Was fixed in PR. https://github.com/web3j/web3j/pull/1673

ShoaibKakal commented 2 years ago

I'm still facing this issue, struct are not generate by web3j.