0

I am trying to write a Prediction Markets smart contract in Solidity. Whenever I call the following function closeMarket in Remix, the function is reverted, even though all the parameters and require statements are all fulfilled. I also compiled the contract and no errors appeared.

The called function should be payable if you send value and the value you send should be less than your current balance.

 function closeMarket(string memory _eventName, bool result) payable public {
        Event storage currentEvent = events[_eventName];
        require(currentEvent.eventCreator == msg.sender, "Only the event creator can close this market");
        if (result) {
            // Calculate total profit for the "yes" outcome
            uint totalSharesYes = currentEvent.sharesYes;
            uint totalPayoutYes = totalSharesYes.mul(currentEvent.sharePriceYes);
            uint profitYes = totalPayoutYes.sub(totalSharesYes.mul(currentEvent.sharePriceYes.div(2)));
            balance -= profitYes;
            // Pay out profits to shareholders of the "yes" outcome
            uint profitPerShareYes = currentEvent.sharePriceYes.div(2);
            for (uint i = 0; i < totalSharesYes; i++) {
                address shareholder = currentEvent.shareholdersYes[i];
                payable(shareholder).transfer(profitPerShareYes);
            }
        } else {
            // Calculate total profit for the "no" outcome
            uint totalSharesNo = currentEvent.sharesNo;
            uint totalPayoutNo = totalSharesNo.mul(currentEvent.sharePriceNo);
            uint profitNo = totalPayoutNo.sub(totalSharesNo.mul(currentEvent.sharePriceNo.div(2)));
            balance -= profitNo;
            // Pay out profits to shareholders of the "no" outcome
            uint profitPerShareNo = currentEvent.sharePriceNo.div(2);
            for (uint i = 0; i < totalSharesNo; i++) {
                address shareholder = currentEvent.shareholdersNo[i];
                payable(shareholder).transfer(profitPerShareNo);
            }
        }
    }

I think there might be some kind of unhandled exception going on when the function is called, such as dividing by zero or something wrong with the arthemetic operations that I am running, but couldn't find the cause.

Here is the rest of my contract :

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract PredictionMarket {
    using SafeMath for uint;

    // The address of the contract owner
    address public owner;

    // A mapping of event names to event data
    mapping(string => Event) public events;

    mapping(uint => string) public eventIdToName;


    // The event data for a specific event
    struct Event {
        address eventCreator;
        string name;
        uint eventId;
        uint totalShares;
        uint sharePriceYes;
        uint sharePriceNo;
        uint sharesYes;
        uint sharesNo;
        address[] shareholdersYes;
        address[] shareholdersNo;
    }

    uint public eventId = 0;

    // The balance of the contract
    uint public balance;

    // Events for logging
    event Purchase(address buyer, string eventName, uint shares, uint totalCost);
    event Refund(address buyer, string eventName, uint shares, uint refundAmount);
    event NewEvent(address eventCreator, string eventName, uint totalShares, uint sharePriceYes, uint sharePriceNo);

    // Constructor function
    constructor() {
        owner = msg.sender;
    }

    // Function to create a new event
    function createEvent(string memory _eventName, uint _totalShares, uint _sharePriceYes, uint _sharePriceNo) public {
        require(_sharePriceYes > 0, "Share price must be greater than zero");
        require(_sharePriceNo > 0, "Share price must be greater than zero");
        eventIdToName[eventId] = _eventName;
        eventId++; 
        events[_eventName] = Event(msg.sender, _eventName, eventId, _totalShares, _sharePriceYes, _sharePriceNo, 0, 0, new address[](0), new address[](0));
        emit NewEvent(msg.sender, _eventName, _totalShares, _sharePriceYes, _sharePriceNo);
    }

    function getEventNameById(uint _eventId) public view returns (string memory) {
        return eventIdToName[_eventId];
    }

    // Function to buy shares in the "yes" outcome of a specific event
    function buySharesYes(string memory _eventName, uint _payment) payable public {
        Event storage currentEvent = events[_eventName];
        require(_payment >= currentEvent.sharePriceYes, "Insufficient payment");
        uint shares = _payment / currentEvent.sharePriceYes;
        require(shares <= currentEvent.totalShares - currentEvent.sharesYes, "No more shares available");
        currentEvent.sharesYes += shares;
        balance += _payment;
        currentEvent.shareholdersYes.push(msg.sender);
        events[_eventName] = currentEvent;
        emit Purchase(msg.sender, _eventName, shares, _payment);
    }

    // Function to buy shares in the "no" outcome of a specific event
    function buySharesNo(string memory _eventName, uint _payment) payable public {
        Event storage currentEvent = events[_eventName];
        require(_payment >= currentEvent.sharePriceNo, "Insufficient payment");
        uint shares = _payment / currentEvent.sharePriceNo;
        require(shares <= currentEvent.totalShares - currentEvent.sharesNo, "No more shares available");
        currentEvent.sharesNo += shares;
        balance += _payment;
        currentEvent.shareholdersNo.push(msg.sender);
        events[_eventName] = currentEvent;
        emit Purchase(msg.sender, _eventName, shares, _payment);
    }

    function closeMarket(string memory _eventName, bool result) payable public {
        Event storage currentEvent = events[_eventName];
        require(currentEvent.eventCreator == msg.sender, "Only the event creator can close this market");
        if (result) {
            // Calculate total profit for the "yes" outcome
            uint totalSharesYes = currentEvent.sharesYes;
            uint totalPayoutYes = totalSharesYes.mul(currentEvent.sharePriceYes);
            uint profitYes = totalPayoutYes.sub(totalSharesYes.mul(currentEvent.sharePriceYes.div(2)));
            balance -= profitYes;
            // Pay out profits to shareholders of the "yes" outcome
            uint profitPerShareYes = currentEvent.sharePriceYes.div(2);
            for (uint i = 0; i < totalSharesYes; i++) {
                address shareholder = currentEvent.shareholdersYes[i];
                payable(shareholder).transfer(profitPerShareYes);
            }
        } else {
            // Calculate total profit for the "no" outcome
            uint totalSharesNo = currentEvent.sharesNo;
            uint totalPayoutNo = totalSharesNo.mul(currentEvent.sharePriceNo);
            uint profitNo = totalPayoutNo.sub(totalSharesNo.mul(currentEvent.sharePriceNo.div(2)));
            balance -= profitNo;
            // Pay out profits to shareholders of the "no" outcome
            uint profitPerShareNo = currentEvent.sharePriceNo.div(2);
            for (uint i = 0; i < totalSharesNo; i++) {
                address shareholder = currentEvent.shareholdersNo[i];
                payable(shareholder).transfer(profitPerShareNo);
            }
        }
    }

}

Any help would be appreciated. Thanks

2
  • can you add the functions in order you called and the sample data along with it, I was not able to recreate the issue, in both true and false paths? Dec 25, 2022 at 10:41
  • 1. I called createEvent, set event name to = "Event1", _totalShares = 1,000,000 , _sharePriceYes = 80, _sharePriceNo = 20 -> 2. Then I called buySharesYes, entered the same event name, and set payment = 160 (bought 2 yes shares) ->. 3. Then I called buySharesNo, entered the same event name, and set payment = 40 (bought 2 no shares) ->. 4. Called closeMarket, the problematic function, set result = true. I got the error from here. I am wondering, what values did you put for the mentioned parameters that got it to work? Thanks!
    – Anon.eth
    Dec 25, 2022 at 13:53

1 Answer 1

0

According to the last comment there are a particularly two issues that I noticed.

  1. following loops gives index out of bounds hence the looping condition is wrong.

       for (uint i = 0; i < totalSharesYes; i++) - instead of this, 
       for (uint i = 0; i < totalSharesYes-1; i++) - this should be there
    
       for (uint i = 0; i < totalSharesNo; i++) - instead of this, 
       for (uint i = 0; i < totalSharesNo-1; i++) - this should be there
    
  2. Ether transfers that you have done is wrong. instead of _payment what you should be doing is using msg.value to get the ether payments.

    payable(shareholder).transfer(profitPerShareYes)
    

    referring to the above code line, Here you are trying to pay the shareholder back, but according to your comment seems you haven't paid the ether values when the buys are happening. Hence, when you are trying to payback the shareholders, the contract is not having any ether balance, which is the main cause you error.

There is a small trick that you can do to understand the issue betterProof after you fix the issue 01 and when you are re running, you can follow the steps you mentioned in the comment, in the last step instead of paying 0 ethers pay 40, you will see the function is getting executed properly. This is because with the close market function you are giving the contract the Ether which it needs to transfer to its shareholders.

2
  • Thanks a lot, it works.
    – Anon.eth
    Dec 26, 2022 at 14:30
  • @Anon.eth you are welcome :) Dec 26, 2022 at 15:50

Your Answer

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

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