From dfbd6ae2845c5b33dec4d161ffbe9c35827e169d Mon Sep 17 00:00:00 2001 From: Katty Barroso <51223655+kattylucy@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:29:31 +0100 Subject: [PATCH] Pool structure - create pool (#2543) * Add pool structure UI changes * Small UI fix * Avoid deletion on entries if less than one * Add logic to single / multi sign * Fix linter errors --- .../src/components/PoolCard/index.tsx | 2 +- .../PoolOverview/TrancheTokenCards.tsx | 7 +- centrifuge-app/src/components/Tooltips.tsx | 20 + .../IssuerCreatePool/IssuerCategories.tsx | 17 +- .../IssuerCreatePool/PoolDetailsSection.tsx | 2 - .../IssuerCreatePool/PoolSetupSection.tsx | 345 ++++++++++-------- .../IssuerCreatePool/PoolStructureSection.tsx | 7 +- .../src/pages/IssuerCreatePool/index.tsx | 2 +- .../src/pages/IssuerCreatePool/oldindex.tsx | 5 + .../src/pages/IssuerCreatePool/types.ts | 3 +- .../IssuerPool/Access/AssetOriginators.tsx | 1 + .../src/pages/Pool/Assets/index.tsx | 4 +- centrifuge-js/src/modules/pools.ts | 8 +- 13 files changed, 253 insertions(+), 170 deletions(-) diff --git a/centrifuge-app/src/components/PoolCard/index.tsx b/centrifuge-app/src/components/PoolCard/index.tsx index 82b4109a3b..a68974abc1 100644 --- a/centrifuge-app/src/components/PoolCard/index.tsx +++ b/centrifuge-app/src/components/PoolCard/index.tsx @@ -199,7 +199,7 @@ export function PoolCard({ } }) .reverse() - }, [isTinlakePool, metaData?.tranches, tinlakeKey, tranches]) + }, [isTinlakePool, metaData?.tranches, tinlakeKey, tranches, createdAt, poolId]) return ( diff --git a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx index be41ed4ab6..927b584c4c 100644 --- a/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx +++ b/centrifuge-app/src/components/PoolOverview/TrancheTokenCards.tsx @@ -42,9 +42,6 @@ export const TrancheTokenCards = ({ return 'mezzanine' } - const getTarget = (tranche: Token) => - (isTinlakePool && tranche.seniority === 0) || poolId === DYF_POOL_ID || poolId === NS3_POOL_ID - const columns = useMemo(() => { return [ { @@ -121,6 +118,8 @@ export const TrancheTokenCards = ({ }, [pool.tranches, metadata, poolId, pool?.currency.symbol]) const dataTable = useMemo(() => { + const getTarget = (tranche: Token) => + (isTinlakePool && tranche.seniority === 0) || poolId === DYF_POOL_ID || poolId === NS3_POOL_ID return trancheTokens.map((tranche) => { const calculateApy = (trancheToken: Token) => { if (isTinlakePool && getTrancheText(trancheToken) === 'senior') return formatPercentage(trancheToken.apy) @@ -145,7 +144,7 @@ export const TrancheTokenCards = ({ isTarget: getTarget(tranche), } }) - }, [trancheTokens, getTarget]) + }, [trancheTokens, daysSinceCreation, isTinlakePool, poolId]) return ( diff --git a/centrifuge-app/src/components/Tooltips.tsx b/centrifuge-app/src/components/Tooltips.tsx index c7bc13c28b..0bca55ddcf 100644 --- a/centrifuge-app/src/components/Tooltips.tsx +++ b/centrifuge-app/src/components/Tooltips.tsx @@ -358,6 +358,26 @@ export const tooltipText = { label: '', body: 'This pool will have three classes. Senior tranche is the safest tranche with priority in repayment. Mezzanine tranche has intermediate risk and receives payment after Senior tranche obligations are met. Junior tranche which only receives returns after both Senior and Mezzanine tranches are paid.', }, + singleMultisign: { + label: '', + body: 'Setup a wallet where only one private key is required to authorise changes to the pool configuration.', + }, + multiMultisign: { + label: '', + body: 'Setup a wallet that requires multiple private keys to authorise changes to the pool configuration.', + }, + centrifugeOnboarding: { + label: '', + body: 'Investors will go through the Centrifuge onboarding provider, Shuftipro, before they can invest in your pool.', + }, + externalOnboarding: { + label: '', + body: 'You can select the provider you want to KYC/onboard your investors.', + }, + noneOnboarding: { + label: '', + body: 'You can directly whitelist the addresses that can invest in the pool.', + }, } export type TooltipsProps = { diff --git a/centrifuge-app/src/pages/IssuerCreatePool/IssuerCategories.tsx b/centrifuge-app/src/pages/IssuerCreatePool/IssuerCategories.tsx index 09b981e850..4d70a2f345 100644 --- a/centrifuge-app/src/pages/IssuerCreatePool/IssuerCategories.tsx +++ b/centrifuge-app/src/pages/IssuerCreatePool/IssuerCategories.tsx @@ -16,13 +16,15 @@ const PROVIDERS = [ { label: 'Other', value: 'other' }, ] -const LabelWithDeleteButton = ({ onDelete }: { onDelete: () => void }) => { +const LabelWithDeleteButton = ({ onDelete, hideButton }: { onDelete: () => void; hideButton: boolean }) => { return ( Name of provider - - - + {!hideButton && ( + + + + )} ) } @@ -56,7 +58,12 @@ export const IssuerCategoriesSection = () => { {({ field, meta }: FieldProps) => ( remove(index)} />} + label={ + remove(index)} + hideButton={form.values.issuerCategories.length === 1} + /> + } placeholder="Type here..." maxLength={100} /> diff --git a/centrifuge-app/src/pages/IssuerCreatePool/PoolDetailsSection.tsx b/centrifuge-app/src/pages/IssuerCreatePool/PoolDetailsSection.tsx index 7975d4fa94..97178d4d75 100644 --- a/centrifuge-app/src/pages/IssuerCreatePool/PoolDetailsSection.tsx +++ b/centrifuge-app/src/pages/IssuerCreatePool/PoolDetailsSection.tsx @@ -11,7 +11,6 @@ import { TextInput, } from '@centrifuge/fabric' import { Field, FieldProps, useFormikContext } from 'formik' -import { useTheme } from 'styled-components' import { FieldWithErrorMessage } from '../../../src/components/FieldWithErrorMessage' import { Tooltips } from '../../../src/components/Tooltips' import { isTestEnv } from '../../../src/config' @@ -27,7 +26,6 @@ export const AddButton = ({ onClick }: { onClick: () => void }) => ( ) export const PoolDetailsSection = () => { - const theme = useTheme() const form = useFormikContext() const createLabel = (label: string) => `${label}${isTestEnv ? '' : '*'}` diff --git a/centrifuge-app/src/pages/IssuerCreatePool/PoolSetupSection.tsx b/centrifuge-app/src/pages/IssuerCreatePool/PoolSetupSection.tsx index 92dd2f95fe..b3bfede001 100644 --- a/centrifuge-app/src/pages/IssuerCreatePool/PoolSetupSection.tsx +++ b/centrifuge-app/src/pages/IssuerCreatePool/PoolSetupSection.tsx @@ -1,7 +1,6 @@ import { PoolMetadataInput } from '@centrifuge/centrifuge-js' import { Box, - Button, Checkbox, FileUpload, Grid, @@ -14,16 +13,14 @@ import { Text, TextInput, } from '@centrifuge/fabric' -import { Field, FieldProps, useFormikContext } from 'formik' +import { Field, FieldArray, FieldProps, useFormikContext } from 'formik' import { useTheme } from 'styled-components' import { FieldWithErrorMessage } from '../../../src/components/FieldWithErrorMessage' import { Tooltips } from '../../../src/components/Tooltips' -import { feeCategories, isTestEnv } from '../../../src/config' +import { feeCategories } from '../../../src/config' import { AddButton } from './PoolDetailsSection' import { CheckboxOption, Line, StyledGrid } from './PoolStructureSection' -const MAX_FEES = 5 - const FEE_TYPES = [ { label: 'Direct charge', value: 'chargedUpTo' }, { label: 'Fixed %', value: 'fixed' }, @@ -35,18 +32,12 @@ export const PoolSetupSection = () => { const theme = useTheme() const form = useFormikContext() const { values } = form - const createLabel = (label: string) => `${label}${isTestEnv ? '' : '*'}` return ( - - - Management setup - - - + + Management setup + Pool managers* @@ -57,29 +48,57 @@ export const PoolSetupSection = () => { Security requirement } /> } /> - + Wallet addresses - - {({ field, form }: FieldProps) => } - - - {({ field, form }: FieldProps) => } - - + + {({ push }) => ( + <> + {values.adminMultisigEnabled ? ( + values.adminMultisig?.signers?.map((_, index) => ( + + + {({ field }: FieldProps) => } + + + )) + ) : ( + + + {({ field }: FieldProps) => } + + + )} + {values.adminMultisigEnabled && ( + + { + if (form.values.adminMultisig && form.values.adminMultisig.signers?.length <= 10) { + push('') + } + }} + /> + + )} + + )} + + @@ -88,12 +107,18 @@ export const PoolSetupSection = () => { {({ field, meta, form }: FieldProps) => ( form.setFieldValue(`poolFees.${index}.category`, event.target.value)} - onBlur={field.onBlur} - errorMessage={meta.touched && meta.error ? meta.error : undefined} - value={field.value} - options={feeCategories.map((cat) => ({ label: cat, value: cat }))} - /> - )} - - - {({ field, meta }: FieldProps) => { - return ( - { - form.setFieldValue(`poolFees.${index}.feeType`, event.target.value) - }} - onBlur={field.onBlur} - errorMessage={meta.touched && meta.error ? meta.error : undefined} - value={field.value} - options={FEE_TYPES} + + {({ field, meta }: FieldProps) => ( + form.setFieldValue(`poolFees.${index}.feePosition`, event.target.value)} + onBlur={field.onBlur} + errorMessage={meta.touched && meta.error ? meta.error : undefined} + value={field.value} + options={FEE_POSISTIONS} + /> + )} + + + {({ field, meta }: FieldProps) => ( +