В контексте обновляемых интеллектуальных контрактов когда следует использовать интерфейсы, а когда библиотеки ?
Я прочитал несколько похожих вопросов и сообщений в блоге, но ни один из них не дает прямого ответа:
Я понимаю, что основными критериями, которые следует учитывать (помимо безопасности) при проектировании для обновляемости, являются:
- модульность - для возможности повторного использования и упрощения обслуживания
- газовый лимит - разбить огромные контракты, чтобы их можно было использовать в нескольких транзакциях, чтобы не превышать газовый лимит
- стоимость обновления - сколько стоит каждое обновление контракта. После (небольшого) изменения в одном контракте, какие другие контракты должны быть повторно развернуты?
- стоимость исполнения - отдельные контракты могут привести к накладным расходам газа при каждом вызове. Старайтесь держать это над головой низко.
В этом посте Medium предлагается использовать библиотеки для инкапсуляции логики (например, при взаимодействии с «договорами хранения») и использовать интерфейсы для отделения межконтрактной связи. Другие посты предлагают разные методы. Насколько я понимаю, библиотеки связаны с контрактами до развертывания, поэтому, как только контракт меняется, библиотеки должны быть повторно развернуты. Почему не лучше использовать интерфейсы для взаимодействия с договорами хранения?
Ниже я представляю два решения, которые я видел до сих пор - одно с библиотекой, а другое с интерфейсом. (Я бы хотел избежать решений со встроенной сборкой ...)
Решение с библиотекой
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 ...
}
}
Учитывая вышеупомянутые критерии, какое решение предпочтительнее для разделения хранилища и логики и почему ? В каких других случаях другое решение лучше?