Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feat: display contract names for WalletConnect txs #3760

Merged
merged 9 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/fixtures/txhistory_data_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"transactionHash": "0xa5dd...b064",
"safeTxHash": "0x4dd0...b2b8",
"nativeTransfer": {
"title": "native transfer",
"title": "Native transfer",
"description": "Interact with (and send < 0.00001 ETH to)"
}
},
Expand Down
19 changes: 14 additions & 5 deletions src/components/safe-messages/MsgDetails/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { useMemo, type ReactElement } from 'react'
import { Accordion, AccordionSummary, Typography, AccordionDetails, Box } from '@mui/material'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import CodeIcon from '@mui/icons-material/Code'
import classNames from 'classnames'
import { SafeMessageStatus } from '@safe-global/safe-gateway-typescript-sdk'
import { useMemo } from 'react'
import type { SafeMessage } from '@safe-global/safe-gateway-typescript-sdk'
import type { ReactElement } from 'react'
import { SafeMessageStatus, type SafeMessage } from '@safe-global/safe-gateway-typescript-sdk'

import { formatDateTime } from '@/utils/date'
import EthHashInfo from '@/components/common/EthHashInfo'
Expand All @@ -14,20 +12,22 @@ import { generateDataRowValue, TxDataRow } from '@/components/transactions/TxDet
import MsgSigners from '@/components/safe-messages/MsgSigners'
import useWallet from '@/hooks/wallets/useWallet'
import SignMsgButton from '@/components/safe-messages/SignMsgButton'
import { generateSafeMessageMessage } from '@/utils/safe-messages'
import { generateSafeMessageMessage, isEIP712TypedData } from '@/utils/safe-messages'

import txDetailsCss from '@/components/transactions/TxDetails/styles.module.css'
import singleTxDecodedCss from '@/components/transactions/TxDetails/TxData/DecodedData/SingleTxDecoded/styles.module.css'
import infoDetailsCss from '@/components/transactions/InfoDetails/styles.module.css'
import { DecodedMsg } from '../DecodedMsg'
import CopyButton from '@/components/common/CopyButton'
import NamedAddressInfo from '@/components/common/NamedAddressInfo'

const MsgDetails = ({ msg }: { msg: SafeMessage }): ReactElement => {
const wallet = useWallet()
const isConfirmed = msg.status === SafeMessageStatus.CONFIRMED
const safeMessage = useMemo(() => {
return generateSafeMessageMessage(msg.message)
}, [msg.message])
const verifyingContract = isEIP712TypedData(msg.message) ? msg.message.domain.verifyingContract : undefined

return (
<div className={txDetailsCss.container}>
Expand All @@ -44,6 +44,15 @@ const MsgDetails = ({ msg }: { msg: SafeMessage }): ReactElement => {
/>
</InfoDetails>
</div>

{verifyingContract && (
<div className={txDetailsCss.txData}>
<InfoDetails title="Verifying contract:">
<NamedAddressInfo address={verifyingContract} shortAddress={false} showCopyButton hasExplorer />
</InfoDetails>
</div>
)}

<div className={txDetailsCss.txData}>
<InfoDetails
title={
Expand Down
9 changes: 7 additions & 2 deletions src/components/safe-messages/MsgSummary/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import TxConfirmations from '@/components/transactions/TxConfirmations'

import css from '@/components/transactions/TxSummary/styles.module.css'
import useIsSafeMessagePending from '@/hooks/messages/useIsSafeMessagePending'
import { isEIP712TypedData } from '@/utils/safe-messages'

const getStatusColor = (value: SafeMessageStatus, palette: Palette): string => {
switch (value) {
Expand All @@ -28,14 +29,18 @@ const MsgSummary = ({ msg }: { msg: SafeMessage }): ReactElement => {
const txStatusLabel = useSafeMessageStatus(msg)
const isConfirmed = msg.status === SafeMessageStatus.CONFIRMED
const isPending = useIsSafeMessagePending(msg.messageHash)
let type = ''
if (isEIP712TypedData(msg.message)) {
type = (msg.message as unknown as { primaryType: string }).primaryType
}

return (
<Box className={css.gridContainer}>
<Box className={[css.gridContainer, css.message].join(' ')}>
<Box gridArea="type">
<MsgType msg={msg} />
</Box>

<Box gridArea="info">Off-chain signature</Box>
<Box gridArea="info">{type || 'Signature'}</Box>

<Box gridArea="date" className={css.date}>
<DateTime value={msg.modifiedTimestamp} />
Expand Down
42 changes: 31 additions & 11 deletions src/components/safe-messages/MsgType/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
import { Box } from '@mui/material'
import { Box, SvgIcon } from '@mui/material'
import type { SafeMessage } from '@safe-global/safe-gateway-typescript-sdk'

import RequiredIcon from '@/public/images/messages/required.svg'
import ImageFallback from '@/components/common/ImageFallback'

import txTypeCss from '@/components/transactions/TxType/styles.module.css'
import { isEIP712TypedData } from '@/utils/safe-messages'

const FALLBACK_LOGO_URI = '/images/transactions/custom.svg'
const MAX_TRIMMED_LENGTH = 20

const getMessageName = (msg: SafeMessage) => {
if (msg.name != null) return msg.name

if (isEIP712TypedData(msg.message)) {
return msg.message.domain?.name || ''
}

const firstLine = msg.message.split('\n')[0]
let trimmed = firstLine.slice(0, MAX_TRIMMED_LENGTH)
if (trimmed.length < firstLine.length) {
trimmed += '…'
}
return trimmed
}

const MsgType = ({ msg }: { msg: SafeMessage }) => {
return (
<Box className={txTypeCss.txType}>
<ImageFallback
src={msg.logoUri || FALLBACK_LOGO_URI}
fallbackSrc={FALLBACK_LOGO_URI}
alt="Message type"
width={16}
height={16}
/>
{msg.name}
{msg.logoUri ? (
<ImageFallback
src={msg.logoUri || FALLBACK_LOGO_URI}
fallbackSrc={FALLBACK_LOGO_URI}
alt="Message type"
width={16}
height={16}
/>
) : (
<SvgIcon component={RequiredIcon} viewBox="0 0 32 32" fontSize="small" />
)}
{getMessageName(msg)}
</Box>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('SingleTxDecoded', () => {
/>,
)

expect(result.queryByText('native transfer')).not.toBeNull()
expect(result.queryByText('Native transfer')).not.toBeNull()
})

it('should show unknown contract interactions', () => {
Expand All @@ -52,7 +52,7 @@ describe('SingleTxDecoded', () => {
/>,
)

expect(result.queryByText('Unknown contract interaction')).not.toBeNull()
expect(result.queryByText('Contract interaction')).not.toBeNull()
})

it('should show decoded data ', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const SingleTxDecoded = ({
}: SingleTxDecodedProps) => {
const chain = useCurrentChain()
const isNativeTransfer = tx.value !== '0' && (!tx.data || isEmptyHexData(tx.data))
const method = tx.dataDecoded?.method || (isNativeTransfer ? 'native transfer' : 'Unknown contract interaction')
const method = tx.dataDecoded?.method || (isNativeTransfer ? 'Native transfer' : 'Contract interaction')
const { decimals, symbol } = chain?.nativeCurrency || {}
const amount = tx.value ? formatVisualAmount(tx.value, decimals) : 0

Expand Down
7 changes: 6 additions & 1 deletion src/components/transactions/TxSummary/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
grid-template-columns:
var(--grid-nonce) var(--grid-type) var(--grid-info) var(--grid-date) var(--grid-confirmations)
var(--grid-status) var(--grid-actions);
grid-template-areas: 'nonce type info date confirmations status actions';
grid-template-areas: 'nonce type info date confirmations status actions';
}

.gridContainer > * {
Expand All @@ -35,6 +35,11 @@
grid-template-areas: 'type info date confirmations status actions';
}

.gridContainer.message {
grid-template-columns: var(--grid-type) var(--grid-info) var(--grid-date) var(--grid-status) var(--grid-confirmations);
grid-template-areas: 'type info date status confirmations';
}

.gridContainer.untrusted {
opacity: 0.4;
}
Expand Down
9 changes: 0 additions & 9 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,6 @@ export enum SafeAppsTag {
ONRAMP = 'onramp',
}

export const WC_APP_PROD = {
id: 111,
url: 'https://apps-portal.safe.global/wallet-connect',
}
export const WC_APP_DEV = {
id: 25,
url: 'https://safe-apps.dev.5afe.dev/wallet-connect',
}

// Help Center
export const HELP_CENTER_URL = 'https://help.safe.global'
export const HelpCenterArticle = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,10 @@ describe('WalletConnectProvider', () => {
1,
{ method: 'fake', params: [] },
{
id: 25,
id: -1,
name: 'name',
description: 'description',
url: 'https://safe-apps.dev.5afe.dev/wallet-connect',
url: 'https://apps-portal.safe.global/wallet-connect',
iconUrl: 'iconUrl',
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { formatJsonRpcError } from '@walletconnect/jsonrpc-utils'
import useSafeInfo from '@/hooks/useSafeInfo'
import useSafeWalletProvider from '@/services/safe-wallet-provider/useSafeWalletProvider'
import { asError } from '@/services/exceptions/utils'
import { IS_PRODUCTION, WC_APP_DEV, WC_APP_PROD } from '@/config/constants'
import { IS_PRODUCTION } from '@/config/constants'
import { getPeerName, stripEip155Prefix } from '@/features/walletconnect/services/utils'
import { trackRequest } from '@/features/walletconnect//services/tracking'
import { wcPopupStore } from '@/features/walletconnect/components'
Expand All @@ -23,8 +23,6 @@ export enum WCLoadingState {
DISCONNECT = 'Disconnect',
}

const WalletConnectSafeApp = IS_PRODUCTION ? WC_APP_PROD : WC_APP_DEV

const walletConnectSingleton = new WalletConnectWallet()

const getWrongChainError = (dappName: string): Error => {
Expand Down Expand Up @@ -90,9 +88,9 @@ export const WalletConnectProvider = ({ children }: { children: ReactNode }) =>

// Get response from Safe Wallet Provider
return safeWalletProvider.request(event.id, event.params.request, {
id: WalletConnectSafeApp.id,
url: WalletConnectSafeApp.url,
name: getPeerName(session.peer) || 'Unknown dApp',
id: -1,
url: session.peer.metadata.url,
name: getPeerName(session.peer) || 'WalletConnect',
katspaugh marked this conversation as resolved.
Show resolved Hide resolved
description: session.peer.metadata.description,
iconUrl: session.peer.metadata.icons[0],
})
Expand Down
9 changes: 0 additions & 9 deletions src/pages/apps/open.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import { AppRoutes } from '@/config/routes'
import { getOrigin } from '@/components/safe-apps/utils'
import { useHasFeature } from '@/hooks/useChains'
import { FEATURES } from '@/utils/chains'
import { openWalletConnect } from '@/features/walletconnect/components'
import { isWalletConnectSafeApp } from '@/utils/gateway'

const SafeApps: NextPage = () => {
const chainId = useChainId()
Expand All @@ -28,7 +26,6 @@ const SafeApps: NextPage = () => {
const safeAppData = allSafeApps.find((app) => app.url === appUrl)
const { safeApp, isLoading } = useSafeAppFromManifest(appUrl || '', chainId, safeAppData)
const isSafeAppsEnabled = useHasFeature(FEATURES.SAFE_APPS)
const isWalletConnectEnabled = useHasFeature(FEATURES.NATIVE_WALLETCONNECT)

const { addPermissions, getPermissions, getAllowedFeaturesList } = useBrowserPermissions()
const origin = getOrigin(appUrl)
Expand Down Expand Up @@ -58,12 +55,6 @@ const SafeApps: NextPage = () => {
// appUrl is required to be present
if (!isSafeAppsEnabled || !appUrl || !router.isReady) return null

if (isWalletConnectEnabled && isWalletConnectSafeApp(appUrl)) {
openWalletConnect()
goToList()
return null
}

if (isModalVisible) {
return (
<SafeAppsInfoModal
Expand Down
16 changes: 2 additions & 14 deletions src/services/analytics/__tests__/tx-tracking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,12 @@ describe('getTransactionTrackingType', () => {
expect(txType).toEqual(TX_TYPES.rejection)
})

it('should return walletconnect for walletconnect transactions', async () => {
it('should return walletconnect for transactions w/o safeAppInfo', async () => {
const txType = await getMockTxType({
txInfo: {
type: TransactionInfoType.CUSTOM,
},
safeAppInfo: {
url: 'https://safe-apps.dev.5afe.dev/wallet-connect',
},
safeAppInfo: null,
} as unknown)

expect(txType).toEqual(TX_TYPES.walletconnect)
Expand Down Expand Up @@ -175,14 +173,4 @@ describe('getTransactionTrackingType', () => {

expect(txType).toEqual(TX_TYPES.batch)
})

it('should return custom for unknown transactions', async () => {
const txType = await getMockTxType({
txInfo: {
type: TransactionInfoType.CUSTOM,
},
} as unknown)

expect(txType).toEqual(TX_TYPES.custom)
})
})
5 changes: 3 additions & 2 deletions src/services/analytics/tx-tracking.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { TX_TYPES } from '@/services/analytics/events/transactions'
import { getTxDetails } from '@/services/transactions'
import { isWalletConnectSafeApp } from '@/utils/gateway'
import { SettingsInfoType, type TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
import {
isERC721Transfer,
Expand Down Expand Up @@ -63,12 +62,14 @@ export const getTransactionTrackingType = async (chainId: string, txId: string):
}

if (details.safeAppInfo) {
return isWalletConnectSafeApp(details.safeAppInfo.url) ? TX_TYPES.walletconnect : details.safeAppInfo.url
return details.safeAppInfo.url
}

if (isMultiSendTxInfo(txInfo)) {
return TX_TYPES.batch
}

return TX_TYPES.walletconnect
katspaugh marked this conversation as resolved.
Show resolved Hide resolved
}

return TX_TYPES.custom
Expand Down
5 changes: 0 additions & 5 deletions src/utils/gateway.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { JsonRpcSigner } from 'ethers'
import { type ChainInfo, deleteTransaction } from '@safe-global/safe-gateway-typescript-sdk'
import { WC_APP_PROD, WC_APP_DEV } from '@/config/constants'
import { signTypedData } from './web3'

export const _replaceTemplate = (uri: string, data: Record<string, string>): string => {
Expand Down Expand Up @@ -30,10 +29,6 @@ export const getExplorerLink = (
return { href, title }
}

export const isWalletConnectSafeApp = (url: string): boolean => {
return url === WC_APP_PROD.url || url === WC_APP_DEV.url
}

const signTxServiceMessage = async (
chainId: string,
safeAddress: string,
Expand Down
Loading