JustPaste.it

AccountManager.sol

pragma solidity 0.5.12;

contract Ownable{
    address public owner;

    modifier onlyOwner(){
        require(msg.sender == owner);
        _; //Continue execution
    }

    constructor() public{
        owner = msg.sender;
    }
}

contract Destroyable is Ownable {

  function destroy() public onlyOwner {
    address payable receiver = msg.sender;
    selfdestruct(receiver);
  }
}

contract Creatable is Destroyable {
    
    struct Account{
        bool created;
        string name;
        address[] users;
        mapping(address => Rights) userRights;    // mapping of each user's rights
        uint saldo;
    }
    
    struct Rights{          // struct containing user rights to:
        bool user;          // make transactions and withdraw funds
        bool manager;       // add and delete users
        bool creator;       // change user rights
    }
    
    mapping(address => bytes32[]) internal accountIds;          // mapping of all account of each individual user
    mapping(bytes32 => Account) internal accounts;              // mapping of each account to its id
    
    event accountCreated(bytes32 accId, address creator);
    
    
    // create an account with one user (creator) and balance equal to value sent with function
    function createAccount( string memory name) public payable {
        
        address creator = msg.sender;
        bytes32 id =  keccak256(  abi.encodePacked( name, creator )  );     // create account ID
        
        // verify if this is account already created
        require(!accounts[id].created, "You already have an account with that name");      
        
        // create temp Account 
        Account memory newAccount;                                          
        newAccount.created = true;
        
        newAccount.name = name;                                         // set account name
        
        address[] memory allUsers = new address[](1);                   // insert creator's address into users array
        allUsers[0] = creator;
        newAccount.users = allUsers;
        
        newAccount.saldo = msg.value;                                   // set balance equal to value sent
        
        accounts[id] = newAccount;                                      // insert account to "accounts" mapping by ID
        accounts[id].userRights[creator] = Rights(true,true,true);      // give all rights to creator
        accountIds[creator].push(id);                                   // insert account ID into user's array of IDs
        
        // assert if account was inserted correctly
        assert(
            keccak256(  
                abi.encodePacked(
                    newAccount.created,
                    newAccount.name,
                    newAccount.saldo
                )
            ) 
            ==
            keccak256(  
                abi.encodePacked(
                    accounts[id].created,
                    accounts[id].name,
                    accounts[id].saldo
                )
            )
        );
        
        emit accountCreated(id,creator);
    }
}

contract Functional is Creatable{
    
    // check if the given accIndex is within the "accountIDs" array
    modifier checkAccIndex(uint accIndex){
        require(accIndex < accountIds[ msg.sender ].length, "You do not have that many accounts");
        _;
    }
    
    // check if the given userIndex is within the account's "users" array
    modifier checkUserIndex(bytes32 accId, uint userIndex){
        require(userIndex < accounts[ accId ].users.length, "There isn't that many users in account");
        _;
    }
    
    modifier onlyUser(bytes32 accId){
        require(accounts[ accId ].userRights[msg.sender].user, "You don't have rights to do this");
        _;
    }
    
    modifier onlyManager(bytes32 accId){
        require(accounts[ accId ].userRights[msg.sender].manager, "You don't have rights to do this");
        _;
    }
    
    modifier onlyCreator(bytes32 accId){
        require(accounts[ accId ].userRights[msg.sender].creator, "You don't have rights to do this");
        _;
    }
    
    // check if the newUser address is already added to the account
    modifier noUser(bytes32 accId, address newUser){
        require(!accounts[accId].userRights[newUser].user, "User already added");
        _;
    }
    
    // returns biggest index of sender's "accountIds" array
    function myAccounIdMaxIndex() public view checkAccIndex(0) returns(uint){
        return( accountIds[ msg.sender ].length -1 );
    }
    
    // return ID of senders account with chosen index
    function myAccountId( uint accIndex ) public view checkAccIndex(accIndex) returns( bytes32 ) {
        return( accountIds[ msg.sender ][ accIndex ] );
    }
    
    // returns biggest index of 
    function maxUserIndex( bytes32 accId ) public view onlyUser(accId) returns(uint){
        return( accounts[ accId ].users.length -1 );
    }
    
    // return address of chosen user of one of your accounts
    function userAddress( bytes32 accId, uint userIndex ) public view onlyUser(accId) checkUserIndex(accId, userIndex) returns( address user ){
        return( accounts[ accId ].users[ userIndex ] );
    }
    
    // return user rights
    function userRights(bytes32 accId, address user) public view onlyUser(accId) returns (bool, bool, bool){
        return(
            accounts[accId].userRights[user].user, 
            accounts[accId].userRights[user].manager,
            accounts[accId].userRights[user].creator
            );
    }
}

contract Manageable is Functional{
    
    event userAdded(bytes32 accId, address newUser);
    event userDeleted(bytes32 accId, address deletedUser);
    event userRightsChanged(bytes32 accId, address user, bool manager, bool owner);
    
    // add a new user to your account
    // new user will receive user rights by default
    function addUser( bytes32 accId, address newUser ) 
        public onlyManager(accId) noUser(accId, newUser) {
            
       uint usersCount = accounts[ accId ].users.length;
        
        accounts[ accId ].users.push( newUser );            // add new user to account
        accounts[ accId ].userRights[newUser].user = true;  // mark new user as created
        accountIds[ newUser ].push( accId );                // add account to user's "accountIds" array 
        
        assert( accounts[ accId ].users.length == usersCount + 1 );
        emit userAdded(accId, newUser);
    }
    
    
    // delete user from your account
    // removes also the account from user's "accountIds" array
    function deleteUser( bytes32 accId, uint userIndex ) 
        public onlyManager(accId) checkUserIndex(accId, userIndex) {
            
        require(userIndex > 0, "You cannot remove creator");
        
        uint usersCount = accounts[accId].users.length;             // get user count
        address deletedUser = accounts[accId].users[userIndex];     // get deleted user address
        
        delete accounts[accId].users[userIndex];                    // delete user from account
        
        if(userIndex < usersCount -1){                              // move all to the left to fill gap
            for (uint i=userIndex; i<usersCount-1; i++){
                accounts[accId].users[i] = accounts[accId].users[i+1];
            }
            assert(accounts[accId].users[userIndex] != deletedUser);
        }
        
        accounts[accId].users.length--;                             // decrease length
        assert(accounts[accId].users[usersCount-2] != deletedUser);
        
        delete accounts[accId].userRights[deletedUser];             // delete user rights
        
        deleteAccFromUser(deletedUser, accId);                      // delete account from user's accIds array
        
        emit userDeleted(accId, deletedUser);
    }
    
    // remove account from user's "accountIds" array
    function deleteAccFromUser(address deletedUser, bytes32 accId) internal {
        
        uint userAccCount = accountIds[deletedUser].length;         // get length of user's accIds array
        
        for (uint i=0; i < userAccCount; i++){                      // find index of the account
            if (accountIds[deletedUser][i] == accId){               
                
                if (userAccCount==1){                               // if it is the only user's account
                    delete accountIds[deletedUser];                 // delete user from mapping
                    
                } else {                                                
                    delete accountIds[deletedUser][i];              // delete account from user
                    
                    
                    // move all to the left to fill gap
                    if(i < userAccCount -1){                        
                        for (uint j=i; j<userAccCount -1; j++){
                            accountIds[deletedUser][j] = accountIds[deletedUser][j+1];
                        }
                        assert(accountIds[deletedUser][i] != accId);
                    }
                    
                    
                    // update array length
                    accountIds[deletedUser].length--;               
                    assert(accountIds[deletedUser][ userAccCount-2 ] != accId);
                    
                    break; 
                    
                }
            }
        }
    }
    
    
    // modify user's "manager" and "owner" rights values
    function changeUserRights(bytes32 accId, uint userIndex, bool manager, bool creator) 
    public onlyCreator(accId) {
        
        address user = userAddress(accId, userIndex);
        
        accounts[ accId ].userRights[user].manager = manager;
        accounts[ accId ].userRights[user].creator = creator;
        
        emit userRightsChanged(accId, user, manager, creator);
    }
    
}

contract AccountManager is Manageable{
    
    event toppedUp(bytes32 accId, uint amount);
    event transferSuccessful(bytes32 senderId, address receiver, uint amount);
    event internalTransferSuccessful(bytes32 senderId, bytes32 receiverId, uint amount);
    event withdrawalSuccessful(bytes32 senderId, address receiver, uint amount);
    
    modifier nonZeroValue(uint value){
        require(value > 0, "Non-zero value required");
        _;
    }
    
    modifier balanceCheck(bytes32 accId, uint toTransfer){
        require(toTransfer <= accounts[ accId ].saldo, "Insufficient funds");
        _;
    }
    
    function topUp( bytes32 accId ) public payable nonZeroValue(msg.value){
        accounts[ accId ].saldo += msg.value;
        
        emit toppedUp(accId, msg.value);
    }
    
    function transfer(bytes32 accId, address payable receiver, uint toTransfer) 
        public onlyUser(accId) nonZeroValue(toTransfer) balanceCheck( accId, toTransfer){
            
        uint oldBalance = accounts[ accId ].saldo;
        accounts[ accId ].saldo = oldBalance - toTransfer;
        receiver.transfer(toTransfer);
        
        emit transferSuccessful(accId,receiver, toTransfer);
    }
    
    function internalTransfer(bytes32 senderAccId, bytes32 receiverAccId, uint toTransfer) 
        public onlyUser(senderAccId) nonZeroValue(toTransfer) balanceCheck( senderAccId, toTransfer){
        
        uint balanceSum = accounts[ senderAccId ].saldo + accounts[ receiverAccId ].saldo;
        accounts[ senderAccId ].saldo -= toTransfer;
        accounts[ receiverAccId ].saldo += toTransfer;
        
        assert( balanceSum == accounts[ senderAccId ].saldo + accounts[ receiverAccId ].saldo );
        emit internalTransferSuccessful(senderAccId, receiverAccId, toTransfer);
    }
    
    function withdraw(bytes32 accId, uint toTransfer) 
        public onlyUser(accId) nonZeroValue(toTransfer) balanceCheck( accId, toTransfer) {
        
        uint oldBalance = accounts[ accId ].saldo;
        accounts[ accId ].saldo = oldBalance - toTransfer;
        msg.sender.transfer(toTransfer);
        
        emit withdrawalSuccessful(accId, msg.sender, toTransfer);
    }
    
    function accountBalance( bytes32 accId ) public view onlyUser(accId) returns(uint)  {
        return( accounts[ accId ].saldo );
    }
    
}