import BigNumber from 'bignumber.js' import { Duration, intervalToDuration } from 'date-fns' import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { StyleSheet, Text, View } from 'react-native' import { LabelWithInfo } from 'src/components/LabelWithInfo' import { formatValueToDisplay } from 'src/components/TokenDisplay' import { IconSize } from 'src/components/TokenIcon' import TokenIcons from 'src/earn/poolInfoScreen/TokenIcons' import { getEarnPositionBalanceValues } from 'src/earn/utils' import { useDollarsToLocalAmount } from 'src/localCurrency/hooks' import { getLocalCurrencySymbol, usdToLocalCurrencyRateSelector } from 'src/localCurrency/selectors' import type { EarningItem } from 'src/positions/types' import { EarnPosition } from 'src/positions/types' import { useSelector } from 'src/redux/hooks' import Colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' import { useTokenInfo, useTokensInfo } from 'src/tokens/hooks' import { TokenBalance } from 'src/tokens/slice' import { formattedDuration } from 'src/utils/time' function EarningItemLineItem({ earnItem }: { earnItem: EarningItem }) { const { t } = useTranslation() const tokenInfo = useTokenInfo(earnItem.tokenId) const amountInUsd = tokenInfo?.priceUsd?.multipliedBy(earnItem.amount) const localCurrencyExchangeRate = useSelector(usdToLocalCurrencyRateSelector) const amountInLocalCurrency = new BigNumber(localCurrencyExchangeRate ?? 0).multipliedBy( amountInUsd ?? 0 ) const localCurrencySymbol = useSelector(getLocalCurrencySymbol) return ( {earnItem.label} {t('earnFlow.poolInfoScreen.lineItemAmountDisplay', { localCurrencySymbol, localCurrencyAmount: formatValueToDisplay(amountInLocalCurrency), cryptoAmount: formatValueToDisplay(new BigNumber(earnItem.amount)), cryptoSymbol: tokenInfo?.symbol, })} ) } export function DepositAndEarningsCard({ earnPosition, onInfoIconPress, }: { earnPosition: EarnPosition onInfoIconPress: () => void }) { const { t } = useTranslation() const { balance, pricePerShare } = earnPosition const { earningItems, depositTokenId, cantSeparateCompoundedInterest } = earnPosition.dataProps const depositTokenInfo = useTokenInfo(depositTokenId) const localCurrencySymbol = useSelector(getLocalCurrencySymbol) const localCurrencyExchangeRate = useSelector(usdToLocalCurrencyRateSelector) const { poolBalanceInUsd: depositBalanceInUsd, poolBalanceInDepositToken } = useMemo( () => getEarnPositionBalanceValues({ pool: earnPosition }), [earnPosition] ) // Deposit items used to calculate the total balance and total deposited const depositBalanceInLocalCurrency = new BigNumber(localCurrencyExchangeRate ?? 0).multipliedBy( depositBalanceInUsd ?? 0 ) // Earning items used to calculate the total balance and total deposited const earningItemsTokenIds = earningItems.map((item) => item.tokenId) const earningItemsTokenInfo = useTokensInfo(earningItemsTokenIds) const totalBalanceInLocalCurrency = useMemo(() => { return depositBalanceInLocalCurrency.plus( earningItems .filter((item) => !item.includedInPoolBalance) .reduce((acc, item) => { const tokenInfo = earningItemsTokenInfo.find((token) => token?.tokenId === item.tokenId) const amountInUsd = tokenInfo?.priceUsd?.multipliedBy(item.amount) const amountInLocalCurrency = new BigNumber(localCurrencyExchangeRate ?? 0).multipliedBy( amountInUsd ?? 0 ) return acc.plus(amountInLocalCurrency ?? 0) }, new BigNumber(0)) ) }, [ depositBalanceInLocalCurrency, earningItems, earningItemsTokenInfo, localCurrencyExchangeRate, ]) const totalDepositBalanceInCrypto = useMemo(() => { return poolBalanceInDepositToken.minus( earningItems .filter((item) => item.includedInPoolBalance) .reduce((acc, item) => { const tokenInfo = earningItemsTokenInfo.find((token) => token?.tokenId === item.tokenId) return acc.plus( new BigNumber(item.amount) .multipliedBy(tokenInfo?.priceUsd ?? 0) .dividedBy(depositTokenInfo?.priceUsd ?? 1) ) }, new BigNumber(0)) ) }, [balance, pricePerShare, earningItems, earningItemsTokenInfo, depositTokenInfo]) const totalDepositBalanceInLocalCurrency = useDollarsToLocalAmount( totalDepositBalanceInCrypto.multipliedBy(depositTokenInfo?.priceUsd ?? 0) ) ?? new BigNumber(0) return ( {t('earnFlow.poolInfoScreen.titleLocalAmountDisplay', { localCurrencySymbol, localCurrencyAmount: formatValueToDisplay(totalBalanceInLocalCurrency), })} {cantSeparateCompoundedInterest ? ( {t('earnFlow.poolInfoScreen.depositAndEarnings')} ) : ( {t('earnFlow.poolInfoScreen.deposit')} )} {t('earnFlow.poolInfoScreen.lineItemAmountDisplay', { localCurrencySymbol, localCurrencyAmount: formatValueToDisplay(totalDepositBalanceInLocalCurrency), cryptoAmount: formatValueToDisplay(totalDepositBalanceInCrypto), cryptoSymbol: depositTokenInfo?.symbol, })} {earningItems.map((item, index) => ( ))} ) } export function YieldCard({ onInfoIconPress, tokensInfo, earnPosition, }: { onInfoIconPress: () => void tokensInfo: TokenBalance[] earnPosition: EarnPosition }) { const { t } = useTranslation() const yieldRateSum = earnPosition.dataProps.yieldRates .map((rate) => rate.percentage) .reduce((acc, rate) => acc + rate, 0) return ( {yieldRateSum > 0.00005 ? t('earnFlow.poolInfoScreen.ratePercent', { rate: yieldRateSum.toFixed(2) }) : '--'} {earnPosition.dataProps.yieldRates.map((rate, index) => { // TODO: investigate how to do when there are multiple tokens in a yield rate const tokenInfo = tokensInfo.filter((token) => token.tokenId === rate.tokenId) return ( {rate.label} {t('earnFlow.poolInfoScreen.ratePercent', { rate: rate.percentage.toFixed(2) })} ) })} ) } export function DailyYieldRateCard({ dailyYieldRate, onInfoIconPress, }: { dailyYieldRate: number onInfoIconPress: () => void }) { const { t } = useTranslation() return ( {t('earnFlow.poolInfoScreen.ratePercent', { rate: dailyYieldRate.toFixed(4) })} ) } export function TvlCard({ earnPosition, onInfoIconPress, }: { earnPosition: EarnPosition onInfoIconPress: () => void }) { const localCurrencySymbol = useSelector(getLocalCurrencySymbol) const { t } = useTranslation() const tvl = earnPosition.dataProps.tvl const tvlInFiat = useDollarsToLocalAmount(tvl ?? null) const tvlString = useMemo(() => { return `${localCurrencySymbol}${tvlInFiat ? formatValueToDisplay(tvlInFiat) : '--'}` }, [localCurrencySymbol, tvlInFiat]) return ( {tvlString} ) } export function AgeCard({ ageOfPool, onInfoIconPress, }: { ageOfPool: Date onInfoIconPress: () => void }) { const { t } = useTranslation() const dateInterval: Duration = intervalToDuration({ start: ageOfPool, end: new Date(), }) return ( {formattedDuration(dateInterval)} ) } export const styles = StyleSheet.create({ flexShrink: { flexShrink: 1, }, card: { padding: Spacing.Regular16, borderColor: Colors.borderSecondary, backgroundColor: Colors.backgroundSecondary, borderWidth: 1, borderRadius: 12, gap: Spacing.Regular16, }, cardLineContainer: { flex: 1, flexDirection: 'row', justifyContent: 'space-between', }, cardLineLabel: { paddingRight: 20, // Prevents Icon from being cut off on long labels }, yieldRateLabelContainer: { flexDirection: 'row', gap: Spacing.Tiny4, }, cardTitleText: { ...typeScale.labelSemiBoldMedium, }, cardLabelText: { ...typeScale.bodyMedium, color: Colors.contentSecondary, }, depositAndEarningCard: { backgroundColor: Colors.backgroundSecondary, padding: 0, gap: 0, }, depositAndEarningCardTitleContainer: { padding: Spacing.Regular16, alignItems: 'center', gap: Spacing.Tiny4, borderBottomWidth: 1, borderColor: Colors.borderSecondary, }, depositAndEarningCardTitleText: { ...typeScale.titleMedium, }, depositAndEarningCardSubtitleContainer: { padding: Spacing.Regular16, borderBottomLeftRadius: 12, borderBottomRightRadius: 12, gap: Spacing.Smallest8, }, depositAndEarningsCardLabelText: { ...typeScale.bodyMedium, flexWrap: 'wrap', textAlign: 'left', }, depositAndEarningsCardValueText: { ...typeScale.bodyMedium, flexWrap: 'wrap', textAlign: 'right', }, })