【问题标题】:upgradable smart contracts with Solidity: interface vs library?使用 Solidity 可升级的智能合约:接口 vs 库?
【发布时间】:2018-04-25 12:27:55
【问题描述】:

在可升级智能合约的背景下,什么时候应该使用接口,什么时候应该使用库? 我阅读了几个类似的问题和博客文章,但没有一个给出直截了当的答案:

我了解在设计可升级性时要考虑的主要标准(除了安全性)是:

  • 模块化 - 可重用性和更易于维护
  • gas 限制 - 拆分大量合约,以便它们可以部署在多个交易中,以免达到 gas 限制
  • 升级成本 - 每个合约升级的成本是多少。在一个合同发生(小)变化后,需要重新部署哪些其他合同?
  • 执行成本 - 单独的合约可能会导致每次调用的 gas 开销。尽量保持低开销。

This Medium post 建议使用库来封装逻辑(例如,与“存储合约”交互时)并使用接口来解耦合约间通信。其他帖子提出了不同的技术。据我了解,库在部署之前已链接到合同,因此一旦合同发生更改,库就需要重新部署。为什么使用接口与存储合约交互不是更好?

下面我介绍了我目前看到的两种解决方案 - 一种使用库,一种使用接口。 (我想避免使用内联汇编的解决方案...)

库解决方案

StorageWithLib.sol

contract StorageWithLib {
    uint public data;

    function getData() public returns(uint) {
        return data;
    }
}

StorageLib.sol

import './StorageWithLib.sol';

library StorageLib {

    function getData(address _storageContract) public view returns(uint) {
        return StorageWithLib(_storageContract).getData();
    }
}

ActionWithLib.sol

import './StorageLib.sol';

contract ActionWithLib {
    using StorageLib for address;
    address public storageContract;

    function ActionWithLib(address _storageContract) public {
        storageContract = _storageContract;
    }

    function doSomething() public {
        uint data = storageContract.getData();
        // do something with data ...
    }
}

接口解决方案

IStorage.sol

contract IStorage {     
    function getData() public returns(uint);
}

StorageWithInterface.sol

import './IStorage.sol';

contract StorageWithInterface is IStorage {
    uint public data;

    function getData() public returns(uint) {
        return data;
    }
}

ActionWithInterface.sol

import './IStorage.sol';

contract ActionWithInterface {
    IStorage public storageContract;

    function ActionWithInterface(address _storageContract) public {
        storageContract = IStorage(_storageContract);
    }

    function doSomething() public {
        uint data = storageContract.getData();
        // do something with data ...
    }   
}

考虑到上述标准,哪种解决方案更适合分离存储和逻辑,为什么?在哪些其他情况下,其他解决方案更好?

【问题讨论】:

    标签: interface ethereum solidity smartcontracts


    【解决方案1】:

    库和接口确实不同,并且在不同的情况下使用。我个人认为它们在合同设计中不可互换。下面我试图概述两者的主要特征。请注意,interface 我的意思是抽象合同(这就是您在上面的示例中所拥有的)。在 Solidity 的接口中仍然存在 imo 问题,我之前在这里强调过 https://medium.com/@elena_di/hi-there-answers-below-6378b08cfcef

    • 可以包含逻辑并用于从合约中提取代码以实现可维护性和重用目的

    • 部署一次,然后在合同中引用。它们的字节码是单独部署的,不是引用它们的合约的一部分。这在我上面的文章(“在 Solidity 中编写可升级合约”)中定义为单例,我解释了诸如降低部署成本等好处。

    抽象合约/接口

    • Cannon 只包含逻辑接口定义

    • 主要用作其他合同的导入,提供与合同实施的交互。接口的部署/导入大小比实施者合约小得多

    • 为可升级性提供抽象,我在文章“使用‘接口’解耦合约间通信”一节中也对此进行了描述

    我认为上述两者之间唯一的相似之处是它们都不能包含存储变量。

    【讨论】:

    • 虽然这解释了接口和库之间的区别,但它根本没有回答问题。正如@adamkipnis 在他们的回答中解释的那样,在这种特定情况下,两者都可以用来达到相同的结果,而且差别不大。
    【解决方案2】:

    我希望有人能给出更好的答案,但这是一个很好的问题,想发表我的看法。

    简而言之,由于它特别涉及可升级合同,我认为这两种方法之间没有真正的区别。无论哪种实现,您仍然有一个单独的存储合约,并且您仍在向存储合约发出call(一个通过接口来自动作合约,另一个通过库间接来自动作合约)。

    唯一的具体区别在于气体消耗。通过该界面,您将发出一个 call 操作。通过一个库,您添加了一层抽象,最后得到一个delegatecall,然后是一个call。不过,gas 开销并不是很大,所以归根结底,我认为这两种方法非常相似。您采取的方法是个人喜好。

    这并不意味着库通常没有用处。我经常将它们用于常见数据结构或操作的代码重用(例如,由于在 Solidity 中迭代很痛苦,我有一个库可用作基本的可迭代集合)。我只是看不到将它们用于我的存储合同集成的价值。我很想知道 Solidity 专家是否有不同的看法。

    【讨论】:

      猜你喜欢
      • 2022-07-19
      • 1970-01-01
      • 2019-08-12
      • 2022-08-05
      • 1970-01-01
      • 1970-01-01
      • 2021-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多