1

I am getting the Error: VM Exception while processing transaction: revert when one of my contracts is calling another contract to make a transaction but not if I directly make a call to a contract for a transaction to occur - I don't know why this is happening.

My following javascript code is the following for making a transaction between two instances of the same contract (which works):

it('can make a transaction between 2 household contracts',async () =>{
        addressExchange = exchange.options.address;

        //set the exchange address necessary to interact with it
        await household1.methods.setExchange(addressExchange).send({
            from: accounts[0],
            gas: '1000000'
        });
        await household2.methods.setExchange(addressExchange).send({
            from: accounts[1],
            gas: '1000000'
        });
        await exchange.methods.deposit().send({
            from:accounts[2],
            gas: '1000000',
            value:  web3.utils.toWei( '1', 'ether')
        });


        const check1 = await household1.methods.exchangeAddress().call();
        const check2 = await household2.methods.exchangeAddress().call();

        //check for correct address
        assert.equal(addressExchange, check1 );
        assert.equal(addressExchange, check2 );


        pre_balance1 = await web3.eth.getBalance(household1.options.address);
        pre_balance2 = await web3.eth.getBalance(household2.options.address);
        balanceEx = await web3.eth.getBalance(exchange.options.address);

        console.log('balance of exchange contract', balanceEx);        
        console.log('balance1', pre_balance1);
        console.log('balance2', pre_balance2);


        confirmation1=await household1.methods.buyEnergy(2000, household2.options.address, 10).send({
            from: accounts[0],
            gas: '1999999'
        });

        confirmation2=await household1.methods.buyEnergy(2000, household2.options.address, 10).send({
            from: accounts[0],
            gas: '1999999'
        });

        balance1 = await web3.eth.getBalance(household1.options.address);
        balance2 = await web3.eth.getBalance(household2.options.address);
        amountOfCharge = await household2.methods.amountOfCharge().call();

        //checking for balance post transaction
        console.log(amountOfCharge);
        console.log('balance1', balance1);
        console.log('balance2', balance2);
        console.log('confirmation1', confirmation1);
        console.log('confirmation2', confirmation2);
        difference = balance2-balance1;
        if(difference>19){
            assert.ok(true);
            console.log(difference);    
        }

    });

When trying to call a function of 'Household' contract to call submitBid to 'Exchange' contract which thereby calls back an instance of 'Household' contract to transact some ether to another instance of 'Household' contract, the same operation of the previous test fails. This is the current test that I am using:

it('can submit a Bid and Ask which match', async () =>{
        addressExchange = exchange.options.address;

        //set the exchange address necessary to interact with it
        await household1.methods.setExchange(addressExchange).send({
            from: accounts[0],
            gas: '1000000'
        });
        await household2.methods.setExchange(addressExchange).send({
            from: accounts[1],
            gas: '1000000'
        });
        //deposit some ether to exchange
        await exchange.methods.deposit().send({
            from:accounts[2],
            gas: '1000000',
            value:  web3.utils.toWei( '1', 'ether')
        });

        //check on amountOfCharge and balance before transaction
        pre_amountOfCharge1 = await household1.methods.amountOfCharge().call();
        pre_amountOfCharge2 = await household2.methods.amountOfCharge().call();
        pre_balance1 = await web3.eth.getBalance(household1.options.address);
        pre_balance2 = await web3.eth.getBalance(household2.options.address);

        //send a Bid and an Ask that will match to provoke a transaction between both contracts(Households)
        await household1.methods.submitBid(10, 1000).send({
            from: accounts[0],
            gas: '1999999'
        });
        await household2.methods.submitBid(9, 2000).send({
            from: accounts[1],
            gas: '1999999'
        });
        await household2.methods.submitAsk(10, 1000).send({
            from: accounts[1],
            gas: '1999999'
        });

         //check on amountOfCharge after transaction
         post_amountOfCharge1 = await household1.methods.amountOfCharge().call();
         post_amountOfCharge2 = await household2.methods.amountOfCharge().call();
         post_balance1 = await web3.eth.getBalance(household1.options.address);
         post_balance2 = await web3.eth.getBalance(household2.options.address);   

    });

My Household contract:

contract Household{

    uint public demand;
    uint public supply;
    uint public batteryCapacity;
    uint public amountOfCharge;
    uint public excessEnergy;



    struct Bid{
        address origin;
        uint price;
        uint amount;
        uint date;
    }

    Bid[] public Bids;
    Bid[] public Asks;
    address public owner;
    address public contractAddress;
    address public parent;
    address public exchangeAddress;
    uint public balanceContract;
    Exchange ex;
    Household hh;


    constructor(uint capacity, address creator, address watch_address) public {
        owner = creator;
        batteryCapacity = capacity;
        parent = watch_address;
        contractAddress = address(this);
    }

    function deposit() public payable {
    }

    function () public payable {}

    function getBids(uint index) public view returns(address, uint, uint, uint){
        return (Bids[index].origin,
                Bids[index].price,
                Bids[index].amount,
                Bids[index].date
        );
    }

    function getAsks(uint index) public view returns(address, uint, uint, uint){
        return (Asks[index].origin,
                Asks[index].price,
                Asks[index].amount,
                Asks[index].date
        );
    }

    function setExchange(address exchange) public {
        exchangeAddress = exchange;
    }

    function charge(uint amount) public {
        amountOfCharge += amount;
    }

    function discharge(uint amount) public {
        amountOfCharge -= amount;
    }

    function submitBid(uint price, uint amount) public  {
        Bid memory newBid = Bid({
            origin: contractAddress,
            price: price,
            amount: amount,
            date: now
        });

        Bids.push(newBid);
        ex = Exchange(exchangeAddress);
        ex.submitBid(price, amount);
    }

    function submitAsk(uint price, uint amount) public  {
        Bid memory newAsk = Bid({
            origin: contractAddress,
            price: price,
            amount: amount,
            date: now
        });

        Asks.push(newAsk);
        ex = Exchange(exchangeAddress);
        ex.submitAsk(price, amount);
    }

    function buyEnergy(uint amount, address recipient, uint price ) external payable returns(bool successful){
        //require(address(this).balance > price);

        amountOfCharge += amount;

        hh = Household(recipient);
        hh.discharge(amount);
        recipient.transfer(price*amount/1000); //calculate for kWh

        return true;
    }

    modifier restricted() {
        require(msg.sender == owner);
        _;
    }

}

My Exchange Contract:

contract Exchange {

    struct Bid {
        address owner;
        uint price;
        uint amount;
        uint date;
    }

    struct Ask {
        address owner;
        uint price;
        uint amount;
        uint date;
    }

    Bid[] public Bids;
    Ask[] public Asks;
    Household hh;

    function deposit() public payable {
    }

    function () public payable{}


    function submitBid(uint _price, uint _amount) external returns (bool) {
        Bid memory b;
        b.owner = msg.sender;
        b.price = _price;
        b.amount = _amount;
        b.date = now;

        //if it's already higher price than the highest price in Bids, then place it at end of array
        //check if it's larger price than ask and match them if it is
        if (Bids.length > 0) {
            if (_price > Bids[Bids.length-1].price){

                Bids.push(b);

                if(Asks.length>0){
                    matchBid(Bids.length-1 ,Asks.length-1 );
                }
                return true;
            }
        }

        for(uint i = 0; i < Bids.length; i++) {
            if(Bids[i].price > _price) {
                Bid[] memory tempBids = new Bid[](Bids.length - i);
                for(uint j = i; j < Bids.length; j++) {
                    tempBids[j-i] = Bids[j];
                }
                Bids[i] = b;
                Bids.length = Bids.length + 1;
                for(uint k = 0; k < tempBids.length; k++) {
                    Bids[i+k+1] = tempBids[k];
                }

                if(Asks.length>0){
                    matchBid(Bids.length-1 ,Asks.length-1 );
                }
                return true;
            }
        }

        Bids.push(b);
        if(Asks.length>0){

            matchBid(Bids.length-1 ,Asks.length-1 );

        }
        return true;
    }

    function submitAsk(uint _price, uint _amount) external returns (bool) {
        Ask memory a;
        a.owner = msg.sender;
        a.price = _price;
        a.amount = _amount;
        a.date = now;

        //if it's already lower price than the lowest price in asks, then place it at end of array
        //check if it's lower price than bid and match them if it is
        if (Asks.length > 0) {
            if (_price < Asks[Bids.length-1].price){

                Asks.push(a);

                if (Bids.length>0){
                    matchBid(Bids.length-1,Asks.length-1 );
                }
                return true;
            }
        }

        for (uint i = 0; i < Asks.length; i ++) {
            if(Asks[i].price < _price) {
                Ask[] memory tempAsks = new Ask[](Asks.length - i);
                for (uint j = i; j < Asks.length; j++) {
                    tempAsks[j-i] = Asks[j];
                }
                Asks[i] = a;
                Asks.length = Asks.length + 1;
                for (uint k = 0; k < tempAsks.length; k++) {
                    Asks[i+k+1] = tempAsks[k];
                }

                if (Bids.length>0){
                    matchBid(Bids.length-1,Asks.length-1 );
                }
                return true;
            }
        }
        Asks.push(a);
        if(Bids.length > 0) {
            matchBid(Bids.length-1,Asks.length-1 );
        }
        return true;
    }

    function matchBid(uint bid_index, uint ask_index) public returns (bool) {
        if (Bids[bid_index].amount <= 0 || Bids[bid_index].price < Asks[ask_index].price) {
            cleanAskLedger();
            return true;
        }

        hh = Household(Bids[bid_index].owner);
        //uint remainder = Bids[bid_index].amount - Asks[ask_index].amount;
        uint price = (Asks[ask_index].price + Bids[bid_index].price) / 2;
        if(int(Bids[bid_index].amount - Asks[ask_index].amount) >= 0){
            uint remainder = Bids[bid_index].amount - Asks[ask_index].amount;
            uint calcAmount = Bids[bid_index].amount - remainder;

            hh.buyEnergy(calcAmount, Asks[ask_index].owner, price);

            Bids[bid_index].amount = remainder;
            Asks[ask_index].amount = 0;

            cleanAskLedger();
            return true;
        }

        if(int(Bids[bid_index].amount - Asks[ask_index].amount) < 0){
            remainder = Asks[ask_index].amount - Bids[bid_index].amount;
            calcAmount = Asks[ask_index].amount - remainder;

            hh.buyEnergy(calcAmount, Asks[ask_index].owner, price);

            Bids[bid_index].amount = 0;
            Asks[ask_index].amount = remainder;

            cleanBidLedger();
            return true;
        }
    }

    function cleanAskLedger() public returns (bool) {
        for(uint i = Asks.length - 1; i >= 0; i--) {
            if (Asks[i].amount == 0) {
                delete Asks[i];
            }
        }
        return true;
    }

    function cleanBidLedger() public returns (bool) {
        for(uint i = Bids.length -1; i >= 0; i--) {
            if(Bids[i].amount > 0) {
                delete Bids[i];
            }
        }
        return true;
    }

    function getBidsCount() public view returns(uint) {
        return Bids.length;
    }

    function getAsksCount() public view returns(uint) {
        return Asks.length;
    }
}

To clarify, what I am trying to do is call to submitBid (Household Contract) which calls submitBid (Exchange contract), which then calls matchBid (Exchange Contract) which then calls buyEnergy (Household Contract) to transact some corresponding ether to the bid placed by one household to the other. This method doesn't work, however, if I simply call buyEnergy from a Household Contract to another, it does with no problems. Could anyone help me understanding why I am getting this behaviour? I have no requires in these contracts which normally is the origin of this error.

Note: not using truffle, did the compile and deploy scripts myself, however, I am using ganache.

Your Answer

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

Browse other questions tagged or ask your own question.