Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redesign: Pool ratings support multiple and pdf #2493

Merged
merged 8 commits into from
Oct 15, 2024
5 changes: 3 additions & 2 deletions centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { daysBetween, formatDate } from '../../utils/date'
import { formatBalance, formatBalanceAbbreviated, formatPercentage } from '../../utils/formatting'
import { useLoans } from '../../utils/useLoans'
import { useDailyPoolStates, usePool } from '../../utils/usePools'
import { DYF_POOL_ID } from '../PoolCard'
import { Tooltips, tooltipText } from '../Tooltips'
import { TooltipContainer, TooltipTitle } from './Tooltip'
import { getOneDayPerMonth, getRangeNumber } from './utils'
Expand Down Expand Up @@ -160,7 +161,7 @@ function PoolPerformanceChart() {
nav: todayAssetValue,
juniorTokenPrice: tranchePrices.juniorTokenPrice ?? 0,
seniorTokenPrice: tranchePrices.seniorTokenPrice ?? null,
juniorAPY: pool.id === '1655476167' ? 15 : todayJuniorApy,
juniorAPY: pool.id === DYF_POOL_ID ? 15 : todayJuniorApy,
seniorAPY: todaySeniorApy,
}
}
Expand All @@ -181,7 +182,7 @@ function PoolPerformanceChart() {
nav: todayAssetValue,
price: todayPrice,
currency: pool.currency.symbol,
juniorAPY: pool.id === '1655476167' ? 15 : todayJuniorApy,
juniorAPY: pool.id === DYF_POOL_ID ? 15 : todayJuniorApy,
seniorAPY: todaySeniorApy,
...trancheTodayPrice,
}
Expand Down
55 changes: 37 additions & 18 deletions centrifuge-app/src/components/IssuerSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -221,27 +221,46 @@ const Links = ({ links }: { links: { label: string; href?: string; show: boolean
}

export function RatingDetails({ metadata }: IssuerSectionProps) {
const rating = metadata?.pool?.rating
const ratings = metadata?.pool?.poolRatings
const cent = useCentrifuge()

return rating?.ratingAgency || rating?.ratingValue || rating?.ratingReportUrl ? (
return ratings?.length ? (
<Stack gap={1}>
<Text variant="heading2">Pool rating</Text>
<Shelf flexDirection="column" alignItems="flex-start">
<Shelf gap={1}>
{rating.ratingAgency && (
<LabelValueStack label="Rating agency" value={<Text variant="body2">{rating.ratingAgency}</Text>} />
)}
{rating.ratingValue && (
<LabelValueStack label="Rating" value={<Text variant="body2">{rating.ratingValue}</Text>} />
)}
</Shelf>
</Shelf>
<Shelf>
{rating?.ratingReportUrl && (
<AnchorButton href={rating.ratingReportUrl} target="_blank" variant="secondary" icon={IconExternalLink}>
View full report
</AnchorButton>
)}
<Shelf flexDirection="column" alignItems="flex-start" gap={2}>
{ratings?.map((rating) => {
return (
<Stack gap={1} key={rating.agency}>
<Shelf flexDirection="column" alignItems="flex-start">
<Shelf gap={1}>
{rating.agency && (
<LabelValueStack label="Rating agency" value={<Text variant="body2">{rating.agency}</Text>} />
)}
{rating.value && (
<LabelValueStack label="Rating" value={<Text variant="body2">{rating.value}</Text>} />
)}
</Shelf>
</Shelf>
<Shelf gap={2}>
{rating?.reportUrl && (
<AnchorButton href={rating.reportUrl} target="_blank" variant="secondary" icon={IconExternalLink}>
View full report
</AnchorButton>
)}
{rating?.reportFile && (
<AnchorButton
href={cent.metadata.parseMetadataUrl(rating.reportFile.uri)}
target="_blank"
variant="secondary"
icon={IconExternalLink}
>
Download report
</AnchorButton>
)}
</Shelf>
</Stack>
)
})}
</Shelf>
</Stack>
) : null
Expand Down
97 changes: 54 additions & 43 deletions centrifuge-app/src/components/PoolCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
}
}

type TinlakeTranchesKey = 'silver' | 'blocktowerThree' | 'blocktowerFour'

type TrancheWithCurrency = Pick<Token, 'yield30DaysAnnualized' | 'interestRatePerSec' | 'currency' | 'id' | 'seniority'>

const StyledRouterTextLink = styled(RouterTextLink)`
Expand Down Expand Up @@ -58,32 +56,55 @@
`

const tinlakeTranches = {
silver: {
Junior: '15%',
Senior: '7%',
NS2: {
name: 'New Silver 3',
tranches: [
{ name: 'Junior', apr: '15%', minInvestment: '-' },
{ name: 'Senior', apr: '7%', minInvestment: '5K' },
],
shortDescription: ' Real estate bridge loans for fix and flip projects, maturing in 12-24 months.',
InvestorType: 'Qualified Investors',
investorType: 'Qualified Investors',
},
blocktowerThree: {
Junior: '15%',
Senior: '4%',
BT3: {
name: 'BlockTower Series 3',
tranches: [
{ name: 'Junior', apr: '15%', minInvestment: '-' },
{ name: 'Senior', apr: '4%', minInvestment: '-' },
],
shortDescription: ' Investment-grade consumer ABS, auto ABS, and CLOs under 4 years.',
InvestorType: 'Private',
investorType: 'Private',
},
blocktowerFour: {
Junior: '15%',
Senior: '4%',
BT4: {
name: 'BlockTower Series 4',
tranches: [
{ name: 'Junior', apr: '15%', minInvestment: '-' },
{ name: 'Senior', apr: '4%', minInvestment: '-' },
],
shortDescription: 'Investment-grade consumer ABS, auto ABS, and CLOs under 4 years.',
InvestorType: 'Private',
investorType: 'Private',
},
none: {
Junior: '-',
Senior: '-',
name: '-',
tranches: [
{ name: 'Junior', apr: '-', minInvestment: '-' },
{ name: 'Senior', apr: '-', minInvestment: '-' },
],
shortDescription: '',
InvestorType: '-',
investorType: '-',
},
}

export const DYF_POOL_ID = '1655476167'
export const NS3_POOL_ID = '1615768079'

export type CentrifugeTargetAPYs = keyof typeof centrifugeTargetAPYs
export const centrifugeTargetAPYs = {
[DYF_POOL_ID]: ['15%'],
[NS3_POOL_ID]: ['8.0%', '16%'],
}

type TinlakeTranchesKey = keyof typeof tinlakeTranches

export type PoolCardProps = {
poolId?: string
name?: string
Expand Down Expand Up @@ -112,25 +133,14 @@
}: PoolCardProps) {
const theme = useTheme()
const isOneTranche = tranches && tranches?.length === 1
const isTinlakePool =
poolId === '0x53b2d22d07E069a3b132BfeaaD275b10273d381E' ||
poolId === '0x90040F96aB8f291b6d43A8972806e977631aFFdE' ||
poolId === '0x55d86d51Ac3bcAB7ab7d2124931FbA106c8b60c7'

const tinlakeObjKey = () => {
if (name?.includes('Silver')) return 'silver'
else if (name?.includes('BlockTower Series 3')) return 'blocktowerThree'
else if (name?.includes('BlockTower Series 4')) return 'blocktowerFour'
else return 'none'
}
const isTinlakePool = poolId?.startsWith('0x')

const getTinlakeMinInvestment = (trancheName: 'Junior' | 'Senior') => {
if (name?.includes('Silver') && trancheName === 'Senior') return '5K'
else return '-'
}
const tinlakeKey = (Object.keys(tinlakeTranches).find(
(key) => tinlakeTranches[key as TinlakeTranchesKey].name === name
) || 'none') as TinlakeTranchesKey

const renderText = (text: string, isApr?: boolean) => {
if (isApr && poolId === '1615768079') {
if (isApr && poolId === NS3_POOL_ID) {
return (
<Box display="flex">
<Text fontWeight={500} as="h2" variant={isOneTranche ? 'heading1' : 'body1'} style={{ width: 35 }}>
Expand All @@ -149,11 +159,11 @@
)
}

const calculateApy = (tranche: TrancheWithCurrency) => {

Check warning on line 162 in centrifuge-app/src/components/PoolCard/index.tsx

View workflow job for this annotation

GitHub Actions / build-app

The 'calculateApy' function makes the dependencies of useMemo Hook (at line 199) change on every render. Move it inside the useMemo callback. Alternatively, wrap the definition of 'calculateApy' in its own useCallback() Hook

Check warning on line 162 in centrifuge-app/src/components/PoolCard/index.tsx

View workflow job for this annotation

GitHub Actions / ff-prod / build-app

The 'calculateApy' function makes the dependencies of useMemo Hook (at line 199) change on every render. Move it inside the useMemo callback. Alternatively, wrap the definition of 'calculateApy' in its own useCallback() Hook
const daysSinceCreation = createdAt ? daysBetween(createdAt, new Date()) : 0
if (poolId === '1655476167') return '15%'
if (poolId === '1615768079' && tranche.seniority === 0) return '8.0%'
if (poolId === '1615768079' && tranche.seniority === 1) return '16%'
if (poolId === DYF_POOL_ID) return centrifugeTargetAPYs[DYF_POOL_ID][0]
if (poolId === NS3_POOL_ID && tranche.seniority === 0) return centrifugeTargetAPYs[NS3_POOL_ID][0]
if (poolId === NS3_POOL_ID && tranche.seniority === 1) return centrifugeTargetAPYs[NS3_POOL_ID][1]
if (daysSinceCreation > 30 && tranche.yield30DaysAnnualized)
return formatPercentage(tranche.yield30DaysAnnualized, true, {}, 1)
if (tranche.interestRatePerSec) {
Expand All @@ -165,7 +175,6 @@
const tranchesData = useMemo(() => {
return tranches
?.map((tranche: TrancheWithCurrency) => {
const key = tinlakeObjKey() as TinlakeTranchesKey
const words = tranche.currency.name.trim().split(' ')
const metadata = metaData?.tranches[tranche.id] ?? null
const trancheName = words[words.length - 1]
Expand All @@ -176,16 +185,18 @@

return {
name: trancheName,
apr: isTinlakePool ? tinlakeTranches[key][trancheName as 'Junior' | 'Senior'] : calculateApy(tranche),
apr: isTinlakePool
? tinlakeTranches[tinlakeKey].tranches.find((t) => t.name === trancheName)?.apr
: calculateApy(tranche),
minInvestment: isTinlakePool
? getTinlakeMinInvestment(trancheName as 'Junior' | 'Senior')
? tinlakeTranches[tinlakeKey].tranches.find((t) => t.name === trancheName)?.minInvestment
: metadata && metadata.minInitialInvestment
? `$${formatBalanceAbbreviated(investmentBalance, '', 0)}`
: '-',
}
})
.reverse()
}, [calculateApy, getTinlakeMinInvestment, isTinlakePool, metaData?.tranches, tinlakeObjKey, tranches])
}, [calculateApy, isTinlakePool, metaData?.tranches, tinlakeKey, tranches])

return (
<RouterTextLink to={`${poolId}`} style={{ textDecoration: 'none' }}>
Expand Down Expand Up @@ -240,7 +251,7 @@
)}
<Stack>
<Text as="span" variant="body3" color="textButtonPrimaryDisabled">
{poolId === '1655476167' ? 'Target' : 'APY'}
{poolId === DYF_POOL_ID ? 'Target' : 'APY'}
</Text>
{tranchesData?.map((tranche) => renderText(`${tranche.apr}`, true))}
</Stack>
Expand All @@ -256,7 +267,7 @@
<Box marginY={12}>
<Text as="p" variant="body2" color="textButtonPrimaryDisabled">
{isTinlakePool
? tinlakeTranches[tinlakeObjKey()].shortDescription
? tinlakeTranches[tinlakeKey].shortDescription
: metaData?.pool?.issuer?.shortDescription}
</Text>
</Box>
Expand All @@ -269,7 +280,7 @@
<Text variant="body2">Investor type</Text>
<Text variant="body2">
{' '}
{isTinlakePool ? tinlakeTranches[tinlakeObjKey()].InvestorType : metaData?.pool?.investorType ?? '-'}
{isTinlakePool ? tinlakeTranches[tinlakeKey].investorType : metaData?.pool?.investorType ?? '-'}
</Text>
</Box>
</StyledCard>
Expand Down
Loading
Loading