I'm playing around w/ Gnosis' MultiSigWallet and noticed in their replaceOwner function it has a modifier that requires msg.sender to be the MultiSigWallet contract itself:

modifier onlyWallet() {
    require(msg.sender == address(this));

In Truffle console, how would I go about testing this and calling the replaceOwner function from the MultiSigWallet's address? I.e., if I try something like this:

wallet.replaceOwner('owner_address','newOwner_address', {from: 'multisigwallet_address'})

I'll get an Error: sender account not recognized

Is there a way within Truffle's console to make calls from contract addresses or is it only possible to make calls from the user addresses?

This post is related and I understand how this works, but I'm more curious to see if we can do this easily w/in Truffle for expedient testing purposes.


1 Answer 1


Here you go (got plenty more coverage for this contract if you're interested):

contract("MultiSigWallet", function(accounts) {
    let hMultiSigWallet;

    let utils = require("./utils");

    let NUM_OF_OWNERS    = 6;
    let NUM_OF_REQ_CONFS = 5;

    let owners   = accounts.slice(1, 1 + NUM_OF_OWNERS);
    let newOwner = accounts[1 + NUM_OF_OWNERS];

    beforeEach(async function() {
        hMultiSigWallet = await artifacts.require("MultiSigWallet").new(owners, NUM_OF_REQ_CONFS);

    describe("functional assertion:", function() {
        it("function replaceOwner should abort with an error if called with a non-existing current owner", async function() {
            await execute(hMultiSigWallet.contract.replaceOwner, [newOwner, newOwner], "ExecutionFailure");
        it("function replaceOwner should abort with an error if called with an already-existing new owner", async function() {
            await execute(hMultiSigWallet.contract.replaceOwner, [owners[0], owners[0]], "ExecutionFailure");
        it("function replaceOwner should complete successfully if called with legal input arguments", async function() {
            await execute(hMultiSigWallet.contract.replaceOwner, [owners[0], newOwner], "Execution");

    async function execute(func, args, state) {
        let submitReceipt = await hMultiSigWallet.submitTransaction(hMultiSigWallet.address, 0, func.getData(...args), {from: owners[0]});
        let transactionId = utils.getParamFromTxEvent(submitReceipt, "transactionId", null, "Submission");
        for (let i = 1; i < NUM_OF_REQ_CONFS - 1; i++) {
            let confirmReceipt   = await hMultiSigWallet.confirmTransaction(transactionId, {from: owners[i]});
            let newTransactionId = utils.getParamFromTxEvent(confirmReceipt, "transactionId", null, "Confirmation");
            assert(newTransactionId.equals(transactionId), `expected = ${transactionId}, actual = ${newTransactionId}`);
        let confirmReceipt   = await hMultiSigWallet.confirmTransaction(transactionId, {from: owners[NUM_OF_REQ_CONFS - 1]});
        let newTransactionId = utils.getParamFromTxEvent(confirmReceipt, "transactionId", null, state);
        assert(newTransactionId.equals(transactionId), `expected = ${transactionId}, actual = ${newTransactionId}`);
  • Just make sure that you start Ganache with a sufficient number of accounts (at least NUM_OF_OWNERS + 2). Dec 13, 2018 at 16:19
  • Thanks! So, it looks like the fastest way to test is w/ unit tests? There's no easy / quick way to do it w/in truffle console? Dec 13, 2018 at 18:40
  • @dougiebuckets: I don't use truffle console so it's hard for me to answer this question, but you can get it running via truffle test. Dec 13, 2018 at 19:09
  • @dougiebuckets The trick is that, as you pointed out, the contract itself has to call replaceOwner. The only way to get it to do that is the same way you make the contract do anything; call submitTransaction and then confirmTransaction a sufficient number of times from the various owner accounts. So there's no quick shortcut to this... it's the nature of the contract you're using.
    – user19510
    Dec 14, 2018 at 0:10

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.