如何通过其他合约调用EIP-2535的diamondCut?移除函数时调用失败
问题排查与解决方案
核心问题:错误使用delegatecall
你的代码用delegatecall调用钻石合约的diamondCut,但delegatecall的执行上下文是当前合约(ExecuteDiamondCuts),而非目标钻石合约:
- 钻石合约的
diamondCut通常会校验调用者权限(比如是否为合约owner),但delegatecall会让这个校验针对ExecuteDiamondCuts的存储,而非钻石合约的,直接触发权限验证失败。 - 即便权限没问题,
delegatecall会尝试在当前合约的存储中执行钻石合约逻辑,破坏存储结构,必然导致回滚。
修复方式:将delegatecall替换为普通call,同时修正abi.encodeWithSignature中的结构体类型声明(需完整写出FacetCut的结构体类型):
(bool success,) = CONTRACT_ADDRESS.call( abi.encodeWithSignature( "diamondCut((address,uint8,bytes4[])[],address,bytes)", _facetCuts, address(0), "" ) ); require(success, "Diamond cut failed");
其他潜在问题点
- 权限验证:确保
ExecuteDiamondCuts合约已被钻石合约授权调用diamondCut(比如由钻石owner给它授权,或钻石的权限规则允许其调用)。 - 函数签名校验:确认
functionsToRemove中的签名与目标facet中的完全一致,比如uint与uint256是别名,但尽量保持写法统一避免潜在问题。 - Facet地址有效性:在循环中添加检查,确保获取到的facet地址不为空,避免无效地址触发回滚:
require(facetWithFn != address(0), "Function not found in any facet");
修复后的完整代码示例
contract ExecuteDiamondCuts { address constant CONTRACT_ADDRESS = 0xblahblah; // dummy address string[] functionsToRemove; constructor() { functionsToRemove.push("exampleFunctionA(address)"); functionsToRemove.push("exampleFunctionB(uint)"); functionsToRemove.push("exampleFunctionC()"); } function cutSomeDiamonds() external { IDiamondLoupe _loupe = IDiamondLoupe(CONTRACT_ADDRESS); IDiamondCut.FacetCut[] memory _facetCuts = new IDiamondCut.FacetCut[](functionsToRemove.length); for (uint i=0; i<functionsToRemove.length; i++) { bytes4 selector = bytes4(keccak256(bytes(functionsToRemove[i]))); address facetWithFn = _loupe.facetAddress(selector); require(facetWithFn != address(0), "Function not found"); bytes4[] memory _fnSelectors = new bytes4[](1); _fnSelectors[0] = selector; IDiamondCut.FacetCut memory _facetCut = IDiamondCut.FacetCut({ facetAddress: facetWithFn, action: IDiamondCut.FacetCutAction.Remove, functionSelectors: _fnSelectors }); _facetCuts[i] = _facetCut; } (bool success,) = CONTRACT_ADDRESS.call( abi.encodeWithSignature( "diamondCut((address,uint8,bytes4[])[],address,bytes)", _facetCuts, address(0), "" ) ); require(success, "Diamond cut failed"); } }
内容的提问来源于stack exchange,提问作者pildra
相关产品推荐
相关产品推荐

