{"content":{"title":"如何判断目标合约是否实现了某个方法","body":"## 0x01 碰到的问题\r\n比如下面的代码：\r\n我们想从 store 合约里通过调用 getValue 方法来获取数据。但问题是，我们并不能确定 store 合约一定实现了 getValue 方法。很典型的一种情况是，我们需要将 StoreReader 不同的环境，不同的环境都有自己的 store 实现，一些比较老的 store 实例是没有 getValue 方法的。如果某个环境的 store 合约没有 getValue 实现，这个调用可能就会失败，并会导致其它相关联的业务操作失败。\r\n```\r\ncontract StoreReader {\r\n  address store;\r\n  function readStoreValue() external view returns (uint256) {\r\n    return StoreInterface(store).getValue();\r\n  }\r\n}\r\n```\r\n\r\n## 0x02 解决思路\r\n1. 升级 store 合约\r\n研究发现， store 合约是不可升级的，升级 store 合约，因为着要对这个合约进行整体替换。因为 store 里面存储的还有其它各种状态变量，替换合约要把这些状态变量的值一个不差的迁移过去，还是有一定风险的。\r\n\r\n 2. 不同的 StoreReader 实现\r\n我们可以尝试不同的环境实现不同的 StoreReader, 用不同的方式来实现 readStoreValue 这个方法。但问题是其它合约对 StoreReader 也有静态依赖，有些合约是继承自 StoreReader 的，改一发而动全身。\r\n\r\n3. staticcall\r\n最终想起了 staticcall 调用，这种调用当发生调用失败时并不会把交椅 revert，而是返回 bool 类型的状态，这样我们就能动态判断目标合约 store 是否实现 getValue 方法了。\r\n示例代码如下：\r\n```\r\n  function readStoreValue() external view returns (uint256) {\r\n     (bool success, bytes memory rtValue) = store.staticcall(abi.encodeWithSignature(\"getValue()\"));\r\n        if (success) {\r\n            return (abi.decode(rtValue, (uint256)));\r\n        } else {\r\n            // return another value\r\n        }\r\n  }\r\n```\r\n需要注意的是，staticcall 返回的数据都是 bytes 类型，我们需要使用 abi.decode 对bytes 数据进行进一步的解析。\r\n\r\n## 0x03 后记\r\n为啥不用 ERC165? 如果是全新的设计，是可以考虑 ERC165 的，现在是想着对现有代码做最小程度的改动和最大程度的兼容。"},"author":{"user":"https://learnblockchain.cn/people/29","address":null},"history":null,"timestamp":1677759555,"version":1}