Programming Project - Phase 1

Thank you @dan-i I figured it out! To update the metamask account balance on the front end I simply have to move the window.ethereum.enable() into the function itself:

function refreshMetamaskBalance() {
  //Get player's Metamask account balance and address
  window.ethereum.enable().then(function(accounts) {

    this.web3.eth.getBalance(accounts[0], (err, balance) => {
      balance = web3.utils.fromWei(balance, "ether");
      $("#metamask_balance_output").text(balance + " ETH");
      console.log("Metamask account balance: " + balance + " ETH");
    });
  });
}

Could you explain to me why is this?

Also - could you elaborate more on how I would “catch” an event with web3 or "save the result and get it with a .call function? I did emit an event but I’m not sure what you mean by catching it. Is this going into the logs to find values of payable functions because payable functions cannot return values?

I’ve finished this phase of the project and here are my screenshots. Thank you so much for all your help!

0 init 1 place bet 5 2 results lose 3 place bet 10 4 results lose 5 place bet 15 6 results win

Hey @mervxxgotti

Regarding catching events, I wrote a basic FAQ that should clarify your doubts :slight_smile:
Check it out and let me know: FAQ - How to listen for events

About save the result and get it with a .call function what I mean is the following, let me give you an example:

pragma solidity 0.7.5;

contract Test {

    struct User {
        uint number;
    }
    
    mapping(address => User) public users;
    
    
    /**
     * Set number in User struct.
     * @param _n The number to save.
     */ 
    function setNumber (uint _n) public returns (uint) {
        users[msg.sender].number = _n;
        return users[msg.sender].number;
    } 
    
    /**
     * Get number in User struct.
     */ 
    function getNumber () public view returns (uint) {
        return users[msg.sender].number;
    }
}

From your front end, you would call:

instance.method.setNumber(10).send();

If you would save the returned value of the call above, you would end up with the transaction receipt.

So I created another function in the contract getNumber that just returns the number.

instance.method.getNumber().call();

You could do:

function test () {
    instance.method.setNumber(10).send();
    const result = instance.method.getNumber().call();
    console.log(result);
}

You should use events anyway, that’s the right path to follow.

Cheers,
Dani

Hi there.

Source code for the project can be found here: https://github.com/nielvosloo/iot-coinflip
And a short video showing it’s functionality can be seen here https://drive.google.com/file/d/1_NZKUxjmW_6KproQwBvITvA20nJTheSX/view?usp=sharing

Hello. I would like for you to review my code and tell me what else I need to do

Thanks
Claude

/*--------------------------------------------Flip.sol----------------------------------------------------------*/
import "./Ownable.sol";
pragma solidity 0.5.12;

contract Flip is Ownable{

    uint public balance;

    modifier costs(uint cost){
        require(msg.value >= cost);
        _;
    }

  //  event flipCreated(uint balance, address creator);

    function createFlip() public payable costs(1 ether){
      require(msg.value >= 1 ether);
      balance += msg.value;

    // emit flipCreated(balance, msg.sender);
    }

    function flip() public view returns (uint){
      return now % 2;

    }

   function withdrawAll() public onlyOwner returns(uint) {
       uint toTransfer = balance;
       balance = 0;
       msg.sender.transfer(toTransfer);
       return toTransfer;
   }

   function getBalance() public view returns(uint){
        uint personsBalance = balance;
        return personsBalance;
   }

}


/*-----------------------------------------Main.js-----------------------------------------------------------------*/

var web3 = new Web3(Web3.givenProvider);
var contractInstance;
var wager;


$(document).ready(function() {
    window.ethereum.enable().then(function(accounts){
        contractInstance = new web3.eth.Contract(abi, "0x1B40F0090428a9B81FbC5a9c291eAFA4B068F8AC", {from: accounts[0]});
        console.log(contractInstance);
    });

    $("#coinFlip").click(winLose);
    $("#get_wager").click(inputWager);
  });


    function inputWager(){
        wager = $("#wager").val();

        sendCoins(wager);
    }

    function sendCoins(arg1){

        var config = {
          value: web3.utils.toWei(arg1, "ether")
        }

        contractInstance.methods.createFlip().send(config)
        .on("transactionHash", function(hash){
          console.log(hash);
        })
        .on("confirmation", function(confirmatinoNr){
          console.log(confirmationNr);
        })
        .on("receipt", function(receipt){
          console.log(receipt);
        })
    }


    function winLose(){

      contractInstance.methods.flip().call().then(function(flip){
        var choice = $("input[type='radio'][name='choices']:checked").val();
        console.log(choice);

        if(flip == 1 && choice == "heads"){

            $("#result_output").text("YOU WIN "+ wager +" coins");
            console.log("heads you win :"+ wager);
            //sendCoins(wager);
        }
        else if (flip == 1 && choice != "heads") {
            //lostCoins = contractInstance.methods.withdrawAll().call();
            $("#result_output").text("YOU LOSE " + wager + " coins");
            console.log("heads you lose :"+ wager);
        }
        else if (flip == 0 && choice == "tail") {
            $("#result_output").text("YOU WIN "+ wager +" coins");
            //sendCoins(wager);
            console.log("tail you win :" + wager);
        }
        else if (flip == 0 && choice != "tail"){
          //  lostCoins = contractInstance.methods.withdrawAll().call();
            $("#result_output").text("YOU LOSE " + wager + " coins");
            console.log("tail you lose :"+ wager);
        }

      });


    }
1 Like

Hey @nielvosloo

Why are you using a for loop to generate a binary random number?
You code uses lots of resources (gas) compared to what it is actually doing :slight_smile:
Because you are coding on a blockchain, make sure to keep you code as light as possibile and avoid loops / array and strings as much as you can.

See you in Phase two :slight_smile:

Happy coding,
Dani

1 Like

This is my solution:

1 3

1 Like

I’m having trouble getting this to work in remix. All functions are working except for my coinFlip function.

getting:

[vm]

from: 0x5B3...eddC4

to: CoinFlip.coinFlip() 0x236...b00D8

value: 1000000000000000000 wei

data: 0x5ac...947cb

logs: 0

hash: 0x9c4...4cc5a

Debug
transact to CoinFlip.coinFlip errored: VM error: invalid opcode. invalid opcode The execution might have thrown. Debug the transaction to get more information.

I’ve been unsuccessful at debugging. @filip can you see any obvious errors in my coinFlip logic? What am I missing? My front end is almost totally wired up, I just can’t get this function to work!

pragma solidity ^0.5.12;

import "./Ownable.sol";

import "node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol";

// Contract executes the following:
// Allows for ETH deposits
// Allows for ETH withdrawals
// Generates a random number 1 or 0 with 50/50 probability
// Executes a simulated coin flip, adding 100% of wager to balance if won, or deducting 100% of wager if lost
// emits Deposit, Withdrawal and Flip events

contract CoinFlip is Ownable {
  using SafeMath for uint;
  // Variables
  uint private balance;
  uint private bet;
  uint private treasury;
  uint private allPlayersBalance;

  constructor() public payable {
    uint initFund = msg.value;
    treasury = treasury.add(initFund);
    assert(treasury == address(this).balance);
  }

  // mappings
  // Tracks the user balances stored in the contract
  mapping (address => uint256) private playerBalances;

  // players array
  address payable[] public players;

  // events
  event Deposit(address user, uint256 amount, uint256 balance);
  event Withdraw(address user, uint256 withdrawAmount, uint256 currentBalance);
  event Bet(address user, uint256 bet, string outcome);
  event Fund(uint value);
  // structs
  
  // modifiers
  modifier costs(uint cost) {
    require(msg.value >= cost, "The minimum bet is 0.001 Ether.");
    _;
  }

  // functions

  // player deposits funds into contract for betting use
  function deposit() public payable returns(uint){        
      playerBalances[msg.sender] = playerBalances[msg.sender].add(msg.value);
      allPlayersBalance = allPlayersBalance.add(msg.value);
      assert(treasury + allPlayersBalance == address(this).balance);
      emit Deposit(msg.sender, msg.value, playerBalances[msg.sender]);
      return playerBalances[msg.sender];
  }

  // owner funds the treasury balance
  function fundTreasury() public payable onlyOwner returns(uint) {
    require(msg.value != 0);
    treasury = treasury.add(msg.value);
    emit Fund(msg.value);
    assert(treasury + allPlayersBalance == address(this).balance);
    return msg.value;
  }

  // querries the player's available balance in the contract
  function playerBalance() public view returns (uint256){
    return playerBalances[msg.sender];
  }

  // the total balance available in the contract treasury, these funds still belong to owner
  function getBalance() public view returns (address, uint) {
    return(address(this), treasury);
  }

  // the total funds in the contract, including all player funds + treasury funds
  function totalBalance() public view returns (address, uint) {
    assert(treasury + allPlayersBalance == address(this).balance);
    return(address(this), address(this).balance);
  }

  // only player can withdraw their own funds, contract owner cannot withdraw or transfer these funds
  function playerWithdraw(uint256 _amount) public payable {
      require(playerBalances[msg.sender] >= _amount);
      playerBalances[msg.sender] = playerBalances[msg.sender].sub(_amount);
      allPlayersBalance = allPlayersBalance.sub(_amount);
      msg.sender.transfer(_amount);
      assert(treasury + allPlayersBalance == address(this).balance);
      emit Withdraw(msg.sender, _amount, playerBalances[msg.sender]);
  }

  // only owner can withdraw the treasury funds
  function withdraw(uint _amount) public onlyOwner returns(uint) {
    require(_amount <= treasury);
    msg.sender.transfer(_amount);
    treasury = treasury.sub(_amount);
    assert(treasury + allPlayersBalance == address(this).balance);
  }
  
  function random() public view returns (uint) {
    return now % 2;
  }

  function coinFlip() public payable costs(0.001 ether) returns(string memory) {
    require(msg.value <= playerBalances[msg.sender]);
    require(msg.value <= treasury);

    uint256 randomNum = random();
    string memory outcome;
    if(randomNum == 1){
      outcome = "win";
      playerBalances[msg.sender] = playerBalances[msg.sender].add(msg.value);
      allPlayersBalance = allPlayersBalance.add(msg.value);
      treasury = treasury.sub(msg.value);

    } else {
      outcome = "lose";
      playerBalances[msg.sender] = playerBalances[msg.sender].sub(msg.value);
      allPlayersBalance = allPlayersBalance.sub(msg.value);
      treasury = treasury.add(msg.value);
    }
    
    assert(treasury + allPlayersBalance == address(this).balance);
    emit Bet(msg.sender, msg.value, outcome);
    return outcome;
  }
}

Hi @jonsax

The error is in your assert statement, double check that one.
I am able to play by:

  • removing the assert;
  • deploy contract with 10 eth;
  • deposit 5 ether so that player balance is updated;
  • play;

@filip @dan-i
Just had to get this information out here, these courses were my introduction to MetaMask, and while using mainly Ganache + Meta, I started using Meta for some ETH on Mainnet, and also Testnet. in one of the courses, Filip showed how to store a .secret file with a seep phrase, little did I know, that the seed phrase was the same for Main- and Test-net, so I pushed the code + the .secret file to GitHub, two days ago, my funds from ETH MainNet was gone, 630 SAND Tokens and about 0.2 ETH.

This is a very expensive way to learn about MetaMask, so I implore you to include some information about this in the courses. I can’t remember if there was any explanation on this, if there is, let this still be a lesson that everyone can learn from :sweat_smile:

Hey @voljumet

I am really sorry for what happened, unfortunately there are bots that scan Github waiting for seeds.
We suggest to check the git ignore faq in the description of this video: https://academy.ivanontech.com/products/old-ethereum-smart-contract-programming-201/categories/2004134/posts/6718484

This is the faq I wrote: FAQ - How to .gitignore

Again I can imagine how frustrating that is.

Cheers,
Dani

Thank you, I’m familiar with git and .gitignore, but I was not familiar with MetaMask and how the seed works across all networks, so that was my error here. :sweat_smile:

Here’s a screen record of my dapp. I created it using React/Redux.

-Deposit ETH into contract to “Fund Treasury” (only owner)
-Deposit ETH into contract to play the coinFilp game (owner and public)
-Option to choose a username to see your bets tracked on the “Leaderboard”
-Withdraw ETH from player accounts (only account owners can transfer funds, owner cannot)
-Play with funds using the CoinFlip function inside contract, players can only bet with deposited funds
-A “Leaderboard” tracks all bets placed according to “username” from highest win to highest loss.
-Implemented a CoinFlip animated spinning coin as the “spinner” to show during async function awaits.

Here is a link to the project in action:
https://drive.google.com/file/d/1950Onrnb3frCnF4dNAxGX0-gpOYBoyrD/view?usp=sharing

Let me know what y’all think!!

1 Like

Well done I think it’s really cool! I also like the leaderboard :smiley:
Ready for phase two, can’t wait to see the final result.

Happy coding,
Dani

I’m having some issues with this contract. When I deployed to Kovan, I am only losing. None of my bets are winning at all. Is there an obvious flaw in my code?

  function random() public view returns (uint) {
    return now % 2;
  }

  function coinFlip(uint _amount) public returns(string memory) {
    require(_amount <= playerBalances[msg.sender]);
    require(_amount <= treasury);

    string memory _username = usernames[msg.sender];
    uint256 randomNum = random();
    string memory outcome;

    if(randomNum == 1){
      outcome = "win";
      playerBalances[msg.sender] = playerBalances[msg.sender].add(_amount);
      allPlayersBalance = allPlayersBalance.add(_amount);
      treasury = treasury.sub(_amount);

    } else {
      outcome = "lose";
      playerBalances[msg.sender] = playerBalances[msg.sender].sub(_amount);
      allPlayersBalance = allPlayersBalance.sub(_amount);
      treasury = treasury.add(_amount);
    }

    assert(treasury + allPlayersBalance == address(this).balance);
    emit Bet(msg.sender, _username, _amount, outcome, now);
    return outcome;
  }

I’m wondering if there’s an issue where due to the longer block confirmations, perhaps the “now” time is always at the start of a block, so it’s always even? Maybe for testnets the pseudo-randomness doesn’t work? Anyone else have this issue??

1 Like

Hey @jonsax

Testnets and mainned blocktime is different than the Javascript VM one.
The Javascript VM mines a block for each transaction you send, the blockchain has instead a random blocktime (around 10 seconds for each block) therefore you might have to wait some time before having different results (win/ loss).

Copy paste this code and run it many times:

pragma solidity 0.6.0; 

contract Game { 
    

    function coinFlip() public view returns(uint) {
      
       return block.timestamp;

    }

   
}

Check the timestamp :slight_smile:

Cheers,
Dani

I am just gonna share the smart contract source here. I created a simple version of betting Dapp: generate random number (‘0’ or ‘1’), pay fund to contract, pay fund to player, contract balance, etc

pragma solidity 0.6.12;

contract FlipBet {
    uint public contractBalance;
    
    constructor() public payable {
        sendFundToContract();
    }

    function random() public view returns(uint) {
        return uint(keccak256(abi.encodePacked(block.difficulty, now))) % 2;
    }

    function sendFundToPlayer(uint amt) public {
        require(amt <= contractBalance, "Contract doesn't have enough balance!");
        msg.sender.transfer(amt);
        contractBalance -= amt;
    }

    function sendFundToContract() public payable {
        require(msg.value <= msg.sender.balance, "Player doesn't have enough balance!");
        contractBalance += msg.value;
    }
    
    function getContractBalance() public view returns(uint) {
        return address(this).balance;
    }
}

Here is the deployed contract on rinkeby: https://rinkeby.etherscan.io/address/0x160f0e828909ec4b9f21e5b4d20c6694d48b0c78

You can try to interact with it using remix. Thanks.

1 Like

Here’s a screen recording of my coin flip app: https://vimeo.com/547442125

No prizes for design, but hey it gets the job done :smile:
If the user wins, they get 2X their total bet, otherwise they lose it all.
On deployment, 20 ETH is sent to the contract for prize money.
If the user wins but the contract can’t afford the payout, it sends all remaining balance instead.
Thanks.
Paddy

1 Like

Good job @paddyc1, ready for phase two!

1 Like

Hey guys so i finally got around to coding the first part of the coinflip dapp with the non external source of randomness aka just using the function Filip provides. The conract is very simple really so i decided to try make a nicer front end and put more time into that rather than the backend even though my front end isnt amazing either. This is actually the first front end or webpage i have ever made a have learned a lot so far. Front end is very fun and addictive i find because you can litreally find an awnser to any style your trying to implement on youtube and tweak a few things and keep doing that over and over

Having said that i have only begun to link the backend its far from done there are still many functions i have to implement such as a deposit/ withdrawal function. Currently i have it so that the pay dirctly into the contract but i would prefer to have a deposit function and then you can play with the funds you deposit in. I have still not implemted withdrawals and there is so much more interactivity i plan to add as i am only just beginning to add loading messages between seting the net and flipping the coin etc but just posting this here because i pretty much have the first part done and this feels good for a first draft to share as my work for the first part.

I am planning to wrap up everything tomorrrow or the day after by putting in a solid 8-10 hours more and make it real nice. Keep yous updated and as always i will do a indetail post walking through how i did everything and what i learned along the way. Below is the Link to a small video demo of the bare bones.

Capture.PNG

Video Link
https://vimeo.com/manage/videos/557864688

**EDIT theres a typo in my video its the balance is displayed in wei i frogot to do the conversion will chnge that

2 Likes

See you in phase two :slight_smile:

1 Like