Authorizable — Ethereum Smart Contracts Governance Made Easy

Francesco Sullo
0xNIL
Published in
3 min readApr 4, 2018

We are in the early stage of smart contract development, and things that are normal in centralized software development are just going to happen in the decentralized world. Governance is one of those things.

An example: CryptoKitties

The CryptoKitties smart contract is at https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code. If you take a look at the code, you will see how they solved the governance extending where needed the contract KittyAccessControl:

contract KittyAccessControl {
...
address public ceoAddress;
address public cfoAddress;
address public cooAddress;

modifier onlyCEO() {
require(msg.sender == ceoAddress);
_;
}

modifier onlyCFO() {
require(msg.sender == cfoAddress);
_;
}

modifier onlyCOO() {
require(msg.sender == cooAddress);
_;
}

modifier onlyCLevel() {
require(
msg.sender == cooAddress ||
msg.sender == ceoAddress ||
msg.sender == cfoAddress
);
_;
}

function setCEO(address _newCEO) external onlyCEO {
require(_newCEO != address(0));

ceoAddress = _newCEO;
}

function setCFO(address _newCFO) external onlyCEO {
require(_newCFO != address(0));

cfoAddress = _newCFO;
}

function setCOO(address _newCOO) external onlyCEO {
require(_newCOO != address(0));

cooAddress = _newCOO;
}
...
}

This approach works in this context because the contract cannot be modified. A better approach to smart contract development is to allow the contract to evolve, as summarized by Jack Tanner in this post:

Doing so, you need some general approach to governance that allows flexibility while keeping the contract safe. An advanced solution is AragonOS:

In the post above, Jorge Izquierdo writes about upgradeability:

Smart contract upgradeability is a topic we spend a lot of time thinking about at Aragon. We need to ensure that the organizations that run Aragon will be securely available for years to come.

When I first thought about Authorizable, I made the same reflection. My solution is not as sophisticated as AragonOS, but it is simpler and effortless to implement.

The Authorizable smart contract

First off, you can look at the code at https://github.com/tweedentity/authorizable/blob/master/contracts/Authorizable.sol

Any wallet able to perform something in a smart contract has an assigned level. The level is a uint256, which means that you can manage a huge amount of levels and combine them to do everything you want.

To simplify your life, though, you’d probably prefer to set a limited number of levels. For this reason, there are two special variables with a reasonable default value:

uint public maxLevel = 64;  
uint public authorizerLevel = 56;

The second is the minimum level required to authorize other wallets. The only restriction is that as soon as you authorize a wallet, to keep the contract consistent, you won’t be able to change these two variables.

When you have set some roles, you’ve a bunch of self-explaining useful modifiers:

onlyOwner        // inherited by ZeppelinOS’s Ownable
onlyAuthorized
onlyAuthorizedAtLevel(uint _level)
onlyAuthorizedAtLevels(uint[] _levels)
onlyAuthorizedAtLevelsWithin(uint _minLevel, uint _maxLevel)
onlyOwnerOrAuthorized
onlyOwnerOrAuthorizedAtLevel(uint _level)
onlyOwnerOrAuthorizedAtLevels(uint[] _levels)
onlyOwnerOrAuthorizedAtLevelsWithin(uint _minLevel, uint _maxLevel)
onlyAuthorizer

It could have set a generic modifier with many options, but I thought that it was better to explicitly cover all the primary cases without adding exoteric modifiers. You can always add some in your contract.

The CryptoKitties case

First off, they didn’t need KittyAccessControl because Authorizable covers all their needs.

In KittyAccessControl they considered three roles that in our case could be:

CEOLevel: 60 // able to authorize other wallets because >= 56
CTOLevel: 50
CFOLevel: 40

To authorize the ceoAddress the owner could call:

authorize(ceoAddress, 60);

After, ceoAddress could do the same to authorize cfoAddress and ctoAddress. Then, in the contract, the following function

function withdrawBalance() external onlyCFO {
...
}

would be

function withdrawBalance() external onlyAuthorizedAtLevel(40) {
...
}

The advantage is clear. CryptoKitties is growing. In the future, there could be an entire department working on financials. In this case, it is very likely that there will be more than one address authorized to do that job. Using Authorizable that’s easy; using the actual CryptoKitties contract that’s impossible.

Finally, the onlyClevel modifier would be substituted like:

uint[] Clevels = [40, 50, 60];function setSecondsPerBlock(uint256 secs) external
onlyAuthorizedAtLevels(Clevels) {
...
};

I’ll write in a future post about use cases (for example: whitelists), bringing some practical example. In the meantime, what do you think? Any suggestion, comment, or pull request is very welcome.

If you like this post, please 👏 and share it!

--

--

Francesco Sullo
0xNIL
Editor for

Polymath. CTO at Superpower Labs & @MOBLANDHQ. Before founded @Passpack, and was at @Turo, @Yahoo, @Tronfoundationand others. More at https://sullo.co