Skip to content

Commit

Permalink
feat: add multichain support to pools user data queries
Browse files Browse the repository at this point in the history
  • Loading branch information
chefjackson committed Apr 20, 2023
1 parent f57e6e0 commit b351cf0
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 107 deletions.
85 changes: 0 additions & 85 deletions apps/web/src/state/pools/fetchPoolsUser.ts

This file was deleted.

8 changes: 4 additions & 4 deletions apps/web/src/state/pools/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,22 @@ export const useDeserializedPoolByVaultKey = (vaultKey) => {
}

export const usePoolsPageFetch = () => {
const { address: account } = useAccount()
const dispatch = useAppDispatch()
const { account, chainId } = useActiveWeb3React()
useFetchPublicPoolsData()

useFastRefreshEffect(() => {
batch(() => {
dispatch(fetchCakeVaultPublicData())
dispatch(fetchCakeFlexibleSideVaultPublicData())
dispatch(fetchIfoPublicDataAsync())
if (account) {
dispatch(fetchPoolsUserDataAsync(account))
if (account && chainId) {
dispatch(fetchPoolsUserDataAsync({ account, chainId }))
dispatch(fetchCakeVaultUserData({ account }))
dispatch(fetchCakeFlexibleSideVaultUserData({ account }))
}
})
}, [account, dispatch])
}, [account, chainId, dispatch])

useEffect(() => {
batch(() => {
Expand Down
31 changes: 16 additions & 15 deletions apps/web/src/state/pools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
fetchPoolsTotalStaking,
fetchPoolsProfileRequirement,
fetchPoolsStakingLimits,
fetchPoolsAllowance,
fetchUserBalances,
fetchUserPendingRewards,
fetchUserStakeBalances,
} from '@pancakeswap/pools'
import { ChainId } from '@pancakeswap/sdk'

Expand All @@ -34,12 +38,6 @@ import { farmV3ApiFetch } from 'state/farmsV3/hooks'

import fetchFarms from '../farms/fetchFarms'
import getFarmsPrices from '../farms/getFarmsPrices'
import {
fetchPoolsAllowance,
fetchUserBalances,
fetchUserPendingRewards,
fetchUserStakeBalances,
} from './fetchPoolsUser'
import { fetchPublicVaultData, fetchVaultFees, fetchPublicFlexibleSideVaultData } from './fetchVaultPublic'
import { getTokenPricesFromFarm } from './helpers'
import { resetUserState } from '../global/actions'
Expand Down Expand Up @@ -254,14 +252,17 @@ export const fetchPoolsStakingLimitsAsync = (chainId: ChainId) => async (dispatc

export const fetchPoolsUserDataAsync = createAsyncThunk<
{ sousId: number; allowance: any; stakingTokenBalance: any; stakedBalance: any; pendingReward: any }[],
string
>('pool/fetchPoolsUserData', async (account, { rejectWithValue }) => {
{
account: string
chainId: ChainId
}
>('pool/fetchPoolsUserData', async ({ account, chainId }, { rejectWithValue }) => {
try {
const [allowances, stakingTokenBalances, stakedBalances, pendingRewards] = await Promise.all([
fetchPoolsAllowance(account),
fetchUserBalances(account),
fetchUserStakeBalances(account),
fetchUserPendingRewards(account),
fetchPoolsAllowance({ account, chainId, provider }),
fetchUserBalances({ account, chainId, provider }),
fetchUserStakeBalances({ account, chainId, provider }),
fetchUserPendingRewards({ account, chainId, provider }),
])

const userData = poolsConfig.map((pool) => ({
Expand All @@ -279,9 +280,9 @@ export const fetchPoolsUserDataAsync = createAsyncThunk<

export const updateUserAllowance = createAsyncThunk<
{ sousId: number; field: string; value: any },
{ sousId: number; account: string }
>('pool/updateUserAllowance', async ({ sousId, account }) => {
const allowances = await fetchPoolsAllowance(account)
{ sousId: number; account: string; chainId: ChainId }
>('pool/updateUserAllowance', async ({ sousId, account, chainId }) => {
const allowances = await fetchPoolsAllowance({ account, chainId, provider })
return { sousId, field: 'allowance', value: allowances[sousId] }
})

Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/views/Pools/components/Modals/StakeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useERC20 } from 'hooks/useContract'
import { getDecimalAmount } from '@pancakeswap/utils/formatBalance'
import { useApprovePool } from 'views/Pools/hooks/useApprove'
import { usePool } from 'state/pools/hooks'
import { useActiveChainId } from 'hooks/useActiveChainId'

import useStakePool from '../../hooks/useStakePool'
import useUnstakePool from '../../hooks/useUnstakePool'
Expand All @@ -25,6 +26,7 @@ const StakeModalContainer = ({
stakingTokenPrice,
}: Pool.StakeModalPropsType<Token>) => {
const { t } = useTranslation()
const { chainId } = useActiveChainId()

const {
sousId,
Expand Down Expand Up @@ -117,7 +119,7 @@ const StakeModalContainer = ({

const handleEnableApprove = async () => {
await handleApprove()
dispatch(updateUserAllowance({ sousId, account }))
dispatch(updateUserAllowance({ sousId, account, chainId }))
}

return (
Expand Down
5 changes: 4 additions & 1 deletion apps/web/src/views/Pools/hooks/useApprove.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import useCatchTxError from 'hooks/useCatchTxError'
import { ToastDescriptionWithTx } from 'components/Toast'
import useCakeApprovalStatus from 'hooks/useCakeApprovalStatus'
import useCakeApprove from 'hooks/useCakeApprove'
import { useActiveChainId } from 'hooks/useActiveChainId'

export const useApprovePool = (lpContract: Contract, sousId, earningTokenSymbol) => {
const { toastSuccess } = useToast()
const { chainId } = useActiveChainId()
const { fetchWithCatchTxError, loading: pendingTx } = useCatchTxError()
const { callWithGasPrice } = useCallWithGasPrice()
const { t } = useTranslation()
Expand All @@ -34,9 +36,10 @@ export const useApprovePool = (lpContract: Contract, sousId, earningTokenSymbol)
{t('You can now stake in the %symbol% pool!', { symbol: earningTokenSymbol })}
</ToastDescriptionWithTx>,
)
dispatch(updateUserAllowance({ sousId, account }))
dispatch(updateUserAllowance({ sousId, account, chainId }))
}
}, [
chainId,
account,
dispatch,
lpContract,
Expand Down
3 changes: 2 additions & 1 deletion packages/multicall/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"./Multicall.json": "./Multicall.json"
}
}
107 changes: 107 additions & 0 deletions packages/pools/src/queries/fetchUserPoolsData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { createMulticall, multicallAddresses } from '@pancakeswap/multicall'
import multiCallAbi from '@pancakeswap/multicall/Multicall.json'
import { ChainId } from '@pancakeswap/sdk'
import BigNumber from 'bignumber.js'
import uniq from 'lodash/uniq'
import fromPairs from 'lodash/fromPairs'

import { getPoolsConfig } from '../constants'
import sousChefABI from '../abis/ISousChef.json'
import erc20ABI from '../abis/IERC20.json'
import { OnChainProvider, SerializedPool } from '../types'

// 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 getPoolsFactory = (filter: (pool: SerializedPool) => boolean) => (chainId: ChainId) => {
const poolsConfig = getPoolsConfig(chainId)
if (!poolsConfig) {
throw new Error(`Unable to get pools config on chain ${chainId}`)
}
return poolsConfig.filter(filter)
}
const getNonBnbPools = getPoolsFactory((pool) => pool.stakingToken.symbol !== 'BNB')
const getBnbPools = getPoolsFactory((pool) => pool.stakingToken.symbol === 'BNB')
const getNonMasterPools = getPoolsFactory((pool) => pool.sousId !== 0)

interface FetchUserDataParams {
account: string
chainId: ChainId
provider: OnChainProvider
}

export const fetchPoolsAllowance = async ({ account, chainId, provider }: FetchUserDataParams) => {
const nonBnbPools = getNonBnbPools(chainId)
const calls = nonBnbPools.map(({ contractAddress, stakingToken }) => ({
address: stakingToken.address,
name: 'allowance',
params: [account, contractAddress[chainId]],
}))

const { multicall } = createMulticall(provider)
const allowances = await multicall(erc20ABI, calls)
return fromPairs(nonBnbPools.map((pool, index) => [pool.sousId, new BigNumber(allowances[index]).toJSON()]))
}

export const fetchUserBalances = async ({ account, chainId, provider }: FetchUserDataParams) => {
const nonBnbPools = getNonBnbPools(chainId)
const bnbPools = getBnbPools(chainId)
// Non BNB pools
const tokens = uniq(nonBnbPools.map((pool) => pool.stakingToken.address))
const tokenBalanceCalls = tokens.map((token) => ({
abi: erc20ABI,
address: token,
name: 'balanceOf',
params: [account],
}))
const bnbBalanceCall = {
abi: multiCallAbi,
address: multicallAddresses[chainId],
name: 'getEthBalance',
params: [account],
}
const { multicallv3 } = createMulticall(provider)
const tokenBnbBalancesRaw = await multicallv3({ calls: [...tokenBalanceCalls, bnbBalanceCall] })
const bnbBalance = tokenBnbBalancesRaw.pop()
const tokenBalances = fromPairs(tokens.map((token, index) => [token, tokenBnbBalancesRaw[index]]))

const poolTokenBalances = fromPairs(
nonBnbPools
.map<[number, string] | null>((pool) => {
if (!tokenBalances[pool.stakingToken.address]) return null
return [pool.sousId, new BigNumber(tokenBalances[pool.stakingToken.address]).toJSON()]
})
.filter((p): p is [number, string] => Boolean(p)),
)

// BNB pools
const bnbBalanceJson = new BigNumber(bnbBalance.toString()).toJSON()
const bnbBalances = fromPairs(bnbPools.map((pool) => [pool.sousId, bnbBalanceJson]))

return { ...poolTokenBalances, ...bnbBalances }
}

export const fetchUserStakeBalances = async ({ account, chainId, provider }: FetchUserDataParams) => {
const nonMasterPools = getNonMasterPools(chainId)
const calls = nonMasterPools.map(({ contractAddress }) => ({
address: contractAddress[chainId],
name: 'userInfo',
params: [account],
}))
const { multicall } = createMulticall(provider)
const userInfo = await multicall(sousChefABI, calls)
return fromPairs(
nonMasterPools.map((pool, index) => [pool.sousId, new BigNumber(userInfo[index].amount._hex).toJSON()]),
)
}

export const fetchUserPendingRewards = async ({ account, chainId, provider }: FetchUserDataParams) => {
const nonMasterPools = getNonMasterPools(chainId)
const calls = nonMasterPools.map(({ contractAddress }) => ({
address: contractAddress[chainId],
name: 'pendingReward',
params: [account],
}))
const { multicall } = createMulticall(provider)
const res = await multicall(sousChefABI, calls)
return fromPairs(nonMasterPools.map((pool, index) => [pool.sousId, new BigNumber(res[index]).toJSON()]))
}
1 change: 1 addition & 0 deletions packages/pools/src/queries/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './fetchPools'
export * from './fetchUserPoolsData'

0 comments on commit b351cf0

Please sign in to comment.