From 765020c320a8fb75c062c9a7286f7864b909e8d6 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:14:12 +0200 Subject: [PATCH 01/81] Fix: tx-details redesign fixes (#4059) * Fix: for approvals, move method call to advanced details * Fix: restore "native transfer"; wrap "Interacted with" (#4060) * Approval check * FieldsGrid wrap --- src/components/tx/DecodedTx/index.tsx | 1 + src/components/tx/FieldsGrid/index.tsx | 5 +++-- src/components/tx/SignOrExecuteForm/index.tsx | 7 +++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/tx/DecodedTx/index.tsx b/src/components/tx/DecodedTx/index.tsx index 3cf55b1043..d6c2d96429 100644 --- a/src/components/tx/DecodedTx/index.tsx +++ b/src/components/tx/DecodedTx/index.tsx @@ -87,6 +87,7 @@ const DecodedTx = ({ {isMethodCallInAdvanced && decodedData?.method} + {!showMethodCall && !decodedData?.method && tx?.data.value && 'native transfer'} diff --git a/src/components/tx/FieldsGrid/index.tsx b/src/components/tx/FieldsGrid/index.tsx index 2f724189f1..36c1e4f475 100644 --- a/src/components/tx/FieldsGrid/index.tsx +++ b/src/components/tx/FieldsGrid/index.tsx @@ -1,11 +1,12 @@ import { type ReactNode } from 'react' import { Grid, Typography } from '@mui/material' -const minWidth = { xl: '25%', lg: '2vw' } +const minWidth = { xl: '25%', lg: '100px' } +const wrap = { flexWrap: { xl: 'nowrap' } } const FieldsGrid = ({ title, children }: { title: string; children: ReactNode }) => { return ( - + {title} diff --git a/src/components/tx/SignOrExecuteForm/index.tsx b/src/components/tx/SignOrExecuteForm/index.tsx index fb829a1825..326da17b40 100644 --- a/src/components/tx/SignOrExecuteForm/index.tsx +++ b/src/components/tx/SignOrExecuteForm/index.tsx @@ -35,6 +35,7 @@ import { TwapFallbackHandlerWarning } from '@/features/swap/components/TwapFallb import useIsSafeOwner from '@/hooks/useIsSafeOwner' import useTxDetails from '@/hooks/useTxDetails' import TxData from '@/components/transactions/TxDetails/TxData' +import { useApprovalInfos } from '../ApprovalEditor/hooks/useApprovalInfos' export type SubmitCallback = (txId: string, isExecuted?: boolean) => void @@ -94,6 +95,8 @@ export const SignOrExecuteForm = ({ const isSwapOrder = isConfirmationViewOrder(decodedData) const [txDetails] = useTxDetails(props.txId) const showTxDetails = props.txId && txDetails && !isCustomTxInfo(txDetails.txInfo) + const [readableApprovals] = useApprovalInfos({ safeTransaction: safeTx }) + const isApproval = readableApprovals && readableApprovals.length > 0 const { safe } = useSafeInfo() const isSafeOwner = useIsSafeOwner() @@ -144,7 +147,7 @@ export const SignOrExecuteForm = ({ )} Error parsing data}> - + {isApproval && } {showTxDetails && } @@ -153,7 +156,7 @@ export const SignOrExecuteForm = ({ txId={props.txId} decodedData={decodedData} showMultisend={!props.isBatch} - showMethodCall={props.showMethodCall && !showTxDetails && !isSwapOrder} + showMethodCall={props.showMethodCall && !showTxDetails && !isSwapOrder && !isApproval} /> From 0b1e51c63ef101dba5ef5134bd54db041bf9b5de Mon Sep 17 00:00:00 2001 From: James Mealy Date: Wed, 14 Aug 2024 09:19:38 +0200 Subject: [PATCH 02/81] Feat: create a dashboard banner to enable user accounts with SIWE (#3997) * add create account function * feat: add enable sign in banner * feat: make banner dismissable * rename banner * update siging message * Use createAccount and getAccount from gateway-sdk * Add banner to storybook, remove from dashboard * fix: use logError * Update src/services/exceptions/ErrorCodes.ts Co-authored-by: katspaugh <381895+katspaugh@users.noreply.github.com> --------- Co-authored-by: katspaugh <381895+katspaugh@users.noreply.github.com> --- public/images/common/download-cloud.svg | 12 +++ .../EnableAccountBanner/index.stories.tsx | 37 ++++++++++ .../components/EnableAccountBanner/index.tsx | 73 +++++++++++++++++++ .../EnableAccountBanner/style.module.css | 11 +++ src/services/exceptions/ErrorCodes.ts | 2 + src/services/siwe/index.ts | 7 +- 6 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 public/images/common/download-cloud.svg create mode 100644 src/features/siweAccounts/components/EnableAccountBanner/index.stories.tsx create mode 100644 src/features/siweAccounts/components/EnableAccountBanner/index.tsx create mode 100644 src/features/siweAccounts/components/EnableAccountBanner/style.module.css diff --git a/public/images/common/download-cloud.svg b/public/images/common/download-cloud.svg new file mode 100644 index 0000000000..b6dd6403cc --- /dev/null +++ b/public/images/common/download-cloud.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/features/siweAccounts/components/EnableAccountBanner/index.stories.tsx b/src/features/siweAccounts/components/EnableAccountBanner/index.stories.tsx new file mode 100644 index 0000000000..223aae84c9 --- /dev/null +++ b/src/features/siweAccounts/components/EnableAccountBanner/index.stories.tsx @@ -0,0 +1,37 @@ +import type { Meta, StoryObj } from '@storybook/react' +import EnableAccountBanner from './index' +import { Paper } from '@mui/material' +import type { Eip1193Provider } from 'ethers' +import { BrowserProvider } from 'ethers' + +const mockBrowserProvider = new BrowserProvider({ + request: () => {}, +} as unknown as Eip1193Provider) + +const meta = { + component: EnableAccountBanner, + parameters: { + layout: 'centered', + }, + decorators: [ + (Story) => { + return ( + + + + ) + }, + ], + + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args +export const Default: Story = { + args: { + provider: mockBrowserProvider, + }, +} diff --git a/src/features/siweAccounts/components/EnableAccountBanner/index.tsx b/src/features/siweAccounts/components/EnableAccountBanner/index.tsx new file mode 100644 index 0000000000..126c3d750b --- /dev/null +++ b/src/features/siweAccounts/components/EnableAccountBanner/index.tsx @@ -0,0 +1,73 @@ +import { signInWithEthereum } from '@/services/siwe' +import { Alert, Box, Button, IconButton, Typography } from '@mui/material' +import useWallet from '@/hooks/wallets/useWallet' +import CloseIcon from '@mui/icons-material/Close' +import DownloadCloud from '@/public/images/common/download-cloud.svg' +import ChevronRightIcon from '@mui/icons-material/ChevronRight' + +import css from './style.module.css' +import { useState } from 'react' +import { createAccount, getAccount } from '@safe-global/safe-gateway-typescript-sdk' +import type { BrowserProvider } from 'ethers' +import { logError } from '@/services/exceptions' +import ErrorCodes from '@/services/exceptions/ErrorCodes' + +const SignInBanner = ({ provider }: { provider: BrowserProvider | undefined }) => { + const { address = '' } = useWallet() || {} + const [isDismissed, setIsDismissed] = useState(false) + + if (!provider || isDismissed) return null + + const signIn = async () => { + let account + try { + await signInWithEthereum(provider) + account = await getAccount(address) + } catch (error) { + logError(ErrorCodes._640, error) + } + if (!account) { + try { + account = await createAccount({ address: address as `0x${string}` }) + } catch (error) { + logError(ErrorCodes._641, error) + } + } + } + + return ( +
+ } + action={ + <> + + { + setIsDismissed(true) + }} + > + + + + } + > + + + Access your accounts on any device! Enable cloud storage to switch devices effortlessly and keep your + data secure. + + + +
+ ) +} + +export default SignInBanner diff --git a/src/features/siweAccounts/components/EnableAccountBanner/style.module.css b/src/features/siweAccounts/components/EnableAccountBanner/style.module.css new file mode 100644 index 0000000000..075c499000 --- /dev/null +++ b/src/features/siweAccounts/components/EnableAccountBanner/style.module.css @@ -0,0 +1,11 @@ +.container :global .MuiAlert-message { + padding: 4px; +} + +.container :global .MuiAlert-action { + padding-top: 2px; +} + +.container :global .MuiAlert-root { + border: 1px solid var(--color-secondary-light); +} diff --git a/src/services/exceptions/ErrorCodes.ts b/src/services/exceptions/ErrorCodes.ts index be96e60985..b2b380d7db 100644 --- a/src/services/exceptions/ErrorCodes.ts +++ b/src/services/exceptions/ErrorCodes.ts @@ -42,6 +42,8 @@ enum ErrorCodes { _631 = '631: Transaction failed to be relayed', _632 = '632: Error fetching relay task status', _633 = '633: Notification (un-)registration failed', + _640 = '640: User account not found', + _641 = '641: Error creating user account', _700 = '700: Failed to read from local/session storage', _701 = '701: Failed to write to local/session storage', diff --git a/src/services/siwe/index.ts b/src/services/siwe/index.ts index 682e79c1bc..cc051b6606 100644 --- a/src/services/siwe/index.ts +++ b/src/services/siwe/index.ts @@ -5,7 +5,7 @@ import type { BrowserProvider } from 'ethers' * Prompt the user to sign in with their wallet and set an access_token cookie * @param provider */ -async function signInWithEthereum(provider: BrowserProvider) { +export const signInWithEthereum = async (provider: BrowserProvider) => { const { nonce } = await getAuthNonce() const [network, signer] = await Promise.all([provider.getNetwork(), provider.getSigner()]) @@ -14,7 +14,8 @@ async function signInWithEthereum(provider: BrowserProvider) { domain: window.location.host, address: signer.address as `0x${string}`, // Results in special signing window in MetaMask - statement: 'Sign in with Ethereum to the app.', + statement: + 'By signing, you are agreeing to store this data on the Safe Cloud. This does not initiate a transaction or cost any fees.', uri: window.location.origin, version: '1', chainId: Number(network.chainId), @@ -37,5 +38,3 @@ Issued At: ${message.issuedAt.toISOString()}` return verifyAuth({ message: signableMessage, signature }) } - -export default signInWithEthereum From 32bccca7b80305b5e42d2f01b9ec38aff36daa4b Mon Sep 17 00:00:00 2001 From: Manuel Gellfart Date: Wed, 14 Aug 2024 10:30:08 +0200 Subject: [PATCH 03/81] fix: safe creation for chains without 1.4.1 deployed (#4067) --- src/components/new-safe/create/AdvancedCreateSafe.tsx | 7 ++++--- src/components/new-safe/create/index.tsx | 7 +++++-- src/components/new-safe/create/steps/StatusStep/index.tsx | 5 ++--- src/utils/chains.ts | 3 ++- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/components/new-safe/create/AdvancedCreateSafe.tsx b/src/components/new-safe/create/AdvancedCreateSafe.tsx index 631209df72..3c434e3bfd 100644 --- a/src/components/new-safe/create/AdvancedCreateSafe.tsx +++ b/src/components/new-safe/create/AdvancedCreateSafe.tsx @@ -14,14 +14,15 @@ import { CREATE_SAFE_CATEGORY } from '@/services/analytics' import type { CreateSafeInfoItem } from '@/components/new-safe/create/CreateSafeInfos' import CreateSafeInfos from '@/components/new-safe/create/CreateSafeInfos' import { useState } from 'react' -import { LATEST_SAFE_VERSION } from '@/config/constants' import { type NewSafeFormData } from '.' -import { type SafeVersion } from '@safe-global/safe-core-sdk-types' import AdvancedOptionsStep from './steps/AdvancedOptionsStep' +import { getLatestSafeVersion } from '@/utils/chains' +import { useCurrentChain } from '@/hooks/useChains' const AdvancedCreateSafe = () => { const router = useRouter() const wallet = useWallet() + const chain = useCurrentChain() const [safeName, setSafeName] = useState('') const [dynamicHint, setDynamicHint] = useState() @@ -86,7 +87,7 @@ const AdvancedCreateSafe = () => { owners: [], threshold: 1, saltNonce: 0, - safeVersion: LATEST_SAFE_VERSION as SafeVersion, + safeVersion: getLatestSafeVersion(chain), } const onClose = () => { diff --git a/src/components/new-safe/create/index.tsx b/src/components/new-safe/create/index.tsx index f82516b23d..7683691eb1 100644 --- a/src/components/new-safe/create/index.tsx +++ b/src/components/new-safe/create/index.tsx @@ -17,8 +17,10 @@ import type { CreateSafeInfoItem } from '@/components/new-safe/create/CreateSafe import CreateSafeInfos from '@/components/new-safe/create/CreateSafeInfos' import { type ReactElement, useMemo, useState } from 'react' import ExternalLink from '@/components/common/ExternalLink' -import { HelpCenterArticle, LATEST_SAFE_VERSION } from '@/config/constants' +import { HelpCenterArticle } from '@/config/constants' import { type SafeVersion } from '@safe-global/safe-core-sdk-types' +import { getLatestSafeVersion } from '@/utils/chains' +import { useCurrentChain } from '@/hooks/useChains' export type NewSafeFormData = { name: string @@ -99,6 +101,7 @@ const staticHints: Record< const CreateSafe = () => { const router = useRouter() const wallet = useWallet() + const chain = useCurrentChain() const [safeName, setSafeName] = useState('') const [dynamicHint, setDynamicHint] = useState() @@ -158,7 +161,7 @@ const CreateSafe = () => { owners: [], threshold: 1, saltNonce: Date.now(), - safeVersion: LATEST_SAFE_VERSION as SafeVersion, + safeVersion: getLatestSafeVersion(chain) as SafeVersion, } const onClose = () => { diff --git a/src/components/new-safe/create/steps/StatusStep/index.tsx b/src/components/new-safe/create/steps/StatusStep/index.tsx index 47ae72545d..d7c218136c 100644 --- a/src/components/new-safe/create/steps/StatusStep/index.tsx +++ b/src/components/new-safe/create/steps/StatusStep/index.tsx @@ -17,8 +17,7 @@ import { Alert, AlertTitle, Box, Button, Paper, Stack, SvgIcon, Typography } fro import Link from 'next/link' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' -import { LATEST_SAFE_VERSION } from '@/config/constants' -import { type SafeVersion } from '@safe-global/safe-core-sdk-types' +import { getLatestSafeVersion } from '@/utils/chains' const SPEED_UP_THRESHOLD_IN_SECONDS = 15 @@ -85,7 +84,7 @@ export const CreateSafeStatus = ({ threshold: pendingSafe.props.safeAccountConfig.threshold, saltNonce: Number(pendingSafe.props.safeDeploymentConfig?.saltNonce), safeAddress, - safeVersion: pendingSafe.props.safeDeploymentConfig?.safeVersion ?? (LATEST_SAFE_VERSION as SafeVersion), + safeVersion: pendingSafe.props.safeDeploymentConfig?.safeVersion ?? getLatestSafeVersion(chain), }) } diff --git a/src/utils/chains.ts b/src/utils/chains.ts index 40cce412cc..4d3940839e 100644 --- a/src/utils/chains.ts +++ b/src/utils/chains.ts @@ -4,6 +4,7 @@ import { getExplorerLink } from './gateway' import { type SafeVersion } from '@safe-global/safe-core-sdk-types' import { getSafeSingletonDeployment } from '@safe-global/safe-deployments' import semverSatisfies from 'semver/functions/satisfies' +import { LATEST_SAFE_VERSION } from '@/config/constants' /** This version is used if a network does not have the LATEST_SAFE_VERSION deployed yet */ const FALLBACK_SAFE_VERSION = '1.3.0' as const @@ -62,7 +63,7 @@ export const isRouteEnabled = (route: string, chain?: ChainInfo) => { } export const getLatestSafeVersion = (chain: ChainInfo | undefined): SafeVersion => { - const latestSafeVersion = chain && hasFeature(chain, FEATURES.SAFE_141) ? '1.4.1' : '1.3.0' + const latestSafeVersion = chain && hasFeature(chain, FEATURES.SAFE_141) ? LATEST_SAFE_VERSION : FALLBACK_SAFE_VERSION // Without version filter it will always return the LATEST_SAFE_VERSION constant to avoid automatically updating to the newest version if the deployments change const latestDeploymentVersion = (getSafeSingletonDeployment({ network: chain?.chainId, released: true })?.version ?? FALLBACK_SAFE_VERSION) as SafeVersion From 68890bbebbb9dc7f7e78c3552707601dcb20b978 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:40:33 +0200 Subject: [PATCH 04/81] Fix: native transfer only if value > 0 (#4070) * Fix: native transfer only if value > 0 * Hide Advanced details and Tx checks for Rejection txs --- .../tx-flow/flows/RejectTx/RejectTx.tsx | 2 +- src/components/tx/DecodedTx/index.tsx | 2 +- src/components/tx/SignOrExecuteForm/index.tsx | 32 ++++++++++--------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/components/tx-flow/flows/RejectTx/RejectTx.tsx b/src/components/tx-flow/flows/RejectTx/RejectTx.tsx index 654e461562..5e47cc5b50 100644 --- a/src/components/tx-flow/flows/RejectTx/RejectTx.tsx +++ b/src/components/tx-flow/flows/RejectTx/RejectTx.tsx @@ -19,7 +19,7 @@ const RejectTx = ({ txNonce }: RejectTxProps): ReactElement => { }, [txNonce, setNonce, setSafeTx, setSafeTxError]) return ( - + To reject the transaction, a separate rejection transaction will be created to replace the original one. diff --git a/src/components/tx/DecodedTx/index.tsx b/src/components/tx/DecodedTx/index.tsx index d6c2d96429..a68c7b615a 100644 --- a/src/components/tx/DecodedTx/index.tsx +++ b/src/components/tx/DecodedTx/index.tsx @@ -87,7 +87,7 @@ const DecodedTx = ({ {isMethodCallInAdvanced && decodedData?.method} - {!showMethodCall && !decodedData?.method && tx?.data.value && 'native transfer'} + {!showMethodCall && !decodedData?.method && Number(tx?.data.value) > 0 && 'native transfer'} diff --git a/src/components/tx/SignOrExecuteForm/index.tsx b/src/components/tx/SignOrExecuteForm/index.tsx index 326da17b40..e7326adcd5 100644 --- a/src/components/tx/SignOrExecuteForm/index.tsx +++ b/src/components/tx/SignOrExecuteForm/index.tsx @@ -146,24 +146,26 @@ export const SignOrExecuteForm = ({ )} - Error parsing data}> - {isApproval && } - - {showTxDetails && } - - - + {!props.isRejection && ( + Error parsing data}> + {isApproval && } + + {showTxDetails && } + + + + )} - {!isCounterfactualSafe && } + {!isCounterfactualSafe && !props.isRejection && } - {!isCounterfactualSafe && } + {!isCounterfactualSafe && !props.isRejection && } Date: Thu, 15 Aug 2024 08:56:33 +0200 Subject: [PATCH 05/81] 1.41.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c4f827b05a..eb7ab76006 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "safe-wallet-web", "homepage": "https://github.com/safe-global/safe-wallet-web", "license": "GPL-3.0", - "version": "1.40.1", + "version": "1.41.0", "type": "module", "scripts": { "dev": "next dev", From 1d7a1400ce9bbb5b14497038bb064a7c0b5c8dac Mon Sep 17 00:00:00 2001 From: Manuel Gellfart Date: Thu, 15 Aug 2024 10:44:03 +0200 Subject: [PATCH 06/81] fix: update latest Safe version when switching chains during Safe creation (#4071) --- .../create/steps/SetNameStep/index.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/new-safe/create/steps/SetNameStep/index.tsx b/src/components/new-safe/create/steps/SetNameStep/index.tsx index c71d1e7ca2..a831ac4d2a 100644 --- a/src/components/new-safe/create/steps/SetNameStep/index.tsx +++ b/src/components/new-safe/create/steps/SetNameStep/index.tsx @@ -17,13 +17,19 @@ import MUILink from '@mui/material/Link' import Link from 'next/link' import { useRouter } from 'next/router' import NoWalletConnectedWarning from '../../NoWalletConnectedWarning' +import { type SafeVersion } from '@safe-global/safe-core-sdk-types' +import { useCurrentChain } from '@/hooks/useChains' +import { useEffect } from 'react' +import { getLatestSafeVersion } from '@/utils/chains' type SetNameStepForm = { name: string + safeVersion: SafeVersion } enum SetNameStepFields { name = 'name', + safeVersion = 'safeVersion', } const SET_NAME_STEP_FORM_ID = 'create-safe-set-name-step-form' @@ -31,22 +37,22 @@ const SET_NAME_STEP_FORM_ID = 'create-safe-set-name-step-form' function SetNameStep({ data, onSubmit, - setStep, setSafeName, }: StepRenderProps & { setSafeName: (name: string) => void }) { const router = useRouter() const fallbackName = useMnemonicSafeName() const isWrongChain = useIsWrongChain() + const chain = useCurrentChain() + const formMethods = useForm({ mode: 'all', - defaultValues: { - [SetNameStepFields.name]: data.name, - }, + defaultValues: data, }) const { handleSubmit, + setValue, formState: { errors, isValid }, } = formMethods @@ -65,6 +71,11 @@ function SetNameStep({ router.push(AppRoutes.welcome.index) } + // whenever the chain switches we need to update the latest Safe version + useEffect(() => { + setValue(SetNameStepFields.safeVersion, getLatestSafeVersion(chain)) + }, [chain, setValue]) + const isDisabled = isWrongChain || !isValid return ( From 270051cf18acd470b7a0bcaae6589709a902032d Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Thu, 15 Aug 2024 11:10:54 +0200 Subject: [PATCH 07/81] Fix: ellipsis for Safe names (#4072) --- src/components/welcome/MyAccounts/AccountItem.tsx | 2 +- src/components/welcome/MyAccounts/styles.module.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/welcome/MyAccounts/AccountItem.tsx b/src/components/welcome/MyAccounts/AccountItem.tsx index bbdd383776..cd162f0d65 100644 --- a/src/components/welcome/MyAccounts/AccountItem.tsx +++ b/src/components/welcome/MyAccounts/AccountItem.tsx @@ -81,7 +81,7 @@ const AccountItem = ({ onLinkClick, safeItem, safeOverview }: AccountItemProps) {name && ( - + {name} )} diff --git a/src/components/welcome/MyAccounts/styles.module.css b/src/components/welcome/MyAccounts/styles.module.css index bce1359936..4b97a15341 100644 --- a/src/components/welcome/MyAccounts/styles.module.css +++ b/src/components/welcome/MyAccounts/styles.module.css @@ -79,6 +79,7 @@ } } +.safeName, .safeAddress { white-space: nowrap; overflow: hidden; From 3cef4b114436ff93b4d313b3dab8394d10dfbac7 Mon Sep 17 00:00:00 2001 From: katspaugh Date: Thu, 15 Aug 2024 11:11:25 +0200 Subject: [PATCH 08/81] 1.41.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb7ab76006..0fc8db4b07 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "safe-wallet-web", "homepage": "https://github.com/safe-global/safe-wallet-web", "license": "GPL-3.0", - "version": "1.41.0", + "version": "1.41.1", "type": "module", "scripts": { "dev": "next dev", From 45f140896aaac7860b8ba82f1de8f9766d702d65 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Fri, 16 Aug 2024 07:45:24 +0200 Subject: [PATCH 09/81] Refactor: plain img tag for safe app icons (#4028) * Refactor: crossOrigin=anonymous for safe app icons * Rm crossorigin * Restore isSafeSrc * Fallback for external hosts * Always add crossOrigin="anonymous" * Rm crossorigin --- .../safe-apps/SafeAppIconCard/index.test.ts | 14 ----- .../safe-apps/SafeAppIconCard/index.test.tsx | 18 +++++++ .../safe-apps/SafeAppIconCard/index.tsx | 53 +------------------ src/config/securityHeaders.ts | 6 +-- 4 files changed, 23 insertions(+), 68 deletions(-) delete mode 100644 src/components/safe-apps/SafeAppIconCard/index.test.ts create mode 100644 src/components/safe-apps/SafeAppIconCard/index.test.tsx diff --git a/src/components/safe-apps/SafeAppIconCard/index.test.ts b/src/components/safe-apps/SafeAppIconCard/index.test.ts deleted file mode 100644 index 8ac514a8b0..0000000000 --- a/src/components/safe-apps/SafeAppIconCard/index.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { _isSafeSrc } from '.' - -describe('SafeAppIconCard', () => { - it('should detect unsafe src', () => { - expect(_isSafeSrc('https://google.com/test.jpg')).toBe(false) - expect(_isSafeSrc('data:image/png;base64,')).toBe(false) - }) - - it('should detect safe src', () => { - expect(_isSafeSrc('https://safe-transaction-assets.safe.global/contracts/logos/0x34CfAC646f3.png')).toBe(true) - expect(_isSafeSrc('https://safe-transaction-assets.staging.5afe.dev/contracts/logos/0x34CfAC.png')).toBe(true) - expect(_isSafeSrc('/images/transactions/incoming.svg')).toBe(true) - }) -}) diff --git a/src/components/safe-apps/SafeAppIconCard/index.test.tsx b/src/components/safe-apps/SafeAppIconCard/index.test.tsx new file mode 100644 index 0000000000..5997c6f67c --- /dev/null +++ b/src/components/safe-apps/SafeAppIconCard/index.test.tsx @@ -0,0 +1,18 @@ +import { render } from '@/tests/test-utils' +import SafeAppIconCard from '.' + +describe('SafeAppIconCard', () => { + it('should render an icon', () => { + const src = 'https://safe-transaction-assets.safe.global/safe_apps/160/icon.png' + const { queryByAltText } = render( + , + ) + + const img = queryByAltText('test') + expect(img).toBeInTheDocument() + expect(img).toHaveAttribute('src', src) + expect(img).toHaveAttribute('height', '100') + expect(img).toHaveAttribute('width', '100') + expect(img).not.toHaveAttribute('crossorigin') + }) +}) diff --git a/src/components/safe-apps/SafeAppIconCard/index.tsx b/src/components/safe-apps/SafeAppIconCard/index.tsx index b0333aa342..32b398fff8 100644 --- a/src/components/safe-apps/SafeAppIconCard/index.tsx +++ b/src/components/safe-apps/SafeAppIconCard/index.tsx @@ -1,38 +1,7 @@ import ImageFallback from '@/components/common/ImageFallback' -import { type ReactElement, memo } from 'react' const APP_LOGO_FALLBACK_IMAGE = `/images/apps/app-placeholder.svg` -const getIframeContent = (url: string, width: number, height: number, fallback: string): string => { - return ` - - Safe App logo - - - ` -} - -export const _isSafeSrc = (src: string) => { - const allowedHosts = ['.safe.global', '.5afe.dev'] - const isRelative = src.startsWith('/') - - let hostname = '' - if (!isRelative) { - try { - hostname = new URL(src).hostname - } catch (e) { - return false - } - } - - return isRelative || allowedHosts.some((host) => hostname.endsWith(host)) -} - const SafeAppIconCard = ({ src, alt, @@ -45,24 +14,6 @@ const SafeAppIconCard = ({ width?: number height?: number fallback?: string -}): ReactElement => { - if (_isSafeSrc(src)) { - return - } - - return ( -