Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Onboarding: Support evm on subtrate #1508

Merged
merged 25 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2546057
Automate proxies initialization
sophialittlejohn Jul 7, 2023
5ecf610
Add check before giving pod read access
sophialittlejohn Jul 11, 2023
51b2cc2
Clean up
sophialittlejohn Jul 11, 2023
a656285
Merge branch 'main' of github.com:centrifuge/apps into onboarding/aut…
sophialittlejohn Jul 11, 2023
3ddb550
Merge branch 'main' of github.com:centrifuge/apps into onboarding/aut…
sophialittlejohn Jul 14, 2023
6abb806
Update auth and remark signing for evm on cent chain
sophialittlejohn Jul 21, 2023
7ca2315
Merge branch 'main' of github.com:centrifuge/apps into onboarding/sup…
sophialittlejohn Jul 21, 2023
329dfdf
Adapt auth so it accepts the native substrate address format
sophialittlejohn Jul 24, 2023
153279d
Fix onboarding so it works for substrate again
sophialittlejohn Jul 24, 2023
e349b4f
Fix tinlake onboarding (maxPriortyGasFee bug)
sophialittlejohn Jul 24, 2023
82c79bb
Suuport whitelisting for evm on cent chain
sophialittlejohn Jul 26, 2023
011fd0a
Fix build
sophialittlejohn Jul 26, 2023
56d3075
Add temp wallet migration endpoint
sophialittlejohn Jul 26, 2023
4f68b21
Fix build
sophialittlejohn Jul 26, 2023
3c784f2
Add network to wallets array if missing
sophialittlejohn Jul 26, 2023
3a4f4dc
Query evm chain id on substrate
sophialittlejohn Jul 27, 2023
be196b4
Refactor to better support mutliple networks
sophialittlejohn Jul 27, 2023
3188225
Fix typo
sophialittlejohn Jul 27, 2023
9be2f9d
fix linter
sophialittlejohn Jul 27, 2023
bccb2f9
Clear session storage if verification failed
sophialittlejohn Jul 28, 2023
defec94
Improve error handling
sophialittlejohn Jul 28, 2023
a323361
More handling improvments
sophialittlejohn Jul 28, 2023
5073bbf
Merge branch 'main' of github.com:centrifuge/apps into onboarding/sup…
sophialittlejohn Jul 28, 2023
9591a09
Merge branch 'main' of github.com:centrifuge/apps into onboarding/sup…
sophialittlejohn Aug 1, 2023
d566656
Fix typo
sophialittlejohn Aug 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions centrifuge-app/src/components/OnboardingAuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Centrifuge from '@centrifuge/centrifuge-js'
import { useCentrifuge, useEvmProvider, useWallet } from '@centrifuge/centrifuge-react'
import { hexToU8a } from '@polkadot/util'
import { encodeAddress } from '@polkadot/util-crypto'
import { Wallet } from '@subwallet/wallet-connect/types'
import * as React from 'react'
import { useMutation, useQuery } from 'react-query'
Expand All @@ -13,24 +15,30 @@ export const OnboardingAuthContext = React.createContext<{
const AUTHORIZED_ONBOARDING_PROXY_TYPES = ['Any', 'Invest', 'NonTransfer', 'NonProxy']

export function OnboardingAuthProvider({ children }: { children: React.ReactNode }) {
const { selectedWallet, selectedProxies, selectedAccount } = useWallet().substrate
const { selectedAddress } = useWallet().evm
const {
substrate: { selectedWallet, selectedProxies, selectedAccount, evmChainId },
evm: { selectedAddress },
isEvmOnSubstrate,
} = useWallet()
const cent = useCentrifuge()
const provider = useEvmProvider()
const walletAddress = selectedAccount?.address ?? selectedAddress
// onboarding-api expects the wallet address in the native substrate format
const address = selectedAccount?.address
? encodeAddress(hexToU8a(selectedAccount?.address), cent.getChainId())
: selectedAddress
const proxy = selectedProxies?.[0]

const { data: session, refetch: refetchSession } = useQuery(
['session', selectedAccount?.address, proxy?.delegator, selectedAddress],
() => {
if (selectedAccount?.address || selectedAddress) {
if (address) {
if (proxy) {
const rawItem = sessionStorage.getItem(`centrifuge-onboarding-auth-${walletAddress}-${proxy.delegator}`)
const rawItem = sessionStorage.getItem(`centrifuge-onboarding-auth-${address}-${proxy.delegator}`)
if (rawItem) {
return JSON.parse(rawItem)
}
} else {
const rawItem = sessionStorage.getItem(`centrifuge-onboarding-auth-${walletAddress}`)
const rawItem = sessionStorage.getItem(`centrifuge-onboarding-auth-${address}`)
if (rawItem) {
return JSON.parse(rawItem)
}
Expand All @@ -44,9 +52,12 @@ export function OnboardingAuthProvider({ children }: { children: React.ReactNode
try {
if (selectedAccount?.address && selectedWallet?.signer) {
await loginWithSubstrate(selectedAccount?.address, selectedWallet.signer, cent, proxy)
} else if (isEvmOnSubstrate && selectedAddress && provider?.getSigner()) {
await loginWithEvm(selectedAddress, provider.getSigner(), evmChainId)
} else if (selectedAddress && provider?.getSigner()) {
await loginWithEvm(selectedAddress, provider.getSigner())
}
throw new Error('network not supported')
} catch {
} finally {
refetchSession()
Expand Down Expand Up @@ -95,8 +106,10 @@ export function useOnboardingAuth() {
const verified = (await verifiedRes.json()).verified
return { verified }
}
sessionStorage.clear()
return { verified: false }
} catch (error) {
sessionStorage.clear()
return {
verified: false,
}
Expand All @@ -118,7 +131,9 @@ export function useOnboardingAuth() {
}
}

const loginWithSubstrate = async (address: string, signer: Wallet['signer'], cent: Centrifuge, proxy?: any) => {
const loginWithSubstrate = async (hexAddress: string, signer: Wallet['signer'], cent: Centrifuge, proxy?: any) => {
// onboarding-api expects the wallet address in the native substrate format
const address = encodeAddress(hexToU8a(hexAddress), cent.getChainId())
const nonceRes = await fetch(`${import.meta.env.REACT_APP_ONBOARDING_API_URL}/nonce`, {
method: 'POST',
headers: {
Expand All @@ -144,7 +159,7 @@ const loginWithSubstrate = async (address: string, signer: Wallet['signer'], cen
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({ jw3t: token, nonce }),
body: JSON.stringify({ jw3t: token, nonce, network: 'substrate' }),
})
if (authTokenRes.status !== 200) {
throw new Error('Failed to authenticate wallet')
Expand All @@ -168,7 +183,7 @@ const loginWithSubstrate = async (address: string, signer: Wallet['signer'], cen
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({ jw3t: token, nonce }),
body: JSON.stringify({ jw3t: token, nonce, network: 'substrate' }),
})
if (authTokenRes.status !== 200) {
throw new Error('Failed to authenticate wallet')
Expand All @@ -183,7 +198,7 @@ const loginWithSubstrate = async (address: string, signer: Wallet['signer'], cen
}
}

const loginWithEvm = async (address: string, signer: any) => {
const loginWithEvm = async (address: string, signer: any, evmChainId?: number) => {
const nonceRes = await fetch(`${import.meta.env.REACT_APP_ONBOARDING_API_URL}/nonce`, {
method: 'POST',
headers: {
Expand All @@ -203,7 +218,7 @@ Please sign to authenticate your wallet

URI: ${origin}
Version: 1
Chain ID: ${import.meta.env.REACT_APP_TINLAKE_NETWORK === 'mainnet' ? 1 : 5 /* goerli */}
Chain ID: ${evmChainId ? evmChainId : import.meta.env.REACT_APP_TINLAKE_NETWORK === 'mainnet' ? 1 : 5 /* goerli */}
Nonce: ${nonce}
Issued At: ${new Date().toISOString()}`

Expand All @@ -214,7 +229,13 @@ Issued At: ${new Date().toISOString()}`
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({ message, signature: signedMessage, address, nonce }),
body: JSON.stringify({
message,
signature: signedMessage,
address,
nonce,
network: evmChainId ? 'evmOnSubstrate' : 'evm',
}),
})
if (tokenRes.status !== 200) {
throw new Error('Failed to authenticate wallet')
Expand Down
4 changes: 2 additions & 2 deletions centrifuge-app/src/pages/Onboarding/ApprovalStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ export const ApprovalStatus = ({ signedAgreementUrl }: Props) => {
const trancheId = pool.trancheId
const poolName = pool.name

const onboardingStatus = onboardingUser?.poolSteps?.[poolId]?.[trancheId].status?.status
const onboardingStatus = onboardingUser?.poolSteps?.[poolId]?.[trancheId]?.status?.status

const onFocus = () => {
refetchOnboardingUser()
}

React.useEffect(() => {
if (
onboardingUser.poolSteps?.[poolId]?.[trancheId].status.status === 'pending' ||
onboardingUser.poolSteps?.[poolId]?.[trancheId]?.status?.status === 'pending' ||
(onboardingUser.investorType === 'entity' && onboardingUser?.manualKybStatus)
) {
window.addEventListener('focus', onFocus)
Expand Down
88 changes: 53 additions & 35 deletions centrifuge-app/src/pages/Onboarding/queries/useSignRemark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { lastValueFrom } from 'rxjs'
import { useOnboardingAuth } from '../../../components/OnboardingAuthProvider'
import { ethConfig } from '../../../config'
import { Dec } from '../../../utils/Decimal'
import { useSuitableAccounts } from '../../../utils/usePermissions'
import RemarkerAbi from './abi/Remarker.abi.json'

export const useSignRemark = (
Expand All @@ -22,6 +23,7 @@ export const useSignRemark = (
{
txHash: string
blockNumber: string
isEvmOnSubstrate?: boolean
},
unknown
>
Expand All @@ -33,64 +35,80 @@ export const useSignRemark = (
const { updateTransaction, addOrUpdateTransaction } = useTransactions()
const {
connectedType,
isEvmOnSubstrate,
substrate: { selectedAddress, selectedAccount },
} = useWallet()
const [expectedTxFee, setExpectedTxFee] = React.useState(Dec(0))
const balances = useBalances(selectedAddress || '')
const { authToken } = useOnboardingAuth()
const [account] = useSuitableAccounts({ actingAddress: [selectedAddress || ''] })

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()
try {
await sendDocumentsToIssuer({ txHash, blockNumber })
let txHash: string
let blockNumber: string
// @ts-expect-error
if (isEvmOnSubstrate && result?.[0]?.wait) {
// @ts-expect-error
const evmResult = await result[0].wait()
txHash = evmResult?.transactionHash
blockNumber = evmResult?.blockNumber.toString()
} else {
txHash = result.txHash.toHex()
// @ts-expect-error
blockNumber = result.blockNumber.toString()
}
await sendDocumentsToIssuer({ txHash, blockNumber, isEvmOnSubstrate })
setIsSubstrateTxLoading(false)
} catch (e) {
setIsSubstrateTxLoading(false)
}
},
})

const signSubstrateRemark = async (args: [message: string]) => {
const getBalanceForSigning = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. So this function is not expected to ever receive args?

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: 'pending',
args,
status: 'failed',
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',
},
setIsSubstrateTxLoading(false)
throw new Error('Insufficient funds')
} else {
addOrUpdateTransaction({
id: txIdSignRemark,
title: `Get ${balances?.native.currency.symbol}`,
status: 'succeeded',
args: [],
})
}
}

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,
})
}
const signSubstrateRemark = async (args: [message: string]) => {
setIsSubstrateTxLoading(true)
if (!isEvmOnSubstrate && balances?.native.balance?.toDecimal().lt(expectedTxFee.mul(1.1))) {
await getBalanceForSigning()
}
substrateMutation.execute(args)
substrateMutation.execute(args, { account })
}

useEffect(() => {
Expand Down Expand Up @@ -150,7 +168,7 @@ export const useSignRemark = (
}
}

return connectedType === 'evm'
return connectedType === 'evm' && !isEvmOnSubstrate
? { execute: signEvmRemark, isLoading: isEvmTxLoading }
: { execute: signSubstrateRemark, isLoading: isSubstrateTxLoading }
}
16 changes: 15 additions & 1 deletion centrifuge-js/src/modules/remark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,23 @@ export function getRemarkModule(inst: CentrifugeBase) {
return $api.pipe(switchMap((api) => inst.wrapSignAndSend(api, api.tx.system.remarkWithEvent(message), options)))
}

function validateRemark(blockNumber: string, txHash: string, expectedRemark: string) {
// TODO: validate that the address that signed the remark is the same as the wallet making the request
function validateRemark(blockNumber: string, txHash: string, expectedRemark: string, isEvmOnSubstrate?: boolean) {
return inst.getBlockByBlockNumber(Number(blockNumber)).pipe(
switchMap((block) => {
if (isEvmOnSubstrate) {
const extrinsic = block.block.extrinsics.find((ex) => {
return ex.method.method.toString() === 'transact' && ex.method.section === 'ethereum'
})
// @ts-expect-error
const evmInput = extrinsic?.method.args?.[0].toJSON()?.eip1559?.input.slice(10)
const actualRemark = Buffer.from(`${evmInput}`, 'hex').toString()

if (actualRemark !== expectedRemark) {
throw new Error('Invalid remark')
}
return of(true)
}
const extrinsic = block?.block.extrinsics.find((ex) => ex.hash.toString() === txHash)
const actualRemark = extrinsic?.method.args[0].toHuman()
if (actualRemark !== expectedRemark) {
Expand Down
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/altair.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ COLLATOR_WSS_URL=wss://fullnode.altair.centrifuge.io
RELAY_WSS_URL=wss://kusama-rpc.polkadot.io
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
EVM_NETWORK=goerli
ONBOARDING_STORAGE_BUCKET=altair-onboarding-api
ONBOARDING_STORAGE_BUCKET=altair-onboarding-api
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/catalyst.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ COLLATOR_WSS_URL=wss://fullnode.catalyst.cntrfg.com
RELAY_WSS_URL=wss://rococo-rpc.polkadot.io
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
EVM_NETWORK=goerli
ONBOARDING_STORAGE_BUCKET=centrifuge-onboarding-api-dev
ONBOARDING_STORAGE_BUCKET=centrifuge-onboarding-api-dev
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/development.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ COLLATOR_WSS_URL=wss://fullnode.development.cntrfg.com
RELAY_WSS_URL=wss://fullnode-relay.development.cntrfg.com
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
EVM_NETWORK=goerli
ONBOARDING_STORAGE_BUCKET=centrifuge-onboarding-api-dev
ONBOARDING_STORAGE_BUCKET=centrifuge-onboarding-api-dev
2 changes: 1 addition & 1 deletion onboarding-api/env-vars/production.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ COLLATOR_WSS_URL=wss://fullnode.parachain.centrifuge.io
RELAY_WSS_URL=wss://rpc.polkadot.io
INFURA_KEY=bf808e7d3d924fbeb74672d9341d0550
EVM_NETWORK=mainnet
ONBOARDING_STORAGE_BUCKET=centrifuge-onboarding-api
ONBOARDING_STORAGE_BUCKET=centrifuge-onboarding-api
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Request, Response } from 'express'
import { checkBalanceBeforeSigningRemark } from '../../utils/centrifuge'
import { fetchUser } from '../../utils/fetchUser'
import { HttpError, reportHttpError } from '../../utils/httpError'
import { checkBalanceBeforeSigningRemark } from '../../utils/networks/centrifuge'

export const getBalanceForSigningController = async (req: Request, res: Response) => {
try {
Expand Down
Loading
Loading