diff --git a/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx b/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
index 333ef7ea7..507c104ae 100644
--- a/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
+++ b/centrifuge-app/src/components/Charts/PoolPerformanceChart.tsx
@@ -10,6 +10,7 @@ import { daysBetween, formatDate } from '../../utils/date'
import { formatBalance, formatBalanceAbbreviated, formatPercentage } from '../../utils/formatting'
import { useLoans } from '../../utils/useLoans'
import { useDailyPoolStates, usePool } from '../../utils/usePools'
+import { DYF_POOL_ID } from '../PoolCard'
import { Tooltips, tooltipText } from '../Tooltips'
import { TooltipContainer, TooltipTitle } from './Tooltip'
import { getOneDayPerMonth, getRangeNumber } from './utils'
@@ -160,7 +161,7 @@ function PoolPerformanceChart() {
nav: todayAssetValue,
juniorTokenPrice: tranchePrices.juniorTokenPrice ?? 0,
seniorTokenPrice: tranchePrices.seniorTokenPrice ?? null,
- juniorAPY: pool.id === '1655476167' ? 15 : todayJuniorApy,
+ juniorAPY: pool.id === DYF_POOL_ID ? 15 : todayJuniorApy,
seniorAPY: todaySeniorApy,
}
}
@@ -181,7 +182,7 @@ function PoolPerformanceChart() {
nav: todayAssetValue,
price: todayPrice,
currency: pool.currency.symbol,
- juniorAPY: pool.id === '1655476167' ? 15 : todayJuniorApy,
+ juniorAPY: pool.id === DYF_POOL_ID ? 15 : todayJuniorApy,
seniorAPY: todaySeniorApy,
...trancheTodayPrice,
}
diff --git a/centrifuge-app/src/components/IssuerSection.tsx b/centrifuge-app/src/components/IssuerSection.tsx
index be76a0e04..d18150fc2 100644
--- a/centrifuge-app/src/components/IssuerSection.tsx
+++ b/centrifuge-app/src/components/IssuerSection.tsx
@@ -221,27 +221,46 @@ const Links = ({ links }: { links: { label: string; href?: string; show: boolean
}
export function RatingDetails({ metadata }: IssuerSectionProps) {
- const rating = metadata?.pool?.rating
+ const ratings = metadata?.pool?.poolRatings
+ const cent = useCentrifuge()
- return rating?.ratingAgency || rating?.ratingValue || rating?.ratingReportUrl ? (
+ return ratings?.length ? (
Pool rating
-
-
- {rating.ratingAgency && (
- {rating.ratingAgency}} />
- )}
- {rating.ratingValue && (
- {rating.ratingValue}} />
- )}
-
-
-
- {rating?.ratingReportUrl && (
-
- View full report
-
- )}
+
+ {ratings?.map((rating) => {
+ return (
+
+
+
+ {rating.agency && (
+ {rating.agency}} />
+ )}
+ {rating.value && (
+ {rating.value}} />
+ )}
+
+
+
+ {rating?.reportUrl && (
+
+ View full report
+
+ )}
+ {rating?.reportFile && (
+
+ Download report
+
+ )}
+
+
+ )
+ })}
) : null
diff --git a/centrifuge-app/src/components/PoolCard/index.tsx b/centrifuge-app/src/components/PoolCard/index.tsx
index 3e0c17542..f861de872 100644
--- a/centrifuge-app/src/components/PoolCard/index.tsx
+++ b/centrifuge-app/src/components/PoolCard/index.tsx
@@ -25,8 +25,6 @@ export type MetaData = {
}
}
-type TinlakeTranchesKey = 'silver' | 'blocktowerThree' | 'blocktowerFour'
-
type TrancheWithCurrency = Pick
const StyledRouterTextLink = styled(RouterTextLink)`
@@ -58,32 +56,55 @@ const StyledCard = styled(Card)`
`
const tinlakeTranches = {
- silver: {
- Junior: '15%',
- Senior: '7%',
+ NS2: {
+ name: 'New Silver 3',
+ tranches: [
+ { name: 'Junior', apr: '15%', minInvestment: '-' },
+ { name: 'Senior', apr: '7%', minInvestment: '5K' },
+ ],
shortDescription: ' Real estate bridge loans for fix and flip projects, maturing in 12-24 months.',
- InvestorType: 'Qualified Investors',
+ investorType: 'Qualified Investors',
},
- blocktowerThree: {
- Junior: '15%',
- Senior: '4%',
+ BT3: {
+ name: 'BlockTower Series 3',
+ tranches: [
+ { name: 'Junior', apr: '15%', minInvestment: '-' },
+ { name: 'Senior', apr: '4%', minInvestment: '-' },
+ ],
shortDescription: ' Investment-grade consumer ABS, auto ABS, and CLOs under 4 years.',
- InvestorType: 'Private',
+ investorType: 'Private',
},
- blocktowerFour: {
- Junior: '15%',
- Senior: '4%',
+ BT4: {
+ name: 'BlockTower Series 4',
+ tranches: [
+ { name: 'Junior', apr: '15%', minInvestment: '-' },
+ { name: 'Senior', apr: '4%', minInvestment: '-' },
+ ],
shortDescription: 'Investment-grade consumer ABS, auto ABS, and CLOs under 4 years.',
- InvestorType: 'Private',
+ investorType: 'Private',
},
none: {
- Junior: '-',
- Senior: '-',
+ name: '-',
+ tranches: [
+ { name: 'Junior', apr: '-', minInvestment: '-' },
+ { name: 'Senior', apr: '-', minInvestment: '-' },
+ ],
shortDescription: '',
- InvestorType: '-',
+ investorType: '-',
},
}
+export const DYF_POOL_ID = '1655476167'
+export const NS3_POOL_ID = '1615768079'
+
+export type CentrifugeTargetAPYs = keyof typeof centrifugeTargetAPYs
+export const centrifugeTargetAPYs = {
+ [DYF_POOL_ID]: ['15%'],
+ [NS3_POOL_ID]: ['8.0%', '16%'],
+}
+
+type TinlakeTranchesKey = keyof typeof tinlakeTranches
+
export type PoolCardProps = {
poolId?: string
name?: string
@@ -112,25 +133,14 @@ export function PoolCard({
}: PoolCardProps) {
const theme = useTheme()
const isOneTranche = tranches && tranches?.length === 1
- const isTinlakePool =
- poolId === '0x53b2d22d07E069a3b132BfeaaD275b10273d381E' ||
- poolId === '0x90040F96aB8f291b6d43A8972806e977631aFFdE' ||
- poolId === '0x55d86d51Ac3bcAB7ab7d2124931FbA106c8b60c7'
-
- const tinlakeObjKey = () => {
- if (name?.includes('Silver')) return 'silver'
- else if (name?.includes('BlockTower Series 3')) return 'blocktowerThree'
- else if (name?.includes('BlockTower Series 4')) return 'blocktowerFour'
- else return 'none'
- }
+ const isTinlakePool = poolId?.startsWith('0x')
- const getTinlakeMinInvestment = (trancheName: 'Junior' | 'Senior') => {
- if (name?.includes('Silver') && trancheName === 'Senior') return '5K'
- else return '-'
- }
+ const tinlakeKey = (Object.keys(tinlakeTranches).find(
+ (key) => tinlakeTranches[key as TinlakeTranchesKey].name === name
+ ) || 'none') as TinlakeTranchesKey
const renderText = (text: string, isApr?: boolean) => {
- if (isApr && poolId === '1615768079') {
+ if (isApr && poolId === NS3_POOL_ID) {
return (
@@ -151,9 +161,9 @@ export function PoolCard({
const calculateApy = (tranche: TrancheWithCurrency) => {
const daysSinceCreation = createdAt ? daysBetween(createdAt, new Date()) : 0
- if (poolId === '1655476167') return '15%'
- if (poolId === '1615768079' && tranche.seniority === 0) return '8.0%'
- if (poolId === '1615768079' && tranche.seniority === 1) return '16%'
+ if (poolId === DYF_POOL_ID) return centrifugeTargetAPYs[DYF_POOL_ID][0]
+ if (poolId === NS3_POOL_ID && tranche.seniority === 0) return centrifugeTargetAPYs[NS3_POOL_ID][0]
+ if (poolId === NS3_POOL_ID && tranche.seniority === 1) return centrifugeTargetAPYs[NS3_POOL_ID][1]
if (daysSinceCreation > 30 && tranche.yield30DaysAnnualized)
return formatPercentage(tranche.yield30DaysAnnualized, true, {}, 1)
if (tranche.interestRatePerSec) {
@@ -165,7 +175,6 @@ export function PoolCard({
const tranchesData = useMemo(() => {
return tranches
?.map((tranche: TrancheWithCurrency) => {
- const key = tinlakeObjKey() as TinlakeTranchesKey
const words = tranche.currency.name.trim().split(' ')
const metadata = metaData?.tranches[tranche.id] ?? null
const trancheName = words[words.length - 1]
@@ -176,16 +185,18 @@ export function PoolCard({
return {
name: trancheName,
- apr: isTinlakePool ? tinlakeTranches[key][trancheName as 'Junior' | 'Senior'] : calculateApy(tranche),
+ apr: isTinlakePool
+ ? tinlakeTranches[tinlakeKey].tranches.find((t) => t.name === trancheName)?.apr
+ : calculateApy(tranche),
minInvestment: isTinlakePool
- ? getTinlakeMinInvestment(trancheName as 'Junior' | 'Senior')
+ ? tinlakeTranches[tinlakeKey].tranches.find((t) => t.name === trancheName)?.minInvestment
: metadata && metadata.minInitialInvestment
? `$${formatBalanceAbbreviated(investmentBalance, '', 0)}`
: '-',
}
})
.reverse()
- }, [calculateApy, getTinlakeMinInvestment, isTinlakePool, metaData?.tranches, tinlakeObjKey, tranches])
+ }, [calculateApy, isTinlakePool, metaData?.tranches, tinlakeKey, tranches])
return (
@@ -240,7 +251,7 @@ export function PoolCard({
)}
- {poolId === '1655476167' ? 'Target' : 'APY'}
+ {poolId === DYF_POOL_ID ? 'Target' : 'APY'}
{tranchesData?.map((tranche) => renderText(`${tranche.apr}`, true))}
@@ -256,7 +267,7 @@ export function PoolCard({
{isTinlakePool
- ? tinlakeTranches[tinlakeObjKey()].shortDescription
+ ? tinlakeTranches[tinlakeKey].shortDescription
: metaData?.pool?.issuer?.shortDescription}
@@ -269,7 +280,7 @@ export function PoolCard({
Investor type
{' '}
- {isTinlakePool ? tinlakeTranches[tinlakeObjKey()].InvestorType : metaData?.pool?.investorType ?? '-'}
+ {isTinlakePool ? tinlakeTranches[tinlakeKey].investorType : metaData?.pool?.investorType ?? '-'}
diff --git a/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx b/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
index cd5f263b6..f36c050f6 100644
--- a/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
+++ b/centrifuge-app/src/components/PoolOverview/KeyMetrics.tsx
@@ -1,5 +1,5 @@
import { CurrencyBalance, DailyTrancheState, Price } from '@centrifuge/centrifuge-js'
-import { NetworkIcon, formatBalanceAbbreviated } from '@centrifuge/centrifuge-react'
+import { NetworkIcon, formatBalanceAbbreviated, useCentrifuge } from '@centrifuge/centrifuge-react'
import { Box, Card, IconArrowRightWhite, IconMoody, IconSp, Shelf, Stack, Text, Tooltip } from '@centrifuge/fabric'
import capitalize from 'lodash/capitalize'
import startCase from 'lodash/startCase'
@@ -10,6 +10,7 @@ import { formatBalance, formatPercentage } from '../../utils/formatting'
import { useAverageMaturity } from '../../utils/useAverageMaturity'
import { useActiveDomains } from '../../utils/useLiquidityPools'
import { useDailyTranchesStates, usePool, usePoolFees, usePoolMetadata } from '../../utils/usePools'
+import { centrifugeTargetAPYs } from '../PoolCard'
import { PoolStatus } from '../PoolCard/PoolStatus'
import { getPoolStatus } from '../PoolList'
import { Spinner } from '../Spinner'
@@ -32,10 +33,7 @@ type Tranche = Pick & {
}
}
-type TinlakeDataKey =
- | '0x53b2d22d07E069a3b132BfeaaD275b10273d381E'
- | '0x55d86d51Ac3bcAB7ab7d2124931FbA106c8b60c7'
- | '0x90040F96aB8f291b6d43A8972806e977631aFFdE'
+type TinlakeDataKey = keyof typeof tinlakeData
const tinlakeData = {
'0x53b2d22d07E069a3b132BfeaaD275b10273d381E': '7% - 15% target',
@@ -77,6 +75,7 @@ export const KeyMetrics = ({ poolId }: Props) => {
const tranchesIds = pool.tranches.map((tranche) => tranche.id)
const dailyTranches = useDailyTranchesStates(tranchesIds)
const theme = useTheme()
+ const cent = useCentrifuge()
const averageMaturity = useAverageMaturity(poolId)
const expenseRatio = useMemo(() => {
@@ -107,11 +106,6 @@ export const KeyMetrics = ({ poolId }: Props) => {
})
}, [metadata?.tranches, pool.currency.decimals])
- const getHardCodedApy = () => {
- if (poolId === '1655476167') return '15%'
- if (poolId === '1615768079') return '8% - 16%'
- }
-
const isBT3BT4 =
poolId === '0x53b2d22d07E069a3b132BfeaaD275b10273d381E' ||
poolId === '0x90040F96aB8f291b6d43A8972806e977631aFFdE' ||
@@ -123,11 +117,11 @@ export const KeyMetrics = ({ poolId }: Props) => {
value: `${capitalize(startCase(metadata?.pool?.asset?.class))} - ${metadata?.pool?.asset?.subClass}`,
},
{
- metric: poolId === '1655476167' || poolId === '1615768079' ? 'Target APY' : '30-day APY',
+ metric: centrifugeTargetAPYs[poolId as keyof typeof centrifugeTargetAPYs] ? 'Target APY' : '30-day APY',
value: tinlakeData[poolId as TinlakeDataKey]
? tinlakeData[poolId as TinlakeDataKey]
- : poolId === '1655476167' || poolId === '1615768079'
- ? getHardCodedApy()
+ : centrifugeTargetAPYs[poolId as keyof typeof centrifugeTargetAPYs]
+ ? centrifugeTargetAPYs[poolId as keyof typeof centrifugeTargetAPYs].join(' - ')
: tranchesAPY?.length
? tranchesAPY.map((tranche, index) => {
const formatted = formatPercentage(tranche)
@@ -171,36 +165,45 @@ export const KeyMetrics = ({ poolId }: Props) => {
metric: 'Pool structure',
value: isBT3BT4 ? 'Revolving' : metadata?.pool?.poolStructure ?? '-',
},
- ...(metadata?.pool?.rating?.ratingValue
+ ...(metadata?.pool?.poolRatings?.length
? [
{
metric: 'Rating',
value: (
-
- }
- >
-
- {metadata?.pool?.rating?.ratingAgency?.includes('moody') ? (
-
- ) : (
-
- )}
- {metadata?.pool?.rating?.ratingValue}
-
-
+
+ {metadata?.pool?.poolRatings.map((rating) => (
+
+ }
+ >
+
+ {rating.agency?.includes('moody') ? : }
+ {rating.value}
+
+
+ ))}
+
),
},
]
@@ -259,21 +262,26 @@ const TooltipBody = ({
{links ? (
links.map((link, index) => (
-
+
+
+
+ {link.text}
+
+
+
+
+ ))
+ ) : (
+
+
{subtitle}
- ))
- ) : (
-
-
- {subtitle}
-
-
+
+
)}
-
)
}
diff --git a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx
index 024f8bb18..5c9a84602 100644
--- a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx
+++ b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx
@@ -7,6 +7,7 @@ import { daysBetween } from '../../utils/date'
import { formatBalance, formatPercentage } from '../../utils/formatting'
import { usePool } from '../../utils/usePools'
import { DataTable } from '../DataTable'
+import { CentrifugeTargetAPYs, DYF_POOL_ID, NS3_POOL_ID, centrifugeTargetAPYs } from '../PoolCard'
import { PoolMetaDataPartial } from '../PoolList'
export const TrancheTokenCards = ({
@@ -32,9 +33,11 @@ export const TrancheTokenCards = ({
const columnConfig = useMemo(() => {
const calculateApy = (trancheToken: Token) => {
if (isTinlakePool && getTrancheText(trancheToken) === 'senior') return formatPercentage(trancheToken.apy)
- if (poolId === '1655476167') return '15%'
- if (poolId === '1615768079' && trancheToken.seniority === 0) return '8%'
- if (poolId === '1615768079' && trancheToken.seniority === 1) return '16%'
+ if (poolId === DYF_POOL_ID) return centrifugeTargetAPYs[poolId as CentrifugeTargetAPYs][0]
+ if (poolId === NS3_POOL_ID && trancheToken.seniority === 0)
+ return centrifugeTargetAPYs[poolId as CentrifugeTargetAPYs][0]
+ if (poolId === NS3_POOL_ID && trancheToken.seniority === 1)
+ return centrifugeTargetAPYs[poolId as CentrifugeTargetAPYs][1]
if (daysSinceCreation < 30) return 'N/A'
return trancheToken.yield30DaysAnnualized
? formatPercentage(new Perquintill(trancheToken.yield30DaysAnnualized))
@@ -49,7 +52,7 @@ export const TrancheTokenCards = ({
width: '40%',
},
{
- header: poolId === '1655476167' || poolId === '1615768079' ? 'Target' : 'APY',
+ header: poolId === DYF_POOL_ID || poolId === NS3_POOL_ID ? 'Target' : 'APY',
align: 'left',
formatter: (v: any) => (v ? calculateApy(v) : '-'),
},
diff --git a/centrifuge-app/src/pages/IssuerCreatePool/PoolRatingInput.tsx b/centrifuge-app/src/pages/IssuerCreatePool/PoolRatingInput.tsx
index c46c6c94d..b78183697 100644
--- a/centrifuge-app/src/pages/IssuerCreatePool/PoolRatingInput.tsx
+++ b/centrifuge-app/src/pages/IssuerCreatePool/PoolRatingInput.tsx
@@ -1,20 +1,74 @@
-import { Grid, Stack, Text, TextInput } from '@centrifuge/fabric'
+import { PoolMetadataInput } from '@centrifuge/centrifuge-js'
+import { Box, Button, FileUpload, Grid, IconMinusCircle, Shelf, Stack, Text, TextInput } from '@centrifuge/fabric'
+import { Field, FieldArray, FieldProps, useFormikContext } from 'formik'
import { FieldWithErrorMessage } from '../../components/FieldWithErrorMessage'
export function PoolRatingInput() {
+ const form = useFormikContext()
return (
-
- Pool rating
-
-
-
-
-
-
+
+ {({ push, remove }) => (
+
+
+ Pool rating
+
+
+
+ {form.values.poolRatings.length
+ ? form.values.poolRatings.map((rating, index) => (
+
+ <>
+
+
+
+
+
+ {({ field, meta, form }: FieldProps) => (
+ {
+ form.setFieldTouched(`poolRatings.${index}.reportFile`, true, false)
+ form.setFieldValue(`poolRatings.${index}.reportFile`, file)
+ }}
+ accept="application/pdf"
+ label="Report PDF"
+ placeholder="Choose file"
+ errorMessage={meta.touched && meta.error ? meta.error : undefined}
+ />
+ )}
+
+
+ >
+
+
+
+ ))
+ : null}
+
+
+ )}
+
)
}
diff --git a/centrifuge-app/src/pages/IssuerCreatePool/index.tsx b/centrifuge-app/src/pages/IssuerCreatePool/index.tsx
index 33eddcef8..27c95509d 100644
--- a/centrifuge-app/src/pages/IssuerCreatePool/index.tsx
+++ b/centrifuge-app/src/pages/IssuerCreatePool/index.tsx
@@ -3,6 +3,7 @@ import {
AddFee,
CurrencyKey,
FeeTypes,
+ FileType,
PoolMetadataInput,
TrancheInput,
} from '@centrifuge/centrifuge-js/dist/modules/pools'
@@ -91,7 +92,7 @@ export const createEmptyTranche = (trancheName: string): Tranche => ({
export type CreatePoolValues = Omit<
PoolMetadataInput,
- 'poolIcon' | 'issuerLogo' | 'executiveSummary' | 'adminMultisig' | 'poolFees' | 'poolReport'
+ 'poolIcon' | 'issuerLogo' | 'executiveSummary' | 'adminMultisig' | 'poolFees' | 'poolReport' | 'poolRatings'
> & {
poolIcon: File | null
issuerLogo: File | null
@@ -115,9 +116,12 @@ export type CreatePoolValues = Omit<
investorType: string
issuerShortDescription: string
issuerCategories: { type: string; value: string }[]
- ratingAgency: string
- ratingValue: string
- ratingReportUrl: string
+ poolRatings: {
+ agency?: string
+ value?: string
+ reportUrl?: string
+ reportFile?: File | null
+ }[]
poolStructure: string
}
@@ -150,9 +154,7 @@ const initialValues: CreatePoolValues = {
reportAuthorAvatar: null,
reportUrl: '',
- ratingAgency: '',
- ratingValue: '',
- ratingReportUrl: '',
+ poolRatings: [],
tranches: [createEmptyTranche('')],
adminMultisig: {
@@ -426,15 +428,20 @@ function CreatePoolForm() {
return
}
+ const pinFile = async (file: File): Promise => {
+ const pinned = await lastValueFrom(centrifuge.metadata.pinFile(await getFileDataURI(file)))
+ return { uri: pinned.uri, mime: file.type }
+ }
+
// Handle pinning files (pool icon, issuer logo, and executive summary)
- const promises = [lastValueFrom(centrifuge.metadata.pinFile(await getFileDataURI(values.poolIcon)))]
+ const promises = [pinFile(values.poolIcon)]
if (values.issuerLogo) {
- promises.push(lastValueFrom(centrifuge.metadata.pinFile(await getFileDataURI(values.issuerLogo))))
+ promises.push(pinFile(values.issuerLogo))
}
if (!isTestEnv && values.executiveSummary) {
- promises.push(lastValueFrom(centrifuge.metadata.pinFile(await getFileDataURI(values.executiveSummary))))
+ promises.push(pinFile(values.executiveSummary))
}
const [pinnedPoolIcon, pinnedIssuerLogo, pinnedExecSummary] = await Promise.all(promises)
@@ -454,9 +461,7 @@ function CreatePoolForm() {
if (values.reportUrl) {
let avatar = null
if (values.reportAuthorAvatar) {
- const pinned = await lastValueFrom(
- centrifuge.metadata.pinFile(await getFileDataURI(values.reportAuthorAvatar))
- )
+ const pinned = await pinFile(values.reportAuthorAvatar)
avatar = { uri: pinned.uri, mime: values.reportAuthorAvatar.type }
}
metadataValues.poolReport = {
@@ -466,12 +471,25 @@ function CreatePoolForm() {
url: values.reportUrl,
}
}
- if (values.ratingReportUrl) {
- metadataValues.poolRating = {
- ratingAgency: values.ratingAgency,
- ratingValue: values.ratingValue,
- ratingReportUrl: values.ratingReportUrl,
- }
+ if (values.poolRatings) {
+ const newRatingReportPromise = await Promise.all(
+ values.poolRatings.map((rating) => (rating.reportFile ? pinFile(rating.reportFile) : null))
+ )
+ const ratings = values.poolRatings.map((rating, index) => {
+ let reportFile: FileType | null = rating.reportFile
+ ? { uri: rating.reportFile.name, mime: rating.reportFile.type }
+ : null
+ if (rating.reportFile && newRatingReportPromise[index]?.uri) {
+ reportFile = newRatingReportPromise[index] ?? null
+ }
+ return {
+ agency: rating.agency ?? '',
+ value: rating.value ?? '',
+ reportUrl: rating.reportUrl ?? '',
+ reportFile: reportFile ?? null,
+ }
+ })
+ metadataValues.poolRatings = ratings
}
const nonJuniorTranches = metadataValues.tranches.slice(1)
@@ -631,7 +649,7 @@ function CreatePoolForm() {
{({ field, form, meta }: FieldProps) => (
}
+ label={}
onChange={(event) => form.setFieldValue('poolType', event.target.value)}
onBlur={field.onBlur}
errorMessage={meta.touched && meta.error ? meta.error : undefined}
@@ -668,7 +686,7 @@ function CreatePoolForm() {
{({ field, meta, form }: FieldProps) => (
}
+ label={}
onChange={(event) => {
form.setFieldValue('assetClass', event.target.value)
form.setFieldValue('subAssetClass', '', false)
@@ -687,7 +705,7 @@ function CreatePoolForm() {
{({ field, meta, form }: FieldProps) => (
}
+ label={}
onChange={(event: any) => form.setFieldValue('investorType', event.target.value)}
onBlur={field.onBlur}
errorMessage={meta.touched && meta.error ? meta.error : undefined}
@@ -719,7 +737,7 @@ function CreatePoolForm() {
return (
}
+ label={}
onChange={(event) => form.setFieldValue('currency', event.target.value)}
onBlur={field.onBlur}
errorMessage={meta.touched && meta.error ? meta.error : undefined}
diff --git a/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx b/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx
index e7edcf09d..d1b984e9c 100644
--- a/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx
+++ b/centrifuge-app/src/pages/IssuerPool/Configuration/Issuer.tsx
@@ -34,9 +34,7 @@ type Values = Pick<
| 'reportUrl'
| 'reportAuthorName'
| 'reportAuthorTitle'
- | 'ratingAgency'
- | 'ratingValue'
- | 'ratingReportUrl'
+ | 'poolRatings'
> & {
reportAuthorAvatar: string | null | File
}
@@ -73,9 +71,11 @@ export function Issuer() {
reportAuthorAvatar: metadata?.pool?.reports?.[0]?.author?.avatar
? `avatar.${metadata.pool.reports[0].author.avatar.mime?.split('/')[1]}`
: null,
- ratingAgency: metadata?.pool?.rating?.ratingAgency ?? '',
- ratingValue: metadata?.pool?.rating?.ratingValue ?? '',
- ratingReportUrl: metadata?.pool?.rating?.ratingReportUrl ?? '',
+ poolRatings:
+ metadata?.pool?.poolRatings?.map((rating) => ({
+ ...rating,
+ reportFile: rating.reportFile ? `report.${rating.reportFile.mime?.split('/')[1]}` : ('' as any),
+ })) ?? [],
}),
[metadata, logoFile]
)
@@ -93,21 +93,24 @@ export function Issuer() {
const execSummaryChanged = values.executiveSummary !== initialValues.executiveSummary
const logoChanged = values.issuerLogo !== initialValues.issuerLogo
+ const pinFile = async (file: File) => {
+ const pinned = await lastValueFrom(cent.metadata.pinFile(await getFileDataURI(file as File)))
+ return { uri: pinned.uri, mime: (file as File).type }
+ }
+
if (!hasChanges) {
setIsEditing(false)
actions.setSubmitting(false)
return
}
let execSummaryUri
- if (execSummaryChanged) {
- execSummaryUri = (
- await lastValueFrom(cent.metadata.pinFile(await getFileDataURI(values.executiveSummary as File)))
- ).uri
+ if (execSummaryChanged && values.executiveSummary) {
+ execSummaryUri = (await pinFile(values.executiveSummary)).uri
prefetchMetadata(execSummaryUri)
}
let logoUri
if (logoChanged && values.issuerLogo) {
- logoUri = (await lastValueFrom(cent.metadata.pinFile(await getFileDataURI(values.issuerLogo as File)))).uri
+ logoUri = (await pinFile(values.issuerLogo)).uri
prefetchMetadata(logoUri)
}
const newPoolMetadata: PoolMetadata = {
@@ -132,11 +135,6 @@ export function Issuer() {
website: values.website,
},
details: values.details,
- rating: {
- ratingAgency: values.ratingAgency,
- ratingValue: values.ratingValue,
- ratingReportUrl: values.ratingReportUrl,
- },
},
}
@@ -144,9 +142,7 @@ export function Issuer() {
let avatar = null
const avatarChanged = values.reportAuthorAvatar !== initialValues.reportAuthorAvatar
if (avatarChanged && values.reportAuthorAvatar) {
- const pinned = await lastValueFrom(
- cent.metadata.pinFile(await getFileDataURI(values.reportAuthorAvatar as File))
- )
+ const pinned = await pinFile(values.reportAuthorAvatar as File)
avatar = { uri: pinned.uri, mime: (values.reportAuthorAvatar as File).type }
}
newPoolMetadata.pool.reports = [
@@ -161,6 +157,37 @@ export function Issuer() {
]
}
+ if (values.poolRatings) {
+ const updatedRatings = await Promise.all(
+ values.poolRatings.map(async (newRating, index) => {
+ const existingRating = oldMetadata.pool.poolRatings?.[index]
+
+ if (JSON.stringify(newRating) === JSON.stringify(existingRating)) {
+ return existingRating
+ }
+
+ const newReportFile = typeof newRating.reportFile === 'object' ? newRating.reportFile : null
+ // remove the existing reportFile from the newRating so we don't accidentally overwrite it with the string representation
+ // the existing reportFile will still be captured in the existingRating
+ delete newRating.reportFile
+ const mergedRating = { ...existingRating, ...newRating }
+
+ if (newReportFile) {
+ try {
+ const pinnedFile = await pinFile(newReportFile)
+ mergedRating.reportFile = pinnedFile
+ } catch (error) {
+ console.error('Error pinning file:', error)
+ }
+ }
+
+ return mergedRating
+ })
+ )
+
+ newPoolMetadata.pool.poolRatings = updatedRatings as PoolMetadata['pool']['poolRatings']
+ }
+
execute([poolId, newPoolMetadata], { account })
actions.setSubmitting(false)
},
@@ -220,7 +247,7 @@ export function Issuer() {
{metadata?.pool?.reports?.[0] && }
- {metadata?.pool?.rating && }
+ {metadata?.pool?.poolRatings?.length && }
)}
diff --git a/centrifuge-app/src/pages/Pool/Overview/index.tsx b/centrifuge-app/src/pages/Pool/Overview/index.tsx
index 074ecee36..49b124d10 100644
--- a/centrifuge-app/src/pages/Pool/Overview/index.tsx
+++ b/centrifuge-app/src/pages/Pool/Overview/index.tsx
@@ -3,7 +3,7 @@ import { useWallet } from '@centrifuge/centrifuge-react'
import { Box, Button, Card, Grid, TextWithPlaceholder } from '@centrifuge/fabric'
import Decimal from 'decimal.js-light'
import * as React from 'react'
-import { useParams } from 'react-router'
+import { useNavigate, useParams } from 'react-router'
import styled, { useTheme } from 'styled-components'
import { InvestRedeemContext, InvestRedeemProvider } from '../../../../src/components/InvestRedeem/InvestRedeemProvider'
import { InvestRedeemProps } from '../../../components/InvestRedeem/InvestRedeem'
@@ -155,6 +155,7 @@ export function InvestButton(props: InvestRedeemProps) {
const [open, setOpen] = React.useState(false)
const connectAndOpen = useConnectBeforeAction(() => setOpen(true))
const { connectedType, showNetworks } = useWallet()
+ const navigate = useNavigate()
const getButtonText = (state: any) => {
if (!state.isAllowedToInvest && connectedType !== null) {
@@ -181,7 +182,9 @@ export function InvestButton(props: InvestRedeemProps) {