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

add new sdk to current app #2579

Open
wants to merge 8 commits into
base: reports
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion centrifuge-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@centrifuge/centrifuge-js": "workspace:*",
"@centrifuge/centrifuge-react": "workspace:*",
"@centrifuge/fabric": "workspace:*",
"@centrifuge/sdk": "^0.0.0-alpha.0",
"@centrifuge/sdk": "latest",
"@makerdao/multicall": "^0.12.0",
"@polkadot/react-identicon": "~3.1.4",
"@styled-system/css": "^5.1.5",
Expand Down
26 changes: 14 additions & 12 deletions centrifuge-app/src/components/Charts/SimpleBarChart.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { CurrencyBalance, CurrencyMetadata } from '@centrifuge/centrifuge-js'
import { Shelf, Text } from '@centrifuge/fabric'
import { CurrencyMetadata } from '@centrifuge/centrifuge-js'
import { Box, Shelf, Text } from '@centrifuge/fabric'
import { Currency } from '@centrifuge/sdk'
import { Bar, BarChart, CartesianGrid, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'
import { useTheme } from 'styled-components'
import { formatDate } from '../../../src/utils/date'
import { formatBalance, formatBalanceAbbreviated } from '../../../src/utils/formatting'
import { formatDecimal, formatDecimalAbbreviated } from '../../../src/utils/formatting'
import { LoadBoundary } from '../LoadBoundary'
import { CustomTick } from './PoolPerformanceChart'
import { TooltipContainer, TooltipTitle } from './Tooltip'

type SimpleBarChartProps = {
currency?: CurrencyMetadata
data: { name: string; yAxis: number }[]
data: { name: string; yAxis: Currency }[]
groupBy?: string
}

Expand Down Expand Up @@ -62,10 +63,7 @@ export const SimpleBarChart = ({ currency, data, groupBy }: SimpleBarChartProps)
angle={45}
/>
<YAxis
tickFormatter={(tick: number) => {
const balance = new CurrencyBalance(tick, currency?.decimals || 0)
return formatBalanceAbbreviated(balance, '', 0)
}}
tickFormatter={(tick: any) => formatDecimalAbbreviated(tick, 0)}
tick={{ fontSize: 10, color: theme.colors.textPrimary }}
tickLine={false}
axisLine={false}
Expand All @@ -79,10 +77,14 @@ export const SimpleBarChart = ({ currency, data, groupBy }: SimpleBarChartProps)
if (payload && payload?.length > 0) {
return (
<TooltipContainer>
<TooltipTitle>{formatDate(payload[0].payload.name)}</TooltipTitle>
{payload.map((item) => (
<Text variant="body3">{formatBalance(item.value as number, currency)}</Text>
))}
{payload.map((item) => {
return (
<Box>
<TooltipTitle>{formatDate(item.payload.name)}</TooltipTitle>
<Text variant="body3">{formatDecimal(item.value, 2, currency?.displayName)}</Text>
</Box>
)
})}
</TooltipContainer>
)
}
Expand Down
138 changes: 81 additions & 57 deletions centrifuge-app/src/components/Report/AssetList.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import { Pool } from '@centrifuge/centrifuge-js'
import { Box, Text } from '@centrifuge/fabric'
import {
AssetListReport,
AssetListReportPrivateCredit,
AssetListReportPublicCredit,
} from '@centrifuge/sdk/dist/types/reports'
import { useContext, useEffect, useMemo } from 'react'
import { useBasePath } from '../../../src/utils/useBasePath'
import { formatDate } from '../../utils/date'
import { formatBalance, formatPercentage } from '../../utils/formatting'
import { formatDecimal, formatPercentage } from '../../utils/formatting'
import { getCSVDownloadUrl } from '../../utils/getCSVDownloadUrl'
import { useAllPoolAssetSnapshots, usePoolMetadata } from '../../utils/usePools'
import { usePoolMetadata } from '../../utils/usePools'
import { DataTable, SortableTableHeader } from '../DataTable'
import { Spinner } from '../Spinner'
import { RouterTextLink } from '../TextLink'
import { ReportContext } from './ReportContext'
import { UserFeedback } from './UserFeedback'
import type { TableDataRow } from './index'
import { useReport } from './useReportsQuery'

const noop = (v: any) => v

const valuationLabels = {

Check warning on line 24 in centrifuge-app/src/components/Report/AssetList.tsx

View workflow job for this annotation

GitHub Actions / ff-prod / build-app

'valuationLabels' is assigned a value but never used

Check warning on line 24 in centrifuge-app/src/components/Report/AssetList.tsx

View workflow job for this annotation

GitHub Actions / build-app

'valuationLabels' is assigned a value but never used
discountedCashFlow: 'Non-fungible asset - DCF',
outstandingDebt: 'Non-fungible asset - at par',
oracle: 'Fungible asset - external pricing',
}

function getColumnConfig(isPrivate: boolean, symbol: string) {
type AssetSnapshot = AssetListReport & {
transactionType: 'ACTIVE' | string
name: string
}

function getColumnConfig(isPrivate: boolean, symbol: string, decimals: number) {
if (isPrivate) {
return [
{ header: 'Name', align: 'left', csvOnly: false, formatter: noop },
Expand All @@ -30,42 +41,42 @@
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Principal outstanding',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Interest outstanding',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Principal repaid',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Interest repaid',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Additional repaid',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Origination date',
Expand Down Expand Up @@ -98,25 +109,25 @@
header: 'Collateral value',
align: 'left',
csvOnly: false,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Probability of default (PD)',
align: 'left',
csvOnly: false,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Loss given default (LGD)',
align: 'left',
csvOnly: false,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Discount rate',
align: 'left',
csvOnly: false,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
]
} else {
Expand All @@ -127,28 +138,28 @@
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Face value',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Quantity',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, undefined, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, '') : '-'),
},
{
header: 'Market price',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Maturity date',
Expand All @@ -162,28 +173,38 @@
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
{
header: 'Realized profit',
align: 'left',
csvOnly: false,
sortable: true,
formatter: (v: any) => (v ? formatBalance(v, symbol, 2) : '-'),
formatter: (v: any) => (v ? formatDecimal(v, 2, symbol) : '-'),
},
]
}
}

export function AssetList({ pool }: { pool: Pool }) {
const basePath = useBasePath()
const { loanStatus, startDate, setCsvData } = useContext(ReportContext)
const { loanStatus, startDate, setCsvData, endDate } = useContext(ReportContext)
const { data: poolMetadata } = usePoolMetadata(pool)
const { symbol } = pool.currency
const { symbol, decimals } = pool.currency
const poolCreditType = poolMetadata?.pool?.asset.class || 'privateCredit'
const { data: snapshots } = useAllPoolAssetSnapshots(pool.id, startDate)
const isPrivate = poolCreditType === 'Private credit' || poolCreditType === 'privateCredit'
const columnConfig = getColumnConfig(isPrivate, symbol)
const columnConfig = getColumnConfig(isPrivate, symbol, decimals)

const { data: snapshots = [], isLoading } = useReport(
'assetList',
pool,
new Date(startDate),
new Date(endDate),
undefined,
{
...(loanStatus && { status: loanStatus }),
}
)

const columns = useMemo(
() =>
Expand All @@ -210,19 +231,20 @@
.filter((col) => !col.csvOnly),
[columnConfig, basePath, pool.id]
)

const data = useMemo((): any[] => {
if (!snapshots) return []

return snapshots
.filter((snapshot) => snapshot?.valuationMethod?.toLowerCase() !== 'cash')
return (snapshots as AssetSnapshot[])
.filter((snapshot) =>
isPrivate ? 'valuationMethod' in snapshot && snapshot?.valuationMethod?.toLowerCase() !== 'cash' : true
)
.filter((snapshot) => {
const isMaturityDatePassed = snapshot?.actualMaturityDate
? new Date() > new Date(snapshot.actualMaturityDate)
: false
const isDebtZero = snapshot?.outstandingDebt?.isZero()
const isMaturityDatePassed = snapshot?.maturityDate ? new Date() > new Date(snapshot.maturityDate) : false
const isDebtZero = 'outstandingQuantity' in snapshot ? snapshot.outstandingQuantity?.isZero() : false

if (loanStatus === 'ongoing') {
return snapshot.status === 'ACTIVE' && !isMaturityDatePassed && !isDebtZero
return snapshot.transactionType === 'ACTIVE' && !isMaturityDatePassed && !isDebtZero
} else if (loanStatus === 'repaid') {
return isMaturityDatePassed && isDebtZero
} else if (loanStatus === 'overdue') {
Expand All @@ -231,47 +253,49 @@
})
.sort((a, b) => {
// Sort by actualMaturityDate in descending order
const dateA = new Date(a.actualMaturityDate || 0).getTime()
const dateB = new Date(b.actualMaturityDate || 0).getTime()
const dateA = new Date(a.maturityDate || 0).getTime()
const dateB = new Date(b.maturityDate || 0).getTime()
return dateB - dateA
})
.map((snapshot) => {
const valuationMethod = snapshot?.valuationMethod as keyof typeof valuationLabels
if (isPrivate) {
const privateSnapshot = snapshot as AssetSnapshot & AssetListReportPrivateCredit
return {
name: '',
value: [
snapshot?.name,
snapshot?.presentValue,
snapshot?.outstandingPrincipal,
snapshot?.outstandingInterest,
snapshot?.totalRepaidPrincipal,
snapshot?.totalRepaidInterest,
snapshot?.totalRepaidUnscheduled,
snapshot?.actualOriginationDate,
snapshot?.actualMaturityDate,
valuationMethod || snapshot?.valuationMethod,
snapshot?.advanceRate,
snapshot?.collateralValue,
snapshot?.probabilityOfDefault,
snapshot?.lossGivenDefault,
snapshot?.discountRate,
privateSnapshot.name,
privateSnapshot.presentValue,
privateSnapshot.outstandingPrincipal,
privateSnapshot.outstandingInterest,
privateSnapshot.repaidPrincipal,
privateSnapshot.repaidInterest,
privateSnapshot.repaidUnscheduled,
privateSnapshot.originationDate,
privateSnapshot.maturityDate,
privateSnapshot.valuationMethod,
privateSnapshot.advanceRate,
privateSnapshot.collateralValue,
privateSnapshot.probabilityOfDefault,
privateSnapshot.lossGivenDefault,
privateSnapshot.discountRate,
],
heading: false,
id: snapshot?.assetId,
}
} else {
const publicSnapshot = snapshot as AssetSnapshot & AssetListReportPublicCredit

return {
name: '',
value: [
snapshot?.name,
snapshot?.presentValue,
snapshot?.faceValue,
snapshot?.outstandingQuantity,
snapshot?.currentPrice,
snapshot?.actualMaturityDate,
snapshot?.unrealizedProfitAtMarketPrice,
snapshot?.sumRealizedProfitFifo,
publicSnapshot.name,
publicSnapshot.presentValue,
publicSnapshot.faceValue,
publicSnapshot.outstandingQuantity,
publicSnapshot.currentPrice,
publicSnapshot.maturityDate,
publicSnapshot.unrealizedProfit,
publicSnapshot.realizedProfit,
],
heading: false,
id: snapshot?.assetId,
Expand Down Expand Up @@ -304,7 +328,7 @@
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data])

if (!snapshots) {
if (isLoading) {
return <Spinner />
}

Expand Down
Loading
Loading