Programming Project - Phase 2

True , but as a final project take it as way to add projects to your portfolio.
If you want to find a job in the blockchain industry recruiters will look at your project.
You don’t have to make a coinflip Dapp you can make an other Dapp using the oracle.
If you add more complexity recruiters will see that you are mastering a lot of concept

1 Like

So in fixing the issues raised by gabba, I encountered a weird problem with metamask:

I figured it must have been a bug introduced with my changes so I downloaded my old working version from github, installed it and am getting the same error. So at least I know the code is not the problem.

Any idea how to fix this? been googling for hours but havent been able to get it working again. Even tried reinstalling metamask.

Thanks

@MoneyPrinterGoBrrr Your gas limit is huge :scream: did you try with a lower limit ?
This transaction come from a require which fail. You are using your old code but did you deploy it again and change the contract address ?

hey hey,

I am trying to set up the random example using truffle and Ropsten. When I migrate I get the following error when it hits my random migration file.

open for error
2_random_migration.js
=====================

C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\cli.bundled.js:129032
  throw new Error(
  ^

Error: Could not find artifacts for random from any sources
    at Resolver.require (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\resolver\index.js:25:1)
    at Object.require (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:167:1)
    at ResolverIntercept.require (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\resolverintercept.js:18:1)
    at C:\Users\kira.summers\ethereum\randomOracle\migrations\2_random_migration.js:1:26
    at Script.runInContext (vm.js:142:20)
    at Script.runInNewContext (vm.js:148:17)
    at Object.file (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\require\require.js:92:1)
    at Migration._load (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\migration.js:43:1)
    at Migration.run (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\migration.js:167:1)
    at Object.runMigrations (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:148:1)
    at Object.runFrom (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:110:1)
    at Object.runAll (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:114:1)
    at Object.run (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:79:1)
    at runMigrations (C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate.js:253:1)
    at C:\Users\kira.summers\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate.js:218:1
Truffle v5.1.14-nodeLTS.0 (core: 5.1.13)
Node v14.2.0
PS C:\Users\kira.summers\ethereum\randomOracle>

This is confusing me. I searched online and found some recommendations to --reset, which I tried, but the result was the same.

Does anyone know how I can resolve this please?

Hi @Kiki
Your contract is named flip not random, the artifacts is the name of the contract you are trying to migrate.
Base the repo you have linked before.

Hi @gabba This is a different thing. This is just a quick simple code that uses an oracle to generate a random number. I like to test things individually before I put them together.

If you want to see the github for this, I think it’s here:

@Kiki Is your repository private ? I can’t access it.
Is the artifact your are calling named as your contract ?

@gabba Oops, yes it was. Try now? It should work!

The name of your contract is RandomExample and you are calling random in your artifact.
EDIT: on your github repo it’s migration but i guess it’s not updated regarding your error

:wink:

Durrrrgh what a ditz I am! Thanks :slight_smile:

1 Like

@gabba Finally figured it out with your tip concerning require function that fails. It was this line:
require(msg.sender == provable_cbAddress()); in the __callback function.

Thanks!

1 Like

I continue to wrestle with Angular and Typescript. Making progress, I’ve updated the contract model to use events in a similar way as the ProvableAPI, but not actually using it yet so I can test locally. Having an issue that has me stumped. Maybe someone can offer a hint.

I have an Angular service class that:

  1. Subscribes to the BetPlaced and BetResult contract events
  2. Re-broadcasts those events using an rxjs BehaviorSubject which the UI components can subscribe to and display a notification

PROBLEM: the events are getting double broadcast. So UI notifications are getting double displayed.

Below is the the service and contract code. I’ve left out the ContractService and Web3Service as they just instantiate the contract object and wrap the Web3 service so they can be injected by Angular. See the repo for the full code (the iss5-improve-randomness branch).

Thanks!

/contracts/PlaceCoinFlipBet
pragma solidity 0.5.12;
import './provableAPI.sol';
import './Ownable.sol';

contract PlaceCoinFlipBet is Ownable, usingProvable {

    struct Bet {
        address player;
        bool betOn;
        uint amount;
    }

    mapping(address => uint) balances;
    mapping(bytes32 => Bet) bets; // queryId => Bet

    event BetPlaced(bytes32 id, address player, uint amount, bool betOn);
    event BetResult(bytes32 id, bool flipResult, uint payout);

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

    function getMyBalance() public view returns (uint) {
        return balances[msg.sender];
    }

    function getBet(bytes32 id) public view returns (address player, bool betOn, uint amount) {
        return (bets[id].player, bets[id].betOn, bets[id].amount);
    }

    function placeBet(bool betOn, uint amount) public {
        require(amount <= maxBet(), 'bet above max');
        require(amount <= balances[msg.sender], 'bet above balance');
        require(amount >= minBet(), 'bet below min');

        // Get test queryId to store the bet before flipCoin() invokes the callback.
        // When provableAPI is called can return queryId from flipCoin()
        // as it won't be invoking the callback directly
        // (and trying to access the bet info before it's recorded)
        bytes32 queryId = getQueryId();
        bets[queryId] = Bet(msg.sender, betOn, amount);
        emit BetPlaced(queryId, msg.sender, amount, betOn);
        flipCoin();
    }

    function minBet() public pure returns(uint) {
        return 0.001 ether;
    }

    function maxBet() public view returns(uint) {
        return balances[owner] / 100;
    }

    function __callback(bytes32 _queryId, string memory _result) public {
        bool result = uint(keccak256(abi.encodePacked(_result))) % 2 == 0;
        uint payoutAmt = payout(_queryId, result);
        emit BetResult(_queryId, result, payoutAmt);
    }

    function payout(bytes32 queryId, bool flipResult) private returns(uint) {
        Bet memory bet = bets[queryId];
        if (bet.betOn != flipResult) {
            balances[bet.player] -= bet.amount;
            balances[owner] += bet.amount;
            return 0;
        }

        uint winnings = bet.amount * 2;
        balances[owner] -= winnings;
        balances[bet.player] += winnings;

        return winnings;
    }

    // tails = true, heads = false
    function flipCoin() public returns (bytes32) {
        bytes32 queryId = getQueryId();
        string memory random = now % 2 == 0 ? "0" : "1";
        __callback(queryId, random);

        return queryId;
    }

    function getQueryId() internal view returns (bytes32) {
        return bytes32(keccak256(abi.encodePacked(msg.sender)));
    }

    function addFunds() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdrawFunds(uint amount) external {
        require(amount <= balances[msg.sender], 'withdraw exceeds balance');
        balances[msg.sender] -= amount;
        msg.sender.transfer(amount);
    }

    function () external payable {
        balances[owner] += msg.value;
    }

    function destroy() external onlyOwner {
        selfdestruct(owner);
    }
}

/src/app/coin-flip-bet.service.ts
import BN from 'bn.js';
import { BehaviorSubject } from 'rxjs';

import { Injectable } from '@angular/core';
import { BetPlacedEvent } from './bet-placed-event';
import { BetResultEvent } from './bet-result-event';
import { ContractService } from './contract.service';

@Injectable({
  providedIn: 'root'
})
export class CoinFlipBetService {

  balance = new BN('0');
  readonly betPlaced = new BehaviorSubject<BetPlacedEvent>({
    id: '',
    player: '',
    amount: new BN(''),
    betOn: false
  });
  readonly betResult = new BehaviorSubject<BetResultEvent>({
    id: '',
    flipResult: false,
    payout: new BN('0')
  });
  private betEventCounter = 0; // test

  constructor(private contractService: ContractService) {
    this.subscribtToEvents();
  }

  private subscribtToEvents() {
    this.contractService.getPlaceCoinFlipBet()
      .then(c => {
        c.events.BetPlaced({}, this.onBetPlaced.bind(this))
        c.events.BetResult({}, this.onBetResult.bind(this));
        console.log('Subscribed to bet events')
      });
  }

  
  getBalance(): Promise<BN> {
    return this.contractService.getPlaceCoinFlipBet()
      .then(c => c.methods.getMyBalance().call())
      .then(balance => {
        this.balance = new BN(balance);
        return this.balance;
      });
  }
  
  placeBet(betOn: boolean, amount: BN): Promise<boolean> {
    return this.contractService.getPlaceCoinFlipBet()
      .then(c => c.methods.placeBet(betOn, amount).send())
      .then(res => {
        console.log('Service > placeBet: response', res);
        this.getBalance();
        return true;
      });
  }

  onBetPlaced(error: any, event: any) {    
    console.log('CoinFlipBetService > onBetPlaced ', ++this.betEventCounter, event);
    if (error) {
      console.error(error);
      return;
    } else if (!event || !event.returnValues.id) {
      return;
    }
    const data: BetPlacedEvent = {
      id: event.returnValues.id,
      player: event.returnValues.player,
      amount: new BN(event.returnValues.amount),
      betOn: event.returnValues.betOn
    };
    this.betPlaced.next(data);
  }

  onBetResult(error: any, event: any) {
    console.log('CoinFlipBetService > onBetResult: ', event);
    if (error) {
      console.error(error);
      return;
    } else if (!event || !event.returnValues.id) {
      return;
    }
    const data: BetResultEvent = {
      id: event.returnValues.id,
      flipResult: event.returnValues.flipResult,
      payout: new BN(event.returnValues.payout)
    };
    this.betResult.next(data);
  }

}

EDIT: Bug still exists, but I pushed a workaround to the problem that keeps track of the last event log ID and only broadcasts the event if the ID is different than the last one. Not ideal, but don’t want to spend more time on this specific issue. Want to finish this project so I can focus on the DeFi 202 course :smiley:

2 Likes

Hi Gabba , Thank you for your feedback :slight_smile: I will continue working on it, It is not completed yet.

1 Like

Slight problem with finding artifacts. I may have to switch out SafeMath and use provableAPI. Otherwise, doing well so far.

I had this problem as well. It occurred to me it was because I had added the call to provable_newRandomDSQuery() to my coin flip method and this call only works on a TEST network like ropstien or ropsten, not on the local ganache network. Once I deployed the contracts to kovan it started to work again… but I have other problems now LOL.

2 Likes

Hi @cherrybluemoon
I looked at your code but you didn’t implement the provable api yet ? Do you have any issue ?

1 Like

Hei @gabba,
Yes I had an issue which I still trying to resolve, but without luck. I have not yet switched from SafeMath to provableAPI yet.

/Users/cherrybluemoon/.nvm/versions/node/v10.18.0/lib/node_modules/truffle/build/cli.bundled.js:200372
  throw new Error(
        ^
/Users/cherrybluemoon/.nvm/versions/node/v10.18.0/lib/node_modules/truffle/build/cli.bundled.js:200372
  throw new Error(
  ^

Error: Could not find artifacts for Flipcoin from any sources
    at Resolver.require (/Users/cherrybluemoon/.nvm/versions/node/v10.18.0/lib/node_modules/truffle/build/cli.bundled.js:200372:9)
    at Object.require (/Users/cherrybluemoon/.nvm/versions/node/v10.18.0/lib/node_modules/truffle/build/cli.bundled.js:207896:38)
    at ResolverIntercept.require (/Users/cherrybluemoon/.nvm/versions/node/v10.18.0/lib/node_modules/truffle/build/cli.bundled.js:535629:32)
    at /Users/cherrybluemoon/flipper/migrations/2_flipcoin_migration.js:1:28
    at Script.runInContext (vm.js:133:20)
    at Script.runInNewContext (vm.js:139:17)
    at /Users/cherrybluemoon/.nvm/versions/node/v10.18.0/lib/node_modules/truffle/build/cli.bundled.js:365339:14
    at FSReqWrap.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)

Finally decided to finish this project :smiley: . Had some fun and went a bit all out on the front-end, and implemented it with react, ethersproject,and web3-react. Very interesting to try making for a good ux, when requests take a while and are always asynchournous. It’s a completely different world compared to ‘normal’ apps.

Made it so that a user can flip multiple coins at a time, and the owner has a small interface to see the current balance, deposit or withdraw ether. Might try to improve the contracts some more (implementing SafeMath for example)

Github: https://github.com/ErnoW/flippin-coin
Live: https://flippincoin.netlify.app/

preview:

4 Likes

Nice. Very pretty from what I peeked at. Netlify also looks very interesting. I’ll check them out more and perhaps use them as well.

I have finally finished Phase 2. This was quite challenging. Some of the problems came from me not being able to submit any transactions on the ropsten network for hours (waiting over 30 minutes for failure to submit) or being unable to deploy. At times I was unable to get funds from the various faucets on the network.

A great deal of patience is required. The tooling still feels like its still early on its development cycle. It was great however being forced to use https://ropsten.etherscan.io/.

I honestly felt like I learnt the most from this course just because of how much I had to struggle with everything.

Please forgive the absolutely poor UI updates (I should . I had to wrap up the project as it was taking up so much time. https://github.com/tjma2001/coinflip-dapp