Skip to content

Commit

Permalink
Added Web3 Staking Dapp (#767)
Browse files Browse the repository at this point in the history
* added web3-staking-dapp

* removed files

* removed artifacts  files

* modified gitignore

* made the requested changes

* edited readme files

* resolved the problems

* removed styles.css link

Co-authored-by: Sachin Chaurasiya <sachinchaurasiyachotey87@gmail.com>
  • Loading branch information
nirban256 and Sachin-chaurasiya committed Oct 30, 2022
1 parent 266a6a8 commit 2d47b55
Show file tree
Hide file tree
Showing 15 changed files with 972 additions and 0 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
"codemirror": "^5.65.7",
"date-fns": "^2.28.0",
"downloadjs": "^1.4.7",
"ethers": "^5.7.1",
"firebase": "^9.8.2",
"git-repo-api": "^0.0.17",
"graphql": "^16.5.0",
"hardhat": "^2.11.2",
"html-to-image": "^1.9.0",
"html2canvas": "^1.4.1",
"json-graphql-parser": "^0.0.20",
Expand Down
3 changes: 3 additions & 0 deletions src/plays/web3-staking-dapp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
.env
cache/*
41 changes: 41 additions & 0 deletions src/plays/web3-staking-dapp/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Web3 Staking Dapp

It is a staking dapp built upon polygon testnet in which users can stake matic testnet tokens for a particular time period and gain interest after the completion of the period.

## Play Demographic

- Language: js
- Level: Intermediate

## Creator Information

- User: nirban256
- Gihub Link: https://github.com/nirban256
- Blog: null
- Video: https://www.youtube.com/watch?v=AjaHJicbPkM&ab_channel=NirbanChakraborty

## Implementation Details

The implementation of the Web3 staking dapp play is quite simple.

- All start with `Navbar.js` component, every time when you run the play it will first check whether you have a wallet extension like Metamask in your browser or not, if not then the play will not work. If you have the extension then it will connect with your wallet.

- `stakeMatic()` function in `smart-contract/contracts/Staking.sol` is responsible for staking matic tokens. whenever it gets called it stake the amount specified by you from your wallet to the dapps contract.

- `withdrawToken` function in `smart-contract/contracts/Staking.sol` is responsible for withdrawing tokens from the dapp.

- There are two cases that the user will get:

- If you withdraw before the staking period completes then you will not receive the interest.
- If you withdraw after the staking period then you will get the amount you staked along with the interest.


## Consideration

- Use the polygon testnet tokens to use the dapp.

## Resources

- A browser wallet like Metamask.
- A little knowledge about Web3 dapps functionality.
- Dependencies: HardHat, Ethers
271 changes: 271 additions & 0 deletions src/plays/web3-staking-dapp/Web3StakingDapp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
import PlayHeader from 'common/playlists/PlayHeader';
import React, { useState, useEffect } from "react";
import artifact from "./artifacts/contracts/Staking.sol/Staking.json";
import { ethers } from "ethers";
import Navbar from "./components/Navbar";
import maticLogo from "./images/polygon-matic-logo.svg";
import { FaRupeeSign } from 'react-icons/fa';
import { ContractAddress } from './constant';

// WARNING: Do not change the entry componenet name
function Web3StakingDapp(props) {

// Your Code Start below.

// frontend states
const [provider, setProvider] = useState();
const [account, setAccount] = useState();
const [contract, setContract] = useState();
const [accountAddress, setAccountAddress] = useState();

// assets
const [assetId, setAssetId] = useState([]);
const [assets, setAssets] = useState([]);

// staking
const [stakeModal, setStakeModal] = useState(false);
const [stakingLength, setStakingLength] = useState();
const [stakingPercent, setStakingPercent] = useState();
const [amount, setAmount] = useState(0);

// functions
const toWei = ether => ethers.utils.parseEther(ether);
const toMatic = wei => ethers.utils.formatEther(wei);

const onload = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
setProvider(provider);

const contract = new ethers.Contract(
ContractAddress,
artifact.abi
)
setContract(contract);
}

useEffect(() => {
onload();
}, [])

const isConnected = () => account !== undefined;

const getAssetIds = async (address, account) => {
const assetIds = await contract.connect(account).getStakerIdsByAddresses(address);

return assetIds;
}

const getAssets = async (ids, account) => {
const loopedAssets = await Promise.all(
ids.map(id => contract.connect(account).getStakersById(id))
)

loopedAssets.map(async asset => {
const parsedAsset = {
stakersId: asset.stakerId,
percentInterest: Number(asset.interestPercentage) / 100,
daysRemaining: daysRemaining(Number(asset.dateUnlocked)),
maticInterest: toMatic(asset.interest),
maticStaked: toMatic(asset.amountStaked),
open: asset.open
}

setAssets(prev => [...prev, parsedAsset]);
})
}

const getAccount = async () => {
await provider.send("eth_requestAccounts", [])

const account = provider.getSigner()
return account;
}

const connectAndLoadWallet = async () => {
const account = await getAccount(provider)
setAccount(account);

const accountAddress = await account.getAddress();
setAccountAddress(accountAddress);

const assetIds = await getAssetIds(accountAddress, account);
setAssetId(assetIds);

getAssets(assetIds, account);
}

const stakeMatic = () => {
const wei = toWei(amount);
const data = { value: wei };

contract.connect(account).stakeMatic(stakingLength, data)
}

const stakingModal = (stakingLength, stakingPercent) => {
setStakeModal(true);
setStakingLength(stakingLength);
setStakingPercent(stakingPercent);
}

const daysRemaining = (unlockDate) => {
const currentTime = Date.now() / 1000;
const remainingTime = unlockDate - currentTime;

return Math.max((remainingTime / 60 / 60 / 24).toFixed(0), 0);
}

const withdraw = (stakersId) => {
contract.connect(account).withdrawMatic(stakersId);
}

return (
<>
<div className="play-details">
<PlayHeader play={props} />
<div className="play-details-body">
{/* Your Code Starts Here */}

<div>
<div>
<Navbar isConnected={isConnected} connect={connectAndLoadWallet} />
</div>

<div className=" bg-white rounded-xl md:rounded-3xl my-8 max-w-5xl block m-auto border-[0.5px] border-solid border-[#00f1fe80]">
<div className=" px-3 py-5 md:px-8 md:py-8">
<div className=" flex justify-center">
<span>
<img src={maticLogo} alt="matic logo" className=" w-8 h-8 lg:w-12 lg:h-12" />
</span>
<span className=" text-3xl lg:text-5xl font-semibold px-4 text-[#010326]">Matic Market</span>
</div>

<div className=" flex justify-between items-center my-6 md:my-8">
{stakeModal === false ?
(
<>
<div className=" flex">
<button type='submit' className="bg-[#00f1fec5] hover:bg-[#00f1fe] px-2 rounded-lg" onClick={() => stakingModal(30, '7%')}>
<FaRupeeSign color='#010326' className='w-8' />
</button>

<div className="flex justify-center items-center flex-col-reverse font-bold px-2 text-[#010326]">
<span className='text-[#010326]'>1 Month</span>
<span className="text-[#010326]">7%</span>
</div>
</div>

<div className=" flex">
<button type='submit' className="bg-[#00f1fec5] hover:bg-[#00f1fe] px-2 rounded-lg" onClick={() => stakingModal(90, '10%')}>
<FaRupeeSign color='#010326' className=' w-8' />
</button>

<div className="flex justify-center items-center flex-col-reverse font-bold px-2">
<span className='text-[#010326]'>3 Months</span>
<span className="text-[#010326]">10%</span>
</div>
</div>

<div className=" flex">
<button type='submit' className="bg-[#00f1fec5] hover:bg-[#00f1fe] px-2 rounded-lg" onClick={() => stakingModal(180, '14%')}>
<FaRupeeSign color='#010326' className=' w-8' />
</button>

<div className="flex justify-center items-center flex-col-reverse font-bold px-2">
<span className='text-[#010326]'>6 Months</span>
<span className="text-[#010326]">14%</span>
</div>
</div>
</>
)
:
(
(isConnected !== undefined ?
(
<div className='flex flex-col justify-center items-center'>
<div className="" onClick={e => e.stopPropagation()}>
<div className="">
<h2 className='text-[#010326] font-medium text-3xl mb-2'>Stake Matic</h2>
<div className="flex items-center text-[#010326] font-medium text-2xl">
<div className="">
<input className=' bg-slate-300 outline-none rounded-lg px-4 py-2' type="number" placeholder='0.0' onChange={e => setAmount(e.target.value)} />
</div>

<div className='text-[#010326] font-medium text-3xl px-3'>
<span>Matic</span>
</div>
</div>

<div className="">
<h4 className='text-[#010326] font-medium text-2xl my-4'>
{stakingLength} days @ {stakingPercent} APY
</h4>
</div>

<div className="">
<button type='submit' className="bg-[#00f1fec5] hover:bg-[#00f1fe] rounded-3xl px-2 py-1 text-base md:px-5 md:py-2 md:text-xl font-semibold text-[#010326]" onClick={() => stakeMatic(amount)}>
Stake Matic
</button>
</div>
</div>
</div>
</div>
)
:
(
<p className='text-[#010326] font-medium text-lg'>Connect Wallet to stake matic</p>
)
)
)
}
</div>
</div>
</div>

<div className="bg-white rounded-xl md:rounded-3xl my-8 max-w-5xl block m-auto border-[1px] border-solid border-[#00f1fe80] text-center md:px-4">
<div className="font-bold flex justify-center py-6">
<span className="text-[#010326] text-3xl">Staked Assets</span>
</div>

<div>
<ul className="font-semibold flex justify-between px-3">
<li className="text-[#010326]">Staked</li>
<li className="text-[#010326]">Interest</li>
<li className="text-[#010326]">Time Left</li>
<li className="text-[#010326]">Withdraw</li>
</ul>
</div>
<br />

{assets.length > 0 && assets.map((asset, index) => (
<div className="flex justify-between font-medium px-3 py-3 items-center">
<div className="text-[#010326] text-center pl-4">
{asset.maticStaked}
</div>
<div className="text-[#010326] text-center pl-2">
{asset.percentInterest} %
</div>
<div className="text-[#010326] text-center pl-6">
{asset.daysRemaining}
</div>
<div className="pl-6">
{asset.open ? (
<button type='submit' className="bg-[#00f1fec5] hover:bg-[#00f1fe] rounded-2xl px-1 py-1 md:px-2 md:py-2 md:font-semibold text-[#010326]" onClick={() => withdraw(asset.stakersId)}>
Withdraw
</button>
) : (
<span className='text-[#010326] bg-white'>Closed</span>
)}
</div>
</div>
))}
</div>
</div>

{/* Your Code Ends Here */}
</div>
</div >
</>
);
}

export default Web3StakingDapp;

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "..\\..\\build-info\\5faf9c6c2f8753632f3c7c13937cd196.json"
}
Loading

1 comment on commit 2d47b55

@vercel
Copy link

@vercel vercel bot commented on 2d47b55 Oct 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

react-play – ./

react-play-reactplayio.vercel.app
reactoplay.vercel.app
react-play-git-main-reactplayio.vercel.app

Please sign in to comment.