5

I would like to know if it's possible to change the address of the message sender of contract's function call when writing unit tests in solidity. It really should be, I was trying to find out how but could not.

pragma solidity ^0.4.4;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";

contract A {

    event Test(address add);

    function test() {
        Test(msg.sender);
    }
}

contract TestA {

    function test01() {
        A a = A(DeployedAddresses.A());
        a.test(); // <--- msg.sender is address(this), but how do we use a different account from testrpc?
    }
}

In gitter I got a response so I have tried to run it like this:

a.test({from: 0xf6a948bff792e4f42d7f17e5e4ebe20871d160f2});

It gives the following error:

TypeError: Wrong argument count for function call: 1 arguments given but expected 0. a.test({from: 0xf6a948bff792e4f42d7f17e5e4ebe20871d160f2});

Operating System: Windows 10 Truffle version: v3.4.11 Ethereum client: testrpc v4.1.3 node version: v8.7.0 npm version: 5.4.2

2

You cannot change the sender address from within solidity. The solidity tests are run inside the EVM and it doesn't allow the modification of the msg.sender. It is possible that a modified version of the EVM provides such functionality, but I'm not aware of such modification.

You can modify the function to test to accept an extra parameter, and pass there the sender you want

contract A {
    // We cannot test directly deposit
    function deposit(uint _amount) public {
        doDeposit(msg.sender, _amount);
    }
    // But we can test doDeposit
    function doDeposit(address _sender) public {
    }
}

contract TestA {
    address constant senderA = "0xA00...";
    address constant senderB = "0xB00...";
    function testDeposit() {
        A a = A(DeployedAddresses.A());
        a.doDeposit(senderA, 10000); 
        a.doDeposit(senderB, 10000); 
    }
}

It is not ideal, but it may help find some bugs.

Another option is to use the test contract address

contract TestA {
    address constant senderA = "0xA00...";
    address constant senderB = "0xB00...";
    function testMint() {
        // TestA is the owner a
        A a = new A(this);
        // Only the owner TestA can mint
        a.mint(senderA, 10000);
        // make senderA approve TestA
        a.doApprove(senderA, this, 1000);
        // testA can make a transfer from senderA
        a.transferFrom(senderA, senderB, 1000);
    }
}
  • Good idea. Any comment on encapsulating this so it can't be reached outside of Test contract scope? Deploying it like that would lead to many vulnerabilities or some code and test code would need to be commented out before compilation. – Marko Jakovic Oct 31 '17 at 7:27
  • I've seen some contracts have onlyTest modifier that check for a test variable, but it is very ugly. – Ismael Oct 31 '17 at 16:03
0

Since you have a contract (TestA) calling a function of another contract (A), msg.sender will always be TestA's address in A's test function, because, for contract A, the address that sent the message was TestA.

If you want to log the actual address that called the function originally, you would have to do:

pragma solidity ^0.4.4;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";

contract A {

    event Test(address add);

    function test(address _sender) {
        Test(_sender);
    }
}

contract TestA {

    function test01() {
        A a = A(DeployedAddresses.A());
        a.test(msg.sender); 
    }
}
  • It's a good idea. Please see my comment to Ismael. Also, this wouldn't work for the most of the modifiers, right? I am thinking that they could also be changed in this manner. – Marko Jakovic Oct 31 '17 at 7:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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