🦄 My First Dapp with Moralis

Resources

Smart contract

HARDHAT setup

  1. Install with the following commands line
cd smartcontract
npm i -D hardhat
  1. Initialize new Hardhat project
npx hardhat
> Create a basic sample project
  1. Install some dependencies
npm i -D dotenv
npm i -D @nomiclabs/hardhat-etherscan

Solidity Setup

Write smart contract

write smart contract for voting on specific ticker that owner has set for this smart contract

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.7;

contract MarketSentiment {

    address public owner;
    string[] public tickersArray;

    constructor() {
        owner = msg.sender;
    }

    struct ticker {
        bool exists;
        uint256 up;
        uint256 down;
        mapping(address => bool) Voters;
    }

    event tickerupdated (
        uint256 up,
        uint256 down,
        address voter,
        string ticker
    );

    mapping(string => ticker) private Tickers;

    function addTicker(string memory _ticker) public {
        require(msg.sender == owner, "Only the owner can create tickers");
        ticker storage newTicker = Tickers[_ticker];
        newTicker.exists = true;
        tickersArray.push(_ticker);
    }

    function vote(string memory _ticker, bool _vote) public {
        require(Tickers[_ticker].exists, "Can't vote on this coin");
        require(!Tickers[_ticker].Voters[msg.sender], "You have already voted for this coin");

        ticker storage t = Tickers[_ticker];
        t.Voters[msg.sender] = true;

        if(_vote){
            t.up++;
        } else {
            t.down++;
        }

        emit tickerupdated(t.up, t.down, msg.sender, _ticker);
    }

    function getVotes(string memory _ticker) public view returns (
        uint256 up,
        uint256 down
    ) {
        require(Tickers[_ticker].exists, "No such Ticker Defined");
        ticker storage t = Tickers[_ticker];
        return(t.up, t.down);
    }
}

Configurations

use hardhat to compile this contract and deploy it on any network we want (here testnet polygone mumbai) by updating the sample-script.js

async function main() {
  // We get the contract to deploy
  const MarketSentiment = await hre.ethers.getContractFactory("MarketSentiment");
  // the deploy function takes in parameters the constructor parameters (none here)
  const marketsentiment = await MarketSentiment.deploy();

  await marketsentiment.deployed();

  console.log("MarketSentiment deployed to:", marketsentiment.address);
}

setup environmnent variables

  • endpoint mumbai node ⇒ speedy node on moralis
  • cryptowallet private key
  • polygonescan API key to allow us to verify our contract

updating the hardhat.config.ts file

module.exports = {
  solidity: "0.8.7",
  networks: {
    mumbai: {
      url: process.env.POLYGON_MUMBAI,
      accounts: [process.env.PRIVATE_KEY]
    }
  },
  etherscan: {
    apiKey: process.env.API_KEY
  }
};

install missing dependency

npm i -D @nomiclabs/hardhat-waffle

Compilation & deployment

compile the smart contract with hardhat

npx hardhat clean
npx hardhat compile

=> Compiled 1 Solidity file successfully

deploy the smartcontract

npx hardhat run .\scripts\deployMarketSentiment.js --network mumbai

=> 
Compiled 1 Solidity file successfully
MarketSentiment deployed to: 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Verification and Interact with smart contract

npx hardhat verify 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx--network mumbai

=> 
Nothing to compile
Successfully submitted source code for contract
contracts/MarketSentiment.sol:MarketSentiment at 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
for verification on the block explorer. Waiting for verification result...

Successfully verified contract MarketSentiment on Etherscan.
https://mumbai.polygonscan.com/address/0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx#code

Interact with the smart contract

image.png

image.png

Create BTC, ETH and LINK tickers

React app

Initialize React App

cd ..
yarn
yarn start

Create a Header

take the ConnecButton from web3uikit

import React, { useEffect, useState } from "react";
import "./App.css";
import { ConnectButton } from "web3uikit";
import logo from "./images/Moralis.png";
import Coin from "./components/Coin";

const App = () => {

const [btc, setBtc] = useState(80);

  return (
    <>
     <div className="header">
        <div className="logo">
            <img src={logo} alt="logo" height="50px"/> Sentiment
        </div>
        <ConnectButton/>
     </div>
     <div className="instructions">
        Where do you think these tokens are going? Up or Down?
     </div>

    <div className="list">
         <Coin
          perc={btc}
          setPerc={setBtc}
          token={"BTC"}
         />
     </div>
    </>
  );
};

export default App;
import React, { useEffect, useState } from "react";
import "./Coin.css";

function Coin({perc, setPerc, token}) {

    const [color, setColor] = useState();

    useEffect(() => {
        if(perc < 50) {
            setColor("#c43d08");
        }else {
            setColor("green");
        }
    }, [perc])

  return (
    <>
        <div>
          <div className="token">
            {token}
          </div>

          <div className="circle"
            style={{boxShadow: `0 0 20px ${color}`}}>
              <div className="wave"
                  style={{
                      marginTop: `${100 - perc}%`,
                      boxShadow: `0 0 20px ${color}`,
                      backgroundColor: color,
                  }}>
              </div>
              <div className="percentage">
                {perc}
              </div>
          </div>
        </div>
    </>
  );
}

export default Coin;

image.png

Create Vote button

<div className="votes">

  <Button
      onClick={() => {setPerc(perc + 1)}}
      text="Up"
      theme="primary"
      type="button"
  />
  <Button
      color="red"
      onClick={() => {setPerc(perc - 1)}}
      text="Down"
      theme="colored"
      type="button"
  />

</div>

Go on Moralis to create a server

image.png

image.png

Then update the index.js appId and serverUrl with the moralis server data

ReactDOM.render(
  <React.StrictMode>
    <MoralisProvider **appId**="" **serverUrl**="">
          <App />
    </MoralisProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

Create info modal

<Modal
  isVisible={visible}
  onCloseButtonPressed={() => setVisible(false)}
  hasFooter={false}
  title={modalToken}
  >
  <div>
      <span>About</span>
  </div>
  <div>
      {modalToken && abouts[abouts.findIndex((x) => x.token === modalToken)].about}
  </div>
</Modal>

image.png

Moralis Web3Api Token Price

import { useMoralisWeb3Api } from "react-moralis";

...

const [modalPrice, setModalPrice] = useState();
const Web3Api = useMoralisWeb3Api();

...

useEffect(() => {

    async function fetchTokenPrice() {
        const options= {
        address: abouts[abouts.findIndex((x)=> x.token === modalToken)].address,
        };
        const price = await Web3Api.token.getTokenPrice(options);
        setModalPrice(price.usdPrice.toFixed(2));
    }

    if(modalToken) {
        fetchTokenPrice();
    }

}, [modalToken])

Connect Smart Contract and App

Add Sync contract event on the server with the following value

DescriptionNew Vote
SyncHistory (to sync previous event)true
Topictickerupdated (uint256, uint256, address, string)
Abi{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"up","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"down","type":"uint256"},{"indexed":false,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"string","name":"ticker","type":"string"}],"name":"tickerupdated","type":"event"}
Address0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TableNameVotes

Now Moralis will sync any event on the blockchain with a database and constently observe the smartcontract

image.png

When interacting with the smartcontract, we update the ticker with the last count of up and down so we can get every data by requesting the most recent update

async function getRatio(tick, setPerc) {

    const Votes = Moralis.Object.extend("Votes");
    const query = new Moralis.Query(Votes);
    query.equalTo("ticker", tick);
    query.descending("createdAt");
    const results = await query.first();
    let up = Number(results.attributes.up);
    let down = Number(results.attributes.down);
    let ratio = Math.round(up/(up+down)*100);
    setPerc(ratio)
}

also update the data on the ticker we just voted

async function createLiveQuery() {
    let query = new Moralis.Query('Votes');
    let subscription = await query.subscribe();
    subscription.on('update', (object) => {
        if(object.attributes.ticker === "LINK") {
            getRatio("LINK", setLink);
        }
        if(object.attributes.ticker === "ETH") {
            getRatio("ETH", setEth);
        }
        if(object.attributes.ticker === "BTC") {
            getRatio("BTC", setBtc);
        }
    });
}

Now that we have a reactive frontend, we have to trigger the contract when we click on button

import { useWeb3ExecuteFunction, useMoralis } from "react-moralis";
...
const contractProcessor = useWeb3ExecuteFunction();
const { isAuthenticated } = useMoralis();

...

async function vote(upDown) {

  let options = {
    contractAddress: "0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    functionName: "vote",
    abi: [{"inputs":[{"internalType":"string","name":"_ticker","type":"string"},{"internalType":"bool","name":"_vote","type":"bool"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"}],
    params: {
        _ticker: token,
        _vote: upDown,
    },
  }

  await contractProcessor.fetch({
    params: options,
    onSuccess: () => {
        console.log("vote succesful");
    },
    onError: (error) => {
        alert(error.data.message);
    }
  });
}

...

<Button
  onClick={() => {
      if(isAuthenticated) {
          vote(true);
      } else {
          alert("Authenticate to Vote")
      }}
  }
  text="Up"
  theme="primary"
  type="button"
/>

Now every thing is working

💡 Rollup

  • we created our own smartcontract using solidity and compiling it, deploying it and verifying it with hardhat.
  • we created the react app
  • we used Moralis to integrate the reactapp and smartcontract together to be able to vote and displaying informations.
0
Subscribe to my newsletter

Read articles from Léo-Paul MARTIN directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Léo-Paul MARTIN
Léo-Paul MARTIN

From France