Programming Project - Phase 2

Hi All,

Please See below my Github Repository Link :point_down:
https://github.com/Suveett/CoinFlip-Phase-2.git

Also, some Screenshots of my Code:

Also My solidity code as Follows :

import "./provableAPI.sol";
import { SafeMath } from "./SafeMath.sol";
pragma solidity 0.5.12;

contract Coinflip is usingProvable{

  using SafeMath for uint;

  //Events to listen for :
  event RandomNumberGenerated(uint randomNumber);
  event LogNewProvableQuery(address indexedPlayer);
  event FlipResult(address user, uint amountWon, bool won);
  event ContractFunded(address contractOwner, uint amount);

  // Constructors and Modifiers :
  constructor() public {
    provable_setProof(proofType_Ledger);
    contractBalance = 0;
    contractOwner = msg.sender;
    }

  modifier onlyOwner() {
      require(msg.sender == contractOwner, "You are not the owner");
      _;
  }

  modifier costs(uint cost) {
      require(msg.value >= cost, "Minimum amount >= 0.01 ether");
      _;

  }

  //addresses and other such state Variables:
  address public contractOwner;
  uint public contractBalance;
  address public contractAddress;
  uint private constant NUM_RANDOM_BYTES_REQUESTED = 1;

    struct Temp {
      bytes32 id;
      address playerAddress;
    }

    struct PlayerByAddress {
      address playerAddress;
      uint playerBalance;
      bool inGame;
      uint betAmount;
    }

    mapping(bytes32 => Temp) public temps;
    mapping(address => PlayerByAddress) public playersByAddress;

    //Flip the Coin and check whether user won or lost;
    function flipCoin() public payable costs(0.01 ether) {
        require(address(this).balance >= msg.value, "The contract doesnt have enough balance to play right now. Come Back later");
        require(_isPlaying(msg.sender) == false, "The User currently is in Game");

        playersByAddress[msg.sender].playerAddress = msg.sender;
        playersByAddress[msg.sender].betAmount = msg.value.sub(provable_getPrice("random")); // Contract keeps oracle's fee.
        playersByAddress[msg.sender].inGame = true;



        _update();
    }



      // @dev               Calls the oracle random function.
      //                    Sets Temp for the given player.
      function _update() internal {
        uint QUERY_EXECUTION_DELAY = 0;
        uint GAS_FOR_CALLBACK =200000;
        bytes32 query_id = provable_newRandomDSQuery(QUERY_EXECUTION_DELAY, NUM_RANDOM_BYTES_REQUESTED, GAS_FOR_CALLBACK);

        temps[query_id].id = query_id;
        temps[query_id].playerAddress = msg.sender;

        emit LogNewProvableQuery(msg.sender);
      }

      // @dev               The callback function called by the oracle once the random number is created.
      // @param _queryId    The query unique identifier that is the key to the player Temp.
      // @param _result     The random number generated by the oracle.
      // @param _proof      Used to check if the result has been tampered.
      function __callback(bytes32 _queryId, string memory _result, bytes memory _proof) public {
        require(msg.sender == provable_cbAddress());

        if (provable_randomDS_proofVerify__returnCode(_queryId, _result, _proof) == 0){
          uint randomNumber = uint(keccak256(abi.encodePacked(_result)))%2;
          _verifyResult (randomNumber, _queryId);
          emit RandomNumberGenerated(randomNumber);

        }
      }


      function _verifyResult(uint _randomNumber, bytes32 _queryId) internal {
        address player = temps[_queryId].playerAddress;
        if(_randomNumber == 1){
          playersByAddress[player].playerBalance = playersByAddress[player].playerBalance.add(playersByAddress[player].betAmount.mul(2));
          emit FlipResult(player, playersByAddress[player].betAmount, true);
        }
        else {
          contractBalance = contractBalance.add(playersByAddress[player].betAmount);
          emit FlipResult(player,playersByAddress[player].betAmount, false );
        }

        delete(temps[_queryId]);
        playersByAddress[player].betAmount = 0;
        playersByAddress[player].inGame = false;
      }


    function getBalance() public view returns(address, uint, uint) {
      return(address(this), address(this).balance, contractBalance);
    }



    // withdraw all funds possible only though contractOwner address;
    function withdrawAll() public onlyOwner returns(uint){
        require(playersByAddress[msg.sender].playerBalance > 0, "You are not the Owner");
        msg.sender.transfer(address(this).balance);
        assert(address(this).balance == 0);
        return address(this).balance;
    }



    function fundContract() public payable onlyOwner returns(uint) {

        require(msg.value != 0);
        emit ContractFunded(msg.sender, msg.value);
        return msg.value;
    }

    function _isPlaying(address _player) public view returns(bool){
      return playersByAddress[msg.sender].inGame;
    }
}

Also, see below my main.js file :point_down:

var web3 = new Web3(Web3.givenProvider);
var contractInstance;
const contractAddress = "";

$(document).ready(async function(){
  contractInstance = new web3.eth.Contract(abi,contractAddress);
  await connectMetamask();

    console.log(contractInstance);
    console.log(`Use Contract address: ${contractInstance._address}`)



    $("#flip_button").click(flipCoin);
    $("#get_balance").click(fetchAndDisplay);
    $("#fund_contract_button").click(fundContract);
    $("#withdraw_funds").click(withdrawAll);



});

//EVENT LISTENERS

contractInstance.once('LogNewProvableQuery',
{
  filter: { player: await getPlayerAddress() },
  fromBlock: 'latest'
}, (error, event) => {
  if(error) throw("Error fetching events");
  jQuery("#events").text(`User ${event.returnValues.player} is waiting for the flip result`);
});

contractInstance.once('FlipResult',
{
  filter: { player: await getPlayerAddress() },
  fromBlock: 'latest'
}, (error, event) => {
  if(error) throw("Error fetching events");
  jQuery("#events").text(`User ${event.returnValues.player} won: ${event.returnValues.won}`);
});
});


async function connectMetamask() {
  if (typeof window.ethereum !== undefined) {
    const accounts = await web3.eth.requestAccounts();
    let p = await getPlayerAddress();
    jQuery("#playerAddress").text(p);
  }
}

async function getPlayerAddress() {
  const playerAddr = await web3.eth.getAccounts();
  if(playerAddr[0] !== undefined) {
    return web3.utils.toChecksumAddress(playerAddr[0]);
  }
}






  async function flipCoin(){
        var bet = $("#bet_input").val();
        var config = {
            value: web3.utils.toWei(bet,"ether")
        }
        contractInstance.methods.flipCoin().send(config, {from : await getPlayerAddress()})
        .on("transactionHash", function(hash){
            console.log(hash);
        })
        .on("confirmation", function(confirmationNr){
            console.log(confirmationNr);
        })
        .on("receipt", function(receipt){
            console.log(receipt);
            if(receipt.events.betPlaced.returnValues[2] === false){
                alert("You lost " + bet + " Ether!");
            }
            else if(receipt.events.betPlaced.returnValues[2] === true){
                alert("You won " + bet + " Ether!");
            }
        })

      };


      async function fetchAndDisplay(){
          contractInstance.methods.getBalance().call({from : await getPlayerAddress()}).then(function(res){
            $("#jackpot_output").text("The Contract has : " + web3.utils.fromWei(res[1], "ether") + "Ether");

          })
      };


      async function fundContract(){
        var fund = $("#fund_contract").val();
        var config = {
          value : web3.utils.toWei(fund, "ether")
        }
        contractInstance.methods.fundContract().send(config, {from : await getPlayerAddress()})
        .on("transactionHash", function(hash){
          console.log(hash);
        })
        .on("confirmation", function(confirmationNr){
          console.log(confirmationNr);
        })
        .on("receipt", function(receipt){
          contractInstance.methods.getBalance().call().then(function(res){
          $("#jackpot_output").text("The Contract has : " + web3.utils.fromWei(res[1], "ether") + "Ether");
        })
      };


      async function withdrawAll(){
        contractInstance.methods.withdrawAll().send({from : await getPlayerAddress()});
      };



Dear @dan-i

**I HAVE A QUESTION : **

**I DEPLOYED THE CONTRACT SUCCESSFULLY ON THE ROPSTEN NETWORK , BUT I AM UNABLE TO SEE WHERE I CAN FIND THE CONTRACT ADDRESS ?**
**AND BECAUSE I AM UNABLE TO LOACTE THIS CONTRACT ADDRESS, MY main.js file is incomplete**

**Can you please help me with this, because i was unable to see it in the Terminal  ?** 
Please let me know where I am going wrong because I am unable to interact with Metamask?

Also, see screenshot below : it shows error “Unexpected identifier” in main.js 27
*Whereas I think my Code is correct *

![Screenshot 2021-02-20 at 7.20.20 PM|690x431](upload://q0YPERqyN28I3kdwHGn5oV4q3MM.png) 

Thanks and Regards

Su.kal Crypto
1 Like

Hi everyone this is my project, i would appreciate some feedback.

Also im struggling a little bit… i’m able to listen the events, but so far i can’t to access to the indexed parameters of the event so i can create different messages (for example if the player wins or looses).

https://github.com/carlosalv098/FLIP_COIN_DAPP

Hi @NamaWho

I am quite sure the issue is in the flow, follow me:

function placeBet(uint _choice) public payable costsAtLeast {
        require(msg.value*2 <= balance, "Contract hasn't enough funds to perform this bet");
        require(betters[msg.sender].isWaiting == false, "This address still has an open bet");
        require(_choice <= 1, "The parameter _choice must be 0 or 1");

        //balance += msg.value;
        
        bytes32 betId = update();

        pendingBets[betId] = msg.sender;
        betters[msg.sender].isWaiting = true;
        betters[msg.sender].betAmount = msg.value;
        betters[msg.sender].betChoice = _choice;

Notice that you are populating the mapping pendingBets[betId] once you receive back the betId from your function update().

The problem is that you never receive that betId because you chained multiple methods and you emit DebugClosingBet before populating the mapping.

placeBet -> update -> dev_testRandomFunc (keep in mind that you are calling a new function, therefore you are not returning queryId as the code is executing a different function) -> __callback -> closeBet -> emit DebugClosingBet(_id, pendingBets[_id], betters[pendingBets[_id]].isWaiting ,betters[pendingBets[_id]].betChoice, _result);

Notice how your code still has not populated the mapping pendingBets.

Cheers,
Dani

1 Like

Well after a few days i believe i got it, at least a version 1.0 :joy:. Here is the repo, if you have any doubt of how it is organised please ask.

https://github.com/FrankAst/Coinflip-game

There are a few things to improve such as including a smoother user experience when it comes down to connecting to metamask. I tried to include the code granted by metamask documentation, but i had a hard time trying to implement @metamask/detect-provider since i couldnt install it from npm. Maybe someone could figure it out.

Finally I did it!
I thank you so much @dan-i cause I was thinking about everything apart that issue, and that kept me stuck few days. I should have finished it; you can find it here: https://github.com/NamaWho/coinflip-dapp
Feel free to open an issue or contact me for any suggestions! :slight_smile:

1 Like

Dear @dan-i

I am stuck.

I uploaded my files on github

https://github.com/Suveett/CoinFlip-Phase-2.git

Meanwhile, I also deployed on Ropsten and its showing successfully deployed , see screenshot below :point_down:

But Now look at my main.js file below :point_down:

var web3 = new Web3(Web3.givenProvider);
var contractInstance;
const contractAddress = "";

$(document).ready(async function(){
  contractInstance = new web3.eth.Contract(abi,contractAddress);
  await connectMetamask();

    console.log(contractInstance);
    console.log(`Use Contract address: ${contractInstance._address}`)



    $("#flip_button").click(flipCoin);
    $("#get_balance").click(fetchAndDisplay);
    $("#fund_contract_button").click(fundContract);
    $("#withdraw_funds").click(withdrawAll);



});

//EVENT LISTENERS

contractInstance.once('LogNewProvableQuery',
{
  filter: { player: await getPlayerAddress() },
  fromBlock: 'latest'
}, (error, event) => {
  if(error) throw("Error fetching events");
  jQuery("#events").text(`User ${event.returnValues.player} is waiting for the flip result`);
});

contractInstance.once('FlipResult',
{
  filter: { player: await getPlayerAddress() },
  fromBlock: 'latest'
}, (error, event) => {
  if(error) throw("Error fetching events");
  jQuery("#events").text(`User ${event.returnValues.player} won: ${event.returnValues.won}`);
});
});


async function connectMetamask() {
  if (typeof window.ethereum !== undefined) {
    const accounts = await web3.eth.requestAccounts();
    let p = await getPlayerAddress();
    jQuery("#playerAddress").text(p);
  }
}

async function getPlayerAddress() {
  const playerAddr = await web3.eth.getAccounts();
  if(playerAddr[0] !== undefined) {
    return web3.utils.toChecksumAddress(playerAddr[0]);
  }
}






  async function flipCoin(){
        var bet = $("#bet_input").val();
        var config = {
            value: web3.utils.toWei(bet,"ether")
        }
        contractInstance.methods.flipCoin().send(config, {from : await getPlayerAddress()})
        .on("transactionHash", function(hash){
            console.log(hash);
        })
        .on("confirmation", function(confirmationNr){
            console.log(confirmationNr);
        })
        .on("receipt", function(receipt){
            console.log(receipt);
            if(receipt.events.betPlaced.returnValues[2] === false){
                alert("You lost " + bet + " Ether!");
            }
            else if(receipt.events.betPlaced.returnValues[2] === true){
                alert("You won " + bet + " Ether!");
            }
        })

      };


      async function fetchAndDisplay(){
          contractInstance.methods.getBalance().call({from : await getPlayerAddress()}).then(function(res){
            $("#jackpot_output").text("The Contract has : " + web3.utils.fromWei(res[1], "ether") + "Ether");

          })
      };


      async function fundContract(){
        var fund = $("#fund_contract").val();
        var config = {
          value : web3.utils.toWei(fund, "ether")
        }
        contractInstance.methods.fundContract().send(config, {from : await getPlayerAddress()})
        .on("transactionHash", function(hash){
          console.log(hash);
        })
        .on("confirmation", function(confirmationNr){
          console.log(confirmationNr);
        })
        .on("receipt", function(receipt){
          contractInstance.methods.getBalance().call().then(function(res){
          $("#jackpot_output").text("The Contract has : " + web3.utils.fromWei(res[1], "ether") + "Ether");
        })
      };


      async function withdrawAll(){
        contractInstance.methods.withdrawAll().send({from : await getPlayerAddress()});
      };



Dear @dan-i

**I HAVE A QUESTION : **

**I DEPLOYED THE CONTRACT SUCCESSFULLY ON THE ROPSTEN NETWORK , BUT I AM UNABLE TO SEE WHERE I CAN FIND THE CONTRACT ADDRESS ?**
**SO, I CAN FINALLY PUT THE CONTRACT ADDRESS IN MY MAIN.JS FILE** 

**Can you please help me with this, because i was unable to see it in the Terminal  ?** 
Please let me know where I am going wrong because I am unable to interact with Metamask?

Also, ANOTHER ISSUE IS AS PER SCREENSHOT BELOW: it shows error “Unexpected identifier” in main.js 27
*Whereas I think my Code is correct *

*KINDLY LET ME KNOW WHERE I AM GOING WRONG* 

Thanks and Regards

Su.kal Crypto

Hey @Su.kal.Crypto

When you deploy a contract using Truffle, it will show the contract address during its deployment.
You can copy the address and use it in your JS code.

image

Regards,
Dani

Hi @Jose_Hernandez

provable_getPrice("random") returns the fees required by the oracle.
You can check my implementation if needed: https://github.com/dani69654/CoinFlip/blob/master/contracts/CoinFlip.sol

Regards,
Dani

1 Like

Thanks! Also saw the thread from Feb 2020. Should’ve checked before asking :sweat_smile:

1 Like

No worries :slight_smile: happy coding!

1 Like

Hi @dan-i, Yes i know that !
Somehow i forgot for the first time i deployed. Unfortunately now after putting in the command

truffle migrate --network ropsten 

it reads as follows :point_down:

type or paste code here

Nothing to compile !! 

So now i don’t know how to re-access that Contract address ??

Thanks and looking forward

:grinning:

Su.kal Crypto

Hi @Su.kal.Crypto

Can you post your migration file?

Am stuck here. am getting

Data location can only be specified for array, struct or mapping types, but “memory” was given. function __callback(bytes32 _queryId, string memory _result, bytes32 memory _proof) public{ ^-------------------^

function that throws the error

function __callback(bytes32 _queryId, string memory _result, bytes32 memory _proof) public{
require(msg.sender == provable_cbAddress());

    uint256 randomNumber = uint256(keccak256(abi.encodePacked(_result))) % 100;
    lastedNumber = randomNumber;
    emit generatedRandomNumber(randomNumber);
}

Hey @chim4us

bytes32 memory _proof this should be bytes memory _proof if I am not wrong.
Give it a try.

Cheers,
Dani

For the second stage, I created a folder is the same repository: https://github.com/BogdanStirbat/double-or-nothing/tree/master/oracle-version/smart-contract

I added a screenshot:

1 Like

I found this problem and try to find out how to fix in google and I can’t fix its anyone can help me.

truffle compile

Compiling your contracts...
===========================
Error: TypeError: soljson.Pointer_stringify is not a function
    at Object.compile (/usr/local/lib/node_modules/truffle/build/webpack:/packages/workflow-compile/legacy/index.js:72:1)
Truffle v5.0.42 (core: 5.0.42)
Node v10.22.0

Here is my work
https://github.com/Aoi020608/eth_CoinFlip

Hi @Anonymous

Please follow this faq to downgrade nodeJS and try again: FAQ - How to downgrade Node.Js

Regards,
Dani

Good day! Here is the github link to my project: https://github.com/jlthcrypto/coin-flip

Could definitely use some UI/UX improvements and other best practices regarding smart contract security. Looking forward to update when I get to those courses!

Here are some screenshots:

1 Like

Hi @dan-i

Here is my 2_Deploy_coinflip.js file contents :point_down:

const Coinflip = artifacts.require("Coinflip");

module.exports = function(deployer,network,accounts) {
  deployer.deploy(Coinflip).then(function(instance){
    instance.fundContract({value : web3.utils.toWei("1", "ether"), from : accounts[0]}).then(function(){
      console.log("The Contract successfully got funded with 1 ether from Owner address: " + accounts[0]);
      console.log("The Contract address is :" + Coinflip.address);
    }).catch(function(err){
      console.log("The contract couldn't get funded " + err);
    });
  }).catch(function(err){
    console.log("Failed to deploy ! ");
  });
};


Thanks and Regards

su.kal Crypto