Fictionally Irrelevant.

Wave-at-me

Cover Image for Wave-at-me
Harshit Singhai
Harshit Singhai

A simple web3 app. Wave, write something fun and get a change to win some ether (on test network obviously).

Smart Contract, Here We Go!!!

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;

import "hardhat/console.sol";

contract WavePortal {
    uint256 totalWaves;
    uint256 private seed;
    mapping(address => uint256) mostWaves;
    mapping(address => uint256) public lastWavedAt;

    event NewWave(address indexed from, uint256 timestamp, string message);

    struct Wave {
        address waver;
        string message;
        uint256 timestamp;
    }
    Wave[] waves;

    constructor() payable {
        console.log("Yo yo, I am a Harshit and I am smart");
        seed = (block.timestamp + block.difficulty) % 100;
    }

    function wave(string memory _message) public {
        totalWaves += 1;
        mostWaves[msg.sender] = mostWaves[msg.sender] + 1;
        waves.push(Wave(msg.sender, _message, block.timestamp));

        emit NewWave(msg.sender, block.timestamp, _message);
        console.log("%s has waved!", msg.sender);

        require(
            lastWavedAt[msg.sender] + 30 seconds < block.timestamp,
            "Must wait 30 seconds before waving again."
        );

        lastWavedAt[msg.sender] = block.timestamp;

        //  Generate a new seed for the next user that sends a wave
        seed = (block.difficulty + block.timestamp + seed) % 100;
        console.log("Random # generated: %d", seed);

        if (seed >= 75) {
            console.log("%s won!", msg.sender);
            uint256 prizeAmount = 0.00001 ether;
            require(
                prizeAmount <= address(this).balance,
                "Trying to withdraw more money than the contract"
            );
            (bool success, ) = (msg.sender).call{value: prizeAmount}("");
            require(success, "Failed to withdraw money");
        }
    }

    function getTotalWaves() external view returns (uint256) {
        console.log("We have %d total waves!", totalWaves);
        return totalWaves;
    }

    function getAllWaves() public view returns (Wave[] memory) {
        return waves;
    }

    function getWavesByAddress(address addr) external view returns (uint256) {
        return mostWaves[addr];
    }
}

Writing smart contract is more or less like javascript but with some extra magic thanks to Solidity. Solidity is basically converted to byte code by the compiler. The byte code generated by the compiler is what lives in the blockchain.

We can use web3.py, web3.js or ether.js to interact with the smart contract which resides in the blockchain.

Hardhat is Good.

I used hardhat with solidity for development, it makes a lot of stuff like deployment in various testnets like Rinkeby or Kovan easy. Write scripts and test easily without leaving javascript. The best feature of hardhat is the console.log, it's very difficult to debug smart contracts. With hardhat it gives you the ability you to console.log which you don't get with solidity otherwise.

React

I think React is going to be prominent with web3, even more than ever before. Web3 is taking React as the leading frontiers. I don't think there's as good support of Angular or Vue as it is for React.

with ethers.js and web3.js, react has very good inbuilt support for React.

Running Locally with local blockchain

const main = async () => {
  const [owner, randomPerson] = await hre.ethers.getSigners();
  const waveContractFactory = await hre.ethers.getContractFactory("WavePortal");
  const waveContract = await waveContractFactory.deploy({
    value: hre.ethers.utils.parseEther("0.001"),
  });
  await waveContract.deployed();

  console.log("Contract deployed to:", waveContract.address);
  console.log("Contract deployed by:", owner.address);

  let contractBalance = await hre.ethers.provider.getBalance(
    waveContract.address
  );
  console.log(
    "Contract balance:",
    hre.ethers.utils.formatEther(contractBalance)
  );

  let waveCount;
  waveCount = await waveContract.getTotalWaves();
  console.log("waveCount before: ", waveCount.toString());

  let waveTxn = await waveContract.wave("Something just like this");
  await waveTxn.wait();

  waveCount = await waveContract.getTotalWaves();
  console.log("waveCount after: ", waveCount.toString());

  newTxn = await waveContract.connect(randomPerson);

  let wavetxn;
  wavetxn = await newTxn.wave("Hello World1");
  await wavetxn.wait();
  wavetxn = await newTxn.wave("Hello World2");
  await wavetxn.wait();

  console.log("waveCount after random person: ", waveCount.toString());

  let totalWavesAddress;
  totalWavesAddress = await waveContract.getWavesByAddress(
    randomPerson.address
  );
  console.log(
    "totalWaves by: %s is ",
    randomPerson.address,
    totalWavesAddress.toString()
  );

  totalWavesAddress = await waveContract.getWavesByAddress(
    "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
  );
  console.log(
    "totalWaves by: %s is ",
    waveContract.address,
    totalWavesAddress.toString()
  );

  let allWaves = await waveContract.getAllWaves();
  console.log(allWaves);
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

runMain();

Deploy to Rinkeby like a pro

First, we need to create a config file.

require("@nomiclabs/hardhat-waffle");
require("dotenv").config();

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.4",
  networks: {
    rinkeby: {
      url: process.env.DEVELOPMENT_ALCHEMY_KEY,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};

and then we can deploy it.

const main = async () => {
  const [deployer] = await hre.ethers.getSigners();
  const accountBalance = await deployer.getBalance();

  console.log("Deploying contracts with account ", deployer.address);
  console.log("Account balance ", accountBalance.toString());

  const Token = await hre.ethers.getContractFactory("WavePortal");
  const portal = await Token.deploy({
    value: hre.ethers.utils.parseEther("0.001"),
  });
  await portal.deployed();

  console.log("WavePortal address: ", portal.address);
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

runMain();

Github

https://github.com/harshitsinghai77/web3-wave-at-me

Demo

https://buildspace-web3-wave-at-me.netlify.app/

Conclusion

Web3 is an amazing place to explore. Really great stuff happening around. I hope to get into this more and hopefully build more complex and useful dapp.

That’s it for today, see you soon. :)