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: Human-readable v2 #2465

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e5afdd3
Adjust history and queue layout
usame-algan Aug 29, 2023
04cb49b
fix: Show human descriptions for settings changes
usame-algan Aug 29, 2023
964cdd8
Merge remote-tracking branch 'origin/dev' into human-readable-history
usame-algan Aug 30, 2023
ca111c0
Merge remote-tracking branch 'origin/dev' into human-readable-history
usame-algan Sep 1, 2023
979c724
Use contract name instead of Safe App name when available
katspaugh Sep 4, 2023
8a6393d
fix: lint
usame-algan Sep 4, 2023
32aa756
fix: Add new hook useTransactionDescription to combine txType and txInfo
usame-algan Sep 6, 2023
7e29054
fix: Revert changes to useTransactionType, TxType and TxInfo
usame-algan Sep 7, 2023
5185baa
fix: Upgrade gateway-typescript-sdk and remove redundant types
usame-algan Sep 7, 2023
1414f65
Merge remote-tracking branch 'origin/dev' into human-readable-history
usame-algan Sep 7, 2023
66edeb7
fix: Replace TxType and TxInfo for dashboard txs and confirm tx flow
usame-algan Sep 7, 2023
a80dd13
fix: Add TxDescription to Nonce replacement form
usame-algan Sep 7, 2023
496f47a
chore: Add global fetch to jest config
usame-algan Sep 7, 2023
b82d3e1
fix: Adjust design, refactor fallback components
usame-algan Sep 8, 2023
9e60a65
fix: Add simple transfer description
usame-algan Sep 8, 2023
387a4a8
fix: Remove unused components
usame-algan Sep 8, 2023
8b0f042
fix: Failing tests
usame-algan Sep 8, 2023
3f03923
fix: Mobile view, tx-history e2e test
usame-algan Sep 8, 2023
30e2926
fix: Small visual adjustments
usame-algan Sep 14, 2023
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
4 changes: 2 additions & 2 deletions cypress/e2e/smoke/batch_tx.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ describe('Create batch transaction', () => {
cy.contains('Confirm batch').click()
cy.contains(`This batch contains ${transactionsInBatchList} transactions`).should('be.visible')
cy.contains(funds_first_tx).parents('ul').as('TransactionList')
cy.get('@TransactionList').find('li').eq(0).find('span').eq(0).contains(funds_first_tx)
cy.get('@TransactionList').find('li').eq(1).find('span').eq(0).contains(funds_second_tx)
cy.get('@TransactionList').find('li').eq(0).find('strong').eq(0).contains(funds_first_tx)
cy.get('@TransactionList').find('li').eq(1).find('strong').eq(0).contains(funds_second_tx)
})

it('Should remove a transaction from the batch', () => {
Expand Down
5 changes: 4 additions & 1 deletion cypress/e2e/smoke/dashboard.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ describe('Dashboard', () => {
cy.contains('This Safe has no queued transactions').should('not.exist')

// Queued txns
cy.contains(`a[href^="/transactions/tx?id=multisig_0x"]`, '13' + 'Send' + '-0.00002 GOR' + '1/1').should('exist')
cy.contains(
`a[href^="/transactions/tx?id=multisig_0x"]`,
'13' + 'Send' + '0.00002 GOR' + 'to' + 'gor:0xE297...9665' + '1/1',
).should('exist')

cy.contains(`a[href="/transactions/queue?safe=${encodeURIComponent(SAFE)}"]`, 'View all')
})
Expand Down
24 changes: 13 additions & 11 deletions cypress/e2e/smoke/tx_history.cy.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const SAFE = 'gor:0x97d314157727D517A706B5D08507A1f9B44AaaE9'

const INCOMING = 'Received'
const OUTGOING = 'Sent'
const INCOMING = 'Receive'
const OUTGOING = 'Send'
const CONTRACT_INTERACTION = 'Contract interaction'

describe('Transaction history', () => {
Expand Down Expand Up @@ -32,11 +32,11 @@ describe('Transaction history', () => {
.within(() => {
// Type
cy.get('img').should('have.attr', 'alt', INCOMING)
cy.contains('div', 'Received').should('exist')
cy.contains('div', INCOMING).should('exist')

// Info
cy.get('img[alt="GOR"]').should('be.visible')
cy.contains('span', '0.25 GOR').should('exist')
cy.contains('0.25 GOR').should('exist')

// Time
cy.contains('span', '4:56 PM').should('exist')
Expand Down Expand Up @@ -73,10 +73,12 @@ describe('Transaction history', () => {
// Type
// TODO: update next line after fixing the logo
// cy.find('img').should('have.attr', 'src').should('include', WRAPPED_ETH)
cy.contains('div', 'Wrapped Ether').should('exist')
cy.contains('div', 'WETH').should('exist')

cy.contains('div', 'unlimited').should('exist')

// Info
cy.contains('div', 'approve').should('exist')
cy.contains('div', 'Approve').should('exist')

// Time
cy.contains('span', '5:00 PM').should('exist')
Expand All @@ -91,7 +93,7 @@ describe('Transaction history', () => {
cy.contains('2')

// Type
cy.contains('div', 'Contract interaction').should('exist')
cy.contains('div', 'setPreSignature').should('exist')

// Time
cy.contains('span', '5:01 PM').should('exist')
Expand All @@ -104,10 +106,10 @@ describe('Transaction history', () => {
.within(() => {
// Type
cy.get('img').should('have.attr', 'alt', OUTGOING)
cy.contains('div', 'Sent').should('exist')
cy.contains('div', 'Send').should('exist')

// Info
cy.contains('span', '-0.11 WETH').should('exist')
cy.contains('0.11 WETH').should('exist')

// Time
cy.contains('span', '5:01 PM').should('exist')
Expand All @@ -119,10 +121,10 @@ describe('Transaction history', () => {
.prev()
.within(() => {
// Type
cy.contains('div', 'Received').should('exist')
cy.contains('div', 'Receive').should('exist')

// Info
cy.contains('span', '120,497.61 DAI').should('exist')
cy.contains('120,497.61 DAI').should('exist')

// Time
cy.contains('span', '5:01 PM').should('exist')
Expand Down
3 changes: 3 additions & 0 deletions jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const customJestConfig = {
},
testEnvironment: 'jest-environment-jsdom',
testEnvironmentOptions: { url: 'http://localhost/balances?safe=rin:0xb3b83bf204C458B461de9B0CD2739DB152b4fa5A' },
globals: {
fetch: global.fetch
}
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"static-serve": "yarn build && yarn serve"
},
"engines": {
"node": ">=16"
"node": ">=18"
},
"pre-commit": [
"lint"
Expand All @@ -52,7 +52,7 @@
"@safe-global/safe-core-sdk-utils": "^1.7.4",
"@safe-global/safe-deployments": "1.25.0",
"@safe-global/safe-ethers-lib": "^1.9.4",
"@safe-global/safe-gateway-typescript-sdk": "^3.9.0",
"@safe-global/safe-gateway-typescript-sdk": "^3.12.0",
"@safe-global/safe-modules-deployments": "^1.0.0",
"@safe-global/safe-react-components": "^2.0.6",
"@sentry/react": "^7.28.1",
Expand Down
7 changes: 2 additions & 5 deletions src/components/batch/BatchSidebar/BatchTxItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { Accordion, AccordionDetails, AccordionSummary, Box, ButtonBase, ListIte
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import css from './styles.module.css'
import { type DraftBatchItem } from '@/store/batchSlice'
import TxType from '@/components/transactions/TxType'
import TxInfo from '@/components/transactions/TxInfo'
import DeleteIcon from '@/public/images/common/delete.svg'
import DragIcon from '@/public/images/common/drag.svg'
import TxData from '@/components/transactions/TxDetails/TxData'
import { MethodDetails } from '@/components/transactions/TxDetails/TxData/DecodedData/MethodDetails'
import { TxDataRow } from '@/components/transactions/TxDetails/Summary/TxDataRow'
import { dateString } from '@/utils/formatters'
import { BATCH_EVENTS, trackEvent } from '@/services/analytics'
import { TxDescription } from '@/components/transactions/TxSummary'

type BatchTxItemProps = DraftBatchItem & {
id: string
Expand Down Expand Up @@ -73,10 +72,8 @@ const BatchTxItem = ({
/>
)}

<TxType tx={txSummary} />

<Box flex={1}>
<TxInfo info={txDetails.txInfo} />
<TxDescription tx={txSummary} />
</Box>

{onDelete && (
Expand Down
9 changes: 6 additions & 3 deletions src/components/common/TokenAmount/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import css from './styles.module.css'
import { formatVisualAmount } from '@/utils/formatters'
import TokenIcon from '../TokenIcon'
import classNames from 'classnames'
import hdCss from '@/components/transactions/HumanDescription/styles.module.css'

const TokenAmount = ({
value,
Expand All @@ -12,23 +13,25 @@ const TokenAmount = ({
tokenSymbol,
direction,
fallbackSrc,
size,
}: {
value: string
decimals?: number
logoUri?: string
tokenSymbol?: string
direction?: TransferDirection
fallbackSrc?: string
size?: number
}): ReactElement => {
const sign = direction === TransferDirection.OUTGOING ? '-' : ''
const amount = decimals ? formatVisualAmount(value, decimals) : value

return (
<span className={classNames(css.container, { [css.verticalAlign]: logoUri })}>
{logoUri && <TokenIcon logoUri={logoUri} tokenSymbol={tokenSymbol} fallbackSrc={fallbackSrc} />}
<strong className={classNames(css.container, { [css.verticalAlign]: logoUri, [hdCss.method]: logoUri })}>
{logoUri && <TokenIcon logoUri={logoUri} tokenSymbol={tokenSymbol} fallbackSrc={fallbackSrc} size={size} />}
{sign}
{amount} {tokenSymbol}
</span>
</strong>
)
}

Expand Down
9 changes: 2 additions & 7 deletions src/components/dashboard/PendingTxs/PendingTxListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import ChevronRight from '@mui/icons-material/ChevronRight'
import type { TransactionSummary } from '@safe-global/safe-gateway-typescript-sdk'
import { Box, SvgIcon, Typography } from '@mui/material'
import { isMultisigExecutionInfo } from '@/utils/transaction-guards'
import TxInfo from '@/components/transactions/TxInfo'
import TxType from '@/components/transactions/TxType'
import css from './styles.module.css'
import OwnersIcon from '@/public/images/common/owners.svg'
import { AppRoutes } from '@/config/routes'
import { TxDescription } from '@/components/transactions/TxSummary'

type PendingTxType = {
transaction: TransactionSummary
Expand All @@ -37,11 +36,7 @@ const PendingTx = ({ transaction }: PendingTxType): ReactElement => {
{isMultisigExecutionInfo(transaction.executionInfo) && transaction.executionInfo.nonce}

<Box flex={1}>
<TxType tx={transaction} />
</Box>

<Box flex={1} className={css.txInfo}>
<TxInfo info={transaction.txInfo} />
<TxDescription tx={transaction} />
</Box>

{isMultisigExecutionInfo(transaction.executionInfo) ? (
Expand Down
4 changes: 2 additions & 2 deletions src/components/safe-messages/MsgType/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import type { SafeMessage } from '@safe-global/safe-gateway-typescript-sdk'

import ImageFallback from '@/components/common/ImageFallback'

import txTypeCss from '@/components/transactions/TxType/styles.module.css'
import css from './styles.module.css'

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

const MsgType = ({ msg }: { msg: SafeMessage }) => {
return (
<Box className={txTypeCss.txType}>
<Box className={css.type}>
<ImageFallback
src={msg.logoUri || FALLBACK_LOGO_URI}
fallbackSrc={FALLBACK_LOGO_URI}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.txType {
.type {
display: flex;
align-items: center;
gap: var(--space-1);
Expand Down
50 changes: 50 additions & 0 deletions src/components/transactions/HumanDescription/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
type RichAddressFragment,
type RichDecodedInfo,
type RichTokenValueFragment,
RichFragmentType,
} from '@safe-global/safe-gateway-typescript-sdk/dist/types/human-description'
import EthHashInfo from '@/components/common/EthHashInfo'
import css from './styles.module.css'
import useAddressBook from '@/hooks/useAddressBook'
import TokenAmount from '@/components/common/TokenAmount'
import React from 'react'

const AddressFragment = ({ fragment }: { fragment: RichAddressFragment }) => {
const addressBook = useAddressBook()

return (
<div className={css.address}>
<EthHashInfo address={fragment.value} name={addressBook[fragment.value]} avatarSize={20} />
</div>
)
}

const TokenValueFragment = ({ fragment }: { fragment: RichTokenValueFragment }) => {
return (
<TokenAmount
value={fragment.value}
direction={undefined}
logoUri={fragment.logoUri || undefined}
tokenSymbol={fragment.symbol || undefined}
size={20}
/>
)
}

export const HumanDescription = ({ fragments }: RichDecodedInfo) => {
return (
<>
{fragments.map((fragment) => {
switch (fragment.type) {
case RichFragmentType.Text:
return <span>{fragment.value}</span>
case RichFragmentType.Address:
return <AddressFragment fragment={fragment} />
case RichFragmentType.TokenValue:
return <TokenValueFragment fragment={fragment} />
}
})}
</>
)
}
30 changes: 30 additions & 0 deletions src/components/transactions/HumanDescription/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.summary {
display: flex;
gap: 8px;
align-items: center;
width: 100%;
}

/* TODO: This is a workaround to hide address in case there is a title */
.address div[title] + div {
display: none;
}

.value {
display: flex;
align-items: center;
font-weight: bold;
gap: 4px;
}

.method {
display: inline-flex;
align-items: center;
gap: 0.5em;
}

.wrapper {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
2 changes: 1 addition & 1 deletion src/components/transactions/SingleTx/SingleTx.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('SingleTx', () => {
const button = screen.queryByText('Details')
expect(button).not.toBeInTheDocument()

expect(await screen.findByText('Contract interaction')).toBeInTheDocument()
expect(await screen.findByText('Interact with')).toBeInTheDocument()
})

it('shows an error when the transaction has failed to load', async () => {
Expand Down
Loading
Loading