Skip to content

Commit

Permalink
Merge branch 'main' into onboarding/prepare-for-chain-upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
sophialittlejohn authored Sep 6, 2023
2 parents 13ab6b0 + bc39545 commit f0b31b6
Show file tree
Hide file tree
Showing 18 changed files with 117 additions and 44 deletions.
17 changes: 17 additions & 0 deletions centrifuge-app/src/components/DebugFlags/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react'
import { config } from '../../config'
import { ConvertEvmAddress } from './components/ConvertEvmAddress'

const params = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : {})
Expand Down Expand Up @@ -45,6 +46,8 @@ export type Key =
| 'editAdminConfig'
| 'showPodAccountCreation'
| 'convertEvmAddress'
| 'showPortfolio'
| 'poolCreationType'

export const flagsConfig: Record<Key, DebugFlagConfig> = {
address: {
Expand Down Expand Up @@ -113,4 +116,18 @@ export const flagsConfig: Record<Key, DebugFlagConfig> = {
default: null,
alwaysShow: true,
},
showPortfolio: {
type: 'checkbox',
default: false,
alwaysShow: true,
},
poolCreationType: {
type: 'select',
default: config.poolCreationType || 'immediate',
options: {
immediate: 'immediate',
propose: 'propose',
notePreimage: 'notePreimage',
},
},
}
2 changes: 1 addition & 1 deletion centrifuge-app/src/components/LayoutBase/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export const LogoContainer = styled(Stack)`

export const WalletContainer = styled(Stack)`
z-index: ${({ theme }) => theme.zIndices.header};
position: sticky;
/* position: sticky; */
top: 0;
grid-area: wallet;
// WalletContainer & WalletPositioner are positioned above the main content and would block user interaction (click).
Expand Down
32 changes: 28 additions & 4 deletions centrifuge-app/src/components/PortfolioCta/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { formatBalance, formatBalanceAbbreviated } from '../../utils/formatting'
import { useAddress } from '../../utils/useAddress'
import { useListedPools } from '../../utils/useListedPools'
import { useLoansAcrossPools } from '../../utils/useLoans'
import { useDebugFlags } from '../DebugFlags'
import { useComputeLiquidityRewards } from '../LiquidityRewards/hooks'
import { Cubes } from './Cubes'

Expand All @@ -19,6 +20,7 @@ export function PortfolioCta() {
const balances = useBalances(address)
const consts = useCentrifugeConsts()
const [, listedTokens] = useListedPools()
const { showPortfolio } = useDebugFlags()

const stakes = balances?.tranches.map(({ poolId, trancheId }) => ({ poolId, trancheId })) ?? []
const rewards = useComputeLiquidityRewards(address, stakes)
Expand Down Expand Up @@ -55,7 +57,7 @@ export function PortfolioCta() {
},
]

return (
return showPortfolio ? (
<Box
as="article"
position="relative"
Expand All @@ -65,7 +67,7 @@ export function PortfolioCta() {
borderRadius="card"
borderStyle="solid"
borderWidth={1}
borderColor="borderSecondary"
borderColor={'borderSecondary'}
style={{
boxShadow: `0px 3px 2px -2px ${colors.borderPrimary}`,
}}
Expand All @@ -78,7 +80,6 @@ export function PortfolioCta() {
<Text as="h2" variant="heading2">
Your portfolio
</Text>

<Shelf as="dl" gap={6} flexWrap="wrap" rowGap={2}>
{terms.map(({ title, value }, index) => (
<Stack key={`${title}${index}`} gap="4px">
Expand All @@ -102,5 +103,28 @@ export function PortfolioCta() {
)}
</Stack>
</Box>
)
) : !address ? (
<Box
as="article"
position="relative"
p={3}
pb={5}
overflow="hidden"
borderRadius="card"
borderStyle="solid"
borderWidth={1}
borderColor={'borderSecondary'}
style={{
boxShadow: `0px 3px 2px -2px ${colors.borderPrimary}`,
}}
>
{!address && <Cubes />}
<Stack gap={2} alignItems="start">
<Text as="h2" variant="body1" style={{ maxWidth: '35ch' }}>
Pools on Centrifuge let investors earn yield from real-world assets.
</Text>
<Button onClick={() => showNetworks()}>Get started</Button>
</Stack>
</Box>
) : null
}
17 changes: 10 additions & 7 deletions centrifuge-app/src/pages/IssuerCreatePool/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CurrencyBalance, isSameAddress, Perquintill, Rate } from '@centrifuge/centrifuge-js'
import { CurrencyBalance, isSameAddress, Perquintill, Rate, TransactionOptions } from '@centrifuge/centrifuge-js'
import { CurrencyKey, PoolMetadataInput, TrancheInput } from '@centrifuge/centrifuge-js/dist/modules/pools'
import {
useBalances,
Expand All @@ -25,6 +25,7 @@ import { Field, FieldProps, Form, FormikErrors, FormikProvider, setIn, useFormik
import * as React from 'react'
import { useHistory } from 'react-router'
import { combineLatest, lastValueFrom, switchMap, tap } from 'rxjs'
import { useDebugFlags } from '../../components/DebugFlags'
import { PreimageHashDialog } from '../../components/Dialogs/PreimageHashDialog'
import { ShareMultisigDialog } from '../../components/Dialogs/ShareMultisigDialog'
import { FieldWithErrorMessage } from '../../components/FieldWithErrorMessage'
Expand Down Expand Up @@ -154,6 +155,8 @@ function CreatePoolForm() {
const [preimageHash, setPreimageHash] = React.useState('')
const [createdPoolId, setCreatedPoolId] = React.useState('')
const [multisigData, setMultisigData] = React.useState<{ hash: string; callData: string }>()
const { poolCreationType } = useDebugFlags()
const createType = (poolCreationType as TransactionOptions['createType']) || config.poolCreationType || 'immediate'

React.useEffect(() => {
// If the hash can't be found on Pinata the request can take a long time to time out
Expand Down Expand Up @@ -182,7 +185,7 @@ function CreatePoolForm() {
notePreimage: 'Note preimage',
}
const { execute: createPoolTx, isLoading: transactionIsPending } = useCentrifugeTransaction(
`${txMessage[config.poolCreationType || 'immediate']} 2/2`,
`${txMessage[createType]} 2/2`,
(cent) =>
(
args: [
Expand Down Expand Up @@ -251,15 +254,15 @@ function CreatePoolForm() {
onSuccess: (args) => {
if (form.values.adminMultisigEnabled) setIsMultisigDialogOpen(true)
const [, , , poolId] = args
if (config.poolCreationType === 'immediate') {
if (createType === 'immediate') {
setCreatedPoolId(poolId)
}
},
}
)

const { execute: createProxies, isLoading: createProxiesIsPending } = useCentrifugeTransaction(
`${txMessage[config.poolCreationType || 'immediate']} 1/2`,
`${txMessage[createType]} 1/2`,
(cent) => {
return (_: [nextTx: (adminProxy: string, aoProxy: string) => void], options) =>
cent.getApi().pipe(
Expand Down Expand Up @@ -395,7 +398,7 @@ function CreatePoolForm() {
CurrencyBalance.fromFloat(values.maxReserve, currency.decimals),
metadataValues,
],
{ createType: config.poolCreationType }
{ createType }
)
},
])
Expand All @@ -420,7 +423,7 @@ function CreatePoolForm() {
}, [isStoredIssuerLoading])

React.useEffect(() => {
if (config.poolCreationType === 'notePreimage') {
if (createType === 'notePreimage') {
const $events = centrifuge
.getEvents()
.pipe(
Expand All @@ -436,7 +439,7 @@ function CreatePoolForm() {
.subscribe()
return () => $events.unsubscribe()
}
}, [centrifuge])
}, [centrifuge, createType])

const formRef = React.useRef<HTMLFormElement>(null)
useFocusInvalidInput(form, formRef)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { useWallet } from '@centrifuge/centrifuge-react'
import { useCentrifuge, useWallet } from '@centrifuge/centrifuge-react'
import { encodeAddress } from '@polkadot/util-crypto'
import { useQuery } from 'react-query'
import { getSelectedWallet } from '../../../utils/getSelectedWallet'

export const useGlobalOnboardingStatus = () => {
const wallet = useWallet()
const cent = useCentrifuge()

const selectedWallet = getSelectedWallet(wallet)

const query = useQuery(
['global-onboarding-status', selectedWallet?.address],
async () => {
if (selectedWallet) {
if (selectedWallet && selectedWallet.address) {
const chainId = await cent.getChainId()
const address =
selectedWallet.network === 'substrate'
? encodeAddress(selectedWallet.address, chainId)
: selectedWallet.address
const response = await fetch(
`${import.meta.env.REACT_APP_ONBOARDING_API_URL}/getGlobalOnboardingStatus?address=${
selectedWallet.address
}&network=${selectedWallet.network}`,
`${import.meta.env.REACT_APP_ONBOARDING_API_URL}/getGlobalOnboardingStatus?address=${address}&network=${
selectedWallet.network
}&chainId=${wallet.connectedNetwork}`,
{
method: 'GET',
headers: {
Expand Down
4 changes: 3 additions & 1 deletion centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ export const useSignRemark = (
// @ts-expect-error
blockNumber = result.blockNumber.toString()
}
const chainId = connectedNetwork === 'centrifuge' ? await centrifuge.getChainId() : connectedNetwork

await sendDocumentsToIssuer({
txHash,
blockNumber,
isEvmOnSubstrate,
chainId: connectedNetwork || 'centrifuge',
chainId: chainId || 136,
})
setIsSubstrateTxLoading(false)
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion centrifuge-app/src/pages/Pools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function poolsToPoolCardProps(
status:
tinlakePool && tinlakePool.addresses.CLERK !== undefined && tinlakePool.tinlakeMetadata.maker?.ilk
? 'Maker Pool'
: pool.tranches.at(-1)?.capacity.toFloat()
: pool.tranches.at(0)?.capacity.toFloat() // pool is displayed as "open for investments" if the most junior tranche has a capacity
? 'Open for investments'
: ('Closed' as PoolStatusKey),
iconUri: metaData?.pool?.icon?.uri ? cent.metadata.parseMetadataUrl(metaData?.pool?.icon?.uri) : undefined,
Expand Down
4 changes: 3 additions & 1 deletion centrifuge-app/src/utils/usePodAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { usePodUrl } from './usePools'
export function usePodAuth(poolId: string, accountOverride?: CombinedSubstrateAccount) {
const { selectedCombinedAccount } = useWallet().substrate
const podUrl = usePodUrl(poolId)
const suitableAccounts = useSuitableAccounts({ poolId, poolRole: ['Borrower'], proxyType: ['PodAuth'] })
const suitableAccounts = useSuitableAccounts({ poolId, poolRole: ['Borrower'], proxyType: ['PodAuth'] }).filter(
(acc) => acc.proxies?.length === 1
)
const account = accountOverride || selectedCombinedAccount || suitableAccounts[0]
const cent = useCentrifuge()
const utils = useCentrifugeUtils()
Expand Down
16 changes: 13 additions & 3 deletions onboarding-api/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# VARIABLES
REDIRECT_URL=http://localhost:3000
MEMBERLIST_ADMIN_PURE_PROXY=kAM1ELFDHdHeLDAkAdwEnfufoCL5hpUycGs4ZQkSQKVpHFoXm
COLLATOR_WSS_URL=wss://fullnode.algol.cntrfg.com/public-ws
RELAY_WSS_URL=wss://fullnode-relay.algol.cntrfg.com
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
EVM_NETWORK=goerli
ONBOARDING_STORAGE_BUCKET=centrifuge-onboarding-api-dev
# SECRETS
SHUFTI_PRO_SECRET_KEY=
SHUFTI_PRO_CLIENT_ID=
NODE_ENV=development
JWT_SECRET=
REDIRECT_URL=http://localhost:3000
SENDGRID_API_KEY=
SENDGRID_API_KEY=
COOKIE_SECRET=
EVM_MEMBERLIST_ADMIN_PRIVATE_KEY=
PURE_PROXY_CONTROLLER_SEED=
12 changes: 3 additions & 9 deletions onboarding-api/src/controllers/user/getGlobalOnboardingStatus.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { Request, Response } from 'express'
import { InferType, mixed, object, string } from 'yup'
import { SupportedNetworks } from '../../database'
import { walletSchema } from '../../database'
import { fetchUser } from '../../utils/fetchUser'
import { reportHttpError } from '../../utils/httpError'
import { validateInput } from '../../utils/validateInput'

const getGlobalOnboardingStatusInput = object({
address: string().required(),
network: mixed<SupportedNetworks>().required().oneOf(['evm', 'substrate']),
})

export const getGlobalOnboardingStatusController = async (
req: Request<{}, {}, {}, InferType<typeof getGlobalOnboardingStatusInput>>,
req: Request<{}, {}, {}, Request['wallet']>,
res: Response
) => {
try {
await validateInput(req.query, getGlobalOnboardingStatusInput)
await validateInput(req.query, walletSchema)

const user = await fetchUser(req.query, { suppressError: true })

Expand Down
4 changes: 2 additions & 2 deletions onboarding-api/src/controllers/user/updateInvestorStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ export const updateInvestorStatusController = async (
metadata,
countersignedAgreementPDF
),
sendApproveIssuerMessage(wallet.address, metadata, tranche as Pool['tranches'][0], countersignedAgreementPDF),
sendApproveIssuerMessage(wallet, metadata, tranche as Pool['tranches'][0], countersignedAgreementPDF),
validateAndWriteToFirestore(wallet, updatedUser, user.investorType, ['poolSteps']),
])
return res.status(200).send({ status: 'approved', poolId, trancheId, txHash })
} else if (user?.email && status === 'rejected') {
await Promise.all([
sendRejectInvestorMessage(user.email, metadata),
sendRejectInvestorMessage(user.email, tranche as Pool['tranches'][0], metadata),
validateAndWriteToFirestore(wallet, updatedUser, user.investorType, ['poolSteps']),
])
return res.status(200).send({ status: 'rejected', poolId, trancheId })
Expand Down
4 changes: 2 additions & 2 deletions onboarding-api/src/database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const uboSchema = object({
countryOfCitizenship: string().required(),
})

const walletSchema = object({
export const walletSchema = object({
evm: array().of(string()),
substrate: array().of(string()),
evmOnSubstrate: array().of(string()),
Expand Down Expand Up @@ -115,7 +115,7 @@ export const individualUserSchema = object({
investorType: string().default('individual') as StringSchema<Individual>,
wallets: walletSchema,
kycReference: string().optional(),
email: string().default(null).nullable(), // TODO: coming soon
email: string().default(null).nullable(),
name: string().required(),
dateOfBirth: string().required(),
countryOfCitizenship: string().required(), // TODO: validate with list of countries
Expand Down
1 change: 0 additions & 1 deletion onboarding-api/src/emails/sendApproveInvestorMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export const sendApproveInvestorMessage = async (
},
],
dynamic_template_data: {
poolName: poolMetadata?.pool.name,
trancheName: tranche?.currency.name,
poolUrl: `${process.env.REDIRECT_URL}/pools/${poolId}`,
},
Expand Down
10 changes: 7 additions & 3 deletions onboarding-api/src/emails/sendApproveIssuerMessage.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Pool } from '@centrifuge/centrifuge-js'
import { Request } from 'express'
import { sendEmail, templateIds } from '.'
import { fetchUser } from '../utils/fetchUser'

export const sendApproveIssuerMessage = async (
walletAddress: string,
wallet: Request['wallet'],
metadata: Record<string, any>,
tranche: Pool['tranches'][0],
countersignedAgreementPDF: Uint8Array
) => {
const user = await fetchUser(wallet)
const message = {
personalizations: [
{
Expand All @@ -16,7 +19,8 @@ export const sendApproveIssuerMessage = async (
},
],
dynamic_template_data: {
tokenName: tranche.currency.name,
trancheName: tranche.currency.name,
investorEmail: user.email,
},
},
],
Expand All @@ -28,7 +32,7 @@ export const sendApproveIssuerMessage = async (
attachments: [
{
content: Buffer.from(countersignedAgreementPDF).toString('base64'),
filename: `${walletAddress}-${tranche.currency.name?.replaceAll(' ', '-')}-subscription-agreement.pdf`,
filename: `${wallet.address}-${tranche.currency.name?.replaceAll(' ', '-')}-subscription-agreement.pdf`,
type: 'application/pdf',
disposition: 'attachment',
},
Expand Down
Loading

0 comments on commit f0b31b6

Please sign in to comment.