diff --git a/centrifuge-app/src/pages/Onboarding/queries/useGlobalOnboardingStatus.ts b/centrifuge-app/src/pages/Onboarding/queries/useGlobalOnboardingStatus.ts index 570129cdbf..5cbd235278 100644 --- a/centrifuge-app/src/pages/Onboarding/queries/useGlobalOnboardingStatus.ts +++ b/centrifuge-app/src/pages/Onboarding/queries/useGlobalOnboardingStatus.ts @@ -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: { diff --git a/centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts b/centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts index 092d143cd4..562b1e3115 100644 --- a/centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts +++ b/centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts @@ -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) { diff --git a/onboarding-api/.env.example b/onboarding-api/.env.example index 23f2d5e640..309a009571 100644 --- a/onboarding-api/.env.example +++ b/onboarding-api/.env.example @@ -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= \ No newline at end of file +SENDGRID_API_KEY= +COOKIE_SECRET= +EVM_MEMBERLIST_ADMIN_PRIVATE_KEY= +PURE_PROXY_CONTROLLER_SEED= \ No newline at end of file diff --git a/onboarding-api/src/controllers/user/getGlobalOnboardingStatus.ts b/onboarding-api/src/controllers/user/getGlobalOnboardingStatus.ts index 26d9c99314..9cc601ed86 100644 --- a/onboarding-api/src/controllers/user/getGlobalOnboardingStatus.ts +++ b/onboarding-api/src/controllers/user/getGlobalOnboardingStatus.ts @@ -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().required().oneOf(['evm', 'substrate']), -}) - export const getGlobalOnboardingStatusController = async ( - req: Request<{}, {}, {}, InferType>, + 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 }) diff --git a/onboarding-api/src/controllers/user/updateInvestorStatus.ts b/onboarding-api/src/controllers/user/updateInvestorStatus.ts index 3046246619..888517ff63 100644 --- a/onboarding-api/src/controllers/user/updateInvestorStatus.ts +++ b/onboarding-api/src/controllers/user/updateInvestorStatus.ts @@ -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 }) diff --git a/onboarding-api/src/database/index.ts b/onboarding-api/src/database/index.ts index adb63287f1..fc7b26a829 100644 --- a/onboarding-api/src/database/index.ts +++ b/onboarding-api/src/database/index.ts @@ -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()), @@ -115,7 +115,7 @@ export const individualUserSchema = object({ investorType: string().default('individual') as StringSchema, 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 diff --git a/onboarding-api/src/emails/sendApproveInvestorMessage.ts b/onboarding-api/src/emails/sendApproveInvestorMessage.ts index 7edea5a9a1..28f41948d9 100644 --- a/onboarding-api/src/emails/sendApproveInvestorMessage.ts +++ b/onboarding-api/src/emails/sendApproveInvestorMessage.ts @@ -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}`, }, diff --git a/onboarding-api/src/emails/sendApproveIssuerMessage.ts b/onboarding-api/src/emails/sendApproveIssuerMessage.ts index 48bc1f8bca..382c5957e3 100644 --- a/onboarding-api/src/emails/sendApproveIssuerMessage.ts +++ b/onboarding-api/src/emails/sendApproveIssuerMessage.ts @@ -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, tranche: Pool['tranches'][0], countersignedAgreementPDF: Uint8Array ) => { + const user = await fetchUser(wallet) const message = { personalizations: [ { @@ -16,7 +19,8 @@ export const sendApproveIssuerMessage = async ( }, ], dynamic_template_data: { - tokenName: tranche.currency.name, + trancheName: tranche.currency.name, + investorEmail: user.email, }, }, ], @@ -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', }, diff --git a/onboarding-api/src/emails/sendDocumentsMessage.ts b/onboarding-api/src/emails/sendDocumentsMessage.ts index 0332857c56..3034f58382 100644 --- a/onboarding-api/src/emails/sendDocumentsMessage.ts +++ b/onboarding-api/src/emails/sendDocumentsMessage.ts @@ -2,6 +2,7 @@ import { Request } from 'express' import * as jwt from 'jsonwebtoken' import { sendEmail, templateIds } from '.' import { onboardingBucket } from '../database' +import { fetchUser } from '../utils/fetchUser' import { HttpError } from '../utils/httpError' import { NetworkSwitch } from '../utils/networks/networkSwitch' @@ -11,6 +12,7 @@ export type UpdateInvestorStatusPayload = { trancheId: string } +// send documents to issuer to approve or reject the prospective investor export const sendDocumentsMessage = async ( wallet: Request['wallet'], poolId: string, @@ -19,6 +21,8 @@ export const sendDocumentsMessage = async ( debugEmail?: string ) => { const { metadata, pool } = await new NetworkSwitch(wallet.network).getPoolById(poolId) + const tranche = pool?.tranches.find((t) => t.id === trancheId) + const investorEmail = (await fetchUser(wallet)).email const payload: UpdateInvestorStatusPayload = { wallet, poolId, trancheId } const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '14d', @@ -48,6 +52,8 @@ export const sendDocumentsMessage = async ( token )}&status=approved&metadata=${pool?.metadata}&network=${wallet.network}`, disclaimerLink: `${process.env.REDIRECT_URL}/disclaimer`, + trancheName: tranche?.currency.name, + investorEmail, }, }, ], diff --git a/onboarding-api/src/emails/sendRejectInvestorMessage.ts b/onboarding-api/src/emails/sendRejectInvestorMessage.ts index dd512cc910..cacf7d04af 100644 --- a/onboarding-api/src/emails/sendRejectInvestorMessage.ts +++ b/onboarding-api/src/emails/sendRejectInvestorMessage.ts @@ -1,6 +1,11 @@ +import { Pool } from '@centrifuge/centrifuge-js' import { sendEmail, templateIds } from '.' -export const sendRejectInvestorMessage = async (to: string, metadata: Record) => { +export const sendRejectInvestorMessage = async ( + to: string, + tranche: Pool['tranches'][0], + metadata: Record +) => { const message = { personalizations: [ { @@ -10,8 +15,8 @@ export const sendRejectInvestorMessage = async (to: string, metadata: Record process.env.COLLATOR_WSS_URL.includes(env)) diff --git a/onboarding-api/src/utils/fetchUser.ts b/onboarding-api/src/utils/fetchUser.ts index 0ca7cc18ca..91de34c831 100644 --- a/onboarding-api/src/utils/fetchUser.ts +++ b/onboarding-api/src/utils/fetchUser.ts @@ -25,7 +25,7 @@ export async function fetchUser(wallet: Request['wallet'], options?: OptionsO if (!userSnapshotOnOtherNetwork.empty) { const { user, id } = userSnapshotOnOtherNetwork.docs.map((doc) => ({ user: doc.data(), id: doc.id }))[0] await validateAndWriteToFirestore( - { address: id, network }, + { address: id, network, chainId: wallet.chainId }, { wallets: { ...user.wallets,