You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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))
}

问题根源

  1. 存储结构缺陷:当前合约用Rqst[msg.sender]存储请求,即员工创建的请求仅存于员工地址对应的数组中,经理地址对应的数组为空,调用getRequest访问索引时会触发数组越界回滚,导致RPC错误;
  2. 无关联接收者:现有结构未记录请求的接收者(经理),无法实现经理查看员工提交给自己的请求。

修复方案

修改 Solidity 合约

  1. 更新结构体:添加提交者和接收者地址字段,关联请求双方;
  2. 调整存储结构:用全局数组存储所有请求,同时通过映射维护每个用户的提交/接收请求ID列表;
  3. 重写核心函数:创建请求时同时关联提交者和接收者,提供按用户身份查询请求的方法。

修改后的合约代码示例:

// 定义包含双方地址的请求结构体
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 前端

  1. 创建请求:新增经理地址参数,调用修改后的合约方法:
const createRequest = async () => {
    // 需预先获取经理地址,可通过合约配置或前端输入
    const managerAddress = "0x..."; 
    await contract.methods.createRequest(managerAddress, 'pending', amount, description).send({ from: defaultAccount });
}
  1. 查询请求:根据登录用户身份调用对应的查询方法,经理登录时调用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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 00:18:24