From 827124f973a4788bcc1f918e417fd60b22b5649c Mon Sep 17 00:00:00 2001
From: Jeroen <1748621+hieronx@users.noreply.github.com>
Date: Wed, 21 Feb 2024 16:21:43 +0100
Subject: [PATCH 1/3] fix: subquery asset renamings (#1970)
* Subquery asset renames
* Update prod endpoint
---
centrifuge-app/.env-config/.env.production | 2 +-
.../Portfolio/TransactionTypeChip.tsx | 4 ++--
.../src/components/Portfolio/Transactions.tsx | 4 ++--
.../components/Report/AssetTransactions.tsx | 2 +-
.../src/components/Report/utils.tsx | 12 +++++-----
.../src/pages/Loan/HoldingsValues.tsx | 4 ++--
.../src/pages/Loan/PricingValues.tsx | 2 +-
.../src/pages/Loan/TransactionTable.tsx | 12 +++++-----
centrifuge-app/src/utils/getLatestPrice.ts | 4 ++--
centrifuge-app/src/utils/usePools.ts | 12 +++++-----
centrifuge-js/src/modules/pools.ts | 22 +++++++++----------
centrifuge-js/src/types/subquery.ts | 10 ++++-----
12 files changed, 45 insertions(+), 45 deletions(-)
diff --git a/centrifuge-app/.env-config/.env.production b/centrifuge-app/.env-config/.env.production
index 0f028d5aa4..6635e88113 100644
--- a/centrifuge-app/.env-config/.env.production
+++ b/centrifuge-app/.env-config/.env.production
@@ -9,7 +9,7 @@ REACT_APP_ONBOARDING_API_URL=https://europe-central2-centrifuge-production-x.clo
REACT_APP_PINNING_API_URL=https://europe-central2-centrifuge-production-x.cloudfunctions.net/pinning-api-production
REACT_APP_POOL_CREATION_TYPE=propose
REACT_APP_RELAY_WSS_URL=wss://rpc.polkadot.io
-REACT_APP_SUBQUERY_URL=https://api.subquery.network/sq/centrifuge/pools-centrifuge
+REACT_APP_SUBQUERY_URL=https://api.subquery.network/sq/centrifuge/pools
REACT_APP_SUBSCAN_URL=https://centrifuge.subscan.io
REACT_APP_TINLAKE_NETWORK=mainnet
REACT_APP_INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
diff --git a/centrifuge-app/src/components/Portfolio/TransactionTypeChip.tsx b/centrifuge-app/src/components/Portfolio/TransactionTypeChip.tsx
index ad57d28fcc..b44e886c39 100644
--- a/centrifuge-app/src/components/Portfolio/TransactionTypeChip.tsx
+++ b/centrifuge-app/src/components/Portfolio/TransactionTypeChip.tsx
@@ -1,10 +1,10 @@
-import { BorrowerTransactionType, InvestorTransactionType } from '@centrifuge/centrifuge-js'
+import { AssetTransactionType, InvestorTransactionType } from '@centrifuge/centrifuge-js'
import { StatusChip } from '@centrifuge/fabric'
import * as React from 'react'
import { formatTransactionsType } from '../Report/utils'
type TransactionTypeProps = {
- type: InvestorTransactionType | BorrowerTransactionType
+ type: InvestorTransactionType | AssetTransactionType
trancheTokenSymbol: string
poolCurrencySymbol: string
currencyAmount: number | null
diff --git a/centrifuge-app/src/components/Portfolio/Transactions.tsx b/centrifuge-app/src/components/Portfolio/Transactions.tsx
index cb790ba3ef..e5dc3170be 100644
--- a/centrifuge-app/src/components/Portfolio/Transactions.tsx
+++ b/centrifuge-app/src/components/Portfolio/Transactions.tsx
@@ -1,4 +1,4 @@
-import { BorrowerTransactionType, InvestorTransactionType, Pool, Token, TokenBalance } from '@centrifuge/centrifuge-js'
+import { AssetTransactionType, InvestorTransactionType, Pool, Token, TokenBalance } from '@centrifuge/centrifuge-js'
import { formatBalance } from '@centrifuge/centrifuge-react'
import {
AnchorButton,
@@ -30,7 +30,7 @@ type TransactionsProps = {
}
type Row = {
- action: InvestorTransactionType | BorrowerTransactionType
+ action: InvestorTransactionType | AssetTransactionType
date: number
tranche?: Token
tranchePrice: string
diff --git a/centrifuge-app/src/components/Report/AssetTransactions.tsx b/centrifuge-app/src/components/Report/AssetTransactions.tsx
index 8f1c9565fc..83cc9a11e9 100644
--- a/centrifuge-app/src/components/Report/AssetTransactions.tsx
+++ b/centrifuge-app/src/components/Report/AssetTransactions.tsx
@@ -26,7 +26,7 @@ export function AssetTransactions({ pool }: { pool: Pool }) {
return transactions?.map((tx) => ({
name: '',
value: [
- tx.loanId.split('-').at(-1)!,
+ tx.assetId.split('-').at(-1)!,
tx.epochId.split('-').at(-1)!,
formatDate(tx.timestamp.toString()),
formatAssetTransactionType(tx.type),
diff --git a/centrifuge-app/src/components/Report/utils.tsx b/centrifuge-app/src/components/Report/utils.tsx
index 8ccefab7ff..637bf067e7 100644
--- a/centrifuge-app/src/components/Report/utils.tsx
+++ b/centrifuge-app/src/components/Report/utils.tsx
@@ -1,4 +1,4 @@
-import { BorrowerTransactionType, InvestorTransactionType } from '@centrifuge/centrifuge-js/dist/types/subquery'
+import { AssetTransactionType, InvestorTransactionType } from '@centrifuge/centrifuge-js/dist/types/subquery'
import { Text } from '@centrifuge/fabric'
import { copyToClipboard } from '../../utils/copyToClipboard'
import { truncate } from '../../utils/web3'
@@ -48,7 +48,7 @@ export function formatInvestorTransactionsType({
}
const assetTransactionTypes: {
- [key in BorrowerTransactionType]: string
+ [key in AssetTransactionType]: string
} = {
CREATED: 'Created',
PRICED: 'Priced',
@@ -57,9 +57,9 @@ const assetTransactionTypes: {
CLOSED: 'Closed',
}
-export function formatAssetTransactionType(type: BorrowerTransactionType) {
+export function formatAssetTransactionType(type: AssetTransactionType) {
if (!assetTransactionTypes[type]) {
- console.warn(`Type '${type}' is not assignable to type 'BorrowerTransactionType'`)
+ console.warn(`Type '${type}' is not assignable to type 'AssetTransactionType'`)
return type
}
@@ -72,7 +72,7 @@ export function formatTransactionsType({
poolCurrencySymbol,
currencyAmount,
}: {
- type: InvestorTransactionType | BorrowerTransactionType
+ type: InvestorTransactionType | AssetTransactionType
trancheTokenSymbol: string
poolCurrencySymbol: string
currencyAmount: number | null
@@ -89,7 +89,7 @@ export function formatTransactionsType({
})
}
-function isAssetType(type: InvestorTransactionType | BorrowerTransactionType): type is BorrowerTransactionType {
+function isAssetType(type: InvestorTransactionType | AssetTransactionType): type is AssetTransactionType {
return ['CREATED', 'PRICED', 'BORROWED', 'REPAID', 'CLOSED'].includes(type)
}
diff --git a/centrifuge-app/src/pages/Loan/HoldingsValues.tsx b/centrifuge-app/src/pages/Loan/HoldingsValues.tsx
index 2c46e9680b..3b1234e80b 100644
--- a/centrifuge-app/src/pages/Loan/HoldingsValues.tsx
+++ b/centrifuge-app/src/pages/Loan/HoldingsValues.tsx
@@ -1,4 +1,4 @@
-import { BorrowerTransaction, CurrencyBalance, ExternalPricingInfo, Pool } from '@centrifuge/centrifuge-js'
+import { AssetTransaction, CurrencyBalance, ExternalPricingInfo, Pool } from '@centrifuge/centrifuge-js'
import Decimal from 'decimal.js-light'
import { LabelValueStack } from '../../components/LabelValueStack'
import { Dec } from '../../utils/Decimal'
@@ -6,7 +6,7 @@ import { formatBalance } from '../../utils/formatting'
type Props = {
pool: Pool
- transactions?: BorrowerTransaction[] | null
+ transactions?: AssetTransaction[] | null
currentFace: Decimal | null
pricing: ExternalPricingInfo
}
diff --git a/centrifuge-app/src/pages/Loan/PricingValues.tsx b/centrifuge-app/src/pages/Loan/PricingValues.tsx
index fc7eb18217..345156f792 100644
--- a/centrifuge-app/src/pages/Loan/PricingValues.tsx
+++ b/centrifuge-app/src/pages/Loan/PricingValues.tsx
@@ -27,7 +27,7 @@ export function PricingValues({ loan, pool }: Props) {
const days = getAge(new Date(pricing.oracle.timestamp).toISOString())
const borrowerAssetTransactions = assetTransactions?.filter(
- (borrowerTransaction) => borrowerTransaction.loanId === `${loan.poolId}-${loan.id}`
+ (assetTransaction) => assetTransaction.loanId === `${loan.poolId}-${loan.id}`
)
const latestPrice = getLatestPrice(pricing.oracle.value, borrowerAssetTransactions, pool.currency.decimals)
diff --git a/centrifuge-app/src/pages/Loan/TransactionTable.tsx b/centrifuge-app/src/pages/Loan/TransactionTable.tsx
index a3a7e59d4f..0c6f7c0aa1 100644
--- a/centrifuge-app/src/pages/Loan/TransactionTable.tsx
+++ b/centrifuge-app/src/pages/Loan/TransactionTable.tsx
@@ -1,5 +1,5 @@
-import { BorrowerTransaction, CurrencyBalance, ExternalPricingInfo, PricingInfo } from '@centrifuge/centrifuge-js'
-import { BorrowerTransactionType } from '@centrifuge/centrifuge-js/dist/types/subquery'
+import { AssetTransaction, CurrencyBalance, ExternalPricingInfo, PricingInfo } from '@centrifuge/centrifuge-js'
+import { AssetTransactionType } from '@centrifuge/centrifuge-js/dist/types/subquery'
import { StatusChip, Tooltip } from '@centrifuge/fabric'
import BN from 'bn.js'
import { useMemo } from 'react'
@@ -9,7 +9,7 @@ import { Dec } from '../../utils/Decimal'
import { formatBalance } from '../../utils/formatting'
type Props = {
- transactions: BorrowerTransaction[]
+ transactions: AssetTransaction[]
currency: string
decimals: number
loanType: 'external' | 'internal'
@@ -76,13 +76,13 @@ export const TransactionTable = ({ transactions, currency, loanType, decimals, p
}))
}, [transactions, decimals, pricing])
- const getStatusChipType = (type: BorrowerTransactionType) => {
+ const getStatusChipType = (type: AssetTransactionType) => {
if (type === 'BORROWED' || type === 'CREATED' || type === 'PRICED') return 'info'
if (type === 'REPAID') return 'ok'
return 'default'
}
- const getStatusText = (type: BorrowerTransactionType) => {
+ const getStatusText = (type: AssetTransactionType) => {
if (loanType === 'external' && type === 'BORROWED') return 'Purchase'
if (loanType === 'external' && type === 'REPAID') return 'Sale'
@@ -99,7 +99,7 @@ export const TransactionTable = ({ transactions, currency, loanType, decimals, p
{
align: 'left',
header: 'Type',
- cell: (row: { type: BorrowerTransactionType }) => (
+ cell: (row: { type: AssetTransactionType }) => (
{getStatusText(row.type)}
),
},
diff --git a/centrifuge-app/src/utils/getLatestPrice.ts b/centrifuge-app/src/utils/getLatestPrice.ts
index 8fc2aa54cf..dbb102c8f1 100644
--- a/centrifuge-app/src/utils/getLatestPrice.ts
+++ b/centrifuge-app/src/utils/getLatestPrice.ts
@@ -1,8 +1,8 @@
-import { BorrowerTransaction, CurrencyBalance } from '@centrifuge/centrifuge-js'
+import { AssetTransaction, CurrencyBalance } from '@centrifuge/centrifuge-js'
export const getLatestPrice = (
oracleValue: CurrencyBalance,
- borrowerAssetTransactions: BorrowerTransaction[] | undefined,
+ borrowerAssetTransactions: AssetTransaction[] | undefined,
decimals: number
) => {
if (!borrowerAssetTransactions) return null
diff --git a/centrifuge-app/src/utils/usePools.ts b/centrifuge-app/src/utils/usePools.ts
index 4fc14a1b87..f54997748b 100644
--- a/centrifuge-app/src/utils/usePools.ts
+++ b/centrifuge-app/src/utils/usePools.ts
@@ -1,4 +1,4 @@
-import Centrifuge, { addressToHex, BorrowerTransaction, Loan, Pool, PoolMetadata } from '@centrifuge/centrifuge-js'
+import Centrifuge, { addressToHex, AssetTransaction, Loan, Pool, PoolMetadata } from '@centrifuge/centrifuge-js'
import { useCentrifugeApi, useCentrifugeConsts, useCentrifugeQuery, useWallet } from '@centrifuge/centrifuge-react'
import BN from 'bn.js'
import { useEffect, useMemo } from 'react'
@@ -83,7 +83,7 @@ export function useInvestorTransactions(poolId: string, trancheId?: string, from
export function useAssetTransactions(poolId: string, from?: Date, to?: Date) {
const [result] = useCentrifugeQuery(
['assetTransactions', poolId, from, to],
- (cent) => cent.pools.getBorrowerTransactions([poolId, from, to]),
+ (cent) => cent.pools.getAssetTransactions([poolId, from, to]),
{
enabled: !poolId.startsWith('0x'),
}
@@ -113,11 +113,11 @@ export function useBorrowerAssetTransactions(poolId: string, assetId: string, fr
const [result] = useCentrifugeQuery(
['borrowerAssetTransactions', poolId, assetId, from, to],
(cent) => {
- const borrowerTransactions = cent.pools.getBorrowerTransactions([poolId, from, to])
+ const assetTransactions = cent.pools.getAssetTransactions([poolId, from, to])
- return borrowerTransactions.pipe(
- map((transactions: BorrowerTransaction[]) =>
- transactions.filter((transaction) => transaction.loanId.split('-')[1] === assetId)
+ return assetTransactions.pipe(
+ map((transactions: AssetTransaction[]) =>
+ transactions.filter((transaction) => transaction.assetId.split('-')[1] === assetId)
)
)
},
diff --git a/centrifuge-js/src/modules/pools.ts b/centrifuge-js/src/modules/pools.ts
index 54a3af8cc6..e9bf8c6fe9 100644
--- a/centrifuge-js/src/modules/pools.ts
+++ b/centrifuge-js/src/modules/pools.ts
@@ -10,9 +10,9 @@ import { SolverResult, calculateOptimalSolution } from '..'
import { Centrifuge } from '../Centrifuge'
import { Account, TransactionOptions } from '../types'
import {
- BorrowerTransactionType,
+ AssetTransactionType,
InvestorTransactionType,
- SubqueryBorrowerTransaction,
+ SubqueryAssetTransaction,
SubqueryCurrencyBalances,
SubqueryInvestorTransaction,
SubqueryPoolSnapshot,
@@ -725,14 +725,14 @@ type InvestorTransaction = {
evmAddress?: string
}
-export type BorrowerTransaction = {
+export type AssetTransaction = {
id: string
timestamp: string
poolId: string
accountId: string
epochId: string
loanId: string
- type: BorrowerTransactionType
+ type: AssetTransactionType
amount: CurrencyBalance | undefined
settlementPrice: string | null
quantity: string | null
@@ -2507,21 +2507,21 @@ export function getPoolsModule(inst: Centrifuge) {
)
}
- function getBorrowerTransactions(args: [poolId: string, from?: Date, to?: Date]) {
+ function getAssetTransactions(args: [poolId: string, from?: Date, to?: Date]) {
const [poolId, from, to] = args
const $query = inst.getSubqueryObservable<{
- borrowerTransactions: { nodes: SubqueryBorrowerTransaction[] }
+ assetTransactions: { nodes: SubqueryAssetTransaction[] }
}>(
`query($poolId: String!, $from: Datetime!, $to: Datetime!) {
- borrowerTransactions(
+ assetTransactions(
orderBy: TIMESTAMP_ASC,
filter: {
poolId: { equalTo: $poolId },
timestamp: { greaterThan: $from, lessThan: $to },
}) {
nodes {
- loanId
+ assetId
epochId
type
timestamp
@@ -2543,11 +2543,11 @@ export function getPoolsModule(inst: Centrifuge) {
return $query.pipe(
switchMap(() => combineLatest([$query, getPoolCurrency([poolId])])),
map(([data, currency]) => {
- return data!.borrowerTransactions.nodes.map((tx) => ({
+ return data!.assetTransactions.nodes.map((tx) => ({
...tx,
amount: tx.amount ? new CurrencyBalance(tx.amount, currency.decimals) : undefined,
timestamp: new Date(`${tx.timestamp}+00:00`),
- })) as unknown as BorrowerTransaction[]
+ })) as unknown as AssetTransaction[]
})
)
}
@@ -3446,7 +3446,7 @@ export function getPoolsModule(inst: Centrifuge) {
getDailyPoolStates,
getMonthlyPoolStates,
getInvestorTransactions,
- getBorrowerTransactions,
+ getAssetTransactions,
getNativeCurrency,
getCurrencies,
getDailyTrancheStates,
diff --git a/centrifuge-js/src/types/subquery.ts b/centrifuge-js/src/types/subquery.ts
index 9bdb29437e..4cc7707737 100644
--- a/centrifuge-js/src/types/subquery.ts
+++ b/centrifuge-js/src/types/subquery.ts
@@ -74,17 +74,17 @@ export type SubqueryInvestorTransaction = {
transactionFee?: number | null
}
-export type BorrowerTransactionType = 'CREATED' | 'PRICED' | 'BORROWED' | 'REPAID' | 'CLOSED'
+export type AssetTransactionType = 'CREATED' | 'PRICED' | 'BORROWED' | 'REPAID' | 'CLOSED'
-export type SubqueryBorrowerTransaction = {
- __typename?: 'BorrowerTransaction'
+export type SubqueryAssetTransaction = {
+ __typename?: 'AssetTransaction'
id: string
timestamp: string
poolId: string
accountId: string
epochId: string
- loanId: string
- type: BorrowerTransactionType
+ assetId: string
+ type: AssetTransactionType
amount?: number | null
settlementPrice: string | null
quantity: string | null
From ec7859cd883c93efdfd7ad67375e051b4a0bb3d7 Mon Sep 17 00:00:00 2001
From: Sophia
Date: Wed, 21 Feb 2024 19:52:27 -0500
Subject: [PATCH 2/3] Settlement account display (#1974)
* Add summary component
* Fix prettier
* Add logos to summary
* Add pinned row option to DataTable
* Add reserve to asset table
* Update chip and icon for assets
* Fix type error
* Use outstanding debt to calculate offchain cash sum
* Update centrifuge-app/src/components/LoanList.tsx
Co-authored-by: JP
---------
Co-authored-by: JP
---
.../src/assets/images/currency-dollar.svg | 3 +
centrifuge-app/src/components/DataTable.tsx | 23 ++++
centrifuge-app/src/components/LoanLabel.tsx | 5 +-
centrifuge-app/src/components/LoanList.tsx | 111 ++++++++++++++++--
centrifuge-app/src/components/Tooltips.tsx | 12 ++
centrifuge-app/src/pages/Loan/index.tsx | 4 +-
.../src/pages/Pool/Assets/index.tsx | 60 +++++++---
fabric/src/components/Thumbnail/index.tsx | 8 +-
package.json | 4 +-
yarn.lock | 25 ++--
10 files changed, 209 insertions(+), 46 deletions(-)
create mode 100644 centrifuge-app/src/assets/images/currency-dollar.svg
diff --git a/centrifuge-app/src/assets/images/currency-dollar.svg b/centrifuge-app/src/assets/images/currency-dollar.svg
new file mode 100644
index 0000000000..cbee5b983c
--- /dev/null
+++ b/centrifuge-app/src/assets/images/currency-dollar.svg
@@ -0,0 +1,3 @@
+
diff --git a/centrifuge-app/src/components/DataTable.tsx b/centrifuge-app/src/components/DataTable.tsx
index 622a60e341..e3a91d63cd 100644
--- a/centrifuge-app/src/components/DataTable.tsx
+++ b/centrifuge-app/src/components/DataTable.tsx
@@ -30,12 +30,19 @@ type GroupedProps = {
export type DataTableProps = {
data: Array
+ /**
+ * pinnedData is not included in sorting and will be pinned to the top of the table in the order provided
+ * */
+ pinnedData?: Array
columns: Column[]
keyField?: string
onRowClicked?: (row: T) => string | LinkProps['to']
defaultSortKey?: string
defaultSortOrder?: OrderBy
hoverable?: boolean
+ /**
+ * summary row is not included in sorting
+ */
summary?: T
pageSize?: number
page?: number
@@ -88,6 +95,7 @@ const sorter = >(data: Array, order: OrderBy, s
export const DataTable = >({
data,
+ pinnedData,
columns,
keyField,
onRowClicked,
@@ -140,6 +148,21 @@ export const DataTable = >({
))}
)}
+ {pinnedData?.map((row, i) => (
+ onRowClicked(row))}
+ key={keyField ? row[keyField] : i}
+ tabIndex={onRowClicked ? 0 : undefined}
+ >
+ {columns.map((col, index) => (
+
+ {col.cell(row, i)}
+
+ ))}
+
+ ))}
{sortedAndPaginatedData?.map((row, i) => (
= ({ loan }) => {
: null
const isExternalAssetRepaid = currentFace?.isZero() && loan.status === 'Active'
+ const isCashAsset = 'valuationMethod' in loan.pricing && loan.pricing?.valuationMethod === 'cash'
const [status, text] = getLoanLabelStatus(loan, isExternalAssetRepaid)
+ if (!status || isCashAsset) return null
return {text}
}
diff --git a/centrifuge-app/src/components/LoanList.tsx b/centrifuge-app/src/components/LoanList.tsx
index f504812a4c..a640c7e8d6 100644
--- a/centrifuge-app/src/components/LoanList.tsx
+++ b/centrifuge-app/src/components/LoanList.tsx
@@ -1,4 +1,4 @@
-import { Loan, TinlakeLoan } from '@centrifuge/centrifuge-js'
+import { CurrencyBalance, Loan, Rate, TinlakeLoan } from '@centrifuge/centrifuge-js'
import {
Box,
IconChevronRight,
@@ -11,8 +11,11 @@ import {
Thumbnail,
usePagination,
} from '@centrifuge/fabric'
+import get from 'lodash/get'
import * as React from 'react'
import { useParams, useRouteMatch } from 'react-router'
+import currencyDollar from '../assets/images/currency-dollar.svg'
+import usdcLogo from '../assets/images/usdc-logo.svg'
import { formatNftAttribute } from '../pages/Loan/utils'
import { nftMetadataSchema } from '../schemas'
import { LoanTemplate, LoanTemplateAttribute } from '../types'
@@ -27,10 +30,13 @@ import { Column, DataTable, FilterableTableHeader, SortableTableHeader } from '.
import { LoadBoundary } from './LoadBoundary'
import LoanLabel, { getLoanLabelStatus } from './LoanLabel'
import { prefetchRoute } from './Root'
+import { Tooltips } from './Tooltips'
type Row = (Loan | TinlakeLoan) & {
idSortKey: number
originationDateSortKey: string
+ maturityDate: string | null
+ status: 'Created' | 'Active' | 'Closed' | ''
}
type Props = {
@@ -64,10 +70,25 @@ export function LoanList({ loans }: Props) {
const templateIds = poolMetadata?.loanTemplates?.map((s) => s.id) ?? []
const templateId = templateIds.at(-1)
const { data: templateMetadata } = useMetadata(templateId)
- const loansWithLabelStatus = loans.map((loan) => ({
- ...loan,
- labelStatus: getLoanStatus(loan),
- }))
+ const loansWithLabelStatus = React.useMemo(() => {
+ return loans
+ .map((loan) => ({
+ ...loan,
+ labelStatus: getLoanStatus(loan),
+ }))
+ .sort((a, b) => {
+ const aValuation = get(a, 'pricing.valuationMethod')
+ const bValuation = get(b, 'pricing.valuationMethod')
+ const aId = get(a, 'id') as string
+ const bId = get(b, 'id') as string
+
+ if (aValuation === 'cash' && bValuation !== 'cash') return -1
+ if (aValuation !== 'cash' && bValuation === 'cash') return 1
+ if (aValuation === 'cash' && bValuation === 'cash') return aId.localeCompare(bId)
+
+ return aId.localeCompare(bId)
+ })
+ }, [loans])
const filters = useFilters({
data: loansWithLabelStatus,
})
@@ -113,7 +134,7 @@ export function LoanList({ loans }: Props) {
return l.originationDate && (l.poolId.startsWith('0x') || l.status === 'Active')
? // @ts-expect-error
formatDate(l.originationDate)
- : ''
+ : '-'
},
sortKey: 'originationDateSortKey',
},
@@ -121,7 +142,7 @@ export function LoanList({ loans }: Props) {
{
align: 'left',
header: ,
- cell: (l: Row) => (l.pricing.maturityDate ? formatDate(l.pricing.maturityDate) : ''),
+ cell: (l: Row) => (l?.maturityDate ? formatDate(l.maturityDate) : '-'),
sortKey: 'maturityDate',
},
{
@@ -144,7 +165,7 @@ export function LoanList({ loans }: Props) {
},
{
header: '',
- cell: () => ,
+ cell: (l: Row) => (l.status ? : ''),
width: '52px',
},
].filter(Boolean) as Column[]
@@ -161,10 +182,33 @@ export function LoanList({ loans }: Props) {
!loan?.totalBorrowed?.isZero()
? loan.originationDate
: '',
- maturityDate: loan.pricing.maturityDate,
+ maturityDate:
+ 'valuationMethod' in loan.pricing && loan.pricing.valuationMethod === 'cash' ? null : loan.pricing.maturityDate,
...loan,
}))
+ const pinnedData: Row[] = [
+ {
+ id: 'reserve',
+ // @ts-expect-error
+ status: '',
+ poolId: pool.id,
+ pricing: {
+ valuationMethod: 'discountedCashFlow',
+ maxBorrowAmount: 'upToTotalBorrowed',
+ value: CurrencyBalance.fromFloat(0, 18),
+ maturityDate: '',
+ maturityExtensionDays: 0,
+ advanceRate: Rate.fromFloat(0),
+ interestRate: Rate.fromFloat(0),
+ },
+ asset: { collectionId: '', nftId: '' },
+ totalBorrowed: CurrencyBalance.fromFloat(0, 18),
+ totalRepaid: CurrencyBalance.fromFloat(0, 18),
+ outstandingDebt: CurrencyBalance.fromFloat(0, 18),
+ },
+ ]
+
const pagination = usePagination({ data: rows, pageSize: 20 })
return (
@@ -175,9 +219,11 @@ export function LoanList({ loans }: Props) {
`${basePath}/${poolId}/assets/${row.id}`}
+ onRowClicked={(row) =>
+ row.status ? `${basePath}/${poolId}/assets/${row.id}` : `${basePath}/${poolId}/assets`
+ }
pageSize={20}
page={pagination.page}
/>
@@ -216,14 +262,49 @@ function AssetName({ loan }: { loan: Row }) {
const isTinlakePool = loan.poolId.startsWith('0x')
const nft = useCentNFT(loan.asset.collectionId, loan.asset.nftId, false, isTinlakePool)
const { data: metadata, isLoading } = useMetadata(nft?.metadataUri, nftMetadataSchema)
+ if (loan.id === 'reserve') {
+ return (
+
+
+
+
+
+ Onchain reserve} />
+
+
+ )
+ }
+
+ if (loan.status === 'Active' && 'valuationMethod' in loan.pricing && loan.pricing.valuationMethod === 'cash') {
+ return (
+
+
+
+
+
+ Bank account} />
+
+
+ )
+ }
+
return (
-
+
{metadata?.name}
@@ -261,6 +342,10 @@ function Amount({ loan }: { loan: Row }) {
return formatBalance(l.outstandingDebt, pool?.currency.symbol)
+ // @ts-expect-error
+ case '':
+ return formatBalance(pool.reserve.total, pool?.currency.symbol)
+
default:
return ''
}
diff --git a/centrifuge-app/src/components/Tooltips.tsx b/centrifuge-app/src/components/Tooltips.tsx
index a67329aefa..3398bbf1f9 100644
--- a/centrifuge-app/src/components/Tooltips.tsx
+++ b/centrifuge-app/src/components/Tooltips.tsx
@@ -258,6 +258,18 @@ export const tooltipText = {
label: 'T-Bill APR',
body: 'Based on 3- to 6-month T-bills returns. See pool details for further information.',
},
+ totalNav: {
+ label: "Total NAV",
+ body: "The total Net Asset Value (NAV) reflects the combined present value of assets, cash held in the onchain reserve of the pool, and cash in the bank account designated as offchain cash."
+ } ,
+ onchainReserve: {
+ label: "Onchain reserve",
+ body: "The onchain reserve represents the amount of available liquidity in the pool available for asset originations and redemptions."
+ },
+ offchainCash: {
+ label: "Offchain cash",
+ body: "Offchain cash represents funds held in a traditional bank account or custody account."
+ }
}
export type TooltipsProps = {
diff --git a/centrifuge-app/src/pages/Loan/index.tsx b/centrifuge-app/src/pages/Loan/index.tsx
index b2236eb9fc..c41934fc5e 100644
--- a/centrifuge-app/src/pages/Loan/index.tsx
+++ b/centrifuge-app/src/pages/Loan/index.tsx
@@ -185,7 +185,9 @@ function Loan() {
: nftMetadata?.properties[key],
})) || []
: []),
- ...(loan.pricing.maturityDate
+ ...(loan.pricing.maturityDate &&
+ 'valuationMethod' in loan.pricing &&
+ loan.pricing.valuationMethod !== 'cash'
? [
{
label: 'Maturity date',
diff --git a/centrifuge-app/src/pages/Pool/Assets/index.tsx b/centrifuge-app/src/pages/Pool/Assets/index.tsx
index 3892d71cb3..5133cee25c 100644
--- a/centrifuge-app/src/pages/Pool/Assets/index.tsx
+++ b/centrifuge-app/src/pages/Pool/Assets/index.tsx
@@ -1,7 +1,9 @@
-import { ActiveLoan } from '@centrifuge/centrifuge-js'
+import { ActiveLoan, Loan } from '@centrifuge/centrifuge-js'
import { Box, Shelf, Text } from '@centrifuge/fabric'
import * as React from 'react'
import { useParams } from 'react-router'
+import currencyDollar from '../../../assets/images/currency-dollar.svg'
+import usdcLogo from '../../../assets/images/usdc-logo.svg'
import { LayoutBase } from '../../../components/LayoutBase'
import { LoadBoundary } from '../../../components/LoadBoundary'
import { LoanList } from '../../../components/LoanList'
@@ -13,7 +15,7 @@ import { Dec } from '../../../utils/Decimal'
import { formatBalance } from '../../../utils/formatting'
import { useLoans } from '../../../utils/useLoans'
import { useSuitableAccounts } from '../../../utils/usePermissions'
-import { useAverageAmount, usePool } from '../../../utils/usePools'
+import { usePool } from '../../../utils/usePools'
import { PoolDetailHeader } from '../Header'
export function PoolDetailAssetsTab() {
@@ -31,7 +33,7 @@ export function PoolDetailAssets() {
const { pid: poolId } = useParams<{ pid: string }>()
const pool = usePool(poolId)
const loans = useLoans(poolId)
- const averageAmount = useAverageAmount(poolId)
+ const isTinlakePool = poolId.startsWith('0x')
if (!pool) return null
@@ -47,26 +49,52 @@ export function PoolDetailAssets() {
const ongoingAssets = (loans &&
[...loans].filter((loan) => loan.status === 'Active' && !loan.outstandingDebt.isZero())) as ActiveLoan[]
- const isExternal = 'valuationMethod' in loans[0].pricing && loans[0].pricing.valuationMethod === 'oracle'
-
- const avgAmount = isExternal
- ? averageAmount
- : ongoingAssets
- .reduce((curr, prev) => curr.add(prev.outstandingDebt.toDecimal() || Dec(0)), Dec(0))
- .dividedBy(ongoingAssets.length)
- .toDecimalPlaces(2)
+ const offchainAssets = !isTinlakePool
+ ? loans.filter((loan) => (loan as Loan).pricing.valuationMethod === 'cash')
+ : null
+ const offchainReserve = offchainAssets?.reduce(
+ (curr, prev) => curr.add(prev.status === 'Active' ? prev.outstandingDebt.toDecimal() : Dec(0)),
+ Dec(0)
+ )
- const assetValue = formatBalance(pool.nav.latest.toDecimal().toNumber(), pool.currency.symbol)
+ const overdueAssets = loans.filter(
+ (loan) =>
+ loan.status === 'Active' &&
+ loan.outstandingDebt.gtn(0) &&
+ new Date(loan.pricing.maturityDate).getTime() < Date.now()
+ )
const pageSummaryData: { label: React.ReactNode; value: React.ReactNode }[] = [
{
- label: ,
- value: assetValue,
+ label: ,
+ value: formatBalance(pool.nav.latest.toDecimal(), pool.currency.symbol),
+ },
+ {
+ label: (
+
+
+
+
+ ),
+ value: formatBalance(pool.reserve.total || 0, pool.currency.symbol),
+ },
+ {
+ label: (
+
+
+
+
+ ),
+ value: formatBalance(offchainReserve, 'USD'),
+ },
+ {
+ label: 'Total assets',
+ value: loans.length,
},
{ label: , value: ongoingAssets.length || 0 },
{
- label: ,
- value: formatBalance(avgAmount, pool.currency.symbol),
+ label: 'Overdue assets',
+ value: 0 ? 'statusCritical' : 'inherit'}>{overdueAssets.length},
},
]
diff --git a/fabric/src/components/Thumbnail/index.tsx b/fabric/src/components/Thumbnail/index.tsx
index eda0060078..34ebed6a8f 100644
--- a/fabric/src/components/Thumbnail/index.tsx
+++ b/fabric/src/components/Thumbnail/index.tsx
@@ -54,8 +54,8 @@ const StyledThumbnail = styled(Text)>`
background: 'transparent',
'&::before': {
content: '""',
- width: '80%',
- height: '80%',
+ width: '50%',
+ height: '50%',
position: 'absolute',
left: 0,
top: 0,
@@ -64,9 +64,9 @@ const StyledThumbnail = styled(Text)>`
margin: 'auto',
zIndex: 0,
transform: 'rotate(45deg)',
- background: theme.colors.backgroundThumbnail,
+ background: theme.colors.statusInfo,
color: theme.colors.textInverted,
- borderRadius: '4px',
+ borderRadius: '2px',
},
})
case 'token':
diff --git a/package.json b/package.json
index 518808112d..ff369b2f70 100644
--- a/package.json
+++ b/package.json
@@ -18,8 +18,8 @@
"esbuild": "^0.16.17",
"esbuild-node-externals": "^1.6.0",
"husky": "^6.0.0",
- "prettier": "^2.3.1",
- "prettier-plugin-organize-imports": "1.1.1",
+ "prettier": "^2.4.1",
+ "prettier-plugin-organize-imports": "3.2.4",
"pretty-quick": "^3.1.1",
"ts-node": "9.0.0",
"typescript": "~5.3.3"
diff --git a/yarn.lock b/yarn.lock
index 076f458433..4c18bbd492 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -22478,17 +22478,24 @@ fsevents@~2.3.3:
languageName: node
linkType: hard
-"prettier-plugin-organize-imports@npm:1.1.1":
- version: 1.1.1
- resolution: "prettier-plugin-organize-imports@npm:1.1.1"
+"prettier-plugin-organize-imports@npm:3.2.4":
+ version: 3.2.4
+ resolution: "prettier-plugin-organize-imports@npm:3.2.4"
peerDependencies:
- prettier: ">=1.10"
- typescript: ">=2.8.2"
- checksum: 030b9534ad40741643ca5747ddf85290c68f67b5b96a4c389670d2ac8b39c09df24f7aa821a2f5d5c4a95a99dd699276082bae5b4aeea7817d1e3596434dad10
+ "@volar/vue-language-plugin-pug": ^1.0.4
+ "@volar/vue-typescript": ^1.0.4
+ prettier: ">=2.0"
+ typescript: ">=2.9"
+ peerDependenciesMeta:
+ "@volar/vue-language-plugin-pug":
+ optional: true
+ "@volar/vue-typescript":
+ optional: true
+ checksum: 57ae97d7e403445e650ae92b7da586761d1d88a47e46b3ea274baeb96782165bebd0132db9c652081e185c41b50701ba1d30d615ad1c9000300cc0c67eb12b7a
languageName: node
linkType: hard
-"prettier@npm:^2.1.2, prettier@npm:^2.3.1, prettier@npm:^2.4.1, prettier@npm:^2.8.0":
+"prettier@npm:^2.1.2, prettier@npm:^2.4.1, prettier@npm:^2.8.0":
version: 2.8.8
resolution: "prettier@npm:2.8.8"
bin:
@@ -23905,8 +23912,8 @@ fsevents@~2.3.3:
esbuild: ^0.16.17
esbuild-node-externals: ^1.6.0
husky: ^6.0.0
- prettier: ^2.3.1
- prettier-plugin-organize-imports: 1.1.1
+ prettier: ^2.4.1
+ prettier-plugin-organize-imports: 3.2.4
pretty-quick: ^3.1.1
rxjs: ^7.8.0
ts-node: 9.0.0
From da6762d6920348e6ecff55bb0b6dbe03796e52fa Mon Sep 17 00:00:00 2001
From: Guillermo Perez
Date: Thu, 22 Feb 2024 15:38:02 +0100
Subject: [PATCH 3/3] fix demo pod url
---
centrifuge-app/.env-config/.env.demo | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/centrifuge-app/.env-config/.env.demo b/centrifuge-app/.env-config/.env.demo
index b51d48cbf6..34f76d2ff5 100644
--- a/centrifuge-app/.env-config/.env.demo
+++ b/centrifuge-app/.env-config/.env.demo
@@ -1,5 +1,5 @@
REACT_APP_COLLATOR_WSS_URL=wss://fullnode.demo.k-f.dev
-REACT_APP_DEFAULT_NODE_URL=pod-demo.k-f.dev
+REACT_APP_DEFAULT_NODE_URL=https://pod-demo.k-f.dev
REACT_APP_DEFAULT_UNLIST_POOLS=true
REACT_APP_FAUCET_URL=https://europe-central2-peak-vista-185616.cloudfunctions.net/faucet-api-demo
REACT_APP_IPFS_GATEWAY=https://centrifuge.mypinata.cloud/