Skip to content

Commit

Permalink
fix current value, average, amounts
Browse files Browse the repository at this point in the history
  • Loading branch information
JP Angelle committed Sep 19, 2023
1 parent 0b76c01 commit bcd79c6
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 45 deletions.
7 changes: 6 additions & 1 deletion centrifuge-app/src/components/LoanList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { formatBalance } from '../utils/formatting'
import { useAvailableFinancing } from '../utils/useLoans'
import { useMetadata } from '../utils/useMetadata'
import { useCentNFT } from '../utils/useNFTs'
import { usePool, usePoolMetadata } from '../utils/usePools'
import { useBorrowerAssetTransactions, usePool, usePoolMetadata } from '../utils/usePools'
import { Column, DataTable, SortableTableHeader } from './DataTable'
import { LoadBoundary } from './LoadBoundary'
import LoanLabel, { getLoanLabelStatus } from './LoanLabel'
Expand Down Expand Up @@ -205,6 +205,7 @@ function AssetName({ loan }: { loan: Row }) {
function Amount({ loan }: { loan: Row }) {
const pool = usePool(loan.poolId)
const { current } = useAvailableFinancing(loan.poolId, loan.id)
const { currentFace } = useBorrowerAssetTransactions(loan.poolId, loan.id)

function getAmount(l: Row) {
switch (l.status) {
Expand All @@ -220,6 +221,10 @@ function Amount({ loan }: { loan: Row }) {
return formatBalance(l.totalRepaid, pool?.currency.symbol)
}

if ('valuationMethod' in loan.pricing && loan.pricing.valuationMethod === 'oracle') {
return formatBalance(currentFace, pool?.currency.symbol)
}

return formatBalance(l.outstandingDebt, pool?.currency.symbol)

default:
Expand Down
19 changes: 1 addition & 18 deletions centrifuge-app/src/pages/Loan/ExternalFinanceForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function ExternalFinanceForm({ loan }: { loan: LoanType }) {
const repayFormRef = React.useRef<HTMLFormElement>(null)
useFocusInvalidInput(repayForm, repayFormRef)

const borrowerAssetTransactions = useBorrowerAssetTransactions(loan.poolId, loan.id)
const { currentFace } = useBorrowerAssetTransactions(loan.poolId, loan.id)

if (loan.status === 'Closed' || ('valuationMethod' in loan.pricing && loan.pricing.valuationMethod !== 'oracle')) {
return null
Expand All @@ -102,23 +102,6 @@ export function ExternalFinanceForm({ loan }: { loan: LoanType }) {
const maturityDatePassed =
loan?.pricing && 'maturityDate' in loan.pricing && new Date() > new Date(loan.pricing.maturityDate)

const currentFace =
borrowerAssetTransactions?.reduce((sum, trx) => {
if (trx.type === 'BORROWED') {
sum = new CurrencyBalance(
sum.add(trx.amount ? new BN(trx.amount).mul(new BN(100)) : new CurrencyBalance(0, pool.currency.decimals)),
pool.currency.decimals
)
}
if (trx.type === 'REPAID') {
sum = new CurrencyBalance(
sum.sub(trx.amount ? new BN(trx.amount).mul(new BN(100)) : new CurrencyBalance(0, pool.currency.decimals)),
pool.currency.decimals
)
}
return sum
}, new CurrencyBalance(0, pool.currency.decimals)) || new CurrencyBalance(0, pool.currency.decimals)

return (
<Stack gap={3}>
<Stack as={Card} gap={2} p={2}>
Expand Down
16 changes: 4 additions & 12 deletions centrifuge-app/src/pages/Loan/PricingValues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { TinlakePool } from '../../utils/tinlake/useTinlakePools'
type Props = {
loan: Loan | TinlakeLoan
pool: Pool | TinlakePool
latestSettlementPrice: string | null
latestPrice: CurrencyBalance
}

export function PricingValues({ loan: { pricing }, pool, latestSettlementPrice }: Props) {
export function PricingValues({ loan: { pricing }, pool, latestPrice }: Props) {
const isOutstandingDebtOrDiscountedCashFlow =
'valuationMethod' in pricing &&
(pricing.valuationMethod === 'outstandingDebt' || pricing.valuationMethod === 'discountedCashFlow')
Expand All @@ -21,20 +21,12 @@ export function PricingValues({ loan: { pricing }, pool, latestSettlementPrice }

const days = getAge(new Date(pricing.oracle.timestamp).toISOString())

const getLatestPrice = () => {
if (latestSettlementPrice && pricing.oracle.value.isZero()) {
return new CurrencyBalance(latestSettlementPrice, pool.currency.decimals)
}

return new CurrencyBalance(pricing.oracle.value.toString(), 18).toDecimal()
}

return (
<>
<LabelValueStack label="ISIN" value={pricing.Isin} />
<LabelValueStack
label="Latest price"
value={`${formatBalance(getLatestPrice(), pool.currency.symbol, 6, 2)}`}
label={`Latest price${pricing.oracle.value.isZero() ? ' (settlement)' : ''}`}
value={`${formatBalance(latestPrice, pool.currency.symbol, 6, 2)}`}
/>
<LabelValueStack label="Price last updated" value={days === '0' ? `${days} ago` : `Today`} />
</>
Expand Down
31 changes: 24 additions & 7 deletions centrifuge-app/src/pages/Loan/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Loan as LoanType, Pool, TinlakeLoan } from '@centrifuge/centrifuge-js'
import { CurrencyBalance, Loan as LoanType, Pool, TinlakeLoan } from '@centrifuge/centrifuge-js'
import {
AnchorButton,
Box,
Expand Down Expand Up @@ -90,7 +90,7 @@ const Loan: React.FC<{ setShowOraclePricing?: () => void }> = ({ setShowOraclePr
const metadataIsLoading = poolMetadataIsLoading || nftMetadataIsLoading
const address = useAddress()
const canOraclePrice = useCanSetOraclePrice(address)
const borrowerAssetTransactions = useBorrowerAssetTransactions(poolId, assetId)
const { borrowerAssetTransactions, currentFace } = useBorrowerAssetTransactions(poolId, assetId)

const templateIds = poolMetadata?.loanTemplates?.map((s) => s.id) ?? []
const templateId = templateIds.at(-1)
Expand Down Expand Up @@ -126,9 +126,21 @@ const Loan: React.FC<{ setShowOraclePricing?: () => void }> = ({ setShowOraclePr
return 0
}, [originationDate, loan?.pricing.maturityDate])

const latestSettlementPrice = borrowerAssetTransactions?.length
? borrowerAssetTransactions[borrowerAssetTransactions.length - 1]?.settlementPrice
: null
const getLatestPrice = () => {
if (loan?.pricing && 'oracle' in loan.pricing) {
const latestSettlementPrice = borrowerAssetTransactions?.length
? borrowerAssetTransactions[borrowerAssetTransactions.length - 1]?.settlementPrice
: null

if (latestSettlementPrice && loan.pricing.oracle.value.isZero()) {
return new CurrencyBalance(latestSettlementPrice, pool.currency.decimals)
}

return new CurrencyBalance(loan.pricing.oracle.value.toString(), 18)
}

return new CurrencyBalance(0, 18)
}

return (
<Stack>
Expand Down Expand Up @@ -186,7 +198,12 @@ const Loan: React.FC<{ setShowOraclePricing?: () => void }> = ({ setShowOraclePr
? [
{
label: 'Current value',
value: `${formatBalance(loan.presentValue, pool.currency.symbol, 2, 2)}`,
value: `${formatBalance(
currentFace.toDecimal().mul(getLatestPrice().toDecimal()).div(100),
pool.currency.symbol,
2,
2
)}`,
},
]
: []),
Expand Down Expand Up @@ -223,7 +240,7 @@ const Loan: React.FC<{ setShowOraclePricing?: () => void }> = ({ setShowOraclePr
<PageSection title={<Box>Pricing</Box>}>
<Stack>
<Shelf gap={6} flexWrap="wrap">
<PricingValues loan={loan} pool={pool} latestSettlementPrice={latestSettlementPrice} />
<PricingValues loan={loan} pool={pool} latestPrice={getLatestPrice()} />
</Shelf>
{canOraclePrice &&
setShowOraclePricing &&
Expand Down
15 changes: 10 additions & 5 deletions centrifuge-app/src/pages/Pool/Assets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Tooltips } from '../../../components/Tooltips'
import { Dec } from '../../../utils/Decimal'
import { formatBalance, formatPercentage } from '../../../utils/formatting'
import { useLoans } from '../../../utils/useLoans'
import { usePool } from '../../../utils/usePools'
import { useAverageAmount, usePool } from '../../../utils/usePools'
import { PoolDetailHeader } from '../Header'
import { PoolDetailSideBar } from '../Overview'

Expand All @@ -29,6 +29,7 @@ export const PoolDetailAssets: React.FC = () => {
const { pid: poolId } = useParams<{ pid: string }>()
const pool = usePool(poolId)
const loans = useLoans(poolId)
const averageAmount = useAverageAmount(poolId)

if (!pool) return null

Expand All @@ -52,10 +53,14 @@ export const PoolDetailAssets: React.FC = () => {
.toFixed(2)
.toString()

const avgAmount = ongoingAssets
.reduce<any>((curr, prev) => curr.add(prev.outstandingDebt.toDecimal() || Dec(0)), Dec(0))
.dividedBy(ongoingAssets.length)
.toDecimalPlaces(2)
const isExternal = 'valuationMethod' in loans[0].pricing && loans[0].pricing.valuationMethod === 'oracle'

const avgAmount = isExternal
? averageAmount
: ongoingAssets
.reduce<any>((curr, prev) => curr.add(prev.outstandingDebt.toDecimal() || Dec(0)), Dec(0))
.dividedBy(ongoingAssets.length)
.toDecimalPlaces(2)

const assetValue = formatBalance(pool.nav.latest.toDecimal().toNumber(), pool.currency.symbol)

Expand Down
107 changes: 105 additions & 2 deletions centrifuge-app/src/utils/usePools.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import Centrifuge, { BorrowerTransaction, Pool, PoolMetadata } from '@centrifuge/centrifuge-js'
import Centrifuge, {
BorrowerTransaction,
CurrencyBalance,
ExternalPricingInfo,
Pool,
PoolMetadata,
} from '@centrifuge/centrifuge-js'
import { useCentrifuge, useCentrifugeQuery, useWallet } from '@centrifuge/centrifuge-react'
import BN from 'bn.js'
import Decimal from 'decimal.js-light'
import { useEffect } from 'react'
import { useQuery } from 'react-query'
import { combineLatest, map, Observable } from 'rxjs'
import { Dec } from './Decimal'
import { TinlakePool, useTinlakePools } from './tinlake/useTinlakePools'
import { useLoans } from './useLoans'
import { useMetadata } from './useMetadata'

export function usePools(suspense = true) {
Expand Down Expand Up @@ -73,7 +83,82 @@ export function useBorrowerTransactions(poolId: string, from?: Date, to?: Date)
return result
}

export function useAverageAmount(poolId: string) {
const borrowerTransactions = useBorrowerTransactions(poolId)
const pool = usePool(poolId)
const loans = useLoans(poolId)

if (!loans || !pool || !borrowerTransactions) return new BN(0)

const getLatestPrice = (assetId: string) => {
const pricing = loans.find((loan) => loan.id === assetId)?.pricing as ExternalPricingInfo

const borrowerAssetTransactions = borrowerTransactions.filter(
(borrowerTransaction) => borrowerTransaction.id === assetId
)

const latestSettlementPrice = borrowerAssetTransactions?.length
? borrowerAssetTransactions[borrowerAssetTransactions.length - 1]?.settlementPrice
: null

if (latestSettlementPrice && pricing.oracle.value.isZero()) {
return new CurrencyBalance(latestSettlementPrice, pool.currency.decimals)
}

return new CurrencyBalance(pricing.oracle.value.toString(), 18)
}

const poolsByLoanId =
borrowerTransactions.reduce((pools, pool) => {
const [, assetId] = pool.loanId.split('-')

const somePool = { ...pool, oracleValue: getLatestPrice(assetId) }
if (pools[assetId]) {
pools[assetId] = [...pools[assetId], somePool]
} else {
pools[assetId] = [somePool]
}

return pools
}, {} as Record<string, Array<BorrowerTransaction & { oracleValue: CurrencyBalance }>>) || {}

const currentFaces = Object.entries(poolsByLoanId).reduce((sum, [assetId, transactions]) => {
const item =
transactions.reduce((sum, trx) => {
if (trx.type === 'BORROWED') {
sum = new CurrencyBalance(
sum.add(trx.amount ? new BN(trx.amount).mul(new BN(100)) : new CurrencyBalance(0, pool.currency.decimals)),
pool.currency.decimals
)
}
if (trx.type === 'REPAID') {
sum = new CurrencyBalance(
sum.sub(trx.amount ? new BN(trx.amount).mul(new BN(100)) : new CurrencyBalance(0, pool.currency.decimals)),
pool.currency.decimals
)
}
return sum
}, new CurrencyBalance(0, pool.currency.decimals)) || new CurrencyBalance(0, pool.currency.decimals)

sum = { ...sum, [assetId]: item }

return sum
}, {} as Record<string, CurrencyBalance>)

const currentValues = Object.entries(currentFaces).reduce((values, [assetId, currentFace]) => {
values[assetId] = currentFace.toDecimal().mul(getLatestPrice(assetId).toDecimal()).div(100)

return values
}, {} as Record<string, Decimal>)

return Object.values(currentValues)
.reduce((sum, value) => sum.add(value), Dec(0))
.div(loans.length)
}

export function useBorrowerAssetTransactions(poolId: string, assetId: string, from?: Date, to?: Date) {
const pool = usePool(poolId)

const [result] = useCentrifugeQuery(
['borrowerAssetTransactions', poolId, assetId, from, to],
(cent) => {
Expand All @@ -87,10 +172,28 @@ export function useBorrowerAssetTransactions(poolId: string, assetId: string, fr
},
{
suspense: true,
enabled: !!pool,
}
)

return result
const currentFace =
result?.reduce((sum, trx) => {
if (trx.type === 'BORROWED') {
sum = new CurrencyBalance(
sum.add(trx.amount ? new BN(trx.amount).mul(new BN(100)) : new CurrencyBalance(0, pool.currency.decimals)),
pool.currency.decimals
)
}
if (trx.type === 'REPAID') {
sum = new CurrencyBalance(
sum.sub(trx.amount ? new BN(trx.amount).mul(new BN(100)) : new CurrencyBalance(0, pool.currency.decimals)),
pool.currency.decimals
)
}
return sum
}, new CurrencyBalance(0, pool.currency.decimals)) || new CurrencyBalance(0, pool.currency.decimals)

return { borrowerAssetTransactions: result, currentFace }
}

export function useDailyPoolStates(poolId: string, from?: Date, to?: Date) {
Expand Down

0 comments on commit bcd79c6

Please sign in to comment.