From b03bc81a16219a64b22dcacc8a996c154119a789 Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:34:26 +0800 Subject: [PATCH 01/22] Account management page (#357) * add useActiveChainInfo hook * add default chainId to useAuthErrorHandling --------- Co-authored-by: shan --- .../src/hooks/useAuthErrorHandling.ts | 252 ++++++++++++++++++ .../huma-web-shared/src/hooks/useChainInfo.ts | 16 ++ .../Lend/solanaSupply/4-Transfer.tsx | 1 + 3 files changed, 269 insertions(+) create mode 100644 packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts new file mode 100644 index 00000000..6c183045 --- /dev/null +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts @@ -0,0 +1,252 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { JsonRpcProvider } from '@ethersproject/providers' +import { + AuthService, + CHAIN_TYPE, + SiwsMessage, + SOLANA_CHAINS, + SolanaChainEnum, +} from '@huma-finance/shared' +import { useWallet } from '@solana/wallet-adapter-react' +import { useWeb3React } from '@web3-react/core' +import axios, { HttpStatusCode } from 'axios' +import bs58 from 'bs58' +import { useCallback, useEffect, useState } from 'react' +import { SiweMessage } from 'siwe' +import { useAsyncError } from './useAsyncError' + +type ErrorType = 'NotSignedIn' | 'UserRejected' | 'Other' + +const createSiweMessage = ( + address: string, + chainId: number, + nonce: string, + expiresAt: string, +) => { + const domain = window.location.hostname + const message = new SiweMessage({ + domain, + address, + statement: 'Please sign in to verify your ownership of this wallet', + uri: window.location.origin, + version: '1', + chainId, + nonce, + expirationTime: expiresAt, + }) + return message.prepareMessage() +} + +const createSiwsMessage = ( + address: string, + chainId: SolanaChainEnum, + nonce: string, + expiresAt: string, +) => { + const domain = window.location.hostname + const message = new SiwsMessage({ + domain, + address, + statement: 'Please sign in to verify your ownership of this wallet', + uri: window.location.origin, + version: '1', + chainId: SOLANA_CHAINS[chainId].name, + nonce, + expirationTime: expiresAt, + }) + return message.prepareMessage() +} + +const verifyEvmOwnership = async ( + address: string, + chainId: number, + isDev: boolean, + provider: JsonRpcProvider, + onVerificationComplete: () => void, +) => { + const { nonce, expiresAt } = await AuthService.createSession(chainId, isDev) + const message = createSiweMessage(address, chainId, nonce, expiresAt) + const signer = await provider.getSigner() + const signature = await signer.signMessage(message) + await AuthService.verifySignature(message, signature, chainId, isDev) + onVerificationComplete() +} + +const verifySolanaOwnership = async ( + address: string, + chainId: number, + isDev: boolean, + solanaSignMessage: (message: Uint8Array) => Promise, + onVerificationComplete: () => void, +) => { + try { + const { nonce, expiresAt } = await AuthService.createSession(chainId, isDev) + const message = createSiwsMessage(address, chainId, nonce, expiresAt) + const encodedMessage = new TextEncoder().encode(message) + const signedMessage = await solanaSignMessage(encodedMessage) + const signatureEncoded = bs58.encode(signedMessage as Uint8Array) + + await AuthService.verifySignature(message, signatureEncoded, chainId, isDev) + onVerificationComplete() + } catch (error) { + console.error(error) + } +} + +export type AuthState = { + isWalletOwnershipVerificationRequired: boolean + isWalletOwnershipVerified: boolean + errorType?: ErrorType + error: unknown + setError: React.Dispatch> + reset: () => void +} + +export const useAuthErrorHandling = ( + isDev: boolean, + chainType: CHAIN_TYPE = CHAIN_TYPE.EVM, + defaultChainId?: number, +): AuthState => { + const [error, setError] = useState(null) + const [isVerificationRequired, setIsVerificationRequired] = + useState(false) + const [isVerified, setIsVerified] = useState(false) + const throwError = useAsyncError() + const handleVerificationCompletion = () => { + setIsVerified(true) + } + const [errorType, setErrorType] = useState() + + const { + account: evmAccount, + chainId: evmChainId, + provider: evmProvider, + } = useWeb3React() + const { publicKey: solanaPublicKey, signMessage: solanaSignMessage } = + useWallet() + const solanaAccount = solanaPublicKey?.toString() ?? '' + + const getErrorInfo = useCallback((error: any) => { + const isUnauthorizedError = + axios.isAxiosError(error) && + error.response?.status === HttpStatusCode.Unauthorized && + [ + 'IdTokenNotFoundException', + 'InvalidIdTokenException', + 'WalletMismatchException', + ].includes(error.response?.data?.detail?.type) + + const isWalletNotCreatedError = error === 'WalletNotCreatedException' + const isWalletNotSignInError = error === 'WalletNotSignInException' + + return { + isUnauthorizedError, + isWalletNotCreatedError, + isWalletNotSignInError, + } + }, []) + + useEffect(() => { + if (chainType === CHAIN_TYPE.EVM) { + if (!evmAccount || !evmChainId || !error || !evmProvider) { + return + } + + const { + isUnauthorizedError, + isWalletNotCreatedError, + isWalletNotSignInError, + } = getErrorInfo(error) + + if ( + isUnauthorizedError || + isWalletNotCreatedError || + isWalletNotSignInError + ) { + setErrorType('NotSignedIn') + setIsVerificationRequired(true) + if (chainType === CHAIN_TYPE.EVM) { + verifyEvmOwnership( + evmAccount!, + defaultChainId ?? evmChainId!, + isDev, + evmProvider!, + handleVerificationCompletion, + ).catch((e) => setError(e)) + } + } else if ([4001, 'ACTION_REJECTED'].includes((error as any).code)) { + setErrorType('UserRejected') + } else { + setErrorType('Other') + } + } + }, [ + evmChainId, + isDev, + error, + throwError, + evmAccount, + evmProvider, + getErrorInfo, + chainType, + defaultChainId, + ]) + + useEffect(() => { + if (chainType === CHAIN_TYPE.SOLANA) { + if (!solanaAccount || !error || !solanaSignMessage) { + return + } + + const { + isUnauthorizedError, + isWalletNotCreatedError, + isWalletNotSignInError, + } = getErrorInfo(error) + + if ( + isUnauthorizedError || + isWalletNotCreatedError || + isWalletNotSignInError + ) { + setErrorType('NotSignedIn') + setIsVerificationRequired(true) + verifySolanaOwnership( + solanaAccount, + isDev ? SolanaChainEnum.SolanaDevnet : SolanaChainEnum.SolanaMainnet, + isDev, + solanaSignMessage, + handleVerificationCompletion, + ).catch((e) => setError(e)) + } else if ([4001, 'ACTION_REJECTED'].includes((error as any).code)) { + setErrorType('UserRejected') + } else { + setErrorType('Other') + } + } + }, [ + isDev, + error, + throwError, + chainType, + solanaAccount, + getErrorInfo, + solanaSignMessage, + ]) + + const reset = useCallback(() => { + setIsVerificationRequired(false) + setIsVerified(false) + setError(null) + setErrorType(undefined) + }, []) + + return { + isWalletOwnershipVerificationRequired: isVerificationRequired, + isWalletOwnershipVerified: isVerified, + errorType, + error, + setError, + reset, + } +} diff --git a/packages/huma-web-shared/src/hooks/useChainInfo.ts b/packages/huma-web-shared/src/hooks/useChainInfo.ts index 51488489..1b57e84b 100644 --- a/packages/huma-web-shared/src/hooks/useChainInfo.ts +++ b/packages/huma-web-shared/src/hooks/useChainInfo.ts @@ -41,3 +41,19 @@ export const useChainInfo = ( provider, } } + +export const useActiveChainInfo = ( + isDev: boolean, + activeNetwork: CHAIN_TYPE, +) => { + const evmChainInfo = useChainInfo(isDev, CHAIN_TYPE.EVM) + const solanaChainInfo = useChainInfo(isDev, CHAIN_TYPE.SOLANA) + + if (activeNetwork === CHAIN_TYPE.EVM) { + return evmChainInfo + } + if (activeNetwork === CHAIN_TYPE.SOLANA) { + return solanaChainInfo + } + return null +} diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx index 5f6c775e..a62e6446 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx @@ -9,6 +9,7 @@ import { } from '@huma-finance/shared' import React, { useCallback, useEffect, useState } from 'react' +import { BN } from '@coral-xyz/anchor' import { SolanaPoolState, useHumaProgram, From eb1554601043e184447784a257c4e40d1e92a97b Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:16:34 +0800 Subject: [PATCH 02/22] Account management create account (#366) * update useActiveChainInfo * user login * update account * update account name --------- Co-authored-by: shan --- .../src/services/IdentityServiceV2.ts | 64 ++++++++++++++++++- packages/huma-shared/src/utils/chain.ts | 5 ++ packages/huma-shared/src/utils/config.ts | 9 +++ packages/huma-shared/src/utils/request.ts | 55 ++++++++-------- .../useAuthErrorHandlingSolana.ts | 4 +- .../huma-web-shared/src/hooks/useChainInfo.ts | 13 ++-- .../Lend/solanaSupply/4-Transfer.tsx | 4 +- 7 files changed, 114 insertions(+), 40 deletions(-) diff --git a/packages/huma-shared/src/services/IdentityServiceV2.ts b/packages/huma-shared/src/services/IdentityServiceV2.ts index 1578ceb7..ce4ec297 100644 --- a/packages/huma-shared/src/services/IdentityServiceV2.ts +++ b/packages/huma-shared/src/services/IdentityServiceV2.ts @@ -1,5 +1,6 @@ +import { NETWORK_TYPE } from '../utils/chain' import { configUtil } from '../utils/config' -import { requestGet, requestPost } from '../utils/request' +import { requestGet, requestPatch, requestPost } from '../utils/request' /** * Enum representing the identity status V2. @@ -64,9 +65,29 @@ export type ResumeVerificationResultV2 = { * Object representing the Huma account. * @typedef {Object} HumaAccount * @property {string} accountId The account id. + * @property {string} name The account name. */ export type HumaAccount = { accountId: string + name: string +} + +/** + * Object representing the Huma account. + * @typedef {Object} HumaAccount + * @property {string} accountId The account id. + * @property {string} name The account name. + * @property {Wallet[]} wallets The account wallets. + * @property {boolean} isNewAccount Is new account or not. + */ +export type LoginResult = { + accountId: string + name: string + wallets: { + address: string + chainId: string + }[] + isNewAccount: boolean } /** @@ -248,6 +269,45 @@ const getHumaAccount = async ( )}/wallets/${walletAddress}/account?chainId=${chainId}`, ) +/** + * Huma account login by wallet address and chain. + * + * @param {string} walletAddress The wallet address. + * @param {number} chainId Chain ID. + * @param {boolean} isDev Is dev environment or not. + * @returns {Promise} Promise that returns void. + */ +const humaAccountLogin = async ( + walletAddress: string, + chainId: number, + isDev = false, +): Promise => + requestPost( + `${configUtil.getIdentityAPIUrl(chainId, isDev)}/auth/login`, + { + walletAddress, + chainId: String(chainId), + }, + ) + +/** + * Update huma account. + * + * @param {string} networkType Network type. + * @param {boolean} isDev Is dev environment or not. + * @param {HumaAccount} humaAccount The Huma account. + * @returns {Promise} Promise that returns void. + */ +const humaAccountUpdate = async ( + networkType: NETWORK_TYPE, + humaAccount: Partial, + isDev = false, +): Promise => + requestPatch( + `${configUtil.getIdentityAPIUrlV2(networkType, isDev)}/account`, + humaAccount, + ) + export const IdentityServiceV2 = { getVerificationStatusV2, accredit, @@ -257,4 +317,6 @@ export const IdentityServiceV2 = { approveLender, authenticate, getHumaAccount, + humaAccountLogin, + humaAccountUpdate, } diff --git a/packages/huma-shared/src/utils/chain.ts b/packages/huma-shared/src/utils/chain.ts index a7e42959..db1d16b8 100644 --- a/packages/huma-shared/src/utils/chain.ts +++ b/packages/huma-shared/src/utils/chain.ts @@ -1,6 +1,11 @@ import type { AddEthereumChainParameter } from '@web3-react/types' import { ethers } from 'ethers' +export enum NETWORK_TYPE { + testnet = 'testnet', + mainnet = 'mainnet', +} + export enum CHAIN_TYPE { EVM = 'evm', SOLANA = 'solana', diff --git a/packages/huma-shared/src/utils/config.ts b/packages/huma-shared/src/utils/config.ts index 629a21ed..c128b31f 100644 --- a/packages/huma-shared/src/utils/config.ts +++ b/packages/huma-shared/src/utils/config.ts @@ -44,6 +44,14 @@ const getIdentityAPIUrl = (chainId: number, isDev = false) => isDev, )}.identity-verification.huma.finance` +const getIdentityAPIUrlV2 = ( + networkType: 'testnet' | 'mainnet', + isDev = false, +) => + `https://${getDevPrefix( + isDev, + )}${networkType}.identity-verification.huma.finance` + const getAuthServiceUrl = (chainId: number, isDev = false) => `https://${getNetworkAgnosticServiceUrlPrefix( chainId, @@ -108,6 +116,7 @@ export const configUtil = { getEABaseUrlV1, getRequestAPIUrl, getIdentityAPIUrl, + getIdentityAPIUrlV2, getAuthServiceUrl, getKYCProviderBaseUrl, getCampaignAPIUrl, diff --git a/packages/huma-shared/src/utils/request.ts b/packages/huma-shared/src/utils/request.ts index 2a0a7565..3bd98374 100644 --- a/packages/huma-shared/src/utils/request.ts +++ b/packages/huma-shared/src/utils/request.ts @@ -2,21 +2,21 @@ import axios, { AxiosRequestConfig } from 'axios' axios.defaults.withCredentials = true +const getConfig = (customConfig: AxiosRequestConfig = {}) => ({ + headers: { + 'Content-Type': 'application/json', + }, + withCredentials: true, + ...customConfig, +}) + export const requestGet = async ( url: string, customConfig: AxiosRequestConfig = {}, ): Promise => { - const config = { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - withCredentials: true, - ...customConfig, - } - + const config = getConfig(customConfig) // @ts-ignore - return axios.get(url, {}, config).then((response) => response.data as T) + return axios.get(url, config).then((response) => response.data as T) } export const requestPost = async ( @@ -25,15 +25,7 @@ export const requestPost = async ( payload?: any, customConfig: AxiosRequestConfig = {}, ): Promise => { - const config = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - withCredentials: true, - ...customConfig, - } - + const config = getConfig(customConfig) return ( axios .post(url, payload, config) @@ -48,15 +40,7 @@ export const requestPut = async ( payload?: any, customConfig: AxiosRequestConfig = {}, ): Promise => { - const config = { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - withCredentials: true, - ...customConfig, - } - + const config = getConfig(customConfig) return ( axios .put(url, payload, config) @@ -64,3 +48,18 @@ export const requestPut = async ( .then((response) => response.data as T) ) } + +export const requestPatch = async ( + url: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + payload?: any, + customConfig: AxiosRequestConfig = {}, +): Promise => { + const config = getConfig(customConfig) + return ( + axios + .patch(url, payload, config) + // @ts-ignore + .then((response) => response.data as T) + ) +} diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingSolana.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingSolana.ts index 5414c4f4..529acb67 100644 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingSolana.ts +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingSolana.ts @@ -91,8 +91,8 @@ const verifyOwnershipSolana = async ( isDev, ) } catch (e: unknown) { - console.error(e); - if(e instanceof WalletSignMessageError) { + console.error(e) + if (e instanceof WalletSignMessageError) { // If the wallet does not support message signing, try to sign a transaction const tx = await buildAuthTx(account, message) tx.feePayer = account diff --git a/packages/huma-web-shared/src/hooks/useChainInfo.ts b/packages/huma-web-shared/src/hooks/useChainInfo.ts index 1b57e84b..5f1c84c9 100644 --- a/packages/huma-web-shared/src/hooks/useChainInfo.ts +++ b/packages/huma-web-shared/src/hooks/useChainInfo.ts @@ -49,11 +49,12 @@ export const useActiveChainInfo = ( const evmChainInfo = useChainInfo(isDev, CHAIN_TYPE.EVM) const solanaChainInfo = useChainInfo(isDev, CHAIN_TYPE.SOLANA) - if (activeNetwork === CHAIN_TYPE.EVM) { - return evmChainInfo + switch (activeNetwork) { + case CHAIN_TYPE.EVM: + return evmChainInfo + case CHAIN_TYPE.SOLANA: + return solanaChainInfo + default: + return null } - if (activeNetwork === CHAIN_TYPE.SOLANA) { - return solanaChainInfo - } - return null } diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx index a62e6446..508b784a 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx @@ -7,9 +7,6 @@ import { SolanaTokenUtils, TrancheType, } from '@huma-finance/shared' -import React, { useCallback, useEffect, useState } from 'react' - -import { BN } from '@coral-xyz/anchor' import { SolanaPoolState, useHumaProgram, @@ -23,6 +20,7 @@ import { } from '@solana/spl-token' import { useWallet } from '@solana/wallet-adapter-react' import { PublicKey, Transaction } from '@solana/web3.js' +import React, { useCallback, useEffect, useState } from 'react' import { useAppDispatch, useAppSelector } from '../../../hooks/useRedux' import { setPointsAccumulated, setStep } from '../../../store/widgets.reducers' import { selectWidgetState } from '../../../store/widgets.selectors' From b1767eb0bc325b7506561f959f05e6bcb986ca98 Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:14:35 +0800 Subject: [PATCH 03/22] Account management referral and name (#375) * user login * update account * update account name * add AccountTokenNotFoundException * update useAuthErrorHandling * account update referral code * account name validity (#373) Co-authored-by: shan --------- Co-authored-by: shan --- .../src/services/IdentityServiceV2.ts | 170 +++++++++++++++--- packages/huma-shared/src/utils/config.ts | 7 +- .../src/hooks/useAuthErrorHandling.ts | 2 + .../src/hooks/useAuthErrorHandling/index.ts | 2 + 4 files changed, 150 insertions(+), 31 deletions(-) diff --git a/packages/huma-shared/src/services/IdentityServiceV2.ts b/packages/huma-shared/src/services/IdentityServiceV2.ts index ce4ec297..e3e6b350 100644 --- a/packages/huma-shared/src/services/IdentityServiceV2.ts +++ b/packages/huma-shared/src/services/IdentityServiceV2.ts @@ -64,23 +64,14 @@ export type ResumeVerificationResultV2 = { /** * Object representing the Huma account. * @typedef {Object} HumaAccount - * @property {string} accountId The account id. - * @property {string} name The account name. - */ -export type HumaAccount = { - accountId: string - name: string -} - -/** - * Object representing the Huma account. - * @typedef {Object} HumaAccount + * @property {string} id The account id. * @property {string} accountId The account id. * @property {string} name The account name. * @property {Wallet[]} wallets The account wallets. * @property {boolean} isNewAccount Is new account or not. */ -export type LoginResult = { +export type HumaAccount = { + id: string accountId: string name: string wallets: { @@ -88,6 +79,42 @@ export type LoginResult = { chainId: string }[] isNewAccount: boolean + referralCode: string + numReferrals: number + referrer: { + id: string + name: string + } +} + +/** + * Object representing the accreditation answers. + * @typedef {Object} AccreditationAnswers + * @property {string} question1 The question 1. + * @property {boolean} question1Answer The question 1 answer. + * @property {string} question2 The question 2. + * @property {boolean} question2Answer The question 2 answer. + * @property {string} question3 The question 3. + * @property {boolean} question3Answer The question 3 answer. + */ +export type AccreditationAnswers = { + question1: string + question1Answer: boolean + question2: string + question2Answer: boolean + question3: string + question3Answer: boolean +} + +/** + * Object representing the account name validity. + * @typedef {Object} AccountNameValidity + * @property {string} name The account name. + * @property {string} invalidReason The invalid reason. + */ +export type AccountNameValidity = { + name: string + invalidReason: 'already_taken' | 'inappropriate_language' } /** @@ -110,15 +137,6 @@ const getVerificationStatusV2 = async ( )}/wallets/${walletAddress}/verification-status?&chainId=${chainId}`, ) -export type AccreditationAnswers = { - question1: string - question1Answer: boolean - question2: string - question2Answer: boolean - question3: string - question3Answer: boolean -} - /** * Start wallet's accreditation process. * @@ -250,14 +268,14 @@ const authenticate = async ( ) /** - * Get Huma account. + * Get Huma account old. * * @param {string} walletAddress The wallet address. * @param {number} chainId Chain ID. * @param {boolean} isDev Is dev environment or not. * @returns {Promise} Promise that returns void. */ -const getHumaAccount = async ( +const getHumaAccountOld = async ( walletAddress: string, chainId: number, isDev = false, @@ -269,6 +287,24 @@ const getHumaAccount = async ( )}/wallets/${walletAddress}/account?chainId=${chainId}`, ) +/** + * Get Huma account. + * + * @param {string} networkType Network type. + * @param {boolean} isDev Is dev environment or not. + * @returns {Promise} Promise that returns huma account. + */ +const getHumaAccount = async ( + networkType: NETWORK_TYPE, + isDev = false, +): Promise => { + const { account } = await requestGet<{ account: HumaAccount }>( + `${configUtil.getIdentityAPIUrlV2(networkType, isDev)}/account`, + ) + account.accountId = account.id + return account +} + /** * Huma account login by wallet address and chain. * @@ -281,8 +317,8 @@ const humaAccountLogin = async ( walletAddress: string, chainId: number, isDev = false, -): Promise => - requestPost( +): Promise => + requestPost( `${configUtil.getIdentityAPIUrl(chainId, isDev)}/auth/login`, { walletAddress, @@ -290,6 +326,21 @@ const humaAccountLogin = async ( }, ) +/** + * Huma account logout. + * + * @param {string} networkType Network type. + * @param {boolean} isDev Is dev environment or not. + * @returns {Promise} Promise that returns void. + */ +const humaAccountLogout = async ( + networkType: NETWORK_TYPE, + isDev = false, +): Promise => + requestPost( + `${configUtil.getIdentityAPIUrlV2(networkType, isDev)}/auth/logout`, + ) + /** * Update huma account. * @@ -300,7 +351,7 @@ const humaAccountLogin = async ( */ const humaAccountUpdate = async ( networkType: NETWORK_TYPE, - humaAccount: Partial, + humaAccount: { name: string }, isDev = false, ): Promise => requestPatch( @@ -308,6 +359,68 @@ const humaAccountUpdate = async ( humaAccount, ) +/** + * Update huma account referral code. + * + * @param {string} networkType Network type. + * @param {string} referralCode The referral code. + * @param {boolean} isDev Is dev environment or not. + * @param {HumaAccount} humaAccount The Huma account. + * @returns {Promise} Promise that returns void. + */ +const humaAccountUpdateReferral = async ( + networkType: NETWORK_TYPE, + referralCode: string, + isDev = false, +): Promise => + requestPost( + `${configUtil.getIdentityAPIUrlV2(networkType, isDev)}/account/referral`, + { referralCode }, + ) + +/** + * Huma account adds wallet. + * + * @param {string} networkType Network type. + * @param {string} walletAddress The wallet address. + * @param {number} chainId Chain ID. + * @param {boolean} isDev Is dev environment or not. + * @returns {Promise} Promise that returns huma account. + */ +const humaAccountAddWallet = async ( + networkType: NETWORK_TYPE, + walletAddress: string, + chainId: number, + isDev = false, +): Promise => + requestPost( + `${configUtil.getIdentityAPIUrlV2(networkType, isDev)}/account/wallets`, + { + walletAddress, + chainId: String(chainId), + }, + ) + +/** + * Huma account name validity. + * + * @param {string} networkType Network type. + * @param {name} name Name to check validity. + * @param {boolean} isDev Is dev environment or not. + * @returns {Promise} Promise that returns huma account. + */ +const humaAccountNameValidity = async ( + networkType: NETWORK_TYPE, + name: string, + isDev = false, +): Promise => + requestGet( + `${configUtil.getIdentityAPIUrlV2( + networkType, + isDev, + )}/account/name-validity?name=${name}`, + ) + export const IdentityServiceV2 = { getVerificationStatusV2, accredit, @@ -316,7 +429,12 @@ export const IdentityServiceV2 = { consentToSubscription, approveLender, authenticate, + getHumaAccountOld, getHumaAccount, humaAccountLogin, + humaAccountLogout, humaAccountUpdate, + humaAccountAddWallet, + humaAccountUpdateReferral, + humaAccountNameValidity, } diff --git a/packages/huma-shared/src/utils/config.ts b/packages/huma-shared/src/utils/config.ts index c128b31f..3c2c7f04 100644 --- a/packages/huma-shared/src/utils/config.ts +++ b/packages/huma-shared/src/utils/config.ts @@ -1,5 +1,5 @@ import { isSolanaTestnet, SolanaChainEnum } from '../solana/chain' -import { CHAINS } from './chain' +import { CHAINS, NETWORK_TYPE } from './chain' const getDevPrefix = (isDev = false) => (isDev ? 'dev.' : '') @@ -44,10 +44,7 @@ const getIdentityAPIUrl = (chainId: number, isDev = false) => isDev, )}.identity-verification.huma.finance` -const getIdentityAPIUrlV2 = ( - networkType: 'testnet' | 'mainnet', - isDev = false, -) => +const getIdentityAPIUrlV2 = (networkType: NETWORK_TYPE, isDev = false) => `https://${getDevPrefix( isDev, )}${networkType}.identity-verification.huma.finance` diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts index 6c183045..444fde50 100644 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts @@ -134,6 +134,8 @@ export const useAuthErrorHandling = ( 'IdTokenNotFoundException', 'InvalidIdTokenException', 'WalletMismatchException', + 'AccountTokenNotFoundException', + 'InvalidAccountTokenException', ].includes(error.response?.data?.detail?.type) const isWalletNotCreatedError = error === 'WalletNotCreatedException' diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts index 18d513a7..efca4b54 100644 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts @@ -38,6 +38,8 @@ export const useAuthErrorHandling = ( 'IdTokenNotFoundException', 'InvalidIdTokenException', 'WalletMismatchException', + 'AccountTokenNotFoundException', + 'InvalidAccountTokenException', ].includes(error.response?.data?.detail?.type) const isWalletNotCreatedError = error === 'WalletNotCreatedException' From b7c763a7bdef995455827c5ed6f4504a625e07bc Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Tue, 26 Nov 2024 17:11:57 +0800 Subject: [PATCH 04/22] Account management kyc kyb (#376) * update account * update account name * fix get native currency issue * update HumaAccountLoginResult typing * update identity service v2 to account level * fix approve lender state issue * update approveLender endpoint payload --------- Co-authored-by: shan --- .../src/services/IdentityServiceV2.ts | 140 ++++------ packages/huma-shared/src/solana/chain.ts | 6 + packages/huma-shared/src/utils/chain.ts | 6 +- .../Lend/components/PersonaEvaluation.tsx | 260 +++++++++--------- .../Lend/solanaSupply/1-Evaluation.tsx | 7 +- .../components/Lend/solanaSupply/index.tsx | 5 +- .../components/Lend/supplyV2/2-Evaluation.tsx | 7 +- .../src/components/Lend/supplyV2/index.tsx | 3 +- 8 files changed, 197 insertions(+), 237 deletions(-) diff --git a/packages/huma-shared/src/services/IdentityServiceV2.ts b/packages/huma-shared/src/services/IdentityServiceV2.ts index e3e6b350..a91a457c 100644 --- a/packages/huma-shared/src/services/IdentityServiceV2.ts +++ b/packages/huma-shared/src/services/IdentityServiceV2.ts @@ -78,7 +78,6 @@ export type HumaAccount = { address: string chainId: string }[] - isNewAccount: boolean referralCode: string numReferrals: number referrer: { @@ -87,6 +86,16 @@ export type HumaAccount = { } } +/** + * Object representing the Huma account login result. + * @typedef {Object} HumaAccountLoginResult + * @property {HumaAccount} account The Huma account. + * @property {boolean} isNewAccount Is new account or not. + */ +export type HumaAccountLoginResult = HumaAccount & { + isNewAccount: boolean +} + /** * Object representing the accreditation answers. * @typedef {Object} AccreditationAnswers @@ -118,108 +127,99 @@ export type AccountNameValidity = { } /** - * Get wallet's identity verification status. + * Get account's identity verification status. * - * @param {string} walletAddress The wallet address. - * @param {number} chainId Chain ID. + * @param {string} networkType Network type. * @param {boolean} isDev Is dev environment or not. * @returns {Promise} Promise that returns the verification status result. */ const getVerificationStatusV2 = async ( - walletAddress: string, - chainId: number, + networkType: NETWORK_TYPE, isDev = false, ): Promise => requestGet( - `${configUtil.getIdentityAPIUrl( - chainId, + `${configUtil.getIdentityAPIUrlV2( + networkType, isDev, - )}/wallets/${walletAddress}/verification-status?&chainId=${chainId}`, + )}/account/verification-status`, ) /** - * Start wallet's accreditation process. + * Start account's accreditation process. * - * @param {string} walletAddress The wallet address. - * @param {number} chainId Chain ID. + * @param {NETWORK_TYPE} networkType Network type. * @param {AccreditationAnswers} answers accreditation answer. * @param {boolean} isDev Is dev environment or not. - * @returns {Promise} Promise that returns the start verification result. + * @returns {Promise} Promise that returns the accreditation result. */ const accredit = async ( - walletAddress: string, - chainId: number, + networkType: NETWORK_TYPE, answers: AccreditationAnswers, isDev = false, ): Promise => requestPost( - `${configUtil.getIdentityAPIUrl( - chainId, + `${configUtil.getIdentityAPIUrlV2( + networkType, isDev, - )}/wallets/${walletAddress}/accredit?chainId=${chainId}`, + )}/account/accreditation`, { answers }, ) /** - * Start wallet's verification process. + * Start account's verification process. * - * @param {string} walletAddress The wallet address. - * @param {number} chainId Chain ID. + * @param {NETWORK_TYPE} networkType Network type. * @param {boolean} isDev Is dev environment or not. * @returns {Promise} Promise that returns the start verification result. */ const startVerification = async ( - walletAddress: string, - chainId: number, + networkType: NETWORK_TYPE, isDev = false, ): Promise => requestPost( - `${configUtil.getIdentityAPIUrl( - chainId, + `${configUtil.getIdentityAPIUrlV2( + networkType, isDev, - )}/wallets/${walletAddress}/start-verification?chainId=${chainId}`, + )}/account/verification`, ) /** - * Resume wallet's verification process. + * Resume account's verification process. * - * @param {string} walletAddress The wallet address. - * @param {number} chainId Chain ID. + * @param {NETWORK_TYPE} networkType Network type. * @param {boolean} isDev Is dev environment or not. - * @returns {Promise} Promise that returns the start verification result. + * @returns {Promise} Promise that returns the resume verification result. */ const resumeVerification = async ( - walletAddress: string, - chainId: number, + networkType: NETWORK_TYPE, isDev = false, ): Promise => - requestPost( - `${configUtil.getIdentityAPIUrl( - chainId, + requestPatch( + `${configUtil.getIdentityAPIUrlV2( + networkType, isDev, - )}/wallets/${walletAddress}/resume-verification?chainId=${chainId}`, + )}/account/verification`, ) /** * Consent to subscription. * - * @param {string} walletAddress The wallet address. - * @param {number} chainId Chain ID. + * @param {NETWORK_TYPE} networkType Network type. * @param {string} documentHash The subscription file hash. * @param {boolean} isDev Is dev environment or not. * @returns {Promise} Promise that returns void. */ const consentToSubscription = async ( - walletAddress: string, - chainId: number, + networkType: NETWORK_TYPE, documentHash: string, isDev = false, ): Promise => requestPost( - `${configUtil.getIdentityAPIUrl( - chainId, + `${configUtil.getIdentityAPIUrlV2( + networkType, isDev, - )}/wallets/${walletAddress}/consent-to-subscription?chainId=${chainId}&documentHash=${documentHash}`, + )}/account/purchase-consent`, + { documentHash }, ) /** @@ -239,52 +239,12 @@ const approveLender = async ( isDev = false, chainSpecificData?: Record, ): Promise => - requestPost( + requestPatch( `${configUtil.getIdentityAPIUrl( chainId, isDev, - )}/wallets/${walletAddress}/approve-lender?chainId=${chainId}&contractAddress=${contractAddress}`, - { chain_specific_data: chainSpecificData }, - ) - -/** - * Authenticate wallet account. - * - * @param {string} walletAddress The wallet address. - * @param {number} chainId Chain ID. - * @param {boolean} isDev Is dev environment or not. - * @returns {Promise} Promise that returns void. - */ -const authenticate = async ( - walletAddress: string, - chainId: number, - isDev = false, -): Promise => - requestPost( - `${configUtil.getIdentityAPIUrl( - chainId, - isDev, - )}/wallets/${walletAddress}/authenticate?chainId=${chainId}`, - ) - -/** - * Get Huma account old. - * - * @param {string} walletAddress The wallet address. - * @param {number} chainId Chain ID. - * @param {boolean} isDev Is dev environment or not. - * @returns {Promise} Promise that returns void. - */ -const getHumaAccountOld = async ( - walletAddress: string, - chainId: number, - isDev = false, -): Promise => - requestGet( - `${configUtil.getIdentityAPIUrl( - chainId, - isDev, - )}/wallets/${walletAddress}/account?chainId=${chainId}`, + )}/account/wallets/${chainId}/${walletAddress}`, + { trancheAddress: contractAddress, chainSpecificData }, ) /** @@ -311,14 +271,14 @@ const getHumaAccount = async ( * @param {string} walletAddress The wallet address. * @param {number} chainId Chain ID. * @param {boolean} isDev Is dev environment or not. - * @returns {Promise} Promise that returns void. + * @returns {Promise} Promise that returns HumaAccountLoginResult. */ const humaAccountLogin = async ( walletAddress: string, chainId: number, isDev = false, -): Promise => - requestPost( +): Promise => + requestPost( `${configUtil.getIdentityAPIUrl(chainId, isDev)}/auth/login`, { walletAddress, @@ -428,8 +388,6 @@ export const IdentityServiceV2 = { resumeVerification, consentToSubscription, approveLender, - authenticate, - getHumaAccountOld, getHumaAccount, humaAccountLogin, humaAccountLogout, diff --git a/packages/huma-shared/src/solana/chain.ts b/packages/huma-shared/src/solana/chain.ts index f3f719e5..d1ad6ea6 100644 --- a/packages/huma-shared/src/solana/chain.ts +++ b/packages/huma-shared/src/solana/chain.ts @@ -1,3 +1,5 @@ +import { NETWORK_TYPE } from '../utils' + export enum SolanaChainEnum { SolanaDevnet = 901, SolanaMainnet = 900, @@ -20,6 +22,10 @@ export function isSolanaTestnet(chainId: SolanaChainEnum): boolean { return chainId !== SolanaChainEnum.SolanaMainnet } +export function getSolanaNetworkType(chainId: SolanaChainEnum): NETWORK_TYPE { + return isSolanaTestnet(chainId) ? NETWORK_TYPE.testnet : NETWORK_TYPE.mainnet +} + export function getSolanaExplorerUrl( chainId: SolanaChainEnum, signature: string, diff --git a/packages/huma-shared/src/utils/chain.ts b/packages/huma-shared/src/utils/chain.ts index db1d16b8..84c5ae5e 100644 --- a/packages/huma-shared/src/utils/chain.ts +++ b/packages/huma-shared/src/utils/chain.ts @@ -183,6 +183,10 @@ export function isTestnet(chainId: number): boolean { return CHAINS[chainId].isTestnet ?? false } +export function getEvmNetworkType(chainId: ChainEnum): NETWORK_TYPE { + return isTestnet(chainId) ? NETWORK_TYPE.testnet : NETWORK_TYPE.mainnet +} + export function isChainEnum( chainId: number | string | undefined, ): chainId is keyof typeof ChainEnum { @@ -192,7 +196,7 @@ export function isChainEnum( function isExtendedChainInformation( chainInformation: BasicChainInformation | ExtendedChainInformation, ): chainInformation is ExtendedChainInformation { - return !!(chainInformation as ExtendedChainInformation).nativeCurrency + return !!(chainInformation as ExtendedChainInformation)?.nativeCurrency } export function getAddChainParameters( diff --git a/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx b/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx index 405040d0..5126bc59 100644 --- a/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx +++ b/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx @@ -1,6 +1,4 @@ import { - CAMPAIGN_REFERENCE_CODE, - CampaignService, CHAIN_TYPE, checkIsDev, CloseModalOptions, @@ -8,6 +6,7 @@ import { IdentityVerificationStatusV2, KYCCopy, KYCType, + NETWORK_TYPE, TrancheType, VerificationStatusResultV2, } from '@huma-finance/shared' @@ -33,9 +32,9 @@ type Props = { juniorTrancheVault: string seniorTrancheVault: string } + networkType: NETWORK_TYPE chainType: CHAIN_TYPE isUniTranche: boolean - pointsTestnetExperience: boolean campaign?: Campaign chainSpecificData?: Record changeTranche: (tranche: TrancheType) => void @@ -46,7 +45,7 @@ export function PersonaEvaluation({ poolInfo, isUniTranche, campaign, - pointsTestnetExperience, + networkType, chainType, chainSpecificData, changeTranche, @@ -75,96 +74,73 @@ export function PersonaEvaluation({ const isActionOngoingRef = useRef(false) const isKYCResumedRef = useRef(false) - useEffect(() => { - const createNewWallet = async () => { - if (isWalletOwnershipVerified && campaign && account) { - await CampaignService.createNewWallet( - account, - localStorage.getItem(CAMPAIGN_REFERENCE_CODE) ?? undefined, - isDev, - pointsTestnetExperience, - ) - } - } - createNewWallet() - }, [ - account, - campaign, - isDev, - isWalletOwnershipVerified, - pointsTestnetExperience, - ]) - - const approveLender = useCallback(async () => { - isActionOngoingRef.current = true - setLoadingType('approveLender') - try { - await IdentityServiceV2.approveLender( - account!, - chainId!, - poolInfo.juniorTrancheVault, - isDev, - chainSpecificData, - ) - } catch (e: unknown) { - console.error(e) - } - - if (!isUniTranche) { + const approveLender = useCallback( + async (account: string, chainId: number) => { + isActionOngoingRef.current = true + setLoadingType('approveLender') try { await IdentityServiceV2.approveLender( - account!, - chainId!, - poolInfo.seniorTrancheVault, + account, + chainId, + poolInfo.juniorTrancheVault, isDev, chainSpecificData, ) } catch (e: unknown) { console.error(e) } - } - if (isUniTranche) { - changeTranche('junior') - dispatch(setStep(WIDGET_STEP.ChooseAmount)) - } else { - dispatch(setStep(WIDGET_STEP.ChooseTranche)) - } - isActionOngoingRef.current = false - setLoadingType(undefined) - }, [ - account, - chainId, - chainSpecificData, - changeTranche, - dispatch, - isDev, - isUniTranche, - poolInfo.juniorTrancheVault, - poolInfo.seniorTrancheVault, - ]) - - const checkVerificationStatus = useCallback(async () => { - if (isActionOngoingRef.current || isKYCResumedRef.current) { - return - } - try { - if (account && chainId) { - isActionOngoingRef.current = true - setLoadingType('verificationStatus') - const verificationStatus = - await IdentityServiceV2.getVerificationStatusV2( + if (!isUniTranche) { + try { + await IdentityServiceV2.approveLender( account, chainId, + poolInfo.seniorTrancheVault, isDev, + chainSpecificData, ) + } catch (e: unknown) { + console.error(e) + } + } + if (isUniTranche) { + changeTranche('junior') + dispatch(setStep(WIDGET_STEP.ChooseAmount)) + } else { + dispatch(setStep(WIDGET_STEP.ChooseTranche)) + } + + isActionOngoingRef.current = false + setLoadingType(undefined) + }, + [ + chainSpecificData, + changeTranche, + dispatch, + isDev, + isUniTranche, + poolInfo.juniorTrancheVault, + poolInfo.seniorTrancheVault, + ], + ) + + const checkVerificationStatus = useCallback( + async (account: string, chainId: number) => { + if (isActionOngoingRef.current || isKYCResumedRef.current) { + return + } + try { + isActionOngoingRef.current = true + setLoadingType('verificationStatus') + const verificationStatus = + await IdentityServiceV2.getVerificationStatusV2(networkType, isDev) setVerificationStatus(verificationStatus) setInquiryId(verificationStatus.personaInquiryId) switch (verificationStatus.status) { case IdentityVerificationStatusV2.ACCREDITED: { const startVerificationResult = - await IdentityServiceV2.startVerification(account, chainId, isDev) + await IdentityServiceV2.startVerification(networkType, isDev) setInquiryId(startVerificationResult.personaInquiryId) setKYCCopy(KYCCopies.verifyIdentity) setLoadingType(undefined) @@ -188,11 +164,7 @@ export function PersonaEvaluation({ case IdentityVerificationStatusV2.PENDING: { if (!isKYCCompletedRef.current) { const resumeVerificationResult = - await IdentityServiceV2.resumeVerification( - account, - chainId, - isDev, - ) + await IdentityServiceV2.resumeVerification(networkType, isDev) verificationStatus.status = resumeVerificationResult.status setVerificationStatus(verificationStatus) setSessionToken(resumeVerificationResult.sessionToken) @@ -207,11 +179,7 @@ export function PersonaEvaluation({ case IdentityVerificationStatusV2.EXPIRED: { const resumeVerificationResult = - await IdentityServiceV2.resumeVerification( - account, - chainId, - isDev, - ) + await IdentityServiceV2.resumeVerification(networkType, isDev) verificationStatus.status = resumeVerificationResult.status setVerificationStatus(verificationStatus) setSessionToken(resumeVerificationResult.sessionToken) @@ -247,30 +215,40 @@ export function PersonaEvaluation({ } case IdentityVerificationStatusV2.CONSENTED_TO_SUBSCRIPTION: { - await approveLender() + await approveLender(account, chainId) break } default: break } + } catch (e: unknown) { + setAuthError(e) + } finally { + isActionOngoingRef.current = false } - } catch (e: unknown) { - setAuthError(e) - } finally { - isActionOngoingRef.current = false - } - }, [KYCCopies, account, approveLender, chainId, isDev, setAuthError]) + }, + [KYCCopies, approveLender, isDev, networkType, setAuthError], + ) useEffect(() => { - checkVerificationStatus() - }, [checkVerificationStatus]) + if (account && chainId) { + checkVerificationStatus(account, chainId) + } + }, [account, chainId, checkVerificationStatus]) useEffect(() => { - if (isWalletOwnershipVerificationRequired && isWalletOwnershipVerified) { - checkVerificationStatus() + if ( + account && + chainId && + isWalletOwnershipVerificationRequired && + isWalletOwnershipVerified + ) { + checkVerificationStatus(account, chainId) } }, [ + account, + chainId, checkVerificationStatus, isWalletOwnershipVerificationRequired, isWalletOwnershipVerified, @@ -279,18 +257,20 @@ export function PersonaEvaluation({ useEffect(() => { const interval = setInterval(() => { if ( + account && + chainId && verificationStatus?.status && [ IdentityVerificationStatusV2.CREATED, IdentityVerificationStatusV2.PENDING, ].includes(verificationStatus.status) ) { - checkVerificationStatus() + checkVerificationStatus(account, chainId) } }, 3 * 1000) // eslint-disable-next-line consistent-return return () => clearInterval(interval) - }, [checkVerificationStatus, verificationStatus?.status]) + }, [account, chainId, checkVerificationStatus, verificationStatus?.status]) useEffect(() => { if (errorType === 'NotSignedIn') { @@ -318,41 +298,49 @@ export function PersonaEvaluation({ } }, [showPersonaClient]) - const startKYC = useCallback(async () => { - isActionOngoingRef.current = true - isKYCResumedRef.current = false - setLoadingType('startKYC') - const client: Client = new Persona.Client({ - inquiryId, - sessionToken, - frameWidth: '480px', - onReady: () => { - client.open() - setShowPersonaClient(true) - }, - onComplete: () => { - isKYCCompletedRef.current = true - isActionOngoingRef.current = false - setShowPersonaClient(false) - checkVerificationStatus() - }, - onCancel: () => { - isActionOngoingRef.current = false - setShowPersonaClient(false) - setLoadingType(undefined) - handleClose() - }, - onError: () => { - isActionOngoingRef.current = false - setShowPersonaClient(false) - setLoadingType(undefined) - }, - }) - }, [checkVerificationStatus, handleClose, inquiryId, sessionToken]) + const startKYC = useCallback( + async (account: string, chainId: number) => { + isActionOngoingRef.current = true + isKYCResumedRef.current = false + setLoadingType('startKYC') + const client: Client = new Persona.Client({ + inquiryId, + sessionToken, + frameWidth: '480px', + onReady: () => { + client.open() + setShowPersonaClient(true) + }, + onComplete: () => { + isKYCCompletedRef.current = true + isActionOngoingRef.current = false + setShowPersonaClient(false) + checkVerificationStatus(account, chainId) + }, + onCancel: () => { + isActionOngoingRef.current = false + setShowPersonaClient(false) + setLoadingType(undefined) + handleClose() + }, + onError: () => { + isActionOngoingRef.current = false + setShowPersonaClient(false) + setLoadingType(undefined) + }, + }) + }, + [checkVerificationStatus, handleClose, inquiryId, sessionToken], + ) // Start KYC flow directly for the first time useEffect(() => { - if (isActionOngoingRef.current || isKYCResumedRef.current) { + if ( + !account || + !chainId || + isActionOngoingRef.current || + isKYCResumedRef.current + ) { return } if (verificationStatus && !KYCAutoStarted) { @@ -361,7 +349,7 @@ export function PersonaEvaluation({ case IdentityVerificationStatusV2.CREATED: case IdentityVerificationStatusV2.PENDING: case IdentityVerificationStatusV2.EXPIRED: { - startKYC() + startKYC(account, chainId) setKYCAutoStarted(true) break } @@ -370,16 +358,16 @@ export function PersonaEvaluation({ break } } - }, [KYCAutoStarted, startKYC, verificationStatus]) + }, [KYCAutoStarted, account, chainId, startKYC, verificationStatus]) - const handleAction = () => { - if (verificationStatus) { + const handleAction = useCallback(() => { + if (account && chainId && verificationStatus) { switch (verificationStatus.status) { case IdentityVerificationStatusV2.ACCREDITED: case IdentityVerificationStatusV2.CREATED: case IdentityVerificationStatusV2.PENDING: case IdentityVerificationStatusV2.EXPIRED: - startKYC() + startKYC(account, chainId) break case IdentityVerificationStatusV2.DECLINED: @@ -396,7 +384,7 @@ export function PersonaEvaluation({ break } } - } + }, [account, chainId, handleClose, startKYC, verificationStatus]) const styles = { iconWrapper: css` diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/1-Evaluation.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/1-Evaluation.tsx index 12db7f29..c0248502 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/1-Evaluation.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/1-Evaluation.tsx @@ -1,5 +1,6 @@ import { CHAIN_TYPE, + NETWORK_TYPE, SOLANA_CHAIN_INFO, SolanaPoolInfo, TrancheType, @@ -12,8 +13,8 @@ import { Campaign } from '../supplyV2' type Props = { poolInfo: SolanaPoolInfo isUniTranche: boolean - pointsTestnetExperience: boolean campaign?: Campaign + networkType: NETWORK_TYPE changeTranche: (tranche: TrancheType) => void handleClose: () => void } @@ -21,8 +22,8 @@ type Props = { export function Evaluation({ poolInfo, isUniTranche, - pointsTestnetExperience, campaign, + networkType, changeTranche, handleClose, }: Props): React.ReactElement | null { @@ -38,8 +39,8 @@ export function Evaluation({ }} handleClose={handleClose} isUniTranche={isUniTranche} - pointsTestnetExperience={pointsTestnetExperience} campaign={campaign} + networkType={networkType} chainSpecificData={{ huma_program_id: solanChainInfo.poolProgram, pool_id: poolInfo.poolId, diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx index c730b3fa..7e2094b8 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx @@ -1,5 +1,6 @@ import { CloseModalOptions, + getSolanaNetworkType, SolanaPoolInfo, TrancheType, } from '@huma-finance/shared' @@ -21,9 +22,9 @@ import { WidgetWrapper } from '../../WidgetWrapper' import { Evaluation } from './1-Evaluation' import { ChooseTranche } from './2-ChooseTranche' import { ChooseAmount } from './3-ChooseAmount' +import { ApproveAllowance } from './4-ApproveAllowance' import { Transfer } from './4-Transfer' import { Success } from './5-Success' -import { ApproveAllowance } from './4-ApproveAllowance' export interface Campaign { id: string @@ -122,8 +123,8 @@ export function SolanaLendSupply({ diff --git a/packages/huma-widget/src/components/Lend/supplyV2/2-Evaluation.tsx b/packages/huma-widget/src/components/Lend/supplyV2/2-Evaluation.tsx index e32b775d..ddd23180 100644 --- a/packages/huma-widget/src/components/Lend/supplyV2/2-Evaluation.tsx +++ b/packages/huma-widget/src/components/Lend/supplyV2/2-Evaluation.tsx @@ -1,6 +1,7 @@ import { CHAIN_TYPE, CloseModalOptions, + NETWORK_TYPE, PoolInfoV2, TrancheType, } from '@huma-finance/shared' @@ -13,8 +14,8 @@ import { SecuritizeEvaluation } from '../components/SecuritizeEvaluation' type Props = { poolInfo: PoolInfoV2 isUniTranche: boolean - pointsTestnetExperience: boolean campaign?: Campaign + networkType: NETWORK_TYPE changeTranche: (tranche: TrancheType) => void handleClose: (options?: CloseModalOptions) => void } @@ -23,7 +24,7 @@ export function Evaluation({ poolInfo, isUniTranche, campaign, - pointsTestnetExperience, + networkType, changeTranche, handleClose, }: Props): React.ReactElement | null { @@ -38,8 +39,8 @@ export function Evaluation({ poolInfo={poolInfo} handleClose={handleClose} isUniTranche={isUniTranche} - pointsTestnetExperience={pointsTestnetExperience} campaign={campaign} + networkType={networkType} chainType={CHAIN_TYPE.EVM} changeTranche={changeTranche} /> diff --git a/packages/huma-widget/src/components/Lend/supplyV2/index.tsx b/packages/huma-widget/src/components/Lend/supplyV2/index.tsx index 7cd7b502..8cf2c082 100644 --- a/packages/huma-widget/src/components/Lend/supplyV2/index.tsx +++ b/packages/huma-widget/src/components/Lend/supplyV2/index.tsx @@ -1,5 +1,6 @@ import { CloseModalOptions, + getEvmNetworkType, openInNewTab, POOL_NAME, TrancheType, @@ -180,8 +181,8 @@ export function LendSupplyV2({ handleClose={handleClose} isUniTranche={isUniTranche} changeTranche={setSelectedTranche} - pointsTestnetExperience={pointsTestnetExperience} campaign={campaign} + networkType={getEvmNetworkType(poolInfo.chainId)} /> )} {step === WIDGET_STEP.ChooseAmount && ( From 91001eb212840b599c69596604144786bb8ddc70 Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:08:08 +0800 Subject: [PATCH 05/22] fix account login issue (#379) Co-authored-by: shan --- packages/huma-shared/src/services/IdentityServiceV2.ts | 10 +++++++--- .../components/Lend/components/PersonaEvaluation.tsx | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/huma-shared/src/services/IdentityServiceV2.ts b/packages/huma-shared/src/services/IdentityServiceV2.ts index a91a457c..6233814c 100644 --- a/packages/huma-shared/src/services/IdentityServiceV2.ts +++ b/packages/huma-shared/src/services/IdentityServiceV2.ts @@ -225,6 +225,7 @@ const consentToSubscription = async ( /** * Approve wallet as lender. * + * @param {NETWORK_TYPE} networkType Network type. * @param {string} walletAddress The wallet address. * @param {number} chainId Chain ID. * @param {string} contractAddress The tranche vault contract address. @@ -233,6 +234,7 @@ const consentToSubscription = async ( * @returns {Promise} Promise that returns void. */ const approveLender = async ( + networkType: NETWORK_TYPE, walletAddress: string, chainId: number, contractAddress: string, @@ -240,8 +242,8 @@ const approveLender = async ( chainSpecificData?: Record, ): Promise => requestPatch( - `${configUtil.getIdentityAPIUrl( - chainId, + `${configUtil.getIdentityAPIUrlV2( + networkType, isDev, )}/account/wallets/${chainId}/${walletAddress}`, { trancheAddress: contractAddress, chainSpecificData }, @@ -268,18 +270,20 @@ const getHumaAccount = async ( /** * Huma account login by wallet address and chain. * + * @param {string} networkType Network type. * @param {string} walletAddress The wallet address. * @param {number} chainId Chain ID. * @param {boolean} isDev Is dev environment or not. * @returns {Promise} Promise that returns HumaAccountLoginResult. */ const humaAccountLogin = async ( + networkType: NETWORK_TYPE, walletAddress: string, chainId: number, isDev = false, ): Promise => requestPost( - `${configUtil.getIdentityAPIUrl(chainId, isDev)}/auth/login`, + `${configUtil.getIdentityAPIUrlV2(networkType, isDev)}/auth/login`, { walletAddress, chainId: String(chainId), diff --git a/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx b/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx index 5126bc59..23e35dbd 100644 --- a/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx +++ b/packages/huma-widget/src/components/Lend/components/PersonaEvaluation.tsx @@ -80,6 +80,7 @@ export function PersonaEvaluation({ setLoadingType('approveLender') try { await IdentityServiceV2.approveLender( + networkType, account, chainId, poolInfo.juniorTrancheVault, @@ -93,6 +94,7 @@ export function PersonaEvaluation({ if (!isUniTranche) { try { await IdentityServiceV2.approveLender( + networkType, account, chainId, poolInfo.seniorTrancheVault, @@ -119,6 +121,7 @@ export function PersonaEvaluation({ dispatch, isDev, isUniTranche, + networkType, poolInfo.juniorTrancheVault, poolInfo.seniorTrancheVault, ], From 658977c0d3a42597f535b98cf55172de0aa2316f Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Sat, 30 Nov 2024 23:51:11 +0800 Subject: [PATCH 06/22] Leaderboard (#380) * get leaderboard endpoint * acount points endpoint * recently joined endpoint --------- Co-authored-by: shan --- .../src/services/CampaignService.ts | 109 +++++++++++++++++- .../src/services/IdentityServiceV2.ts | 30 ++++- packages/huma-shared/src/utils/config.ts | 6 + 3 files changed, 143 insertions(+), 2 deletions(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index 5637a30a..d8bed593 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -1,7 +1,7 @@ import { gql } from 'graphql-request' import { SolanaChainEnum, SolanaPoolInfo } from '../solana' -import { ChainEnum } from '../utils/chain' +import { ChainEnum, NETWORK_TYPE } from '../utils/chain' import { configUtil } from '../utils/config' import { requestPost } from '../utils/request' import { PoolInfoV2 } from '../v2/utils/pool' @@ -84,6 +84,25 @@ type CampaignPoints = { lockupPeriodMonths: number } +export type LeaderboardItem = { + accountId: string + accountName: string + points: number + rank: number + referredCount: number +} + +export type AccountPoints = { + accountId: string + basePoints: number + liquidityPoints: number + liquidityPointsList: { + address: string + points: number + }[] + referralPoints: number +} + function checkWalletOwnership( wallet: string, isDev: boolean, @@ -203,6 +222,92 @@ function getWalletRankList( }) } +function getLeaderboard( + seasonId: string, + networkType: NETWORK_TYPE, + isDev: boolean, +): Promise<{ data: LeaderboardItem[] } | undefined> { + const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) + + const query = gql` + query { + leaderboard(seasonId: "${seasonId}") { + ... on LeaderboardResult { + data { + accountId + accountName + points + referredCount + rank + } + } + ... on PointServiceError { + message + } + } + } + ` + + return requestPost<{ + data?: { leaderboard: { data: LeaderboardItem[] } } + errors?: unknown + }>(url, JSON.stringify({ query })) + .then((res) => { + if (res.errors) { + console.error(res.errors) + return undefined + } + return res.data?.leaderboard + }) + .catch((err) => { + console.error(err) + return undefined + }) +} + +function getAccountPoints( + networkType: NETWORK_TYPE, + isDev: boolean, +): Promise { + const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) + + const query = gql` + query { + accountPoints { + ... on AccountPointsResult { + accountId + basePoints + liquidityPoints + liquidityPointsList { + address + points + } + referralPoints + } + ... on PointServiceError { + message + } + } + } + ` + + return requestPost<{ + data?: { accountPoints: AccountPoints } + errors?: unknown + }>(url, JSON.stringify({ query })) + .then((res) => { + if (res.errors) { + console.error(res.errors) + return undefined + } + return res.data?.accountPoints + }) + .catch((err) => { + console.error(err) + return undefined + }) +} + function getRecentJoins( isDev: boolean, pointsTestnetExperience: boolean, @@ -470,4 +575,6 @@ export const CampaignService = { createNewWallet, updateWalletPoints, checkAndCreateWallet, + getLeaderboard, + getAccountPoints, } diff --git a/packages/huma-shared/src/services/IdentityServiceV2.ts b/packages/huma-shared/src/services/IdentityServiceV2.ts index 6233814c..fb64d5e5 100644 --- a/packages/huma-shared/src/services/IdentityServiceV2.ts +++ b/packages/huma-shared/src/services/IdentityServiceV2.ts @@ -68,7 +68,10 @@ export type ResumeVerificationResultV2 = { * @property {string} accountId The account id. * @property {string} name The account name. * @property {Wallet[]} wallets The account wallets. - * @property {boolean} isNewAccount Is new account or not. + * @property {string} referralCode The account referral code. + * @property {number} numReferrals The number of referrals. + * @property {Referrer} referrer The account referrer. + * @property {string} createdAt The account created time. */ export type HumaAccount = { id: string @@ -84,6 +87,7 @@ export type HumaAccount = { id: string name: string } + createdAt: string } /** @@ -385,6 +389,29 @@ const humaAccountNameValidity = async ( )}/account/name-validity?name=${name}`, ) +/** + * Get recently joined accounts. + * + * @param {string} networkType Network type. + * @param {boolean} isDev Is dev environment or not. + * @param {number} limit The limit of the number of accounts to return. + * @returns {Promise} Promise that returns recently joined huma accounts. + */ +const getRecentlyJoinedHumaAccounts = async ( + networkType: NETWORK_TYPE, + isDev = false, + limit = 10, +): Promise => { + const result = await requestGet<{ accounts: HumaAccount[] }>( + `${configUtil.getIdentityAPIUrlV2( + networkType, + isDev, + )}/accounts/recently-joined?limit=${limit}`, + ) + + return result.accounts +} + export const IdentityServiceV2 = { getVerificationStatusV2, accredit, @@ -399,4 +426,5 @@ export const IdentityServiceV2 = { humaAccountAddWallet, humaAccountUpdateReferral, humaAccountNameValidity, + getRecentlyJoinedHumaAccounts, } diff --git a/packages/huma-shared/src/utils/config.ts b/packages/huma-shared/src/utils/config.ts index 3c2c7f04..c642bc4e 100644 --- a/packages/huma-shared/src/utils/config.ts +++ b/packages/huma-shared/src/utils/config.ts @@ -72,6 +72,11 @@ const getCampaignAPIUrl = (isDev: boolean, pointsTestnetExperience: boolean) => pointsTestnetExperience ? 'testnet.' : 'mainnet.' }campaign-points.huma.finance/graphql` +const getCampaignAPIUrlV2 = (networkType: NETWORK_TYPE, isDev: boolean) => + `https://${getDevPrefix( + isDev, + )}${networkType}.campaign-points.huma.finance/graphql` + const getSolanaGraphAPIUrl = ( isDev: boolean, pointsTestnetExperience: boolean, @@ -117,6 +122,7 @@ export const configUtil = { getAuthServiceUrl, getKYCProviderBaseUrl, getCampaignAPIUrl, + getCampaignAPIUrlV2, getSolanaGraphAPIUrl, DEFAULT_CHAIN_ID, } From bcb5fba025e2719b4ebbe475e8b492001603583e Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Sat, 30 Nov 2024 23:55:20 +0800 Subject: [PATCH 07/22] update campaign service (#381) Co-authored-by: shan --- .../src/services/CampaignService.ts | 315 +----------------- packages/huma-widget/API.md | 2 - .../Lend/solanaSupply/4-Transfer.tsx | 11 +- .../Lend/solanaSupply/6-PointsEarned.tsx | 23 +- .../components/Lend/solanaSupply/index.tsx | 5 +- .../components/Lend/supplyV2/5-Transfer.tsx | 11 +- .../src/components/Lend/supplyV2/index.tsx | 5 +- 7 files changed, 39 insertions(+), 333 deletions(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index d8bed593..99b6a825 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -53,30 +53,6 @@ export type SolanaCampaignGroup = { partners: CampaignPartner[] } -type Wallet = { - id: string - address: string - referralCode: string - referrer: { - id: string - address: string - } - createdAt: string -} - -type WalletPoint = { - id: string - rank: number - wallet: Wallet - totalPoints: number - numberOfReferred: number -} - -type WalletRank = { - totalCount: number - walletPoints: WalletPoint[] -} - type CampaignPoints = { campaignId: string juniorTranchePoints: number @@ -105,10 +81,10 @@ export type AccountPoints = { function checkWalletOwnership( wallet: string, + networkType: NETWORK_TYPE, isDev: boolean, - pointsTestnetExperience: boolean, ): Promise { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) + const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) const query = gql` query { @@ -135,93 +111,6 @@ function checkWalletOwnership( }) } -function getWalletInfo( - wallet: string, - isDev: boolean, - pointsTestnetExperience: boolean, -): Promise<{ wallet: Wallet; walletPoint: WalletPoint } | undefined> { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) - - const query = gql` - query { - wallet(address:"${wallet}") { - id - address - referralCode - } - walletPoint(address:"${wallet}") { - rank - numberOfReferred - totalPoints - } - } - ` - - return requestPost<{ - data?: { wallet: Wallet; walletPoint: WalletPoint } - errors?: unknown - }>(url, JSON.stringify({ query })) - .then((res) => { - if (res.errors) { - console.error(res.errors) - return undefined - } - return res.data - }) - .catch((err) => { - console.error(err) - return undefined - }) -} - -function getWalletRankList( - seasonId: string, - first: number, - skip: number, - isDev: boolean, - pointsTestnetExperience: boolean, -): Promise { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) - - const query = gql` - query { - walletPoints( - seasonId: "${seasonId}", - first: ${first}, - skip: ${skip} - ){ - totalCount - walletPoints { - id - rank - wallet { - id - address - } - totalPoints - numberOfReferred - } - } - } - ` - - return requestPost<{ - data?: { walletPoints: WalletRank } - errors?: unknown - }>(url, JSON.stringify({ query })) - .then((res) => { - if (res.errors) { - console.error(res.errors) - return undefined - } - return res.data?.walletPoints - }) - .catch((err) => { - console.error(err) - return undefined - }) -} - function getLeaderboard( seasonId: string, networkType: NETWORK_TYPE, @@ -308,107 +197,13 @@ function getAccountPoints( }) } -function getRecentJoins( - isDev: boolean, - pointsTestnetExperience: boolean, -): Promise { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) - const query = gql` - query { - wallets(first: 5, skip: 0, orderBy: "createdAt", orderDirection: "desc") { - wallets { - id - address - referrer { - id - address - } - createdAt - } - } - } - ` - - return ( - requestPost(url, JSON.stringify({ query })) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .then((res: any) => { - if (res.errors) { - console.error(res.errors) - return undefined - } - return res.data?.wallets?.wallets - }) - .catch((err) => { - console.error(err) - return undefined - }) - ) -} - -function getActiveSeasonAndCampaignGroups( - isDev: boolean, - pointsTestnetExperience: boolean, -): Promise<{ - activeSeason?: CampaignSeason - campaignGroups?: CampaignGroup[] -}> { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) - - const query = gql` - query { - activeSeason { - id - estimatedTotalPoints - name - } - campaignGroups { - id - name - campaigns { - id - name - chainId - juniorMultiplier - lockupPeriodMonths - seniorMultiplier - poolAddress - } - partners { - id - name - } - } - } - ` - - return requestPost<{ - data?: { - activeSeason: CampaignSeason - campaignGroups: CampaignGroup[] - } - errors?: unknown - }>(url, JSON.stringify({ query })) - .then((res) => { - if (res.errors) { - console.error(res.errors) - return {} - } - return res.data ?? {} - }) - .catch((err) => { - console.error(err) - return {} - }) -} - function getEstimatedPoints( campaignGroupId: string, principal: string, + networkType: NETWORK_TYPE, isDev: boolean, - pointsTestnetExperience: boolean, ): Promise { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) + const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) const query = gql` query { @@ -444,80 +239,28 @@ function getEstimatedPoints( }) } -function createNewWallet( - account: string, - referralCode: string | null | undefined, - isDev: boolean, - pointsTestnetExperience: boolean, -): Promise<{ wallet: string } | undefined> { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) - - const query = gql` - mutation { - createWallet( - input: { - walletAddress: "${account}" - referralCode: "${referralCode}" - } - ) { - ... on CreateWalletResult { - wallet { - address - } - } - ... on WalletExistsError { - message - } - ... on UnauthorizedError { - message - } - } - } - ` - - return requestPost<{ - data?: { - createWallet?: { - wallet: string - } - } - errors?: unknown - }>(url, JSON.stringify({ query })) - .then((res) => { - if (res.errors) { - console.error(res.errors) - return undefined - } - return res.data?.createWallet - }) - .catch((err) => { - console.error(err) - return undefined - }) -} - -function updateWalletPoints( - account: string, - hash: string, +function updateAccountPoints( + walletAddress: string, + transactionHash: string, chainId: ChainEnum | SolanaChainEnum, + networkType: NETWORK_TYPE, isDev: boolean, - pointsTestnetExperience: boolean, ): Promise<{ pointsAccumulated?: number }> { - const url = configUtil.getCampaignAPIUrl(isDev, pointsTestnetExperience) + const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) const query = gql` mutation { - updateWalletPoints( + updateAccountPoints( input: { chainId: ${chainId}, - walletAddress: "${account}", - transactionHash: "${hash}" + walletAddress: "${walletAddress}", + transactionHash: "${transactionHash}" } ) { - ... on UpdateWalletPointsResult { + ... on UpdateAccountPointsResult { pointsAccumulated } - ... on UnauthorizedError { + ... on PointServiceError { message } } @@ -526,7 +269,7 @@ function updateWalletPoints( return requestPost<{ data?: { - updateWalletPoints?: { pointsAccumulated?: number } + updateAccountPoints?: { pointsAccumulated?: number } } errors?: unknown }>(url, JSON.stringify({ query })) @@ -535,7 +278,7 @@ function updateWalletPoints( console.error(res.errors) return {} } - return res.data?.updateWalletPoints ?? {} + return res.data?.updateAccountPoints ?? {} }) .catch((err) => { console.error(err) @@ -543,38 +286,14 @@ function updateWalletPoints( }) } -async function checkAndCreateWallet( - account: string, - referralCode: string | null | undefined, - isDev: boolean, - pointsTestnetExperience: boolean, -): Promise<{ wallet: string } | undefined> { - const result = await getWalletInfo(account, isDev, pointsTestnetExperience) - if (!result?.wallet) { - return createNewWallet( - account, - referralCode, - isDev, - pointsTestnetExperience, - ) - } - return undefined -} - /** * An object that contains functions to interact with Huma's campaign service. * @namespace CampaignService */ export const CampaignService = { checkWalletOwnership, - getWalletInfo, - getWalletRankList, - getRecentJoins, - getActiveSeasonAndCampaignGroups, getEstimatedPoints, - createNewWallet, - updateWalletPoints, - checkAndCreateWallet, getLeaderboard, getAccountPoints, + updateAccountPoints, } diff --git a/packages/huma-widget/API.md b/packages/huma-widget/API.md index f3616637..4a95cde6 100644 --- a/packages/huma-widget/API.md +++ b/packages/huma-widget/API.md @@ -702,7 +702,6 @@ To be used when re-enabling autopay and other pool actions that require allowanc | --- | --- | --- | | poolInfo | SolanaPoolInfo |

The metadata of the pool.

| | poolState | SolanaPoolState |

The current state config of the pool.

| -| pointsTestnetExperience | boolean |

If the user is in the testnet experience.

| | handleClose | function |

Function to notify to close the widget modal when user clicks the 'x' close button.

| | handleSuccess | function |

Optional function to notify that the lending pool supply action is successful.

| @@ -747,7 +746,6 @@ To be used when re-enabling autopay and other pool actions that require allowanc | Name | Type | Description | | --- | --- | --- | | poolName | POOL\_NAME |

The name of the pool.

| -| pointsTestnetExperience | boolean |

If the user is in the testnet experience.

| | campaign | Campaign |

The campaign info.

| | handleClose | function |

Function to notify to close the widget modal when user clicks the 'x' close button.

| | handleSuccess | function |

Optional function to notify that the lending pool supply action is successful.

| diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx index 508b784a..805bbaba 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx @@ -3,6 +3,7 @@ import { checkIsDev, convertToShares, getTokenAccounts, + NETWORK_TYPE, SolanaPoolInfo, SolanaTokenUtils, TrancheType, @@ -32,14 +33,14 @@ type Props = { poolInfo: SolanaPoolInfo poolState: SolanaPoolState selectedTranche: TrancheType - pointsTestnetExperience: boolean + networkType: NETWORK_TYPE } export function Transfer({ poolInfo, poolState, selectedTranche, - pointsTestnetExperience, + networkType, }: Props): React.ReactElement | null { const isDev = checkIsDev() const dispatch = useAppDispatch() @@ -71,12 +72,12 @@ export function Transfer({ async (options?: { signature: string }) => { if (publicKey && poolState.campaign && options?.signature) { try { - const result = await CampaignService.updateWalletPoints( + const result = await CampaignService.updateAccountPoints( publicKey.toString(), options.signature, poolInfo.chainId, + networkType, isDev, - pointsTestnetExperience, ) dispatch(setPointsAccumulated(result.pointsAccumulated)) } catch (error) { @@ -88,7 +89,7 @@ export function Transfer({ [ dispatch, isDev, - pointsTestnetExperience, + networkType, poolInfo.chainId, poolState.campaign, publicKey, diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx index ca2e7fc7..d5f9d546 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx @@ -5,6 +5,7 @@ import { CloseModalOptions, formatNumber, isEmpty, + NETWORK_TYPE, } from '@huma-finance/shared' import { SolanaPoolState, @@ -35,14 +36,14 @@ const ERROR_MESSAGE = type Props = { transactionHash: string poolState: SolanaPoolState - pointsTestnetExperience: boolean + networkType: NETWORK_TYPE handleAction: (options?: CloseModalOptions) => void } export function PointsEarned({ transactionHash, poolState, - pointsTestnetExperience, + networkType, handleAction, }: Props): React.ReactElement { const theme = useTheme() @@ -87,8 +88,8 @@ export function PointsEarned({ if (account) { const ownership = await CampaignService.checkWalletOwnership( account, + networkType, isDev, - pointsTestnetExperience, ) setWalletOwnership(ownership) if (!ownership) { @@ -97,7 +98,7 @@ export function PointsEarned({ } } checkWalletOwnership() - }, [account, isDev, pointsTestnetExperience, setAuthError]) + }, [account, isDev, networkType, setAuthError]) useEffect(() => { if (errorType === 'NotSignedIn') { @@ -121,12 +122,12 @@ export function PointsEarned({ const updateWalletPoints = async () => { if (walletOwnership) { try { - const result = await CampaignService.updateWalletPoints( + const result = await CampaignService.updateAccountPoints( account!, transactionHash, chainId!, + networkType, isDev, - pointsTestnetExperience, ) setPointsAccumulated(result.pointsAccumulated) setState(STATE.Congrats) @@ -136,15 +137,7 @@ export function PointsEarned({ } } updateWalletPoints() - }, [ - account, - chainId, - dispatch, - isDev, - pointsTestnetExperience, - transactionHash, - walletOwnership, - ]) + }, [account, chainId, isDev, networkType, transactionHash, walletOwnership]) const handleCloseModal = useCallback(() => { reset() diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx index 7e2094b8..588b6b03 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/index.tsx @@ -36,14 +36,12 @@ export interface Campaign { * @typedef {Object} SolanaLendSupplyProps * @property {SolanaPoolInfo} poolInfo The metadata of the pool. * @property {SolanaPoolState} poolState The current state config of the pool. - * @property {boolean} pointsTestnetExperience If the user is in the testnet experience. * @property {function((CloseModalOptions|undefined)):void} handleClose Function to notify to close the widget modal when user clicks the 'x' close button. * @property {function():void|undefined} handleSuccess Optional function to notify that the lending pool supply action is successful. */ export interface SolanaLendSupplyProps { poolInfo: SolanaPoolInfo poolState: SolanaPoolState - pointsTestnetExperience: boolean handleClose: (options?: CloseModalOptions) => void handleSuccess?: () => void } @@ -51,7 +49,6 @@ export interface SolanaLendSupplyProps { export function SolanaLendSupply({ poolInfo, poolState, - pointsTestnetExperience, handleClose, handleSuccess, }: SolanaLendSupplyProps): React.ReactElement | null { @@ -153,7 +150,7 @@ export function SolanaLendSupply({ poolInfo={poolInfo} poolState={poolState} selectedTranche={selectedTranche} - pointsTestnetExperience={pointsTestnetExperience} + networkType={getSolanaNetworkType(poolInfo.chainId)} /> )} {step === WIDGET_STEP.Done && ( diff --git a/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx b/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx index 72264893..caaa260a 100644 --- a/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx +++ b/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx @@ -1,6 +1,7 @@ import { CampaignService, checkIsDev, + NETWORK_TYPE, PoolInfoV2, TrancheType, } from '@huma-finance/shared' @@ -19,14 +20,14 @@ import { TxSendModalV2 } from '../../TxSendModalV2' type Props = { poolInfo: PoolInfoV2 trancheType: TrancheType - pointsTestnetExperience: boolean + networkType: NETWORK_TYPE campaign?: Campaign } export function Transfer({ poolInfo, trancheType, - pointsTestnetExperience, + networkType, campaign, }: Props): React.ReactElement | null { const isDev = checkIsDev() @@ -49,12 +50,12 @@ export function Transfer({ async (options?: { txHash: string }) => { if (campaign && options?.txHash) { try { - const result = await CampaignService.updateWalletPoints( + const result = await CampaignService.updateAccountPoints( account!, options.txHash, chainId!, + networkType, isDev, - pointsTestnetExperience, ) dispatch(setPointsAccumulated(result.pointsAccumulated)) } catch (error) { @@ -63,7 +64,7 @@ export function Transfer({ } dispatch(setStep(WIDGET_STEP.Done)) }, - [account, campaign, chainId, dispatch, isDev, pointsTestnetExperience], + [account, campaign, chainId, dispatch, isDev, networkType], ) if (!trancheVaultContract || !account) { diff --git a/packages/huma-widget/src/components/Lend/supplyV2/index.tsx b/packages/huma-widget/src/components/Lend/supplyV2/index.tsx index 8cf2c082..11c6de80 100644 --- a/packages/huma-widget/src/components/Lend/supplyV2/index.tsx +++ b/packages/huma-widget/src/components/Lend/supplyV2/index.tsx @@ -39,14 +39,12 @@ export interface Campaign { * Lend pool supply props * @typedef {Object} LendSupplyPropsV2 * @property {POOL_NAME} poolName The name of the pool. - * @property {boolean} pointsTestnetExperience If the user is in the testnet experience. * @property {Campaign} campaign The campaign info. * @property {function((CloseModalOptions|undefined)):void} handleClose Function to notify to close the widget modal when user clicks the 'x' close button. * @property {function((number|undefined)):void|undefined} handleSuccess Optional function to notify that the lending pool supply action is successful. */ export interface LendSupplyPropsV2 { poolName: keyof typeof POOL_NAME - pointsTestnetExperience: boolean campaign?: Campaign handleClose: (options?: CloseModalOptions) => void handleSuccess?: (blockNumber?: number) => void @@ -54,7 +52,6 @@ export interface LendSupplyPropsV2 { export function LendSupplyV2({ poolName: poolNameStr, - pointsTestnetExperience, campaign, handleClose, handleSuccess, @@ -202,8 +199,8 @@ export function LendSupplyV2({ )} {step === WIDGET_STEP.Done && ( From aadec73ec071802e2768fc6740aeb8419f3b5689 Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:51:15 +0800 Subject: [PATCH 08/22] remove duplicate wallet auth and fix auth issue (#385) * remove duplicate wallet auth and fix auth issue * update WalletNotSignedInException --- .../src/hooks/useAuthErrorHandling.ts | 254 ------------------ 1 file changed, 254 deletions(-) delete mode 100644 packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts deleted file mode 100644 index 444fde50..00000000 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling.ts +++ /dev/null @@ -1,254 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { JsonRpcProvider } from '@ethersproject/providers' -import { - AuthService, - CHAIN_TYPE, - SiwsMessage, - SOLANA_CHAINS, - SolanaChainEnum, -} from '@huma-finance/shared' -import { useWallet } from '@solana/wallet-adapter-react' -import { useWeb3React } from '@web3-react/core' -import axios, { HttpStatusCode } from 'axios' -import bs58 from 'bs58' -import { useCallback, useEffect, useState } from 'react' -import { SiweMessage } from 'siwe' -import { useAsyncError } from './useAsyncError' - -type ErrorType = 'NotSignedIn' | 'UserRejected' | 'Other' - -const createSiweMessage = ( - address: string, - chainId: number, - nonce: string, - expiresAt: string, -) => { - const domain = window.location.hostname - const message = new SiweMessage({ - domain, - address, - statement: 'Please sign in to verify your ownership of this wallet', - uri: window.location.origin, - version: '1', - chainId, - nonce, - expirationTime: expiresAt, - }) - return message.prepareMessage() -} - -const createSiwsMessage = ( - address: string, - chainId: SolanaChainEnum, - nonce: string, - expiresAt: string, -) => { - const domain = window.location.hostname - const message = new SiwsMessage({ - domain, - address, - statement: 'Please sign in to verify your ownership of this wallet', - uri: window.location.origin, - version: '1', - chainId: SOLANA_CHAINS[chainId].name, - nonce, - expirationTime: expiresAt, - }) - return message.prepareMessage() -} - -const verifyEvmOwnership = async ( - address: string, - chainId: number, - isDev: boolean, - provider: JsonRpcProvider, - onVerificationComplete: () => void, -) => { - const { nonce, expiresAt } = await AuthService.createSession(chainId, isDev) - const message = createSiweMessage(address, chainId, nonce, expiresAt) - const signer = await provider.getSigner() - const signature = await signer.signMessage(message) - await AuthService.verifySignature(message, signature, chainId, isDev) - onVerificationComplete() -} - -const verifySolanaOwnership = async ( - address: string, - chainId: number, - isDev: boolean, - solanaSignMessage: (message: Uint8Array) => Promise, - onVerificationComplete: () => void, -) => { - try { - const { nonce, expiresAt } = await AuthService.createSession(chainId, isDev) - const message = createSiwsMessage(address, chainId, nonce, expiresAt) - const encodedMessage = new TextEncoder().encode(message) - const signedMessage = await solanaSignMessage(encodedMessage) - const signatureEncoded = bs58.encode(signedMessage as Uint8Array) - - await AuthService.verifySignature(message, signatureEncoded, chainId, isDev) - onVerificationComplete() - } catch (error) { - console.error(error) - } -} - -export type AuthState = { - isWalletOwnershipVerificationRequired: boolean - isWalletOwnershipVerified: boolean - errorType?: ErrorType - error: unknown - setError: React.Dispatch> - reset: () => void -} - -export const useAuthErrorHandling = ( - isDev: boolean, - chainType: CHAIN_TYPE = CHAIN_TYPE.EVM, - defaultChainId?: number, -): AuthState => { - const [error, setError] = useState(null) - const [isVerificationRequired, setIsVerificationRequired] = - useState(false) - const [isVerified, setIsVerified] = useState(false) - const throwError = useAsyncError() - const handleVerificationCompletion = () => { - setIsVerified(true) - } - const [errorType, setErrorType] = useState() - - const { - account: evmAccount, - chainId: evmChainId, - provider: evmProvider, - } = useWeb3React() - const { publicKey: solanaPublicKey, signMessage: solanaSignMessage } = - useWallet() - const solanaAccount = solanaPublicKey?.toString() ?? '' - - const getErrorInfo = useCallback((error: any) => { - const isUnauthorizedError = - axios.isAxiosError(error) && - error.response?.status === HttpStatusCode.Unauthorized && - [ - 'IdTokenNotFoundException', - 'InvalidIdTokenException', - 'WalletMismatchException', - 'AccountTokenNotFoundException', - 'InvalidAccountTokenException', - ].includes(error.response?.data?.detail?.type) - - const isWalletNotCreatedError = error === 'WalletNotCreatedException' - const isWalletNotSignInError = error === 'WalletNotSignInException' - - return { - isUnauthorizedError, - isWalletNotCreatedError, - isWalletNotSignInError, - } - }, []) - - useEffect(() => { - if (chainType === CHAIN_TYPE.EVM) { - if (!evmAccount || !evmChainId || !error || !evmProvider) { - return - } - - const { - isUnauthorizedError, - isWalletNotCreatedError, - isWalletNotSignInError, - } = getErrorInfo(error) - - if ( - isUnauthorizedError || - isWalletNotCreatedError || - isWalletNotSignInError - ) { - setErrorType('NotSignedIn') - setIsVerificationRequired(true) - if (chainType === CHAIN_TYPE.EVM) { - verifyEvmOwnership( - evmAccount!, - defaultChainId ?? evmChainId!, - isDev, - evmProvider!, - handleVerificationCompletion, - ).catch((e) => setError(e)) - } - } else if ([4001, 'ACTION_REJECTED'].includes((error as any).code)) { - setErrorType('UserRejected') - } else { - setErrorType('Other') - } - } - }, [ - evmChainId, - isDev, - error, - throwError, - evmAccount, - evmProvider, - getErrorInfo, - chainType, - defaultChainId, - ]) - - useEffect(() => { - if (chainType === CHAIN_TYPE.SOLANA) { - if (!solanaAccount || !error || !solanaSignMessage) { - return - } - - const { - isUnauthorizedError, - isWalletNotCreatedError, - isWalletNotSignInError, - } = getErrorInfo(error) - - if ( - isUnauthorizedError || - isWalletNotCreatedError || - isWalletNotSignInError - ) { - setErrorType('NotSignedIn') - setIsVerificationRequired(true) - verifySolanaOwnership( - solanaAccount, - isDev ? SolanaChainEnum.SolanaDevnet : SolanaChainEnum.SolanaMainnet, - isDev, - solanaSignMessage, - handleVerificationCompletion, - ).catch((e) => setError(e)) - } else if ([4001, 'ACTION_REJECTED'].includes((error as any).code)) { - setErrorType('UserRejected') - } else { - setErrorType('Other') - } - } - }, [ - isDev, - error, - throwError, - chainType, - solanaAccount, - getErrorInfo, - solanaSignMessage, - ]) - - const reset = useCallback(() => { - setIsVerificationRequired(false) - setIsVerified(false) - setError(null) - setErrorType(undefined) - }, []) - - return { - isWalletOwnershipVerificationRequired: isVerificationRequired, - isWalletOwnershipVerified: isVerified, - errorType, - error, - setError, - reset, - } -} From 031d55bbf4addd112dc242935bc15486ab4b63bb Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:57:46 +0800 Subject: [PATCH 09/22] rename accountPoints to humaAccountPoints (#383) Co-authored-by: shan --- .../src/services/CampaignService.ts | 45 ++++++++++++++----- .../Lend/solanaSupply/4-Transfer.tsx | 2 +- .../Lend/solanaSupply/6-PointsEarned.tsx | 2 +- .../components/Lend/supplyV2/5-Transfer.tsx | 2 +- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index 99b6a825..6d926003 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -68,8 +68,9 @@ export type LeaderboardItem = { referredCount: number } -export type AccountPoints = { +export type HumaAccountPoints = { accountId: string + totalPoints: number basePoints: number liquidityPoints: number liquidityPointsList: { @@ -115,7 +116,13 @@ function getLeaderboard( seasonId: string, networkType: NETWORK_TYPE, isDev: boolean, -): Promise<{ data: LeaderboardItem[] } | undefined> { +): Promise< + | { + leaderboardItems: LeaderboardItem[] + accountLeaderboard: LeaderboardItem | undefined + } + | undefined +> { const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) const query = gql` @@ -146,7 +153,18 @@ function getLeaderboard( console.error(res.errors) return undefined } - return res.data?.leaderboard + const leaderboardItems = res.data?.leaderboard?.data + let accountLeaderboard: LeaderboardItem | undefined + if (leaderboardItems) { + // This means that the first item is the user itself + if (leaderboardItems[1]?.rank === 1) { + accountLeaderboard = leaderboardItems.shift() + } + } + return { + leaderboardItems: leaderboardItems ?? [], + accountLeaderboard, + } }) .catch((err) => { console.error(err) @@ -154,10 +172,10 @@ function getLeaderboard( }) } -function getAccountPoints( +function getHumaAccountPoints( networkType: NETWORK_TYPE, isDev: boolean, -): Promise { +): Promise { const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) const query = gql` @@ -181,7 +199,7 @@ function getAccountPoints( ` return requestPost<{ - data?: { accountPoints: AccountPoints } + data?: { accountPoints: HumaAccountPoints } errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { @@ -189,7 +207,14 @@ function getAccountPoints( console.error(res.errors) return undefined } - return res.data?.accountPoints + const accountPoints = res.data?.accountPoints + if (accountPoints) { + accountPoints.totalPoints = + accountPoints.basePoints + + accountPoints.liquidityPoints + + accountPoints.referralPoints + } + return accountPoints }) .catch((err) => { console.error(err) @@ -239,7 +264,7 @@ function getEstimatedPoints( }) } -function updateAccountPoints( +function updateHumaAccountPoints( walletAddress: string, transactionHash: string, chainId: ChainEnum | SolanaChainEnum, @@ -294,6 +319,6 @@ export const CampaignService = { checkWalletOwnership, getEstimatedPoints, getLeaderboard, - getAccountPoints, - updateAccountPoints, + getHumaAccountPoints, + updateHumaAccountPoints, } diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx index 805bbaba..a9516993 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/4-Transfer.tsx @@ -72,7 +72,7 @@ export function Transfer({ async (options?: { signature: string }) => { if (publicKey && poolState.campaign && options?.signature) { try { - const result = await CampaignService.updateAccountPoints( + const result = await CampaignService.updateHumaAccountPoints( publicKey.toString(), options.signature, poolInfo.chainId, diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx index d5f9d546..f6c9e893 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/6-PointsEarned.tsx @@ -122,7 +122,7 @@ export function PointsEarned({ const updateWalletPoints = async () => { if (walletOwnership) { try { - const result = await CampaignService.updateAccountPoints( + const result = await CampaignService.updateHumaAccountPoints( account!, transactionHash, chainId!, diff --git a/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx b/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx index caaa260a..d7cefa75 100644 --- a/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx +++ b/packages/huma-widget/src/components/Lend/supplyV2/5-Transfer.tsx @@ -50,7 +50,7 @@ export function Transfer({ async (options?: { txHash: string }) => { if (campaign && options?.txHash) { try { - const result = await CampaignService.updateAccountPoints( + const result = await CampaignService.updateHumaAccountPoints( account!, options.txHash, chainId!, From 0f6b50b8f7040e21dfdda93ac220631d8b22ff1d Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:17:22 +0800 Subject: [PATCH 10/22] fix wallet sign in issue (#386) Co-authored-by: shan --- .../useAuthErrorHandling/useAuthErrorHandlingEvm.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingEvm.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingEvm.ts index 34fa082b..661f9888 100644 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingEvm.ts +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/useAuthErrorHandlingEvm.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { JsonRpcProvider } from '@ethersproject/providers' -import { AuthService, CHAIN_TYPE } from '@huma-finance/shared' +import { AuthService, CHAIN_TYPE, CHAINS } from '@huma-finance/shared' import { useWeb3React } from '@web3-react/core' import { useEffect } from 'react' import { SiweMessage } from 'siwe' @@ -68,6 +68,14 @@ export const useAuthErrorHandlingEvm = ( return } + const isChainSupported = Object.values(CHAINS).some( + (chain) => chain.id === chainId, + ) + + if (!isChainSupported) { + return + } + const { isUnauthorizedError, isWalletNotCreatedError, From d12da0efe66e7c329f4c9c95c516adc562034cbb Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:52:38 +0800 Subject: [PATCH 11/22] update campaign service to get huma account rank (#387) * update campaign service to get huma account rank * update function name --------- Co-authored-by: shan --- .../src/services/CampaignService.ts | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index 6d926003..a9e5ddd1 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -116,13 +116,7 @@ function getLeaderboard( seasonId: string, networkType: NETWORK_TYPE, isDev: boolean, -): Promise< - | { - leaderboardItems: LeaderboardItem[] - accountLeaderboard: LeaderboardItem | undefined - } - | undefined -> { +): Promise { const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) const query = gql` @@ -138,7 +132,7 @@ function getLeaderboard( } } ... on PointServiceError { - message + errMessage } } } @@ -153,18 +147,48 @@ function getLeaderboard( console.error(res.errors) return undefined } - const leaderboardItems = res.data?.leaderboard?.data - let accountLeaderboard: LeaderboardItem | undefined - if (leaderboardItems) { - // This means that the first item is the user itself - if (leaderboardItems[1]?.rank === 1) { - accountLeaderboard = leaderboardItems.shift() + return res.data?.leaderboard?.data + }) + .catch((err) => { + console.error(err) + return undefined + }) +} + +function getHumaAccountRanking( + seasonId: string, + networkType: NETWORK_TYPE, + isDev: boolean, +): Promise { + const url = configUtil.getCampaignAPIUrlV2(networkType, isDev) + + const query = gql` + query { + myRankingEntry(seasonId: "${seasonId}") { + ... on LeaderboardItem { + accountId + accountName + points + referredCount + rank + } + ... on PointServiceError { + errMessage } } - return { - leaderboardItems: leaderboardItems ?? [], - accountLeaderboard, + } + ` + + return requestPost<{ + data?: { myRankingEntry: LeaderboardItem } + errors?: unknown + }>(url, JSON.stringify({ query })) + .then((res) => { + if (res.errors) { + console.error(res.errors) + return undefined } + return res.data?.myRankingEntry }) .catch((err) => { console.error(err) @@ -192,7 +216,7 @@ function getHumaAccountPoints( referralPoints } ... on PointServiceError { - message + errMessage } } } @@ -286,7 +310,7 @@ function updateHumaAccountPoints( pointsAccumulated } ... on PointServiceError { - message + errMessage } } } @@ -319,6 +343,7 @@ export const CampaignService = { checkWalletOwnership, getEstimatedPoints, getLeaderboard, + getHumaAccountRanking, getHumaAccountPoints, updateHumaAccountPoints, } From 89391de635b2f592ce8906beeeb6123db8e58c96 Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:06:22 +0800 Subject: [PATCH 12/22] update campaign service (#389) Co-authored-by: shan --- packages/huma-shared/src/services/CampaignService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index a9e5ddd1..5672e67e 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -64,7 +64,7 @@ export type LeaderboardItem = { accountId: string accountName: string points: number - rank: number + ranking: number referredCount: number } @@ -128,7 +128,7 @@ function getLeaderboard( accountName points referredCount - rank + ranking } } ... on PointServiceError { @@ -170,7 +170,7 @@ function getHumaAccountRanking( accountName points referredCount - rank + ranking } ... on PointServiceError { errMessage From 8f82abb41a1a809e1506fed3752db010598cc24f Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:05:12 +0800 Subject: [PATCH 13/22] update get wallet address abbr (#388) Co-authored-by: shan --- packages/huma-shared/src/utils/chain.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/huma-shared/src/utils/chain.ts b/packages/huma-shared/src/utils/chain.ts index 84c5ae5e..c6904022 100644 --- a/packages/huma-shared/src/utils/chain.ts +++ b/packages/huma-shared/src/utils/chain.ts @@ -226,12 +226,12 @@ export const URLS: { [chainId: number]: string[] } = Object.keys( return accumulator }, {}) -export const getWalletAddressAbbr = (address: string) => { +export const getWalletAddressAbbr = (address: string, startNum = 6) => { if (!address) { return address } const { length } = address - return `${address.slice(0, 6)}...${address.slice(length - 4, length)}` + return `${address.slice(0, startNum)}...${address.slice(length - 4, length)}` } /** From 6f2ac5b62e1c475b2550f22580c946fa8f291d9c Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Sun, 8 Dec 2024 10:30:12 +0800 Subject: [PATCH 14/22] campaign service add bonus points (#390) Co-authored-by: shan --- packages/huma-shared/src/services/CampaignService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index 5672e67e..fc90a5bb 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -78,6 +78,7 @@ export type HumaAccountPoints = { points: number }[] referralPoints: number + bonusPoints: number } function checkWalletOwnership( @@ -214,6 +215,7 @@ function getHumaAccountPoints( points } referralPoints + bonusPoints } ... on PointServiceError { errMessage @@ -236,7 +238,8 @@ function getHumaAccountPoints( accountPoints.totalPoints = accountPoints.basePoints + accountPoints.liquidityPoints + - accountPoints.referralPoints + accountPoints.referralPoints + + accountPoints.bonusPoints } return accountPoints }) From 6dd3485899cef545663984b4348d2076532a8244 Mon Sep 17 00:00:00 2001 From: shan-57blocks <115970472+shan-57blocks@users.noreply.github.com> Date: Mon, 9 Dec 2024 07:41:28 +0800 Subject: [PATCH 15/22] fix account id not found issue (#393) * fix account id not found issue * remove accountId field from humaAccount * remove unnecessary change --- packages/huma-shared/src/services/IdentityServiceV2.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/huma-shared/src/services/IdentityServiceV2.ts b/packages/huma-shared/src/services/IdentityServiceV2.ts index fb64d5e5..9482d7b5 100644 --- a/packages/huma-shared/src/services/IdentityServiceV2.ts +++ b/packages/huma-shared/src/services/IdentityServiceV2.ts @@ -65,7 +65,6 @@ export type ResumeVerificationResultV2 = { * Object representing the Huma account. * @typedef {Object} HumaAccount * @property {string} id The account id. - * @property {string} accountId The account id. * @property {string} name The account name. * @property {Wallet[]} wallets The account wallets. * @property {string} referralCode The account referral code. @@ -75,7 +74,6 @@ export type ResumeVerificationResultV2 = { */ export type HumaAccount = { id: string - accountId: string name: string wallets: { address: string @@ -97,6 +95,7 @@ export type HumaAccount = { * @property {boolean} isNewAccount Is new account or not. */ export type HumaAccountLoginResult = HumaAccount & { + account: HumaAccount isNewAccount: boolean } @@ -267,7 +266,6 @@ const getHumaAccount = async ( const { account } = await requestGet<{ account: HumaAccount }>( `${configUtil.getIdentityAPIUrlV2(networkType, isDev)}/account`, ) - account.accountId = account.id return account } From 753ffebb3cc330c2f0ac321d2efbd78c39c655ed Mon Sep 17 00:00:00 2001 From: shan Date: Thu, 12 Dec 2024 17:34:57 +0800 Subject: [PATCH 16/22] add useChainsInfo hook --- .../huma-web-shared/src/hooks/useChainInfo.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/huma-web-shared/src/hooks/useChainInfo.ts b/packages/huma-web-shared/src/hooks/useChainInfo.ts index 5f1c84c9..2a2ccf2c 100644 --- a/packages/huma-web-shared/src/hooks/useChainInfo.ts +++ b/packages/huma-web-shared/src/hooks/useChainInfo.ts @@ -42,6 +42,24 @@ export const useChainInfo = ( } } +export const useChainsInfo = (isDev: boolean) => { + const { account: evmAccount, chainId: evmChainId } = useChainInfo( + isDev, + CHAIN_TYPE.EVM, + ) + const { account: solanaAccount, chainId: solanaChainId } = useChainInfo( + isDev, + CHAIN_TYPE.SOLANA, + ) + + return { + evmAccount, + evmChainId, + solanaAccount, + solanaChainId, + } +} + export const useActiveChainInfo = ( isDev: boolean, activeNetwork: CHAIN_TYPE, From 29e0452f02c73bf266c7f38e32276fe5f8dd9e83 Mon Sep 17 00:00:00 2001 From: shan-57blocks Date: Fri, 13 Dec 2024 15:13:43 +0800 Subject: [PATCH 17/22] campaign service add error handling --- .../src/services/CampaignService.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index fc90a5bb..688a2fd9 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -96,12 +96,12 @@ function checkWalletOwnership( return requestPost<{ data?: { - walletOwnership: boolean + walletOwnership: boolean & { errMessage: string } } errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors) { + if (res.errors || res.data?.walletOwnership?.errMessage) { console.error(res.errors) return undefined } @@ -140,11 +140,11 @@ function getLeaderboard( ` return requestPost<{ - data?: { leaderboard: { data: LeaderboardItem[] } } + data?: { leaderboard: { data: LeaderboardItem[] & { errMessage: string } } } errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors) { + if (res.errors || res.data?.leaderboard?.data?.errMessage) { console.error(res.errors) return undefined } @@ -181,11 +181,11 @@ function getHumaAccountRanking( ` return requestPost<{ - data?: { myRankingEntry: LeaderboardItem } + data?: { myRankingEntry: LeaderboardItem & { errMessage: string } } errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors) { + if (res.errors || res.data?.myRankingEntry?.errMessage) { console.error(res.errors) return undefined } @@ -225,11 +225,11 @@ function getHumaAccountPoints( ` return requestPost<{ - data?: { accountPoints: HumaAccountPoints } + data?: { accountPoints: HumaAccountPoints & { errMessage: string } } errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors) { + if (res.errors || res.data?.accountPoints?.errMessage) { console.error(res.errors) return undefined } @@ -274,12 +274,12 @@ function getEstimatedPoints( data?: { calculateEstimatedPoints?: { campaignPointsEstimations?: CampaignPoints[] - } + } & { errMessage: string } } errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors) { + if (res.errors || res.data?.calculateEstimatedPoints?.errMessage) { console.error(res.errors) return [] } @@ -321,12 +321,14 @@ function updateHumaAccountPoints( return requestPost<{ data?: { - updateAccountPoints?: { pointsAccumulated?: number } + updateAccountPoints?: { pointsAccumulated?: number } & { + errMessage: string + } } errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors) { + if (res.errors || res.data?.updateAccountPoints?.errMessage) { console.error(res.errors) return {} } From 9306114f763dfab53e49eddaffc9596fa2039d2f Mon Sep 17 00:00:00 2001 From: shan-57blocks Date: Fri, 13 Dec 2024 17:17:12 +0800 Subject: [PATCH 18/22] campaign service error handling --- .../src/services/CampaignService.ts | 81 +++++++++++++------ packages/huma-shared/src/utils/const.ts | 13 +++ .../src/hooks/useAuthErrorHandling/index.ts | 18 +++-- .../Lend/solanaSupply/8-PointsEarned.tsx | 21 +++-- 4 files changed, 94 insertions(+), 39 deletions(-) diff --git a/packages/huma-shared/src/services/CampaignService.ts b/packages/huma-shared/src/services/CampaignService.ts index 688a2fd9..609112e0 100644 --- a/packages/huma-shared/src/services/CampaignService.ts +++ b/packages/huma-shared/src/services/CampaignService.ts @@ -3,6 +3,7 @@ import { gql } from 'graphql-request' import { SolanaChainEnum, SolanaPoolInfo } from '../solana' import { ChainEnum, NETWORK_TYPE } from '../utils/chain' import { configUtil } from '../utils/config' +import { COMMON_ERROR_MESSAGE } from '../utils/const' import { requestPost } from '../utils/request' import { PoolInfoV2 } from '../v2/utils/pool' @@ -101,15 +102,20 @@ function checkWalletOwnership( errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors || res.data?.walletOwnership?.errMessage) { - console.error(res.errors) - return undefined + if (res.errors) { + console.log(res.errors) + throw new Error(COMMON_ERROR_MESSAGE) + } + const errMessage = res.data?.walletOwnership?.errMessage + if (errMessage) { + console.error(errMessage) + throw new Error(errMessage) } return res.data?.walletOwnership }) .catch((err) => { console.error(err) - return undefined + throw new Error(COMMON_ERROR_MESSAGE) }) } @@ -144,15 +150,20 @@ function getLeaderboard( errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors || res.data?.leaderboard?.data?.errMessage) { - console.error(res.errors) - return undefined + if (res.errors) { + console.log(res.errors) + throw new Error(COMMON_ERROR_MESSAGE) + } + const errMessage = res.data?.leaderboard?.data?.errMessage + if (errMessage) { + console.error(errMessage) + throw new Error(errMessage) } return res.data?.leaderboard?.data }) .catch((err) => { console.error(err) - return undefined + throw new Error(COMMON_ERROR_MESSAGE) }) } @@ -185,15 +196,21 @@ function getHumaAccountRanking( errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors || res.data?.myRankingEntry?.errMessage) { - console.error(res.errors) - return undefined + if (res.errors) { + console.log(res.errors) + throw new Error(COMMON_ERROR_MESSAGE) + } + const errMessage = res.data?.myRankingEntry?.errMessage + if (errMessage) { + console.error(errMessage) + throw new Error(errMessage) } + return res.data?.myRankingEntry }) .catch((err) => { console.error(err) - return undefined + throw new Error(COMMON_ERROR_MESSAGE) }) } @@ -229,10 +246,16 @@ function getHumaAccountPoints( errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors || res.data?.accountPoints?.errMessage) { - console.error(res.errors) - return undefined + if (res.errors) { + console.log(res.errors) + throw new Error(COMMON_ERROR_MESSAGE) } + const errMessage = res.data?.accountPoints?.errMessage + if (errMessage) { + console.error(errMessage) + throw new Error(errMessage) + } + const accountPoints = res.data?.accountPoints if (accountPoints) { accountPoints.totalPoints = @@ -245,7 +268,7 @@ function getHumaAccountPoints( }) .catch((err) => { console.error(err) - return undefined + throw new Error(COMMON_ERROR_MESSAGE) }) } @@ -279,15 +302,21 @@ function getEstimatedPoints( errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors || res.data?.calculateEstimatedPoints?.errMessage) { + if (res.errors) { console.error(res.errors) - return [] + throw new Error(COMMON_ERROR_MESSAGE) } + const errMessage = res.data?.calculateEstimatedPoints?.errMessage + if (errMessage) { + console.error(errMessage) + throw new Error(errMessage) + } + return res.data?.calculateEstimatedPoints?.campaignPointsEstimations ?? [] }) .catch((err) => { console.error(err) - return [] + throw new Error(COMMON_ERROR_MESSAGE) }) } @@ -328,15 +357,21 @@ function updateHumaAccountPoints( errors?: unknown }>(url, JSON.stringify({ query })) .then((res) => { - if (res.errors || res.data?.updateAccountPoints?.errMessage) { - console.error(res.errors) - return {} + if (res.errors) { + console.log(res.errors) + throw new Error(COMMON_ERROR_MESSAGE) + } + const errMessage = res.data?.updateAccountPoints?.errMessage + if (errMessage) { + console.error(errMessage) + throw new Error(errMessage) } + return res.data?.updateAccountPoints ?? {} }) .catch((err) => { console.error(err) - return {} + throw new Error(COMMON_ERROR_MESSAGE) }) } diff --git a/packages/huma-shared/src/utils/const.ts b/packages/huma-shared/src/utils/const.ts index fe5b20ba..4eb9d9fd 100644 --- a/packages/huma-shared/src/utils/const.ts +++ b/packages/huma-shared/src/utils/const.ts @@ -3,6 +3,9 @@ export const EARejectReason = 'Your wallet does not meet qualifications' export const EARejectMessage = 'Based on your wallet transaction history your application was not approved.' +export const COMMON_ERROR_MESSAGE = + 'Sorry, there was an error. Please try again.' + export enum CURRENCY_CODE { USD = 840, } @@ -10,3 +13,13 @@ export enum CURRENCY_CODE { export const CAMPAIGN_REFERENCE_CODE = 'CAMPAIGN_REFERENCE_CODE' export const BP_FACTOR_NUMBER = 10000 + +export enum HUMA_ACCOUNT_EXCEPTION { + IdTokenNotFoundException = 'IdTokenNotFoundException', + InvalidIdTokenException = 'InvalidIdTokenException', + WalletMismatchException = 'WalletMismatchException', + AccountTokenNotFoundException = 'AccountTokenNotFoundException', + InvalidAccountTokenException = 'InvalidAccountTokenException', + WalletNotSignedInException = 'WalletNotSignedInException', + WalletNotCreatedException = 'WalletNotCreatedException', +} diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts index efca4b54..68005fd6 100644 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { CHAIN_TYPE } from '@huma-finance/shared' +import { CHAIN_TYPE, HUMA_ACCOUNT_EXCEPTION } from '@huma-finance/shared' import axios, { HttpStatusCode } from 'axios' import { useCallback, useState } from 'react' import { useAuthErrorHandlingEvm } from './useAuthErrorHandlingEvm' @@ -35,15 +35,17 @@ export const useAuthErrorHandling = ( axios.isAxiosError(error) && error.response?.status === HttpStatusCode.Unauthorized && [ - 'IdTokenNotFoundException', - 'InvalidIdTokenException', - 'WalletMismatchException', - 'AccountTokenNotFoundException', - 'InvalidAccountTokenException', + HUMA_ACCOUNT_EXCEPTION.IdTokenNotFoundException, + HUMA_ACCOUNT_EXCEPTION.InvalidIdTokenException, + HUMA_ACCOUNT_EXCEPTION.WalletMismatchException, + HUMA_ACCOUNT_EXCEPTION.AccountTokenNotFoundException, + HUMA_ACCOUNT_EXCEPTION.InvalidAccountTokenException, ].includes(error.response?.data?.detail?.type) - const isWalletNotCreatedError = error === 'WalletNotCreatedException' - const isWalletNotSignInError = error === 'WalletNotSignedInException' + const isWalletNotCreatedError = + error === HUMA_ACCOUNT_EXCEPTION.WalletNotCreatedException + const isWalletNotSignInError = + error === HUMA_ACCOUNT_EXCEPTION.WalletNotSignedInException return { isUnauthorizedError, diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx index 4b4ccf15..c6737a88 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx @@ -4,6 +4,7 @@ import { checkIsDev, CloseModalOptions, formatNumber, + HUMA_ACCOUNT_EXCEPTION, isEmpty, NETWORK_TYPE, } from '@huma-finance/shared' @@ -86,14 +87,18 @@ export function PointsEarned({ useEffect(() => { const checkWalletOwnership = async () => { if (account) { - const ownership = await CampaignService.checkWalletOwnership( - account, - networkType, - isDev, - ) - setWalletOwnership(ownership) - if (!ownership) { - setAuthError('WalletNotSignedInException') + try { + const ownership = await CampaignService.checkWalletOwnership( + account, + networkType, + isDev, + ) + setWalletOwnership(ownership) + if (!ownership) { + setAuthError(HUMA_ACCOUNT_EXCEPTION.WalletNotSignedInException) + } + } catch (error) { + setAuthError(HUMA_ACCOUNT_EXCEPTION.WalletNotSignedInException) } } } From e934383297662b3366ad50d4fe29f88f657aca34 Mon Sep 17 00:00:00 2001 From: shan Date: Mon, 16 Dec 2024 14:01:17 +0800 Subject: [PATCH 19/22] remove unnecessary wallet check and exception code --- packages/huma-shared/src/utils/const.ts | 2 - .../src/hooks/useAuthErrorHandling/index.ts | 2 - .../Lend/solanaSupply/8-PointsEarned.tsx | 100 +++--------------- 3 files changed, 15 insertions(+), 89 deletions(-) diff --git a/packages/huma-shared/src/utils/const.ts b/packages/huma-shared/src/utils/const.ts index 4eb9d9fd..f143fa4a 100644 --- a/packages/huma-shared/src/utils/const.ts +++ b/packages/huma-shared/src/utils/const.ts @@ -15,8 +15,6 @@ export const CAMPAIGN_REFERENCE_CODE = 'CAMPAIGN_REFERENCE_CODE' export const BP_FACTOR_NUMBER = 10000 export enum HUMA_ACCOUNT_EXCEPTION { - IdTokenNotFoundException = 'IdTokenNotFoundException', - InvalidIdTokenException = 'InvalidIdTokenException', WalletMismatchException = 'WalletMismatchException', AccountTokenNotFoundException = 'AccountTokenNotFoundException', InvalidAccountTokenException = 'InvalidAccountTokenException', diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts index 68005fd6..88271a76 100644 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts @@ -35,8 +35,6 @@ export const useAuthErrorHandling = ( axios.isAxiosError(error) && error.response?.status === HttpStatusCode.Unauthorized && [ - HUMA_ACCOUNT_EXCEPTION.IdTokenNotFoundException, - HUMA_ACCOUNT_EXCEPTION.InvalidIdTokenException, HUMA_ACCOUNT_EXCEPTION.WalletMismatchException, HUMA_ACCOUNT_EXCEPTION.AccountTokenNotFoundException, HUMA_ACCOUNT_EXCEPTION.InvalidAccountTokenException, diff --git a/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx b/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx index c6737a88..4971959d 100644 --- a/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx +++ b/packages/huma-widget/src/components/Lend/solanaSupply/8-PointsEarned.tsx @@ -4,22 +4,16 @@ import { checkIsDev, CloseModalOptions, formatNumber, - HUMA_ACCOUNT_EXCEPTION, isEmpty, NETWORK_TYPE, } from '@huma-finance/shared' -import { - SolanaPoolState, - txAtom, - useAuthErrorHandling, - useChainInfo, -} from '@huma-finance/web-shared' +import { SolanaPoolState, txAtom, useChainInfo } from '@huma-finance/web-shared' import { Box, css, useTheme } from '@mui/material' import { useResetAtom } from 'jotai/utils' import React, { useCallback, useEffect, useState } from 'react' import { useDispatch } from 'react-redux' -import { resetState, setError } from '../../../store/widgets.reducers' +import { resetState } from '../../../store/widgets.reducers' import { BottomButton } from '../../BottomButton' import { CongratulationsIcon, HumaPointsIcon, RibbonIcon } from '../../icons' import { LoadingModal } from '../../LoadingModal' @@ -31,9 +25,6 @@ enum STATE { Congrats = 'Congrats', } -const ERROR_MESSAGE = - 'Failed to update wallet points. Be assured that your points will be added later.' - type Props = { transactionHash: string poolState: SolanaPoolState @@ -62,87 +53,26 @@ export function PointsEarned({ ) const monthText = lockupMonths > 1 ? `${lockupMonths} months` : `${lockupMonths} month` - - const { - errorType, - setError: setAuthError, - isWalletOwnershipVerified, - isWalletOwnershipVerificationRequired, - } = useAuthErrorHandling(isDev) - const [walletOwnership, setWalletOwnership] = useState() const [state, setState] = useState(STATE.Loading) - useEffect(() => { - if (isWalletOwnershipVerificationRequired) { - setState(STATE.Loading) - } - }, [isWalletOwnershipVerificationRequired]) - - useEffect(() => { - if (isWalletOwnershipVerified) { - setWalletOwnership(true) - } - }, [isWalletOwnershipVerified]) - - useEffect(() => { - const checkWalletOwnership = async () => { - if (account) { - try { - const ownership = await CampaignService.checkWalletOwnership( - account, - networkType, - isDev, - ) - setWalletOwnership(ownership) - if (!ownership) { - setAuthError(HUMA_ACCOUNT_EXCEPTION.WalletNotSignedInException) - } - } catch (error) { - setAuthError(HUMA_ACCOUNT_EXCEPTION.WalletNotSignedInException) - } - } - } - checkWalletOwnership() - }, [account, isDev, networkType, setAuthError]) - - useEffect(() => { - if (errorType === 'NotSignedIn') { - setState(STATE.SignIn) - } else if (errorType === 'UserRejected') { - dispatch( - setError({ - errorMessage: 'User has rejected the transaction.', - }), - ) - } else if (errorType === 'Other') { - dispatch( - setError({ - errorMessage: ERROR_MESSAGE, - }), - ) - } - }, [dispatch, errorType]) - useEffect(() => { const updateWalletPoints = async () => { - if (walletOwnership) { - try { - const result = await CampaignService.updateHumaAccountPoints( - account!, - transactionHash, - chainId!, - networkType, - isDev, - ) - setPointsAccumulated(result.pointsAccumulated) - setState(STATE.Congrats) - } catch (error) { - console.error('Failed to update wallet points', error) - } + try { + const result = await CampaignService.updateHumaAccountPoints( + account!, + transactionHash, + chainId!, + networkType, + isDev, + ) + setPointsAccumulated(result.pointsAccumulated) + setState(STATE.Congrats) + } catch (error) { + console.error('Failed to update wallet points', error) } } updateWalletPoints() - }, [account, chainId, isDev, networkType, transactionHash, walletOwnership]) + }, [account, chainId, isDev, networkType, transactionHash]) const handleCloseModal = useCallback(() => { reset() From 30f40f41b4c420bc6cff2eeebd4365d5a4a36f51 Mon Sep 17 00:00:00 2001 From: shan Date: Tue, 17 Dec 2024 15:46:35 +0800 Subject: [PATCH 20/22] remove WalletMismatchException --- packages/huma-shared/src/utils/const.ts | 1 - packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/huma-shared/src/utils/const.ts b/packages/huma-shared/src/utils/const.ts index f143fa4a..2e003b79 100644 --- a/packages/huma-shared/src/utils/const.ts +++ b/packages/huma-shared/src/utils/const.ts @@ -15,7 +15,6 @@ export const CAMPAIGN_REFERENCE_CODE = 'CAMPAIGN_REFERENCE_CODE' export const BP_FACTOR_NUMBER = 10000 export enum HUMA_ACCOUNT_EXCEPTION { - WalletMismatchException = 'WalletMismatchException', AccountTokenNotFoundException = 'AccountTokenNotFoundException', InvalidAccountTokenException = 'InvalidAccountTokenException', WalletNotSignedInException = 'WalletNotSignedInException', diff --git a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts index 88271a76..2f971b9e 100644 --- a/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts +++ b/packages/huma-web-shared/src/hooks/useAuthErrorHandling/index.ts @@ -35,7 +35,6 @@ export const useAuthErrorHandling = ( axios.isAxiosError(error) && error.response?.status === HttpStatusCode.Unauthorized && [ - HUMA_ACCOUNT_EXCEPTION.WalletMismatchException, HUMA_ACCOUNT_EXCEPTION.AccountTokenNotFoundException, HUMA_ACCOUNT_EXCEPTION.InvalidAccountTokenException, ].includes(error.response?.data?.detail?.type) From d74768a0c1259cd1071767fc2766c96ab0d8d327 Mon Sep 17 00:00:00 2001 From: shan Date: Fri, 20 Dec 2024 16:51:25 +0800 Subject: [PATCH 21/22] update useDebouncedValue hook --- packages/huma-web-shared/src/hooks/useDebouncedValue.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/huma-web-shared/src/hooks/useDebouncedValue.ts b/packages/huma-web-shared/src/hooks/useDebouncedValue.ts index 1ed1480c..dc64ceac 100644 --- a/packages/huma-web-shared/src/hooks/useDebouncedValue.ts +++ b/packages/huma-web-shared/src/hooks/useDebouncedValue.ts @@ -1,9 +1,9 @@ import { useEffect, useState } from 'react' -export const useDebouncedValue = ( - value: number | string = '', +export const useDebouncedValue = ( + value: T, delay = 500, -): string | number => { +): T => { const [debouncedValue, setDebouncedValue] = useState(value) useEffect(() => { From 54f443aede511cef16ae63cc52c7588324cc4c8c Mon Sep 17 00:00:00 2001 From: shan-57blocks Date: Fri, 3 Jan 2025 22:33:50 +0800 Subject: [PATCH 22/22] fix failed tests --- .../huma-shared/tests/utils/request.test.ts | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/packages/huma-shared/tests/utils/request.test.ts b/packages/huma-shared/tests/utils/request.test.ts index cfbf2cd9..d9b99ee4 100644 --- a/packages/huma-shared/tests/utils/request.test.ts +++ b/packages/huma-shared/tests/utils/request.test.ts @@ -13,15 +13,10 @@ describe('requestGet', () => { const result = await requestGet(url) expect(result).toEqual(responseData) - expect(axios.get).toHaveBeenCalledWith( - url, - {}, - { - headers: { 'Content-Type': 'application/json' }, - method: 'GET', - withCredentials: true, - }, - ) + expect(axios.get).toHaveBeenCalledWith(url, { + headers: { 'Content-Type': 'application/json' }, + withCredentials: true, + }) }) it('throws an error if the GET request fails', async () => { @@ -31,15 +26,10 @@ describe('requestGet', () => { const url = 'https://example.com/api' await expect(requestGet(url)).rejects.toThrow(errorMessage) - expect(axios.get).toHaveBeenCalledWith( - url, - {}, - { - headers: { 'Content-Type': 'application/json' }, - method: 'GET', - withCredentials: true, - }, - ) + expect(axios.get).toHaveBeenCalledWith(url, { + headers: { 'Content-Type': 'application/json' }, + withCredentials: true, + }) }) })