Solidity实现收发双方数据共享及getRequest RPC报错排查
问题分析与修复方案
问题描述
开发员工向经理提交请求的系统时遇到以下问题:
- 员工通过MetaMask登录调用
createRequest创建请求后,经理登录调用getRequest时Web3返回「MetaMask - RPC Error: execution reverted」错误; - 经理自行创建的请求可以正常查询;
- 需要实现提交的请求数据同时对员工(提交者)和经理(接收者)可见。
现有代码
Solidity 合约代码
创建请求函数
function createRequest(Requests memory rqst) external{ Rqst[msg.sender].push(Requests({ status:rqst.status, amount:rqst.amount, description:rqst.description })); }
查询请求函数
function getRequest(uint requestIndex) external view returns (Requests memory){ Requests storage rqst = Rqst[msg.sender][requestIndex]; return rqst; }
React.js 前端代码
创建请求方法
const createRequest = async () => { await contract.methods.createRequest(['pending',amount, description]).send({ from: defaultAccount }); }
注:
defaultAccount为当前MetaMask登录的员工地址
查询请求方法(经理登录时调用)
const getRequests = async (contract, defaultAccount) => { const f = async () => { const len = await contract.methods.getRequestount().call({ from: defaultAccount }); console.log("len",len) let iterator = 0 let contain = [] while ( iterator <= len) { try { console.log("iterator",iterator) let o = await contract.methods.getRequest(String(iterator)).call({ from: defaultAccount }); if (o[0] !== '') { contain.push({ status: o['status'], amount: o['amount'], description: o['description'] }) } else { console.log('The index ' + iterator + ' is empty'); } } catch { console.log('Failed to get requests ' + iterator); } iterator++; } return contain } let c = await f() dispatch(setRequests(c)) }
问题根源
- 存储结构缺陷:当前合约用
Rqst[msg.sender]存储请求,即员工创建的请求仅存于员工地址对应的数组中,经理地址对应的数组为空,调用getRequest访问索引时会触发数组越界回滚,导致RPC错误; - 无关联接收者:现有结构未记录请求的接收者(经理),无法实现经理查看员工提交给自己的请求。
修复方案
修改 Solidity 合约
- 更新结构体:添加提交者和接收者地址字段,关联请求双方;
- 调整存储结构:用全局数组存储所有请求,同时通过映射维护每个用户的提交/接收请求ID列表;
- 重写核心函数:创建请求时同时关联提交者和接收者,提供按用户身份查询请求的方法。
修改后的合约代码示例:
// 定义包含双方地址的请求结构体 struct Requests { address submitter; address receiver; string status; uint256 amount; string description; } // 全局存储所有请求 Requests[] public allRequests; // 映射:用户地址 -> 自己提交的请求ID列表 mapping(address => uint256[]) public submittedRequests; // 映射:用户地址 -> 自己收到的请求ID列表 mapping(address => uint256[]) public receivedRequests; // 创建请求:需传入经理地址作为接收者 function createRequest(address receiver, string memory status, uint256 amount, string memory description) external { uint256 requestId = allRequests.length; allRequests.push(Requests({ submitter: msg.sender, receiver: receiver, status: status, amount: amount, description: description })); // 将请求ID添加到提交者和接收者的列表中 submittedRequests[msg.sender].push(requestId); receivedRequests[receiver].push(requestId); } // 查询当前用户提交的所有请求 function getSubmittedRequests() external view returns (Requests[] memory) { uint256[] memory requestIds = submittedRequests[msg.sender]; Requests[] memory result = new Requests[](requestIds.length); for (uint256 i = 0; i < requestIds.length; i++) { result[i] = allRequests[requestIds[i]]; } return result; } // 查询当前用户收到的所有请求 function getReceivedRequests() external view returns (Requests[] memory) { uint256[] memory requestIds = receivedRequests[msg.sender]; Requests[] memory result = new Requests[](requestIds.length); for (uint256 i = 0; i < requestIds.length; i++) { result[i] = allRequests[requestIds[i]]; } return result; }
适配 React.js 前端
- 创建请求:新增经理地址参数,调用修改后的合约方法:
const createRequest = async () => { // 需预先获取经理地址,可通过合约配置或前端输入 const managerAddress = "0x..."; await contract.methods.createRequest(managerAddress, 'pending', amount, description).send({ from: defaultAccount }); }
- 查询请求:根据登录用户身份调用对应的查询方法,经理登录时调用
getReceivedRequests:
const getRequests = async (contract, defaultAccount) => { try { // 经理登录时调用getReceivedRequests,员工登录时调用getSubmittedRequests const requests = await contract.methods.getReceivedRequests().call({ from: defaultAccount }); const formattedRequests = requests.map(r => ({ status: r.status, amount: r.amount, description: r.description, submitter: r.submitter // 可选,显示提交员工的地址 })); dispatch(setRequests(formattedRequests)); } catch (err) { console.error('获取请求失败:', err); } }
内容的提问来源于stack exchange,提问作者KD.S.T.
相关产品推荐
相关产品推荐

