From 7af66efe677359bac37daf2af892baaa38714e21 Mon Sep 17 00:00:00 2001 From: Alejandro Loaiza Date: Wed, 13 Nov 2024 14:58:20 +1100 Subject: [PATCH] feat: Adding sanctions validation to Swap widget (#2383) --- .../view-context/SwapViewContextTypes.ts | 8 ++++- .../src/widgets/swap/SwapWidget.tsx | 34 +++++++++++++++++++ .../src/widgets/swap/components/SwapForm.tsx | 15 +++++++- .../src/widgets/swap/context/SwapContext.ts | 17 +++++++++- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/packages/checkout/widgets-lib/src/context/view-context/SwapViewContextTypes.ts b/packages/checkout/widgets-lib/src/context/view-context/SwapViewContextTypes.ts index 9171485d0f..31f0fb51ce 100644 --- a/packages/checkout/widgets-lib/src/context/view-context/SwapViewContextTypes.ts +++ b/packages/checkout/widgets-lib/src/context/view-context/SwapViewContextTypes.ts @@ -9,6 +9,7 @@ export enum SwapWidgetViews { FAIL = 'FAIL', PRICE_SURGE = 'PRICE_SURGE', APPROVE_ERC20 = 'APPROVE_ERC20_SWAP', + SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE', } export type SwapWidgetView = @@ -17,7 +18,8 @@ export type SwapWidgetView = | SwapSuccessView | PriceSurgeView | SwapFailView - | ApproveERC20View; + | ApproveERC20View + | ServiceUnavailableView; export interface SwapSuccessView extends ViewType { type: SwapWidgetViews.SUCCESS; @@ -50,6 +52,10 @@ interface ApproveERC20View extends ViewType { data: ApproveERC20SwapData } +interface ServiceUnavailableView extends ViewType { + type: SwapWidgetViews.SERVICE_UNAVAILABLE; +} + interface SwapInProgressView extends ViewType { type: SwapWidgetViews.IN_PROGRESS; data: { diff --git a/packages/checkout/widgets-lib/src/widgets/swap/SwapWidget.tsx b/packages/checkout/widgets-lib/src/widgets/swap/SwapWidget.tsx index aff9550ea7..62abd71262 100644 --- a/packages/checkout/widgets-lib/src/widgets/swap/SwapWidget.tsx +++ b/packages/checkout/widgets-lib/src/widgets/swap/SwapWidget.tsx @@ -9,6 +9,7 @@ import { import { TokenFilterTypes, IMTBLWidgetEvents, SwapWidgetParams, SwapDirection, + fetchRiskAssessment, } from '@imtbl/checkout-sdk'; import { useTranslation } from 'react-i18next'; import { SwapCoins } from './views/SwapCoins'; @@ -50,6 +51,7 @@ import { ConnectLoaderContext } from '../../context/connect-loader-context/Conne import { EventTargetContext } from '../../context/event-target-context/EventTargetContext'; import { getAllowedBalances } from '../../lib/balance'; import { UserJourney, useAnalytics } from '../../context/analytics-provider/SegmentAnalyticsProvider'; +import { ServiceUnavailableErrorView } from '../../views/error/ServiceUnavailableErrorView'; export type SwapWidgetInputs = SwapWidgetParams & { config: StrongCheckoutWidgetsConfig; @@ -186,6 +188,28 @@ export default function SwapWidget({ })(); }, [checkout, provider]); + useEffect(() => { + if (!checkout || swapState.riskAssessment) { + return; + } + + (async () => { + const address = await provider?.getSigner()?.getAddress(); + + if (!address) { + return; + } + + const assessment = await fetchRiskAssessment([address], checkout.config); + swapDispatch({ + payload: { + type: SwapActions.SET_RISK_ASSESSMENT, + riskAssessment: assessment, + }, + }); + })(); + }, [checkout, provider]); + useEffect(() => { swapDispatch({ payload: { @@ -341,6 +365,16 @@ export default function SwapWidget({ errorEventActionLoading={errorViewLoading} /> )} + {viewState.view.type === SwapWidgetViews.SERVICE_UNAVAILABLE && ( + sendSwapWidgetCloseEvent(eventTarget)} + onBackButtonClick={() => { + viewDispatch({ + payload: { type: ViewActions.UPDATE_VIEW, view: { type: SwapWidgetViews.SWAP } }, + }); + }} + /> + )} {viewState.view.type === SharedViews.TOP_UP_VIEW && ( { if (!quote) return; + if (riskAssessment && isAddressSanctioned(riskAssessment)) { + viewDispatch({ + payload: { + type: ViewActions.UPDATE_VIEW, + view: { + type: SwapWidgetViews.SERVICE_UNAVAILABLE, + }, + }, + }); + + return; + } const transaction = quote; const isValid = SwapFormValidator(); // Tracking swap from data here and is valid or not to understand behaviour diff --git a/packages/checkout/widgets-lib/src/widgets/swap/context/SwapContext.ts b/packages/checkout/widgets-lib/src/widgets/swap/context/SwapContext.ts index d7e98f8535..1c146c96a1 100644 --- a/packages/checkout/widgets-lib/src/widgets/swap/context/SwapContext.ts +++ b/packages/checkout/widgets-lib/src/widgets/swap/context/SwapContext.ts @@ -4,6 +4,7 @@ import { NetworkInfo, TokenInfo, SwapDirection, + AssessmentResult, } from '@imtbl/checkout-sdk'; import { Exchange } from '@imtbl/dex-sdk'; import { createContext } from 'react'; @@ -16,6 +17,7 @@ export interface SwapState { supportedTopUps: TopUpFeature | null; allowedTokens: TokenInfo[]; autoProceed: boolean; + riskAssessment: AssessmentResult | undefined; } export interface TopUpFeature { @@ -32,6 +34,7 @@ export const initialSwapState: SwapState = { supportedTopUps: null, allowedTokens: [], autoProceed: false, + riskAssessment: undefined, }; export interface SwapContextState { @@ -50,7 +53,8 @@ type ActionPayload = | SetSupportedTopUpPayload | SetTokenBalancesPayload | SetAllowedTokensPayload - | SetAutoProceedPayload; + | SetAutoProceedPayload + | SetRiskAssessmentPayload; export enum SwapActions { SET_EXCHANGE = 'SET_EXCHANGE', @@ -60,6 +64,7 @@ export enum SwapActions { SET_TOKEN_BALANCES = 'SET_TOKEN_BALANCES', SET_ALLOWED_TOKENS = 'SET_ALLOWED_TOKENS', SET_AUTO_PROCEED = 'SET_AUTO_PROCEED', + SET_RISK_ASSESSMENT = 'SET_RISK_ASSESSMENT', } export interface SetExchangePayload { @@ -98,6 +103,11 @@ export interface SetAutoProceedPayload { direction: SwapDirection; } +export interface SetRiskAssessmentPayload { + type: SwapActions.SET_RISK_ASSESSMENT; + riskAssessment: AssessmentResult; +} + // eslint-disable-next-line @typescript-eslint/naming-convention export const SwapContext = createContext({ swapState: initialSwapState, @@ -155,6 +165,11 @@ export const swapReducer: Reducer = ( autoProceed: action.payload.autoProceed, direction: action.payload.direction, }; + case SwapActions.SET_RISK_ASSESSMENT: + return { + ...state, + riskAssessment: action.payload.riskAssessment, + }; default: return state; }