Solidity无法向JS传递struct数组,循环请求仅返回第一条request如何解决?
问题原因
- 最常见的触发原因是
getRequestsCount()返回的结果是字符串类型的数值(以太坊RPC接口返回整数默认会封装为字符串或BN对象,避免大数精度损失),直接传入Array()构造函数时会被识别为单个元素而非数组长度,最终生成的数组长度仅为1,因此只会执行一次索引为0的请求,只能拿到第一条数据。 - 若确认计数的类型为数字,第二种可能是所使用的web3/ethers库旧版本存在并行调用参数复用的bug,或者节点对并行的call请求处理存在异常。
- 第三种可能是Solidity侧公开
requests数组的自动生成getter存在兼容性问题:当struct包含动态长度类型(string、bytes、动态数组)时,低版本solc编译器生成的ABI无法被JS侧的web3库正确解析多组并行调用。
解决方案
方案1:修正计数类型,优先排查类型问题
先将请求计数强制转为数字类型再构造数组,修改后的代码如下:
const address = context.params.id; const campaign = Campaign(address); // 把返回的计数转为数字 const requestCount = Number(await campaign.methods.getRequestsCount().call()); const donatorsCount = await campaign.methods.donatorsCount().call(); console.log("requests.count", requestCount); const requests = await Promise.all( Array(requestCount) .fill() .map((element, index) => { return campaign.methods.requests(index).call(); }) ); console.log("requests", requests);
方案2:改为串行调用排除并行请求异常
如果类型修正后还是存在问题,可以改为串行逐个拉取请求,排查是否为并行调用的兼容性问题:
const address = context.params.id; const campaign = Campaign(address); const requestCount = Number(await campaign.methods.getRequestsCount().call()); const donatorsCount = await campaign.methods.donatorsCount().call(); console.log("requests.count", requestCount); const requests = []; for(let i = 0; i < requestCount; i++) { const req = await campaign.methods.requests(i).call(); requests.push(req); } console.log("requests", requests);
方案3:Solidity侧新增批量查询接口(推荐)
目前Solidity 0.8及以上版本已经支持直接返回struct数组给外部调用者,无需在JS侧逐个拉取,效率更高也不会出现索引异常问题,可在合约中新增如下方法:
// 替换为你自己的Request结构体定义 struct Request { address recipient; uint256 amount; string description; bool completed; uint256 approvalCount; } Request[] public requests; function getAllRequests() external view returns(Request[] memory) { return requests; }
部署更新合约后,JS侧直接调用该接口即可拿到完整的请求数组:
const requests = await campaign.methods.getAllRequests().call();
内容的提问来源于stack exchange,提问作者user16763075
相关产品推荐
相关产品推荐

