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

Solidity高权限调用方案:如何限制仅质押合约可调用NFT铸造方法

解决方案

完全不需要给用户分配临时高权限,该方案风险极高,极易出现权限漏删、重入攻击等问题导致NFT被恶意增发,采用合约代理调用的方案即可完全匹配你的业务需求,同时满足安全要求,调整步骤如下:

  • 第一步:配置NFT合约权限
    你现有的NFT合约CONTRACT_ROLE仅需要授予质押合约StakingExample的地址即可,不需要给任何普通用户分配该角色。
    操作方法:NFT合约部署完成后,由持有DEFAULT_ADMIN_ROLE的账号(一般为部署者)执行一次授权操作:
    nftContract.grantRole(keccak256("CONTRACT_ROLE"), 你的StakingExample合约地址);
    
    完成授权后,只有StakingExample合约可以调用NFT合约的mintItem方法,从根源上杜绝普通用户直接调用铸造接口的可能。
  • 第二步:调整质押合约的claimNFT方法权限
    你现有代码里claimNFT方法加了onlyRole(CONTRACT_ROLE)修饰符,该配置错误,这个方法本身就是给完成质押的普通用户调用的,应该移除该修饰符,替换为自定义的资格校验逻辑即可。
  • 第三步:跑通业务流程
    1. 用户前端调用someStakingFunction完成质押,质押合约内标记该用户存在未领取的NFT资格
    2. 用户前端调用claimNFT方法,质押合约校验用户确实有未领取的NFT后,主动调用NFT合约的mintItem方法将NFT铸造给调用用户
      全程普通用户不需要持有NFT合约的任何高权限,所有铸造请求都由质押合约发起,完全符合权限控制要求。

调整后的核心代码示例

质押合约修改后代码

// SPDX-License-Identifier: unlicensed
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface INFTExample {
    function mintItem(address _to, string memory _tokenURI) external returns (uint256);
}

contract StakingExample is AccessControl {
    using SafeERC20 for IERC20;

    IERC20 public immutable stakingToken;
    INFTExample public immutable nftContract;
    // 记录用户NFT领取状态
    mapping(address => bool) public userHasUnclaimedNFT;

    constructor(address _stakingToken, address _nftContract) {
        stakingToken = IERC20(_stakingToken);
        nftContract = INFTExample(_nftContract);
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    function someStakingFunction(uint256 _stakeAmount) external {
        // 原有质押逻辑
        stakingToken.safeTransferFrom(msg.sender, address(this), _stakeAmount);
        // 标记用户有可领取NFT
        userHasUnclaimedNFT[msg.sender] = true;
    }

    // 移除了onlyRole(CONTRACT_ROLE)修饰符
    function claimNFT(string memory _tokenURI) external {
        require(userHasUnclaimedNFT[msg.sender], "No available NFT to claim");
        // 先标记为已领取,避免重入攻击
        userHasUnclaimedNFT[msg.sender] = false;
        // 质押合约发起铸造调用,权限校验通过
        nftContract.mintItem(msg.sender, _tokenURI);
    }
}

临时赋权方案说明(不推荐生产使用)

如果坚持要使用临时赋权方案,需要严格控制权限生效周期,调用铸造方法前后分别做赋权和删除权限的操作,该方案Gas成本更高,且存在逻辑漏洞风险:

function claimNFT(string memory _tokenURI) external {
    require(userHasUnclaimedNFT[msg.sender], "No available NFT to claim");
    userHasUnclaimedNFT[msg.sender] = false;
    // 临时给用户赋权
    nftContract.grantRole(keccak256("CONTRACT_ROLE"), msg.sender);
    // 调用铸造方法
    nftContract.mintItem(msg.sender, _tokenURI);
    // 立刻删除用户权限
    nftContract.revokeRole(keccak256("CONTRACT_ROLE"), msg.sender);
}

内容的提问来源于stack exchange,提问作者LuxuryWaffles

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 14:15:06