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

时间:2018-04-25 12:27:56

标签: interface ethereum solidity smartcontracts

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

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

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

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 ...
    }   
}

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

2 个答案:

答案 0 :(得分:3)

库和接口实际上是不同的,并且在不同的情况下使用。在合同设计中,我个人认为它们不可互换。下面我试图概述两者的关键特征。请注意,interface我指的是一个抽象合同(这是你在上面的例子中所拥有的)。我在之前的https://medium.com/@elena_di/hi-there-answers-below-6378b08cfcef

中强调了Interfaces in Solidity中的问题

<强>库

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

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

抽象合同/接口

  • Cannon包含逻辑接口定义

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

  • 小得多
  • 提供可升级性的抽象,我在我的文章“&34;使用'接口'来解除合同间沟通&#34;

  • 一节中也有描述。

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

答案 1 :(得分:2)

我希望有人可以用更好的答案来衡量,但这是一个很好的问题,我想发表意见。

简而言之,由于它与可升级合同有关,我不认为这两种方法之间确实存在差异。无论是哪种实现,您仍然拥有单独的存储合同,并且您仍然向存储合同发出call(一个来自操作合同,一个来自操作合同,另一个来自动作合同,间接地通过库)。

唯一的具体差异来自燃气消耗。通过界面,您将发出一个call操作。通过一个库,您将添加一个抽象层,最后得到一个delegatecall,后跟一个call。然而,天然气开销不是很大,所以在一天结束时,我认为这两种方法非常相似。你采取的方法是个人偏好。

这并不意味着一般来说库不是很有用。我将它们用于常见数据结构或操作的代码重用(例如,因为在Solidity中迭代很麻烦,我有一个库用作基本的可迭代集合)。我没有看到使用它们进行存储合同集成的重要价值。我很想知道那里的Solidity专家是否有不同的观点。