Merge branch 'development' of github.com:gnosis/safe-react into bug-web3connect-android-fix

This commit is contained in:
mmv 2019-11-14 11:17:08 +04:00
commit 0e77694f5e
25 changed files with 760 additions and 1755 deletions

View File

@ -33,7 +33,7 @@
"dependencies": { "dependencies": {
"@gnosis.pm/safe-contracts": "^1.0.0", "@gnosis.pm/safe-contracts": "^1.0.0",
"@gnosis.pm/util-contracts": "2.0.4", "@gnosis.pm/util-contracts": "2.0.4",
"@material-ui/core": "4.6.0", "@material-ui/core": "4.6.1",
"@material-ui/icons": "4.5.1", "@material-ui/icons": "4.5.1",
"@portis/web3": "^2.0.0-beta.45", "@portis/web3": "^2.0.0-beta.45",
"@testing-library/jest-dom": "4.2.3", "@testing-library/jest-dom": "4.2.3",
@ -57,7 +57,7 @@
"react-dom": "16.11.0", "react-dom": "16.11.0",
"react-final-form": "6.3.0", "react-final-form": "6.3.0",
"react-final-form-listeners": "^1.0.2", "react-final-form-listeners": "^1.0.2",
"react-hot-loader": "4.12.16", "react-hot-loader": "4.12.17",
"react-qr-reader": "^2.2.1", "react-qr-reader": "^2.2.1",
"react-redux": "7.1.3", "react-redux": "7.1.3",
"react-router-dom": "5.1.2", "react-router-dom": "5.1.2",
@ -116,7 +116,7 @@
"detect-port": "^1.3.0", "detect-port": "^1.3.0",
"eslint": "5.16.0", "eslint": "5.16.0",
"eslint-config-airbnb": "18.0.1", "eslint-config-airbnb": "18.0.1",
"eslint-plugin-flowtype": "4.3.0", "eslint-plugin-flowtype": "4.4.1",
"eslint-plugin-import": "2.18.2", "eslint-plugin-import": "2.18.2",
"eslint-plugin-jest": "23.0.3", "eslint-plugin-jest": "23.0.3",
"eslint-plugin-jsx-a11y": "6.2.3", "eslint-plugin-jsx-a11y": "6.2.3",
@ -124,7 +124,7 @@
"ethereumjs-abi": "0.6.8", "ethereumjs-abi": "0.6.8",
"extract-text-webpack-plugin": "^4.0.0-beta.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "4.2.0", "file-loader": "4.2.0",
"flow-bin": "0.111.3", "flow-bin": "0.112.0",
"fs-extra": "8.1.0", "fs-extra": "8.1.0",
"html-loader": "^0.5.5", "html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
@ -141,7 +141,7 @@
"storybook-host": "5.1.0", "storybook-host": "5.1.0",
"storybook-router": "^0.3.4", "storybook-router": "^0.3.4",
"style-loader": "1.0.0", "style-loader": "1.0.0",
"truffle": "5.0.44", "truffle": "5.1.0",
"truffle-contract": "4.0.31", "truffle-contract": "4.0.31",
"truffle-solidity-loader": "0.1.32", "truffle-solidity-loader": "0.1.32",
"uglifyjs-webpack-plugin": "2.2.0", "uglifyjs-webpack-plugin": "2.2.0",

View File

@ -1,6 +1,5 @@
// @flow // @flow
import React from 'react' import React from 'react'
import { connect } from 'react-redux'
import Web3Connect from 'web3connect' import Web3Connect from 'web3connect'
import Torus from '@toruslabs/torus-embed' import Torus from '@toruslabs/torus-embed'
import WalletConnectProvider from '@walletconnect/web3-provider' import WalletConnectProvider from '@walletconnect/web3-provider'
@ -54,15 +53,11 @@ web3Connect.on('connect', (provider: any) => {
}) })
type Props = { type Props = {
registerProvider: Function,
enqueueSnackbar: Function, enqueueSnackbar: Function,
closeSnackbar: Function, closeSnackbar: Function,
} }
const ConnectButton = ({ const ConnectButton = (props: Props) => (
registerProvider, ...props
}: Props) => (
<Button <Button
color="primary" color="primary"
variant="contained" variant="contained"
@ -76,7 +71,4 @@ const ConnectButton = ({
</Button> </Button>
) )
export default connect( export default ConnectButton
null,
{ registerProvider: fetchProvider },
)(ConnectButton)

View File

@ -0,0 +1,23 @@
// @flow
import React from 'react'
import { makeStyles } from '@material-ui/core/styles'
import ReactDOM from 'react-dom'
import Backdrop from '@material-ui/core/Backdrop'
const useStyles = makeStyles({
root: {
zIndex: 1300,
},
})
const BackdropLayout = ({ isOpen = false }: { isOpen: boolean }) => {
if (!isOpen) {
return null
}
const classes = useStyles()
return ReactDOM.createPortal(<Backdrop classes={{ root: classes.root }} open />, document.body)
}
export default BackdropLayout

View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<g fill="none" fill-rule="evenodd">
<rect width="2" height="8" x="9" y="8" fill="#B2B5B2" rx="1"/>
<rect width="2" height="2" x="9" y="4" fill="#B2B5B2" stroke="#B2B5B2" stroke-width=".5" rx="1"/>
<circle cx="10" cy="10" r="9" stroke="#B2B5B2" stroke-width="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@ -1,14 +1,20 @@
// @flow // @flow
import * as React from 'react' import * as React from 'react'
import { connect } from 'react-redux'
import { SnackbarProvider } from 'notistack' import { SnackbarProvider } from 'notistack'
import { withStyles } from '@material-ui/core/styles' import { withStyles } from '@material-ui/core/styles'
import { getNetwork } from '~/config'
import { ETHEREUM_NETWORK } from '~/logic/wallets/getWeb3'
import SidebarProvider from '~/components/Sidebar' import SidebarProvider from '~/components/Sidebar'
import Header from '~/components/Header' import Header from '~/components/Header'
import Backdrop from '~/components/layout/Backdrop'
import Img from '~/components/layout/Img' import Img from '~/components/layout/Img'
import Notifier from '~/components/Notifier' import Notifier from '~/components/Notifier'
import AlertLogo from './assets/alert.svg' import { networkSelector } from '~/logic/wallets/store/selectors'
import CheckLogo from './assets/check.svg' import AlertIcon from './assets/alert.svg'
import ErrorLogo from './assets/error.svg' import CheckIcon from './assets/check.svg'
import ErrorIcon from './assets/error.svg'
import InfoIcon from './assets/info.svg'
import styles from './index.scss' import styles from './index.scss'
const notificationStyles = { const notificationStyles = {
@ -40,11 +46,11 @@ const notificationStyles = {
boxShadow: '0 0 10px 0 rgba(212, 212, 211, 0.59)', boxShadow: '0 0 10px 0 rgba(212, 212, 211, 0.59)',
}, },
info: { info: {
background: '#e8673c', background: '#ffffff',
fontFamily: 'Averta', fontFamily: 'Averta',
fontSize: '14px', fontSize: '14px',
lineHeight: 1.43, lineHeight: 1.43,
color: '#ffffff', color: '#001428',
minHeight: '58px', minHeight: '58px',
boxShadow: '0 0 10px 0 rgba(212, 212, 211, 0.59)', boxShadow: '0 0 10px 0 rgba(212, 212, 211, 0.59)',
}, },
@ -53,10 +59,17 @@ const notificationStyles = {
type Props = { type Props = {
children: React.Node, children: React.Node,
classes: Object, classes: Object,
currentNetwork: string,
} }
const PageFrame = ({ children, classes }: Props) => ( const desiredNetwork = getNetwork()
const PageFrame = ({ children, classes, currentNetwork }: Props) => {
const isWrongNetwork = currentNetwork !== ETHEREUM_NETWORK.UNKNOWN && currentNetwork !== desiredNetwork
return (
<div className={styles.frame}> <div className={styles.frame}>
<Backdrop isOpen={isWrongNetwork} />
<SnackbarProvider <SnackbarProvider
maxSnack={5} maxSnack={5}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }} anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
@ -67,10 +80,10 @@ const PageFrame = ({ children, classes }: Props) => (
variantInfo: classes.info, variantInfo: classes.info,
}} }}
iconVariant={{ iconVariant={{
success: <Img src={CheckLogo} alt="Success" />, success: <Img src={CheckIcon} alt="Success" />,
error: <Img src={ErrorLogo} alt="Error" />, error: <Img src={ErrorIcon} alt="Error" />,
warning: <Img src={AlertLogo} alt="Warning" />, warning: <Img src={AlertIcon} alt="Warning" />,
info: '', info: <Img src={InfoIcon} alt="Info" />,
}} }}
> >
<Notifier /> <Notifier />
@ -81,5 +94,13 @@ const PageFrame = ({ children, classes }: Props) => (
</SnackbarProvider> </SnackbarProvider>
</div> </div>
) )
}
export default withStyles(notificationStyles)(PageFrame) export default withStyles(notificationStyles)(
connect(
(state) => ({
currentNetwork: networkSelector(state),
}),
null,
)(PageFrame),
)

View File

@ -8,101 +8,90 @@ import closeSnackbarAction from '~/logic/notifications/store/actions/closeSnackb
import { type Notification, NOTIFICATIONS } from './notificationTypes' import { type Notification, NOTIFICATIONS } from './notificationTypes'
export type NotificationsQueue = { export type NotificationsQueue = {
beforeExecution: Notification, beforeExecution: Notification | null,
pendingExecution: { pendingExecution: Notification | null,
noMoreConfirmationsNeeded: Notification, afterExecution: {
moreConfirmationsNeeded: Notification, noMoreConfirmationsNeeded: Notification | null,
moreConfirmationsNeeded: Notification | null,
}, },
afterExecution: Notification, afterExecutionError: Notification | null,
afterExecutionError: Notification, afterRejection: Notification | null,
afterRejection: Notification,
} }
const standardTxNotificationsQueue: NotificationsQueue = { const standardTxNotificationsQueue: NotificationsQueue = {
beforeExecution: NOTIFICATIONS.SIGN_TX_MSG, beforeExecution: NOTIFICATIONS.SIGN_TX_MSG,
pendingExecution: { pendingExecution: NOTIFICATIONS.TX_PENDING_MSG,
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_PENDING_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.TX_PENDING_MORE_CONFIRMATIONS_MSG,
},
afterRejection: NOTIFICATIONS.TX_REJECTED_MSG, afterRejection: NOTIFICATIONS.TX_REJECTED_MSG,
afterExecution: NOTIFICATIONS.TX_EXECUTED_MSG, afterExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MORE_CONFIRMATIONS_MSG,
},
afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG, afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG,
} }
const confirmationTxNotificationsQueue: NotificationsQueue = { const confirmationTxNotificationsQueue: NotificationsQueue = {
beforeExecution: NOTIFICATIONS.SIGN_TX_MSG, beforeExecution: NOTIFICATIONS.SIGN_TX_MSG,
pendingExecution: { pendingExecution: NOTIFICATIONS.TX_CONFIRMATION_PENDING_MSG,
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_CONFIRMATION_PENDING_MSG, afterRejection: NOTIFICATIONS.TX_REJECTED_MSG,
afterExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_CONFIRMATION_EXECUTED_MSG,
moreConfirmationsNeeded: null, moreConfirmationsNeeded: null,
}, },
afterRejection: NOTIFICATIONS.TX_REJECTED_MSG,
afterExecution: NOTIFICATIONS.TX_CONFIRMATION_EXECUTED_MSG,
afterExecutionError: NOTIFICATIONS.TX_CONFIRMATION_FAILED_MSG, afterExecutionError: NOTIFICATIONS.TX_CONFIRMATION_FAILED_MSG,
} }
const cancellationTxNotificationsQueue: NotificationsQueue = { const cancellationTxNotificationsQueue: NotificationsQueue = {
beforeExecution: NOTIFICATIONS.SIGN_TX_MSG, beforeExecution: NOTIFICATIONS.SIGN_TX_MSG,
pendingExecution: { pendingExecution: NOTIFICATIONS.TX_PENDING_MSG,
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_PENDING_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.TX_PENDING_MORE_CONFIRMATIONS_MSG,
},
afterRejection: NOTIFICATIONS.TX_REJECTED_MSG, afterRejection: NOTIFICATIONS.TX_REJECTED_MSG,
afterExecution: NOTIFICATIONS.TX_EXECUTED_MSG, afterExecution: {
afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG, noMoreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MSG,
} moreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MORE_CONFIRMATIONS_MSG,
const ownerChangeTxNotificationsQueue: NotificationsQueue = {
beforeExecution: NOTIFICATIONS.SIGN_OWNER_CHANGE_MSG,
pendingExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.OWNER_CHANGE_PENDING_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.OWNER_CHANGE_PENDING_MORE_CONFIRMATIONS_MSG,
}, },
afterRejection: NOTIFICATIONS.OWNER_CHANGE_REJECTED_MSG, afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG,
afterExecution: NOTIFICATIONS.OWNER_CHANGE_EXECUTED_MSG,
afterExecutionError: NOTIFICATIONS.OWNER_CHANGE_FAILED_MSG,
} }
const safeNameChangeNotificationsQueue: NotificationsQueue = { const safeNameChangeNotificationsQueue: NotificationsQueue = {
beforeExecution: null, beforeExecution: null,
pendingExecution: { pendingExecution: null,
noMoreConfirmationsNeeded: null, afterRejection: null,
afterExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.SAFE_NAME_CHANGED_MSG,
moreConfirmationsNeeded: null, moreConfirmationsNeeded: null,
}, },
afterRejection: null,
afterExecution: NOTIFICATIONS.SAFE_NAME_CHANGED_MSG,
afterExecutionError: null, afterExecutionError: null,
} }
const ownerNameChangeNotificationsQueue: NotificationsQueue = { const ownerNameChangeNotificationsQueue: NotificationsQueue = {
beforeExecution: null, beforeExecution: null,
pendingExecution: { pendingExecution: null,
noMoreConfirmationsNeeded: null, afterRejection: null,
afterExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.OWNER_NAME_CHANGE_EXECUTED_MSG,
moreConfirmationsNeeded: null, moreConfirmationsNeeded: null,
}, },
afterRejection: null,
afterExecution: NOTIFICATIONS.OWNER_NAME_CHANGE_EXECUTED_MSG,
afterExecutionError: null, afterExecutionError: null,
} }
const thresholdChangeTxNotificationsQueue: NotificationsQueue = { const settingsChangeTxNotificationsQueue: NotificationsQueue = {
beforeExecution: NOTIFICATIONS.SIGN_THRESHOLD_CHANGE_MSG, beforeExecution: NOTIFICATIONS.SIGN_SETTINGS_CHANGE_MSG,
pendingExecution: { pendingExecution: NOTIFICATIONS.SETTINGS_CHANGE_PENDING_MSG,
noMoreConfirmationsNeeded: NOTIFICATIONS.THRESHOLD_CHANGE_PENDING_MSG, afterRejection: NOTIFICATIONS.SETTINGS_CHANGE_REJECTED_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.THRESHOLD_CHANGE_PENDING_MORE_CONFIRMATIONS_MSG, afterExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.SETTINGS_CHANGE_EXECUTED_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.SETTINGS_CHANGE_EXECUTED_MORE_CONFIRMATIONS_MSG,
}, },
afterRejection: NOTIFICATIONS.THRESHOLD_CHANGE_REJECTED_MSG, afterExecutionError: NOTIFICATIONS.SETTINGS_CHANGE_FAILED_MSG,
afterExecution: NOTIFICATIONS.THRESHOLD_CHANGE_EXECUTED_MSG,
afterExecutionError: NOTIFICATIONS.THRESHOLD_CHANGE_FAILED_MSG,
} }
const defaultNotificationsQueue: NotificationsQueue = { const defaultNotificationsQueue: NotificationsQueue = {
beforeExecution: NOTIFICATIONS.SIGN_TX_MSG, beforeExecution: NOTIFICATIONS.SIGN_TX_MSG,
pendingExecution: { pendingExecution: NOTIFICATIONS.TX_PENDING_MSG,
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_PENDING_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.TX_PENDING_MORE_CONFIRMATIONS_MSG,
},
afterRejection: NOTIFICATIONS.TX_REJECTED_MSG, afterRejection: NOTIFICATIONS.TX_REJECTED_MSG,
afterExecution: NOTIFICATIONS.TX_EXECUTED_MSG, afterExecution: {
noMoreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MSG,
moreConfirmationsNeeded: NOTIFICATIONS.TX_EXECUTED_MORE_CONFIRMATIONS_MSG,
},
afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG, afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG,
} }
@ -122,8 +111,8 @@ export const getNotificationsFromTxType = (txType: string) => {
notificationsQueue = cancellationTxNotificationsQueue notificationsQueue = cancellationTxNotificationsQueue
break break
} }
case TX_NOTIFICATION_TYPES.OWNER_CHANGE_TX: { case TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX: {
notificationsQueue = ownerChangeTxNotificationsQueue notificationsQueue = settingsChangeTxNotificationsQueue
break break
} }
case TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX: { case TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX: {
@ -134,10 +123,6 @@ export const getNotificationsFromTxType = (txType: string) => {
notificationsQueue = ownerNameChangeNotificationsQueue notificationsQueue = ownerNameChangeNotificationsQueue
break break
} }
case TX_NOTIFICATION_TYPES.THRESHOLD_CHANGE_TX: {
notificationsQueue = thresholdChangeTxNotificationsQueue
break
}
default: { default: {
notificationsQueue = defaultNotificationsQueue notificationsQueue = defaultNotificationsQueue
break break
@ -151,8 +136,8 @@ export const enhanceSnackbarForAction = (notification: Notification) => ({
...notification, ...notification,
options: { options: {
...notification.options, ...notification.options,
action: (key) => ( action: (key: number) => (
<IconButton onClick={() => store.dispatch(closeSnackbarAction(key))}> <IconButton onClick={() => store.dispatch(closeSnackbarAction({ key }))}>
<IconClose /> <IconClose />
</IconButton> </IconButton>
), ),

View File

@ -34,9 +34,9 @@ export type Notifications = {
// Regular/Custom Transactions // Regular/Custom Transactions
SIGN_TX_MSG: Notification, SIGN_TX_MSG: Notification,
TX_PENDING_MSG: Notification, TX_PENDING_MSG: Notification,
TX_PENDING_MORE_CONFIRMATIONS_MSG: Notification,
TX_REJECTED_MSG: Notification, TX_REJECTED_MSG: Notification,
TX_EXECUTED_MSG: Notification, TX_EXECUTED_MSG: Notification,
TX_EXECUTED_MORE_CONFIRMATIONS_MSG: Notification,
TX_FAILED_MSG: Notification, TX_FAILED_MSG: Notification,
// Approval Transactions // Approval Transactions
@ -51,20 +51,11 @@ export type Notifications = {
OWNER_NAME_CHANGE_EXECUTED_MSG: Notification, OWNER_NAME_CHANGE_EXECUTED_MSG: Notification,
// Owners // Owners
SIGN_OWNER_CHANGE_MSG: Notification, SIGN_SETTINGS_CHANGE_MSG: Notification,
OWNER_CHANGE_PENDING_MSG: Notification, SETTINGS_CHANGE_PENDING_MSG: Notification,
OWNER_CHANGE_PENDING_MORE_CONFIRMATIONS_MSG: Notification, SETTINGS_CHANGE_REJECTED_MSG: Notification,
OWNER_CHANGE_REJECTED_MSG: Notification, SETTINGS_CHANGE_EXECUTED_MORE_CONFIRMATIONS_MSG: Notification,
OWNER_CHANGE_EXECUTED_MSG: Notification, SETTINGS_CHANGE_FAILED_MSG: Notification,
OWNER_CHANGE_FAILED_MSG: Notification,
// Threshold
SIGN_THRESHOLD_CHANGE_MSG: Notification,
THRESHOLD_CHANGE_PENDING_MSG: Notification,
THRESHOLD_CHANGE_PENDING_MORE_CONFIRMATIONS_MSG: Notification,
THRESHOLD_CHANGE_REJECTED_MSG: Notification,
THRESHOLD_CHANGE_EXECUTED_MSG: Notification,
THRESHOLD_CHANGE_FAILED_MSG: Notification,
// Rinkeby version // Rinkeby version
RINKEBY_VERSION_MSG: Notification, RINKEBY_VERSION_MSG: Notification,
@ -109,15 +100,11 @@ export const NOTIFICATIONS: Notifications = {
// Regular/Custom Transactions // Regular/Custom Transactions
SIGN_TX_MSG: { SIGN_TX_MSG: {
message: 'Please sign the transaction', message: 'Please sign the transaction',
options: { variant: SUCCESS, persist: true }, options: { variant: INFO, persist: true },
}, },
TX_PENDING_MSG: { TX_PENDING_MSG: {
message: 'Transaction pending', message: 'Transaction pending',
options: { variant: SUCCESS, persist: true }, options: { variant: INFO, persist: true },
},
TX_PENDING_MORE_CONFIRMATIONS_MSG: {
message: 'Transaction pending: More confirmations required to execute',
options: { variant: SUCCESS, persist: true },
}, },
TX_REJECTED_MSG: { TX_REJECTED_MSG: {
message: 'Transaction rejected', message: 'Transaction rejected',
@ -127,6 +114,10 @@ export const NOTIFICATIONS: Notifications = {
message: 'Transaction successfully executed', message: 'Transaction successfully executed',
options: { variant: SUCCESS, persist: false, autoHideDuration: longDuration }, options: { variant: SUCCESS, persist: false, autoHideDuration: longDuration },
}, },
TX_EXECUTED_MORE_CONFIRMATIONS_MSG: {
message: 'Transaction successfully created. More confirmations needed to execute',
options: { variant: SUCCESS, persist: false, autoHideDuration: longDuration },
},
TX_FAILED_MSG: { TX_FAILED_MSG: {
message: 'Transaction failed', message: 'Transaction failed',
options: { variant: ERROR, persist: false, autoHideDuration: longDuration }, options: { variant: ERROR, persist: false, autoHideDuration: longDuration },
@ -135,7 +126,7 @@ export const NOTIFICATIONS: Notifications = {
// Approval Transactions // Approval Transactions
TX_CONFIRMATION_PENDING_MSG: { TX_CONFIRMATION_PENDING_MSG: {
message: 'Confirmation transaction pending', message: 'Confirmation transaction pending',
options: { variant: SUCCESS, persist: true }, options: { variant: INFO, persist: true },
}, },
TX_CONFIRMATION_EXECUTED_MSG: { TX_CONFIRMATION_EXECUTED_MSG: {
message: 'Confirmation transaction succesful', message: 'Confirmation transaction succesful',
@ -158,62 +149,36 @@ export const NOTIFICATIONS: Notifications = {
options: { variant: SUCCESS, persist: false, autoHideDuration: shortDuration }, options: { variant: SUCCESS, persist: false, autoHideDuration: shortDuration },
}, },
// Owners // Settings
SIGN_OWNER_CHANGE_MSG: { SIGN_SETTINGS_CHANGE_MSG: {
message: 'Please sign the owner change', message: 'Please sign the settings change',
options: { variant: SUCCESS, persist: true }, options: { variant: INFO, persist: true },
}, },
OWNER_CHANGE_PENDING_MSG: { SETTINGS_CHANGE_PENDING_MSG: {
message: 'Owner change pending', message: 'Settings change pending',
options: { variant: SUCCESS, persist: true }, options: { variant: INFO, persist: true },
}, },
OWNER_CHANGE_PENDING_MORE_CONFIRMATIONS_MSG: { SETTINGS_CHANGE_REJECTED_MSG: {
message: 'Owner change pending: More confirmations required to execute', message: 'Settings change rejected',
options: { variant: SUCCESS, persist: true },
},
OWNER_CHANGE_REJECTED_MSG: {
message: 'Owner change rejected',
options: { variant: ERROR, persist: false, autoHideDuration: longDuration }, options: { variant: ERROR, persist: false, autoHideDuration: longDuration },
}, },
OWNER_CHANGE_EXECUTED_MSG: { SETTINGS_CHANGE_EXECUTED_MSG: {
message: 'Owner change successfully executed', message: 'Settings change successfully executed',
options: { variant: SUCCESS, persist: false, autoHideDuration: longDuration }, options: { variant: SUCCESS, persist: false, autoHideDuration: longDuration },
}, },
OWNER_CHANGE_FAILED_MSG: { SETTINGS_CHANGE_EXECUTED_MORE_CONFIRMATIONS_MSG: {
message: 'Owner change failed', message: 'Settings change successfully created. More confirmations needed to execute',
options: { variant: ERROR, persist: false, autoHideDuration: longDuration },
},
// Threshold
SIGN_THRESHOLD_CHANGE_MSG: {
message: 'Please sign the required confirmations change',
options: { variant: SUCCESS, persist: true },
},
THRESHOLD_CHANGE_PENDING_MSG: {
message: 'Required confirmations change pending',
options: { variant: SUCCESS, persist: true },
},
THRESHOLD_CHANGE_PENDING_MORE_CONFIRMATIONS_MSG: {
message: 'Required confirmations change pending: More confirmations required to execute',
options: { variant: SUCCESS, persist: true },
},
THRESHOLD_CHANGE_REJECTED_MSG: {
message: 'Required confirmations change rejected',
options: { variant: ERROR, persist: false, autoHideDuration: longDuration },
},
THRESHOLD_CHANGE_EXECUTED_MSG: {
message: 'Required confirmations change successfully executed',
options: { variant: SUCCESS, persist: false, autoHideDuration: longDuration }, options: { variant: SUCCESS, persist: false, autoHideDuration: longDuration },
}, },
THRESHOLD_CHANGE_FAILED_MSG: { SETTINGS_CHANGE_FAILED_MSG: {
message: 'Required confirmations change failed', message: 'Settings change failed',
options: { variant: ERROR, persist: false, autoHideDuration: longDuration }, options: { variant: ERROR, persist: false, autoHideDuration: longDuration },
}, },
// Network // Network
RINKEBY_VERSION_MSG: { RINKEBY_VERSION_MSG: {
message: "Rinkeby Version: Don't send Mainnet assets to this Safe", message: "Rinkeby Version: Don't send Mainnet assets to this Safe",
options: { variant: INFO, persist: true, preventDuplicate: true }, options: { variant: WARNING, persist: true, preventDuplicate: true },
}, },
WRONG_NETWORK_MSG: { WRONG_NETWORK_MSG: {
message: `Wrong network: Please use ${capitalize(getNetwork())}`, message: `Wrong network: Please use ${capitalize(getNetwork())}`,

View File

@ -18,9 +18,20 @@ export default handleActions<NotificationReducerState, *>(
return state.set(notification.key, makeNotification(notification)) return state.set(notification.key, makeNotification(notification))
}, },
[CLOSE_SNACKBAR]: (state: NotificationReducerState, action: ActionType<Function>): NotificationReducerState => { [CLOSE_SNACKBAR]: (state: NotificationReducerState, action: ActionType<Function>): NotificationReducerState => {
const key = action.payload const { key, dismissAll } = action.payload
if (key) {
return state.update(key, (prev) => prev.set('dismissed', true)) return state.update(key, (prev) => prev.set('dismissed', true))
}
if (dismissAll) {
return state.withMutations((map) => {
map.forEach((notification, notificationKey) => {
map.set(notificationKey, notification.set('dismissed', true))
})
})
}
return state
}, },
[REMOVE_SNACKBAR]: (state: NotificationReducerState, action: ActionType<Function>): NotificationReducerState => { [REMOVE_SNACKBAR]: (state: NotificationReducerState, action: ActionType<Function>): NotificationReducerState => {
const key = action.payload const key = action.payload

View File

@ -1,71 +0,0 @@
// @flow
import { List } from 'immutable'
import { type Transaction } from '~/routes/safe/store/models/transaction'
import { executeTransaction, approveTransaction } from '~/logic/safe/transactions'
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
import { getWeb3 } from '~/logic/wallets/getWeb3'
import { type Safe } from '~/routes/safe/store/models/safe'
import { storeSubject } from '~/utils/storage/transactions'
export const TX_NAME_PARAM = 'txName'
export const TX_DESTINATION_PARAM = 'txDestination'
export const TX_VALUE_PARAM = 'txValue'
export const EXECUTED_CONFIRMATION_HASH = 'EXECUTED'
const hasOneOwner = (safe: Safe) => {
const owners = safe.get('owners')
if (!owners) {
throw new Error('Received a Safe without owners when creating a tx')
}
return owners.count() === 1
}
export const createTransaction = async (
safe: Safe,
name: string,
to: string,
value: string,
nonce: number,
sender: string,
data: string = EMPTY_DATA,
) => {
const web3 = getWeb3()
const safeAddress = safe.get('address')
const threshold = safe.get('threshold')
const valueInWei = web3.utils.toWei(value, 'ether')
const CALL = 0
const isExecution = hasOneOwner(safe) || threshold === 1
const txHash = isExecution
? await executeTransaction(safeAddress, to, valueInWei, data, CALL, nonce, sender, List([]))
: await approveTransaction(safeAddress, to, valueInWei, data, CALL, nonce, sender)
storeSubject(safeAddress, nonce, name)
return txHash
}
export const processTransaction = async (
safeAddress: string,
tx: Transaction,
alreadyConfirmed: number,
sender: string,
threshold: number,
usersConfirmed: List<string>,
) => {
const nonce = tx.get('nonce')
const valueInWei = tx.get('value')
const to = tx.get('destination')
const data = tx.get('data')
const CALL = 0
const thresholdReached = threshold === alreadyConfirmed + 1
const txHash = thresholdReached
? await executeTransaction(safeAddress, to, valueInWei, data, CALL, nonce, sender, usersConfirmed)
: await approveTransaction(safeAddress, to, valueInWei, data, CALL, nonce, sender)
return txHash
}

View File

@ -4,18 +4,16 @@ export type NotifiedTransaction = {
STANDARD_TX: string, STANDARD_TX: string,
CONFIRMATION_TX: string, CONFIRMATION_TX: string,
CANCELLATION_TX: string, CANCELLATION_TX: string,
OWNER_CHANGE_TX: string, SETTINGS_CHANGE_TX: string,
SAFE_NAME_CHANGE_TX: string, SAFE_NAME_CHANGE_TX: string,
OWNER_NAME_CHANGE_TX: string, OWNER_NAME_CHANGE_TX: string,
THRESHOLD_CHANGE_TX: string,
} }
export const TX_NOTIFICATION_TYPES: NotifiedTransaction = { export const TX_NOTIFICATION_TYPES: NotifiedTransaction = {
STANDARD_TX: 'STANDARD_TX', STANDARD_TX: 'STANDARD_TX',
CONFIRMATION_TX: 'CONFIRMATION_TX', CONFIRMATION_TX: 'CONFIRMATION_TX',
CANCELLATION_TX: 'CANCELLATION_TX', CANCELLATION_TX: 'CANCELLATION_TX',
OWNER_CHANGE_TX: 'OWNER_CHANGE_TX', SETTINGS_CHANGE_TX: 'SETTINGS_CHANGE_TX',
SAFE_NAME_CHANGE_TX: 'SAFE_NAME_CHANGE_TX', SAFE_NAME_CHANGE_TX: 'SAFE_NAME_CHANGE_TX',
OWNER_NAME_CHANGE_TX: 'OWNER_NAME_CHANGE_TX', OWNER_NAME_CHANGE_TX: 'OWNER_NAME_CHANGE_TX',
THRESHOLD_CHANGE_TX: 'THRESHOLD_CHANGE_TX',
} }

View File

@ -4,9 +4,8 @@ import { ETHEREUM_NETWORK_IDS, ETHEREUM_NETWORK, getProviderInfo } from '~/logic
import { getNetwork } from '~/config' import { getNetwork } from '~/config'
import type { ProviderProps } from '~/logic/wallets/store/model/provider' import type { ProviderProps } from '~/logic/wallets/store/model/provider'
import { makeProvider } from '~/logic/wallets/store/model/provider' import { makeProvider } from '~/logic/wallets/store/model/provider'
import { NOTIFICATIONS, showSnackbar, enhanceSnackbarForAction } from '~/logic/notifications' import { NOTIFICATIONS, enhanceSnackbarForAction } from '~/logic/notifications'
import enqueueSnackbar from '~/logic/notifications/store/actions/enqueueSnackbar' import enqueueSnackbar from '~/logic/notifications/store/actions/enqueueSnackbar'
import closeSnackbar from '~/logic/notifications/store/actions/closeSnackbar'
import addProvider from './addProvider' import addProvider from './addProvider'
@ -35,7 +34,7 @@ const handleProviderNotification = (provider: ProviderProps, dispatch: Function)
} }
if (ETHEREUM_NETWORK_IDS[network] !== getNetwork()) { if (ETHEREUM_NETWORK_IDS[network] !== getNetwork()) {
dispatch(enqueueSnackbar(enhanceSnackbarForAction(NOTIFICATIONS.WRONG_NETWORK_MSG))) dispatch(enqueueSnackbar(NOTIFICATIONS.WRONG_NETWORK_MSG))
return return
} }
if (ETHEREUM_NETWORK.RINKEBY === getNetwork()) { if (ETHEREUM_NETWORK.RINKEBY === getNetwork()) {

View File

@ -2,9 +2,10 @@
import type { Store, AnyAction } from 'redux' import type { Store, AnyAction } from 'redux'
import { type GlobalState } from '~/store/' import { type GlobalState } from '~/store/'
import { ADD_PROVIDER, REMOVE_PROVIDER } from '../actions' import { ADD_PROVIDER, REMOVE_PROVIDER } from '../actions'
import { getWeb3, getProviderInfo } from '~/logic/wallets/getWeb3' import { getWeb3, getProviderInfo, WALLET_PROVIDER } from '~/logic/wallets/getWeb3'
import { fetchProvider } from '~/logic/wallets/store/actions' import { fetchProvider } from '~/logic/wallets/store/actions'
import { loadFromStorage, saveToStorage, removeFromStorage } from '~/utils/storage' import { loadFromStorage, saveToStorage, removeFromStorage } from '~/utils/storage'
import closeSnackbar from '~/logic/notifications/store/actions/closeSnackbar'
const watchedActions = [ADD_PROVIDER, REMOVE_PROVIDER] const watchedActions = [ADD_PROVIDER, REMOVE_PROVIDER]
@ -30,17 +31,24 @@ const providerWatcherMware = (store: Store<GlobalState>) => (next: Function) =>
clearInterval(watcherInterval) clearInterval(watcherInterval)
} }
if (currentProviderProps.name === WALLET_PROVIDER.METAMASK && window.ethereum) {
window.ethereum.autoRefreshOnNetworkChange = false
}
saveToStorage(LAST_USED_PROVIDER_KEY, currentProviderProps.name) saveToStorage(LAST_USED_PROVIDER_KEY, currentProviderProps.name)
watcherInterval = setInterval(async () => { watcherInterval = setInterval(async () => {
const web3 = getWeb3() const web3 = getWeb3()
const providerInfo = await getProviderInfo(web3) const providerInfo = await getProviderInfo(web3)
if ( const networkChanged = currentProviderProps.network !== providerInfo.network
currentProviderProps.account !== providerInfo.account
|| currentProviderProps.network !== providerInfo.network if (networkChanged) {
) { store.dispatch(closeSnackbar({ dismissAll: true }))
store.dispatch(fetchProvider(web3, () => {}, () => {})) }
if (currentProviderProps.account !== providerInfo.account || networkChanged) {
store.dispatch(fetchProvider(web3))
} }
}, 2000) }, 2000)

View File

@ -37,7 +37,7 @@ const ChangeSafeName = (props: Props) => {
updateSafe({ address: safeAddress, name: values.safeName }) updateSafe({ address: safeAddress, name: values.safeName })
const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX) const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX)
showSnackbar(notification.afterExecution, enqueueSnackbar, closeSnackbar) showSnackbar(notification.afterExecution.noMoreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
} }
return ( return (

View File

@ -52,7 +52,7 @@ export const sendAddOwner = async (
safeAddress, safeAddress,
0, 0,
txData, txData,
TX_NOTIFICATION_TYPES.OWNER_CHANGE_TX, TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
enqueueSnackbar, enqueueSnackbar,
closeSnackbar, closeSnackbar,
) )

View File

@ -61,7 +61,7 @@ const ThresholdForm = ({
</Row> </Row>
<Row> <Row>
<Paragraph weight="bolder"> <Paragraph weight="bolder">
Any transaction over any daily limit requires the confirmation of: Any transaction requires the confirmation of:
</Paragraph> </Paragraph>
</Row> </Row>
<Row margin="xl" align="center" className={classes.inputRow}> <Row margin="xl" align="center" className={classes.inputRow}>

View File

@ -52,7 +52,7 @@ const EditOwnerComponent = ({
editSafeOwner({ safeAddress, ownerAddress, ownerName: values.ownerName }) editSafeOwner({ safeAddress, ownerAddress, ownerName: values.ownerName })
const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.OWNER_NAME_CHANGE_TX) const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.OWNER_NAME_CHANGE_TX)
showSnackbar(notification.afterExecution, enqueueSnackbar, closeSnackbar) showSnackbar(notification.afterExecution.noMoreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
onClose() onClose()
} }

View File

@ -64,7 +64,7 @@ export const sendRemoveOwner = async (
safeAddress, safeAddress,
0, 0,
txData, txData,
TX_NOTIFICATION_TYPES.OWNER_CHANGE_TX, TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
enqueueSnackbar, enqueueSnackbar,
closeSnackbar, closeSnackbar,
) )

View File

@ -65,7 +65,7 @@ const ThresholdForm = ({
</Row> </Row>
<Row> <Row>
<Paragraph weight="bolder"> <Paragraph weight="bolder">
Any transaction over any daily limit requires the confirmation of: Any transaction requires the confirmation of:
</Paragraph> </Paragraph>
</Row> </Row>
<Row margin="xl" align="center" className={classes.inputRow}> <Row margin="xl" align="center" className={classes.inputRow}>

View File

@ -60,7 +60,7 @@ export const sendReplaceOwner = async (
safeAddress, safeAddress,
0, 0,
txData, txData,
TX_NOTIFICATION_TYPES.OWNER_CHANGE_TX, TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
enqueueSnackbar, enqueueSnackbar,
closeSnackbar, closeSnackbar,
) )

View File

@ -162,7 +162,6 @@ class ManageOwners extends React.Component<Props, State> {
</TableCell> </TableCell>
))} ))}
<TableCell component="td"> <TableCell component="td">
{granted && (
<Row align="end" className={classes.actions}> <Row align="end" className={classes.actions}>
<Img <Img
alt="Edit owner" alt="Edit owner"
@ -171,6 +170,8 @@ class ManageOwners extends React.Component<Props, State> {
onClick={this.onShow('EditOwner', row)} onClick={this.onShow('EditOwner', row)}
testId={RENAME_OWNER_BTN_TEST_ID} testId={RENAME_OWNER_BTN_TEST_ID}
/> />
{granted && (
<>
<Img <Img
alt="Replace owner" alt="Replace owner"
className={classes.replaceOwnerIcon} className={classes.replaceOwnerIcon}
@ -187,8 +188,9 @@ class ManageOwners extends React.Component<Props, State> {
testId={REMOVE_OWNER_BTN_TEST_ID} testId={REMOVE_OWNER_BTN_TEST_ID}
/> />
)} )}
</Row> </>
) } ) }
</Row>
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}

View File

@ -85,12 +85,6 @@ const ChangeThreshold = ({
{() => ( {() => (
<> <>
<Block className={classes.modalContent}> <Block className={classes.modalContent}>
<Row>
<Paragraph>
Every transaction outside any specified daily limits, needs to be confirmed by all specified owners.
If no daily limits are set, all owners will need to sign for transactions.
</Paragraph>
</Row>
<Row> <Row>
<Paragraph weight="bolder">Any transaction requires the confirmation of:</Paragraph> <Paragraph weight="bolder">Any transaction requires the confirmation of:</Paragraph>
</Row> </Row>

View File

@ -52,7 +52,7 @@ const ThresholdSettings = ({
safeAddress, safeAddress,
0, 0,
txData, txData,
TX_NOTIFICATION_TYPES.THRESHOLD_CHANGE_TX, TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
enqueueSnackbar, enqueueSnackbar,
closeSnackbar, closeSnackbar,
) )

View File

@ -15,12 +15,7 @@ import {
TX_TYPE_EXECUTION, TX_TYPE_EXECUTION,
saveTxToHistory, saveTxToHistory,
} from '~/logic/safe/transactions' } from '~/logic/safe/transactions'
import { import { type NotificationsQueue, getNotificationsFromTxType, showSnackbar } from '~/logic/notifications'
type Notification,
type NotificationsQueue,
getNotificationsFromTxType,
showSnackbar,
} from '~/logic/notifications'
import { getErrorMessage } from '~/test/utils/ethereumErrors' import { getErrorMessage } from '~/test/utils/ethereumErrors'
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
import { SAFELIST_ADDRESS } from '~/routes/routes' import { SAFELIST_ADDRESS } from '~/routes/routes'
@ -75,16 +70,8 @@ const createTransaction = (
.once('transactionHash', async (hash) => { .once('transactionHash', async (hash) => {
txHash = hash txHash = hash
closeSnackbar(beforeExecutionKey) closeSnackbar(beforeExecutionKey)
const pendingExecutionNotification: Notification = isExecution
? { pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
message: notificationsQueue.pendingExecution.noMoreConfirmationsNeeded.message,
options: notificationsQueue.pendingExecution.noMoreConfirmationsNeeded.options,
}
: {
message: notificationsQueue.pendingExecution.moreConfirmationsNeeded.message,
options: notificationsQueue.pendingExecution.moreConfirmationsNeeded.options,
}
pendingExecutionKey = showSnackbar(pendingExecutionNotification, enqueueSnackbar, closeSnackbar)
try { try {
await saveTxToHistory( await saveTxToHistory(
@ -108,9 +95,14 @@ const createTransaction = (
.then((receipt) => { .then((receipt) => {
closeSnackbar(pendingExecutionKey) closeSnackbar(pendingExecutionKey)
if (isExecution) { showSnackbar(
showSnackbar(notificationsQueue.afterExecution, enqueueSnackbar, closeSnackbar) isExecution
} ? notificationsQueue.afterExecution.noMoreConfirmationsNeeded
: notificationsQueue.afterExecution.moreConfirmationsNeeded,
enqueueSnackbar,
closeSnackbar,
)
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
return receipt.transactionHash return receipt.transactionHash

View File

@ -17,7 +17,6 @@ import {
TX_TYPE_CONFIRMATION, TX_TYPE_CONFIRMATION,
} from '~/logic/safe/transactions' } from '~/logic/safe/transactions'
import { import {
type Notification,
type NotificationsQueue, type NotificationsQueue,
getNotificationsFromTxType, getNotificationsFromTxType,
showSnackbar, showSnackbar,
@ -107,11 +106,8 @@ const processTransaction = (
.once('transactionHash', async (hash) => { .once('transactionHash', async (hash) => {
txHash = hash txHash = hash
closeSnackbar(beforeExecutionKey) closeSnackbar(beforeExecutionKey)
const notification: Notification = {
message: notificationsQueue.pendingExecution.noMoreConfirmationsNeeded.message, pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
options: notificationsQueue.pendingExecution.noMoreConfirmationsNeeded.options,
}
pendingExecutionKey = showSnackbar(notification, enqueueSnackbar, closeSnackbar)
try { try {
await saveTxToHistory( await saveTxToHistory(
@ -135,7 +131,13 @@ const processTransaction = (
.then((receipt) => { .then((receipt) => {
closeSnackbar(pendingExecutionKey) closeSnackbar(pendingExecutionKey)
showSnackbar(notificationsQueue.afterExecution, enqueueSnackbar, closeSnackbar) showSnackbar(
shouldExecute
? notificationsQueue.afterExecution.noMoreConfirmationsNeeded
: notificationsQueue.afterExecution.moreConfirmationsNeeded,
enqueueSnackbar,
closeSnackbar,
)
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
return receipt.transactionHash return receipt.transactionHash

1941
yarn.lock

File diff suppressed because it is too large Load Diff