Skip to content

Commit

Permalink
feat: Block Sanctioned Addresses (#2044)
Browse files Browse the repository at this point in the history
Co-authored-by: Mimi Immutable <[email protected]>
  • Loading branch information
jwhardwick and mimi-imtbl authored Aug 1, 2024
1 parent fdaa3cc commit b639c09
Show file tree
Hide file tree
Showing 17 changed files with 208 additions and 99 deletions.
2 changes: 2 additions & 0 deletions packages/checkout/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ export {
PostMessageData,
} from './postMessageHandler';

export { isAddressSanctioned } from './sanctions';

export type { ErrorType } from './errors';

export { CheckoutErrorType } from './errors';
Expand Down
1 change: 1 addition & 0 deletions packages/checkout/sdk/src/sanctions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './sanctions';
23 changes: 23 additions & 0 deletions packages/checkout/sdk/src/sanctions/sanctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import axios from 'axios';
import { Environment } from '@imtbl/config';
import { CHECKOUT_CDN_BASE_URL } from '../env';

export const isAddressSanctioned = async (
address: string,
environment: Environment,
): Promise<boolean> => {
let isSanctioned = false;
try {
const response = await axios.get(
`${CHECKOUT_CDN_BASE_URL[environment]}/v1/address/check/${address}`,
);

if (response.data.identifications.length > 0) {
isSanctioned = true;
}
} catch (error) {
return false;
}

return isSanctioned;
};
Original file line number Diff line number Diff line change
Expand Up @@ -229,38 +229,38 @@ export function ConnectLoader({
return (
<>
{(connectionStatus === ConnectionStatus.LOADING) && (
<LoadingView loadingText="Loading" />
<LoadingView loadingText="Loading" />
)}
<ConnectLoaderContext.Provider value={connectLoaderReducerValues}>
{(connectionStatus === ConnectionStatus.NOT_CONNECTED_NO_PROVIDER
|| connectionStatus === ConnectionStatus.NOT_CONNECTED
|| connectionStatus === ConnectionStatus.CONNECTED_WRONG_NETWORK) && (
<ConnectWidget
config={widgetConfig}
targetChainId={targetChainId}
web3Provider={provider}
checkout={checkout}
deepLink={deepLink}
sendCloseEventOverride={closeEvent}
allowedChains={allowedChains}
/>
|| connectionStatus === ConnectionStatus.NOT_CONNECTED
|| connectionStatus === ConnectionStatus.CONNECTED_WRONG_NETWORK) && (
<ConnectWidget
config={widgetConfig}
targetChainId={targetChainId}
web3Provider={provider}
checkout={checkout}
deepLink={deepLink}
sendCloseEventOverride={closeEvent}
allowedChains={allowedChains}
/>
)}
{/* If the user has connected then render the widget */}
{connectionStatus === ConnectionStatus.CONNECTED_WITH_NETWORK && (children)}
</ConnectLoaderContext.Provider>
{connectionStatus === ConnectionStatus.ERROR && (
<ErrorView
onCloseClick={closeEvent}
onActionClick={() => {
connectLoaderDispatch({
payload: {
type: ConnectLoaderActions.UPDATE_CONNECTION_STATUS,
connectionStatus: ConnectionStatus.NOT_CONNECTED,
},
});
}}
actionText="Try Again"
/>
<ErrorView
onCloseClick={closeEvent}
onActionClick={() => {
connectLoaderDispatch({
payload: {
type: ConnectLoaderActions.UPDATE_CONNECTION_STATUS,
connectionStatus: ConnectionStatus.NOT_CONNECTED,
},
});
}}
actionText="Try Again"
/>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export interface SetProviderPayload {
// eslint-disable-next-line @typescript-eslint/naming-convention
export const ConnectLoaderContext = createContext<ConnectLoaderContextState>({
connectLoaderState: initialConnectLoaderState,
connectLoaderDispatch: () => {},
connectLoaderDispatch: () => { },
});

export type Reducer<S, A> = (prevState: S, action: A) => S;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export enum SharedViews {
}

export type SharedView =
LoadingView
| ErrorView
| ServiceUnavailableErrorView
| TopUpView;
LoadingView
| ErrorView
| ServiceUnavailableErrorView
| TopUpView;

interface LoadingView extends ViewType {
type: SharedViews.LOADING_VIEW
Expand All @@ -30,7 +30,7 @@ export interface ErrorView extends ViewType {
tryAgain?: () => Promise<any>
}

export interface ServiceUnavailableErrorView extends ViewType {
interface ServiceUnavailableErrorView extends ViewType {
type: SharedViews.SERVICE_UNAVAILABLE_ERROR_VIEW;
error: Error;
}
Expand Down Expand Up @@ -96,7 +96,7 @@ export interface GoBackToPayload {
// eslint-disable-next-line @typescript-eslint/naming-convention
export const ViewContext = createContext<ViewContextState>({
viewState: initialViewState,
viewDispatch: () => {},
viewDispatch: () => { },
});

ViewContext.displayName = 'ViewContext'; // help with debugging Context in browser
Expand All @@ -118,7 +118,7 @@ export const viewReducer: Reducer<ViewState, ViewAction> = (
const { history } = state;
if (
history.length === 0
|| history[history.length - 1].type !== view.type
|| history[history.length - 1].type !== view.type
) {
// currentViewData should only be set on the current view before updating
if (currentViewData) {
Expand Down
2 changes: 1 addition & 1 deletion packages/checkout/widgets-lib/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,6 @@ export const WITHDRAWAL_CLAIM_GAS_LIMIT = 91000;
*/
export const CHECKOUT_APP_URL = {
[ENV_DEVELOPMENT]: 'https://checkout.dev.immutable.com',
[Environment.SANDBOX]: 'https://checkout.sandbox.immutable.com',
[Environment.SANDBOX]: 'http://localhost:3001',
[Environment.PRODUCTION]: 'https://checkout.immutable.com',
};
6 changes: 4 additions & 2 deletions packages/checkout/widgets-lib/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@
},
"SERVICE_UNAVAILABLE_ERROR_VIEW": {
"heading": {
"swap": "Swapping is not available in your region"
"swap": "Swapping is not available in your region",
"generic": "This service is not available in your region"
},
"body": {
"swap": "Please refer to <quickswapLink>Quickswap’s</quickswapLink> website for further information."
"swap": "Please refer to <quickswapLink>Quickswap’s</quickswapLink> website for further information.",
"generic": "For further assistance visit the <immutableSupport>Immutable</immutableSupport> support page."
}
},
"LOADING_VIEW": {
Expand Down
6 changes: 4 additions & 2 deletions packages/checkout/widgets-lib/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@
},
"SERVICE_UNAVAILABLE_ERROR_VIEW": {
"heading": {
"swap": "お住まいの地域では交換が利用できません"
"swap": "お住まいの地域では交換が利用できません",
"generic": "このサービスはお住まいの地域では利用できません"
},
"body": {
"swap": "<quickswapLink>Quickswap</quickswapLink>のウェブサイトで詳細情報をご覧ください。"
"swap": "<quickswapLink>Quickswap</quickswapLink>のウェブサイトで詳細情報をご覧ください。",
"generic": "詳しいサポートについては、<immutableSupport>Immutable</immutableSupport>サポートページをご覧ください."
}
},
"LOADING_VIEW": {
Expand Down
6 changes: 4 additions & 2 deletions packages/checkout/widgets-lib/src/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@
},
"SERVICE_UNAVAILABLE_ERROR_VIEW": {
"heading": {
"swap": "귀하의 지역에서는 스왑이 가능하지 않습니다"
"swap": "귀하의 지역에서는 스왑이 가능하지 않습니다",
"generic": "이 서비스는 귀하의 지역에서 이용할 수 없습니다"
},
"body": {
"swap": "자세한 정보는 <quickswapLink>Quickswap</quickswapLink> 웹사이트를 참조하십시오."
"swap": "자세한 정보는 <quickswapLink>Quickswap</quickswapLink> 웹사이트를 참조하십시오.",
"generic": "추가 지원이 필요하시면 <immutableSupport>Immutable</immutableSupport> 지원 페이지를 방문하세요."
}
},
"LOADING_VIEW": {
Expand Down
6 changes: 4 additions & 2 deletions packages/checkout/widgets-lib/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@
},
"SERVICE_UNAVAILABLE_ERROR_VIEW": {
"heading": {
"swap": "您所在的地区不支持兑换"
"swap": "您所在的地区不支持兑换",
"generic": "该服务在您的地区不可用"
},
"body": {
"swap": "请参考<quickswapLink>Quickswap</quickswapLink>的网站了解更多信息。"
"swap": "请参考<quickswapLink>Quickswap</quickswapLink>的网站了解更多信息。",
"generic": "如需进一步帮助,请访问<immutableSupport>Immutable</immutableSupport>支持页面。"
}
},
"LOADING_VIEW": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export function ServiceUnavailableErrorView({
size="small"
rc={<a target="_blank" href="https://quickswap.exchange" rel="noreferrer" />}
/>,
immutableSupport: <Link
size="small"
rc={<a target="_blank" href="https://support.immutable.com/" rel="noreferrer" />}
/>,
}}
/>
</SimpleTextBody>
Expand All @@ -59,41 +63,41 @@ export function ServiceUnavailableErrorView({
>

{primaryActionText && onPrimaryButtonClick && (
<Box
sx={{
paddingX: 'base.spacing.x4',
paddingBottom: 'base.spacing.x2',
}}
>
<Button
sx={{ width: '100%' }}
testId="primary-action-button"
variant="primary"
size="large"
onClick={onPrimaryButtonClick}
<Box
sx={{
paddingX: 'base.spacing.x4',
paddingBottom: 'base.spacing.x2',
}}
>
{primaryActionText}
</Button>
</Box>
<Button
sx={{ width: '100%' }}
testId="primary-action-button"
variant="primary"
size="large"
onClick={onPrimaryButtonClick}
>
{primaryActionText}
</Button>
</Box>
)}

{secondaryActionText && onSecondaryButtonClick && (
<Box
sx={{
paddingX: 'base.spacing.x4',
paddingBottom: 'base.spacing.x2',
}}
>
<Button
sx={{ width: '100%' }}
testId="secondary-action-button"
variant="secondary"
size="large"
onClick={onSecondaryButtonClick}
<Box
sx={{
paddingX: 'base.spacing.x4',
paddingBottom: 'base.spacing.x2',
}}
>
{secondaryActionText}
</Button>
</Box>
<Button
sx={{ width: '100%' }}
testId="secondary-action-button"
variant="secondary"
size="large"
onClick={onSecondaryButtonClick}
>
{secondaryActionText}
</Button>
</Box>
)}
</Box>
</SimpleLayout>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export enum ServiceType {
SWAP = 'swap',
GENERIC = 'generic',
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import {
BridgeWidgetViews,
} from '../../context/view-context/BridgeViewContextTypes';
import { ClaimWithdrawal } from './views/ClaimWithdrawal';
import { ServiceType } from '../../views/error/serviceTypes';
import { ServiceUnavailableErrorView } from '../../views/error/ServiceUnavailableErrorView';

export type BridgeWidgetInputs = BridgeWidgetParams & {
config: StrongCheckoutWidgetsConfig,
Expand Down Expand Up @@ -337,6 +339,12 @@ export default function BridgeWidget({
testId="claim-withdrawal-fail-view"
/>
)}
{viewState.view.type === SharedViews.SERVICE_UNAVAILABLE_ERROR_VIEW && (
<ServiceUnavailableErrorView
service={ServiceType.GENERIC}
onCloseClick={() => sendBridgeWidgetCloseEvent(eventTarget)}
/>
)}
</CryptoFiatProvider>
</BridgeContext.Provider>
</ViewContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {
useMemo,
useState,
} from 'react';
import { ChainId, WalletProviderName, WalletProviderRdns } from '@imtbl/checkout-sdk';
import {
ChainId, isAddressSanctioned, WalletProviderName, WalletProviderRdns,
} from '@imtbl/checkout-sdk';
import { Web3Provider } from '@ethersproject/providers';
import { useTranslation } from 'react-i18next';
import { BridgeWidgetViews } from '../../../context/view-context/BridgeViewContextTypes';
Expand All @@ -24,7 +26,7 @@ import {
} from '../../../lib/provider';
import { getL1ChainId, getL2ChainId } from '../../../lib';
import { getChainNameById } from '../../../lib/chains';
import { ViewActions, ViewContext } from '../../../context/view-context/ViewContext';
import { SharedViews, ViewActions, ViewContext } from '../../../context/view-context/ViewContext';
import { abbreviateAddress } from '../../../lib/addressUtils';
import {
useAnalytics,
Expand Down Expand Up @@ -206,6 +208,21 @@ export function WalletAndNetworkSelector() {
}
const web3Provider = new Web3Provider(event.provider as any);
const connectedProvider = await connectToProvider(checkout, web3Provider, changeAccount);

// CM-793 Check for sanctioned address
if (await isAddressSanctioned(await connectedProvider.getSigner().getAddress(), checkout.config.environment)) {
viewDispatch({
payload: {
type: ViewActions.UPDATE_VIEW,
view: {
type: SharedViews.SERVICE_UNAVAILABLE_ERROR_VIEW,
error: new Error('Sanctioned address'),
},
},
});
return;
}

await handleFromWalletConnectionSuccess(connectedProvider);
},
[checkout],
Expand Down Expand Up @@ -322,6 +339,20 @@ export function WalletAndNetworkSelector() {
const web3Provider = new Web3Provider(event.provider as any);
const connectedProvider = await connectToProvider(checkout, web3Provider, false);

// CM-793 Check for sanctioned address
if (await isAddressSanctioned(await connectedProvider.getSigner().getAddress(), checkout.config.environment)) {
viewDispatch({
payload: {
type: ViewActions.UPDATE_VIEW,
view: {
type: SharedViews.SERVICE_UNAVAILABLE_ERROR_VIEW,
error: new Error('Sanctioned address'),
},
},
});
return;
}

if (isWalletConnectProvider(connectedProvider)) {
handleWalletConnectToWalletConnection(connectedProvider);
} else {
Expand Down
Loading

0 comments on commit b639c09

Please sign in to comment.