Using functions to execute business logic

So, we've gained some confidence with functions in how they work and how we create them, so now let's put this into practical context and see how we use them to implement the business logic of our smart contract. And thinking back to our app, we're going to display a random number to our player in our UI, and they'll place a wager on whether they think the mystery number is going to be higher or lower. The displayed number and their wager are sent to our smart contract, where we execute the rules of the game using Solidity code: 

Let's take a look at our winOrLose function once again. It's changed a little bit since we last saw it. When the player is ready to play around, they'll make their wager and the app will call this function. When it does, it will send along the number that was displayed to the player, the players guess, and their wager will be attached to this transaction as the special msg.value variable. Since this gets called from our UI, it has to be marked as external, and since it receives their wager in the form of ether, it has to be marked as payable.

We define that it returns two objects: a boolean and an unsigned integer. The boolean indicates whether they won or lost, and the unsigned integer will return the mysteryNumber_ that they were betting against. That's going to allow us to show the player the mysteryNumber_ when we tell them that they won or lost:

function winOrLose(unit display, bool guess, unit wager) external payable returns (bool, unit) {
/* Use true for a higher guess, false for a lower guess */
require(online == true);
require(msg.sender.balance > msg.value, "Insufficient funds");
unit mysteryNumber_ = mysteryNumber();
bool isWinner = determineWinner(mysteryNumber_, display, guess);
if (isWinner == true) {
/* Player won */
emit PlayerWon(msg.sender, msg.value);
msg.sender.transfer(msg.value * 2);
return (true, mysteryNumber_);
} else if (isWinner == false) {
/* Player lost */
return (false, mysteryNumber_);
}
}

Inside our function, we have two require statements. I'm going to skip over those because we'll be covering them in detail in the next section. 

We have an unsigned integer that gets its value from a function called mysteryNumber; that means our mysteryNumber function must return an unsigned integer – we declare that when we define the function – and since nothing outside of our contract needs to access this function we'll mark it as private. Our function also doesn't do anything to modify the state. It simply returns a number so we can mark it as a view function as well:

function mysteryNumber() private view returns (unit) {
unit randomNumber = unit(blockhash(block.number-1))%10 + 1;
return randomNumber;
}

And now we have all the data we need to determine if the player won or lost this round. So, we declare a boolean called isWinner that's going to get its value by sending all of the needed information to the determineWinner function, and that function looks like this:

function determineWinner(unit number, unit display, bool guess)
internal pure returns (bool) {
if (guess == true} {
if (number > display) {
return true;
}
} else if (guess == false) {
if (number > display) {
return false;
}
}
}

It has parameters to accept a mystery number, the displayed number, and the player's guess. Once again, there's no reason for anything outside of the contract to call this function, so it's marked as internal, and because it doesn't read or modify state, we mark it as pure. Next, we go through all the different combinations of winning and losing, and once we have enough information to determine whether this round was a win or a loss, we exit the function using return, and then true for a win or false for a loss. When that function returns, it leaves us right here, where we can evaluate the isWinner variable, then take the appropriate action based on a win or a loss, which includes emitting events to indicate the status of that round, sending any money won back to the player, and returning the result of that round for the player to see. Let's take a look at that visually to help cement the relationships. Our app calls the winOrLose function, which in turn gets a new mystery number from the mysteryNumber_ function, then it sends the data over to the determineWinner function to see if the player won or lost, then it takes the appropriate actions, and finally notifies the player of the results. And next up, we're going to talk about modifiers. They're powerful functions that allow you to place constraints around when your function should and should not execute.