1

I deployed two contracts 'WalletLibrary' and 'Wallet': most of the functions in Wallet delegateCall() the functions from WalletLibrary. I want to recreate an Attack, where I call the initWallet() function from WalletLibrary by triggering the fallback function from Wallet. However when I do that, the Owner in Wallet is updated to my address, but the one in WalletLibrary is not. Further the owner of WalletLibrary is 0000000000000000 when deployed (did I do sth wrong with deploying maybe?), I would expect the owner should be the address I deployed it from.

From my understanding, when I update some variable in Wallet, then it should also be updated in WalletLibrary, because delegateCall() preserves context, right? My Question is, why it does not work. Please tell me, if I got something wrong. Hope anyone can help.

Below, the code for both contracts...

WalletLibrary.sol:

    pragma solidity ^0.5.4;
    
    contract WalletLibrary {
        address walletLibrary; 
        address payable owner;
        address payable student;
    
        event LogValue(uint256 exitcode,uint256 amount);
    
        function initWallet(address payable _owner) public payable {
        owner = _owner;
        }
    
        function getOwner() public view returns (address payable) {
        return owner;
        }
    
        function changeOwner(address payable new_owner) public returns (bool success) {
        if (msg.sender == owner) {
            owner = new_owner;
            return true;
        } else {
            return false;
        }
        }
    
        function withdraw(uint256 amount) public returns (bool success) {
            if (msg.sender == owner) {
                return owner.send(amount);
            } else {
                return false;
            }
        }
    
        function () external payable {
            emit LogValue(200,msg.value);
        }
    }

Wallet.sol:

    pragma solidity ^0.5.4;
    
    contract Wallet {
        address walletLibrary;
        address payable owner;
        address payable student;
    
        event LogValue(uint256 exitcode,uint256 amount);
    
        // constructor, called once when this contract is created 
        constructor(address payable _student, address lib) public payable {
            student = _student;  
            walletLibrary = lib; // hardcode lib address at deploy time
            // init the owner with the respective lib contract
            walletLibrary.delegatecall(abi.encodeWithSignature("initWallet(address)", msg.sender));
        }
    
        function getOwner() public view returns (address payable) {
            return owner;
        } 
    
        function getWalletLibrary() public view returns (address) {
            return walletLibrary;
        }
    
        function withdraw(uint256 amount) public returns (bool) {
            (bool success, bytes memory data) = walletLibrary.delegatecall(abi.encodeWithSignature("withdraw(uint256)", amount));
            if ( success ) {
                emit LogValue(200,amount);
            } else {
                emit LogValue(401,amount);
            }
            return success;
        }
    
        function changeOwner(address payable new_owner) public returns (bool) {
            (bool success, bytes memory data) = walletLibrary.delegatecall(abi.encodeWithSignature("changeOwner(address)", new_owner));
            return success;
        }
    
        function getStudent() public view returns (address) {
            return student;
        }
    
        // fallback function gets called if no other function matches call
        function () external payable {
            emit LogValue(301,msg.value);
            require( tx.origin == student ); 
            walletLibrary.delegatecall(msg.data);
        }
    }

1 Answer 1

1

When you use delegatecall then it will execute the code of the target in the context of the caller. That means when your Wallet contract used delegatecall to your WalletLibrary it will use the code from the library but the state (e.g. owner, student, etc) from the Wallet.

Therefore to change the state of the WalletLibrary you would have to directly interact with that contract.

A detailed introduction for delegatecalls can be found here: https://medium.com/coinmonks/delegatecall-calling-another-contract-function-in-solidity-b579f804178c

The owner of your WalletLibrary is 0x0000...0000 because you do not have any constructor, therefore the owner is not intialized. In your Wallet you call the initWallet function to do so, therefore the Wallet owner is set to msg.sender.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.