具有Solidity的可升级智能合约:界面与库?

Mik*_*ike 6 interface ethereum solidity smartcontracts

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

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

  • 模块化 - 可重用性和易于维护
  • 天然气限制 - 拆分巨额合同,以便他们可以部署在几个交易中,以免达到天然气限制
  • 升级费用 - 每份合同升级费用多少.在一个合同中(小)改变之后,需要重新部署哪些其他合同?
  • 执行成本 - 单独的合同可能导致每次通话的天然气费用.尽量保持低开销.

这篇Medium帖子建议使用库来封装逻辑(例如,当与"存储契约"交互时)并使用接口来解耦合同间通信.其他帖子提出了不同的技巧 据我所知,库在部署之前与合同相关联,因此一旦合同发生变化,就需要重新部署库.为什么使用接口与存储合同进行交互并不是更好?

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

库的解决方案

StorageWithLib.sol:

contract StorageWithLib {
    uint public data;

    function getData() public returns(uint) {
        return data;
    }
}
Run Code Online (Sandbox Code Playgroud)

StorageLib.sol:

import './StorageWithLib.sol';

library StorageLib {

    function getData(address _storageContract) public view returns(uint) {
        return StorageWithLib(_storageContract).getData();
    }
}
Run Code Online (Sandbox Code Playgroud)

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 ...
    }
}
Run Code Online (Sandbox Code Playgroud)

接口解决方案

IStorage.sol:

contract IStorage {     
    function getData() public returns(uint);
}
Run Code Online (Sandbox Code Playgroud)

StorageWithInterface.sol:

import './IStorage.sol';

contract StorageWithInterface is IStorage {
    uint public data;

    function getData() public returns(uint) {
        return data;
    }
}
Run Code Online (Sandbox Code Playgroud)

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 ...
    }   
}
Run Code Online (Sandbox Code Playgroud)

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

小智 5

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

\n\n

图书馆

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

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

  • \n
\n\n

抽象合约/接口

\n\n
    \n
  • Cannon 只包含接口定义的逻辑

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

  • \n
  • 提供可升级性的抽象,我也在我的文章“使用 \xe2\x80\x98interfaces\xe2\x80\x99 解耦合约间通信”部分中进行了描述

  • \n
\n\n

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

\n


Ada*_*nis 4

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

简而言之,由于它专门与可升级合约相关,因此我认为这两种方法之间并没有真正的区别。无论采用哪种实现方式,您仍然拥有单独的存储合约,并且仍然call向存储合约发出 a (一个通过接口从操作合约发出,另一个通过库间接从操作合约发出)。

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

这并不意味着库通常没有用。我经常使用它们来实现常见数据结构或操作的代码重用(例如,由于在 Solidity 中进行迭代很痛苦,所以我有一个库可以用作基本的可迭代集合)。我只是不认为将它们用于我的存储合同集成有多大价值。我很好奇 Solidity 专家是否有不同的观点。