Programming Project - Phase 2

Welcome to the forum discussion for this section. Here you can ask questions or post feedback about the projects second phase.

1 Like

Issue related to setting up Truffle for the Oracle part of the project.

Anyone facing the same issue while calling npm install @truffle/hdwallet-provider? (this is an updated command desciption as truffle indicates)


Thanks for the heads up. It seems thereā€™s an open question on Github related to the very same problem but it has not been solved yetā€¦

1 Like

Interesting. It works for me, I can install the new package. What version of npm are you running? You can check with npm -v

Iā€™m running 6.4.1.

1 Like

I have 6.12.0 npm version.

Ok. I saw the github issue thread as well, which was empty.

I would try to downgrade your npm version and try again.

2 Likes

Hey,
Iā€™m getting closer to finsh the project. Iā€™m aware that there still is a lot of potential improvements. Like

  • withdrawAll() function security
  • alert the user about result like in the DApp of Phase 1
  • better UI/UX
  • ā€¦

I will tackle them soon. But for now I would like to show you my current progress. It was a real challange to get to this point.
Maybe someone can give me a little hint or suggestion, how to solve the problem of getting the user to know about the result.
As I already mentioned. In the previous DApp there was an alert function which let the user know about the result. But due to oracle/provable implementation this is not function any more.
My Idea was to get the event: betTaken from the FlipContract.sol in main.js somehow like I already tried in line 43 in main.js. But unfortunatly there is still something wrong.

Here is my GitHub: https://github.com/MaxiIT/BetFlipDApp/tree/Phase-2

Thank you @filip for the great course. I really have learned a lot and Iā€™m looking forward to learn more. :smiley: I might also have a suggestion for your next content. What about showing us how to host our DApp on a web server so that we can share and access the frontend through the browser? I the past I had some vague experience with glitch. It was kind of easy to get a little web app to run. But I donā€™t know how difficult it is to host such project like we created. Probably it isnā€™t so difficult as long as you know how, right? I hope I can figure this out soon. :grimacing:
But i have also heard about an other interessting way to host the frontend of our dapps. Throught IPFS our DApp could get fully decentraliced, if i got it correctly. Iā€™m not sure if this might be a meaningful option. Maybe you can tell us more about in your next course?
Meanwhile I will check by my own. :wink:

3 Likes

Wow, good job dude! I love it. Looks very good.

In order to alert the user and update the frontend. You need to listen for an event that the smart contract emits when the callback is done that is unique for that particular player. So how can you do that?

  1. We already have a unique bet id, the queryId. Modify the generatedRandomNumber event so that it also sends the queryId in the event. Make sure it is an indexed parameter.

  2. When the user places the bet, make sure to return the queryId from that function call to the frontend. Save that temporarily in the frontend and use it to listen for the generatedRandomNumber event filtered by that specific queryId. You can read more about listening and filtering for events here: https://web3js.readthedocs.io/en/v1.2.4/web3-eth-contract.html#events

When that event is fired. You know that the bet is complete and you know the result. You can then update the frontend. In the mean time you can show some nice loading graphic or something.

Another thing I would do for security measures is to NOT pay out the prize to the user directly. Instead I would store that balance in the contract until the player chooses to execute a withdraw function. Itā€™s safer for gas purposes. I talk about this in the smart contract security course, push vs pull :slight_smile:

3 Likes

Thank you @filip.
Basically that is what I have already tried over and over again but I guess I got stuck on a really stupid mistake and I couldnā€™t figure out how to solve it.
My issue is that Iā€™m not able to listen to events any more at all.
The browser console.log says:

TypeError: contractInstance.generatedRandomNumber is not a function

Sounds like there is a typo in my main.js file, right?
Letā€™s have a look:

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

$(document).ready(function() {
    window.ethereum.enable().then(function(accounts){
        contractInstance = new web3.eth.Contract(abi, "0xff93D08C89fB0f894C3c24a3bC135fd1D975Eb0b", {from: accounts[0]});
        console.log(contractInstance);
        
    });
  
    
    web3.eth.getBalance("0xff93D08C89fB0f894C3c24a3bC135fd1D975Eb0b").then(function(result){
        $("#jackpot_output").text(web3.utils.fromWei(result, "ether") + " Ether");
    });
    
    $("#flip_button").click(flip);
    $("#fund_contract_button").click(fundContract);
    $("#withdraw_button").click(withdrawAll);
    
    
    //Event listener 3
    //contractInstance.events.generatedRandomNumber({})
    //    .on('data', event => console.log(event));
    
});

function flip(){
    var bet = $("#bet_input").val();
    var config = {
        value: web3.utils.toWei(bet,"ether")
    }
    contractInstance.methods.flip().send(config)
    .on("transactionHash", function(hash){
        console.log(hash);
    })
    .on("confirmation", function(confirmationNr){
        console.log(confirmationNr);
    })
    .on("receipt", function(receipt){
        console.log(receipt);
        /* //This event listener worked just fine in the Phase 1 project
        if(receipt.events.betTaken.returnValues[2] === false){
            alert("You lost " + bet + " Ether!");
        }
        else if(receipt.events.betTaken.returnValues[2] === true){
            alert("You won " + bet + " Ether!");
        }
        */
    })
    
    
    
/*  Event listener 2
    contractInstance.once('betTaken', {
        filter: {player: '0xff93D08C89fB0f894C3c24a3bC135fd1D975Eb0b'}, // for test: player and contract owner are the same
        fromBlock: 7097288
    }, function(error, event){ console.log(event); });
*/
   
    //Event listener 1
    var event = contractInstance.generatedRandomNumber({}, {fromBlock:7097288, toBlock: 'latest'})
   
    event.watch(function(error, result){
        if(!error){
           console.log("Block Number: " + result.blockNumber);
        }
    });
   
}


function fundContract(){
    var fund = $("#fund_input").val();
    var config = {
        value: web3.utils.toWei(fund,"ether")
    }
    contractInstance.methods.fundContract().send(config)
    .on("transactionHash", function(hash){
        console.log(hash);
    })
    .on("confirmation", function(confirmationNr){
        console.log(confirmationNr);
    })
    .on("receipt", function(receipt){
        console.log(receipt);
    })
}

function withdrawAll(){
    contractInstance.methods.withdrawAll().send();
}

I have already tried a bunch of more listeners but is always the same. But sometimes the console.log says:

contractInstance is not defined

But why? I donā€™t know. It is declared in line 2 and initiated in line 6. At this point I have no more idea.
I have build in 3 example event listeners. Can someone please have a look and tell me what I did wrong?

The events are getting fired correcly.


Iā€™m just wondering why EtherScan is not naming the events which have more then 1 variable input. :thinking:

Event listener 2 seems like the correct one to me. What error do you get if you use that one?

1 Like

Hmm it gives me the Error right after I click the function button. It seems like it is not waiting for the event respons.
But anyway, after i call the function button a second time, after an event was emited at the first time, it should at least give me the event from the first action. (So I think about this now)
Here is the error I get. :thinking:

TypeError: n is undefined

Thanks for helping me. :slightly_smiling_face:

Append:

contractInstance.once('generatedRandomNumber', {
        fromBlock: 7118133
    }, function(error, event){ console.log(event); });

It is the same when I try this event call.
But there are fired: https://ropsten.etherscan.io/address/0x94574B4a3Afa53f35a75Ca960324D0C208B3801c#events

Do you get that error in the browser console? On what row is that error thrown? I donā€™t understand the error, you have no ā€˜nā€™ in the code you posted. Can you share the entire .js file?

Yes it is an error which appears in the browser console log right after I click the ā€œFlip the coinā€ button. It look like this.

Oh, I donā€™t understand it either. :rofl: It is really strange.
My main.js code looks like this. There definitly is no ā€˜nā€™.

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

$(document).ready(function() {
    window.ethereum.enable().then(function(accounts){
        contractInstance = new web3.eth.Contract(abi, "0x94574B4a3Afa53f35a75Ca960324D0C208B3801c", {from: accounts[0]});
        console.log(contractInstance);
        
    });
  
    
    web3.eth.getBalance("0x94574B4a3Afa53f35a75Ca960324D0C208B3801c").then(function(result){
        $("#jackpot_output").text(web3.utils.fromWei(result, "ether") + " Ether");
    });
    
    $("#flip_button").click(flip);
    $("#fund_contract_button").click(fundContract);
    $("#withdraw_button").click(withdrawAll);
    
    
    //Event listener 3
    //contractInstance.events.generatedRandomNumber({})
    //    .on('data', event => console.log(event));
    
});

function flip(){
    var bet = $("#bet_input").val();
    var config = {
        value: web3.utils.toWei(bet,"ether")
    }
    contractInstance.methods.flip().send(config)
    .on("transactionHash", function(hash){
        console.log(hash);
    })
    .on("confirmation", function(confirmationNr){
        console.log(confirmationNr);
    })
    .on("receipt", function(receipt){
        console.log(receipt);
        /*
        if(receipt.events.betTaken.returnValues[2] === false){
            alert("You lost " + bet + " Ether!");
        }
        else if(receipt.events.betTaken.returnValues[2] === true){
            alert("You won " + bet + " Ether!");
        }
        */
    })
    
    
    
    //Event listener 2
    contractInstance.once('generatedRandomNumber', {
        filter: {player: '0xD2f4F4C7C640d45d02DC5338CF249E378E9F58EC'},
        fromBlock: 7118133
    }, function(error, event){ console.log(event); });

   
    //Event listener 1
 /*    var event = contractInstance.generatedRandomNumber({}, {fromBlock:7097288, toBlock: 'latest'})
   
    event.watch(function(error, result){
        if(!error){
           console.log("Block Number: " + result.blockNumber);
        }
    });
    */  
}


function fundContract(){
    var fund = $("#fund_input").val();
    var config = {
        value: web3.utils.toWei(fund,"ether")
    }
    contractInstance.methods.fundContract().send(config)
    .on("transactionHash", function(hash){
        console.log(hash);
    })
    .on("confirmation", function(confirmationNr){
        console.log(confirmationNr);
    })
    .on("receipt", function(receipt){
        console.log(receipt);
    })
}

function withdrawAll(){
    contractInstance.methods.withdrawAll().send();
}

Ok I think I found the issue now. First of all you need to change your generatedRandomNumber event so that the player input is indexed. That makes it so that we can filter for certain parameters. So it might look like this:

event generatedRandomNumber(uint256 randomNumber, address indexed player);

Then you have to compile your code in truffle and update the abi file in your frontend. Because right now it seems like web3.js is saying there is no event with an indexed parameter named player. But maybe youā€™ve already done all of these things?

I downloaded your old github code and tried doing these things and the listener seems to work now.

1 Like

Oh Filip :man_facepalming:, thank you so much.:clap: :hugs:
I have already thougth about to put in an address indexed player in the event declaration. And I already have try it like that with an other event (betPlaced) but it was not functioning aswell. So i thought about somethink else.
What I totally forgot was to update the abi.js file after compiling the contract. No wonder thet I was searching for the event logs in the console log like a dumbass. :see_no_evil:
I told you, it must be something very silly.
Now the event logs are listed nicely.
Thank you, Filip.

Booom! Sweet! Web3.js reads all info about the contract from the abi file. So without it in the abi, web3.js wonā€™t find the event.

1 Like

Hello @filip , Frederic here again. Great tutorial as always, easy to follow along.

I got caught up at work for a bit so I couldnā€™t continue working on my dApp for a while. But now I finished adding the oracle and I think I did a good job; at least the contract is working as intended without any obvious flaws or errors.

Iā€™ll start working on tests and front end soon, but first I wanted to focus on implementing a good dApp.

You can check out my code in my GitHub repository.

As always, any help or tips are appreciated. Thank you!

1 Like

Hello all,

Im trying to test my project on Ropsten (It works just fine on my localhost) but I keep getting this error even though my accounts have ether in them. Any thoughts?

This specific transaction is a funding function that just funds 1 ether to the contract.

1 Like

it seems that the function which does the migration is failing because there is no found associate, did you pushed it to your repo ?
How are deploying on ropsten ? with truffle migrate --ropsten ?

1 Like

I used infura to push my contract on ropsten with the following truffle-config.js

const HDWalletProvider = require("@truffle/hdwallet-provider");
const mnemonicDev =
  "BLA BLA BLA";

 const infuraKey = "XXX";
//
 const fs = require('fs');
 const mnemonic = fs.readFileSync(".secret").toString().trim();

module.exports = {
  networks: {
    development: {
      provider: () =>
        new HDWalletProvider(mnemonicDev, "http://127.0.0.1:7545"),
        network_id: "*" // Any network (default: none)
    },
    geth: {
      provider: () =>
        new HDWalletProvider(mnemonicDev, "http://127.0.0.1:8545"),
      network_id: "*"
    },

    ropsten: {
       provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/<Infura KEY HERE>`),
       network_id: 3,       // Ropsten's id
       gas: 5500000,        // Ropsten has a lower block limit than mainnet
       confirmations: 2,    // # of confs to wait between deployments. (default: 0)
       timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)
       skipDryRun: true     // Skip dry run before migrations? (default: false for public nets )
    },

    // Useful for private networks
    private: {
      // provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
      // network_id: 2111,   // This network is yours, in the cloud.
      // production: true    // Treats this network as if it was a public net. (default: false)
    }
  },

  // Set default mocha options here, use special reporters etc.
  mocha: {
    // timeout: 100000
  },

  // Configure your compilers
  compilers: {
    solc: {

    }
  }
};
1 Like

Hey! Thanks for the advice. I got it working. Though iā€™m not sure why it wasnā€™t to begin with.

I redeployed the contract with 1 ETH and it was working great then redeployed once more using migrate --reset --network ropsten without sending ETH and it also worked. I think the migrate reset command was the fix. Thanks!

1 Like