Need help personal project - user predicts up or down -> get oracle price to calc results -> output address list of winners

Hi all,

Working on a personal project and ran into a bit of a wall… hope someone here can give some insight or pointers on how to move onto the next step.

There’s still a lot of broken parts, and not expecting anyone to write a complete magic solution, but hoping some of you experts can give any insight at all into how i can improve the code or the logic, or what mistakes there are (e.g. using the wrong structures to store the data) and generally any small or big things I can do to improve.

I’m very very very new to programming and completed the solidity 101 course less than a week ago so any help at all would be appreciated!! Thank you in advance!!! <3 <3 <3

OVERVIEW

What does the contract do?

Users will be able to make a binary outcome prediction, i.e. will price of ETH be higher or lower when a ‘round’ ends vs when it started.

How does it work?

Please see comments at the beginning of code block for a detailed explanation of how I’m thinking about the solution, and the code plan listing some inputs, functions, calculations required.

Problem

/* UP OR DOWN
 *
 * Users can make predictions on whether the price of ETH will be higher or lower when a Round closes vs when it opened.
 *
 * A round is started by owner calling `createRoundAndStart`, and also sets the timeNoMoreBets and timeEnd.
 * During the round duration before timeNoMoreBets, any user can call `submitPrediction` to submit their answer.
 * After timeNoMoreBets, no more predictions can be submitted.
 * After timeEnd, call `computeResult` to get the `priceEnd`. 
 * Then compute if the round was up or down, and compute and return the list of Winners of that round.
 */


// CODE PLAN

    // types of data points:
    // 1. list of ADDRESS => PREDICTIONS 
    // 2. list of ROUNDS => INDEX, STARTTIME, ENDTIME, NOMOREBETS, STARTPRICE, ENDPRICE, RESULT
    // 3. for ROUND[index] ==> list of predictors addresses => PREDICTION, WIN/LOSE

    // functions to:
    //create (and start) a round
    //let user submit a prediction
    //after round end, get endPrice
    //w endPrice we know if startPrice higher or lower, and hence know if user's prediction is Win or Lose

    // what outputs do i want?
    //For ROUND[n], i want to know the data: startPrice, endPrice, boolean for Up or Down
    //For ROUND[n], i also want to know the list of all addresses that submited prediction + what was their prediction,
    //              from this list compare UpOrDown result to know if their PredictionWinOrLose is true or false.
    //          i.e. ADDRESS => UPorDOWN.boolean => ... compare with results ... => PredictionWinOrLose.boolean
    // Then LIST all WINNERS ; and list all LOSERS

pragma solidity 0.8.15;

contract UpOrDown {

    // 3 lines to make onlyOwner work in function headers:

    address owner;

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

    constructor() {
        owner = msg.sender;
    }

    // Handling owner create round

    struct Round{
        uint roundId;
        uint timeStart;
        uint timeEnd; // input for createRound
        uint noMoreBets; // input for createRound
        uint priceStart; 
        uint priceEnd; // THIS IS NOT KNOWN WHEN CREATEROUND. CAN PUSH NULL VALUE TO THE ARRAY?
        bool roundResult; // THIS IS NOT KNOWN WHEN CREATEROUND. CAN PUSH NULL VALUE TO THE ARRAY?
    } 

    Round[] roundLog;

    function createRoundAndStart(uint _timeEnd, uint _noMoreBets) public onlyOwner {
        // require timeStart > timeCurrent
        _priceStart = getLatestPrice(); // priceStart
        _timeStart = getLatestPriceTimestamp(); // timeStart // or should it be the time/block the function was executed

        roundLog.push( Round( roundLog.length, _timeStart, _timeEnd, _noMoreBets, _priceStart ) );
    } 
    // add chainlink oracle feed to get latestPrice=startPrice.
    
    // timeEnd and noMoreBets can then become input with SECONDS from now instead of unix timestamp. i.e. roundDuration, and noMoreBetsCutoff (e.g. 60 mins before roundEnd)
    // add priceStart to struct

    // Handling user submit prediction

    struct Users{
        uint userId;
        address predictor;
        bool prediction; // true for up, false for down
    }

    Users[] userLog;

    function submitPrediction(bool _prediction) public {
        userLog.push( Users( userLog.length, msg.sender, _prediction ) );
    }

    // REMAINING TASKS

    // // get priceStart and priceEnd for timeStart and timeEnd // no need timeStart anymore; priceStart generated with createRoundAndStart
    // ==> get priceEnd for timeEnd

    // HOW DO I GET THE priceEnd ?
    // AT timeEnd, HOW DO I AUTOMATICALLY CALL A FUNCTION?
    //      OR AT ANY TIME AFTER timeEnd, I must manually CALL FUNCTION, 
    //      which will check back to the price at timestamp that's closest to the timeEnd of round.

    function fetchPriceEndAndComputeResults() public {
        // if timeCurrent > timeEnd, fetch priceEnd
        _priceEnd = getLatestPrice(); // function need to look back at price timestamps to determine closest one to endTime.
        /* e.g. 
         * get latest roundID, latest timeStamp from priceFeed. also get endTime of this contract.
         * count roundID down from current with i--, until we find a timeStamp that is < than endTime.
         * take the 2nd last one (the last one which timeStamp > endTime), and get PRICE for that timestamp, equal to = priceEnd !!
         */
        
        // if priceEnd >= priceStart , then true
        // if priceEnd < priceStart , then false
        if (_priceEnd >= priceStart) {
            _roundResult = true;
        } else {
            _roundResult = false;
        }

        roundLog.push( Round( x, x, x, x, x, _priceEnd, _roundResult  ) );

    }

    function getWinners() public {
        // `roundResult` and `prediction` both true or both false means USER==WIN i.e. bool.userPrediction == bool.roundResult
        // Get USERS[address] list , by filtering all users in the round where USERS[prediction] == ROUND[roundResult]
        for roundLog[roundResult]
    }
        
}



    // // HOW TO HANDLE USERS IN MULTIPLE ROUNDS? make no clashes in predictions/results etc., how to structure rounds/users/...? 

    // array[Round, Users]
    // array[Round, Users, Results]