diff --git a/centrifuge-app/src/components/ListItemCardStyles.tsx b/centrifuge-app/src/components/ListItemCardStyles.tsx
index e06ab21f8..0b1a290d1 100644
--- a/centrifuge-app/src/components/ListItemCardStyles.tsx
+++ b/centrifuge-app/src/components/ListItemCardStyles.tsx
@@ -34,3 +34,7 @@ export const Ellipsis = styled.span`
overflow: hidden;
text-overflow: ellipsis;
`
+export const CardHeader = styled(Box)`
+ display: flex;
+ justify-content: space-between;
+`
diff --git a/centrifuge-app/src/components/PoolCard/PoolStatus.tsx b/centrifuge-app/src/components/PoolCard/PoolStatus.tsx
index ad254c685..ef76f5071 100644
--- a/centrifuge-app/src/components/PoolCard/PoolStatus.tsx
+++ b/centrifuge-app/src/components/PoolCard/PoolStatus.tsx
@@ -3,7 +3,7 @@ import { StatusChip, StatusChipProps } from '@centrifuge/fabric'
export type PoolStatusKey = 'Open for investments' | 'Closed' | 'Upcoming' | 'Archived'
const statusColor: { [key in PoolStatusKey]: StatusChipProps['status'] } = {
- 'Open for investments': 'ok',
+ 'Open for investments': 'warning',
Closed: 'default',
Upcoming: 'default',
Archived: 'default',
diff --git a/centrifuge-app/src/components/PoolCard/index.tsx b/centrifuge-app/src/components/PoolCard/index.tsx
index 1f3e9495f..8befebd01 100644
--- a/centrifuge-app/src/components/PoolCard/index.tsx
+++ b/centrifuge-app/src/components/PoolCard/index.tsx
@@ -1,18 +1,48 @@
-import { useBasePath } from '@centrifuge/centrifuge-app/src/utils/useBasePath'
-import { Rate } from '@centrifuge/centrifuge-js'
-import { Box, Grid, Text, TextWithPlaceholder, Thumbnail } from '@centrifuge/fabric'
+import { CurrencyBalance, Rate } from '@centrifuge/centrifuge-js'
+import { formatBalance } from '@centrifuge/centrifuge-react'
+import { Box, Card, Divider, Stack, Text, Thumbnail } from '@centrifuge/fabric'
import Decimal from 'decimal.js-light'
-import { useTheme } from 'styled-components'
-import { formatBalance, formatPercentage } from '../../utils/formatting'
-import { useIsAboveBreakpoint } from '../../utils/useIsAboveBreakpoint'
+import { useNavigate } from 'react-router'
+import styled from 'styled-components'
+import { formatBalanceAbbreviated, formatPercentage } from '../../utils/formatting'
import { Eththumbnail } from '../EthThumbnail'
-import { Anchor, Ellipsis, Root } from '../ListItemCardStyles'
-import { Tooltips } from '../Tooltips'
+import { CardHeader } from '../ListItemCardStyles'
+import { RouterTextLink } from '../TextLink'
import { PoolStatus, PoolStatusKey } from './PoolStatus'
-const columns_base = 'minmax(150px, 2fr) minmax(100px, 1fr) 140px 70px 150px'
-const columns_extended = 'minmax(200px, 2fr) minmax(100px, 1fr) 140px 100px 150px'
-export const COLUMNS = ['minmax(100px, 1fr) 1fr', 'minmax(100px, 1fr) 1fr', columns_base, columns_extended]
-export const COLUMN_GAPS = [3, 3, 6, 8]
+
+type TrancheData = {
+ name: string
+ apr: string
+ minInvestment: string
+}
+
+export type InnerMetadata = {
+ minInitialInvestment?: CurrencyBalance
+}
+
+export type MetaData = {
+ tranches: {
+ [key: string]: InnerMetadata
+ }
+}
+export type Tranche = {
+ id: string
+ currency: {
+ name: string
+ decimals: CurrencyBalance | number
+ }
+ interestRatePerSec: {
+ toAprPercent: () => Decimal
+ } | null
+ capacity?: CurrencyBalance | number
+ metadata?: MetaData
+}
+
+const StyledRouterTextLink = styled(RouterTextLink)`
+ font-size: 12px;
+ margin-top: 8px;
+ text-decoration: none;
+`
export type PoolCardProps = {
poolId?: string
@@ -23,7 +53,8 @@ export type PoolCardProps = {
apr?: Rate | null | undefined
status?: PoolStatusKey
iconUri?: string
- isLoading?: boolean
+ tranches?: Tranche[]
+ metaData?: MetaData
}
export function PoolCard({
@@ -32,100 +63,116 @@ export function PoolCard({
assetClass,
valueLocked,
currencySymbol,
- apr,
status,
iconUri,
- isLoading,
+ tranches,
+ metaData,
}: PoolCardProps) {
- const isMedium = useIsAboveBreakpoint('M')
- const basePath = useBasePath('/pools')
- const { sizes, zIndices } = useTheme()
-
- return (
-
-
-
-
- {iconUri ? (
-
- ) : (
-
- )}
-
-
-
- {name}
-
-
-
- {isMedium && (
-
- {assetClass}
-
- )}
+ const navigate = useNavigate()
+ const isOneTranche = tranches && tranches?.length === 1
+ const renderText = (text: string) => (
+
+ {text}
+
+ )
-
- {valueLocked ? formatBalance(valueLocked, currencySymbol) : '-'}
-
+ const tranchesData: TrancheData[] = tranches?.map((tranche: Tranche) => {
+ const words = tranche.currency.name.trim().split(' ')
+ const metadata = metaData?.tranches[tranche.id] ?? null
+ const trancheName = words[words.length - 1]
- {isMedium && (
-
-
- {apr ? (
- formatPercentage(apr.toAprPercent(), true, {
- minimumFractionDigits: 1,
- maximumFractionDigits: 1,
- })
- ) : poolId === '4139607887' ? (
-
-
- 5.0%
-
- target{' '}
- >
- }
- />
- ) : poolId === '1655476167' ? (
-
-
- 15.0%
-
- target{' '}
- >
- }
- />
- ) : (
- '—'
- )}
-
- {status === 'Upcoming' && apr ? target : ''}
-
- )}
+ return {
+ name: trancheName,
+ apr: tranche.interestRatePerSec
+ ? formatPercentage(tranche.interestRatePerSec.toAprPercent(), true, {
+ minimumFractionDigits: 1,
+ maximumFractionDigits: 1,
+ })
+ : '-',
+ minInvestment:
+ metadata && metadata.minInitialInvestment
+ ? formatBalanceAbbreviated(metadata.minInitialInvestment, '', 0)
+ : '-',
+ }
+ }) as TrancheData[]
- {isMedium && (
-
-
-
+ return (
+ navigate(`/pools/${poolId}`)}
+ padding={18}
+ style={{ cursor: 'pointer' }}
+ height={320}
+ >
+
+
+
+
+ {name}
+
+
+
+ {iconUri ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ TVL ({currencySymbol})
+
+ {valueLocked ? formatBalance(valueLocked, '') : '-'}
+
+
+ {!isOneTranche && (
+
+
+ Tranches
+
+ {tranchesData?.map((tranche) => renderText(tranche.name))}
+ {tranches && tranches.length > 2 ? (
+ View all
+ ) : null}
+
)}
-
-
- {status === 'Upcoming' ? null : }
-
+
+
+ APY
+
+ {tranchesData?.map((tranche) => renderText(`${tranche.apr}`))}
+
+
+
+ Min Investment
+
+ {tranchesData?.map((tranche) => renderText(`${tranche.minInvestment}`))}
+
+
+
+
+ Fully onchain, BVI-licensed fund holding T-Bills with a maximum maturity of 6 months.
+
+
+
+ Asset Type
+ {assetClass ?? '-'}
+
+
+ Investor Type
+ Non-US professionals
+
+
)
}
diff --git a/centrifuge-app/src/components/PoolList.tsx b/centrifuge-app/src/components/PoolList.tsx
index f0881b5c4..26680ead8 100644
--- a/centrifuge-app/src/components/PoolList.tsx
+++ b/centrifuge-app/src/components/PoolList.tsx
@@ -1,6 +1,6 @@
import Centrifuge, { Pool, PoolMetadata } from '@centrifuge/centrifuge-js'
import { useCentrifuge } from '@centrifuge/centrifuge-react'
-import { Box, Grid, InlineFeedback, Shelf, Stack, Text } from '@centrifuge/fabric'
+import { Box, Shelf, Stack, Text } from '@centrifuge/fabric'
import * as React from 'react'
import { useLocation } from 'react-router'
import styled from 'styled-components'
@@ -9,11 +9,9 @@ import { TinlakePool } from '../utils/tinlake/useTinlakePools'
import { useIsAboveBreakpoint } from '../utils/useIsAboveBreakpoint'
import { useListedPools } from '../utils/useListedPools'
import { useMetadataMulti } from '../utils/useMetadata'
-import { COLUMNS, COLUMN_GAPS, PoolCard, PoolCardProps } from './PoolCard'
+import { MetaData, PoolCard, PoolCardProps, Tranche } from './PoolCard'
import { PoolStatusKey } from './PoolCard/PoolStatus'
-import { PoolFilter } from './PoolFilter'
import { filterPools } from './PoolFilter/utils'
-import { ButtonTextLink } from './TextLink'
export type MetaDataById = Record
export type PoolMetaDataPartial = Partial | undefined
@@ -31,6 +29,7 @@ export function PoolList() {
const { search } = useLocation()
const [showArchived, setShowArchived] = React.useState(false)
const [listedPools, , metadataIsLoading] = useListedPools()
+ const isLarge = useIsAboveBreakpoint('L')
const isMedium = useIsAboveBreakpoint('M')
const centPools = listedPools.filter(({ id }) => !id.startsWith('0x')) as Pool[]
@@ -67,44 +66,40 @@ export function PoolList() {
}
return (
-
-
+
+
-
-
- {!filteredPools.length ? (
-
-
-
- No results found with these filters. Try different filters.
-
-
-
- ) : (
-
- {metadataIsLoading
- ? Array(6)
- .fill(true)
- .map((_, index) => (
-
-
-
- ))
- : filteredPools.map((pool) => (
-
-
-
- ))}
-
- )}
+
+ {metadataIsLoading
+ ? Array(6)
+ .fill(true)
+ .map((_, index) => (
+
+
+
+ ))
+ : filteredPools.map((pool) => (
+
+
+
+ ))}
+
{!metadataIsLoading && archivedPools.length > 0 && (
<>
-
- setShowArchived((show) => !show)}>
- {showArchived ? 'Hide archived pools' : 'View archived pools'}
-
+ setShowArchived((show) => !show)}
+ variant="body2"
+ >
+ {showArchived ? 'Hide archived pools' : 'View archived pools >'}
{showArchived && }
>
@@ -115,39 +110,21 @@ export function PoolList() {
function ArchivedPools({ pools }: { pools: PoolCardProps[] }) {
const isMedium = useIsAboveBreakpoint('M')
-
+ const isLarge = useIsAboveBreakpoint('L')
return (
-
-
- Pool name
-
- {isMedium && (
-
- Asset class
-
- )}
-
- Value locked
-
- {isMedium && (
-
- APR
-
- )}
- {isMedium && (
-
- Pool status
-
- )}
-
-
+
{pools.map((pool) => (
))}
-
+
)
}
@@ -159,7 +136,6 @@ export function poolsToPoolCardProps(
): PoolCardProps[] {
return pools.map((pool) => {
const tinlakePool = pool.id?.startsWith('0x') && (pool as TinlakePool)
- const mostSeniorTranche = pool?.tranches?.slice(1).at(-1)
const metaData = typeof pool.metadata === 'string' ? metaDataById[pool.id] : pool.metadata
return {
@@ -168,16 +144,17 @@ export function poolsToPoolCardProps(
assetClass: metaData?.pool?.asset.subClass,
valueLocked: getPoolValueLocked(pool),
currencySymbol: pool.currency.symbol,
- apr: mostSeniorTranche?.interestRatePerSec,
status:
tinlakePool && tinlakePool.tinlakeMetadata.isArchived
? 'Archived'
: tinlakePool && tinlakePool.addresses.CLERK !== undefined && tinlakePool.tinlakeMetadata.maker?.ilk
? 'Closed'
- : pool.tranches.at(0)?.capacity.toFloat() // pool is displayed as "open for investments" if the most junior tranche has a capacity
+ : pool.tranches.at(0)?.capacity?.toFloat() // pool is displayed as "open for investments" if the most junior tranche has a capacity
? 'Open for investments'
: ('Closed' as PoolStatusKey),
iconUri: metaData?.pool?.icon?.uri ? cent.metadata.parseMetadataUrl(metaData?.pool?.icon?.uri) : undefined,
+ tranches: pool.tranches as Tranche[],
+ metaData: metaData as MetaData,
}
})
}
diff --git a/centrifuge-app/src/pages/Pools.tsx b/centrifuge-app/src/pages/Pools.tsx
index 5bb1574a8..cd7f41d52 100644
--- a/centrifuge-app/src/pages/Pools.tsx
+++ b/centrifuge-app/src/pages/Pools.tsx
@@ -33,8 +33,8 @@ export default function PoolsPage() {
}, [])
return (
-
-
+
+
Pools of real-world assets
diff --git a/fabric/src/theme/tokens/colors.ts b/fabric/src/theme/tokens/colors.ts
index 5cfa4064e..4db028f5a 100644
--- a/fabric/src/theme/tokens/colors.ts
+++ b/fabric/src/theme/tokens/colors.ts
@@ -14,7 +14,7 @@ export const yellowScale = {
50: '#FFF4D9',
100: '#FFE299',
500: '#FFC012',
- 800: '#CC9700',
+ 800: '#6B4F00',
}
export const blackScale = {
diff --git a/fabric/src/theme/tokens/theme.ts b/fabric/src/theme/tokens/theme.ts
index c9d0ed054..c9b9bd342 100644
--- a/fabric/src/theme/tokens/theme.ts
+++ b/fabric/src/theme/tokens/theme.ts
@@ -3,7 +3,7 @@ import { black, blackScale, centrifugeBlue, gold, grayScale, yellowScale } from
const statusDefault = grayScale[800]
const statusInfo = '#1253ff'
const statusOk = '#519b10'
-const statusWarning = yellowScale[500]
+const statusWarning = yellowScale[800]
const statusCritical = '#d43f2b'
const statusPromote = '#f81071'
@@ -31,7 +31,7 @@ const colors = {
backgroundThumbnail: grayScale[500],
backgroundInverted: grayScale[900],
- borderPrimary: grayScale[100],
+ borderPrimary: grayScale[300],
borderSecondary: grayScale[300],
statusDefault,