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

Fix Tinlake and LP portfolio #2191

Merged
merged 7 commits into from
Jun 6, 2024
Merged
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
5 changes: 2 additions & 3 deletions centrifuge-app/src/components/InvestRedeem/InvestRedeem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
TextWithPlaceholder,
} from '@centrifuge/fabric'
import * as React from 'react'
import { useHistory, useParams } from 'react-router-dom'
import { useHistory } from 'react-router-dom'
import { useTheme } from 'styled-components'
import { ethConfig } from '../../config'
import { formatBalance } from '../../utils/formatting'
Expand Down Expand Up @@ -218,8 +218,7 @@ function Footer() {
function OnboardingButton() {
const { showNetworks, connectedType } = useWallet()
const { state } = useInvestRedeem()
const { pid: poolId } = useParams<{ pid: string }>()
const pool = usePool(poolId)
const pool = usePool(state.poolId)
const { data: metadata } = usePoolMetadata(pool)
const isTinlakePool = pool.id.startsWith('0x')
const history = useHistory()
Expand Down
4 changes: 1 addition & 3 deletions centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ export const KeyMetrics = ({ assetType, averageMaturity, loans, poolId }: Props)
const metrics = [
{
metric: 'Asset class',
value: `${capitalize(startCase(assetType?.class)).replace(/^Us /, 'US ')} - ${capitalize(
startCase(assetType?.subClass)
).replace(/^Us /, 'US ')}`,
value: `${capitalize(startCase(assetType?.class))} - ${assetType?.subClass}`,
},
...(isBT3BT4
? []
Expand Down
34 changes: 19 additions & 15 deletions centrifuge-app/src/components/Portfolio/AssetAllocation.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
import { useBalances } from '@centrifuge/centrifuge-react'
import { Box, Shelf, Text } from '@centrifuge/fabric'
import Decimal from 'decimal.js-light'
import capitalize from 'lodash/capitalize'
import startCase from 'lodash/startCase'
import { useTheme } from 'styled-components'
import { Dec } from '../../utils/Decimal'
import { formatBalanceAbbreviated } from '../../utils/formatting'
import { usePoolMetadataMulti, usePools } from '../../utils/usePools'
import { useListedPools } from '../../utils/useListedPools'
import { usePoolMetadataMulti } from '../../utils/usePools'
import { LabelValueStack } from '../LabelValueStack'
import { AssetClassChart } from './AssetClassChart'
import { useHoldings } from './Holdings'

export function AssetAllocation({ address }: { address?: string }) {
const balances = useBalances(address)
const pools = usePools()
export function AssetAllocation({ address, chainId }: { address?: string; chainId?: number }) {
const holdings = useHoldings(address, chainId)
const [pools] = useListedPools()
const theme = useTheme()
const poolIds = new Set(balances?.tranches.map((t) => t.poolId))
const poolIds = new Set(holdings.map((h) => h.poolId).filter((i) => !!i))
const filteredPools = pools?.filter((p) => poolIds.has(p.id)) ?? []
const metas = usePoolMetadataMulti(filteredPools)
const assetClasses = [...new Set(metas.map((m) => m.data?.pool?.asset?.class as string).filter(Boolean))]

const assetClasses = [
...new Set(metas.map((m) => capitalize(startCase(m.data?.pool?.asset?.class)) as string).filter(Boolean)),
]
const valueByClass: Record<string, Decimal> = Object.fromEntries(assetClasses.map((item) => [item, Dec(0)]))
let total = Dec(0)
balances?.tranches.forEach((balance) => {
const poolIndex = filteredPools.findIndex((p) => p.id === balance.poolId)
const price =
filteredPools[poolIndex]?.tranches.find((t) => t.id === balance.trancheId)?.tokenPrice?.toDecimal() ?? Dec(0)
const asset = metas[poolIndex]?.data?.pool?.asset?.class
const value = balance.balance.toDecimal().mul(price)
holdings.forEach((holding) => {
const poolIndex = filteredPools.findIndex((p) => p.id === holding.poolId)
const asset = capitalize(startCase(metas[poolIndex]?.data?.pool?.asset?.class))
const value = holding.marketValue
total = total.add(value)
valueByClass[asset!] = valueByClass[asset!]?.add(value)
})

const shades = [600, 800, 200, 400]
const shades = [700, 500]
const shares = assetClasses
.map((item, index) => {
const nextShade = shades[index % shades.length]
Expand All @@ -41,7 +45,7 @@ export function AssetAllocation({ address }: { address?: string }) {
})
.sort((a, b) => (b.value > a.value ? 1 : a === b ? 0 : -1))

return address && !!balances?.tranches && !!balances?.tranches.length ? (
return address && !!holdings.length ? (
<Shelf gap={8}>
<AssetClassChart data={shares} currency="USD" total={total.toNumber()} />
<Shelf as="ul" alignSelf="stretch" alignItems="stretch" flex={1} gap={6}>
Expand Down
21 changes: 16 additions & 5 deletions centrifuge-app/src/components/Portfolio/CardPortfolioValue.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { evmToSubstrateAddress } from '@centrifuge/centrifuge-js'
import { Box, Shelf, Stack, Text, TextWithPlaceholder } from '@centrifuge/fabric'
import * as React from 'react'
import styled, { useTheme } from 'styled-components'
import { config } from '../../config'
import { Dec } from '../../utils/Decimal'
import { isEvmAddress } from '../../utils/address'
import { formatBalance } from '../../utils/formatting'
import { useTransactionsByAddress } from '../../utils/usePools'
import { LoadBoundary } from '../LoadBoundary'
Expand All @@ -22,9 +24,18 @@ const rangeFilters = [
{ value: 'all', label: 'All' },
] as const

export function CardPortfolioValue({ address }: { address?: string }) {
const tokens = useHoldings(address)
const transactions = useTransactionsByAddress(address)
export function CardPortfolioValue({
address,
chainId,
showGraph = true,
}: {
address?: string
chainId?: number
showGraph?: boolean
}) {
const tokens = useHoldings(address, chainId)
const centAddress = address && chainId && isEvmAddress(address) ? evmToSubstrateAddress(address, chainId) : address
const transactions = useTransactionsByAddress(showGraph ? centAddress : undefined)

const { colors } = useTheme()

Expand Down Expand Up @@ -76,7 +87,7 @@ export function CardPortfolioValue({ address }: { address?: string }) {
</Shelf>
</Shelf>
</Stack>
{address && transactions?.investorTransactions.length ? (
{showGraph && centAddress && transactions?.investorTransactions.length ? (
<>
<Stack gap={1}>
<Shelf justifyContent="flex-end" pr="20px">
Expand All @@ -102,7 +113,7 @@ export function CardPortfolioValue({ address }: { address?: string }) {

<Box width="100%" height="300px">
<LoadBoundary>
<PortfolioValue rangeValue={range.value} address={address} />
<PortfolioValue rangeValue={range.value} address={centAddress} />
</LoadBoundary>
</Box>
</>
Expand Down
68 changes: 28 additions & 40 deletions centrifuge-app/src/components/Portfolio/Holdings.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import { Token } from '@centrifuge/centrifuge-js'
import { Token, evmToSubstrateAddress } from '@centrifuge/centrifuge-js'
import { formatBalance, useBalances, useCentrifuge, useWallet } from '@centrifuge/centrifuge-react'
import {
AnchorButton,
Box,
Grid,
IconDownload,
IconExternalLink,
IconMinus,
IconPlus,
IconSend,
Shelf,
Text,
Thumbnail,
} from '@centrifuge/fabric'
import { Box, Grid, IconDownload, IconMinus, IconPlus, IconSend, Shelf, Text, Thumbnail } from '@centrifuge/fabric'
import Decimal from 'decimal.js-light'
import { useHistory, useLocation, useRouteMatch } from 'react-router-dom'
import { useTheme } from 'styled-components'
Expand All @@ -22,7 +10,7 @@ import centLogo from '../../assets/images/logoCentrifuge.svg'
import usdcLogo from '../../assets/images/usdc-logo.svg'
import usdtLogo from '../../assets/images/usdt-logo.svg'
import { Dec } from '../../utils/Decimal'
import { isEvmAddress, isSubstrateAddress } from '../../utils/address'
import { isEvmAddress } from '../../utils/address'
import { formatBalanceAbbreviated } from '../../utils/formatting'
import { useTinlakeBalances } from '../../utils/tinlake/useTinlakeBalances'
import { useTinlakePools } from '../../utils/tinlake/useTinlakePools'
Expand All @@ -46,7 +34,7 @@ type Row = {
tokenPrice: Decimal
showActions?: boolean
address?: string
connectedNetwork?: string
connectedNetwork?: string | null
}

const columns: Column[] = [
Expand Down Expand Up @@ -96,26 +84,15 @@ const columns: Column[] = [
align: 'left',
header: '', // invest redeem buttons
cell: ({ showActions, poolId, trancheId, currency, connectedNetwork }: Row) => {
const isTinlakePool = poolId.startsWith('0x')
return (
<Grid gap={1} justifySelf="end">
{isTinlakePool ? (
<AnchorButton
variant="tertiary"
small
icon={IconExternalLink}
href="https://legacy.tinlake.centrifuge.io/portfolio"
target="_blank"
>
View on Tinlake
</AnchorButton>
) : showActions ? (
{showActions ? (
trancheId ? (
<Shelf>
<RouterLinkButton to={`?redeem=${poolId}-${trancheId}`} small variant="tertiary" icon={IconMinus}>
<RouterLinkButton to={`?redeem=${poolId}.${trancheId}`} small variant="tertiary" icon={IconMinus}>
Redeem
</RouterLinkButton>
<RouterLinkButton to={`?invest=${poolId}-${trancheId}`} small variant="tertiary" icon={IconPlus}>
<RouterLinkButton to={`?invest=${poolId}.${trancheId}`} small variant="tertiary" icon={IconPlus}>
Invest
</RouterLinkButton>
</Shelf>
Expand All @@ -136,15 +113,16 @@ const columns: Column[] = [
},
]

export function useHoldings(address?: string, showActions = true) {
export function useHoldings(address?: string, chainId?: number, showActions = true) {
const centAddress = address && chainId && isEvmAddress(address) ? evmToSubstrateAddress(address, chainId) : address
const { data: tinlakeBalances } = useTinlakeBalances(address && isEvmAddress(address) ? address : undefined)
const centBalances = useBalances(address && isSubstrateAddress(address) ? address : undefined)
const centBalances = useBalances(centAddress)
const match = useRouteMatch<{ address: string }>('/portfolio')
const isPortfolioPage = match?.isExact

const wallet = useWallet()
const tinlakePools = useTinlakePools()
const portfolioTokens = usePortfolioTokens(address)
const portfolioTokens = usePortfolioTokens(centAddress)
const currencies = usePoolCurrencies()
const CFGPrice = useCFGTokenPrice()

Expand All @@ -154,13 +132,15 @@ export function useHoldings(address?: string, showActions = true) {
tokenPrice: token.tokenPrice.toDecimal() || Dec(0),
showActions,
})),
...(tinlakeBalances?.tranches.filter((tranche) => !tranche.balance.isZero()) || []).map((balance) => {
...(tinlakeBalances?.tranches.filter((tranche) => !tranche.balancePending.isZero()) || []).map((balance) => {
const pool = tinlakePools.data?.pools?.find((pool) => pool.id === balance.poolId)
const tranche = pool?.tranches.find((tranche) => tranche.id === balance.trancheId)
if (!tranche) return null as never
return {
position: balance.balance.toDecimal(),
marketValue: tranche.tokenPrice ? balance.balance.toDecimal().mul(tranche?.tokenPrice.toDecimal()) : Dec(0),
position: balance.balancePending.toDecimal(),
marketValue: tranche.tokenPrice
? balance.balancePending.toDecimal().mul(tranche?.tokenPrice.toDecimal())
: Dec(0),
tokenPrice: tranche.tokenPrice?.toDecimal() || Dec(0),
trancheId: balance.trancheId,
poolId: balance.poolId,
Expand Down Expand Up @@ -226,7 +206,15 @@ export function useHoldings(address?: string, showActions = true) {
return tokens
}

export function Holdings({ showActions = true, address }: { showActions?: boolean; address?: string }) {
export function Holdings({
showActions = true,
address,
chainId,
}: {
showActions?: boolean
address?: string
chainId?: number
}) {
const { search, pathname } = useLocation()
const history = useHistory()
const params = new URLSearchParams(search)
Expand All @@ -235,10 +223,10 @@ export function Holdings({ showActions = true, address }: { showActions?: boolea
const openInvestDrawer = params.get('invest')
const openRedeemDrawer = params.get('redeem')

const [investPoolId, investTrancheId] = openInvestDrawer?.split('-') || []
const [redeemPoolId, redeemTrancheId] = openRedeemDrawer?.split('-') || []
const [investPoolId, investTrancheId] = openInvestDrawer?.split('.') || []
const [redeemPoolId, redeemTrancheId] = openRedeemDrawer?.split('.') || []

const tokens = useHoldings(address, showActions)
const tokens = useHoldings(address, chainId, showActions)

return address && tokens.length ? (
<>
Expand Down
12 changes: 6 additions & 6 deletions centrifuge-app/src/components/Portfolio/usePortfolio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function useDailyPortfolioValue(address: string, rangeValue?: number) {
{} as Record<string, InvestorTransaction[]>
)

const daysSinceFirstTx = transactions?.investorTransactions
const daysSinceFirstTx = transactions?.investorTransactions?.[0]
? Math.ceil(
(new Date().getTime() - new Date(transactions.investorTransactions.at(-1)!.timestamp).getTime()) /
(1000 * 3600 * 24)
Expand Down Expand Up @@ -125,9 +125,9 @@ const getPriceAtDate = (
)
}

export function usePortfolio(address?: string) {
// const [result] = useCentrifugeQuery(['accountPortfolio', address], (cent) => cent.pools.getPortfolio([address!]), {
// enabled: !!address,
export function usePortfolio(substrateAddress?: string) {
// const [result] = useCentrifugeQuery(['accountPortfolio', substrateAddress], (cent) => cent.pools.getPortfolio([substrateAddress!]), {
// enabled: !!substrateAddress,
// })
// return result

Expand Down Expand Up @@ -169,10 +169,10 @@ export function usePortfolio(address?: string) {
}
}`,
{
account: address && addressToHex(address),
account: substrateAddress && addressToHex(substrateAddress),
},
{
enabled: !!address,
enabled: !!substrateAddress,
}
)

Expand Down
2 changes: 1 addition & 1 deletion centrifuge-app/src/pages/Loan/TransactionTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Props = {
decimals: number
loanType: 'external' | 'internal'
pricing: PricingInfo
poolType: 'publicCredit' | 'privateCredit' | undefined
poolType?: string
maturityDate: Date
originationDate: Date | undefined
}
Expand Down
2 changes: 1 addition & 1 deletion centrifuge-app/src/pages/Loan/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ function Loan() {
? 'external'
: 'internal'
}
poolType={poolMetadata?.pool?.asset.class as 'publicCredit' | 'privateCredit' | undefined}
poolType={poolMetadata?.pool?.asset.class}
decimals={pool.currency.decimals}
pricing={loan.pricing as PricingInfo}
maturityDate={new Date(loan.pricing.maturityDate)}
Expand Down
17 changes: 10 additions & 7 deletions centrifuge-app/src/pages/Portfolio/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { evmToSubstrateAddress } from '@centrifuge/centrifuge-js'
import { useWallet } from '@centrifuge/centrifuge-react'
import { Button, Stack, Text } from '@centrifuge/fabric'
import * as React from 'react'
import { LayoutBase } from '../../components/LayoutBase'
import { LayoutSection } from '../../components/LayoutBase/LayoutSection'
import { AssetAllocation } from '../../components/Portfolio/AssetAllocation'
import { CardPortfolioValue } from '../../components/Portfolio/CardPortfolioValue'
import { Holdings } from '../../components/Portfolio/Holdings'
import { Transactions } from '../../components/Portfolio/Transactions'
import { RouterLinkButton } from '../../components/RouterLinkButton'
import { isEvmAddress } from '../../utils/address'
import { useAddress } from '../../utils/useAddress'
import { useTransactionsByAddress } from '../../utils/usePools'

Expand All @@ -22,7 +23,9 @@ export default function PortfolioPage() {
function Portfolio() {
const address = useAddress()
const transactions = useTransactionsByAddress(address)
const { showNetworks, connectedNetwork } = useWallet()
const { showNetworks, connectedNetwork, evm } = useWallet()
const chainId = evm.chainId ?? undefined
const centAddress = address && chainId && isEvmAddress(address) ? evmToSubstrateAddress(address, chainId) : address

return (
<>
Expand All @@ -35,7 +38,7 @@ function Portfolio() {
Track and manage your portfolio
</Text>
</Stack>
<CardPortfolioValue address={address} />
<CardPortfolioValue address={address} chainId={chainId} showGraph={false} />
</LayoutSection>

{transactions?.investorTransactions.length === 0 && connectedNetwork === 'centrifuge' ? (
Expand Down Expand Up @@ -64,15 +67,15 @@ function Portfolio() {
)}

<LayoutSection title="Holdings">
<Holdings address={address} />
<Holdings address={address} chainId={chainId} />
</LayoutSection>

<LayoutSection title="Transaction history">
<Transactions onlyMostRecent address={address} />
<Transactions onlyMostRecent address={centAddress} />
</LayoutSection>

<LayoutSection title="Allocation">
<AssetAllocation address={address} />
<LayoutSection title="Allocation" pb={5}>
<AssetAllocation address={address} chainId={chainId} />
</LayoutSection>
</>
)
Expand Down
Loading
Loading