Skip to main content

DemocraticOwnership

The DemocraticOwnership.sol smart contract has been created by our team to reduce the ownership risks. For some applications, we need to create functions that are restricted to the development team. Those functions insure the good behavior of the smart contract. Having one owner can be risky if his hot wallet get exposed. Acording to SlowMist Stolen Hot Wallet is the nineth most popular hack in blockchain industry. (First one being SCAM !)

How does the Democratic Ownership work?​

When you create the Democratic Ownership smart contract, you initialize the list of owners that will be able to participate the voting process. In our case, we decided to have a total of 5 different owner addresses. Once the contract is deployed, there is no way to add other addresses. For Lenny Verse project we are using Ledger cold wallets to add extra security. Each time one or many restricted have to be called, a voting process have to be done where 3 owners have to call the voteForChange() function. The second owner who calls the function will be granted the right to call the restricted functions. Once the 3 votes are done, the second voter has duration amount of seconds to execute the changes needed after calling the function updateSC() in the other contracts. The work flow will go as follow :

Note: Each contract that wants to use this implementation have to inherit the Democratic.sol abstract.

Inheritances​

This contract inherits from the Context.sol implementation from Openzeppelin.

Interfaces​

This contract inherits from IDOSC.sol to insure the correct implementation of the contract

State Variables, Enums and Structs​

address public firstVoter : the public address of the first voter.

address public secondVoter : the public address of the second voter.

address private addressAuthorized : address of the current authorized address to call restricted functions.

address private endChangeTime : timestamp until a restricted call is possible.

address public endVotingTime : timestamp until the voting process is reset

address public immutable duration : number of seconds during the voting process active and restricted functions are callable

address public endVotingTime : current number of vote during the voting process

address public endVotingTime : number of restricted call registered

Call() (Struct)​

date : block timestamp of the registered Call

SmartContractName : name of the smart contract that has a restricted call

FunctionName : name of the function that proceed to the restricted call

Mappings​

public calls that maps the call id uint256 to the struct Call;

public ownership that maps the calling address to the bool that indicates true if caller is an owner;

Events​

ChangeRegistered(uint256 time, string scname, string funcname)​

It contains the same variables as the Call struct.

Modifiers​

onlyAdmins()​

This modifier is used to avoid none adminators to participate to the voting process.

Functions​

readAuthorizedAddress() → address (external)​

Return the current address authorized to execute restricted functions in other contracts.

readEndChangeTime() → uint256 (external)​

Return the latest block timestamp until a change in any contract parameter is allowed.

registerCall(string scname, string funcname) (external)​

Restricted functions in other smart contract have to call this function to register the call. First, it check if the changing period is still active then it updates the numberOfChanges value as well as the calls mapping with the proper Call structure. Finally, it emits the ChangeRegistered events.

    function registerCall(string memory scname, string memory funcname) external override {
uint time = block.timestamp;
require(time <= endChangeTime, 'No Change is possible');
numberOfChanges += 1;
Call memory newcall = Call(time, scname, funcname);
calls[numberOfChanges] = newcall;

emit ChangeRegistered(time, scname, funcname);
}

voteForChange() (external)​

An admin call this function vote to allow a call on any restricted function managed by the contract.

    function voteForChange () external onlyAdmins(){
if (block.timestamp > endVotingTime && numberOfVote != 0){
reInitVoters();
}

require(_msgSender() != firstVoter && _msgSender() != secondVoter, 'You already voted');
numberOfVote += 1;

if( numberOfVote == 1) {
endVotingTime = block.timestamp + duration;
firstVoter = _msgSender();
} else if (numberOfVote == 2) {
secondVoter = _msgSender();
addressAuthorized = _msgSender();
} else {
endChangeTime = block.timestamp + duration;
reInitVoters();
}
}

constructor(address[] _listAdmin, uint256 _duration) (public)​

To instantiate the smart contract, we need to provide a list of the owner public addresses. The _duration should be a short period of time (recommended 30 minutes -- 1800 seconds).

    constructor(address[] memory _listAdmin, uint _duration){
for(uint i=0; i < _listAdmin.length; i++){
ownership[_listAdmin[i]] = true;
}
duration = _duration;
}