Programming Project - Phase 2

Hey @zanjakobjez

Good job :smiley:

Few tips for you:

  • Try to use the parameter proof here:

function __callback(bytes32 _queryId, string memory _result, bytes memory _proof) public{

  • Avoid strings, Solidity does not like strings at all and they require lots of gas to be handled.

  • Verify the inputs received by the users, a good example is the parameter betHeadsTails.

function flipCoin(uint betHeadsTails,

  • Make sure that your contract can be used by multiple users at the same time.

You are using global variables at the moment, while you should save data in mappings and structs so that multiple users can play at the same time.

An easy example if you need some suggestions: https://github.com/dani69654/CoinFlip/blob/master/contracts/CoinFlip.sol

Happy coding,
Dani

I’m having a weird problem. So everything works fine for a few times of using the flip dapp, but then after a few flips it keeps returning an error. Even after a day of not using it MetaMask says an error was thrown in the smart contract and the transaction doesn’t go through. I also get this terminal message:
Screen Shot 2021-03-04 at 7.28.10 AM

But if I switch to an account that hasn’t used the dapp yet it works fine. Does anyone have an idea why this is happening? Could it be that the oracle never returned a random number so the user is still “waiting”? Thanks in advance!

1 Like

So after some more investigating it seems like the issue is that the oracle stops responding at some point. So the player is considered “waiting” and cannot send a new bet nor can they get their money back. How can I fix this? What kind of functionality can I implement so the user can get their money back if this ever happens and how can I prevent the Oracle from not responding? I’m planning to make something similar for real world use (obviously with security upgrades) so any help would be greatly appreciated!

1 Like

Hi @a.lwsn

That’s a good question and you should find a solution yourself as anyone can have different ideas on how bypass it.
One idea can be to save the block timestamp when a user bets and allow a user to ‘unblock’ its funds if the new timestamp is ‘x’ bigger than the saved one.

Cheers,
Dani

Hi @filip @dan-i

Can you please have a look at my Github Code and reply where i am Going wrong , as I am still struggling even after 4-5 days…

You see from below screenshot the error is in await getPlayerAddress();

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

thanks and Regards

su.kal crypto

Hi @Su.kal.Crypto. First of all, love your Star Trek reference in your name.

I think you problem in your main.js code is that you are defining the event listeners outside the $(document).ready(async function(){ function.

This means that by the time the code reaches line 26, the contractInstance has not yet been instanciated (line 6),

Another thing that might be wrong is that you are using an awaitable call on line 28 without being in a async function.

What I would suggest is that you move all the definitions of the event listeners inside the $(document).ready(async function(){ function (perhaps to line 9).

That way you ensure that the contract instance is instanciated and that you are within a async function.

Let me know it works or not, and we will take it from there.

Hey @Capplequoppe
Thanks a Ton. I am so sorry i made such silly mistakes, which i have now immediately corrected on my git code

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

and thanks a Ton for the appreciation of my name, Really Glad !!

*However, I am now receiving the below error * :point_down:

Can you please help me with this one too ? :pray:

Thanks and Regards

Su.Kal Crypto

So on line 59 you have: const accounts = await web3.eth.requestAccounts();

According to the documentation on: https://web3js.readthedocs.io/en/v1.2.6/web3-eth.html#requestaccounts
it says like this:
“This method will request/enable the accounts from the current environment it is running (Metamask, Status or Mist). It doesn’t work if you’re connected to a node with a default Web3.js provider. (WebsocketProvider, HttpProvidder and IpcProvider) This method will only work if you’re using the injected provider from a application like Status, Mist or Metamask.”

Since you originally wrote

Even I am unable to interact on this webpage with my Metamask ( I have checked Metamask account is same as before and is currently on the Ropsten Network)

This might explain it…

Try if it works better with getAccounts().
You can see the documentation here: https://web3js.readthedocs.io/en/v1.2.6/web3-eth.html#getaccounts

In my solution I took this approach in the document ready function:

 window.ethereum.enable().then(async (accounts) => {
        account = accounts[0];
        web3.eth.Contract(abi,"0xBE47870737d075Fa019eB8813172a87D84270003", {from: accounts[0]});
    })

This way you already have access to the accounts and you don’t need to call either requestAccounts() nor getAccounts()

Hope it helps…

Hi @Capplequoppe

I am really trying hard for last 6-8 hours, but unable to really wrap my Head around events. I have read some Docs also regarding the same, but still getting errors

Please see my revised code ( not changed on Github as yet but used it the way you suggested ) , hence sharing below :point_down: :

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

$(document).ready(async function(){
  window.ethereum.enable().then(function(accounts){
    contractInstance = new web3.eth.Contract(abi,contractAddress, {from: accounts[0]});
    console.log(contractInstance);
    console.log(`Use Contract address: ${contractInstance._address}`)
    });

    $("#flip_button").click(async function(){
      await flipCoin();
    });
    $("#get_balance").click(async function() {
      await fetchAndDisplay();
    });
    $("#fund_contract_button").click(async function(){
      await fundContract();
    });
    $("#withdraw_funds").click(async function(){
      await withdrawFunds();
    });
    $("#withdraw_all_funds").click(async function(){
      await withdrawAll();
    });
    $("#get_balance").click(async function(){
      await fetchAndDisplay();
    });


    //EVENT LISTENERS

    contractInstance.once('LogNewProvableQuery',

    (error, event) => {
      if(error) throw("Error fetching events");
      jQuery("#events").text(`User ${event.returnValues.player} is waiting for the flip result`);
    });

    contractInstance.once('FlipResult',

    (error, event) => {
      if(error) throw("Error fetching events");
      jQuery("#events").text(`User ${event.returnValues.player} won: ${event.returnValues.won}`);
    });

});




  async function flipCoin(){
        var bet = $("#bet_input").val();
        var config = {
            value: web3.utils.toWei(bet,"ether")
        }
        await 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.FlipResult.returnValues.bool === false){
                alert("You lost " + bet + " Ether!");
            }
            else if(receipt.events.FlipResult.returnValues.bool === true){
                alert("You won " + bet + " Ether!");
            }
        })

      };


      async function fetchAndDisplay(){
          await 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")
        }
        await 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){
          console.log(receipt);
          //contractInstance.methods.getBalance().call().then(function(res){
        //  $("#jackpot_output").text("The Contract has : " + web3.utils.fromWei(res[1], "ether") + "Ether");
        })
      };


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

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

And now , I am getting the Below error :point_down:

I have tried different permutations and combinations but it throws up the same error again and again that “Cannot read property of Undefined”

What Am I doing wrong ? What am i Unable to understand/ grasp ? In the above code I have not used the await connectMetamask() function and neither the await getPlayerAddress() function and instead used the window.ethereum.enable().then(function(accounts){“code”}) as you suggested .
Please Help :pray:

Thanks and Regards

Su.Kal Crypto

I completely understand your frustration, I have been in your situation so many times.
Don’t give up, just take a step back and go trough logically what is happening.
and if you reach some code that you don’t really know what it is doing, then take your time and do some research on it.

But well done doing everything you can to read up on the error first before posting!

Now to your problem.
Let’s go through the process of understanding what is wrong.
If we look at the error message:

Cannot read property 'once' of undefined

and as we can see further down this happens at line 34.

So first of all, when you get the error: cannot read property ‘propertyName’ of undefined it basically means that it can’t find the thing specified just before property name.
This can happen for at least two reasons:

  1. spelling mistake
    Example:
var myString = "test";
console.log(nyString.length); // Notice the spelling mistake. I am using an 'n' instead of an 'm'

This will result in the error cannot read property 'length' of undefined.
Why? Because nyString is in fact not defined, we made a spelling mistake.

  1. Using a variable that has not your been initialized
    Example:
var person;
$(document).ready(async function(){
    person = await loadPersonFromRemoteServer();
});

console.log(person.name);

Here we will reach the console.log(person.name); long before the person has been set.
Why? Because $(document).ready() is first of all not called until the document is in fact completely ready (ie. fully loaded), while the code outside that function runs immediately.

In your case we have this scenario.
You did the right thing moving the events inside the $(document).ready(), however we have another “delayed call” or promise as it is called on this line:
window.ethereum.enable().then(function(accounts){ contractInstance = new web3.eth.Contract....
The function in which the contractInstance is set is in the “then” call.
This means that the code where your instance is defined is called first after ethereum has been enabled, which in reality can be done in an unknown future.
Meanwhile the code below that code block is executed.
In that code you have your event setup which tries to access the contractInstance that is still not initialized (since the then() function has not yet been called).

So when this code is reached: contractInstance.once('LogNewProvableQuery'..., the contractInstance is not defined and you get the error message: cannot read property 'once' of undefined.

So what do you have to do?
Well, without giving you the whole answer you should find a way to run the event initialization when you can guarantee that the contractInstance is set.

When you get this working you will probably get more errors.
for example in the connectMetamask function.
A tip here is that you already know the accounts of the user because they are stored in accounts when window.ethereum.enable().then(function(accounts){ is called.

Best of luck, and let us know if you get stuck again!

Hi @dan-i

I successfully deployed contract on Ropsten and even interacted with it a bit. I had some troubles with withdraw function and in order to troubleshoot I wanted to add a function to .sol file and re-deploy it (like I’ve done in the past). However when I $ truffle migrate --network ropsten --reset (this worked before), I get this error.

Screen Shot 2021-03-09 at 13.07.27

I have googled but I don’t understand what replay attack is or why I am getting this error now, and not before (even though I added balance to the contract before as well). Can you point me in the right direction? Thanks

Hey @ZigaP

I need to check this one as I never experienced this error.
Can you post your GitHub repo? I will deploy and check :slight_smile:

Cheers,
Dani

So I’ve deployed this contract already several times with updates each time, however now I’m getting this error when I try to deploy to any network. I have not changed the constructor in any deployment. Any idea why I’m getting this error?

Error: *** Deployment Failed ***

“RaffleContract” hit a require or revert statement somewhere in its constructor. Try:

  • Verifying that your constructor params satisfy all require conditions.
  • Adding reason strings to your require statements.

Here is my constructor:

 constructor () public  {
        provable_setProof(proofType_Ledger);
        //flip();
    }

I’ve tried commenting out the constructor and the contract deploys but is basically not functional. Also there are no require statements in provable_setProof().
Thanks in advance for your help!

Hi @a.lwsn

I’ve tried commenting out the constructor and the contract deploys but is basically not functional.

Can you elaborate ‘is basically not functional’?
Also push your code on GitHub and send me the repo, I will check it :slight_smile:

Cheers,
Dani

By “basically not functional” I mean it will allow me to deposit funds but not to “flip”. It returns an error every time I try to flip, probably because there is no proof type set (my best guess). Here is a link to the contract on GitHub. Don’t mind the comments, they’re just notes for when I reconfigure this project for another dapp. Thank you for your time! https://github.com/aglawson/CoinFlipDapp_Phase1/blob/phase2/contracts/RaffleContract.sol

Hey @a.lwsn

I have cloned your repo and deployed your project successfully on Ropsten.
Here the contract: https://ropsten.etherscan.io/address/0x2d90fa617b3D4a9e080c2eDc78A1c588b0206260

I tried to play but I noticed that in your main.js, your function flip() is calling contractInstance.methods.update(), but I think there is an issue there as your contract RaffleContract.sol has a function called flip() not update().

Also make sure to send at least 0.01 ether as stated in you modifier costs.

I have not received the callback from the oracle so that is something that needs to be checked.

Also in your function __callback() you are sending betting[_queryId].value)*2 in case the user wins. Make sure that your contract has enough balance to pay the user :slight_smile:

Keep me posted!

Happy coding,
Dani

1 Like

Thanks for taking the time to do all that! I’ll look into these issues. I really appreciate it!

1 Like

Hi guys!

So I have been struggling for a long time now with (I believe so) the event listeners.
I checked the code a few times, checked forum posts, tried few new options but at the end no callback from the oracle.

As I am running out of ideas, I would really appreciate if someone can have a look here:
https://github.com/margaritavas/CoinFlip.git

Thanks a mil! :slight_smile:

Hey @margaritavas

I deployed your contract and I noticed that the oracle is not sending any random number back.
I saw the same thing in another project yesterday and I also test my own.
The oracle seems to be down, therefore your result event is not triggered.

For the sake of testing and play with events, I suggest you to add an event in the function addBalance() so that you can deposit ether and see if you can retrieve the event itself.

Keep me posted :slight_smile:

Dani

1 Like

Hi @Capplequoppe

I have finally managed to solve the issue ( I hope so !!) , and i spent hours and hours of frustration (which is eventually making me a better coder) , and Yet I am still not fully confident, inspite of the fact that the console is showing no errors.
can you please have a look at my code on github :point_down:
https://github.com/Suveett/CoinFlip-Phase-2.git

and let me know if my Logic is right ? I am specifically asking for these lines of code :point_down:

window.ethereum.enable().then(function(accounts){
    contractInstance = new web3.eth.Contract(abi,contractAddress);
    console.log(contractInstance);
    console.log(`Use Contract address: ${contractInstance._address}`);

because as you can see I have used the param accounts while enabling window.ethereum, but I havent utilised the accounts anywhere while initializing window.ethereum and the accounts actually get inserted through await metaMask () directly upon clicking on the async functions like flipcoin(), or fundContract() etc…

So is this the right way, or Am I missing something ??

And also as you rightly told me that i will encounter further problems ( which I am now experiencing )
Metamask is showing Revert Errors, i checked the code and now I am so exhausted after solving the Logic issues that i just cannot figure this one out.
Would you not mind helping me on this one too :pray: ??

Please have a look at my entore code on github and kindly please revert which silly mistakes (if at all) I am making ??

Thanks and Regards

Su.kal Crypto