diff --git a/centrifuge-app/src/components/IssuerSection.tsx b/centrifuge-app/src/components/IssuerSection.tsx index a714435a0e..0af8d87dba 100644 --- a/centrifuge-app/src/components/IssuerSection.tsx +++ b/centrifuge-app/src/components/IssuerSection.tsx @@ -182,13 +182,13 @@ export function IssuerDetails({ metadata }: IssuerSectionProps) { {metadata?.pool?.issuer?.categories?.length ? ( - + {metadata?.pool?.issuer?.categories.map((category) => ( - + {formatCamelCase(category.customType) || formatCamelCase(category.type)} - + {category.type.includes('Rate') ? formatPercentage(category.value) : category.value} diff --git a/centrifuge-app/src/components/PoolCard/index.tsx b/centrifuge-app/src/components/PoolCard/index.tsx index 0faa0ccd67..22b91605a9 100644 --- a/centrifuge-app/src/components/PoolCard/index.tsx +++ b/centrifuge-app/src/components/PoolCard/index.tsx @@ -36,8 +36,7 @@ const StyledRouterTextLink = styled(RouterTextLink)` const StyledCard = styled(Card)` width: 100%; max-width: 100%; - height: 300px; - + height: 320px; margin-right: 12px; margin-bottom: 12px; padding: 12px; @@ -72,7 +71,8 @@ const tinlakeTranches = { { 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 US and non-US investors', + assetType: 'Residential real estate', }, BT3: { name: 'BlockTower Series 3', @@ -82,6 +82,7 @@ const tinlakeTranches = { ], shortDescription: ' Investment-grade consumer ABS, auto ABS, and CLOs under 4 years.', investorType: 'Private', + assetType: 'Structured Credit', }, BT4: { name: 'BlockTower Series 4', @@ -91,6 +92,7 @@ const tinlakeTranches = { ], shortDescription: 'Investment-grade consumer ABS, auto ABS, and CLOs under 4 years.', investorType: 'Private', + assetType: 'Structured Credit', }, none: { name: '-', @@ -100,16 +102,18 @@ const tinlakeTranches = { ], shortDescription: '', investorType: '-', + assetType: '-', }, } export const DYF_POOL_ID = '1655476167' export const NS3_POOL_ID = '1615768079' +export const NS2 = '0x53b2d22d07E069a3b132BfeaaD275b10273d381E' export type CentrifugeTargetAPYs = keyof typeof centrifugeTargetAPYs export const centrifugeTargetAPYs = { [DYF_POOL_ID]: ['15%'], - [NS3_POOL_ID]: ['8.0%', '16%'], + [NS3_POOL_ID]: ['16%', '8%'], } type TinlakeTranchesKey = keyof typeof tinlakeTranches @@ -148,19 +152,26 @@ export function PoolCard({ (key) => tinlakeTranches[key as TinlakeTranchesKey].name === name ) || 'none') as TinlakeTranchesKey - const renderText = (text: string, isApr?: boolean) => { - if (isApr && poolId === NS3_POOL_ID) { + const renderText = (text: string, isApr?: boolean, seniority?: number) => { + if ( + (isApr && poolId === NS3_POOL_ID) || + (isApr && poolId === DYF_POOL_ID) || + (isApr && isTinlakePool && seniority === 0) + ) { return ( - + {text} - + ) } @@ -196,6 +207,7 @@ export function PoolCard({ ).toDecimal() return { + seniority: tranche.seniority, name: trancheName, apr: isTinlakePool ? tinlakeTranches[tinlakeKey].tranches.find((t) => t.name === trancheName)?.apr @@ -263,12 +275,12 @@ export function PoolCard({ )} - {poolId === DYF_POOL_ID ? 'Target' : 'APY'} + APY - {tranchesData?.map((tranche) => renderText(`${tranche.apr}`, true))} + {tranchesData?.map((tranche) => renderText(`${tranche.apr}`, true, tranche.seniority))} - + Min. investment {tranchesData?.map((tranche) => renderText(`${tranche.minInvestment}`))} @@ -276,19 +288,21 @@ export function PoolCard({ {(metaData?.pool?.issuer?.shortDescription || isTinlakePool) && ( - + {isTinlakePool ? tinlakeTranches[tinlakeKey].shortDescription : metaData?.pool?.issuer?.shortDescription} )} - Asset type - {assetClass ?? '-'} + Asset type + + {isTinlakePool ? tinlakeTranches[tinlakeKey].assetType : assetClass ?? '-'} + - Investor type - + Investor type + {isTinlakePool ? tinlakeTranches[tinlakeKey].investorType : metaData?.pool?.investorType ?? '-'} diff --git a/centrifuge-app/src/components/PoolList.tsx b/centrifuge-app/src/components/PoolList.tsx index 2d4c3b9a74..5113ecced6 100644 --- a/centrifuge-app/src/components/PoolList.tsx +++ b/centrifuge-app/src/components/PoolList.tsx @@ -87,7 +87,7 @@ export function PoolList() { ? Array(6) .fill(true) .map((_, index) => ( - + )) @@ -96,7 +96,7 @@ export function PoolList() { as="li" key={pool.poolId} status={pool.status} - width={isExtraLarge ? '25%' : isLarge ? '33%' : isMedium ? '48%' : '100%'} + width={isLarge ? '33%' : isMedium ? '48%' : '100%'} > @@ -136,7 +136,7 @@ function ArchivedPools({ pools }: { pools: PoolCardProps[] }) { as="li" key={pool.poolId} status={pool.status} - width={isExtraLarge ? '25%' : isLarge ? '33%' : isMedium ? '48%' : '100%'} + width={isLarge ? '33%' : isMedium ? '48%' : '100%'} > diff --git a/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx b/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx index a1e585ff4f..46898995ae 100644 --- a/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx +++ b/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx @@ -1,6 +1,17 @@ import { CurrencyBalance, DailyTrancheState, Price } from '@centrifuge/centrifuge-js' import { NetworkIcon, formatBalanceAbbreviated, useCentrifuge } from '@centrifuge/centrifuge-react' -import { Box, Card, IconArrowRightWhite, IconMoody, IconSp, Shelf, Stack, Text, Tooltip } from '@centrifuge/fabric' +import { + Box, + Card, + IconArrowRightWhite, + IconMoody, + IconParticula, + IconSp, + Shelf, + Stack, + Text, + Tooltip, +} from '@centrifuge/fabric' import capitalize from 'lodash/capitalize' import startCase from 'lodash/startCase' import { useMemo } from 'react' @@ -35,6 +46,11 @@ type Tranche = Pick & { type TinlakeDataKey = keyof typeof tinlakeData +const ratingIcons: { [key: string]: JSX.Element } = { + "Moody's": , + Particula: , +} + const tinlakeData = { '0x53b2d22d07E069a3b132BfeaaD275b10273d381E': '7% - 15%', '0x55d86d51Ac3bcAB7ab7d2124931FbA106c8b60c7': '4% - 15%', @@ -124,7 +140,7 @@ export const KeyMetrics = ({ poolId }: Props) => { value: tinlakeData[poolId as TinlakeDataKey] ? tinlakeData[poolId as TinlakeDataKey] : centrifugeTargetAPYs[poolId as keyof typeof centrifugeTargetAPYs] - ? centrifugeTargetAPYs[poolId as keyof typeof centrifugeTargetAPYs].join(' - ') + ? centrifugeTargetAPYs[poolId as keyof typeof centrifugeTargetAPYs].reverse().join(' - ') : tranchesAPY?.length ? tranchesAPY.map((tranche, index) => { const formatted = formatPercentage(tranche) @@ -176,17 +192,16 @@ export const KeyMetrics = ({ poolId }: Props) => { {metadata?.pool?.poolRatings.map((rating) => ( { borderRadius={20} padding="2px 10px" display="flex" + alignItems="center" + width={80} + justifyContent="center" > - {rating.agency?.includes('moody') ? : } - {rating.value} + {rating.agency && ratingIcons[rating.agency] ? ratingIcons[rating.agency] : } + {rating.value} ))} diff --git a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx index 36596481e6..146a009e43 100644 --- a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx +++ b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx @@ -10,6 +10,7 @@ import { usePool } from '../../utils/usePools' import { DataTable } from '../DataTable' import { CentrifugeTargetAPYs, DYF_POOL_ID, NS3_POOL_ID, centrifugeTargetAPYs } from '../PoolCard' import { PoolMetaDataPartial } from '../PoolList' +import { Tooltips } from '../Tooltips' type Row = { tokenName: string @@ -18,10 +19,9 @@ type Row = { tokenPrice: Decimal subordination: Decimal trancheId: string + isTarget: boolean } -const NS2 = '0x53b2d22d07E069a3b132BfeaaD275b10273d381E' - export const TrancheTokenCards = ({ trancheTokens, poolId, @@ -34,6 +34,7 @@ export const TrancheTokenCards = ({ const pool = usePool(poolId) const theme = useTheme() const isTinlakePool = poolId.startsWith('0x') + const daysSinceCreation = pool?.createdAt ? daysBetween(new Date(pool.createdAt), new Date()) : 0 const getTrancheText = (trancheToken: Token) => { if (trancheToken.seniority === 0) return 'junior' @@ -43,19 +44,21 @@ export const TrancheTokenCards = ({ const calculateApy = (trancheToken: Token) => { if (isTinlakePool && getTrancheText(trancheToken) === 'senior') return formatPercentage(trancheToken.apy) - if (poolId === NS2 && trancheToken.seniority === 0) return '15%' + if (isTinlakePool && trancheToken.seniority === 0) return '15%' if (poolId === DYF_POOL_ID) return centrifugeTargetAPYs[poolId as CentrifugeTargetAPYs][0] if (poolId === NS3_POOL_ID && trancheToken.seniority === 0) return centrifugeTargetAPYs[poolId as CentrifugeTargetAPYs][0] if (poolId === NS3_POOL_ID && trancheToken.seniority === 1) return centrifugeTargetAPYs[poolId as CentrifugeTargetAPYs][1] - const daysSinceCreation = pool?.createdAt ? daysBetween(new Date(pool.createdAt), new Date()) : 0 if (daysSinceCreation < 30) return 'N/A' return trancheToken.yield30DaysAnnualized ? formatPercentage(new Perquintill(trancheToken.yield30DaysAnnualized)) : '-' } + const getTarget = (tranche: Token) => + (isTinlakePool && tranche.seniority === 0) || poolId === DYF_POOL_ID || poolId === NS3_POOL_ID + const columns = useMemo(() => { return [ { @@ -71,13 +74,16 @@ export const TrancheTokenCards = ({ }, }, { - header: poolId === DYF_POOL_ID || poolId === NS3_POOL_ID ? 'Target' : 'APY', + header: 'APY', align: 'left', cell: (row: Row) => { return ( - - {row.apy} - + + + {row.apy} + + {row.isTarget && } + ) }, }, @@ -137,9 +143,10 @@ export const TrancheTokenCards = ({ tokenPrice: tranche.tokenPrice, subordination: tranche.protection, trancheId: tranche.id, + isTarget: getTarget(tranche), } }) - }, [trancheTokens]) + }, [trancheTokens, getTarget]) return ( diff --git a/centrifuge-app/src/pages/Pool/Overview/index.tsx b/centrifuge-app/src/pages/Pool/Overview/index.tsx index 1b9e43283d..ffcc1edaf5 100644 --- a/centrifuge-app/src/pages/Pool/Overview/index.tsx +++ b/centrifuge-app/src/pages/Pool/Overview/index.tsx @@ -156,15 +156,16 @@ export function InvestButton(props: InvestRedeemProps) { const connectAndOpen = useConnectBeforeAction(() => setOpen(true)) const { connectedType, showNetworks } = useWallet() const navigate = useNavigate() + const isTinlakePool = poolId.startsWith('0x') const getButtonText = (state: any) => { - if (!state.isAllowedToInvest && connectedType !== null) { + if (!state.isAllowedToInvest && connectedType !== null && !isTinlakePool) { return 'Onboard' - } else if (connectedType === null) { + } else if (connectedType === null && !isTinlakePool) { return 'Connect' - } else { - return state.isFirstInvestment ? 'Invest' : 'Invest/Redeem' - } + } else if (isTinlakePool) { + return 'Closed' + } else return state.isFirstInvestment ? 'Invest' : 'Invest/Redeem' } return ( @@ -193,6 +194,7 @@ export function InvestButton(props: InvestRedeemProps) { }} variant="primary" loading={isLoading} + disabled={isTinlakePool} > {buttonText} diff --git a/fabric/src/icon-svg/Icon-particula.svg b/fabric/src/icon-svg/Icon-particula.svg new file mode 100644 index 0000000000..3e01504980 --- /dev/null +++ b/fabric/src/icon-svg/Icon-particula.svg @@ -0,0 +1,10 @@ + + + + + + + + + +