Technical description of the incubator

Taniguchi
Watch, 14 min
BEGINNER

In the beginning

I’m Taniguchi, an engineer at Frame00.
I would like to give you a technical explanation of our new feature, Incubator, which was released recently.

Overview

Description

Role settings permalink

The incubator is designed to operate with three roles: an administrator, a storage administrator, and an operator.
The administrator has combined authority to perform all functions, including those of the storage administrator and the operator, while the storage administrator and the operator have authority to perform for each relevant function.
Incubator implements this role configuration feature using OpenZeppelin's AccessControl.
OpenZeppelin is a Solidity library. By using OpenZeppelin, you can dramatically increase the efficiency of developing contracts using Solidity.
One of them is a contract called AccessControl. By using this contract, you can implement a contract that assumes operation with separate roles. By using this, it is possible to implement a contract that assumes operation with separate roles.
AccessControl was a bit complex due to a variety of functions implemented, so we created a wrapped contract to simply implement the only necessary functions.
It is available in the public domain, and you can use it if you like (at your own risk).

// Installation command
npm install @devprotocol/util-contracts
// Implementation example
pragma solidity >=0.7.6;

import {Admin} from "@devprotocol/util-contracts/contracts/access/Admin.sol";

// By inheriting the Admin contract, addAdmin, deleteAdmin and isAdmin functions are available.
// If necessary, you can add, delete, or confirm users who hold Admin privileges.
contract Logic is Admin {

// Since the Admin contract inherits AccessControl,
// it is possible to create new roles and link them with addresses depending on the situation.
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

// After the constructor of the Logic contract is executed, the constructor of the Admin contract will be executed.
// Then, Admin privileges will be automatically granted to deployers.
constructor() {
// The setup here is that when Admin rights are granted, operator rights are also granted.
_setRoleAdmin(OPERATOR_ROLE, DEFAULT_ADMIN_ROLE);
// Operator privileges are also granted to deployers.
grantRole(OPERATOR_ROLE, _msgSender());
}

// Modifiers for checking Operator privileges
modifier onlyOperator {
require(isOperator(_msgSender()), "operator only.");
_;
}

// By granting onlyOperator, it can only be executed by a users with Operator privileges
function testFunc() external pure onlyOperator returns (uint256){
return 100;
}

// You can check whether you have Operator privileges or not.
function isOperator(address account) public view returns (bool) {
return hasRole(OPERATOR_ROLE, account);
}

// You can grant the Operator privilege.
// This function cannot be executed without Admin privileges, since it is marked as "onlyAdmin".
function addOperator(address _operator) external onlyAdmin {
grantRole(OPERATOR_ROLE, _operator);
}

// You can take away the Operator privileges.
function deleteOperator(address _operator) external onlyAdmin {
revokeRole(OPERATOR_ROLE, _operator);
}
}

OpenZeppelin also has a contract called Ownable.
If there are only two patterns, a role as an administrator and a role other than that, use Ownable instead of AccessControl.
This is simpler to implement, which reduces deployment and execution costs, and makes it easier to operate.

Eternal Storage permalink

The incubator is designed to be up-gradable.
By retaining the data in an external contract, if a problem is found in the logic of the incubator itself, or if a change in specifications is necessary, the program can be modified as necessary, the storage can be replaced, and the operation can continue.
This mechanism is called "Eternal Storage". It’s not my invention, but it is one of the famous Solidity design patterns. Simply put, it is just a hashmap.
Contracts deployed on the blockchain are not modifiable, so if you want to operate them continuously like a web application, you can use this mechanism.
This is a very useful design pattern, but it has some disadvantages. Since the data is written to a separate contract, the cost of gas will be higher. You need to consider the actual operation, compare advantages and disadvantages, and decide whether to use it or not.
As with the aforementioned Admin contract, it is available as util-contracts, so if you wish to use it (at your own risk), please do so.

// Installation command
npm install @devprotocol/util-contracts
// Implementation example
pragma solidity >=0.7.6;

import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {UsingStorage} from "@devprotocol/util-contracts/contracts/storage/UsingStorage.sol";

// Create a contract that inherits UsingStorage.
contract Logic is UsingStorage {
// To prevent overflow, SafeMath is used for the calculation process.
using SafeMath for uint256;

function setValue(string memory _key, uint256 _value_) internal
{
// Here we have stored uint256 as an example, but you can use address, string, or boolean.
// For more details, please refer to the EternalStorage contract that is generated inside UsingStorage.
eternalStorage().setUint(getKey(_key), _price);
}

function getValue(string memory _key) public view returns (uint256)
{
return eternalStorage().getUint(getKey(_key));
}

function getKey(string memory _key) private pure returns (bytes32)
{
// In EVM, data is always saved as 32 bytes when it is stored in Storage.
// Therefore, considering the amount of data to be stored and the amount of gas to be used,
// setting it at 32 bytes will be the most efficient.
return keccak256(abi.encodePacked("_key", _key));
}

// This function increments the number each time the add function is executed.
// By storing the direct in external storage instead of internal variables, it can be operated continuously.
function add(string memory _key) external {
uint256 tmp = getValue(_key);
tmp = tmp.add(1);
setValue(_key, tmp);
}
}

Example of operation permalink

[When first deployed]

  1. After deploying the Logic contract, run the createStorage function to create the storage.

[When deploying for the second time or later]

  1. If you want to change the specification of the Logic contract, deploy the modified Logic contract first.
  2. Run the old Logic contract's getStorageAddress function to get the address of EternalStorage.
  3. Run the setStorage function of the new Logic contract to set the address of the EternalStorage.
  4. Run the old Logic contract's changeOwner function to delegate storage write permission to the new Logic contract.

Interface permalink

An interface in Solidity is a description of a function definition that can be executed outside the contract.

// 例 Dev Protocol's Allocator contract interface
// SPDX-License-Identifier: MPL-2.0
pragma solidity >=0.5.17;

interface IAllocator {
function beforeBalanceChange(
address _property,
address _from,
address _to

) external;

function calculateMaxRewardsPerBlock() external view returns (uint256);
}

The interface itself can only contain externals, but there is no problem if the actual contract is public.
(Except the case of inheritance).
The biggest advantage of using interfaces is that they can resolve dependencies.

// SPDX-License-Identifier: MPL-2.0
pragma solidity >=0.7.6;

interface ILogic {
function hogehoge() external;
}
// SPDX-License-Identifier: MPL-2.0
pragma solidity >=0.7.6;

import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {ILogic} from "./ILogic.sol";

contract Logic is ILogic{
using SafeMath for uint256;
uint256 private counter;

function hogehoge() external {
counter = counter.add(1);
}
}
// Example: Using an interface
// SPDX-License-Identifier: MPL-2.0
pragma solidity >=0.7.6;

import {ILogic} from "./ILogic.sol";

contract Logic2UseInterface {

address private logic = 0x00000.........;

function hogehoge() external {
ILogic(logic).hogehoge();
}
}
// Example: Not using the interface
// SPDX-License-Identifier: MPL-2.0
pragma solidity >=0.7.6;

import {Logic} from "./Logic.sol";

contract Logic2NotUseInterfase {

address private logic = 0x00000.........;

function hogehoge() external {
Logic(logic).hogehoge();
}
}

When using an interface, you don't need to be aware of what you are doing in the caller (Logic.hogehoge() in this case).
If you create a flatten file(*1) for Logic2NotUseInterfase, it will contain the Logic2 contract, the Logic contract, and the SafeMath library referenced from it.
If the SafeMath library refers to another large contract or library, it must also be included.
When this happens, the size of the bytecode generated from it becomes large, and the gas cost at the time of deployment becomes bloated, creating a major operational burden.
Conversely, if you want to create a flatten file for Logic2UseInterfase, you only need the Logic2 contract and the ILogic interface.
The size of the bytecode will be smaller, and the operational cost will be less.
There is also the advantage of reduced implementation cost, as the interface allows building without the main contract.
We are benefiting from the use of Dev Protocol interface as well as Incubator.
The Dev Protocol interface is open for everyone to use, and you are welcome to use it if you like.

*1 A file that combines all relevant sol files into one in order to register programming code with Etherscan.

// Installation command
npm install @devprotocol/protocol
// Implementation example
pragma solidity >=0.7.6;

import {IDev} from "@devprotocol/protocol/contracts/interface/IDev.sol";

contract Logic {

address private devToken = 0x5cAf454Ba92e6F2c929DF14667Ee360eD9fD5b26

function lockup(address _property, uint256 _staking) external {
// staking!
IDev(devToken).deposit(_property, _staking);
}
}

Finally

There might be more to share, but I have described the most common ones.
We will release more information on how to write and operate test cases as well as Solidity.

🌈 Was this helpful ?
In order to provide you with better contents in the future, please let us know your questions, feedbacks etc.🌱
Post on Forum

- Dev Protocol releases all codes as OSS in public. Your contributions are welcome.(Sometimes there are bounties.)
Dev Protocol GitHub

- DIP (Dev Improvement Proposal) process is also released. We’re looking forward to seeing your comments on it.🌟
DIP URL