web3j
web3j copied to clipboard
Generated wrapper around struct containing a `address[]` member can't be instantiated
Steps To Reproduce
- Write a struct containing an array of
addresses and a function to access such an object:
struct Task
{
TaskStatusEnum status;
bytes32 dealid;
uint256 idx;
uint256 timeref;
uint256 contributionDeadline;
uint256 revealDeadline;
uint256 finalDeadline;
bytes32 consensusValue;
uint256 revealCounter;
uint256 winnerCounter;
address[] contributors; <-----Note the `address[]` type
bytes32 resultDigest;
bytes results;
uint256 resultsTimestamp;
bytes resultsCallback; // Expansion - result separation
}
function viewTask(bytes32 _taskid)
external view override returns (IexecLibCore_v5.Task memory)
{
return m_tasks[_taskid];
}
- Generate the Java wrapper using Web3j. It should give the following constructor and function:
public Task(Uint8 status,
Bytes32 dealid,
Uint256 idx,
Uint256 timeref,
Uint256 contributionDeadline,
Uint256 revealDeadline,
Uint256 finalDeadline,
Bytes32 consensusValue,
Uint256 revealCounter,
Uint256 winnerCounter,
DynamicArray<Address> contributors, <---- Note the parameter declaration
Bytes32 resultDigest,
DynamicBytes results,
Uint256 resultsTimestamp,
DynamicBytes resultsCallback) {
super(status, dealid, idx, timeref, contributionDeadline, revealDeadline, finalDeadline, consensusValue, revealCounter, winnerCounter, contributors, resultDigest, results, resultsTimestamp, resultsCallback);
this.status = status.getValue();
this.dealid = dealid.getValue();
this.idx = idx.getValue();
this.timeref = timeref.getValue();
this.contributionDeadline = contributionDeadline.getValue();
this.revealDeadline = revealDeadline.getValue();
this.finalDeadline = finalDeadline.getValue();
this.consensusValue = consensusValue.getValue();
this.revealCounter = revealCounter.getValue();
this.winnerCounter = winnerCounter.getValue();
this.contributors = contributors.getValue().stream().map(v -> v.getValue()).collect(Collectors.toList());
this.resultDigest = resultDigest.getValue();
this.results = results.getValue();
this.resultsTimestamp = resultsTimestamp.getValue();
this.resultsCallback = resultsCallback.getValue();
}
public RemoteFunctionCall<Task> viewTask(byte[] param0) {
final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_VIEWTASK,
Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Bytes32(param0)),
Arrays.<TypeReference<?>>asList(new TypeReference<Task>() {
}));
return executeRemoteCallSingleValueReturn(function, Task.class);
}
- Call the method to retrieve an instance of this object... :boom:
2024-01-16 09:50:55.626 ERROR 1 --- [o-13300-exec-10] c.i.c.p.chain.IexecHubAbstractService : Failed to get ChainTask [chainTaskId:0x1f3e39ef6942fe708d8030e39c0bf737761f6f2c1064666f5f91dfea4f3fa9d5]
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)
at com.iexec.commons.poco.chain.IexecHubAbstractService.getChainTask(IexecHubAbstractService.java:802)
at com.iexec.commons.poco.chain.IexecHubAbstractService.lambda$repeatGetChainTask$7(IexecHubAbstractService.java:791)
at net.jodah.failsafe.Functions.lambda$get$0(Functions.java:48)
at net.jodah.failsafe.RetryPolicyExecutor.lambda$supply$0(RetryPolicyExecutor.java:66)
at net.jodah.failsafe.Execution.executeSync(Execution.java:128)
at net.jodah.failsafe.FailsafeExecutor.call(FailsafeExecutor.java:379)
at net.jodah.failsafe.FailsafeExecutor.get(FailsafeExecutor.java:68)
at com.iexec.commons.poco.utils.Retryer.repeatCall(Retryer.java:63)
at com.iexec.commons.poco.chain.IexecHubAbstractService.repeatGetChainTask(IexecHubAbstractService.java:791)
at com.iexec.commons.poco.chain.IexecHubAbstractService.repeatGetTaskDescriptionFromChain(IexecHubAbstractService.java:1069)
at com.iexec.commons.poco.chain.IexecHubAbstractService.getTaskDescriptionFromChain(IexecHubAbstractService.java:1059)
at com.iexec.commons.poco.chain.IexecHubAbstractService.isTeeTask(IexecHubAbstractService.java:1093)
at com.iexec.sms.authorization.AuthorizationService.isAuthorizedOnExecutionWithDetailedIssue(AuthorizationService.java:64)
at com.iexec.sms.tee.TeeController.generateTeeSession(TeeController.java:143)
[... Java / Spring internal calls ...]
Expected behavior
The generated code should be able to build an instance without encountering a NPE.
Actual behavior
Web3j can't find the type for the contributors parameter. It knows the parameter is some sort of dynamic array, but can't find out the generics type because of type erasure. It misses some sort of information to find out the expect type of each contributor is String.
As far as I understand Web3j code, the constructor misses the @Parameterized annotation. Its signature should be the following:
public Task(Uint8 status,
Bytes32 dealid,
Uint256 idx,
Uint256 timeref,
Uint256 contributionDeadline,
Uint256 revealDeadline,
Uint256 finalDeadline,
Bytes32 consensusValue,
Uint256 revealCounter,
Uint256 winnerCounter,
@Parameterized(type = String.class) DynamicArray<Address> contributors, <---- Note the newly-added annotation
Bytes32 resultDigest,
DynamicBytes results,
Uint256 resultsTimestamp,
DynamicBytes resultsCallback)
In fact, if I manually add this annotation to the generated wrappers, then everything works fine! So I guess this annotation should be added during the wrappers generation.
Environment
- Web3j version: 4.9.7
- Java 11
Additional context
References: