import React, { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import SwapToken from "../SwapToken"; import history from "../Utils/history"; import Loading from "../Utils/Loading"; import AlertModal from "../Utils/AlertModal"; import SuccessModal from "../Utils/SuccessModal"; import metamask from "../../assets/metamask.png"; import { precision } from "../../utils/precision"; import { marlin } from "../../utils/marlin"; import { time } from "../../utils/time"; import * as erc20Abi from "../../abis/erc20Abi.json" import * as loanPoolAaveAbi from "../../abis/loanPoolAave.json"; import * as loanPoolMstableAbi from "../../abis/loanPoolMstable.json"; import Bid from "../Bid"; import { Card, Row, Col, Image, Button, CardDeck } from "react-bootstrap"; import Participate from "../Participate"; export default function ViewPool() { let routes; const DAI = "0xFf795577d9AC8bD7D90Ee22b6C1703490b6512FD"; const mUSD = "0x70605Bdd16e52c86FC7031446D995Cf5c7E1b0e7"; const { lendingPool, loanPoolAddress } = useParams(); const [loading, setLoading] = useState(true); let [erc20Instance, setErc20Instance] = useState(); let [contractInstance, setContractInstance] = useState(); const [state, setState] = useState({ maxBidAmount: 0, minBidAmount: 0, poolStartTimestamp: 0, isParticipant: false, alreadyTakenLoan: false, loanAmount: 0, collateralAmount: 0, totalParticipants: 0, auctionCount: 0, autionStartTimestamp: 0, autionCloseTimestamp: 0, auctionInterval: 0, poolCloseTimestamp: 0, highestBidAmount: 0, isLoanWinner: false, winnerInAuction: 0, userCurrentBid: 0, claimedFinalYield: false, erc20Balance: 0, createdAt: 0, }); const [successModal, setSuccessModal] = useState({ msg: "", open: false }); const [errorModal, setErrorModal] = useState({ msg: "", open: false }); const [claimingLoan, setClaimingLoan] = useState(false); const [claimingYield, setClaimingYield] = useState(false); const [showBid, setShowBid] = useState(false); const [showParticipate, setShowParticipate] = useState(false); const [showMetamaskError, setShowMetamaskError] = useState(false); const handleClaimLoan = async () => { contractInstance.methods.claimLoan() .send() .on('transactionHash', () => { setClaimingLoan(true); }) .on('receipt', () => { setClaimingLoan(false); fetchContractData(); setSuccessModal({ open: true, msg: "Congratulations 🎉 !! " + "You received loan amount in your wallet !!", }); }) .catch((error) => { setClaimingLoan(false); setErrorModal({ open: true, // onConfirm={handleReload} msg: error.message, }); }); } const handleClaimFinalYield = async () => { contractInstance.methods.claimFinalYield() .send() .on('transactionHash', () => { setClaimingYield(true); }) .on('receipt', () => { setClaimingYield(false); fetchContractData(); setSuccessModal({ open: true, msg: "Congratulations 🎉 !! " + "You received your final yield !!", }); }) .catch((error) => { setClaimingYield(false); setErrorModal({ open: true, msg: error.message, }); }); } const fetchContractData = async () => { try { let result; if (!contractInstance) { result = await createContractInstance(); } contractInstance = contractInstance ? contractInstance : result.contract; erc20Instance = erc20Instance ? erc20Instance : result.erc20; if (contractInstance) { const result = await marlin.fetchPoolInfo( loanPoolAddress ); const isParticipant = await contractInstance .methods.isParticipant(window.userAddress).call(); const alreadyTakenLoan = await contractInstance .methods.takenLoan(window.userAddress).call(); const totalParticipants = await contractInstance .methods.totalParticipants().call(); const auctionCount = await contractInstance .methods.getAuctionCount().call(); const userCurrentBid = await marlin.fetchUserBid( loanPoolAddress, window.userAddress, auctionCount ); const highestBidAmount = await contractInstance .methods.highestBidAmount(auctionCount).call(); const poolCloseTimestamp = await contractInstance .methods.poolCloseTimestamp().call(); let autionStartTimestamp, autionCloseTimestamp; if (Number(totalParticipants) > 1) { autionStartTimestamp = await contractInstance .methods.nextAutionStartTimestamp().call(); autionCloseTimestamp = await contractInstance .methods.nextAutionCloseTimestamp().call(); } let isLoanWinner = false, winnerInAuction = 0; if (Number(auctionCount) > 0) { const loanStatus = await contractInstance .methods.checkWinnerStatus(window.userAddress).call(); winnerInAuction = loanStatus[1]; if (winnerInAuction < auctionCount || (Number(auctionCount) === Number(totalParticipants) - 1 && time.currentUnixTime() > Number(autionCloseTimestamp)) ) { isLoanWinner = loanStatus[0]; } } let loanAmount; if (isLoanWinner || alreadyTakenLoan) { loanAmount = await contractInstance .methods.loanAmount(window.userAddress).call(); } let claimedFinalYield; if (time.currentUnixTime() >= Number(poolCloseTimestamp)) { claimedFinalYield = await contractInstance .methods.claimedFinalYield(window.userAddress).call(); } let erc20Balance = await precision.remove(await erc20Instance .methods.balanceOf(window.userAddress).call(), 18); const maxBidAmount = Number(result.collateralAmount) / Number(result.maxParticipants); setState({ isParticipant, alreadyTakenLoan, loanAmount, totalParticipants, auctionCount, autionStartTimestamp, autionCloseTimestamp, highestBidAmount, isLoanWinner, winnerInAuction, poolCloseTimestamp, erc20Balance, maxBidAmount, userCurrentBid, claimedFinalYield, minBidAmount: Number(result.minimumBidAmount), collateralAmount: Number(result.collateralAmount), auctionInterval: Number(result.auctionInterval), createdAt: Number(result.createdAt), }); setShowParticipate(false); setShowBid(false); setLoading(false); } } catch (error) { setErrorModal({ open: true, msg: error.message, }); } }; const createContractInstance = () => { return new Promise((resolve, reject) => { try { let contract; if (lendingPool === "Aave") { contract = new window.web3.eth.Contract( loanPoolAaveAbi.default, loanPoolAddress, { from: window.userAddress } ); } else if (lendingPool === "Mstable") { contract = new window.web3.eth.Contract( loanPoolMstableAbi.default, loanPoolAddress, { from: window.userAddress } ); } const erc20 = new window.web3.eth.Contract( erc20Abi.default, lendingPool === "Aave" ? DAI : mUSD, { from: window.userAddress } ); setErc20Instance(erc20); setContractInstance(contract); resolve({ contract, erc20 }); } catch (error) { reject(error); } }); }; const getTokenSymbol = () => { return lendingPool === "Aave" ? "DAI" : "mUSD"; } useEffect(() => { if (typeof window.ethereum === 'undefined' || !window.ethereum.isConnected() || !window.ethereum.selectedAddress ) { setLoading(false); setShowMetamaskError(true); } if (typeof window.ethereum !== 'undefined' && window.ethereum.selectedAddress && window.ethereum.isConnected() && !state.isParticipant ) { fetchContractData(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); if (loading) { routes = <Loading />; } else { routes = ( <div> {showMetamaskError ? <AlertModal open={showMetamaskError} toggle={() => { setShowMetamaskError(false); history.push('/'); }} > <div> {typeof window.ethereum === 'undefined' ? <div> You can't use these features without Metamask. <br /> Please install <Image width="50px" src={metamask}></Image> first !! </div> : <div> Please connect to <Image width="50px" src={metamask}></Image> to use this feature !! </div> } </div> </AlertModal> : <CardDeck> <Card className="hidden-card"></Card> <Card className="mx-auto view-pool-card"> <Card.Body style={{ textAlign: "left", fontWeight: "bold" }}> <p className="view-pool-header"> <u>Loan Pool</u> </p> <Row style={{ paddingBottom: "20px" }}> <Col> <u>Total Participants</u> <span> :</span> <span className="float-right"> {state.totalParticipants} </span> </Col> <Col> <u>Lending Pool</u> <span> :</span> <span className="float-right"> Aave </span> </Col> </Row> <Row style={{ paddingBottom: "20px" }}> <Col> <u>Auction Done</u> <span> :</span> <span className="float-right"> {state.totalParticipants > 1 ? state.auctionCount : 0 } </span> </Col> <Col> <u>Deposit Amount</u> <span> :</span> <span className="float-right"> <span>{state.collateralAmount} {getTokenSymbol()}</span> </span> </Col> </Row> <Row style={{ paddingBottom: "20px" }}> <Col> <u>Max Bid Amount</u> <span> : </span> <span className="float-right"> <span>{state.maxBidAmount} {getTokenSymbol()}</span> </span> </Col> <Col> <u>Min Bid Amount</u> <span> : </span> <span className="float-right"> <span>{state.minBidAmount} {getTokenSymbol()}</span> </span> </Col> </Row> {state.totalParticipants > 1 && Number(state.autionCloseTimestamp) > time.currentUnixTime() ? <div> {time.currentUnixTime() < state.autionStartTimestamp ? <Row className="text-center" style={{ paddingBottom: "20px" }}> <Col> <u>Next Auction Start</u> <span> : </span> <span> {time.getRemaingTime(state.autionStartTimestamp)} </span> </Col> </Row> : <div style={{ marginTop: "10px" }}> <div className="auction-message"> Auction Going On </div> <Row className="text-center" style={{ paddingBottom: "20px" }}> <Col> <u>Highest Bid Amount</u> <span> : </span> <span> {state.highestBidAmount} {getTokenSymbol()} </span> </Col> </Row> </div> } <Row className="text-center"> <Col> <u>Auction Close In</u> <span> : </span> <span> {time.getRemaingTime(state.autionCloseTimestamp)} </span> </Col> </Row> </div> : (state.totalParticipants > 1 ? ( Number(state.poolCloseTimestamp) < time.currentUnixTime() ? <div className="auction-alert-message"> Pool Already Closed </div> : <Row className="text-center"> <Col> <u>Pool Closing In</u> <span> : </span> <span> {time.getRemaingTime(state.poolCloseTimestamp)} </span> </Col> </Row> ) : null ) } {showBid ? <Bid contractInstance={contractInstance} totalAmount={state.collateralAmount} token={getTokenSymbol()} callback={fetchContractData} /> : null} {showParticipate ? (state.erc20Balance >= state.collateralAmount ? <Participate poolAddress={loanPoolAddress} contractInstance={contractInstance} erc20Instance={erc20Instance} buyToken={lendingPool === "Aave" ? "DAI" : "mUSD"} availableBalance={state.erc20Balance} balanaceNeeded={state.collateralAmount} callback={fetchContractData} /> : <SwapToken fixedBuyToken={true} buyToken={lendingPool === "Aave" ? "DAI" : "mUSD"} availableBalance={state.erc20Balance} balanaceNeeded={state.collateralAmount} /> ) : null} </Card.Body> {state.isParticipant ? (time.currentUnixTime() >= Number(state.poolCloseTimestamp) ? (!state.claimedFinalYield ? <Card.Footer className="view-pool-footer"> <Button onClick={handleClaimFinalYield} variant="success" > {claimingYield ? <div className="d-flex align-items-center"> Processing <span className="loading ml-2"></span> </div> : <div>Claim Final Yield</div> } </Button> </Card.Footer> : <div className="info-message"> Thank you for your participation in the pool.<br /> You have already claimed your Final yield. <br /> Hope to see you on other pools <span role="img" aria-label="smile-emoji"> 🙂</span> </div> ) : (state.alreadyTakenLoan ? <div className="info-message"> Congratulations <span role="img" aria-label="congratualation-emoji"> 🎉</span><br /> You have already won a Loan of amount {state.loanAmount} {getTokenSymbol()}<br /> Now, You can't take part in bidding process. </div> : (!state.isLoanWinner && time.currentUnixTime() > Number(state.autionStartTimestamp) && time.currentUnixTime() < Number(state.autionCloseTimestamp) ? <div> {state.userCurrentBid > 0 && !showBid ? <div className="info-message"> You have successfully placed your bid for this auction.<br /> <span> Your bid is {state.userCurrentBid} {getTokenSymbol()}<br /> </span> </div> : null } <Card.Footer className="view-pool-footer"> <Button onClick={() => setShowBid(true)} variant="warning" > {state.userCurrentBid > 0 ? <div>Want to Bid Higher ?</div> : <div>Want to Bid ?</div> } </Button> </Card.Footer> </div> : (state.isLoanWinner ? <div> <div className="info-message"> You have successfully won the bid in auction {state.winnerInAuction} <br />click below button to claim your loan of <span> {state.loanAmount} {getTokenSymbol()}.</span> </div> <Card.Footer className="view-pool-footer"> <Button onClick={handleClaimLoan} variant="success" > {claimingLoan ? <div className="d-flex align-items-center"> Processing <span className="loading ml-2"></span> </div> : <div>Claim Your Loan</div> } </Button> </Card.Footer> </div> : <div className="info-message"> Thank you for your participation in the pool.<br /> {state.totalParticipants <= 1 ? <div> The bid will start once at least one more person join the pool. </div> : <div> Please wait till next auction. </div> } </div> ) ) ) ) : (time.currentUnixTime() < (state.createdAt + state.auctionInterval * 3600) ? <Card.Footer className="view-pool-footer"> <Button onClick={() => setShowParticipate(true)} variant="success" > Want to Participate ? </Button> </Card.Footer> : <div className="alert-message"> Participation time already over.<br /> Please check other Pools. </div> ) } </Card> <Card className="hidden-card"></Card> </CardDeck> } <AlertModal open={errorModal.open} toggle={() => setErrorModal({ ...errorModal, open: false })} > {errorModal.msg} </AlertModal> <SuccessModal open={successModal.open} toggle={() => setSuccessModal({ ...successModal, open: false })} > {successModal.msg} </SuccessModal> </div > ); } return routes; }