📜 ⬆️ ⬇️

Universal smart multi-signature contract in Ethereum

A few days ago, we at BitClave read about the recent incident with the multi- subscription wallets of Parity Technologies , we decided to look at the code of their smart contract. A recent blog post from Zeppelin Solutions describes the incident in detail from the technical side, so we would like in our article to focus more on the principles of designing smart contracts.

Ethereum wallet

There are several widely known principles of OOP included in the abbreviation SOLID :


There are many supporters and opponents of using these principles, but we looked at many libraries in Solidity and came to the conclusion that the approach of Zeppelin Solutions is the most convenient and secure. Their library, OpenZeppelin.org, provides many small smart contracts that can be compiled to achieve a more complex mixin pattern through the use of multiple inheritance in the Solidity language. You need to decompose the responsibilities of your final smart contract into multiple smart contracts with the only responsibilities — each smart contract should serve a single purpose. Moreover, you will most likely find some of the contracts you need in libraries like the one offered by Zeppelin Solutions . In addition, you can also test each of the contracts separately.
')
Guided by these principles, we have developed smart contracts for the sale of tokens: github.com/bitclave/crowdsale . You can see BonusCrowdsale and TokensCappedCrowdsale smart contracts in the repository, which were designed to handle such aspects of our sales as the processing of participants' bonuses depending on the time and amount of investment, as well as to control the total number of tokens sold. We received a rather laudable review of the smart contract security auditor for our code:

“Excellent work on reusing existing OpenZeppelin library contracts! Additional contracts look very thoughtfully designed and look like a good extension of this framework "(" Great work reusing the existing OpenZeppelin libraries! The additional contracts are very thoughtfully designed, and Zeppelin Solutions. The full conclusion can be found on the link .

In order to successfully apply these principles in practice, it is necessary to clearly understand how multiple inheritance works. In fact, the Solidity compiler converts multiple inheritance into single inheritance. In this way, after compilation, each smart contract will have only 1 parent, which can be accessed through the super keyword. Perhaps the following example will additionally help to understand how the linearization of C3 multiple inheritance 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 that smart contract A cannot redefine C , because C redefines B , which in turn redefines A :

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

It is also necessary to take into account that any direct inheritance of contracts can be prevented in the process of inheriting child classes. Notice how the following example compiles the contract W , and its parent contract Z becomes the successor of the contract Y (which is the successor of X ), instead of directly inheriting from 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 multi-signature wallet smart contract, we noticed that it was not decomposed at all. The only architectural solution noted by us: the removal of the common code of all wallets in a single library in order to reduce the cost of loading the contract. By the way, it was this feature that allowed a random developer to disrupt the work of all wallets by breaking down a single library with common code. We reflected on the topic of multiple ownership of the resource and prepared our solution to this problem in the manner of the OpenZeppelin libraries. We are pleased to present you the Multiownable.sol contract, which will allow you to easily add multi-signature functionality to any of your contracts. Its use is as simple as using the usual Ownable contract — you just need to follow it and add onlyAnyOwner and onlyManyOwners to the necessary functions:

 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 the smart contract will be called only after all the wallet owners call it with the same arguments. You will not believe it, but this is the complete code of the simplest multi-signature wallet for the Ether currency. Additionally, you can implement support for any tokens compatible with the ERC20 specification, this is the following method:

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

We will be glad to any feedback of the community: github.com/bitclave/Multiownable

PS Updated the example with the latest library changes - added protection against nested calls, in case the recipient is a contract that has a transfer method.

Source: https://habr.com/ru/post/342100/


All Articles