Skip to content

Commit

Permalink
Onboarding: Faucet for remark signing (#1335)
Browse files Browse the repository at this point in the history
* Add payment info check before signing remark

* Add endpoint to get native currency from and hook up to frontend

* Fix bug in endpoint and more toasts

* Improve toasts and error handling

* Fix bug where wss closed before remark signing was complete

* Comment out proxy for now

* Check country codes for manual review on server and send verify email

* Fix bug in document signing if no agreement available

* Fix bugs in manual flow

* Fix proxies
  • Loading branch information
sophialittlejohn authored May 30, 2023
1 parent 6542786 commit e4ef5fb
Show file tree
Hide file tree
Showing 16 changed files with 370 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTransactions } from '@centrifuge/centrifuge-react'
import { useMutation } from 'react-query'
import { useOnboardingAuth } from '../../../components/OnboardingAuthProvider'
import { OnboardingPool, useOnboarding } from '../../../components/OnboardingProvider'
Expand All @@ -6,12 +7,21 @@ import { OnboardingUser } from '../../../types'
export const useSignAndSendDocuments = () => {
const { refetchOnboardingUser, pool, nextStep } = useOnboarding<OnboardingUser, NonNullable<OnboardingPool>>()
const { authToken } = useOnboardingAuth()
const { addOrUpdateTransaction } = useTransactions()
const txIdSendDocs = Math.random().toString(36).substr(2)

const poolId = pool.id
const trancheId = pool.trancheId

const mutation = useMutation(
async (transactionInfo: { txHash: string; blockNumber: string }) => {
addOrUpdateTransaction({
id: txIdSendDocs,
title: `Send documents to issuers`,
status: 'pending',
args: [],
})

const response = await fetch(`${import.meta.env.REACT_APP_ONBOARDING_API_URL}/signAndSendDocuments`, {
method: 'POST',
headers: {
Expand All @@ -27,8 +37,20 @@ export const useSignAndSendDocuments = () => {
})

if (response.status === 201) {
addOrUpdateTransaction({
id: txIdSendDocs,
title: `Send documents to issuers`,
status: 'succeeded',
args: [],
})
return response
}
addOrUpdateTransaction({
id: txIdSendDocs,
title: `Send documents to issuers`,
status: 'failed',
args: ['An error occured uploading documents'],
})
throw response.statusText
},
{
Expand Down
92 changes: 87 additions & 5 deletions centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { useCentrifugeTransaction, useEvmProvider, useTransactions, useWallet } from '@centrifuge/centrifuge-react'
import {
useBalances,
useCentrifuge,
useCentrifugeTransaction,
useEvmProvider,
useTransactions,
useWallet,
} from '@centrifuge/centrifuge-react'
import { Contract } from '@ethersproject/contracts'
import React from 'react'
import React, { useEffect } from 'react'
import { UseMutateFunction } from 'react-query'
import { lastValueFrom } from 'rxjs'
import { useOnboardingAuth } from '../../../components/OnboardingAuthProvider'
import { ethConfig } from '../../../config'
import { Dec } from '../../../utils/Decimal'
import RemarkerAbi from './abi/Remarker.abi.json'

export const useSignRemark = (
Expand All @@ -18,18 +28,88 @@ export const useSignRemark = (
) => {
const evmProvider = useEvmProvider()
const [isEvmTxLoading, setIsEvmTxLoading] = React.useState(false)
const [isSubstrateTxLoading, setIsSubstrateTxLoading] = React.useState(false)
const centrifuge = useCentrifuge()
const { updateTransaction, addOrUpdateTransaction } = useTransactions()
const { connectedType } = useWallet()
const {
connectedType,
substrate: { selectedAddress, selectedAccount },
} = useWallet()
const [expectedTxFee, setExpectedTxFee] = React.useState(Dec(0))
const balances = useBalances(selectedAddress || '')
const { authToken } = useOnboardingAuth()

const substrateMutation = useCentrifugeTransaction('Sign remark', (cent) => cent.remark.signRemark, {
onSuccess: async (_, result) => {
const txHash = result.txHash.toHex()
// @ts-expect-error
const blockNumber = result.blockNumber.toString()
await sendDocumentsToIssuer({ txHash, blockNumber })
try {
await sendDocumentsToIssuer({ txHash, blockNumber })
setIsSubstrateTxLoading(false)
} catch (e) {
setIsSubstrateTxLoading(false)
}
},
})

const signSubstrateRemark = async (args: [message: string]) => {
const txIdSignRemark = Math.random().toString(36).substr(2)
setIsSubstrateTxLoading(true)
if (balances?.native.balance?.toDecimal().lt(expectedTxFee.mul(1.1))) {
addOrUpdateTransaction({
id: txIdSignRemark,
title: `Get ${balances?.native.currency.symbol}`,
status: 'pending',
args,
})
// add just enough native currency to be able to sign remark
const response = await fetch(`${import.meta.env.REACT_APP_ONBOARDING_API_URL}/getBalanceForSigning`, {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
'Content-Type': 'application/json',
},
})

if (response.status !== 201) {
addOrUpdateTransaction({
id: txIdSignRemark,
title: `Get ${balances?.native.currency.symbol}`,
status: 'failed',
args,
})
setIsSubstrateTxLoading(false)
throw new Error('Insufficient funds')
} else {
addOrUpdateTransaction({
id: txIdSignRemark,
title: `Get ${balances?.native.currency.symbol}`,
status: 'succeeded',
args,
})
}
}
substrateMutation.execute(args)
}

useEffect(() => {
const executePaymentInfo = async () => {
if (selectedAccount && selectedAccount.signer) {
const api = await centrifuge.connect(selectedAccount.address, selectedAccount.signer)
const paymentInfo = await lastValueFrom(
api.remark.signRemark([`Signed subscription agreement for pool: 12324565 tranche: 0xacbdefghijklmn`], {
paymentInfo: selectedAccount.address,
})
)
// @ts-expect-error
const txFee = paymentInfo.partialFee.toDecimal()
setExpectedTxFee(txFee)
}
}
executePaymentInfo()
}, [centrifuge, selectedAccount])

const signEvmRemark = async (args: [message: string]) => {
const txId = Math.random().toString(36).substr(2)
setIsEvmTxLoading(true)
Expand Down Expand Up @@ -71,5 +151,7 @@ export const useSignRemark = (
}
}

return connectedType === 'evm' ? { execute: signEvmRemark, isLoading: isEvmTxLoading } : substrateMutation
return connectedType === 'evm'
? { execute: signEvmRemark, isLoading: isEvmTxLoading }
: { execute: signSubstrateRemark, isLoading: isSubstrateTxLoading }
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export const useVerifyBusiness = () => {
? `${values.jurisdictionCode}_${values.regionCode}`
: values.jurisdictionCode,
dryRun: import.meta.env.REACT_APP_ONBOARDING_API_URL.includes('production') ? false : true,
manualReview: values?.manualReview ?? false,
...(values?.manualReview && poolId && trancheId && { poolId, trancheId }),
}),
headers: {
Expand Down
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/altair.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
REDIRECT_URL=https://app.altair.centrifuge.io
MEMBERLIST_ADMIN_PURE_PROXY=''
MEMBERLIST_ADMIN_PURE_PROXY=kALJqPUHFzDR2VkoQYWefPQyzjGzKznNny2smXGQpSf3aMw19
COLLATOR_WSS_URL=wss://fullnode.altair.centrifuge.io
RELAY_WSS_URL=wss://kusama-rpc.polkadot.io
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
Expand Down
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/catalyst.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
REDIRECT_URL=https://app-catalyst.k-f.dev
MEMBERLIST_ADMIN_PURE_PROXY=4dn2oY5FAD1v5vQCWvVc7ErDstsPfjLKa39uHU9YgLNGdGsy
MEMBERLIST_ADMIN_PURE_PROXY=kALJqPUHFzDR2VkoQYWefPQyzjGzKznNny2smXGQpSf3aMw19
COLLATOR_WSS_URL=wss://fullnode.catalyst.cntrfg.com
RELAY_WSS_URL=wss://rococo-rpc.polkadot.io
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
Expand Down
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/demo.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
REDIRECT_URL=https://app-demo.k-f.dev
MEMBERLIST_ADMIN_PURE_PROXY=kAL8bY5Sijge4K8VbogBwjDfDFQN6ezXh24SonbXVNa1eUvK2
MEMBERLIST_ADMIN_PURE_PROXY=kALJqPUHFzDR2VkoQYWefPQyzjGzKznNny2smXGQpSf3aMw19
COLLATOR_WSS_URL=wss://fullnode.demo.cntrfg.com
RELAY_WSS_URL=wss://fullnode-relay.demo.cntrfg.com
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
Expand Down
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/development.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
REDIRECT_URL=https://app-dev.k-f.dev
MEMBERLIST_ADMIN_PURE_PROXY=kAJJW74FALrFpNTet8PgS5f5JhepTHy2Vm3GCM51fhorPzTuF
MEMBERLIST_ADMIN_PURE_PROXY=kALJqPUHFzDR2VkoQYWefPQyzjGzKznNny2smXGQpSf3aMw19
COLLATOR_WSS_URL=wss://fullnode.development.cntrfg.com
RELAY_WSS_URL=wss://fullnode-relay.development.cntrfg.com
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
Expand Down
21 changes: 21 additions & 0 deletions onboarding-api/src/controllers/agreement/getBalanceForSigning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Request, Response } from 'express'
import { checkBalanceBeforeSigningRemark } from '../../utils/centrifuge'
import { fetchUser } from '../../utils/fetchUser'
import { HttpError, reportHttpError } from '../../utils/httpError'

export const getBalanceForSigningController = async (req: Request, res: Response) => {
try {
const { wallet } = req
const user = await fetchUser(wallet)
if (!user.globalSteps.verifyIdentity.completed) {
throw new HttpError(401, 'Unauthorized')
}

await checkBalanceBeforeSigningRemark(wallet)

return res.status(201).end()
} catch (e) {
const error = reportHttpError(e)
return res.status(error.code).send({ error: error.message })
}
}
10 changes: 7 additions & 3 deletions onboarding-api/src/controllers/auth/authenticateWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Request, Response } from 'express'
import * as jwt from 'jsonwebtoken'
import { SiweMessage } from 'siwe'
import { InferType, object, string } from 'yup'
import { centrifuge, isValidSubstrateAddress } from '../../utils/centrifuge'
import { getCentrifuge, isValidSubstrateAddress } from '../../utils/centrifuge'
import { reportHttpError } from '../../utils/httpError'
import { validateInput } from '../../utils/validateInput'

Expand Down Expand Up @@ -52,7 +52,7 @@ export const authenticateWalletController = async (
const AUTHORIZED_ONBOARDING_PROXY_TYPES = ['Any', 'Invest', 'NonTransfer', 'NonProxy']
async function verifySubstrateWallet(req: Request, res: Response) {
const { jw3t: token, nonce } = req.body
const { verified, payload } = await centrifuge.auth.verify(token!)
const { verified, payload } = await await getCentrifuge().auth.verify(token!)

const onBehalfOf = payload?.on_behalf_of
const address = payload.address
Expand All @@ -68,7 +68,11 @@ async function verifySubstrateWallet(req: Request, res: Response) {
res.clearCookie(`onboarding-auth-${address.toLowerCase()}`)

if (verified && onBehalfOf) {
const isVerifiedProxy = await centrifuge.auth.verifyProxy(address, onBehalfOf, AUTHORIZED_ONBOARDING_PROXY_TYPES)
const isVerifiedProxy = await getCentrifuge().auth.verifyProxy(
address,
onBehalfOf,
AUTHORIZED_ONBOARDING_PROXY_TYPES
)
if (isVerifiedProxy.verified) {
req.wallet.address = address
} else if (verified && !onBehalfOf) {
Expand Down
2 changes: 1 addition & 1 deletion onboarding-api/src/controllers/kyb/manualKybCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const manualKybCallbackController = async (
throw new HttpError(400, 'Agreement not found')
}

sendDocumentsMessage(wallet, query.poolId, query.trancheId, signedAgreement)
await sendDocumentsMessage(wallet, query.poolId, query.trancheId, signedAgreement)
}

return res.status(200).end()
Expand Down
Loading

0 comments on commit e4ef5fb

Please sign in to comment.