如何实现仅允许指定外部合约修改本合约mapping状态变量?
实现方案
要实现Contract B专属更新Contract A内mapping的权限控制,核心是在Contract A的mapping更新逻辑入口加严格的调用源校验,仅放行预先指定的Contract B地址的调用,其余来源调用直接回滚交易。
- 部署阶段提前绑定双方地址,优先用不可篡改的变量存储授权地址,避免权限泄露
- Contract A的mapping更新方法不对外开放通用调用权限,入口第一行做调用方身份校验
- Contract B通过接口引用Contract A的实例,在自身业务逻辑触发时调用A的更新方法完成mapping修改
参考实现代码
以下示例基于Solidity 0.8.x版本编写,自带溢出检查与原生revert能力:
Contract A 代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract ContractA { // 目标mapping状态变量,可根据实际业务调整key、value的类型 mapping(address => uint256) public userData; // 存储唯一授权的Contract B地址,immutable修饰表示部署后不可篡改 address public immutable ALLOWED_CONTRACT_B; // 部署Contract A时传入已部署好的Contract B地址,完成权限绑定 constructor(address _contractBAddr) { require(_contractBAddr != address(0), "Invalid Contract B address"); ALLOWED_CONTRACT_B = _contractBAddr; } // 仅允许Contract B调用的mapping更新方法 function setUserData(address _user, uint256 _data) external { // 核心权限校验:调用方不是授权的Contract B直接回滚 if (msg.sender != ALLOWED_CONTRACT_B) { revert("Permission denied: only Contract B can update mapping"); } userData[_user] = _data; } }
Contract B 代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; // 定义Contract A的调用接口 interface IContractA { function setUserData(address _user, uint256 _data) external; } contract ContractB { IContractA public immutable contractARef; // 部署Contract B时传入已部署的Contract A地址 constructor(address _contractAAddr) { contractARef = IContractA(_contractAAddr); } // 业务方法:内部逻辑执行完成后触发Contract A的mapping更新 function executeBusinessLogic(address _targetUser, uint256 _newData) external { // 此处可编写Contract B自身的业务校验、逻辑处理代码 // ... // 调用Contract A的更新方法修改mapping contractARef.setUserData(_targetUser, _newData); } }
注意事项
- 不要给mapping添加其他无校验的公开修改入口,所有修改逻辑必须收敛到带权限校验的方法内
- 优先使用
immutable修饰授权地址变量,部署后无法篡改,从底层避免权限被恶意篡改的风险 - 部署顺序需注意:先部署Contract B拿到链上地址,再将该地址作为参数传入部署Contract A,避免地址填错导致权限失效
- 如果后续需要支持升级Contract B的逻辑,不要直接开放授权地址的任意修改权限,建议搭配时间锁、多签钱包管控权限修改操作,避免单点风险
内容的提问来源于stack exchange,提问作者Prince Singhal
相关产品推荐
相关产品推荐

