Ethereum Multiownable smart contract

A few days ago BitClave team heard about the latest Parity Technologies multisig wallet issue, and decided to inspect its smart contract code in depth. Recent Zeppelin Solutions blog post covers issue in detail, so we wanna talk about common Solidity smart contract design principles.

There are several well-known OOD-principles called SOLID:

There are many pros and cons to using these principles, but we‘ve looked through a lot of Solidity libraries and came to a conclusion that Zeppelin Solutions architecture is the most comfortable to work with: OpenZeppelin.org. It provides you a lot of tiny contracts, which can be composed like mixins by using Solidity multiple inheritance feature. You need to decompose your final smart contract into multiple single responsibility contracts — every contract should serve to exactly one purpose. Some of them can be found already implemented in Zeppelin Solutions’s library or in any other. Also each of this contract should be tested separately.

We used these principles to develop our crowdsale smart contracts: http://github.com/bitclave/crowdsale. You may discover contracts BonusCrowdsale and TokensCappedCrowdsale in repo, which were designed to implement different aspects of our crowdsale. And got this review from security auditor:

«Great work reusing the existing OpenZeppelin libraries! The additional contracts are very thoughtfully designed, and are a good extension of the framework» — Zeppelin Solutions. See full report here.

To follow this technique you should clearly understand how multiple inheritance is working. Simply: Solidity compiler transforms multiple inheritance into linear inheritance. So after compilation every smart contract will have a single parent, which can be accessed by super keyword. Maybe following example will help you to understand how C3 inheritance linearization works:

contract A { }
contract B { }
contract C is A, B { } // C(A,B) = ABC
contract D is C, A { } // D(C(A,B),A) = D(ABC,A) = ABCAD 💔 Error
contract E is A, C { } // E(A,C(A,B)) = E(A,ABC) = ABCE

The problem is smart contract A cannot override C because C overrides B which overrides A:

TypeError: Linearization of inheritance graph impossible
contract D is C, A { }
^ — — — — — — — — — — ^
Compiliation failed. See above.

Also you should be aware that any direct inheritance can be interrupted by subclassing. Take a look at the following example, where in compiled contract W its parent contract Z became a subclass of Y(which one is subclass of X), instead of a direct subclass of X:

contract X {}
contract Y is X {} // Y(X) = XY
contract Z is X {} // Z(X) = XZ
contract W is Y, Z {} // W(Y(X),Z(X)) = W(XY, XZ) = XYZW

Returning to the Parity Technologies’s smart contract we have noticed that it was not decomposed at all, only non-constant methods were moved to a single library to achieve thin wallets for cheaper deployment. So we thought a bit on multi-ownership task and provided our vision for its implementation in OpenZeppelin manner. We are very honored to present you Multiownable.sol contract, which easily allows you to ship multi-ownership functionality to any smart contract. Its usage is as simple as Ownable contract usage — just subclass from it and use onlyAnyOwner and onlyManyOwners modifiers:

contract SimplestMultiWallet is Multiownable {
    bool avoidReentrancy = false;
    function () payable { }
    function transferTo(address to, uint256 amount) onlyManyOwners {
require(!avoidReentrancy);
avoidReentrancy = true;
to.transfer(amount);
avoidReentrancy = false;
}
}

The transferTo method of smart contract will be executed only when all of its owners call it with the same arguments. This is complete code of simplest multisig wallet for Ether currency. Additionally it is possible to implement an ability to transfer any ERC20 compatible tokens by adding the following method:

function transferTokensTo(address token, address to, uint256 amount) onlyManyOwners {
require(!avoidReentrancy);
avoidReentrancy = true;
ERC20(token).transfer(to, amount);
avoidReentrancy = false;
}

Can’t wait to get any feedback from the community: https://github.com/bitclave/Multiownable