This article aims to guide developers interested in DApp development to build a decentralized app based on Ethereum, taking the first steps in DApp development by developing a fully functional quiz game with examples of the common syntax of the Solidity language and how the front end interacts with smart contracts.

If you have never been involved in DApp development before, you can read “Programming on Blockchain: Introduction to DApp Development” to supplement your prior knowledge.


With the popularity of crypto cat, FOMO3D and other games, decentralized applications are popping up everywhere in the game field. Here is a simple and interesting DApp game to help you get familiar with DApp development. The contract function of this DApp is as follows: the user bets with any combination of three digits from 0-6, and the contract calculates A random number of three digits. According to the combination rules of random numbers, the user will be rewarded with different multiples. For example, if the random number is AAA, and A equals to 6, the user will be rewarded with at least 20 times the bet amount, namely all the balance of the prize pool. If A is not equal to 6, the reward is 5 times the betting amount; Random number is AAB, then reward 2 times of the betting amount; If the random number is ABC, no prize will be awarded. Meanwhile, users can check the balance of prize pool and personal betting record.

Contract writing

It can be seen that the contract needs to realize the functions of user betting, generating random numbers, awarding rewards and checking the balance of prize pool. Next, we write the contract code.

Create a new contract file for lottery.sol and declare the contract version. ^ indicates that the compiled version of the contract is 0.4.0 to 0.5.0 (excluding 0.5.0).

pragma solidity ^0.4. 0;
Copy the code

Define the basic terms of the contract and state the minimum bet.

contract Lottery {
  uint public betMoney = 10 finney;
}
Copy the code

Generate random numbers, using block difficulty. Difficulty and built-in function keccak256 to generate random numbers. Common data storage locations in EVM: Function parameters and return values are stored in memory by default, and state variables are stored in storage by default. We can change the default storage location by declaring memory and storage. But storage is much more expensive than memory.

contract Lottery {
  ...
  function generateRandomNumber() private view returns(uint[]) {
    uint[] memory dices = new uint[](3);
    for (uint i = 0; i < 3; i++) {
      dices[i] = uint(keccak256(abi.encodePacked(block.difficulty, now, i))) % 6 + 1;
    }
    returndices; }... }Copy the code

To obtain the contract balance, the important member attributes of address type mainly include balance and Transfer, which are to obtain the address balance and transfer ETH to the address respectively. The default ETH unit is WEI.

contract Lottery {
  ...
  function getBankBalance() public view returns(uint) {
    return address(this).balance; }... }Copy the code

The user bets. The method of ‘payable’ needs to be described with the keyword ‘payable’, which means eth can be received. Get the transaction sender address and eth attached to the current transaction with msg.sender and msg.value. Require is usually used to verify external input parameters. When the judgment condition is false, the remaining gas will be returned and the transaction will be rolled back. Assert is used to handle logic errors within the contract, and when an error occurs it consumes all gas and rolls back the transaction.

contract Lottery {
  ...
  function bet() public payable {
    uint amount = msg.value;
    require(amount >= betMoney, 'bet money not less than 10 finney');
    require(address(this).balance >= amount * 20.'contract money not enough to pay reward');

    uint[] memory dices = generateRandomNumber();
    require(dices.length == 3.'dices illegal');

    address addr = msg.sender;
    bool isReward = false;
    uint reward = 0;

    if ((dices[0] == dices[1]) && (dices[1] == dices[2]) && (dices[0] = =6)) {
      isReward = true;
      reward = address(this).balance;
    } else if ((dices[0] == dices[1]) && (dices[1] == dices[2]) && (dices[0] != 6)) {
      isReward = true;
      reward = amount * 5;
    } else if ((dices[0] == dices[1]) || (dices[0] == dices[2]) || (dices[1] == dices[2])) {
      isReward = true;
      reward = amount * 2;
    }

    if(isReward) { addr.transfer(reward); }}...Copy the code

Define the event, through the contract internal trigger event, Web3 listens to the event callback for the corresponding logic processing, so as to carry out page UI update; In addition, the front-end can obtain log information by event name.

contract Lottery {
  ...
  event BetList(
    address addr,
    uint amount,
    bool isReward,
    uint reward,
    uint[] dices
  );

  function bet() public payable {... emit BetList(addr, amount, isReward, reward, dices); }...Copy the code

Interact with the contract

So far, we have written the contract code, front-end page implementation will not be described here, mainly introduces how to use Web3 and contract interaction, web3 version 1.0 is used here, Web3 1.0 and 0.2x.x API call way is quite different, 1.0 API support asynchronous call.

When you install the Metamask browser plug-in, you inject a Web3 instance into the browser page. Detect if there is a Web3 instance on the page, and if not, connect to your own instance.

import Web3 from 'web3';

if (typeofweb3 ! = ='undefined') {
  web3 = new Web3(web3.currentProvider);
} else {
  web3 = new Web3(new Web3.providers.HttpProvider(NODE_NRL));
}
Copy the code

Pass in the contract ABI, the contract address, and instantiate the contract object.

this.contract = new web3.eth.Contract(
  CONTRACT_ABI,
  CONTRACT_ADDR,
);
Copy the code

Call the betting method in the contract. Try catch catches the Metamask popup to cancel the trade operation.

userBet = async() = > {try {
    await this.contract.methods
      .bet(
        ...
      )
      .send({
        from: ACCOUNT,
        value: MONEY,
      });
  } catch(error) { ... }}Copy the code

You can specify the event name, block height, and filtering conditions to query recorded logs. It is worth noting that logs cannot be queried in the contract.

queryEvent = async() = > {const event = await this.contract.getPastEvents(
    EVENT_NAME,
    {
      filter: {},
      fromBlock: 0.toBlock: 'latest'})},Copy the code

Expanded function

For example, sensitive operations such as modifying the user’s betting amount and recharge need the administrator’s authority to operate. Similarly, we can also expand the functions of sponsors by ranking the accumulated amount of top-up prize pool to display sponsors’ advertisements, which will not be expanded here.

Define modifiers that set the administrator address in the constructor and set the account that created the contract to administrator.

contract Lottery {
  ...
  address public owner;

  modifier onlyOwner() {
    require(owner == msg.sender);
    _;
  }

  constructor() public { owner = msg.sender; }... }Copy the code

Only the administrator account can modify the betting amount.

contract Lottery {
  ...
  function setBetMoney(uint _betMoney) public onlyOwner {
    betMoney = _betMoney;
  }

  function deposit() public payable onlyOwner {}... }Copy the code

conclusion

The current implementation of random numbers is through on-chain information generation, which is vulnerable to dishonest node attacks. The next article will use several examples of third-party libraries (Oricalize, SafeMath, OpenZepplin) to generate secure random numbers.

The resources

  • Solidity
  • Web3js
  • Cryptozombies
  • Coursetro

Precursors:

Programming on blockchain: Introduction to DApp Development

(Nuggets community link here)


The text/ielapp

A simple programmer

Sound/fluorspar

This article has been authorized by the author, the copyright belongs to chuangyu front. Welcome to indicate the source of this article. Link to this article: knownsec-fed.com/2018-10-07-…

To subscribe for more sharing from the front line of KnownsecFED development, please search our wechat official account KnownsecFED. Welcome to leave a comment to discuss, we will reply as far as possible.

Thank you for reading.