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

Release 1.4.0 #1599

Merged
merged 34 commits into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d2b0397
render an img tag in SafeAppIcon iframe (#1516)
mmv08 Jan 11, 2023
c703b8c
fix: show input loading state when "Add Safe" from the sidebar (#1528)
DiogoSoaress Jan 12, 2023
06b7f28
feat(safe-apps): Use SignMessageLib from safe-core-sdk (#1419)
yagopv Jan 12, 2023
f2a28e8
fix: wait until successful SDK call to set pending state (#1529)
DiogoSoaress Jan 13, 2023
9694877
fix: Extend create safe e2e test (#1543)
usame-algan Jan 13, 2023
2e85fbd
Feat: explorer link for unproposed txs (#1542)
katspaugh Jan 16, 2023
db190fc
style: mobile Address Book layout (#1503)
DiogoSoaress Jan 16, 2023
7e6aa6c
refactor: replace Stepper for MUI's List (#1467)
DiogoSoaress Jan 18, 2023
ebb9237
fix: GA tracking wrong eventLabels, missing event type, overtracking …
usame-algan Jan 19, 2023
e842dd2
fix: Remove last used wallet when disconnecting (#1571)
usame-algan Jan 20, 2023
20b2191
fix: prevent scrollbars on Safe App icons (#1565)
iamacook Jan 20, 2023
707c3dd
fix: position executor in transaction stepper (#1574)
iamacook Jan 20, 2023
73d29fc
fix: align the ...-menu with EthHashInfo
abheektripathy Jan 22, 2023
0626cc5
fix: ref forwarding (#1578)
iamacook Jan 23, 2023
57e0ba4
fix: Reduce cummulative layout shift (CLS) (#1577)
usame-algan Jan 23, 2023
471b3af
feat: add smoke test for spending limit modal (#1581)
iamacook Jan 23, 2023
076a9fd
fix: "Recipient" -> "Recipient address or ENS" (#1570)
katspaugh Jan 23, 2023
9395488
fix: DOM nesting error (#1585)
iamacook Jan 23, 2023
1bfb137
fix: transaction info styling (#1576)
abheektripathy Jan 23, 2023
cd34595
fix: `merge` hydration of persisted state (#1552)
iamacook Jan 23, 2023
5f7336f
fix: `EthHashInfo` nesting (#1590)
iamacook Jan 24, 2023
f0ba1ea
fix: reset error boundary after navigation (#1586)
iamacook Jan 24, 2023
7a65685
fix: Handle safe creation errors (#1591)
usame-algan Jan 24, 2023
ab547e7
fix: fallback to `chainId` of `wallet` (#1587)
iamacook Jan 24, 2023
2e61792
fix: asset selection validation (#1583)
iamacook Jan 24, 2023
6f54491
Add increment for failing estimations in Gnosis Chain (#1559)
dasanra Jan 24, 2023
253fefe
fix: iterate signing method on error (#1589)
iamacook Jan 24, 2023
e4c58a9
Fix: propose immediately executed txs (#1569)
katspaugh Jan 24, 2023
49a2755
Feat: support 1.0.0 Safes (#1588)
katspaugh Jan 25, 2023
deb7a38
v1.4.0
katspaugh Jan 25, 2023
a3036ee
fix: unread badge colour in dark mode (#1598)
iamacook Jan 26, 2023
e84003f
Fix: default wallet when none are enabled (#1597)
katspaugh Jan 26, 2023
c066c45
fix: prevent multiple pairing tracking calls (#1605)
iamacook Jan 27, 2023
bb6fa15
fix: migrate/sanitise legacy owner format (#1604)
iamacook Jan 30, 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
2 changes: 1 addition & 1 deletion .github/workflows/cla.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
# branch should not be protected
branch: 'main'
# user names of users allowed to contribute without CLA
allowlist: lukasschor,mikheevm,rmeissner,germartinez,Uxio0,dasanra,francovenica,tschubotz,luarx,gnosis-info,bot*,DaniSomoza,iamacook,yagopv,usame-algan,schmanu,DiogoSoaress,JagoFigueroa
allowlist: lukasschor,rmeissner,germartinez,Uxio0,dasanra,francovenica,tschubotz,luarx,DaniSomoza,iamacook,yagopv,usame-algan,schmanu,DiogoSoaress,JagoFigueroa,bot*

# the followings are the optional inputs - If the optional inputs are not given, then default values will be taken
# enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ const DEFAULT_OWNER_ADDRESS = '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED'
const OWNER_ADDRESS = '0xE297437d6b53890cbf004e401F3acc67c8b39665'

describe('Create Safe form', () => {
before(() => {
localStorage.setItem('SAFE_v2__lastWallet', JSON.stringify('E2E Wallet'))
})
it('should navigate to the form', () => {
//cy.connectE2EWallet()
cy.connectE2EWallet()

cy.visit('/welcome')

Expand All @@ -27,18 +24,40 @@ describe('Create Safe form', () => {

// Input a custom name
cy.get('input[name="name"]').type('Test safe name').should('have.value', 'Test safe name')
})

it('should allow changing the network', () => {
// Switch to a different network
cy.get('[data-cy="create-safe-select-network"]').click()
cy.contains('Ethereum').click()

// Network hint should be displayed
cy.contains('Change your wallet network').should('be.visible')
cy.contains('button', 'Next').should('be.disabled')

// Switch back to Görli
cy.get('[data-cy="create-safe-select-network"]').click()
cy.contains('li span', 'Görli').click()

cy.contains('button', 'Next').click()
})

it('should display a default owner and threshold', () => {
// Default owner
cy.get('input[name="owners.0.address"]').should('have.value', DEFAULT_OWNER_ADDRESS)
cy.get('input[name="owners.0.name"]').type('Test Owner Name').should('have.value', 'Test Owner Name')

// Default threshold
cy.get('input[name="threshold"]').should('have.value', 1)
})

it('should allow changing the owner name', () => {
cy.get('input[name="owners.0.name"]').type('Test Owner Name')
cy.contains('button', 'Back').click()
cy.contains('button', 'Next').click()
cy.get('input[name="owners.0.name"]').should('have.value', 'Test Owner Name')
})

it('should add a new owner and update threshold', () => {
// Add new owner
cy.contains('button', 'Add new owner').click()
cy.get('input[name="owners.1.address"]').should('exist')
Expand All @@ -47,14 +66,21 @@ describe('Create Safe form', () => {
// Update threshold
cy.get('input[name="threshold"]').parent().click()
cy.contains('li', '2').click()
})

it('should remove an owner and update threshold', () => {
// Remove owner
cy.get('button[aria-label="Remove owner"]').click()

// Threshold should change back to 1
cy.get('input[name="threshold"]').should('have.value', 1)

cy.contains('button', 'Next').click()
})

it('should display summary on review page', () => {
cy.contains('Test safe name')
cy.contains(OWNER_ADDRESS)
cy.contains(DEFAULT_OWNER_ADDRESS)
cy.contains('2 out of 2')
cy.contains('1 out of 1')
})
})
59 changes: 59 additions & 0 deletions cypress/e2e/spending_limit.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const HW_WALLET = '0xff6E053fBf4E5895eDec09146Bc10f705E8c4b3D'
const SPENDING_LIMIT_SAFE = 'gor:0x28F95E682D1dd632b54Dc61740575f49DB39Eb7F'

// TODO: Extend with non-owner spending limit beneficiaries when
// https://github.com/safe-global/web-core/pull/1572 is merged

describe('Check spending limit modal', () => {
before(() => {
cy.connectE2EWallet()

cy.visit(`/${SPENDING_LIMIT_SAFE}/home`, { failOnStatusCode: false })

cy.contains('Accept selection').click()
})

it('should open the spending limit modal', () => {
// Assert that "New transaction" button is visible
cy.contains('New transaction', {
timeout: 60_000, // `lastWallet` takes a while initialize in CI
}).should('be.visible')

// Open the new transaction modal
cy.contains('New transaction').click()
})

it('should draft a spending limit transaction', () => {
// Modal is open
cy.contains('h2', 'New transaction').should('be.visible')

cy.contains('Send tokens').click()

// Fill transaction data
cy.get('input[name="recipient"]').type(SPENDING_LIMIT_SAFE)

// Click on the Token selector
cy.get('input[name="tokenAddress"]').prev().click()
cy.get('ul[role="listbox"]').contains('Görli Ether').click()

// Insert max amount
cy.contains('Spending Limit Transaction (0.1 GOR)').click()

// Insert max amount
cy.contains('Max').click()

cy.contains('Next').click()
})

it('should review the spending limit transaction', () => {
cy.contains(
'Spending limit transactions only appear in the interface once they are successfully processed and indexed. Pending transactions can only be viewed in your signer wallet application or under your wallet address on a Blockchain Explorer.',
)

// Alias for New transaction modal
cy.contains('h2', 'Review transaction').parents('div').as('modal')

// Estimation is loaded
cy.get('button[type="submit"]').should('not.be.disabled')
})
})
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"homepage": "https://github.com/safe-global/web-core",
"license": "GPL-3.0",
"type": "module",
"version": "1.3.0",
"version": "1.4.0",
"scripts": {
"dev": "next dev",
"start": "next dev",
Expand Down Expand Up @@ -45,8 +45,8 @@
"@mui/material": "^5.11.2",
"@mui/x-date-pickers": "^5.0.12",
"@reduxjs/toolkit": "^1.8.2",
"@safe-global/safe-core-sdk": "^3.2.2",
"@safe-global/safe-ethers-lib": "^1.8.0",
"@safe-global/safe-core-sdk": "^3.3.0",
"@safe-global/safe-ethers-lib": "^1.9.0",
"@safe-global/safe-gateway-typescript-sdk": "^3.5.5",
"@sentry/react": "^7.28.1",
"@sentry/tracing": "^7.28.1",
Expand Down Expand Up @@ -83,7 +83,7 @@
},
"devDependencies": {
"@next/bundle-analyzer": "^13.1.1",
"@safe-global/safe-core-sdk-types": "^1.8.0",
"@safe-global/safe-core-sdk-types": "^1.9.0",
"@sentry/types": "^7.28.1",
"@svgr/webpack": "^6.3.1",
"@testing-library/cypress": "^8.0.7",
Expand Down
6 changes: 6 additions & 0 deletions public/images/apps/app-placeholder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/common/cancel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/common/circle-check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/common/circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/common/created.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/common/dot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 8 additions & 24 deletions src/components/address-book/AddressBookHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, SvgIcon, Grid, IconButton } from '@mui/material'
import { Button, SvgIcon, Grid } from '@mui/material'
import type { ReactElement, ElementType } from 'react'
import InputAdornment from '@mui/material/InputAdornment'
import SearchIcon from '@/public/images/common/search.svg'
Expand All @@ -14,8 +14,6 @@ import ImportIcon from '@/public/images/address-book/import.svg'
import ExportIcon from '@/public/images/address-book/export.svg'
import AddCircleIcon from '@/public/images/common/add-outlined.svg'

import css from './styles.module.css'

const HeaderButton = ({
icon,
onClick,
Expand All @@ -27,26 +25,12 @@ const HeaderButton = ({
disabled?: boolean
children: string
}): ReactElement => {
const svg = <SvgIcon component={icon} inheritViewBox />
const svg = <SvgIcon component={icon} inheritViewBox fontSize="small" />

return (
<>
<IconButton color="primary" onClick={onClick} disabled={disabled} className={css.iconButton}>
{svg}
</IconButton>

<Button
onClick={onClick}
disabled={disabled}
variant="text"
color="primary"
size="small"
startIcon={svg}
className={css.button}
>
{children}
</Button>
</>
<Button onClick={onClick} disabled={disabled} variant="text" color="primary" size="small" startIcon={svg}>
{children}
</Button>
)
}

Expand All @@ -65,8 +49,8 @@ const AddressBookHeader = ({ handleOpenModal, searchQuery, onSearchQueryChange }
title="Address book"
noBorder
action={
<Grid container pb={1}>
<Grid item xs={8} md={5} xl={4.5}>
<Grid container pb={1} spacing={1}>
<Grid item xs={12} md={5} xl={4.5}>
<TextField
placeholder="Search"
variant="filled"
Expand All @@ -87,7 +71,7 @@ const AddressBookHeader = ({ handleOpenModal, searchQuery, onSearchQueryChange }
size="small"
/>
</Grid>
<Grid item xs={4} md={7} display="flex" justifyContent="flex-end" alignItems="center">
<Grid item xs={12} md={7} display="flex" justifyContent={['space-between', , 'flex-end']} alignItems="center">
<Track {...ADDRESS_BOOK_EVENTS.IMPORT_BUTTON}>
<HeaderButton onClick={handleOpenModal(ModalType.IMPORT)} icon={ImportIcon}>
Import
Expand Down
17 changes: 0 additions & 17 deletions src/components/address-book/AddressBookHeader/styles.module.css

This file was deleted.

11 changes: 2 additions & 9 deletions src/components/address-book/AddressBookTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import Tooltip from '@mui/material/Tooltip'
import RemoveDialog from '@/components/address-book/RemoveDialog'
import useIsGranted from '@/hooks/useIsGranted'
import NewTxModal from '@/components/tx/modals/NewTxModal'
import css from './styles.module.css'
import EthHashInfo from '@/components/common/EthHashInfo'
import AddressBookHeader from '../AddressBookHeader'
import useAddressBook from '@/hooks/useAddressBook'
Expand Down Expand Up @@ -114,13 +113,7 @@ const AddressBookTable = () => {

{isGranted && (
<Track {...ADDRESS_BOOK_EVENTS.SEND}>
<Button
variant="contained"
color="primary"
size="small"
onClick={() => setSelectedAddress(address)}
className={css.sendButton}
>
<Button variant="contained" color="primary" size="small" onClick={() => setSelectedAddress(address)}>
Send
</Button>
</Track>
Expand All @@ -141,7 +134,7 @@ const AddressBookTable = () => {

<main>
{filteredEntries.length > 0 ? (
<EnhancedTable rows={rows} headCells={headCells} />
<EnhancedTable rows={rows} headCells={headCells} mobileVariant />
) : (
<Box bgcolor="background.paper" borderRadius={1}>
<PagePlaceholder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,4 @@
.container td:last-of-type button {
opacity: 1;
}

.sendButton {
display: none;
}
}
2 changes: 1 addition & 1 deletion src/components/address-book/EntryDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const EntryDialog = ({
<Box>
<AddressInput
name="address"
label="Address"
label="Contact"
variant="outlined"
fullWidth
required
Expand Down
15 changes: 10 additions & 5 deletions src/components/common/AddressInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ReactElement } from 'react'
import { useEffect, useCallback, useRef, useMemo } from 'react'
import { useEffect, useCallback, useRef, useMemo, useState } from 'react'
import { InputAdornment, TextField, type TextFieldProps, CircularProgress, Grid } from '@mui/material'
import { useFormContext, useWatch, type Validate, get } from 'react-hook-form'
import { FEATURES } from '@safe-global/safe-gateway-typescript-sdk'
Expand All @@ -18,16 +18,18 @@ const AddressInput = ({ name, validate, required = true, deps, ...props }: Addre
register,
setValue,
control,
formState: { errors, isValidating },
formState: { errors },
trigger,
} = useFormContext()
const currentChain = useCurrentChain()
const rawValueRef = useRef<string>('')
const watchedValue = useWatch({ name, control })
const currentShortName = currentChain?.shortName || ''
const [isValidating, setIsValidating] = useState<boolean>(false)

// Fetch an ENS resolution for the current address
const isDomainLookupEnabled = !!currentChain && hasFeature(currentChain, FEATURES.DOMAIN_LOOKUP)
const label = `${props.label} address${isDomainLookupEnabled ? ' or ENS' : ''}`
const { address, resolverError, resolving } = useNameResolver(isDomainLookupEnabled ? watchedValue : '')

// errors[name] doesn't work with nested field names like 'safe.address', need to use the lodash get
Expand Down Expand Up @@ -60,7 +62,7 @@ const AddressInput = ({ name, validate, required = true, deps, ...props }: Addre
<TextField
{...props}
autoComplete="off"
label={<>{error?.message || props.label}</>}
label={<>{error?.message || label}</>}
error={!!error}
fullWidth
spellCheck={false}
Expand Down Expand Up @@ -94,10 +96,13 @@ const AddressInput = ({ name, validate, required = true, deps, ...props }: Addre
return parsePrefixedAddress(value).address
},

validate: () => {
validate: async () => {
const value = rawValueRef.current
if (value) {
return validatePrefixed(value) || validate?.(parsePrefixedAddress(value).address)
setIsValidating(true)
const result = validatePrefixed(value) || (await validate?.(parsePrefixedAddress(value).address))
setIsValidating(false)
return result
}
},

Expand Down
Loading