upgradable smart contracts with Solidity: interface vs library?
Asked Answered
T

2

11

In the context of upgradable smart contracts, when should one use interfaces and when libraries? I read several similar questions and blog posts, but none of them give a straight-to-the-point answer:

I understand that the main criteria to consider (besides security) when designing for upgradability are:

  • modularity - for reusability and easier maintenance
  • gas limit - split huge contracts so that they can be deployed in several transactions, so as to not hit the gas limit
  • cost of upgrade - how much does each contract upgrade cost. After a (small) change in one contract, which other contracts need to be re-deployed?
  • cost of execution - separate contracts may result in gas overhead on each call. Try to keep that overhead low.

This Medium post suggests to use libraries to encapsulate logic (e.g. when interacting with "storage contracts") and to use interfaces to decouple inter-contract communication. Other posts suggest different techniques. As far as I understand, libraries are linked to contracts prior to deployment, so once the contract changes, libraries need to be re-deployed. Why it is not better to use interfaces for interacting with storage contracts?

Below I present the two solutions I have seen so far - one with library and one with an interface. (I'd like to avoid solutions with inline assembly...)

Solution with library

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

Solution with interface

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

Considering the above criteria, which solution is preferred for separating storage and logic, and why? In which other cases is the other solution better?

Troudeloup answered 25/4, 2018 at 12:27 Comment(0)
W
5

Libraries and Interfaces are really different and used in different cases. I personally don't see them as interchangeable in the contract design. Below I've tried to outline the key characteristics of the two. Note that by interface I mean an abstract contract (which is what you have in your example above). There are still issues imo in Interfaces in Solidity which I highlighted previously here https://medium.com/@elena_di/hi-there-answers-below-6378b08cfcef

Libraries:

  • Can contain logic and are used to extract code away from the contract for maintainability and reuse purposes

  • Deployed once, then referenced in contracts. Their bytecode is deployed separately and is NOT part of the contracts that references them. This is defined as a singleton in my article above ("Writing upgradable contracts in Solidity") where I explain the benefits such as lower deployment cost.

Abstract contracts / Interfaces

  • Cannon contain logic just interface definition

  • Mainly useful as imports to other contracts providing interaction with contract implementations. Interfaces have a much smaller deploy/import size than the implementer contract

  • Provide abstraction for upgradability which I've also described in my article in section on "Use ‘interfaces’ to decouple inter-contract communication"

The only similarity I could think between the two above is that they both can't contain storage variables.

Wordsmith answered 28/4, 2018 at 13:22 Comment(1)
While this explains the difference between both an interface and a library, it does not answer the question at all. As @adamkipnis explains in their answer, in this specific case both can be used to achieve the same result and there is little difference.Paroxysm
G
4

I'm hoping someone can weigh in with a better answer, but this is a good question and wanted to give my opinion.

In short, as it relates specifically to upgradeable contracts, I don't think there is really a difference between the two approaches. With either implementation, you still have a separate storage contract and you are still issuing a call to the storage contract (one from the action contract through the interface and the other from the action contract indirectly through the library).

The only concrete difference comes with gas consumption. Going through the interface, you are issuing a single call operation. Going through a library, you are adding one layer of abstraction and end up with a delegatecall followed by a call. The gas overhead is not very big, though, so at the end of the day, I think the two approaches are very similar. The approach you take is personal preference.

This is not meant to imply that libraries in general aren't useful. I use them a lot for code reuse for common data structures or operations (for example, since iterating in Solidity is a pain, I have a library to use as a basic iterable collection). I just don't see much value in using them for my storage contract integration. I'm curious to see if Solidity experts out there have a different view.

Gracchus answered 26/4, 2018 at 15:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.