Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Fix #596: Notification when safe is already updated #599

Merged
merged 18 commits into from
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
34aacb7
Merge branch 'development' of https://github.com/gnosis/safe-react
Agupane Dec 18, 2019
c4f39a4
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Dec 26, 2019
fdf4e48
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Jan 15, 2020
71e760a
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Jan 23, 2020
7b544a7
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Jan 29, 2020
9912420
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Feb 6, 2020
7387d2e
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Feb 6, 2020
ae84a3a
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Feb 12, 2020
852fd81
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Feb 14, 2020
6666c04
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Feb 14, 2020
290ff60
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Feb 18, 2020
b6540fc
Merge branch 'development' of https://github.com/gnosis/safe-react in…
Agupane Feb 19, 2020
909df23
Fix notification of update if the safe is already updated
Agupane Feb 20, 2020
a92d042
Makes the notification clickable
Agupane Feb 20, 2020
c247d63
Identify upgrade tx
fernandomg Feb 20, 2020
71b0002
Merge branch 'feature/#581-contract-upgrade-improvements' into 596-no…
fernandomg Feb 20, 2020
774d93e
Add red badge to Settings tab
fernandomg Feb 20, 2020
1f106a2
Fixs Padding
Agupane Feb 21, 2020
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 src/logic/notifications/notificationTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export const NOTIFICATIONS: Notifications = {

// Safe Version
SAFE_NEW_VERSION_AVAILABLE: {
message: 'There is a new version available for this Safe',
message: 'There is a new version available for this Safe. Update now!',
options: { variant: WARNING, persist: false, preventDuplicate: true },
},
}
8 changes: 8 additions & 0 deletions src/logic/tokens/utils/tokenHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,11 @@ export const isAddressAToken = async (tokenAddress: string) => {

export const isTokenTransfer = (data: string, value: number): boolean =>
!!data && data.substring(0, 10) === '0xa9059cbb' && value === 0

export const isMultisendTransaction = (data: string, value: number): boolean =>
!!data && data.substring(0, 10) === '0x8d80ff0a' && value === 0

// f08a0323 - setFallbackHandler (308, 8)
// 7de7edef - changeMasterCopy (550, 8)
export const isUpgradeTransaction = (data: string) =>
!!data && data.substr(308, 8) === 'f08a0323' && data.substr(550, 8) === '7de7edef'
31 changes: 29 additions & 2 deletions src/routes/safe/components/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import classNames from 'classnames/bind'
import { Switch, Redirect, Route, withRouter } from 'react-router-dom'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Badge from '@material-ui/core/Badge'
import CallMade from '@material-ui/icons/CallMade'
import CallReceived from '@material-ui/icons/CallReceived'
import { withStyles } from '@material-ui/core/styles'
Expand Down Expand Up @@ -35,6 +36,7 @@ import { AddressBookIcon } from './assets/AddressBookIcon'
import { TransactionsIcon } from './assets/TransactionsIcon'
import { BalancesIcon } from './assets/BalancesIcon'
import { AppsIcon } from './assets/AppsIcon'
import { getSafeVersion } from '~/logic/safe/utils/safeVersion'

export const BALANCES_TAB_BTN_TEST_ID = 'balances-tab-btn'
export const SETTINGS_TAB_BTN_TEST_ID = 'settings-tab-btn'
Expand Down Expand Up @@ -102,6 +104,23 @@ const Layout = (props: Props) => {
onClose: null,
})

const [needUpdate, setNeedUpdate] = useState(false)

React.useEffect(() => {
const checkUpdateRequirement = async () => {
let safeVersion = {}

try {
safeVersion = await getSafeVersion(safe.address)
} catch (e) {
console.error('failed to check version', e)
}
setNeedUpdate(safeVersion.needUpdate)
}

checkUpdateRequirement()
}, [safe && safe.address])

const handleCallToRouter = (_, value) => {
const { history } = props

Expand Down Expand Up @@ -147,11 +166,19 @@ const Layout = (props: Props) => {
Apps
</>
)

const labelSettings = (
<>
<SettingsIcon />
Settings
<Badge
badgeContent=""
variant="dot"
invisible={!needUpdate || !granted}
color="error"
style={{ paddingRight: '10px' }}
>
Settings
</Badge>
</>
)
const labelBalances = (
Expand Down
23 changes: 22 additions & 1 deletion src/routes/safe/components/Settings/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react'
import cn from 'classnames'
import { List } from 'immutable'
import { connect } from 'react-redux'
import Badge from '@material-ui/core/Badge'
import { withStyles } from '@material-ui/core/styles'
import Paragraph from '~/components/layout/Paragraph'
import { OwnersIcon } from './assets/icons/OwnersIcon'
Expand All @@ -25,12 +26,14 @@ import { styles } from './style'
import RemoveSafeIcon from './assets/icons/bin.svg'
import type { Safe } from '~/routes/safe/store/models/safe'
import type { AddressBook } from '~/logic/addressBook/model/addressBook'
import { getSafeVersion } from '~/logic/safe/utils/safeVersion'

export const OWNERS_SETTINGS_TAB_TEST_ID = 'owner-settings-tab'

type State = {
showRemoveSafe: boolean,
menuOptionIndex: number,
needUpdate: boolean,
}

type Props = Actions & {
Expand Down Expand Up @@ -63,9 +66,25 @@ class Settings extends React.Component<Props, State> {
this.state = {
showRemoveSafe: false,
menuOptionIndex: 1,
needUpdate: false,
}
}

componentDidMount(): void {
const checkUpdateRequirement = async () => {
let safeVersion = {}

try {
safeVersion = await getSafeVersion(this.props.safe.address)
} catch (e) {
console.error('failed to check version', e)
}
this.setState({ needUpdate: safeVersion.needUpdate })
}

checkUpdateRequirement()
}

handleChange = menuOptionIndex => () => {
this.setState({ menuOptionIndex })
}
Expand Down Expand Up @@ -124,7 +143,9 @@ class Settings extends React.Component<Props, State> {
onClick={this.handleChange(1)}
>
<SafeDetailsIcon />
Safe details
<Badge badgeContent=" " variant="dot" invisible={!this.state.needUpdate || !granted} color="error" style={{paddingRight: '10px'}}>
Safe details
</Badge>
</Row>
<Hairline className={classes.hairline} />
<Row
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ const TxDescription = ({ tx, classes }: Props) => {
cancellationTx,
customTx,
creationTx,
upgradeTx,
data,
} = getTxData(tx)
const amount = getTxAmount(tx)
Expand All @@ -179,8 +180,11 @@ const TxDescription = ({ tx, classes }: Props) => {
{modifySettingsTx && (
<SettingsDescription removedOwner={removedOwner} newThreshold={newThreshold} addedOwner={addedOwner} />
)}
{customTx && <CustomDescription data={data} amount={amount} recipient={recipient} classes={classes} />}
{!cancellationTx && !modifySettingsTx && !customTx && !creationTx && (
{!upgradeTx && customTx && (
<CustomDescription data={data} amount={amount} recipient={recipient} classes={classes} />
)}
{upgradeTx && <div>{data}</div>}
{!cancellationTx && !modifySettingsTx && !customTx && !creationTx && !upgradeTx && (
<TransferDescription amount={amount} recipient={recipient} />
)}
</Block>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ type DecodedTxData = {
data: string,
}

const getSafeVersion = (data: string) => {
const contractAddress = data.substr(582, 40).toLowerCase()

return (
{
'34cfac646f301356faa8b21e94227e3583fe3f5f': '1.1.1',
}[contractAddress] || 'X.x.x'
)
}

export const getTxData = (tx: Transaction): DecodedTxData => {
const web3 = getWeb3()
const { toBN, fromWei } = web3.utils
Expand Down Expand Up @@ -57,6 +67,9 @@ export const getTxData = (tx: Transaction): DecodedTxData => {
txData.cancellationTx = true
} else if (tx.creationTx) {
txData.creationTx = true
} else if (tx.upgradeTx) {
txData.upgradeTx = true
txData.data = `The contract of this Safe is upgraded to Version ${getSafeVersion(tx.data)}`
} else {
txData.recipient = tx.recipient
txData.value = 0
Expand Down
29 changes: 18 additions & 11 deletions src/routes/safe/components/Transactions/TxsTable/TxType/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow
import * as React from 'react'
import { withStyles } from '@material-ui/core/styles'
import { makeStyles } from '@material-ui/core/styles'
import Block from '~/components/layout/Block'
import Paragraph from '~/components/layout/Paragraph/'
import Img from '~/components/layout/Img'
Expand All @@ -12,7 +12,6 @@ import SettingsTxIcon from './assets/settings.svg'
import { styles } from './style'

type Props = {
classes: Object,
txType: TransactionType,
}

Expand All @@ -23,6 +22,7 @@ const typeToIcon = {
settings: SettingsTxIcon,
creation: SettingsTxIcon,
cancellation: SettingsTxIcon,
upgrade: SettingsTxIcon,
}

const typeToLabel = {
Expand All @@ -32,15 +32,22 @@ const typeToLabel = {
settings: 'Modify settings',
creation: 'Safe created',
cancellation: 'Cancellation transaction',
upgrade: 'Contract Upgrade',
}

const TxType = ({ classes, txType }: Props) => (
<Block className={classes.container}>
<Img src={typeToIcon[txType]} alt={typeToLabel[txType]} className={classes.img} />
<Paragraph className={classes.type} noMargin>
{typeToLabel[txType]}
</Paragraph>
</Block>
)
const useStyles = makeStyles(styles)

export default withStyles(styles)(TxType)
const TxType = ({ txType }: Props) => {
const classes = useStyles()

return (
<Block className={classes.container}>
<Img src={typeToIcon[txType]} alt={typeToLabel[txType]} className={classes.img} />
<Paragraph className={classes.type} noMargin>
{typeToLabel[txType]}
</Paragraph>
</Block>
)
}

export default TxType
2 changes: 2 additions & 0 deletions src/routes/safe/components/Transactions/TxsTable/columns.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ const getTransactionTableData = (tx: Transaction, cancelTx: ?Transaction): Trans
txType = 'custom'
} else if (tx.creationTx) {
txType = 'creation'
} else if (tx.upgradeTx) {
txType = 'upgrade'
}

return {
Expand Down
8 changes: 6 additions & 2 deletions src/routes/safe/store/actions/fetchTransactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { getLocalSafe } from '~/logic/safe/utils'
import { addTransactions } from './addTransactions'
import { addIncomingTransactions } from './addIncomingTransactions'
import { getHumanFriendlyToken } from '~/logic/tokens/store/actions/fetchTokens'
import { isTokenTransfer } from '~/logic/tokens/utils/tokenHelpers'
import { isMultisendTransaction, isTokenTransfer, isUpgradeTransaction } from '~/logic/tokens/utils/tokenHelpers'
import { decodeParamsFromSafeMethod } from '~/logic/contracts/methodIds'
import { ALTERNATIVE_TOKEN_ABI } from '~/logic/tokens/utils/alternativeAbi'
import type { TransactionProps } from '~/routes/safe/store/models/transaction'
Expand Down Expand Up @@ -89,7 +89,9 @@ export const buildTransactionFrom = async (safeAddress: string, tx: TxServiceMod
const modifySettingsTx = sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 && !!tx.data
const cancellationTx = sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 && !tx.data
const isSendTokenTx = isTokenTransfer(tx.data, Number(tx.value))
const customTx = !sameAddress(tx.to, safeAddress) && !!tx.data && !isSendTokenTx
const isMultiSendTx = isMultisendTransaction(tx.data, Number(tx.value))
const customTx = !sameAddress(tx.to, safeAddress) && !!tx.data && !isSendTokenTx && !isMultiSendTx
const isUpgradeTx = isMultiSendTx && isUpgradeTransaction(tx.data)

let refundParams = null
if (tx.gasPrice > 0) {
Expand Down Expand Up @@ -165,6 +167,8 @@ export const buildTransactionFrom = async (safeAddress: string, tx: TxServiceMod
executionTxHash: tx.transactionHash,
safeTxHash: tx.safeTxHash,
isTokenTransfer: isSendTokenTx,
multiSendTx: isMultiSendTx,
upgradeTx: isUpgradeTx,
decodedParams,
modifySettingsTx,
customTx,
Expand Down
28 changes: 22 additions & 6 deletions src/routes/safe/store/middleware/notificationsMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { enhanceSnackbarForAction, NOTIFICATIONS } from '~/logic/notifications'
import closeSnackbarAction from '~/logic/notifications/store/actions/closeSnackbar'
import { getIncomingTxAmount } from '~/routes/safe/components/Transactions/TxsTable/columns'
import updateSafe from '~/routes/safe/store/actions/updateSafe'
import { safesMapSelector } from '~/routes/safe/store/selectors'
import { safeParamAddressFromStateSelector, safesMapSelector } from '~/routes/safe/store/selectors'
import { isUserOwner } from '~/logic/wallets/ethAddresses'
import { ADD_SAFE } from '~/routes/safe/store/actions/addSafe'
import { getSafeVersion } from '~/logic/safe/utils/safeVersion'
import { grantedSelector } from '~/routes/safe/container/selector'

const watchedActions = [ADD_TRANSACTIONS, ADD_INCOMING_TRANSACTIONS, ADD_SAFE]

Expand Down Expand Up @@ -106,12 +107,27 @@ const notificationsMiddleware = (store: Store<GlobalState>) => (next: Function)
break
}
case ADD_SAFE: {
const { safe } = action.payload
const { needUpdate } = await getSafeVersion(safe.address)
const state: GlobalState = store.getState()
const currentSafeAddress = safeParamAddressFromStateSelector(state)
const isUserOwner = grantedSelector(state)
const { needUpdate } = await getSafeVersion(currentSafeAddress)

const notificationKey = `${safe.address}`
if (needUpdate) {
dispatch(enqueueSnackbar(enhanceSnackbarForAction(NOTIFICATIONS.SAFE_NEW_VERSION_AVAILABLE, notificationKey)))
const notificationKey = `${currentSafeAddress}`
const onNotificationClicked = () => {
dispatch(closeSnackbarAction({ key: notificationKey }))
dispatch(push(`/safes/${currentSafeAddress}/settings`))
}

if (needUpdate && isUserOwner) {
dispatch(
enqueueSnackbar(
enhanceSnackbarForAction(
NOTIFICATIONS.SAFE_NEW_VERSION_AVAILABLE,
notificationKey,
onNotificationClicked,
),
),
)
}
break
}
Expand Down
4 changes: 4 additions & 0 deletions src/routes/safe/store/models/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export type TransactionProps = {
cancellationTx: boolean,
customTx: boolean,
creationTx: boolean,
multiSendTx: boolean,
upgradeTx: boolean,
safeTxHash: string,
executor: string,
executionTxHash?: ?string,
Expand Down Expand Up @@ -74,6 +76,8 @@ export const makeTransaction: RecordFactory<TransactionProps> = Record({
cancellationTx: false,
customTx: false,
creationTx: false,
multiSendTx: false,
upgradeTx: false,
status: 'awaiting',
decimals: 18,
isTokenTransfer: false,
Expand Down