Table of contents
Resources
- Moralis Projects - Build Your First Dapp
- How to Create a Crypto Sentiment Dapp " Moralis " The Ultimate Web3 Development Platform
- Build Your First Dapp - Web3 Programming with Solidity, Hardhat, Moralis, React, Full-Stack Dapp
Smart contract
HARDHAT setup
- Install with the following commands line
cd smartcontract
npm i -D hardhat
- Initialize new Hardhat project
npx hardhat
> Create a basic sample project
- 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
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;
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
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>
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
Description | New Vote |
SyncHistory (to sync previous event) | true |
Topic | tickerupdated (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"} |
Address | 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
TableName | Votes |
Now Moralis will sync any event on the blockchain with a database and constently observe the smartcontract
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.