Programming Project - Phase 1

Hi, I didnt’ change the contract, anyway I tried to copy paste the abi and to check the contract address but it gives me the same error:

Uncaught SyntaxError: await is only valid in async functions and async generators

Hey @Pigott

When you send a transaction with web3 it is mandatory to specify the sender account.
You can pass the sender account as parameter to your function, or fetch the accounts web3.eth.getAccounts(); this command returns an array of accounts :slight_smile:

Happy coding,
Dani

Hi @enrico

You can use await only in async functions so:

  $("#get_balance_button").click(async function(){
      let res = await contractInstance.balance().call();
    });

1 Like

It’s not pretty, but it works! :grinning:

https://streamable.com/u3ejle

1 Like

Hello! I’m hitting this error and I’m not sure how to fix it. withdrawAllProfits() is definitely a real function in my contract. I’ve renamed it a few times and re-updated the abi (and cleared my browser’s cache) to no avail.

image

    function withdrawAllProfits() public
    {
        uint toTransfer = players[msg.sender];
        require(toTransfer > 0);
        players[msg.sender] = 0;
        msg.sender.transfer(toTransfer);
    }

I have a button on my Dapp so that, when you click it, it calls the following function in main.js:

function withdrawMoney()
{
    contractInstance.methods.withdrawAllProfits().on("receipt", async function(receipt)
    {
        // NOTE: Don't print it out like this or else you'll see [object Object] instead of the actual data in the object.
        //console.log("Receipt: " + receipt);
        console.log(receipt);
        alert("Done");
    })
}

Any ideas anybody? Thanks in advance.

Hey @Warphine

It does look like the function is not in the abi, can you please post your abi file?
Also please console.log(contractInstace.methods) and post the results here
Thanks
Dani

1 Like

Hey, thanks for the quick response!

You were right, the function that was in the abi had an old name. I fixed that by migrating the contract again, and then updating the abi array in the file. I’d upload the abi file but it won’t let me. Here’s the guts:

var abi = [
  {
    "constant": true,
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "name": "",
        "type": "address"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "balance",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "name": "",
        "type": "bool"
      }
    ],
    "name": "result",
    "type": "event"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "random",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "fundContract",
    "outputs": [],
    "payable": true,
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "triggerCoinFlip",
    "outputs": [],
    "payable": true,
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "getWinnings",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "withdrawAllProfits",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  }
];

After clearing my browser’s cache and refreshing the webpage, I see console.log(contractInstance.methods); prints out the correct function name:

{owner: ƒ, 0x8da5cb5b: ƒ, owner(): ƒ, balance: ƒ, 0xb69ef8a8: ƒ, …}
0x3e86fd90: ƒ ()
0x5ec01e4d: ƒ ()
0x8da5cb5b: ƒ ()
0x70b7596b: ƒ ()
0xa7e84603: ƒ ()
0xb69ef8a8: ƒ ()
0xbd097e21: ƒ ()
balance: ƒ ()
balance(): ƒ ()
fundContract: ƒ ()
fundContract(): ƒ ()
getWinnings: ƒ ()
getWinnings(): ƒ ()
owner: ƒ ()
owner(): ƒ ()
random: ƒ ()
random(): ƒ ()
triggerCoinFlip: ƒ ()
triggerCoinFlip(): ƒ ()
withdrawAllProfits: ƒ ()
withdrawAllProfits(): ƒ ()
__proto__: Object

But when I click the button to withdraw all funds, it still gives me the same error as I described in the original post.

EDIT: Actually nevermind, the error I’m getting now is about the .on() function not existing from within the withdrawAllProfits() function. If I remove that, I no longer see an error so I guess it’s fixed! Thanks!

1 Like

Is it normal that often (not always, but often) my betting transaction fails on these errors?

inpage.js:1 MetaMask - RPC Error: Error: [ethjs-rpc] rpc error with payload {"id":2677390418933,"jsonrpc":"2.0","params":["0xf8748202a28504a817c80082a5d89409ab5590c11351d242bfac8579446a813b0fb1da88016345785d8a000084a7e84603822d45a01c3b189522d406420d4a63b9a0925e4ed705a7d1c6f865a2ad73fa178894b9f6a0413c4f753fd6905607ef27467e20561007b8ff96745293615beaf56892f3449f"],"method":"eth_sendRawTransaction"} [object Object] {code: -32603, message: "Error: [ethjs-rpc] rpc error with payload {"id":26…method":"eth_sendRawTransaction"} [object Object]", stack: "Error: Error: [ethjs-rpc] rpc error with payload {…method":"eth_sendRawTransaction"} [object Object]"}
Uncaught (in promise) {code: -32603, message: "Error: [ethjs-rpc] rpc error with payload {"id":26…method":"eth_sendRawTransaction"} [object Object]", stack: "Error: Error: [ethjs-rpc] rpc error with payload {…method":"eth_sendRawTransaction"} [object Object]"}

Looks like I have another issue. transfer() only seems to work in “truffle test” i.e. JavaScript/Web3 but doesn’t work in Solidity on the Dapp.

For instance, I’m trying to transfer the user accounts winnings to them. Here’s the function (I’ve tried both transfer() and the call.value I’m doing below, same result):

    function withdrawAllProfits() public
    {
        uint toTransfer = players[msg.sender];
        require(toTransfer > 0);
        players[msg.sender] = 0;
        require(players[msg.sender] == 0);

        // transfer() hardcodes the gas amount (a response due to the DAO hack back in the day - the fixed 2300 gas amount was thought to reduce the number of operations that could be run after transferring)
        //msg.sender.transfer(toTransfer);

        // The following transfer method is preferred because gas is NOT constant so 2300 gas isn't always enough.
        // WARNING: Higher chance of reentrancy with this approach so make sure you guard your code when you use this.
        (bool success, ) = msg.sender.call.value(toTransfer)(" ");
        require(success, "Transfer failed.");
    }

In JavaScript/Web3:

Before Winnings: 100
After Winnings: 0
    √ should allow users to withdraw all winnings (1325ms)

But output from the Dapp:

Before Winnings: 0.2
After Winnings: 0.2

Before is before I call to transfer the winnings and After is the amount after I run the transfer.

Here is the function as it exists in my main.js file:

async function withdrawMoney()
{
    await contractInstance.methods.getWinnings().call().then(async function(result)
    {
        console.log(result);
        let formattedCurrWinnings = web3.utils.fromWei(result.toString(), "ether");
        console.log("Before Winnings: " + formattedCurrWinnings);
    })

    await contractInstance.methods.withdrawAllProfits().call().then(async function(result)
    {
        console.log(result);
        await contractInstance.methods.getWinnings().call().then(async function(result)
        {
            console.log(result);
            let formattedAfterWinnings = web3.utils.fromWei(result.toString(), "ether");
            console.log("After Winnings: " + formattedAfterWinnings);
            $("#winnings_output").text(formattedAfterWinnings);
            alert("Done");
        })
        .catch(function(err)
        {
            console.log("getWinnings() error: " + err);
        });
    })
    .catch(function(err)
    {
        console.log("withdrawAllProfits() error: " + err);
    });
}

Any help is appreciated! Thanks!

1 Like

Hey @Warphine

There are some things that have to be double checked :slight_smile:

1- You should not use msg.sender.call() in this situation. It is not a safe operation and does not revert in case of error (true that you’ve added a require statement but still this is not the situation where.call() should be used.

I highly suggest you to replace it with msg.sender.transfer(toTransfer).

2- Consider now your main.js file.
await contractInstance.methods.withdrawAllProfits().call(). use .send(senderAddress).
It is indeed required to send a trigger a transaction.
.call is used when you are querying a function that returns a value (such as a getter).

Give it a try and let me know.
Happy coding,
Dani

2 Likes

Here my version of the project . https://drive.google.com/file/d/1HtWqsH7hjO9TydqI2P4qSKVag1w8GQxX/view?usp=sharing

I’m open to suggestions and criticism. I want to improve as much as possible.

If you want you can give me your feedback.

Thanks in advance
Matteo

1 Like

thanks dan-i for the help, just go the transaction to go through!

1 Like

Thanks for the info. The withdraw profits function is working now. Marvelous!

1 Like

Most of this journey for me… :thinking: :grinning: :smiley: :smile: :slightly_smiling_face: :face_with_raised_eyebrow: :thinking: :confused: :confused: :angry: :neutral_face: :angry: :thinking: :astonished: :grinning: :laughing: :hushed: :rage: :angry: :neutral_face: :thinking: :thinking: :thinking: :neutral_face: :expressionless: :open_mouth: :laughing: :crazy_face: :laughing: :smiley: :slightly_smiling_face: :yawning_face: :yawning_face: :thinking: :thinking: :neutral_face: :face_with_raised_eyebrow: :neutral_face: :scream: :rage: :angry: :sob: :sob: :expressionless: :neutral_face: :neutral_face: :astonished: :astonished: :slightly_smiling_face: :smile: :smiley: :laughing: :sunglasses:

This has been a lot of work over the last several days… soo many hours… but did learn a lot… :slightly_smiling_face:

Pictures from left to right–>
The initial deposit - Owner’s view to the empty contract… (the only thing I wasn’t able to figure out thus far, how to initialize the contract with a balance… I was thinking this would be done during migration… maybe you guys know)

The User’s view -->

Error Handling and value checks --> (input checks and random Metamask errors)

Examples notifications for users and the owner --> (deposit/withdraw/win/lose/error/deleted contract)

I welcome any feedback or questions… but now I’m going to bed! :sleeping:

3 Likes

Interesting assignment! Here’s my coinflip dapp showcase:
https://www.dropbox.com/s/ygzv02iptwuvxxr/Coinflip%20dapp.m4v?dl=0

1 Like

I think the Some Advice video is missing a link to download the Random.sol file he mentions in the video. Are we just supposed to try to recreate the video using the video? I’m not sure he even shows the whole thing.

1 Like

Hello @Warphine, hope you are great!

would you be so kind to send me the link of the lesson which have the download file broken? I try to look into the Programmin Project Phase 1 Category and didn’t find any link.

If you have any more questions, please let us know so we can help you! :slight_smile:

Carlos Z.

I meant to post this under Phase 2 forum. Sorry.

Hey @dan-i i started from scratch, can you check on these to contracts why do i get this error: “TypeError: contractInstance.methods.settleBet is not a function”
js.file

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

var alert = `<div class="alert alert-dismissible fade show" role="alert">
               <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                   <span aria-hidden="true">&times;</span>
               </button>
           </div>`

$(document).ready(function() {
   window.ethereum.enable().then(async function(accounts) {
       contractInstance = new web3.eth.Contract(abi, "0x91E6933Ba2CbFCF12a27D21b047f3f1bD703Df82", {from: accounts[0]});
       
       console.log(contractInstance);
      
       contractInstance.events.allEvents()
           .on('data', function(event){
               if(event.event == "flipWon") {
                   let winningAlert = $.parseHTML(alert);
                   $(winningAlert).addClass("alert-success");
                   $(winningAlert).prepend("<strong>Flip Won!</strong> Your winnings have been transferred.");
                   $("#bet-alerts").prepend(winningAlert);
                   setTimeout(() => $(winningAlert).alert('close'), 5000);
               } else if (event.event == "flipLost") {
                   let losingAlert = $.parseHTML(alert);
                   $(losingAlert).addClass("alert-danger");
                   $(losingAlert).prepend("<strong>Flip Lost</strong> Thanks for playing!");
                   $("#bet-alerts").prepend(losingAlert);
                   setTimeout(() => $(losingAlert).alert('close'), 5000);
               } else if (event.event == "coinFlipped") {
                   let flippedAlert = $.parseHTML(alert);
                   $(flippedAlert).addClass("alert-primary");
                   if(event.returnValues.result == "0")
                   {
                       $(flippedAlert).prepend("<strong>Heads</strong>");
                   } else {
                       $(flippedAlert).prepend("<strong>Tails</strong>");
                   }
                   $("#bet-alerts").prepend(flippedAlert);
                   setTimeout(() => $(flippedAlert).alert('close'), 5000);
               }
           });
   });

   $("#place_bet_button").click(placeBet);

});

async function placeBet(){
   var prediction = parseInt($("#prediction").val());
   var bet = parseFloat($("#bet_input").val()) * (10 ** 18); 
   var balance = await contractInstance.methods.balance().call();
   balance = parseFloat(balance);
   
   if(balance >= bet && bet > 0) {
       contractInstance.methods.settleBet(prediction).send({value: bet})
   } else {
       let warning = $.parseHTML(alert);
       $(warning).addClass("alert-danger");
       $(warning).prepend("Bet must be <strong>greater</strong> than 0 and <strong>less</strong> than " + balance/(10**18) + " ETH.");
       $("#bet-alerts").prepend(warning);
       setTimeout(() => $(warning).alert('close'), 5000);
   }
}


and sol. contract

import “./Ownable.sol”;
pragma solidity ^0.5.12;

contract CoinFlip is Ownable{
uint public balance;

event coinFlipped(uint result);
event flipWon();
event flipLost();

modifier validateBet(){
    require(msg.value > 0 && msg.value <= balance);
    _;
}

function flipCoin() private returns(uint) {
    uint result = now % 2;
    emit coinFlipped(result);
    return result;
}

function settleBet(uint prediction) public payable validateBet {
    require(prediction == 0 || prediction == 1);
    uint outcome = flipCoin();
    if(outcome == prediction) {
        emit flipWon();
        balance -= msg.value;
        msg.sender.transfer(msg.value * 2);
    } else {
        emit flipLost();
        balance += msg.value;     
    }
}

function addFunds() public onlyOwner payable returns(uint) {
    require(msg.value > 0);
    balance += msg.value;
    return balance;
}

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

}

}

1 Like

Hello,

So for the first phase of the project, I’ve made a recording where I present the initial functionality of the project and my current code.

The link to the recording: https://ufile.io/xqxnf6t6
The link to the Github repo: https://github.com/stefandol/TheOddFlip

I’m starting the second pahse of the project asap.

In the mean time, any feedback on the code that I have submitted to Github would be much appreciated.

1 Like