Skip to content

Commit

Permalink
Redesign: Pool ratings support multiple and pdf (#2493)
Browse files Browse the repository at this point in the history
* Update inputs, UI, and types to support multiple ratings

* Add report pdf support

* Undo

* Improve rendering of multiple ratings

* Fix editing file upload

* Clean up tinlake data

* Fix link to internal onboarding if no external URL is provided
  • Loading branch information
sophialittlejohn authored Oct 15, 2024
1 parent 466b704 commit 51d7612
Show file tree
Hide file tree
Showing 10 changed files with 333 additions and 192 deletions.
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 @@ export type MetaData = {
}
}

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 StyledCard = styled(Card)`
`

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 @@ export function PoolCard({
}: 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 @@ -151,9 +161,9 @@ export function PoolCard({

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 / 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

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

View workflow job for this annotation

GitHub Actions / deploy-development / webapp / 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 / app-demo / 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 @@ export function PoolCard({
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 @@ export function PoolCard({

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 @@ export function PoolCard({
)}
<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 @@ export function PoolCard({
<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 @@ export function PoolCard({
<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

0 comments on commit 51d7612

Please sign in to comment.