diff --git a/.eslintrc b/.eslintrc index db7ff9c9a..f7f79391d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -16,6 +16,7 @@ "no-console": ["warn", { "allow": ["info", "warn", "error"] }], "no-plusplus": 0, "prefer-destructuring": ["warn", { "object": true, "array": false }], + "no-underscore-dangle": 0, // Start temporary rules // These rules are here just to keep the lint error to 0 during the migration to the new rule set // They need to be removed and fixed as soon as possible diff --git a/src/App.tsx b/src/App.tsx index 3efd9fc11..176461539 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' import { useWallet } from 'use-wallet' import { ResetCSS, Footer } from '@pancakeswap-libs/uikit' -import useStateInit from 'state/hooks' +import { useFetchPublicData } from 'state/hooks' import GlobalStyle from './style/Global' import Menu from './components/Menu' import Farms from './views/Farms' @@ -24,7 +24,7 @@ const App: React.FC = () => { } }, [account, connect]) - useStateInit() + useFetchPublicData() return ( diff --git a/src/Providers.tsx b/src/Providers.tsx index b3427b360..905bf7982 100644 --- a/src/Providers.tsx +++ b/src/Providers.tsx @@ -7,6 +7,7 @@ import SushiProvider from 'contexts/SushiProvider' import { LanguageContextProvider } from 'contexts/Localisation/languageContext' import { ThemeContextProvider } from 'contexts/ThemeContext' import { BlockContextProvider } from 'contexts/BlockContext' +import { RefreshContextProvider } from 'contexts/RefreshContext' import store from 'state' const Providers: React.FC = ({ children }) => { @@ -24,7 +25,9 @@ const Providers: React.FC = ({ children }) => { > - {children} + + {children} + diff --git a/src/contexts/BlockContext.tsx b/src/contexts/BlockContext.tsx index c7ac72097..dbdc788ed 100644 --- a/src/contexts/BlockContext.tsx +++ b/src/contexts/BlockContext.tsx @@ -15,7 +15,7 @@ const BlockContextProvider = ({ children }) => { previousBlock.current = blockNumber setBlock(blockNumber) } - }, 5000) + }, 6000) return () => clearInterval(interval) }, []) diff --git a/src/contexts/RefreshContext.tsx b/src/contexts/RefreshContext.tsx new file mode 100644 index 000000000..d1f600b59 --- /dev/null +++ b/src/contexts/RefreshContext.tsx @@ -0,0 +1,30 @@ +import React, { useState, useEffect } from 'react' + +const FAST_INTERVAL = 10000 +const SLOW_INTERVAL = 60000 + +const RefreshContext = React.createContext({ slow: 0, fast: 0 }) + +// This context maintain 2 counters that can be used as a dependencies on other hooks to force a periodic refresh +const RefreshContextProvider = ({ children }) => { + const [slow, setSlow] = useState(0) + const [fast, setFast] = useState(0) + + useEffect(() => { + const interval = setInterval(async () => { + setFast((prev) => prev + 1) + }, FAST_INTERVAL) + return () => clearInterval(interval) + }, []) + + useEffect(() => { + const interval = setInterval(async () => { + setSlow((prev) => prev + 1) + }, SLOW_INTERVAL) + return () => clearInterval(interval) + }, []) + + return {children} +} + +export { RefreshContext, RefreshContextProvider } diff --git a/src/hooks/rework/useAllowance.ts b/src/hooks/rework/useAllowance.ts deleted file mode 100644 index 6fd713593..000000000 --- a/src/hooks/rework/useAllowance.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useEffect, useState } from 'react' -import BigNumber from 'bignumber.js' -import { Contract } from 'web3-eth-contract' -import { useWallet } from 'use-wallet' - -export const useAllowance = (tokenContract: Contract, spenderAddress: string, dependency?: any) => { - const { account }: { account: string } = useWallet() - const [allowance, setAllowance] = useState(null) - - useEffect(() => { - const fetch = async () => { - try { - const res = await tokenContract.methods.allowance(account, spenderAddress).call() - setAllowance(new BigNumber(res)) - } catch (e) { - setAllowance(null) - } - } - fetch() - }, [account, spenderAddress, tokenContract, dependency]) - - return allowance -} - -export default useAllowance diff --git a/src/hooks/rework/useApprove.ts b/src/hooks/rework/useApprove.ts deleted file mode 100644 index c04c69234..000000000 --- a/src/hooks/rework/useApprove.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useCallback } from 'react' -import { Contract } from 'web3-eth-contract' -import { ethers } from 'ethers' -import { useWallet } from 'use-wallet' - -export const useApprove = (tokenContract: Contract, spenderAddress: string) => { - const { account } = useWallet() - const onApprove = useCallback(async () => { - try { - const tx = await tokenContract.methods - .approve(spenderAddress, ethers.constants.MaxUint256) - .send({ from: account }) - return tx - } catch { - return false - } - }, [account, spenderAddress, tokenContract]) - - return onApprove -} - -export default useApprove diff --git a/src/hooks/rework/useBnbBalance.ts b/src/hooks/rework/useBnbBalance.ts deleted file mode 100644 index e781dc95d..000000000 --- a/src/hooks/rework/useBnbBalance.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useEffect, useState } from 'react' -import BigNumber from 'bignumber.js' -import { useWallet } from 'use-wallet' -import useWeb3 from 'hooks/rework/useWeb3' -import useBlock from '../useBlock' - -const ZERO = new BigNumber(0) - -const useUserBnbBalance = () => { - const { account } = useWallet() - const web3 = useWeb3() - const [balance, setBalance] = useState(ZERO) - const block = useBlock() - - useEffect(() => { - const fetch = async () => { - try { - const res = new BigNumber(await web3.eth.getBalance(account)) - setBalance(res) - } catch (error) { - console.error(error) - } - } - if (account) { - fetch() - } - }, [account, web3, block]) - - return balance -} - -export default useUserBnbBalance diff --git a/src/hooks/useAllEarnings.ts b/src/hooks/useAllEarnings.ts index d36a87dba..2fb6e378a 100644 --- a/src/hooks/useAllEarnings.ts +++ b/src/hooks/useAllEarnings.ts @@ -4,17 +4,17 @@ import multicall from 'utils/multicall' import masterChefABI from 'sushi/lib/abi/masterchef.json' import addresses from 'sushi/lib/constants/contracts' import { farmsConfig } from 'sushi/lib/constants' -import useBlock from './useBlock' +import useRefresh from './useRefresh' const useAllEarnings = () => { const [balances, setBalance] = useState([]) const { account }: { account: string } = useWallet() - const block = useBlock() + const { fastRefresh } = useRefresh() useEffect(() => { const fetchAllBalances = async () => { const calls = farmsConfig.map((farm) => ({ - address: addresses.masterChef[56], + address: addresses.masterChef[process.env.REACT_APP_CHAIN_ID], name: 'pendingCake', params: [farm.pid, account], })) @@ -27,7 +27,7 @@ const useAllEarnings = () => { if (account) { fetchAllBalances() } - }, [account, block]) + }, [account, fastRefresh]) return balances } diff --git a/src/hooks/useAllowance.ts b/src/hooks/useAllowance.ts index 2e9bfb0bc..188c3d975 100644 --- a/src/hooks/useAllowance.ts +++ b/src/hooks/useAllowance.ts @@ -4,50 +4,49 @@ import { useWallet } from 'use-wallet' import { Contract } from 'web3-eth-contract' import useSushi from './useSushi' import { getAllowance } from '../utils/erc20' -import { getSushiContract, getSousChefContract } from '../sushi/utils' +import { getSushiContract } from '../sushi/utils' import { getLotteryContract } from '../sushi/lotteryUtils' -export const useSousAllowance = (lpContract: Contract, sousId) => { +// Retrieve lottery allowance +export const useLotteryAllowance = () => { const [allowance, setAllowance] = useState(new BigNumber(0)) const { account }: { account: string } = useWallet() const sushi = useSushi() - const sousChefContract = getSousChefContract(sushi, sousId) + const lotteryContract = getLotteryContract(sushi) + const cakeContract = getSushiContract(sushi) useEffect(() => { const fetchAllowance = async () => { - const res = await getAllowance(lpContract, sousChefContract, account) + const res = await getAllowance(cakeContract, lotteryContract, account) setAllowance(new BigNumber(res)) } - if (account && sousChefContract && lpContract) { + if (account && cakeContract && cakeContract) { fetchAllowance() } const refreshInterval = setInterval(fetchAllowance, 10000) return () => clearInterval(refreshInterval) - }, [account, sousChefContract, lpContract]) + }, [account, cakeContract, lotteryContract]) return allowance } -export const useLotteryAllowance = () => { - const [allowance, setAllowance] = useState(new BigNumber(0)) +// Retrieve IFO allowance +export const useIfoAllowance = (tokenContract: Contract, spenderAddress: string, dependency?: any) => { const { account }: { account: string } = useWallet() - const sushi = useSushi() - const lotteryContract = getLotteryContract(sushi) - const cakeContract = getSushiContract(sushi) + const [allowance, setAllowance] = useState(null) useEffect(() => { - const fetchAllowance = async () => { - const res = await getAllowance(cakeContract, lotteryContract, account) - setAllowance(new BigNumber(res)) - } - - if (account && cakeContract && cakeContract) { - fetchAllowance() + const fetch = async () => { + try { + const res = await tokenContract.methods.allowance(account, spenderAddress).call() + setAllowance(new BigNumber(res)) + } catch (e) { + setAllowance(null) + } } - const refreshInterval = setInterval(fetchAllowance, 10000) - return () => clearInterval(refreshInterval) - }, [account, cakeContract, lotteryContract]) + fetch() + }, [account, spenderAddress, tokenContract, dependency]) return allowance } diff --git a/src/hooks/useApprove.ts b/src/hooks/useApprove.ts index b91c3ea59..8247a8264 100644 --- a/src/hooks/useApprove.ts +++ b/src/hooks/useApprove.ts @@ -1,11 +1,16 @@ import { useCallback } from 'react' import { useWallet } from 'use-wallet' import { Contract } from 'web3-eth-contract' +import { ethers } from 'ethers' +import { useDispatch } from 'react-redux' +import { updateUserAllowance, fetchFarmUserDataAsync } from 'state/actions' +import { getSushiContract, approve, getMasterChefContract, getSousChefContract } from 'sushi/utils' +import { getLotteryContract } from 'sushi/lotteryUtils' import useSushi from './useSushi' -import { getSushiContract, approve, getMasterChefContract, getSousChefContract } from '../sushi/utils' -import { getLotteryContract } from '../sushi/lotteryUtils' -const useApprove = (lpContract: Contract) => { +// Approve a Farm +export const useApprove = (lpContract: Contract, pid: number) => { + const dispatch = useDispatch() const { account }: { account: string } = useWallet() const sushi = useSushi() const masterChefContract = getMasterChefContract(sushi) @@ -13,16 +18,19 @@ const useApprove = (lpContract: Contract) => { const handleApprove = useCallback(async () => { try { const tx = await approve(lpContract, masterChefContract, account) + dispatch(fetchFarmUserDataAsync(pid, account)) return tx } catch (e) { return false } - }, [account, lpContract, masterChefContract]) + }, [account, dispatch, lpContract, masterChefContract, pid]) return { onApprove: handleApprove } } +// Approve a Pool export const useSousApprove = (lpContract: Contract, sousId) => { + const dispatch = useDispatch() const { account }: { account: string } = useWallet() const sushi = useSushi() const sousChefContract = getSousChefContract(sushi, sousId) @@ -30,15 +38,17 @@ export const useSousApprove = (lpContract: Contract, sousId) => { const handleApprove = useCallback(async () => { try { const tx = await approve(lpContract, sousChefContract, account) + dispatch(updateUserAllowance(sousId, account)) return tx } catch (e) { return false } - }, [account, lpContract, sousChefContract]) + }, [account, dispatch, lpContract, sousChefContract, sousId]) return { onApprove: handleApprove } } +// Approve the lottery export const useLotteryApprove = () => { const { account }: { account: string } = useWallet() const sushi = useSushi() @@ -57,4 +67,19 @@ export const useLotteryApprove = () => { return { onApprove: handleApprove } } -export default useApprove +// Approve an IFO +export const useIfoApprove = (tokenContract: Contract, spenderAddress: string) => { + const { account } = useWallet() + const onApprove = useCallback(async () => { + try { + const tx = await tokenContract.methods + .approve(spenderAddress, ethers.constants.MaxUint256) + .send({ from: account }) + return tx + } catch { + return false + } + }, [account, spenderAddress, tokenContract]) + + return onApprove +} diff --git a/src/hooks/useBlock.ts b/src/hooks/useBlock.ts index 5b8411b73..670d5e160 100644 --- a/src/hooks/useBlock.ts +++ b/src/hooks/useBlock.ts @@ -1,10 +1,8 @@ import { useContext } from 'react' - import { BlockContext } from 'contexts/BlockContext' const useBlock = () => { const block: number = useContext(BlockContext) - return block } diff --git a/src/hooks/rework/useContract.ts b/src/hooks/useContract.ts similarity index 97% rename from src/hooks/rework/useContract.ts rename to src/hooks/useContract.ts index 8db20f8a8..8d28b4618 100644 --- a/src/hooks/rework/useContract.ts +++ b/src/hooks/useContract.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' import { AbiItem } from 'web3-utils' import { ContractOptions } from 'web3-eth-contract' -import useWeb3 from 'hooks/rework/useWeb3' +import useWeb3 from 'hooks/useWeb3' import ifo from 'sushi/lib/abi/ifo.json' import erc20 from 'sushi/lib/abi/erc20.json' import rabbitmintingfarm from 'sushi/lib/abi/rabbitmintingfarm.json' diff --git a/src/hooks/useEarnings.ts b/src/hooks/useEarnings.ts deleted file mode 100644 index dc291abec..000000000 --- a/src/hooks/useEarnings.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useEffect, useState } from 'react' -import BigNumber from 'bignumber.js' -import { useWallet } from 'use-wallet' -import { getEarned, getSousEarned, getSousChefContract, getMasterChefContract } from 'sushi/utils' -import { usePoolFromPid } from 'state/hooks' -import useSushi from './useSushi' -import useBlock from './useBlock' - -export const useSousEarnings = (sousId) => { - const block = useBlock() - const [balance, setBalance] = useState(new BigNumber(0)) - const { account }: { account: string } = useWallet() - const sushi = useSushi() - const sousChefContract = getSousChefContract(sushi, sousId) - const masterChefContract = getMasterChefContract(sushi) - - useEffect(() => { - const fetchBalance = async () => { - if (sousId === 0) { - const res = await getEarned(masterChefContract, '0', account) - setBalance(new BigNumber(res)) - } else { - const res = await getSousEarned(sousChefContract, account) - setBalance(new BigNumber(res)) - } - } - - if (account && sousChefContract) { - fetchBalance() - } - }, [account, masterChefContract, sousChefContract, sousId, block]) - - return balance -} - -export const useSousLeftBlocks = (sousId) => { - const { startBlock, endBlock, isFinished } = usePoolFromPid(sousId) - const block = useBlock() - - return { - blocksUntilStart: Math.max(startBlock - block, 0), - blocksRemaining: Math.max(endBlock - block, 0), - isFinished: sousId === 0 ? false : isFinished || block > endBlock, - } -} diff --git a/src/hooks/useFarmsWithBalance.ts b/src/hooks/useFarmsWithBalance.ts index 6d6980977..fec8c1094 100644 --- a/src/hooks/useFarmsWithBalance.ts +++ b/src/hooks/useFarmsWithBalance.ts @@ -1,55 +1,40 @@ import { useEffect, useState } from 'react' import BigNumber from 'bignumber.js' import { useWallet } from 'use-wallet' -import { Contract } from 'web3-eth-contract' -import { getEarned, getMasterChefContract, getFarms } from '../sushi/utils' -import useSushi from './useSushi' -import useBlock from './useBlock' - -export interface Farm { - pid: number - name: string - lpSymbol: string - lpAddress: string - lpContract: Contract - tokenAddress: string - earnToken: string - earnTokenAddress: string - tokenSymbol: string - multiplier: string -} - -export interface FarmWithBalance extends Farm { +import multicall from 'utils/multicall' +import masterChefABI from 'sushi/lib/abi/masterchef.json' +import addresses from 'sushi/lib/constants/contracts' +import { farmsConfig } from 'sushi/lib/constants' +import { FarmConfig } from 'sushi/lib/constants/types' +import useRefresh from './useRefresh' + +export interface FarmWithBalance extends FarmConfig { balance: BigNumber } const useFarmsWithBalance = () => { const [farmsWithBalances, setFarmsWithBalances] = useState([]) const { account } = useWallet() - const sushi = useSushi() - const farms = getFarms(sushi) - const masterChefContract = getMasterChefContract(sushi) - const block = useBlock() + const { fastRefresh } = useRefresh() useEffect(() => { const fetchBalances = async () => { - const newList: Promise[] = farms.map(async (farm) => { - const balance = await getEarned(masterChefContract, farm.pid, account) + const calls = farmsConfig.map((farm) => ({ + address: addresses.masterChef[process.env.REACT_APP_CHAIN_ID], + name: 'pendingCake', + params: [farm.pid, account], + })) - return { - ...farm, - balance: new BigNumber(balance), - } - }) + const rawResults = await multicall(masterChefABI, calls) + const results = farmsConfig.map((farm, index) => ({ ...farm, balance: new BigNumber(rawResults[index]) })) - const results = await Promise.all(newList) setFarmsWithBalances(results) } - if (account && masterChefContract && sushi) { + if (account) { fetchBalances() } - }, [account, farms, masterChefContract, sushi, block]) + }, [account, fastRefresh]) return farmsWithBalances } diff --git a/src/hooks/useHarvest.ts b/src/hooks/useHarvest.ts index 217654163..6c1a46df3 100644 --- a/src/hooks/useHarvest.ts +++ b/src/hooks/useHarvest.ts @@ -1,17 +1,21 @@ import { useCallback } from 'react' import { useWallet } from 'use-wallet' +import { useDispatch } from 'react-redux' +import { fetchFarmUserDataAsync, updateUserBalance, updateUserPendingReward } from 'state/actions' +import { soushHarvest, soushHarvestBnb, harvest, getMasterChefContract, getSousChefContract } from 'sushi/utils' import useSushi from './useSushi' -import { soushHarvest, soushHarvestBnb, harvest, getMasterChefContract, getSousChefContract } from '../sushi/utils' export const useHarvest = (farmPid: number) => { + const dispatch = useDispatch() const { account } = useWallet() const sushi = useSushi() const masterChefContract = getMasterChefContract(sushi) const handleHarvest = useCallback(async () => { const txHash = await harvest(masterChefContract, farmPid, account) + dispatch(fetchFarmUserDataAsync(farmPid, account)) return txHash - }, [account, farmPid, masterChefContract]) + }, [account, dispatch, farmPid, masterChefContract]) return { onReward: handleHarvest } } @@ -33,6 +37,7 @@ export const useAllHarvest = (farmPids: number[]) => { } export const useSousHarvest = (sousId, isUsingBnb = false) => { + const dispatch = useDispatch() const { account } = useWallet() const sushi = useSushi() const sousChefContract = getSousChefContract(sushi, sousId) @@ -40,16 +45,15 @@ export const useSousHarvest = (sousId, isUsingBnb = false) => { const handleHarvest = useCallback(async () => { if (sousId === 0) { - const txHash = await harvest(masterChefContract, 0, account) - return txHash + await harvest(masterChefContract, 0, account) + } else if (isUsingBnb) { + await soushHarvestBnb(sousChefContract, account) + } else { + await soushHarvest(sousChefContract, account) } - if (isUsingBnb) { - const txHash = await soushHarvestBnb(sousChefContract, account) - return txHash - } - const txHash = await soushHarvest(sousChefContract, account) - return txHash - }, [account, isUsingBnb, masterChefContract, sousChefContract, sousId]) + dispatch(updateUserPendingReward(sousId, account)) + dispatch(updateUserBalance(sousId, account)) + }, [account, dispatch, isUsingBnb, masterChefContract, sousChefContract, sousId]) return { onReward: handleHarvest } } diff --git a/src/hooks/useRefresh.ts b/src/hooks/useRefresh.ts new file mode 100644 index 000000000..1ce2d936b --- /dev/null +++ b/src/hooks/useRefresh.ts @@ -0,0 +1,9 @@ +import { useContext } from 'react' +import { RefreshContext } from 'contexts/RefreshContext' + +const useRefresh = () => { + const { fast, slow } = useContext(RefreshContext) + return { fastRefresh: fast, slowRefresh: slow } +} + +export default useRefresh diff --git a/src/hooks/useStake.ts b/src/hooks/useStake.ts index 171f7948e..dc5f6c1db 100644 --- a/src/hooks/useStake.ts +++ b/src/hooks/useStake.ts @@ -1,26 +1,29 @@ import { useCallback } from 'react' - import { useWallet } from 'use-wallet' +import { useDispatch } from 'react-redux' +import { fetchFarmUserDataAsync, updateUserStakedBalance, updateUserBalance } from 'state/actions' +import { stake, sousStake, sousStakeBnb, getMasterChefContract, getSousChefContract } from 'sushi/utils' import useSushi from './useSushi' -import { stake, sousStake, sousStakeBnb, getMasterChefContract, getSousChefContract } from '../sushi/utils' - const useStake = (pid: number) => { + const dispatch = useDispatch() const { account } = useWallet() const sushi = useSushi() const handleStake = useCallback( async (amount: string) => { const txHash = await stake(getMasterChefContract(sushi), pid, amount, account) + dispatch(fetchFarmUserDataAsync(pid, account)) console.info(txHash) }, - [account, pid, sushi], + [account, dispatch, pid, sushi], ) return { onStake: handleStake } } export const useSousStake = (sousId, isUsingBnb = false) => { + const dispatch = useDispatch() const { account } = useWallet() const sushi = useSushi() @@ -33,8 +36,10 @@ export const useSousStake = (sousId, isUsingBnb = false) => { } else { await sousStake(getSousChefContract(sushi, sousId), amount, account) } + dispatch(updateUserStakedBalance(sousId, account)) + dispatch(updateUserBalance(sousId, account)) }, - [account, isUsingBnb, sousId, sushi], + [account, dispatch, isUsingBnb, sousId, sushi], ) return { onStake: handleStake } diff --git a/src/hooks/useStakedBalance.ts b/src/hooks/useStakedBalance.ts deleted file mode 100644 index c6166b6f9..000000000 --- a/src/hooks/useStakedBalance.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' -import BigNumber from 'bignumber.js' -import { useWallet } from 'use-wallet' -import { getStaked, getMasterChefContract, getSousChefContract, getSousStaked } from '../sushi/utils' -import useSushi from './useSushi' -import useBlock from './useBlock' - -const useSousStakedBalance = (sousId) => { - const [balance, setBalance] = useState(new BigNumber(0)) - const { account }: { account: string } = useWallet() - const sushi = useSushi() - const sousChefContract = getSousChefContract(sushi, sousId) - const masterChefContract = getMasterChefContract(sushi) - const block = useBlock() - - const fetchBalance = useCallback(async () => { - if (sousId === 0) { - const res = await getStaked(masterChefContract, '0', account) - setBalance(new BigNumber(res)) - } else { - const res = await getSousStaked(sousChefContract, account) - setBalance(new BigNumber(res)) - } - }, [sousId, masterChefContract, account, sousChefContract]) - - useEffect(() => { - if (account && sushi) { - fetchBalance() - } - }, [account, setBalance, block, sushi, fetchBalance]) - - return balance -} - -export default useSousStakedBalance diff --git a/src/hooks/useTickets.ts b/src/hooks/useTickets.ts index 09fd8da1e..f68dd68ab 100644 --- a/src/hooks/useTickets.ts +++ b/src/hooks/useTickets.ts @@ -2,7 +2,7 @@ import { useCallback, useState, useEffect } from 'react' import { useWallet } from 'use-wallet' import BigNumber from 'bignumber.js' import useSushi from './useSushi' -import useBlock from './useBlock' +import useRefresh from './useRefresh' import { getTotalRewards, getTotalClaim, @@ -19,7 +19,7 @@ const useTickets = () => { const sushi = useSushi() const ticketsContract = getTicketsContract(sushi) const lotteryContract = getLotteryContract(sushi) - const block = useBlock() + const { fastRefresh } = useRefresh() useEffect(() => { const fetchBalance = async () => { @@ -30,7 +30,7 @@ const useTickets = () => { if (account && lotteryContract && ticketsContract && sushi) { fetchBalance() } - }, [account, lotteryContract, sushi, ticketsContract, block]) + }, [account, lotteryContract, sushi, ticketsContract, fastRefresh]) return tickets } @@ -40,7 +40,7 @@ export const useTotalRewards = () => { const { account } = useWallet() const sushi = useSushi() const lotteryContract = getLotteryContract(sushi) - const block = useBlock() + const { fastRefresh } = useRefresh() useEffect(() => { const fetchBalance = async () => { @@ -51,7 +51,7 @@ export const useTotalRewards = () => { if (account && lotteryContract && sushi) { fetchBalance() } - }, [account, lotteryContract, sushi, block]) + }, [account, lotteryContract, sushi, fastRefresh]) return rewards } @@ -85,7 +85,7 @@ export const useWinningNumbers = () => { const { account } = useWallet() const sushi = useSushi() const lotteryContract = getLotteryContract(sushi) - const block = useBlock() + const { fastRefresh } = useRefresh() useEffect(() => { const fetchBalance = async () => { @@ -96,7 +96,7 @@ export const useWinningNumbers = () => { if (account && lotteryContract && sushi) { fetchBalance() } - }, [account, block, lotteryContract, setWinningNumbers, sushi]) + }, [account, fastRefresh, lotteryContract, setWinningNumbers, sushi]) return winngNumbers } @@ -106,7 +106,7 @@ export const useMatchingRewardLength = (numbers) => { const { account } = useWallet() const sushi = useSushi() const lotteryContract = getLotteryContract(sushi) - const block = useBlock() + const { fastRefresh } = useRefresh() useEffect(() => { const fetchBalance = async () => { @@ -117,7 +117,7 @@ export const useMatchingRewardLength = (numbers) => { if (account && lotteryContract && sushi) { fetchBalance() } - }, [account, lotteryContract, numbers, sushi, block]) + }, [account, lotteryContract, numbers, sushi, fastRefresh]) return matchingNumbers } diff --git a/src/hooks/useTokenBalance.ts b/src/hooks/useTokenBalance.ts index 73863030f..dbeae8200 100644 --- a/src/hooks/useTokenBalance.ts +++ b/src/hooks/useTokenBalance.ts @@ -2,15 +2,16 @@ import { useEffect, useState } from 'react' import BigNumber from 'bignumber.js' import { useWallet } from 'use-wallet' import { provider } from 'web3-core' +import cakeABI from 'sushi/lib/abi/sushi.json' +import addresses from 'sushi/lib/constants/contracts' +import { getContract } from 'utils/web3' import { getTokenBalance } from '../utils/erc20' -import { getSushiSupply } from '../sushi/utils' -import useBlock from './useBlock' -import useSushi from './useSushi' +import useRefresh from './useRefresh' const useTokenBalance = (tokenAddress: string) => { const [balance, setBalance] = useState(new BigNumber(0)) const { account, ethereum }: { account: string; ethereum: provider } = useWallet() - const block = useBlock() + const { fastRefresh } = useRefresh() useEffect(() => { const fetchBalance = async () => { @@ -21,25 +22,24 @@ const useTokenBalance = (tokenAddress: string) => { if (account && ethereum) { fetchBalance() } - }, [account, ethereum, tokenAddress, block]) + }, [account, ethereum, tokenAddress, fastRefresh]) return balance } export const useTotalSupply = () => { - const sushi = useSushi() - const block = useBlock() + const { slowRefresh } = useRefresh() const [totalSupply, setTotalSupply] = useState() useEffect(() => { async function fetchTotalSupply() { - const supply = await getSushiSupply(sushi) - setTotalSupply(supply) + const cakeContract = getContract(cakeABI, addresses.sushi[process.env.REACT_APP_CHAIN_ID]) + const supply = await cakeContract.methods.totalSupply().call() + setTotalSupply(new BigNumber(supply)) } - if (sushi) { - fetchTotalSupply() - } - }, [block, sushi]) + + fetchTotalSupply() + }, [slowRefresh]) return totalSupply } @@ -47,7 +47,7 @@ export const useTotalSupply = () => { export const useBurnedBalance = (tokenAddress: string) => { const [balance, setBalance] = useState(new BigNumber(0)) const { account, ethereum }: { account: string; ethereum: provider } = useWallet() - const block = useBlock() + const { slowRefresh } = useRefresh() useEffect(() => { const fetchBalance = async () => { @@ -58,7 +58,7 @@ export const useBurnedBalance = (tokenAddress: string) => { if (account && ethereum) { fetchBalance() } - }, [account, ethereum, tokenAddress, block]) + }, [account, ethereum, tokenAddress, slowRefresh]) return balance } diff --git a/src/hooks/useUnstake.ts b/src/hooks/useUnstake.ts index 39b4c99b4..0b54be6d8 100644 --- a/src/hooks/useUnstake.ts +++ b/src/hooks/useUnstake.ts @@ -1,11 +1,17 @@ import { useCallback } from 'react' - import { useWallet } from 'use-wallet' +import { useDispatch } from 'react-redux' +import { + fetchFarmUserDataAsync, + updateUserStakedBalance, + updateUserBalance, + updateUserPendingReward, +} from 'state/actions' +import { unstake, sousUnstake, getMasterChefContract, getSousChefContract, sousEmegencyUnstake } from 'sushi/utils' import useSushi from './useSushi' -import { unstake, sousUnstake, getMasterChefContract, getSousChefContract, sousEmegencyUnstake } from '../sushi/utils' - const useUnstake = (pid: number) => { + const dispatch = useDispatch() const { account } = useWallet() const sushi = useSushi() const masterChefContract = getMasterChefContract(sushi) @@ -13,9 +19,10 @@ const useUnstake = (pid: number) => { const handleUnstake = useCallback( async (amount: string) => { const txHash = await unstake(masterChefContract, pid, amount, account) + dispatch(fetchFarmUserDataAsync(pid, account)) console.info(txHash) }, - [account, masterChefContract, pid], + [account, dispatch, masterChefContract, pid], ) return { onUnstake: handleUnstake } @@ -24,6 +31,7 @@ const useUnstake = (pid: number) => { const SYRUPIDS = [5, 6, 3, 1] export const useSousUnstake = (sousId) => { + const dispatch = useDispatch() const { account } = useWallet() const sushi = useSushi() const sousChefContract = getSousChefContract(sushi, sousId) @@ -42,8 +50,11 @@ export const useSousUnstake = (sousId) => { const txHash = await sousUnstake(sousChefContract, amount, account) console.info(txHash) } + dispatch(updateUserStakedBalance(sousId, account)) + dispatch(updateUserBalance(sousId, account)) + dispatch(updateUserPendingReward(sousId, account)) }, - [sousId, isOldSyrup, masterChefContract, account, sousChefContract], + [account, dispatch, isOldSyrup, masterChefContract, sousChefContract, sousId], ) return { onUnstake: handleUnstake } diff --git a/src/hooks/rework/useWeb3.ts b/src/hooks/useWeb3.ts similarity index 100% rename from src/hooks/rework/useWeb3.ts rename to src/hooks/useWeb3.ts diff --git a/src/state/actions.ts b/src/state/actions.ts index f372f9cf2..d2a0a0cb6 100644 --- a/src/state/actions.ts +++ b/src/state/actions.ts @@ -1,2 +1,9 @@ export { fetchFarmsPublicDataAsync, fetchFarmUserDataAsync } from './farms' -export { fetchPoolsPublicDataAsync } from './pools' +export { + fetchPoolsPublicDataAsync, + fetchPoolsUserDataAsync, + updateUserAllowance, + updateUserBalance, + updateUserPendingReward, + updateUserStakedBalance, +} from './pools' diff --git a/src/state/farms/fetchFarmUser.ts b/src/state/farms/fetchFarmUser.ts index 409ba41af..b182dc3f5 100644 --- a/src/state/farms/fetchFarmUser.ts +++ b/src/state/farms/fetchFarmUser.ts @@ -44,7 +44,6 @@ const fetchFarmUser = async (pid: number, account: string) => { return { allowance: new BigNumber(allowance).toJSON(), tokenBalance: new BigNumber(tokenBalance).toJSON(), - // eslint-disable-next-line no-underscore-dangle stakedBalance: new BigNumber(userInfo[0]._hex).toJSON(), earnings: new BigNumber(earnings).toJSON(), } diff --git a/src/state/farms/fetchFarms.ts b/src/state/farms/fetchFarms.ts index 5f2e77049..576911eb9 100644 --- a/src/state/farms/fetchFarms.ts +++ b/src/state/farms/fetchFarms.ts @@ -83,7 +83,6 @@ const fetchFarms = async () => { }, ]) - // eslint-disable-next-line no-underscore-dangle const poolWeight = new BigNumber(info.allocPoint._hex).div(new BigNumber(totalAllocPoint)) return { diff --git a/src/state/hooks.ts b/src/state/hooks.ts index 5c7b60117..a76197d14 100644 --- a/src/state/hooks.ts +++ b/src/state/hooks.ts @@ -1,17 +1,24 @@ import BigNumber from 'bignumber.js' import { useEffect } from 'react' import { useSelector, useDispatch } from 'react-redux' -import { fetchFarmsPublicDataAsync, fetchFarmUserDataAsync, fetchPoolsPublicDataAsync } from './actions' +import useRefresh from 'hooks/useRefresh' +import { + fetchFarmsPublicDataAsync, + fetchFarmUserDataAsync, + fetchPoolsPublicDataAsync, + fetchPoolsUserDataAsync, +} from './actions' import { State, Farm, Pool } from './types' const ZERO = new BigNumber(0) -const useStateInit = () => { +export const useFetchPublicData = () => { const dispatch = useDispatch() + const { slowRefresh } = useRefresh() useEffect(() => { dispatch(fetchFarmsPublicDataAsync()) dispatch(fetchPoolsPublicDataAsync()) - }, [dispatch]) + }, [dispatch, slowRefresh]) } // Farms @@ -33,14 +40,14 @@ export const useFarmFromSymbol = (lpSymbol: string): Farm => { export const useFarmUser = (pid, account) => { const dispatch = useDispatch() + const { fastRefresh } = useRefresh() const farm = useFarmFromPid(pid) useEffect(() => { - if (!farm.userData && account) { + if (account) { dispatch(fetchFarmUserDataAsync(pid, account)) } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [account, pid]) + }, [account, dispatch, pid, fastRefresh]) return { allowance: farm.userData ? new BigNumber(farm.userData.allowance) : new BigNumber(0), @@ -52,7 +59,15 @@ export const useFarmUser = (pid, account) => { // Pools -export const usePools = (): Pool[] => { +export const usePools = (account): Pool[] => { + const { fastRefresh } = useRefresh() + const dispatch = useDispatch() + useEffect(() => { + if (account) { + dispatch(fetchPoolsUserDataAsync(account)) + } + }, [account, dispatch, fastRefresh]) + const pools = useSelector((state: State) => state.pools.data) return pools } @@ -76,5 +91,3 @@ export const usePriceCakeBusd = (): BigNumber => { const farm = useFarmFromPid(pid) return farm.tokenPriceVsQuote ? bnbPriceUSD.times(farm.tokenPriceVsQuote) : ZERO } - -export default useStateInit diff --git a/src/state/pools/fetchPoolsUser.ts b/src/state/pools/fetchPoolsUser.ts new file mode 100644 index 000000000..04515c32b --- /dev/null +++ b/src/state/pools/fetchPoolsUser.ts @@ -0,0 +1,99 @@ +import { AbiItem } from 'web3-utils' +import poolsConfig from 'sushi/lib/constants/pools' +import masterChefABI from 'sushi/lib/abi/masterchef.json' +import sousChefABI from 'sushi/lib/abi/sousChef.json' +import erc20ABI from 'sushi/lib/abi/erc20.json' +import { QuoteToken } from 'sushi/lib/constants/types' +import addresses from 'sushi/lib/constants/contracts' +import multicall from 'utils/multicall' +import { getWeb3 } from 'utils/web3' +import BigNumber from 'bignumber.js' + +const CHAIN_ID = process.env.REACT_APP_CHAIN_ID + +// Pool 0, Cake / Cake is a different kind of contract (master chef) +// BNB pools use the native BNB token (wrapping ? unwrapping is done at the contract level) +const nonBnbPools = poolsConfig.filter((p) => p.stakingTokenName !== QuoteToken.BNB) +const bnbPools = poolsConfig.filter((p) => p.stakingTokenName === QuoteToken.BNB) +const nonMasterPools = poolsConfig.filter((p) => p.sousId !== 0) +const web3 = getWeb3() +const masterChefContract = new web3.eth.Contract((masterChefABI as unknown) as AbiItem, addresses.masterChef[CHAIN_ID]) + +export const fetchPoolsAllowance = async (account) => { + const calls = nonBnbPools.map((p) => ({ + address: p.stakingTokenAddress, + name: 'allowance', + params: [account, p.contractAddress[CHAIN_ID]], + })) + + const allowances = await multicall(erc20ABI, calls) + return nonBnbPools.reduce( + (acc, pool, index) => ({ ...acc, [pool.sousId]: new BigNumber(allowances[index]).toJSON() }), + {}, + ) +} + +export const fetchUserBalances = async (account) => { + // Non BNB pools + const calls = nonBnbPools.map((p) => ({ + address: p.stakingTokenAddress, + name: 'balanceOf', + params: [account], + })) + const tokenBalancesRaw = await multicall(erc20ABI, calls) + const tokenBalances = nonBnbPools.reduce( + (acc, pool, index) => ({ ...acc, [pool.sousId]: new BigNumber(tokenBalancesRaw[index]).toJSON() }), + {}, + ) + + // BNB pools + const bnbBalance = await web3.eth.getBalance(account) + const bnbBalances = bnbPools.reduce( + (acc, pool) => ({ ...acc, [pool.sousId]: new BigNumber(bnbBalance).toJSON() }), + {}, + ) + + return { ...tokenBalances, ...bnbBalances } +} + +export const fetchUserStakeBalances = async (account) => { + const calls = nonMasterPools.map((p) => ({ + address: p.contractAddress[CHAIN_ID], + name: 'userInfo', + params: [account], + })) + const userInfo = await multicall(sousChefABI, calls) + const stakedBalances = nonMasterPools.reduce( + (acc, pool, index) => ({ + ...acc, + [pool.sousId]: new BigNumber(userInfo[index].amount._hex).toJSON(), + }), + {}, + ) + + // Cake / Cake pool + const { amount: masterPoolAmount } = await masterChefContract.methods.userInfo('0', account).call() + + return { ...stakedBalances, 0: new BigNumber(masterPoolAmount).toJSON() } +} + +export const fetchUserPendingRewards = async (account) => { + const calls = nonMasterPools.map((p) => ({ + address: p.contractAddress[CHAIN_ID], + name: 'pendingReward', + params: [account], + })) + const res = await multicall(sousChefABI, calls) + const pendingRewards = nonMasterPools.reduce( + (acc, pool, index) => ({ + ...acc, + [pool.sousId]: new BigNumber(res[index]).toJSON(), + }), + {}, + ) + + // Cake / Cake pool + const pendingReward = await masterChefContract.methods.pendingCake('0', account).call() + + return { ...pendingRewards, 0: new BigNumber(pendingReward).toJSON() } +} diff --git a/src/state/pools/index.ts b/src/state/pools/index.ts index 9ebce4305..9735ecec2 100644 --- a/src/state/pools/index.ts +++ b/src/state/pools/index.ts @@ -2,6 +2,12 @@ import { createSlice } from '@reduxjs/toolkit' import poolsConfig from 'sushi/lib/constants/pools' import { fetchPoolsBlockLimits, fetchPoolsTotalStatking } from './fetchPools' +import { + fetchPoolsAllowance, + fetchUserBalances, + fetchUserStakeBalances, + fetchUserPendingRewards, +} from './fetchPoolsUser' import { PoolsState, Pool } from '../types' const initialState: PoolsState = { data: [...poolsConfig] } @@ -12,16 +18,28 @@ export const PoolsSlice = createSlice({ reducers: { setPoolsPublicData: (state, action) => { const livePoolsData: Pool[] = action.payload - state.data = state.data.map((farm) => { - const livePoolData = livePoolsData.find((f) => f.sousId === farm.sousId) - return { ...farm, ...livePoolData } + state.data = state.data.map((pool) => { + const livePoolData = livePoolsData.find((entry) => entry.sousId === pool.sousId) + return { ...pool, ...livePoolData } }) }, + setPoolsUserData: (state, action) => { + const userData = action.payload + state.data = state.data.map((pool) => { + const userPoolData = userData.find((entry) => entry.sousId === pool.sousId) + return { ...pool, userData: userPoolData } + }) + }, + updatePoolsUserData: (state, action) => { + const { field, value, sousId } = action.payload + const index = state.data.findIndex((p) => p.sousId === sousId) + state.data[index] = { ...state.data[index], userData: { ...state.data[index].userData, [field]: value } } + }, }, }) // Actions -export const { setPoolsPublicData } = PoolsSlice.actions +export const { setPoolsPublicData, setPoolsUserData, updatePoolsUserData } = PoolsSlice.actions // Thunks export const fetchPoolsPublicDataAsync = () => async (dispatch) => { @@ -40,4 +58,41 @@ export const fetchPoolsPublicDataAsync = () => async (dispatch) => { dispatch(setPoolsPublicData(liveData)) } +export const fetchPoolsUserDataAsync = (account) => async (dispatch) => { + const allowances = await fetchPoolsAllowance(account) + const stakingTokenBalances = await fetchUserBalances(account) + const stakedBalances = await fetchUserStakeBalances(account) + const pendingRewards = await fetchUserPendingRewards(account) + + const userData = poolsConfig.map((pool) => ({ + sousId: pool.sousId, + allowance: allowances[pool.sousId], + stakingTokenBalance: stakingTokenBalances[pool.sousId], + stakedBalance: stakedBalances[pool.sousId], + pendingReward: pendingRewards[pool.sousId], + })) + + dispatch(setPoolsUserData(userData)) +} + +export const updateUserAllowance = (sousId: string, account: string) => async (dispatch) => { + const allowances = await fetchPoolsAllowance(account) + dispatch(updatePoolsUserData({ sousId, field: 'allowance', value: allowances[sousId] })) +} + +export const updateUserBalance = (sousId: string, account: string) => async (dispatch) => { + const tokenBalances = await fetchUserBalances(account) + dispatch(updatePoolsUserData({ sousId, field: 'stakingTokenBalance', value: tokenBalances[sousId] })) +} + +export const updateUserStakedBalance = (sousId: string, account: string) => async (dispatch) => { + const stakedBalances = await fetchUserStakeBalances(account) + dispatch(updatePoolsUserData({ sousId, field: 'stakedBalance', value: stakedBalances[sousId] })) +} + +export const updateUserPendingReward = (sousId: string, account: string) => async (dispatch) => { + const pendingRewards = await fetchUserPendingRewards(account) + dispatch(updatePoolsUserData({ sousId, field: 'pendingReward', value: pendingRewards[sousId] })) +} + export default PoolsSlice.reducer diff --git a/src/state/types.ts b/src/state/types.ts index a0708ca4a..0f15fe770 100644 --- a/src/state/types.ts +++ b/src/state/types.ts @@ -19,6 +19,12 @@ export interface Pool extends PoolConfig { totalStaked?: BigNumber startBlock?: number endBlock?: number + userData?: { + allowance: BigNumber + stakingTokenBalance: BigNumber + stakedBalance: BigNumber + pendingReward: BigNumber + } } // Slices states diff --git a/src/sushi/lib/contracts.js b/src/sushi/lib/contracts.js index b580b0618..109c44333 100644 --- a/src/sushi/lib/contracts.js +++ b/src/sushi/lib/contracts.js @@ -122,7 +122,6 @@ export default class Contracts { } catch (error) { const data = method.encodeABI() const { from, value } = options - // eslint-disable-next-line no-underscore-dangle const to = method._parent._address error.transactionData = { from, value, data, to } throw error diff --git a/src/sushi/utils.js b/src/sushi/utils.js index 602ae43c9..5fcc7f4dc 100644 --- a/src/sushi/utils.js +++ b/src/sushi/utils.js @@ -1,6 +1,4 @@ import BigNumber from 'bignumber.js' -import get from 'lodash/get' -import memoize from 'lodash/memoize' import { ethers } from 'ethers' BigNumber.config({ @@ -14,9 +12,6 @@ export const getSushiAddress = (sushi) => { export const getSyrupAddress = (sushi) => { return sushi && sushi.syrupAddress } -export const getSyrupContract = (sushi) => { - return sushi && sushi.contracts && sushi.contracts.syrup -} export const getMasterChefContract = (sushi) => { return sushi && sushi.contracts && sushi.contracts.masterChef } @@ -27,29 +22,12 @@ export const getSousChefContract = (sushi, sousId) => { return sushi && sushi.contracts && sushi.contracts.sousChefs.filter((chef) => chef.sousId === sousId)[0]?.sousContract } -export const getFarms = memoize((sushi) => { - const pools = get(sushi, 'contracts.pools', []) - return pools -}) - -export const getEarned = async (masterChefContract, pid, account) => { - return masterChefContract.methods.pendingCake(pid, account).call() -} - -export const getSousEarned = async (sousChefContract, account) => { - return sousChefContract.methods.pendingReward(account).call() -} - export const approve = async (lpContract, masterChefContract, account) => { return lpContract.methods .approve(masterChefContract.options.address, ethers.constants.MaxUint256) .send({ from: account }) } -export const getSushiSupply = async (sushi) => { - return new BigNumber(await sushi.contracts.sushi.methods.totalSupply().call()) -} - export const stake = async (masterChefContract, pid, amount, account) => { if (pid === 0) { return masterChefContract.methods @@ -172,22 +150,3 @@ export const soushHarvestBnb = async (sousChefContract, account) => { return tx.transactionHash }) } - -export const getStaked = async (masterChefContract, pid, account) => { - try { - const { amount } = await masterChefContract.methods.userInfo(pid, account).call() - return new BigNumber(amount) - } catch { - return new BigNumber(0) - } -} - -export const getSousStaked = async (sousChefContract, account) => { - try { - const { amount } = await sousChefContract.methods.userInfo(account).call() - return new BigNumber(amount) - } catch (err) { - console.error(err) - return new BigNumber(0) - } -} diff --git a/src/utils/web3.ts b/src/utils/web3.ts index e64fb93ae..331c92e07 100644 --- a/src/utils/web3.ts +++ b/src/utils/web3.ts @@ -14,10 +14,9 @@ const getWeb3 = () => { const web3 = new Web3(httpProvider) return web3 } - -const getContract = (abi: AbiItem, address: string, contractOptions?: ContractOptions) => { +const getContract = (abi: any, address: string, contractOptions?: ContractOptions) => { const web3 = getWeb3() - return new web3.eth.Contract(abi, address, contractOptions) + return new web3.eth.Contract((abi as unknown) as AbiItem, address, contractOptions) } export { getWeb3, getContract, httpProvider } diff --git a/src/views/Farm/Farm.tsx b/src/views/Farm/Farm.tsx index fc2c90d7e..d51532356 100644 --- a/src/views/Farm/Farm.tsx +++ b/src/views/Farm/Farm.tsx @@ -33,7 +33,7 @@ const Farm: React.FC = () => { - + = ({ pid, earnings, account }) => { - const dispatch = useDispatch() +const Harvest: React.FC = ({ pid, earnings }) => { const TranslateString = useI18n() const [pendingTx, setPendingTx] = useState(false) const { onReward } = useHarvest(pid) @@ -38,7 +34,6 @@ const Harvest: React.FC = ({ pid, earnings, account }) => { onClick={async () => { setPendingTx(true) await onReward() - dispatch(fetchFarmUserDataAsync(pid, account)) setPendingTx(false) }} > diff --git a/src/views/Farm/components/Stake.tsx b/src/views/Farm/components/Stake.tsx index 0ae774565..57801acab 100644 --- a/src/views/Farm/components/Stake.tsx +++ b/src/views/Farm/components/Stake.tsx @@ -4,13 +4,11 @@ import styled from 'styled-components' import { useWallet } from 'use-wallet' import { Contract } from 'web3-eth-contract' import { Button, IconButton, useModal, AddIcon, Card, CardBody } from '@pancakeswap-libs/uikit' -import { useDispatch } from 'react-redux' import Label from 'components/Label' -import useApprove from 'hooks/useApprove' +import { useApprove } from 'hooks/useApprove' import useStake from 'hooks/useStake' import useI18n from 'hooks/useI18n' import useUnstake from 'hooks/useUnstake' -import { fetchFarmUserDataAsync } from 'state/actions' import { getBalanceNumber } from 'utils/formatBalance' import UnlockButton from 'components/UnlockButton' import DepositModal from './DepositModal' @@ -28,41 +26,28 @@ interface StakeProps { } const Stake: React.FC = ({ lpContract, pid, tokenName, allowance, tokenBalance, stakedBalance }) => { - const dispatch = useDispatch() const { account } = useWallet() const [requestedApproval, setRequestedApproval] = useState(false) const TranslateString = useI18n() - const { onApprove } = useApprove(lpContract) + const { onApprove } = useApprove(lpContract, pid) const { onStake } = useStake(pid) const { onUnstake } = useUnstake(pid) - const handleStake = async (amount) => { - await onStake(amount) - dispatch(fetchFarmUserDataAsync(pid, account)) - } - const handleUnStake = async (amount) => { - await onUnstake(amount) - dispatch(fetchFarmUserDataAsync(pid, account)) - } - const [onPresentDeposit] = useModal() + const [onPresentDeposit] = useModal() const [onPresentWithdraw] = useModal( - , + , ) const handleApprove = useCallback(async () => { try { setRequestedApproval(true) - const txHash = await onApprove() - // user rejected tx or didn't go thru - if (txHash) { - dispatch(fetchFarmUserDataAsync(pid, account)) - } + await onApprove() setRequestedApproval(false) } catch (e) { console.error(e) } - }, [account, dispatch, onApprove, pid]) + }, [onApprove]) // We assume the token name is coin pair + lp e.g. CAKE-BNB LP, LINK-BNB LP, // NAR-CAKE LP. The images should be cake-bnb.svg, link-bnb.svg, nar-cake.svg diff --git a/src/views/Ifo/components/IfoCard/IfoCardContribute.tsx b/src/views/Ifo/components/IfoCard/IfoCardContribute.tsx index f84e00f51..a829b55ef 100644 --- a/src/views/Ifo/components/IfoCard/IfoCardContribute.tsx +++ b/src/views/Ifo/components/IfoCard/IfoCardContribute.tsx @@ -3,9 +3,9 @@ import { useModal, Button, Text } from '@pancakeswap-libs/uikit' import { useWallet } from 'use-wallet' import BigNumber from 'bignumber.js' import { Contract } from 'web3-eth-contract' -import { useERC20 } from 'hooks/rework/useContract' -import { useAllowance } from 'hooks/rework/useAllowance' -import { useApprove } from 'hooks/rework/useApprove' +import { useERC20 } from 'hooks/useContract' +import { useIfoAllowance } from 'hooks/useAllowance' +import { useIfoApprove } from 'hooks/useApprove' import { IfoStatus } from 'sushi/lib/constants/types' import { getBalanceNumber } from 'utils/formatBalance' import LabelButton from './LabelButton' @@ -34,8 +34,8 @@ const IfoCardContribute: React.FC = ({ const { account } = useWallet() const contractRaisingToken = useERC20(currencyAddress) - const allowance = useAllowance(contractRaisingToken, address, pendingTx) - const onApprove = useApprove(contractRaisingToken, address) + const allowance = useIfoAllowance(contractRaisingToken, address, pendingTx) + const onApprove = useIfoApprove(contractRaisingToken, address) const [onPresentContributeModal] = useModal( , ) diff --git a/src/views/Ifo/components/IfoCard/index.tsx b/src/views/Ifo/components/IfoCard/index.tsx index 777661fe8..6a63b2a63 100644 --- a/src/views/Ifo/components/IfoCard/index.tsx +++ b/src/views/Ifo/components/IfoCard/index.tsx @@ -7,7 +7,7 @@ import { BSC_BLOCK_TIME } from 'config' import { Ifo, IfoStatus } from 'sushi/lib/constants/types' import useI18n from 'hooks/useI18n' import useBlock from 'hooks/useBlock' -import { useIfoContract } from 'hooks/rework/useContract' +import { useIfoContract } from 'hooks/useContract' import UnlockButton from 'components/UnlockButton' import IfoCardHeader from './IfoCardHeader' import IfoCardProgress from './IfoCardProgress' diff --git a/src/views/Nft/components/BurnNftModal.tsx b/src/views/Nft/components/BurnNftModal.tsx index 3aa8290fd..48b3b4ee9 100644 --- a/src/views/Nft/components/BurnNftModal.tsx +++ b/src/views/Nft/components/BurnNftModal.tsx @@ -5,7 +5,7 @@ import { Button, Checkbox, Modal, Text } from '@pancakeswap-libs/uikit' import useI18n from 'hooks/useI18n' import { Nft } from 'sushi/lib/constants/types' import { RABBIT_MINTING_FARM_ADDRESS } from 'sushi/lib/constants/nfts' -import { useRabbitMintingFarm } from 'hooks/rework/useContract' +import { useRabbitMintingFarm } from 'hooks/useContract' import InfoRow from './InfoRow' interface BurnNftModalProps { diff --git a/src/views/Nft/components/ClaimNftModal.tsx b/src/views/Nft/components/ClaimNftModal.tsx index ec1073c7a..303810168 100644 --- a/src/views/Nft/components/ClaimNftModal.tsx +++ b/src/views/Nft/components/ClaimNftModal.tsx @@ -9,7 +9,7 @@ import { Nft } from 'sushi/lib/constants/types' import useSushi from 'hooks/useSushi' import useTokenBalance from 'hooks/useTokenBalance' import useI18n from 'hooks/useI18n' -import { useRabbitMintingFarm } from 'hooks/rework/useContract' +import { useRabbitMintingFarm } from 'hooks/useContract' import InfoRow from './InfoRow' interface ClaimNftModalProps { diff --git a/src/views/Pools/Syrup.tsx b/src/views/Pools/Syrup.tsx index 0ddb89bc5..64569063c 100644 --- a/src/views/Pools/Syrup.tsx +++ b/src/views/Pools/Syrup.tsx @@ -1,10 +1,10 @@ import React from 'react' import BigNumber from 'bignumber.js' import styled from 'styled-components' +import { useWallet } from 'use-wallet' import { Heading } from '@pancakeswap-libs/uikit' import orderBy from 'lodash/orderBy' import partition from 'lodash/partition' -import useUserBnbBalance from 'hooks/rework/useBnbBalance' import useI18n from 'hooks/useI18n' import { useFarms, usePriceBnbBusd, usePools } from 'state/hooks' import { QuoteToken } from 'sushi/lib/constants/types' @@ -13,9 +13,9 @@ import PoolCard from './components/PoolCard' const Farm: React.FC = () => { const TranslateString = useI18n() + const { account } = useWallet() const farms = useFarms() - const pools = usePools() - const userBnbBalance = useUserBnbBalance() + const pools = usePools(account) const bnbPriceUSD = usePriceBnbBusd() const cakePriceVsBNB = new BigNumber(farms.find((s) => s.tokenSymbol === 'CAKE')?.tokenPriceVsQuote || 0) @@ -58,11 +58,11 @@ const Farm: React.FC = () => { {orderBy(openPools, ['sortOrder']).map((pool) => ( - + ))} {orderBy(finishedPools, ['sortOrder']).map((pool) => ( - + ))} diff --git a/src/views/Pools/components/PoolCard.tsx b/src/views/Pools/components/PoolCard.tsx index 7e6fe91c7..1e4253632 100644 --- a/src/views/Pools/components/PoolCard.tsx +++ b/src/views/Pools/components/PoolCard.tsx @@ -6,15 +6,12 @@ import { useWallet } from 'use-wallet' import { BLOCKS_PER_YEAR } from 'config' import UnlockButton from 'components/UnlockButton' import Label from 'components/Label' -import { useERC20 } from 'hooks/rework/useContract' -import { useSousAllowance } from 'hooks/useAllowance' +import { useERC20 } from 'hooks/useContract' import { useSousApprove } from 'hooks/useApprove' import useI18n from 'hooks/useI18n' -import { useSousEarnings, useSousLeftBlocks } from 'hooks/useEarnings' import { useSousStake } from 'hooks/useStake' -import useSousStakedBalance from 'hooks/useStakedBalance' -import useTokenBalance from 'hooks/useTokenBalance' import { useSousUnstake } from 'hooks/useUnstake' +import useBlock from 'hooks/useBlock' import { getBalanceNumber } from 'utils/formatBalance' import { useSousHarvest } from 'hooks/useHarvest' import Balance from 'components/Balance' @@ -35,11 +32,10 @@ interface PoolWithPrice extends Pool { interface HarvestProps { cakePriceVsBNB: BigNumber - userBnbBalance: BigNumber pool: PoolWithPrice } -const PoolCard: React.FC = ({ cakePriceVsBNB, userBnbBalance, pool }) => { +const PoolCard: React.FC = ({ cakePriceVsBNB, pool }) => { const { sousId, image, @@ -53,29 +49,30 @@ const PoolCard: React.FC = ({ cakePriceVsBNB, userBnbBalance, pool tokenDecimals, poolCategory, totalStaked, + startBlock, + endBlock, + isFinished, + userData, } = pool // Pools using native BNB behave differently than pools using a token const isBnbPool = poolCategory === PoolCategory.BINANCE const TranslateString = useI18n() const stakingTokenContract = useERC20(stakingTokenAddress) - - // allowance and onApprove are used only when isBnbPool === false - const allowance = useSousAllowance(stakingTokenContract, sousId) + const { account } = useWallet() + const block = useBlock() const { onApprove } = useSousApprove(stakingTokenContract, sousId) - const tokenBalance = useTokenBalance(stakingTokenContract.options.address) - const userBalance = isBnbPool ? userBnbBalance : tokenBalance - - const { isFinished, blocksUntilStart, blocksRemaining } = useSousLeftBlocks(sousId) - const stakedBalance = useSousStakedBalance(sousId) - const earnings = useSousEarnings(sousId) const { onStake } = useSousStake(sousId, isBnbPool) const { onUnstake } = useSousUnstake(sousId) const { onReward } = useSousHarvest(sousId, isBnbPool) - const { account } = useWallet() const [requestedApproval, setRequestedApproval] = useState(false) const [pendingTx, setPendingTx] = useState(false) + const allowance = new BigNumber(userData?.allowance || 0) + const stakingTokenBalance = new BigNumber(userData?.stakingTokenBalance || 0) + const stakedBalance = new BigNumber(userData?.stakedBalance || 0) + const earnings = new BigNumber(userData?.pendingReward || 0) + const apy: BigNumber = useMemo(() => { if (cakePriceVsBNB?.isEqualTo(0) || tokenPrice?.isEqualTo(0) || !totalStaked) { return null @@ -88,17 +85,20 @@ const PoolCard: React.FC = ({ cakePriceVsBNB, userBnbBalance, pool return a.div(b).times(100) }, [cakePriceVsBNB, isBnbPool, tokenPerBlock, tokenPrice, totalStaked]) + const blocksUntilStart = Math.max(startBlock - block, 0) + const blocksRemaining = Math.max(endBlock - block, 0) + const isReallyFinished = sousId === 0 ? false : isFinished || block > endBlock const isOldSyrup = stakingTokenName === QuoteToken.SYRUP - const accountHasStakedBalance = account && stakedBalance.toNumber() > 0 + const accountHasStakedBalance = stakedBalance?.toNumber() > 0 const needsApproval = !accountHasStakedBalance && !allowance.toNumber() && !isBnbPool const isCardActive = isFinished && accountHasStakedBalance const [onPresentDeposit] = useModal( = ({ cakePriceVsBNB, userBnbBalance, pool }, [onApprove, setRequestedApproval]) return ( - - {isFinished && sousId !== 0 && } + + {isReallyFinished && sousId !== 0 && }
- + {isOldSyrup && '[OLD]'} {tokenName} {TranslateString(348, 'Pool')}
@@ -146,17 +146,17 @@ const PoolCard: React.FC = ({ cakePriceVsBNB, userBnbBalance, pool )}
{!isOldSyrup ? ( - + ) : ( )} -
- + diff --git a/src/views/Stake/components/Stake.tsx b/src/views/Stake/components/Stake.tsx index 00aba027d..b757abeef 100644 --- a/src/views/Stake/components/Stake.tsx +++ b/src/views/Stake/components/Stake.tsx @@ -9,7 +9,7 @@ import CardContent from '../../../components/CardContent' import CardIcon from '../../../components/CardIcon' import Label from '../../../components/Label' import Value from '../../../components/Value' -import useApprove from '../../../hooks/useApprove' +import { useApprove } from '../../../hooks/useApprove' import useStake from '../../../hooks/useStake' import useTokenBalance from '../../../hooks/useTokenBalance' import useUnstake from '../../../hooks/useUnstake' @@ -38,7 +38,7 @@ const Stake: React.FC = ({ lpContract, pid, tokenName, allowance, to const { onStake } = useStake(pid) const { onUnstake } = useUnstake(pid) - const { onApprove } = useApprove(lpContract) + const { onApprove } = useApprove(lpContract, pid) const syrupBalance = useTokenBalance(getSyrupAddress(sushi)) diff --git a/tsconfig.json b/tsconfig.json index 0f29a9b74..f58d76d95 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,11 +2,7 @@ "compilerOptions": { "baseUrl": "src", "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -18,12 +14,10 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx", + "jsx": "react", "strictNullChecks": false, "noImplicitAny": false, "noFallthroughCasesInSwitch": true }, - "include": [ - "src" - ] + "include": ["src"] }