diff --git a/.gitignore b/.gitignore index 9be5be9d0f21b..4efde88109a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ debug.log* **/apps/web/config/abi/types **/packages/smart-router/evm/abis/types +**/packages/pools/src/abis/types # Sentry .sentryclirc @@ -49,4 +50,4 @@ debug.log* .vercel # store for local dev scripts -scripts/dev/** \ No newline at end of file +scripts/dev/** diff --git a/apps/web/src/hooks/useCakeEnable.tsx b/apps/web/src/hooks/useCakeEnable.tsx index ee9b6a72c2df9..28ba6d8bceeaa 100644 --- a/apps/web/src/hooks/useCakeEnable.tsx +++ b/apps/web/src/hooks/useCakeEnable.tsx @@ -31,10 +31,10 @@ export const useCakeEnable = (enableAmount: BigNumber) => { useEffect(() => { if (pendingEnableTx && transactionHash && !isTransactionPending) { - dispatch(updateUserBalance({ sousId: 0, account })) + dispatch(updateUserBalance({ sousId: 0, account, chainId })) setPendingEnableTx(isTransactionPending) } - }, [account, dispatch, transactionHash, pendingEnableTx, isTransactionPending]) + }, [account, dispatch, transactionHash, pendingEnableTx, isTransactionPending, chainId]) const handleEnable = useCallback(() => { if (!swapCallback) { diff --git a/apps/web/src/state/pools/fetchUserIfo.ts b/apps/web/src/state/pools/fetchUserIfo.ts deleted file mode 100644 index 2d3bdebfd0861..0000000000000 --- a/apps/web/src/state/pools/fetchUserIfo.ts +++ /dev/null @@ -1,27 +0,0 @@ -import BigNumber from 'bignumber.js' -import { BIG_ZERO } from '@pancakeswap/utils/bigNumber' -import { getIfoCreditAddressContract } from 'utils/contractHelpers' - -export const fetchPublicIfoData = async () => { - try { - const ifoCreditAddressContract = getIfoCreditAddressContract() - const ceiling = await ifoCreditAddressContract.ceiling() - return { - ceiling: new BigNumber(ceiling.toString()).toJSON(), - } - } catch (error) { - return { - ceiling: BIG_ZERO.toJSON(), - } - } -} - -export const fetchUserIfoCredit = async (account: string) => { - try { - const ifoCreditAddressContract = getIfoCreditAddressContract() - const credit = await ifoCreditAddressContract.getUserCredit(account) - return new BigNumber(credit.toString()).toJSON() - } catch (error) { - return BIG_ZERO.toJSON() - } -} diff --git a/apps/web/src/state/pools/hooks.ts b/apps/web/src/state/pools/hooks.ts index 52e49e23e9fd9..0124066743968 100644 --- a/apps/web/src/state/pools/hooks.ts +++ b/apps/web/src/state/pools/hooks.ts @@ -120,7 +120,9 @@ export const usePoolsPageFetch = () => { batch(() => { dispatch(fetchCakeVaultPublicData()) dispatch(fetchCakeFlexibleSideVaultPublicData()) - dispatch(fetchIfoPublicDataAsync()) + if (chainId) { + dispatch(fetchIfoPublicDataAsync(chainId)) + } if (account && chainId) { dispatch(fetchPoolsUserDataAsync({ account, chainId })) dispatch(fetchCakeVaultUserData({ account })) @@ -160,14 +162,14 @@ export const useFetchIfo = () => { const dispatch = useAppDispatch() useSWRImmutable( - 'fetchIfoPublicData', + chainId && ['fetchIfoPublicData', chainId], async () => { const cakePriceFarms = await getCakePriceFarms(chainId) await dispatch(fetchFarmsPublicDataAsync({ pids: cakePriceFarms, chainId })) batch(() => { dispatch(fetchCakePoolPublicDataAsync()) dispatch(fetchCakeVaultPublicData()) - dispatch(fetchIfoPublicDataAsync()) + dispatch(fetchIfoPublicDataAsync(chainId)) }) }, { @@ -176,12 +178,12 @@ export const useFetchIfo = () => { ) useSWRImmutable( - account && ['fetchIfoUserData', account], + account && chainId && ['fetchIfoUserData', account, chainId], async () => { batch(() => { dispatch(fetchCakePoolUserDataAsync(account)) dispatch(fetchCakeVaultUserData({ account })) - dispatch(fetchUserIfoCreditDataAsync(account)) + dispatch(fetchUserIfoCreditDataAsync({ account, chainId })) }) }, { diff --git a/apps/web/src/state/pools/index.ts b/apps/web/src/state/pools/index.ts index eaeabb32bb26f..eb4eaafce98b8 100644 --- a/apps/web/src/state/pools/index.ts +++ b/apps/web/src/state/pools/index.ts @@ -14,6 +14,8 @@ import { fetchUserBalances, fetchUserPendingRewards, fetchUserStakeBalances, + fetchPublicIfoData, + fetchUserIfoCredit, } from '@pancakeswap/pools' import { ChainId } from '@pancakeswap/sdk' @@ -41,7 +43,6 @@ import getFarmsPrices from '../farms/getFarmsPrices' import { fetchPublicVaultData, fetchVaultFees, fetchPublicFlexibleSideVaultData } from './fetchVaultPublic' import { getTokenPricesFromFarm } from './helpers' import { resetUserState } from '../global/actions' -import { fetchUserIfoCredit, fetchPublicIfoData } from './fetchUserIfo' import { fetchVaultUser, fetchFlexibleSideVaultUser } from './fetchVaultUser' export const initialPoolVaultState = Object.freeze({ @@ -288,25 +289,25 @@ export const updateUserAllowance = createAsyncThunk< export const updateUserBalance = createAsyncThunk< { sousId: number; field: string; value: any }, - { sousId: number; account: string } ->('pool/updateUserBalance', async ({ sousId, account }) => { - const tokenBalances = await fetchUserBalances(account) + { sousId: number; account: string; chainId: ChainId } +>('pool/updateUserBalance', async ({ sousId, account, chainId }) => { + const tokenBalances = await fetchUserBalances({ account, chainId, provider }) return { sousId, field: 'stakingTokenBalance', value: tokenBalances[sousId] } }) export const updateUserStakedBalance = createAsyncThunk< { sousId: number; field: string; value: any }, - { sousId: number; account: string } ->('pool/updateUserStakedBalance', async ({ sousId, account }) => { - const stakedBalances = await fetchUserStakeBalances(account) + { sousId: number; account: string; chainId: ChainId } +>('pool/updateUserStakedBalance', async ({ sousId, account, chainId }) => { + const stakedBalances = await fetchUserStakeBalances({ account, chainId, provider }) return { sousId, field: 'stakedBalance', value: stakedBalances[sousId] } }) export const updateUserPendingReward = createAsyncThunk< { sousId: number; field: string; value: any }, - { sousId: number; account: string } ->('pool/updateUserPendingReward', async ({ sousId, account }) => { - const pendingRewards = await fetchUserPendingRewards(account) + { sousId: number; account: string; chainId: ChainId } +>('pool/updateUserPendingReward', async ({ sousId, account, chainId }) => { + const pendingRewards = await fetchUserPendingRewards({ chainId, account, provider }) return { sousId, field: 'pendingReward', value: pendingRewards[sousId] } }) @@ -347,19 +348,24 @@ export const fetchCakeVaultUserData = createAsyncThunk('ifoVault/fetchIfoPublicDataAsync', async () => { - const publicIfoData = await fetchPublicIfoData() - return publicIfoData -}) +export const fetchIfoPublicDataAsync = createAsyncThunk( + 'ifoVault/fetchIfoPublicDataAsync', + async (chainId) => { + const publicIfoData = await fetchPublicIfoData(chainId, provider) + return publicIfoData + }, +) -export const fetchUserIfoCreditDataAsync = (account: string) => async (dispatch) => { - try { - const credit = await fetchUserIfoCredit(account) - dispatch(setIfoUserCreditData(credit)) - } catch (error) { - console.error('[Ifo Credit Action] Error fetching user Ifo credit data', error) +export const fetchUserIfoCreditDataAsync = + ({ account, chainId }: { account: string; chainId: ChainId }) => + async (dispatch) => { + try { + const credit = await fetchUserIfoCredit({ account, chainId, provider }) + dispatch(setIfoUserCreditData(credit)) + } catch (error) { + console.error('[Ifo Credit Action] Error fetching user Ifo credit data', error) + } } -} export const fetchCakeFlexibleSideVaultUserData = createAsyncThunk( 'cakeFlexibleSideVault/fetchUser', async ({ account }) => { diff --git a/apps/web/src/views/Pools/components/Modals/CollectModal.tsx b/apps/web/src/views/Pools/components/Modals/CollectModal.tsx index bee199da34fa9..db24bc561f627 100644 --- a/apps/web/src/views/Pools/components/Modals/CollectModal.tsx +++ b/apps/web/src/views/Pools/components/Modals/CollectModal.tsx @@ -6,6 +6,8 @@ import { ToastDescriptionWithTx } from 'components/Toast' import useCatchTxError from 'hooks/useCatchTxError' import { useAppDispatch } from 'state' import { updateUserBalance, updateUserPendingReward, updateUserStakedBalance } from 'state/pools' +import { useActiveChainId } from 'hooks/useActiveChainId' + import useHarvestPool from '../../hooks/useHarvestPool' export const CollectModalContainer = ({ @@ -16,6 +18,7 @@ export const CollectModalContainer = ({ ...rest }: React.PropsWithChildren) => { const { t } = useTranslation() + const { chainId } = useActiveChainId() const { toastSuccess } = useToast() const { address: account } = useAccount() const dispatch = useAppDispatch() @@ -33,12 +36,23 @@ export const CollectModalContainer = ({ {t('Your %symbol% earnings have been sent to your wallet!', { symbol: earningTokenSymbol })} , ) - dispatch(updateUserStakedBalance({ sousId, account })) - dispatch(updateUserPendingReward({ sousId, account })) - dispatch(updateUserBalance({ sousId, account })) + dispatch(updateUserStakedBalance({ sousId, account, chainId })) + dispatch(updateUserPendingReward({ sousId, account, chainId })) + dispatch(updateUserBalance({ sousId, account, chainId })) onDismiss?.() } - }, [account, dispatch, earningTokenSymbol, fetchWithCatchTxError, onDismiss, onReward, sousId, t, toastSuccess]) + }, [ + account, + dispatch, + earningTokenSymbol, + fetchWithCatchTxError, + onDismiss, + onReward, + sousId, + t, + toastSuccess, + chainId, + ]) return ( { - dispatch(updateUserStakedBalance({ sousId, account })) - dispatch(updateUserPendingReward({ sousId, account })) - dispatch(updateUserBalance({ sousId, account })) - }, [dispatch, sousId, account]) + dispatch(updateUserStakedBalance({ sousId, account, chainId })) + dispatch(updateUserPendingReward({ sousId, account, chainId })) + dispatch(updateUserBalance({ sousId, account, chainId })) + }, [dispatch, sousId, account, chainId]) const handleConfirmClick = useCallback( async (stakeAmount: string) => { diff --git a/packages/pools/package.json b/packages/pools/package.json index 68814e361199c..5ebdb92848071 100644 --- a/packages/pools/package.json +++ b/packages/pools/package.json @@ -4,8 +4,10 @@ "sideEffects": false, "private": true, "scripts": { - "build": "tsup", - "test": "vitest --run" + "build": "yarn typechain && tsup", + "test": "vitest --run", + "typechain": "typechain --out-dir src/abis/types --target=ethers-v5 \"src/abis/*.json\"", + "postinstall": "yarn typechain" }, "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -38,8 +40,10 @@ "@pancakeswap/token-lists": "*", "@pancakeswap/tokens": "*", "@pancakeswap/utils": "*", + "@typechain/ethers-v5": "^8.0.5", "tsconfig": "*", "tsup": "^6.6.3", + "typechain": "^6.1.0", "typescript": "^4.9.4", "vitest": "^0.27.2" } diff --git a/packages/pools/src/abis/ICake.json b/packages/pools/src/abis/ICake.json new file mode 100644 index 0000000000000..1aba7d7c2e9be --- /dev/null +++ b/packages/pools/src/abis/ICake.json @@ -0,0 +1,83 @@ +[ + { + "inputs": [ + { "internalType": "contract ICaKePool", "name": "_cakePool", "type": "address" }, + { "internalType": "address", "name": "_admin", "type": "address" }, + { "internalType": "uint256", "name": "_ceiling", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" }, + { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint256", "name": "newCeiling", "type": "uint256" }], + "name": "UpdateCeiling", + "type": "event" + }, + { + "inputs": [], + "name": "MIN_CEILING_DURATION", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cakePool", + "outputs": [{ "internalType": "contract ICaKePool", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ceiling", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "_user", "type": "address" }], + "name": "getUserCredit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "_newCeiling", "type": "uint256" }], + "name": "updateCeiling", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/pools/src/constants/contracts.ts b/packages/pools/src/constants/contracts.ts new file mode 100644 index 0000000000000..d6d935fbd94f1 --- /dev/null +++ b/packages/pools/src/constants/contracts.ts @@ -0,0 +1,11 @@ +import { ChainId } from '@pancakeswap/sdk' + +import { PoolsSupportedChainId } from './pools' + +export type ContractAddresses = { + [chainId in T]: string +} + +export const ICAKE = { + [ChainId.BSC]: '0x3C458828D1622F5f4d526eb0d24Da8C4Eb8F07b1', +} satisfies ContractAddresses diff --git a/packages/pools/src/queries/fetchUserIfo.ts b/packages/pools/src/queries/fetchUserIfo.ts new file mode 100644 index 0000000000000..1c21ba10015cb --- /dev/null +++ b/packages/pools/src/queries/fetchUserIfo.ts @@ -0,0 +1,49 @@ +import BigNumber from 'bignumber.js' +import { BIG_ZERO } from '@pancakeswap/utils/bigNumber' +import { Contract } from '@ethersproject/contracts' +import { ChainId } from '@pancakeswap/sdk' + +import cakeAbi from '../abis/ICake.json' +import { ICAKE } from '../constants/contracts' +import { PoolsSupportedChainId } from '../constants' +import { OnChainProvider } from '../types' + +const getIfoCreditAddressContract = (chainId: ChainId, provider: OnChainProvider) => { + const address = ICAKE[chainId as PoolsSupportedChainId] + if (!address) { + throw new Error(`Unsupported chain ${chainId}`) + } + return new Contract(address, cakeAbi, provider({ chainId })) +} + +export const fetchPublicIfoData = async (chainId: ChainId, provider: OnChainProvider) => { + try { + const ifoCreditAddressContract = getIfoCreditAddressContract(chainId, provider) + const ceiling = await ifoCreditAddressContract.ceiling() + return { + ceiling: new BigNumber(ceiling.toString()).toJSON(), + } + } catch (error) { + console.error(error) + return { + ceiling: BIG_ZERO.toJSON(), + } + } +} + +interface Params { + account: string + chainId: ChainId + provider: OnChainProvider +} + +export const fetchUserIfoCredit = async ({ account, chainId, provider }: Params) => { + try { + const ifoCreditAddressContract = getIfoCreditAddressContract(chainId, provider) + const credit = await ifoCreditAddressContract.getUserCredit(account) + return new BigNumber(credit.toString()).toJSON() + } catch (error) { + console.error(error) + return BIG_ZERO.toJSON() + } +} diff --git a/packages/pools/src/queries/index.ts b/packages/pools/src/queries/index.ts index fb167586bee70..660bf697c0b5d 100644 --- a/packages/pools/src/queries/index.ts +++ b/packages/pools/src/queries/index.ts @@ -1,2 +1,3 @@ export * from './fetchPools' export * from './fetchUserPoolsData' +export * from './fetchUserIfo'