diff --git a/apps/web/package.json b/apps/web/package.json
index 91d459399457a..5c2497ccdddaa 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -31,9 +31,6 @@
"not op_mini all"
]
},
- "engines": {
- "node": ">=16.0.0"
- },
"dependencies": {
"@binance/w3w-utils": "1.1.4",
"@binance/w3w-wagmi-connector-v2": "^1.2.3",
diff --git a/apps/web/src/components/AdPanel/Ads/AdSolv.tsx b/apps/web/src/components/AdPanel/Ads/AdSolv.tsx
new file mode 100644
index 0000000000000..bb263b2a14a64
--- /dev/null
+++ b/apps/web/src/components/AdPanel/Ads/AdSolv.tsx
@@ -0,0 +1,33 @@
+import { useTranslation } from '@pancakeswap/localization'
+import { BodyText } from '../BodyText'
+import { AdButton } from '../Button'
+import { AdCard } from '../Card'
+import { Countdown } from '../Countdown'
+import { AdPlayerProps } from '../types'
+import { getImageUrl } from '../utils'
+
+export const AdSolv = (props: AdPlayerProps) => {
+ const { t } = useTranslation()
+
+ return (
+
+
+ {t('%token% IFO starts in', {
+ token: 'SOLV',
+ })}
+
+
+
+
+
+ {t('Get Started')}
+
+
+ )
+}
diff --git a/apps/web/src/components/AdPanel/config.tsx b/apps/web/src/components/AdPanel/config.tsx
index f182c753c39ba..6b2b03a5674ee 100644
--- a/apps/web/src/components/AdPanel/config.tsx
+++ b/apps/web/src/components/AdPanel/config.tsx
@@ -1,11 +1,10 @@
import { useMatchBreakpoints } from '@pancakeswap/uikit'
import { useMemo } from 'react'
import { AdCakeStaking } from './Ads/AdCakeStaking'
-import { AdOptionsTrading } from './Ads/AdOptionsTrading'
import { AdPCSX } from './Ads/AdPCSX'
-import { AdRocker } from './Ads/AdRocker'
+import { AdSolv } from './Ads/AdSolv'
import { AdSpringboard } from './Ads/AdSpringboard'
-import { AdTradingCompetitionBfg, AdTradingCompetitionVinu } from './Ads/AdTradingCompetition'
+import { AdTradingCompetitionVinu } from './Ads/AdTradingCompetition'
import { ExpandableAd } from './Expandable/ExpandableAd'
import { shouldRenderOnPages } from './renderConditions'
@@ -45,18 +44,9 @@ export const useAdConfig = () => {
component: ,
},
{
- id: 'ad-bfg-tc',
- component: ,
+ id: 'ad-ifo-solv',
+ component: ,
},
-
- // {
- // id: 'ad-mev',
- // component: ,
- // },
- // {
- // id: 'prediction-telegram-bot',
- // component: ,
- // },
{
id: 'pcsx',
component: ,
@@ -65,15 +55,6 @@ export const useAdConfig = () => {
id: 'cake-staking',
component: ,
},
- {
- id: 'clamm-options-trading',
- component: ,
- },
-
- {
- id: 'rocker-meme-career',
- component: ,
- },
],
[shouldRenderOnPage],
)
diff --git a/apps/web/src/components/Menu/config/config.ts b/apps/web/src/components/Menu/config/config.ts
index c4dbd23b27634..ec492386f9603 100644
--- a/apps/web/src/components/Menu/config/config.ts
+++ b/apps/web/src/components/Menu/config/config.ts
@@ -164,7 +164,6 @@ const config: (
],
items: [
{
- status: { text: t('New'), color: 'success' },
label: t('Springboard'),
href: 'https://springboard.pancakeswap.finance',
type: DropdownMenuItemType.EXTERNAL_LINK,
@@ -201,6 +200,7 @@ const config: (
label: t('IFO'),
href: '/ifo',
image: '/images/ifos/ifo-bunny.png',
+ status: { text: t('SOON'), color: 'warning' },
overrideSubNavItems: [
{
label: t('Latest'),
diff --git a/apps/web/src/components/PhishingWarningBanner/SolvStrip.tsx b/apps/web/src/components/PhishingWarningBanner/SolvStrip.tsx
new file mode 100644
index 0000000000000..3bf226101e3a6
--- /dev/null
+++ b/apps/web/src/components/PhishingWarningBanner/SolvStrip.tsx
@@ -0,0 +1,62 @@
+import { useTranslation } from '@pancakeswap/localization'
+import { Box, Link, Text } from '@pancakeswap/uikit'
+import { VerticalDivider } from '@pancakeswap/widgets-internal'
+
+const TextHighlight = ({ text, highlights }: { text: string; highlights: string[] }) => {
+ const prts = text.split(new RegExp(`(${highlights.join('|')})`, 'g'))
+ return prts.map((prt, i) => {
+ const key = `${prt}-${i}`
+ if (highlights.includes(prt)) {
+ return (
+
+ {prt}
+
+ )
+ }
+ return (
+
+ {prt}
+
+ )
+ })
+}
+export const SolvStrip = () => {
+ const { t } = useTranslation()
+
+ return (
+
+ {' '}
+
+ {t('Join Now')}
+
+
+
+ {t('Learn More')}
+
+
+ )
+}
diff --git a/apps/web/src/components/PhishingWarningBanner/index.tsx b/apps/web/src/components/PhishingWarningBanner/index.tsx
index 052990fb7cc26..092e084a0c814 100644
--- a/apps/web/src/components/PhishingWarningBanner/index.tsx
+++ b/apps/web/src/components/PhishingWarningBanner/index.tsx
@@ -8,10 +8,11 @@ import 'swiper/css/effect-fade'
import { ASSET_CDN } from 'config/constants/endpoints'
import { Countdown } from './Countdown'
+import { SolvStrip } from './SolvStrip'
import { Step1 } from './Step1'
import { Step2 } from './Step2'
import { Step3 } from './Step3'
-import { TradingCompetitionBfg, TradingCompetitionVinu } from './TradingCompetition'
+import { TradingCompetitionVinu } from './TradingCompetition'
const Container = styled(Flex).withConfig({ shouldForwardProp: (prop) => !['$background'].includes(prop) })<{
$background?: string
@@ -99,6 +100,12 @@ type BannerConfig = {
}
const CONFIG: BannerConfig[] = [
+ {
+ component: SolvStrip,
+ stripeImage: `${ASSET_CDN}/web/phishing-warning/solv.png?v=1`,
+ stripeImageWidth: '92px',
+ stripeImageAlt: 'SOLV IFO',
+ },
{
component: Step1,
stripeImage: `${ASSET_CDN}/web/phishing-warning/phishing-warning-bunny-1.png`,
@@ -117,12 +124,6 @@ const CONFIG: BannerConfig[] = [
stripeImageWidth: '92px',
stripeImageAlt: 'PCSX',
},
- {
- component: TradingCompetitionBfg,
- stripeImage: `${ASSET_CDN}/web/promotions/bfg_competition.png`,
- stripeImageWidth: '92px',
- stripeImageAlt: 'bfg_competition',
- },
{
component: TradingCompetitionVinu,
stripeImage: `${ASSET_CDN}/web/promotions/vinu_competition.png`,
diff --git a/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardDetails.tsx b/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardDetails.tsx
index d51e1b0eac374..6fae3ddb0f81a 100644
--- a/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardDetails.tsx
+++ b/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardDetails.tsx
@@ -4,7 +4,6 @@ import { CAKE } from '@pancakeswap/tokens'
import { Box, Flex, IfoSkeletonCardDetails, Skeleton, Text, TooltipText, useTooltip } from '@pancakeswap/uikit'
import { BIG_ONE_HUNDRED } from '@pancakeswap/utils/bigNumber'
import { formatNumber, getBalanceNumber } from '@pancakeswap/utils/formatBalance'
-import { DAY_IN_SECONDS } from '@pancakeswap/utils/getTimePeriods'
import BigNumber from 'bignumber.js'
import { useStablecoinPrice } from 'hooks/useStablecoinPrice'
import { ReactNode, useMemo } from 'react'
@@ -141,6 +140,23 @@ const MaxTokenEntry = ({
)
}
+const useTimeUntilDiffTime = (diffTime: number): string => {
+ const { t } = useTranslation()
+ if (diffTime <= 0) {
+ return t('The time has already passed.')
+ }
+
+ const diffInHours = Math.floor(diffTime / (60 * 60))
+ const diffInDays = Math.floor(diffInHours / 24)
+ const unitDay = t('day(s)')
+ const unitHour = t('hour(s)')
+
+ if (diffInDays >= 1) {
+ return `${diffInDays} ${unitDay}`
+ }
+ return `${diffInHours} ${unitHour}`
+}
+
const IfoCardDetails: React.FC> = ({
isEligible,
poolId,
@@ -220,7 +236,8 @@ const IfoCardDetails: React.FC> = (
)
const durationInSeconds = ifo.version >= 3.2 ? poolCharacteristic?.vestingInformation?.duration ?? 0 : 0
- const vestingDays = Math.ceil(durationInSeconds / DAY_IN_SECONDS)
+ // const vestingDays = Math.ceil(durationInSeconds / DAY_IN_SECONDS)
+ const vestingCountdown = useTimeUntilDiffTime(durationInSeconds)
/* Format end */
const renderBasedOnIfoStatus = () => {
@@ -276,9 +293,9 @@ const IfoCardDetails: React.FC> = (
/>
>
diff --git a/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardTokens.tsx b/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardTokens.tsx
index 1abaeb7e3dd7c..cfa088ab1c6a5 100644
--- a/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardTokens.tsx
+++ b/apps/web/src/views/Ifos/components/IfoFoldableCard/IfoPoolCard/IfoCardTokens.tsx
@@ -1,6 +1,6 @@
import { Ifo, PoolIds, cakeBnbLpToken } from '@pancakeswap/ifos'
import { useTranslation } from '@pancakeswap/localization'
-import { Token } from '@pancakeswap/sdk'
+import { ChainId, Token } from '@pancakeswap/sdk'
import { bscTokens } from '@pancakeswap/tokens'
import {
AutoRenewIcon,
@@ -229,9 +229,9 @@ const IfoCardTokens: React.FC> = ({
hasProfile
) {
// If Cross-Chain IFO
- // if (ifo.chainId !== ChainId.BSC) {
- message =
- // }
+ if (ifo.chainId !== ChainId.BSC) {
+ message =
+ }
// Phase this out later, as it applies at the same time
// else message =
}
diff --git a/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Claim.tsx b/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Claim.tsx
index ff3d0b3e7a047..cc5363292a0db 100644
--- a/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Claim.tsx
+++ b/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Claim.tsx
@@ -17,6 +17,7 @@ interface Props {
claimableAmount: string
isVestingInitialized: boolean
fetchUserVestingData: () => void
+ enabled: boolean
}
const ClaimButton: React.FC> = ({
@@ -25,6 +26,7 @@ const ClaimButton: React.FC> = ({
claimableAmount,
isVestingInitialized,
fetchUserVestingData,
+ enabled,
}) => {
const { account, chain } = useWeb3React()
const { t } = useTranslation()
@@ -85,10 +87,10 @@ const ClaimButton: React.FC> = ({
width="100%"
onClick={handleClaim}
isLoading={isPending}
- disabled={isReady}
+ disabled={isReady && !enabled}
endIcon={isPending ? : null}
>
- {t('Claim %symbol%', { symbol: token.symbol })}
+ {t('Claim')}
)
}
diff --git a/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Info.tsx b/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Info.tsx
index 4341072af6b96..f88faf915ab4f 100644
--- a/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Info.tsx
+++ b/apps/web/src/views/Ifos/components/IfoVesting/VestingPeriod/Info.tsx
@@ -1,18 +1,16 @@
-import { useMemo } from 'react'
-import { styled } from 'styled-components'
-import { useTranslation } from '@pancakeswap/localization'
-import { Flex, Text, Progress, Tag } from '@pancakeswap/uikit'
-import { VestingData } from 'views/Ifos/hooks/vesting/fetchUserWalletIfoData'
import { PoolIds } from '@pancakeswap/ifos'
-import { getFullDisplayBalance } from '@pancakeswap/utils/formatBalance'
+import { useTranslation } from '@pancakeswap/localization'
+import { Flex, Progress, Tag, Text } from '@pancakeswap/uikit'
+import dayjs from 'dayjs'
import { useCurrentBlock } from 'state/block/hooks'
+import { styled } from 'styled-components'
import useGetPublicIfoV3Data from 'views/Ifos/hooks/v3/useGetPublicIfoData'
-import BigNumber from 'bignumber.js'
-import dayjs from 'dayjs'
+import { VestingData } from 'views/Ifos/hooks/vesting/fetchUserWalletIfoData'
import { useQuery } from '@tanstack/react-query'
-import Claim from './Claim'
+import { getVestingInfo } from 'views/Ifos/hooks/getVestingInfo'
import { isBasicSale } from '../../../hooks/v7/helpers'
+import Claim from './Claim'
const WhiteCard = styled.div`
background: ${({ theme }) => theme.colors.backgroundAlt};
@@ -41,16 +39,13 @@ const Info: React.FC> = ({
ifoBasicSaleType,
}) => {
const { t } = useTranslation()
- const { token } = data.ifo
const { vestingStartTime } = data.userVestingData
- const {
- isVestingInitialized,
- vestingComputeReleasableAmount,
- offeringAmountInToken,
- vestingInformationPercentage,
- vestingReleased,
- vestingInformationDuration,
- } = data.userVestingData[poolId]
+ const { vestingInformationDuration } = data.userVestingData[poolId]
+ const { isVestingInitialized, isVestingOver, received, claimable, remaining, percentage } = getVestingInfo(
+ poolId,
+ data,
+ )
+
const labelText =
poolId === PoolIds.poolUnlimited
? t('Public Sale')
@@ -74,46 +69,6 @@ const Info: React.FC> = ({
const currentTimeStamp = Date.now()
const timeCliff = vestingStartTime === 0 ? currentTimeStamp : (vestingStartTime + (cliff ?? 0)) * 1000
const timeVestingEnd = (vestingStartTime + vestingInformationDuration) * 1000
- const isVestingOver = currentTimeStamp > timeVestingEnd
-
- const vestingPercentage = useMemo(
- () => new BigNumber(vestingInformationPercentage).times(0.01),
- [vestingInformationPercentage],
- )
-
- const releasedAtSaleEnd = useMemo(() => {
- return new BigNumber(offeringAmountInToken).times(new BigNumber(1).minus(vestingPercentage))
- }, [offeringAmountInToken, vestingPercentage])
-
- const amountReleased = useMemo(() => {
- return new BigNumber(releasedAtSaleEnd).plus(vestingReleased).plus(vestingComputeReleasableAmount)
- }, [releasedAtSaleEnd, vestingReleased, vestingComputeReleasableAmount])
-
- const received = useMemo(() => {
- const alreadyClaimed = new BigNumber(releasedAtSaleEnd).plus(vestingReleased)
- return alreadyClaimed.gt(0) ? getFullDisplayBalance(alreadyClaimed, token.decimals, 4) : '0'
- }, [token, releasedAtSaleEnd, vestingReleased])
-
- const claimable = useMemo(() => {
- const remain = new BigNumber(offeringAmountInToken).minus(amountReleased)
- const claimableAmount = isVestingOver ? vestingComputeReleasableAmount.plus(remain) : vestingComputeReleasableAmount
- return claimableAmount.gt(0) ? getFullDisplayBalance(claimableAmount, token.decimals, 4) : '0'
- }, [offeringAmountInToken, amountReleased, isVestingOver, vestingComputeReleasableAmount, token.decimals])
-
- const remaining = useMemo(() => {
- const remain = new BigNumber(offeringAmountInToken).minus(amountReleased)
- return remain.gt(0) ? getFullDisplayBalance(remain, token.decimals, 4) : '0'
- }, [token, offeringAmountInToken, amountReleased])
-
- const percentage = useMemo(() => {
- const total = new BigNumber(received).plus(claimable).plus(remaining)
- const receivedPercentage = new BigNumber(received).div(total).times(100).toNumber()
- const amountAvailablePercentage = new BigNumber(claimable).div(total).times(100).toNumber()
- return {
- receivedPercentage,
- amountAvailablePercentage: receivedPercentage + amountAvailablePercentage,
- }
- }, [received, claimable, remaining])
if (claimable === '0' && remaining === '0') {
return null
@@ -170,6 +125,7 @@ const Info: React.FC> = ({
> = ({ ifoBas
const { data, fetchUserVestingData } = useFetchVestingData()
useEffect(() => {
- // When switch account need init
if (account) {
setIsFirstTime(true)
fetchUserVestingData()
}
}, [account, fetchUserVestingData, setIsFirstTime])
+ const hasClaimable = getHasClaimable([PoolIds.poolBasic, PoolIds.poolUnlimited], data)
+
const cardStatus = useMemo(() => {
if (account) {
- if (data.length > 0) return IfoVestingStatus[VestingStatus.HAS_TOKENS_CLAIM]
- if (data.length === 0 && !isFirstTime) return IfoVestingStatus[VestingStatus.ENDED]
+ if (hasClaimable) {
+ return IfoVestingStatus[VestingStatus.HAS_TOKENS_CLAIM]
+ }
+ if (!hasClaimable && !isFirstTime) return IfoVestingStatus[VestingStatus.ENDED]
}
return IfoVestingStatus[VestingStatus.NOT_TOKENS_CLAIM]
- }, [data, account, isFirstTime])
+ }, [data, account, isFirstTime, hasClaimable])
const handleFetchUserVesting = useCallback(() => {
setIsFirstTime(false)
@@ -122,15 +127,15 @@ const IfoVesting: React.FC> = ({ ifoBas
)}
{cardStatus.status === VestingStatus.HAS_TOKENS_CLAIM && (
- {data.map((ifo, index) => (
+ {data && (
- ))}
+ )}
)}
{cardStatus.status === VestingStatus.ENDED && }
diff --git a/apps/web/src/views/Ifos/components/VeCakeCard.tsx b/apps/web/src/views/Ifos/components/VeCakeCard.tsx
index ebdfbb3dde92f..8f082af48cee9 100644
--- a/apps/web/src/views/Ifos/components/VeCakeCard.tsx
+++ b/apps/web/src/views/Ifos/components/VeCakeCard.tsx
@@ -1,24 +1,24 @@
-import { Ifo } from '@pancakeswap/widgets-internal'
import { ChainId } from '@pancakeswap/chains'
-import { Button } from '@pancakeswap/uikit'
import { useTranslation } from '@pancakeswap/localization'
+import { CAKE } from '@pancakeswap/tokens'
+import { Button } from '@pancakeswap/uikit'
+import { formatBigInt } from '@pancakeswap/utils/formatBalance'
+import { Ifo } from '@pancakeswap/widgets-internal'
+import BigNumber from 'bignumber.js'
import Link from 'next/link'
+import { useMemo } from 'react'
import { SpaceProps } from 'styled-system'
-import { useAccount } from 'wagmi'
import { Address } from 'viem'
-import { useMemo } from 'react'
-import BigNumber from 'bignumber.js'
-import { CAKE } from '@pancakeswap/tokens'
-import { formatBigInt } from '@pancakeswap/utils/formatBalance'
+import { useAccount } from 'wagmi'
-import { useCakePrice } from 'hooks/useCakePrice'
-import { useActiveChainId } from 'hooks/useActiveChainId'
import ConnectWalletButton from 'components/ConnectWalletButton'
+import { useActiveChainId } from 'hooks/useActiveChainId'
+import { useCakePrice } from 'hooks/useCakePrice'
// TODO should be common hooks
-import { useCakeLockStatus } from 'views/CakeStaking/hooks/useVeCakeUserInfo'
import { useIsMigratedToVeCake } from 'views/CakeStaking/hooks/useIsMigratedToVeCake'
import { useIsUserDelegated } from 'views/CakeStaking/hooks/useIsUserDelegated'
+import { useCakeLockStatus } from 'views/CakeStaking/hooks/useVeCakeUserInfo'
import { useUserIfoInfo } from '../hooks/useUserIfoInfo'
@@ -70,7 +70,7 @@ export function VeCakeCard({ ifoAddress }: Props) {
return nativeUnlockTime
}, [hasProxyCakeButNoNativeVeCake, nativeUnlockTime, proxyUnlockTime])
- const { snapshotTime, credit, veCake } = useUserIfoInfo({ ifoAddress, chainId })
+ const { snapshotTime, credit, veCake, ratio } = useUserIfoInfo({ ifoAddress, chainId })
const creditBN = useMemo(
() => credit && new BigNumber(credit.numerator.toString()).div(credit.decimalScale.toString()),
[credit],
@@ -88,7 +88,7 @@ export function VeCakeCard({ ifoAddress }: Props) {
return (
-
+
{isConnected && hasICake && totalLockCake ? (
diff --git a/apps/web/src/views/Ifos/hooks/getVestingInfo.ts b/apps/web/src/views/Ifos/hooks/getVestingInfo.ts
new file mode 100644
index 0000000000000..18beb8328c689
--- /dev/null
+++ b/apps/web/src/views/Ifos/hooks/getVestingInfo.ts
@@ -0,0 +1,74 @@
+import { PoolIds } from '@pancakeswap/ifos'
+import { getFullDisplayBalance } from '@pancakeswap/utils/formatBalance'
+import BigNumber from 'bignumber.js'
+import { VestingData } from './vesting/fetchUserWalletIfoData'
+
+export const getVestingInfo = (poolId: PoolIds, data: VestingData) => {
+ const { token } = data.ifo
+ const { vestingStartTime } = data.userVestingData
+ const {
+ isVestingInitialized,
+ vestingComputeReleasableAmount,
+ offeringAmountInToken,
+ vestingInformationPercentage,
+ vestingReleased,
+ vestingInformationDuration,
+ } = data.userVestingData[poolId]
+
+ const currentTimeStamp = Date.now()
+ const timeVestingEnd = (vestingStartTime + vestingInformationDuration) * 1000
+ const isVestingOver = currentTimeStamp > timeVestingEnd
+
+ const vestingPercentage = new BigNumber(vestingInformationPercentage).times(0.01)
+
+ const releasedAtSaleEnd = new BigNumber(offeringAmountInToken).times(new BigNumber(1).minus(vestingPercentage))
+
+ const amountReleased = new BigNumber(releasedAtSaleEnd).plus(vestingReleased).plus(vestingComputeReleasableAmount)
+
+ const alreadyClaimed = new BigNumber(releasedAtSaleEnd).plus(vestingReleased)
+ const received = alreadyClaimed.gt(0) ? getFullDisplayBalance(alreadyClaimed, token.decimals, 4) : '0'
+
+ const remain = new BigNumber(offeringAmountInToken).minus(amountReleased)
+ const claimableAmount = isVestingOver
+ ? new BigNumber(vestingComputeReleasableAmount).plus(remain)
+ : vestingComputeReleasableAmount
+ const claimable = claimableAmount.gt(0) ? getFullDisplayBalance(claimableAmount, token.decimals, 4) : '0'
+
+ const remaining = remain.gt(0) ? getFullDisplayBalance(remain, token.decimals, 4) : '0'
+
+ const total = new BigNumber(received).plus(claimable).plus(remaining)
+ const percentage = total.eq(0)
+ ? {
+ receivedPercentage: 0,
+ amountAvailablePercentage: 0,
+ }
+ : {
+ receivedPercentage: new BigNumber(received).div(total).times(100).toNumber(),
+ amountAvailablePercentage:
+ new BigNumber(received).div(total).times(100).toNumber() +
+ new BigNumber(claimable).div(total).times(100).toNumber(),
+ }
+
+ return {
+ isVestingInitialized,
+ isVestingOver,
+ vestingPercentage,
+ releasedAtSaleEnd,
+ amountReleased,
+ received,
+ claimable,
+ remaining,
+ percentage,
+ }
+}
+
+export const getHasClaimable = (poolIds: PoolIds[], data: VestingData | null) => {
+ if (!data) {
+ return false
+ }
+
+ return poolIds.some((poolId) => {
+ const { claimable } = getVestingInfo(poolId, data)
+ return claimable !== '0'
+ })
+}
diff --git a/apps/web/src/views/Ifos/hooks/useUserIfoInfo.ts b/apps/web/src/views/Ifos/hooks/useUserIfoInfo.ts
index 66f9e7ec218e2..7eafa92d76ae4 100644
--- a/apps/web/src/views/Ifos/hooks/useUserIfoInfo.ts
+++ b/apps/web/src/views/Ifos/hooks/useUserIfoInfo.ts
@@ -5,22 +5,24 @@ import { useQuery } from '@tanstack/react-query'
import BigNumber from 'bignumber.js'
import { useMemo } from 'react'
import { Address } from 'viem'
-import { useAccount } from 'wagmi'
import { getViemClients } from 'utils/viem'
+import { useAccount } from 'wagmi'
type ICakeRatioParams = {
chainId?: ChainId
}
export function useICakeRatio({ chainId }: ICakeRatioParams) {
+ const { address: account } = useAccount()
const { data } = useQuery({
- queryKey: [chainId, 'current-ifo-ratio'],
+ queryKey: [chainId, account, 'current-ifo-ratio'],
queryFn: () =>
getCurrentIfoRatio({
chainId,
provider: getViemClients,
+ account,
}),
enabled: Boolean(chainId),
diff --git a/apps/web/src/views/Ifos/hooks/v8/fetchIfoData.ts b/apps/web/src/views/Ifos/hooks/v8/fetchIfoData.ts
new file mode 100644
index 0000000000000..399eccf2b8f48
--- /dev/null
+++ b/apps/web/src/views/Ifos/hooks/v8/fetchIfoData.ts
@@ -0,0 +1,182 @@
+import { Ifo, ifoV8ABI } from '@pancakeswap/ifos'
+import { BIG_ZERO } from '@pancakeswap/utils/bigNumber'
+import BigNumber from 'bignumber.js'
+import { publicClient } from 'utils/wagmi'
+import { Address } from 'viem'
+
+export const fetchIfoData = async (account: Address, ifo: Ifo, version: number, chainId: number) => {
+ const { address } = ifo
+ const client = publicClient({ chainId })
+ const [userInfo, amounts] = await client.multicall({
+ contracts: [
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'viewUserInfo',
+ args: [account, [0, 1]],
+ },
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'viewUserOfferingAndRefundingAmountsForPools',
+ args: [account, [0, 1]],
+ },
+ ],
+ allowFailure: false,
+ })
+
+ let basicId: Address | null = null
+ let unlimitedId: Address | null = null
+ if (version >= 3.2) {
+ const [basicIdDataResult, unlimitedIdDataResult] = await client.multicall({
+ contracts: [
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'computeVestingScheduleIdForAddressAndPid',
+ args: [account, 0],
+ },
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'computeVestingScheduleIdForAddressAndPid',
+ args: [account, 1],
+ },
+ ],
+ })
+
+ basicId = basicIdDataResult.result ?? null
+ unlimitedId = unlimitedIdDataResult.result ?? null
+ }
+
+ basicId = basicId || '0x'
+ unlimitedId = unlimitedId || '0x'
+
+ let isQualifiedNFT: boolean = false
+ let isQualifiedPoints: boolean = false
+
+ let basicSchedule: VestingSchedule | null = null
+ let unlimitedSchedule: VestingSchedule | null = null
+
+ let basicReleasableAmount: bigint | null = null
+ let unlimitedReleasableAmount: bigint | null = null
+
+ if (version >= 3.1) {
+ const [
+ isQualifiedNFTResult,
+ isQualifiedPointsResult,
+ basicScheduleResult,
+ unlimitedScheduleResult,
+ basicReleasableAmountResult,
+ unlimitedReleasableAmountResult,
+ ] = await client.multicall({
+ contracts: [
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'isQualifiedNFT',
+ args: [account],
+ },
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'isQualifiedPoints',
+ args: [account],
+ },
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'getVestingSchedule',
+ args: [basicId],
+ },
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'getVestingSchedule',
+ args: [unlimitedId],
+ },
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'computeReleasableAmount',
+ args: [basicId],
+ },
+ {
+ address,
+ abi: ifoV8ABI,
+ functionName: 'computeReleasableAmount',
+ args: [unlimitedId],
+ },
+ ],
+ allowFailure: true,
+ })
+
+ isQualifiedNFT = isQualifiedNFTResult.result || false
+ isQualifiedPoints = isQualifiedPointsResult.result || false
+ basicSchedule = basicScheduleResult.result || null
+ unlimitedSchedule = unlimitedScheduleResult.result || null
+ basicReleasableAmount = basicReleasableAmountResult.result || null
+ unlimitedReleasableAmount = unlimitedReleasableAmountResult.result || null
+ }
+
+ return {
+ basicPoolData: preparePoolData(
+ 0,
+ userInfo,
+ amounts,
+ basicSchedule,
+ basicReleasableAmount,
+ basicId,
+ isQualifiedNFT,
+ isQualifiedPoints,
+ ),
+ unlimitedPoolData: preparePoolData(1, userInfo, amounts, unlimitedSchedule, unlimitedReleasableAmount, unlimitedId),
+ }
+}
+
+interface VestingSchedule {
+ released: bigint
+ amountTotal: bigint
+ isVestingInitialized: boolean
+}
+
+const preparePoolData = (
+ poolId: 0 | 1,
+ userInfo: any,
+ amounts: any,
+ schedule: any,
+ releasableAmount: bigint | null,
+ vestingId: Address | null,
+ isQualifiedNFT?: boolean,
+ isQualifiedPoints?: boolean,
+) => {
+ const hasClaimed = userInfo[1][poolId]
+ const [offeringRaw, refundingRaw, taxRaw] = amounts[poolId]
+ const vestingReleasedBn = schedule ? new BigNumber(schedule.released.toString()) : BIG_ZERO
+ const vestingTotalBn = schedule ? new BigNumber(schedule.amountTotal.toString()) : BIG_ZERO
+ const releasableBn = releasableAmount ? new BigNumber(releasableAmount.toString()) : BIG_ZERO
+
+ const data = {
+ amountTokenCommittedInLP: new BigNumber(userInfo[0][poolId].toString()),
+ offeringAmountInToken: new BigNumber(offeringRaw.toString()),
+ refundingAmountInLP: new BigNumber(refundingRaw.toString()),
+ taxAmountInLP: new BigNumber(taxRaw.toString()),
+ hasClaimed,
+ isPendingTx: false,
+ vestingReleased: vestingReleasedBn,
+ vestingAmountTotal: vestingTotalBn,
+ isVestingInitialized: schedule ? schedule.isVestingInitialized : false,
+ vestingId: vestingId ? vestingId.toString() : '0',
+ vestingComputeReleasableAmount: releasableBn,
+ }
+
+ if (poolId === 0 && isQualifiedNFT !== undefined && isQualifiedPoints !== undefined) {
+ return {
+ ...data,
+ isQualifiedNFT,
+ isQualifiedPoints,
+ }
+ }
+
+ return data
+}
diff --git a/apps/web/src/views/Ifos/hooks/v8/useGetWalletIfoData.ts b/apps/web/src/views/Ifos/hooks/v8/useGetWalletIfoData.ts
index 2b7af58d91510..b00a658e99b4d 100644
--- a/apps/web/src/views/Ifos/hooks/v8/useGetWalletIfoData.ts
+++ b/apps/web/src/views/Ifos/hooks/v8/useGetWalletIfoData.ts
@@ -1,20 +1,18 @@
-import { Ifo, PoolIds, ifoV8ABI } from '@pancakeswap/ifos'
+import { Ifo, PoolIds } from '@pancakeswap/ifos'
import { BIG_ZERO } from '@pancakeswap/utils/bigNumber'
import BigNumber from 'bignumber.js'
+import { useActiveChainId } from 'hooks/useActiveChainId'
import { useERC20, useIfoV8Contract } from 'hooks/useContract'
import { useCallback, useEffect, useMemo, useState } from 'react'
-import { useAppDispatch } from 'state'
-import { fetchCakeVaultUserData } from 'state/pools'
-import { publicClient } from 'utils/wagmi'
-import { Address } from 'viem'
import { useAccount } from 'wagmi'
-import { useActiveChainId } from 'hooks/useActiveChainId'
-
+import { useAppDispatch } from 'state'
+import { fetchCakeVaultUserData } from 'state/pools'
import { WalletIfoData, WalletIfoState } from '../../types'
import useIfoAllowance from '../useIfoAllowance'
import { useIfoCredit } from '../useIfoCredit'
import { useIfoSourceChain } from '../useIfoSourceChain'
+import { fetchIfoData } from './fetchIfoData'
const initialState = {
isInitialized: false,
@@ -30,6 +28,8 @@ const initialState = {
isVestingInitialized: false,
vestingId: '0',
vestingComputeReleasableAmount: BIG_ZERO,
+ isQualifiedNFT: false,
+ isQualifiedPoints: false,
},
poolUnlimited: {
amountTokenCommittedInLP: BIG_ZERO,
@@ -46,27 +46,23 @@ const initialState = {
},
}
-/**
- * Gets all data from an IFO related to a wallet
- */
const useGetWalletIfoData = (ifo: Ifo): WalletIfoData => {
const { chainId: currentChainId } = useActiveChainId()
const [state, setState] = useState(initialState)
const dispatch = useAppDispatch()
- const { chainId } = ifo
+ const { chainId, version, address } = ifo
const creditAmount = useIfoCredit({ chainId, ifoAddress: ifo.address })
const credit = useMemo(
- () => (creditAmount && BigNumber(creditAmount.quotient.toString())) ?? BIG_ZERO,
+ () => (creditAmount && new BigNumber(creditAmount.quotient.toString())) ?? BIG_ZERO,
[creditAmount],
)
const sourceChain = useIfoSourceChain(chainId)
- const { address, currency, version } = ifo
-
const { address: account } = useAccount()
- const contract = useIfoV8Contract(address, { chainId })
- const currencyContract = useERC20(currency.address, { chainId })
- const allowance = useIfoAllowance(currencyContract, address)
+
+ const contract = useIfoV8Contract(ifo.address, { chainId })
+ const currencyContract = useERC20(ifo.currency.address, { chainId })
+ const allowance = useIfoAllowance(currencyContract, ifo.address)
const setPendingTx = (status: boolean, poolId: PoolIds) =>
setState((prevState) => ({
@@ -87,174 +83,6 @@ const useGetWalletIfoData = (ifo: Ifo): WalletIfoData => {
}))
}
- const fetchIfoData = useCallback(async () => {
- if (!account) {
- return
- }
- const client = publicClient({ chainId })
-
- const [userInfo, amounts] = await client.multicall({
- contracts: [
- {
- address,
- abi: ifoV8ABI,
- functionName: 'viewUserInfo',
- args: [account, [0, 1]],
- },
- {
- address,
- abi: ifoV8ABI,
- functionName: 'viewUserOfferingAndRefundingAmountsForPools',
- args: [account, [0, 1]],
- },
- ],
- allowFailure: false,
- })
-
- let basicId: Address | null = null
- let unlimitedId: Address | null = null
- if (version >= 3.2) {
- const [basicIdDataResult, unlimitedIdDataResult] = await client.multicall({
- contracts: [
- {
- address,
- abi: ifoV8ABI,
- functionName: 'computeVestingScheduleIdForAddressAndPid',
- args: [account, 0],
- },
- {
- address,
- abi: ifoV8ABI,
- functionName: 'computeVestingScheduleIdForAddressAndPid',
- args: [account, 1],
- },
- ],
- })
-
- basicId = basicIdDataResult.result ?? null
- unlimitedId = unlimitedIdDataResult.result ?? null
- }
-
- basicId = basicId || '0x'
- unlimitedId = unlimitedId || '0x'
-
- let [
- isQualifiedNFT,
- isQualifiedPoints,
- basicSchedule,
- unlimitedSchedule,
- basicReleasableAmount,
- unlimitedReleasableAmount,
- ]: [boolean | undefined, boolean | undefined, any | null, any | null, any | null, any | null] = [
- false,
- false,
- null,
- null,
- null,
- null,
- ]
-
- if (version >= 3.1) {
- const [
- isQualifiedNFTResult,
- isQualifiedPointsResult,
- basicScheduleResult,
- unlimitedScheduleResult,
- basicReleasableAmountResult,
- unlimitedReleasableAmountResult,
- ] = await client.multicall({
- contracts: [
- {
- address,
- abi: ifoV8ABI,
- functionName: 'isQualifiedNFT',
- args: [account],
- },
- {
- abi: ifoV8ABI,
- address,
- functionName: 'isQualifiedPoints',
- args: [account],
- },
- {
- abi: ifoV8ABI,
- address,
- functionName: 'getVestingSchedule',
- args: [basicId],
- },
- {
- abi: ifoV8ABI,
- address,
- functionName: 'getVestingSchedule',
- args: [unlimitedId],
- },
- {
- abi: ifoV8ABI,
- address,
- functionName: 'computeReleasableAmount',
- args: [basicId],
- },
- {
- abi: ifoV8ABI,
- address,
- functionName: 'computeReleasableAmount',
- args: [unlimitedId],
- },
- ],
- allowFailure: true,
- })
-
- isQualifiedNFT = isQualifiedNFTResult.result
- isQualifiedPoints = isQualifiedPointsResult.result
- basicSchedule = basicScheduleResult.result
- unlimitedSchedule = unlimitedScheduleResult.result
- basicReleasableAmount = basicReleasableAmountResult.result
- unlimitedReleasableAmount = unlimitedReleasableAmountResult.result
- }
-
- dispatch(fetchCakeVaultUserData({ account, chainId: sourceChain }))
-
- setState(
- (prevState) =>
- ({
- ...prevState,
- isInitialized: true,
- poolBasic: {
- ...prevState.poolBasic,
- amountTokenCommittedInLP: new BigNumber(userInfo[0][0].toString()),
- offeringAmountInToken: new BigNumber(amounts[0][0].toString()),
- refundingAmountInLP: new BigNumber(amounts[0][1].toString()),
- taxAmountInLP: new BigNumber(amounts[0][2].toString()),
- hasClaimed: userInfo[1][0],
- isQualifiedNFT,
- isQualifiedPoints,
- vestingReleased: basicSchedule ? new BigNumber(basicSchedule.released.toString()) : BIG_ZERO,
- vestingAmountTotal: basicSchedule ? new BigNumber(basicSchedule.amountTotal.toString()) : BIG_ZERO,
- isVestingInitialized: basicSchedule ? basicSchedule.isVestingInitialized : false,
- vestingId: basicId ? basicId.toString() : '0',
- vestingComputeReleasableAmount: basicReleasableAmount
- ? new BigNumber(basicReleasableAmount.toString())
- : BIG_ZERO,
- },
- poolUnlimited: {
- ...prevState.poolUnlimited,
- amountTokenCommittedInLP: new BigNumber(userInfo[0][1].toString()),
- offeringAmountInToken: new BigNumber(amounts[1][0].toString()),
- refundingAmountInLP: new BigNumber(amounts[1][1].toString()),
- taxAmountInLP: new BigNumber(amounts[1][2].toString()),
- hasClaimed: userInfo[1][1],
- vestingReleased: unlimitedSchedule ? new BigNumber(unlimitedSchedule.released.toString()) : BIG_ZERO,
- vestingAmountTotal: unlimitedSchedule ? new BigNumber(unlimitedSchedule.amountTotal.toString()) : BIG_ZERO,
- isVestingInitialized: unlimitedSchedule ? unlimitedSchedule.isVestingInitialized : false,
- vestingId: unlimitedId ? unlimitedId.toString() : '0',
- vestingComputeReleasableAmount: unlimitedReleasableAmount
- ? new BigNumber(unlimitedReleasableAmount.toString())
- : BIG_ZERO,
- },
- } as any),
- )
- }, [account, address, dispatch, version, chainId, sourceChain])
-
const resetIfoData = useCallback(() => {
setState({ ...initialState })
}, [])
@@ -266,7 +94,35 @@ const useGetWalletIfoData = (ifo: Ifo): WalletIfoData => {
creditLeft: BigNumber.maximum(BIG_ZERO, creditLeftWithNegative),
}
- useEffect(() => resetIfoData(), [currentChainId, account, resetIfoData])
+ useEffect(() => {
+ if (account) {
+ fetchIfoData(account, ifo, ifo.version, chainId).then(({ basicPoolData, unlimitedPoolData }) => {
+ setState((prevState) => ({
+ ...prevState,
+ isInitialized: true,
+ poolBasic: { ...prevState.poolBasic, ...basicPoolData },
+ poolUnlimited: { ...prevState.poolUnlimited, ...unlimitedPoolData },
+ }))
+ })
+ }
+ }, [account, chainId, dispatch, ifo, sourceChain])
+
+ useEffect(() => {
+ resetIfoData()
+ }, [currentChainId, account, resetIfoData])
+ const fetch = useCallback(async () => {
+ if (!account) {
+ return
+ }
+ const data = await fetchIfoData(account, ifo, version, chainId)
+ dispatch(fetchCakeVaultUserData({ account, chainId: sourceChain }))
+ setState((prevState) => ({
+ ...prevState,
+ isInitialized: true,
+ poolBasic: { ...prevState.poolBasic, ...data.basicPoolData },
+ poolUnlimited: { ...prevState.poolUnlimited, ...data.unlimitedPoolData },
+ }))
+ }, [account, address, dispatch, version, chainId, sourceChain])
return {
...state,
@@ -274,8 +130,8 @@ const useGetWalletIfoData = (ifo: Ifo): WalletIfoData => {
contract,
setPendingTx,
setIsClaimed,
- fetchIfoData,
resetIfoData,
+ fetchIfoData: fetch,
ifoCredit,
version: 8,
}
diff --git a/apps/web/src/views/Ifos/hooks/vesting/useFetchVestingData.ts b/apps/web/src/views/Ifos/hooks/vesting/useFetchVestingData.ts
index b60a6d9f92414..fe97b33dc80ca 100644
--- a/apps/web/src/views/Ifos/hooks/vesting/useFetchVestingData.ts
+++ b/apps/web/src/views/Ifos/hooks/vesting/useFetchVestingData.ts
@@ -1,93 +1,71 @@
-import { useMemo } from 'react'
-import { useAccount } from 'wagmi'
-import { Ifo, PoolIds } from '@pancakeswap/ifos'
-import BigNumber from 'bignumber.js'
+import { PoolIds, UserVestingData } from '@pancakeswap/ifos'
import { useQuery } from '@tanstack/react-query'
+import BigNumber from 'bignumber.js'
+import { useAccount } from 'wagmi'
-import { useIfoConfigsAcrossChains } from 'hooks/useIfoConfig'
import { FAST_INTERVAL } from 'config/constants'
import { useActiveChainId } from 'hooks/useActiveChainId'
+import { useIfoConfigsAcrossChains } from 'hooks/useIfoConfig'
+
+import { fetchUserWalletIfoData, VestingData } from './fetchUserWalletIfoData'
-import { fetchUserWalletIfoData } from './fetchUserWalletIfoData'
+const POOLS = [PoolIds.poolBasic, PoolIds.poolUnlimited]
+
+const isPoolEligible = (poolId: PoolIds, userVestingData: UserVestingData) => {
+ const poolData = userVestingData[poolId]
+ const currentTimeStamp = Date.now()
+ if (!poolData) return false
+
+ if (poolData.offeringAmountInToken.gt(0) || poolData.vestingComputeReleasableAmount.gt(0)) {
+ return true
+ }
+
+ const vestingStartTime = new BigNumber(userVestingData.vestingStartTime)
+ const vestingEndTime = vestingStartTime.plus(poolData.vestingInformationDuration).times(1000)
+
+ return vestingEndTime.gte(currentTimeStamp)
+}
+
+const isEligibleVestingData = (ifo: VestingData) => {
+ const { userVestingData } = ifo
+ return POOLS.some((poolId) => isPoolEligible(poolId, userVestingData))
+}
const useFetchVestingData = () => {
const { address: account } = useAccount()
+
const { chainId } = useActiveChainId()
const configs = useIfoConfigsAcrossChains()
- const allVestingIfo = useMemo(
- () => configs?.filter((ifo) => ifo.version >= 3.2 && ifo.vestingTitle) || [],
- [configs],
- )
- const { data: vestingData, refetch } = useQuery({
- queryKey: ['vestingData', account],
+ // Filter IFOs that are version >= 3.2 and have a vesting title
+ const allVestingIfo = configs?.filter((ifo) => ifo.version >= 3.2 && ifo.vestingTitle) || []
+ const { data: vestingData, refetch } = useQuery({
+ queryKey: ['vestingData', account],
queryFn: async () => {
- const allData = await Promise.all(
- allVestingIfo.map(async (ifo) => {
- const response = await fetchUserWalletIfoData(ifo, account)
- return response
- }),
- )
-
- const currentTimeStamp = Date.now()
-
- return allData.filter(
- // eslint-disable-next-line array-callback-return, consistent-return
- (ifo) => {
- const { userVestingData } = ifo
- if (
- userVestingData[PoolIds.poolBasic].offeringAmountInToken.gt(0) ||
- userVestingData[PoolIds.poolUnlimited].offeringAmountInToken.gt(0)
- ) {
- if (
- userVestingData[PoolIds.poolBasic].vestingComputeReleasableAmount.gt(0) ||
- userVestingData[PoolIds.poolUnlimited].vestingComputeReleasableAmount.gt(0)
- ) {
- return ifo
- }
- const vestingStartTime = new BigNumber(userVestingData.vestingStartTime)
- const isPoolUnlimitedLive = vestingStartTime
- .plus(userVestingData[PoolIds.poolUnlimited].vestingInformationDuration)
- .times(1000)
- .gte(currentTimeStamp)
- if (isPoolUnlimitedLive) return ifo
- const isPoolBasicLive = vestingStartTime
- .plus(userVestingData[PoolIds.poolBasic].vestingInformationDuration)
- .times(1000)
- .gte(currentTimeStamp)
- if (isPoolBasicLive) return ifo
- return false
- }
- return false
- },
- )
- },
+ const allDataSettled = await Promise.allSettled(allVestingIfo.map((ifo) => fetchUserWalletIfoData(ifo, account)))
+ const allData = allDataSettled
+ .filter((result) => result.status === 'fulfilled')
+ .map((result) => (result as PromiseFulfilledResult).value)
+ const filteredData = allData.filter((x) => x.ifo.isActive).filter(isEligibleVestingData)
+
+ const sortedData = filteredData.toSorted((a, b) => {
+ if (a.ifo.chainId === chainId && b.ifo.chainId !== chainId) return -1
+ if (a.ifo.chainId !== chainId && b.ifo.chainId === chainId) return 1
+ return 0
+ })
+
+ return sortedData[0]
+ },
enabled: Boolean(account),
refetchOnWindowFocus: false,
refetchInterval: FAST_INTERVAL,
staleTime: FAST_INTERVAL,
})
- // Sort by active chain
- const data = useMemo(
- () =>
- vestingData &&
- vestingData.toSorted((a, b) => {
- if (a.ifo.chainId === chainId && b.ifo.chainId !== chainId) {
- return -1
- }
- if (a.ifo.chainId !== chainId && b.ifo.chainId === chainId) {
- return 1
- }
- return 0
- }),
- [chainId, vestingData],
- )
-
return {
- data: data || [],
+ data: vestingData || null,
fetchUserVestingData: refetch,
}
}
diff --git a/packages/achievements/src/campaigns.ts b/packages/achievements/src/campaigns.ts
index 0477ba2f39313..6e037c850f56b 100644
--- a/packages/achievements/src/campaigns.ts
+++ b/packages/achievements/src/campaigns.ts
@@ -10,6 +10,12 @@ import { Campaign } from './types'
*/
export const campaigns: Campaign[] = [
+ {
+ id: '512600000',
+ type: 'ifo',
+ title: 'SOLV',
+ badge: 'ifo-solv.svg',
+ },
{
id: '511110000',
type: 'ifo',
diff --git a/packages/ifos/src/abis/ICake.ts b/packages/ifos/src/abis/ICake.ts
index e74bc66728a3c..a60f5af92ec1f 100644
--- a/packages/ifos/src/abis/ICake.ts
+++ b/packages/ifos/src/abis/ICake.ts
@@ -4,6 +4,15 @@ export const iCakeABI = [
stateMutability: 'nonpayable',
type: 'constructor',
},
+ {
+ anonymous: false,
+ inputs: [
+ { indexed: true, internalType: 'address', name: 'delegator', type: 'address' },
+ { indexed: true, internalType: 'address', name: 'VECakeUser', type: 'address' },
+ ],
+ name: 'Approve',
+ type: 'event',
+ },
{
anonymous: false,
inputs: [
@@ -13,6 +22,16 @@ export const iCakeABI = [
name: 'OwnershipTransferred',
type: 'event',
},
+ {
+ anonymous: false,
+ inputs: [
+ { indexed: true, internalType: 'address', name: 'user', type: 'address' },
+ { indexed: true, internalType: 'address', name: 'oldDelegator', type: 'address' },
+ { indexed: true, internalType: 'address', name: 'delegator', type: 'address' },
+ ],
+ name: 'UpdateDelegator',
+ type: 'event',
+ },
{
anonymous: false,
inputs: [{ indexed: true, internalType: 'address', name: 'newAddress', type: 'address' }],
@@ -25,6 +44,15 @@ export const iCakeABI = [
name: 'UpdateRatio',
type: 'event',
},
+ {
+ anonymous: false,
+ inputs: [
+ { indexed: true, internalType: 'address', name: 'userAddress', type: 'address' },
+ { indexed: false, internalType: 'uint256', name: 'ratio', type: 'uint256' },
+ ],
+ name: 'UpdateUserRatioOverride',
+ type: 'event',
+ },
{
inputs: [],
name: 'MIN_CEILING_DURATION',
@@ -46,6 +74,34 @@ export const iCakeABI = [
stateMutability: 'view',
type: 'function',
},
+ {
+ inputs: [{ internalType: 'address', name: '_VECakeUser', type: 'address' }],
+ name: 'approveToVECakeUser',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [{ internalType: 'address', name: '', type: 'address' }],
+ name: 'delegated',
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [{ internalType: 'address', name: '', type: 'address' }],
+ name: 'delegator',
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [{ internalType: 'address', name: '', type: 'address' }],
+ name: 'delegatorApprove',
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ stateMutability: 'view',
+ type: 'function',
+ },
{
inputs: [{ internalType: 'address', name: '_user', type: 'address' }],
name: 'getUserCredit',
@@ -80,6 +136,13 @@ export const iCakeABI = [
stateMutability: 'view',
type: 'function',
},
+ {
+ inputs: [{ internalType: 'address', name: '_user', type: 'address' }],
+ name: 'getVeCakeUser',
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
+ stateMutability: 'view',
+ type: 'function',
+ },
{
inputs: [],
name: 'ifoDeployerAddress',
@@ -101,7 +164,32 @@ export const iCakeABI = [
stateMutability: 'view',
type: 'function',
},
+ { inputs: [], name: 'removeDelegator', outputs: [], stateMutability: 'nonpayable', type: 'function' },
{ inputs: [], name: 'renounceOwnership', outputs: [], stateMutability: 'nonpayable', type: 'function' },
+ {
+ inputs: [{ internalType: 'address', name: '_delegator', type: 'address' }],
+ name: 'setDelegator',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ components: [
+ { internalType: 'address', name: 'VECakeUser', type: 'address' },
+ { internalType: 'address', name: 'delegator', type: 'address' },
+ ],
+ internalType: 'struct ICakeV3.DelegatorConfig[]',
+ name: '_delegatorConfigs',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'setDelegators',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
{
inputs: [{ internalType: 'address', name: 'newOwner', type: 'address' }],
name: 'transferOwnership',
@@ -123,6 +211,23 @@ export const iCakeABI = [
stateMutability: 'nonpayable',
type: 'function',
},
+ {
+ inputs: [
+ { internalType: 'address', name: '_user', type: 'address' },
+ { internalType: 'uint256', name: '_newRatio', type: 'uint256' },
+ ],
+ name: 'updateUserRatio',
+ outputs: [],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [{ internalType: 'address', name: '', type: 'address' }],
+ name: 'userRatioOverride',
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+ stateMutability: 'view',
+ type: 'function',
+ },
{
inputs: [],
name: 'veCakeAddress',
diff --git a/packages/ifos/src/abis/IfoV8.ts b/packages/ifos/src/abis/IfoV8.ts
index f71358530c4e4..88d5e05b5a969 100644
--- a/packages/ifos/src/abis/IfoV8.ts
+++ b/packages/ifos/src/abis/IfoV8.ts
@@ -1,4 +1,46 @@
export const ifoV8ABI = [
+ { inputs: [], stateMutability: 'nonpayable', type: 'constructor' },
+ { inputs: [], name: 'AddressesLengthNotCorrect', type: 'error' },
+ { inputs: [], name: 'AlreadyHarvested', type: 'error' },
+ { inputs: [], name: 'AlreadyInitialized', type: 'error' },
+ { inputs: [], name: 'AmountMustExceedZero', type: 'error' },
+ { inputs: [], name: 'CanNotBeLPToken', type: 'error' },
+ { inputs: [], name: 'CanNotBeOfferingToken', type: 'error' },
+ { inputs: [], name: 'DidNotParticipate', type: 'error' },
+ { inputs: [], name: 'EndTimeTooFar', type: 'error' },
+ { inputs: [], name: 'FlatTaxRateMustBe0WhenHasTaxIsFalse', type: 'error' },
+ { inputs: [], name: 'FlatTaxRateMustBeLessThan1e12', type: 'error' },
+ { inputs: [], name: 'IFOHasEnded', type: 'error' },
+ { inputs: [], name: 'IFOHasStarted', type: 'error' },
+ { inputs: [], name: 'MustHaveAnActiveProfile', type: 'error' },
+ { inputs: [], name: 'NFTRequirementsMustBeMetForHarvest', type: 'error' },
+ { inputs: [], name: 'NFTTokenIdNotSameAsRegistered', type: 'error' },
+ { inputs: [], name: 'NFTUsedByAnotherAddressAlready', type: 'error' },
+ { inputs: [], name: 'NewAmountAboveUserLimit', type: 'error' },
+ { inputs: [], name: 'NotEnoughIFOCreditLeft', type: 'error' },
+ { inputs: [], name: 'NotEnoughLPTokens', type: 'error' },
+ { inputs: [], name: 'NotEnoughOfferingTokens', type: 'error' },
+ { inputs: [], name: 'NotFactory', type: 'error' },
+ { inputs: [], name: 'NotMeetAnyoneOfRequiredConditions', type: 'error' },
+ { inputs: [], name: 'OnlyOwner', type: 'error' },
+ { inputs: [], name: 'PoolIdNotValid', type: 'error' },
+ { inputs: [], name: 'PoolNotSet', type: 'error' },
+ { inputs: [], name: 'ProfileNotActive', type: 'error' },
+ { inputs: [], name: 'ShouldNotLargerThanTheNumberOfPools', type: 'error' },
+ { inputs: [], name: 'StartAndEndTimestampsLengthNotCorrect', type: 'error' },
+ { inputs: [], name: 'StartTimeMustGreaterThanCurrentBlockTime', type: 'error' },
+ { inputs: [], name: 'StartTimeMustInferiorToEndTime', type: 'error' },
+ { inputs: [], name: 'TokensNotDepositedProperly', type: 'error' },
+ { inputs: [], name: 'TooEarly', type: 'error' },
+ { inputs: [], name: 'TooLate', type: 'error' },
+ { inputs: [], name: 'VestingDurationMustExceeds0', type: 'error' },
+ { inputs: [], name: 'VestingIsRevoked', type: 'error' },
+ { inputs: [], name: 'VestingNotEnoughToRelease', type: 'error' },
+ { inputs: [], name: 'VestingNotExist', type: 'error' },
+ { inputs: [], name: 'VestingOnlyBeneficiaryOrOwnerCanRelease', type: 'error' },
+ { inputs: [], name: 'VestingPercentageShouldRangeIn0And100', type: 'error' },
+ { inputs: [], name: 'VestingSlicePerSecondsMustBeExceeds1', type: 'error' },
+ { inputs: [], name: 'VestingSlicePerSecondsMustBeInteriorDuration', type: 'error' },
{
anonymous: false,
inputs: [
diff --git a/packages/ifos/src/constants/contracts.ts b/packages/ifos/src/constants/contracts.ts
index 05fb13a109c45..414616c83e78d 100644
--- a/packages/ifos/src/constants/contracts.ts
+++ b/packages/ifos/src/constants/contracts.ts
@@ -8,7 +8,7 @@ export type ContractAddresses = {
}
export const ICAKE = {
- [ChainId.BSC]: '0x9C46110Eb094866922045729AEFcdff91f2CFFF5',
+ [ChainId.BSC]: '0xd68A31C3f2b61C621a7619b6F4667a2aee959132',
[ChainId.BSC_TESTNET]: '0x5FB0b7a782c2f192493d86922dD3873b6392C8e8',
[ChainId.GOERLI]: '0x45A33F911F9E2ea404303920E9775467518a4ed7',
[ChainId.ARBITRUM_ONE]: '0x747A78d0EB1adAeca945ECc578feB17065a1dA39',
diff --git a/packages/ifos/src/constants/ifos/bsc.ts b/packages/ifos/src/constants/ifos/bsc.ts
index 1ac960a75d79d..87e056a432979 100644
--- a/packages/ifos/src/constants/ifos/bsc.ts
+++ b/packages/ifos/src/constants/ifos/bsc.ts
@@ -4,6 +4,31 @@ import { BaseIfoConfig } from '../../types'
import { cakeBnbLpToken } from '../lpTokens'
export const ifos: BaseIfoConfig[] = [
+ {
+ id: 'solv',
+ address: '0xb0De22aAe05789C13E900688b420F1Bb7c2C3889',
+ isActive: true,
+ cIFO: false,
+ plannedStartTime: new Date('2025-01-16T10:00:00Z').getTime() / 1000,
+ poolBasic: {
+ raiseAmount: '$10,000',
+ },
+ poolUnlimited: {
+ raiseAmount: '$90,000',
+ additionalClaimingFee: true,
+ },
+ name: 'SOLV',
+ currency: bscTokens.cake,
+ token: bscTokens.solv,
+ campaignId: '512600000',
+ articleUrl: 'https://forum.pancakeswap.finance/t/solv-ifo-discussion-thread/993',
+ tokenOfferingPrice: 0.0310559006,
+ version: 8,
+ twitterUrl: 'https://twitter.com/SolvProtocol/',
+ description:
+ 'Solv is a Bitcoin staking protocol that unlocks liquidity and maximizes the utility of idle Bitcoin assets',
+ vestingTitle: 'SOLV is Solv Protocol’s native utility token meant for governance, staking, and fee discounts',
+ },
{
id: 'lista',
address: '0x232c577a3A9c4ecbeeb213E1eb5519cB0C2FDb0F',
diff --git a/packages/ifos/src/queries/fetchUserIfo.ts b/packages/ifos/src/queries/fetchUserIfo.ts
index 35443fb797f02..9cd408f01e8d1 100644
--- a/packages/ifos/src/queries/fetchUserIfo.ts
+++ b/packages/ifos/src/queries/fetchUserIfo.ts
@@ -105,12 +105,23 @@ export async function getUserIfoInfo({ account, ifo, chainId, provider }: GetIfo
}
}
-export async function getCurrentIfoRatio({ chainId, provider }: Omit): Promise {
+export async function getCurrentIfoRatio({ chainId, provider, account }: Params): Promise {
if (!chainId) {
return 0
}
try {
const ifoCreditContract = getIfoCreditAddressContract(chainId, provider)
+ if (account) {
+ const [specialRatio, precision, ratio] = await Promise.all([
+ // @ts-ignore
+ ifoCreditContract.read.userRatioOverride([account]),
+ // @ts-ignore
+ ifoCreditContract.read.RATION_PRECISION(),
+ // @ts-ignore
+ ifoCreditContract.read.ratio(),
+ ])
+ return new BigNumber((specialRatio || ratio).toString()).div(new BigNumber(precision.toString())).toNumber()
+ }
const [ratio, precision] = await Promise.all([
// @ts-ignore
ifoCreditContract.read.ratio(),
diff --git a/packages/localization/src/config/translations.json b/packages/localization/src/config/translations.json
index be65e30e66f8a..7f97569b35449 100644
--- a/packages/localization/src/config/translations.json
+++ b/packages/localization/src/config/translations.json
@@ -3678,5 +3678,12 @@
"Please fix form errors.": "Please fix form errors.",
"Title is required": "Title is required",
"Content is required": "Content is required",
- "Please provide valid choices": "Please provide valid choices"
+ "Please provide valid choices": "Please provide valid choices",
+ "The vested tokens will be released linearly over a period of %countdown%.": "The vested tokens will be released linearly over a period of %countdown%.",
+ "%token% IFO starts in": "%token% IFO starts in",
+ "Join the SOLV Token Launch (IFO) on BNB Chain PancakeSwap": "Join the SOLV Token Launch (IFO) on BNB Chain PancakeSwap",
+ "The time has already passed.": "The time has already passed.",
+ "day(s)": "day(s)",
+ "hour(s)": "hour(s)",
+ "SOON": "SOON"
}
diff --git a/packages/tokens/src/constants/bsc.ts b/packages/tokens/src/constants/bsc.ts
index 84bced710e837..da766a8084e05 100644
--- a/packages/tokens/src/constants/bsc.ts
+++ b/packages/tokens/src/constants/bsc.ts
@@ -3341,4 +3341,20 @@ export const bscTokens = {
'YieldNest: BNB Liquid Restaking',
'https://app.yieldnest.finance/restake/ynBNB',
),
+ listapie: new ERC20Token(
+ ChainId.BSC,
+ '0xFceB31A79F71AC9CBDCF853519c1b12D379EdC46',
+ 18,
+ 'LISTA-PIE',
+ 'Lista Pie',
+ 'https://lista.org/',
+ ),
+ solv: new ERC20Token(
+ ChainId.BSC,
+ '0x04830A96a23EA718fAA695a5AAe74695AAE3A23f',
+ 18,
+ 'SOLV',
+ 'Solv',
+ 'https://solv.finance/',
+ ),
}
diff --git a/packages/uikit/src/widgets/Ifo/IfoGetTokenModal.tsx b/packages/uikit/src/widgets/Ifo/IfoGetTokenModal.tsx
index b57bafb04e5d3..ef49807a56b92 100644
--- a/packages/uikit/src/widgets/Ifo/IfoGetTokenModal.tsx
+++ b/packages/uikit/src/widgets/Ifo/IfoGetTokenModal.tsx
@@ -1,11 +1,11 @@
import { useTranslation } from "@pancakeswap/localization";
-import { useMatchBreakpoints } from "../../contexts";
-import { Modal, ModalBody } from "../Modal";
-import { Text } from "../../components/Text";
-import { Link } from "../../components/Link";
import { Button } from "../../components/Button";
import { Image } from "../../components/Image";
+import { Link } from "../../components/Link";
import { OpenNewIcon } from "../../components/Svg";
+import { Text } from "../../components/Text";
+import { useMatchBreakpoints } from "../../contexts";
+import { Modal, ModalBody } from "../Modal";
interface Props {
symbol: string;
@@ -38,7 +38,7 @@ const IfoGetTokenModal: React.FC> = ({ symbol, ad
mt="1rem"
as={Link}
external
- href="https://bridge.pancakeswap.finance/"
+ href="https://pancakeswap.finance/bridge"
color="invertedContrast"
endIcon={}
minWidth="100%" // Bypass the width="fit-content" on Links
diff --git a/packages/widgets-internal/ifo/ICakeInfo.tsx b/packages/widgets-internal/ifo/ICakeInfo.tsx
index 2bc2af3d1da70..e17a71984e20a 100644
--- a/packages/widgets-internal/ifo/ICakeInfo.tsx
+++ b/packages/widgets-internal/ifo/ICakeInfo.tsx
@@ -1,9 +1,9 @@
-import { ReactNode, useMemo } from "react";
-import { FlexGap, Text, useTooltip } from "@pancakeswap/uikit";
-import { SpaceProps } from "styled-system";
import { useTranslation } from "@pancakeswap/localization";
+import { FlexGap, Text, useTooltip } from "@pancakeswap/uikit";
import { formatUnixTimestamp } from "@pancakeswap/utils/formatTimestamp";
+import { ReactNode, useMemo } from "react";
import styled from "styled-components";
+import { SpaceProps } from "styled-system";
type Props = {
// Unix timestamp of the snapshot