Solidity multidimensional array as return
Opened this issue · 3 comments
Bug_title
Impossible to call Solidity function that returns 2d array from contract
Steps To Reproduce
Simple solidity function:
function get() view external returns (uint128[][] memory) {...}
Generate wrapper, or construct ethCall manually.
Expected behavior
Function should be called successfully and return result
Actual behavior
Exception occurred
java.lang.UnsupportedOperationException: Unable to access parameterized type org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Uint128>>
at org.web3j.abi.TypeDecoder.decodeArrayElements(TypeDecoder.java:694)
at org.web3j.abi.TypeDecoder.decodeDynamicArray(TypeDecoder.java:442)
at org.web3j.abi.DefaultFunctionReturnDecoder.build(DefaultFunctionReturnDecoder.java:100)
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.executeCallMultipleValueReturn(Contract.java:357)
Environment
Web3j version 4.9.8
Additional context
So generated code is using
new TypeReference<DynamicArray<DynamicArray<Uint128>>>() {}
as output value which seems like not really working.
Is there any workaround? When constructing a call manually (as opposed to generating wrapper), what should I set for Types to 'Output parameters' to make it work?
I've just encountered another issue with multi-dimensional arrays as return value, but with the opposite generated code.
For a function defined as
function getStateOfRewards(address _rewardOwner) external view returns (RewardState[][] memory rewardStates);
the generated wrapper was:
final Function function = new Function(FUNC_GETSTATEOFREWARDS,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(160, _rewardOwner)),
Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<RewardState>>() {}));
that didn't work because of the missing nested array. Changing it to:
final Function function = new Function(FUNC_GETSTATEOFREWARDS,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(160, _rewardOwner)),
Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<DynamicArray<RewardState>>>() {}));
made it working.
Tested with today 4.12.0-SNAPSHOT.
The current getTypeAsString
for DynamicArray
only supports DynamicStruct
as a nested type. I just created another class extending DynamicArray and overrode it so it also supported DynamicArray
as a nested type, which worked for me.
override fun getTypeAsString(): String {
val type = if (value.isEmpty()) {
if (StructType::class.java.isAssignableFrom(this.componentType)) {
Utils.getStructType(this.componentType)
} else {
AbiTypes.getTypeAString(this.componentType)
}
} else if (StructType::class.java.isAssignableFrom((value[0] as Type<*>).javaClass) || (DynamicArray::class.java.isAssignableFrom((value[0] as Type<*>).javaClass))) {
(value[0] as Type<*>).typeAsString
} else {
AbiTypes.getTypeAString(this.componentType)
}
return "$type[]"
}