diff --git a/readme.md b/readme.md
index bb883ae2..9746885e 100644
--- a/readme.md
+++ b/readme.md
@@ -102,9 +102,7 @@ We use [SemVer](http://semver.org/) for versioning. For the versions available,
## Authors
-* **Adolfo Panizo** - [apanizo](https://github.com/apanizo)
-
-See also the list of [contributors](https://github.com/gnosis/gnosis-team-safe/contributors) who participated in this project.
+See the list of [contributors](https://github.com/gnosis/gnosis-team-safe/contributors) who participated in this project.
## License
diff --git a/src/components/Footer/index.stories.js b/src/components/Footer/index.stories.js
index f3220920..e0b29e91 100644
--- a/src/components/Footer/index.stories.js
+++ b/src/components/Footer/index.stories.js
@@ -4,7 +4,7 @@ import * as React from 'react'
import styles from '~/components/layout/PageFrame/index.scss'
import Component from './index'
-const FrameDecorator = story => (
+const FrameDecorator = (story) => (
{story()}
diff --git a/src/components/Header/component/CircleDot.jsx b/src/components/Header/component/CircleDot.jsx
index 32ff7f54..7c54d1e2 100644
--- a/src/components/Header/component/CircleDot.jsx
+++ b/src/components/Header/component/CircleDot.jsx
@@ -77,7 +77,7 @@ const KeyRing = ({
const img = isWarning ? triangle : key
return (
-
+ <>
{!hideDot && }
-
+ >
)
}
diff --git a/src/components/Header/component/Layout.stories.js b/src/components/Header/component/Layout.stories.js
index 7d0ccbe3..e8b235b7 100644
--- a/src/components/Header/component/Layout.stories.js
+++ b/src/components/Header/component/Layout.stories.js
@@ -8,7 +8,7 @@ import UserDetails from './ProviderDetails/UserDetails'
import ProviderDisconnected from './ProviderInfo/ProviderDisconnected'
import ConnectDetails from './ProviderDetails/ConnectDetails'
-const FrameDecorator = story =>
{story()}
+const FrameDecorator = (story) =>
{story()}
storiesOf('Components /Header', module)
.addDecorator(FrameDecorator)
diff --git a/src/components/Loader/indes.stories.js b/src/components/Loader/indes.stories.js
index f6576f3b..314b33d1 100644
--- a/src/components/Loader/indes.stories.js
+++ b/src/components/Loader/indes.stories.js
@@ -4,7 +4,7 @@ import * as React from 'react'
import styles from '~/components/layout/PageFrame/index.scss'
import Component from './index'
-const FrameDecorator = story =>
{story()}
+const FrameDecorator = (story) =>
{story()}
storiesOf('Components', module)
.addDecorator(FrameDecorator)
diff --git a/src/components/Table/sorting.js b/src/components/Table/sorting.js
index aad73260..e53480b4 100644
--- a/src/components/Table/sorting.js
+++ b/src/components/Table/sorting.js
@@ -39,7 +39,7 @@ export const stableSort = (dataArray: List
, cmp: any, fixed: boolean): List
return a[1] - b[1]
})
- const sortedElems: List = stabilizedThis.map(el => el[0])
+ const sortedElems: List = stabilizedThis.map((el) => el[0])
return fixedElems.concat(sortedElems)
}
diff --git a/src/components/forms/AddressInput/index.jsx b/src/components/forms/AddressInput/index.jsx
index 71217c95..1ad3ea3a 100644
--- a/src/components/forms/AddressInput/index.jsx
+++ b/src/components/forms/AddressInput/index.jsx
@@ -7,6 +7,7 @@ import {
composeValidators,
required,
mustBeEthereumAddress,
+ mustBeEthereumContractAddress,
} from '~/components/forms/validator'
import { getAddressFromENS } from '~/logic/wallets/getWeb3'
@@ -19,6 +20,7 @@ type Props = {
testId?: string,
validators?: Function[],
inputAdornment?: React.Element,
+ mustBeContract?: boolean,
}
const isValidEnsName = (name) => /^([\w-]+\.)+(eth|test|xyz|luxe)$/.test(name)
@@ -35,6 +37,7 @@ const AddressInput = ({
testId,
inputAdornment,
validators = [],
+ mustBeContract,
}: Props): React.Element<*> => (
<>
{
inputAdornment,
classes,
testId,
+ rows,
+ multiline,
...rest
} = this.props
const helperText = value ? text : undefined
@@ -53,6 +55,8 @@ class TextField extends React.PureComponent {
onChange={onChange}
value={value}
// data-testid={testId}
+ rows={rows}
+ multiline={multiline}
/>
)
}
diff --git a/src/components/forms/TextareaField/index.jsx b/src/components/forms/TextareaField/index.jsx
new file mode 100644
index 00000000..741b793d
--- /dev/null
+++ b/src/components/forms/TextareaField/index.jsx
@@ -0,0 +1,35 @@
+// @flow
+import React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import { TextFieldProps } from '@material-ui/core/TextField'
+import Field from '~/components/forms/Field'
+import TextField from '~/components/forms/TextField'
+
+const styles = () => ({
+ textarea: {
+ '& > div': {
+ height: '140px',
+ paddingTop: '0',
+ paddingBottom: '0',
+ alignItems: 'auto',
+ '& > textarea': {
+ fontSize: '15px',
+ letterSpacing: '-0.5px',
+ lineHeight: '20px',
+ height: '102px',
+ },
+ },
+ },
+})
+
+const TextareaField = ({ classes, ...props }: TextFieldProps) => (
+
+)
+
+export default withStyles(styles)(TextareaField)
diff --git a/src/components/forms/validator.js b/src/components/forms/validator.js
index e41ff0ef..f8bfda94 100644
--- a/src/components/forms/validator.js
+++ b/src/components/forms/validator.js
@@ -22,7 +22,7 @@ export const required = simpleMemoize((value: Field) => (value ? undefined : 'Re
export const mustBeInteger = (value: string) => (!Number.isInteger(Number(value)) || value.includes('.') ? 'Must be an integer' : undefined)
-export const mustBeFloat = (value: number) => (Number.isNaN(Number(value)) ? 'Must be a number' : undefined)
+export const mustBeFloat = (value: number) => (value && Number.isNaN(Number(value)) ? 'Must be a number' : undefined)
export const greaterThan = (min: number) => (value: string) => {
if (Number.isNaN(Number(value)) || Number.parseFloat(value) > Number(min)) {
@@ -66,6 +66,14 @@ export const mustBeEthereumAddress = simpleMemoize((address: Field) => {
return isAddress ? undefined : 'Address should be a valid Ethereum address or ENS name'
})
+export const mustBeEthereumContractAddress = simpleMemoize(async (address: string) => {
+ const contractCode: string = await getWeb3().eth.getCode(address)
+
+ return !contractCode || contractCode.replace('0x', '').replace(/0/g, '') === ''
+ ? 'Address should be a valid Ethereum contract address or ENS name'
+ : undefined
+})
+
export const minMaxLength = (minLen: string | number, maxLen: string | number) => (value: string) => (value.length >= +minLen && value.length <= +maxLen ? undefined : `Should be ${minLen} to ${maxLen} symbols`)
export const ADDRESS_REPEATED_ERROR = 'Address already introduced'
@@ -75,7 +83,7 @@ export const uniqueAddress = (addresses: string[] | List) => simpleMemoi
return addressAlreadyExists ? ADDRESS_REPEATED_ERROR : undefined
})
-export const composeValidators = (...validators: Function[]): FieldValidator => (value: Field) => validators.reduce((error, validator) => error || validator(value), undefined)
+export const composeValidators = (...validators: Function[]): FieldValidator => (value: Field) => validators.reduce((error, validator) => (error || (validator && validator(value)), undefined))
export const inLimit = (limit: number, base: number, baseText: string, symbol: string = 'ETH') => (value: string) => {
const amount = Number(value)
diff --git a/src/config/development.js b/src/config/development.js
index 97867457..02736a84 100644
--- a/src/config/development.js
+++ b/src/config/development.js
@@ -1,9 +1,5 @@
// @flow
-import {
- TX_SERVICE_HOST,
- SIGNATURES_VIA_METAMASK,
- RELAY_API_URL,
-} from '~/config/names'
+import { TX_SERVICE_HOST, SIGNATURES_VIA_METAMASK, RELAY_API_URL } from '~/config/names'
const devConfig = {
[TX_SERVICE_HOST]: 'https://safe-transaction.staging.gnosisdev.com/api/v1/',
diff --git a/src/config/index.js b/src/config/index.js
index 5eebb734..0b7e8db7 100644
--- a/src/config/index.js
+++ b/src/config/index.js
@@ -1,10 +1,6 @@
// @flow
import { ensureOnce } from '~/utils/singleton'
-import {
- TX_SERVICE_HOST,
- SIGNATURES_VIA_METAMASK,
- RELAY_API_URL,
-} from '~/config/names'
+import { TX_SERVICE_HOST, SIGNATURES_VIA_METAMASK, RELAY_API_URL } from '~/config/names'
import devConfig from './development'
import testConfig from './testing'
import prodConfig from './production'
diff --git a/src/config/production.js b/src/config/production.js
index 5a0eab77..fd923f3c 100644
--- a/src/config/production.js
+++ b/src/config/production.js
@@ -1,9 +1,5 @@
// @flow
-import {
- TX_SERVICE_HOST,
- SIGNATURES_VIA_METAMASK,
- RELAY_API_URL,
-} from '~/config/names'
+import { TX_SERVICE_HOST, SIGNATURES_VIA_METAMASK, RELAY_API_URL } from '~/config/names'
const prodConfig = {
[TX_SERVICE_HOST]: 'https://safe-transaction.staging.gnosisdev.com/api/v1/',
diff --git a/src/config/testing.js b/src/config/testing.js
index a43f6f8e..501db9de 100644
--- a/src/config/testing.js
+++ b/src/config/testing.js
@@ -1,9 +1,5 @@
// @flow
-import {
- TX_SERVICE_HOST,
- SIGNATURES_VIA_METAMASK,
- RELAY_API_URL,
-} from '~/config/names'
+import { TX_SERVICE_HOST, SIGNATURES_VIA_METAMASK, RELAY_API_URL } from '~/config/names'
const testConfig = {
[TX_SERVICE_HOST]: 'http://localhost:8000/api/v1/',
diff --git a/src/logic/safe/transactions/send.js b/src/logic/safe/transactions/send.js
index 6bba674d..0381a7f5 100644
--- a/src/logic/safe/transactions/send.js
+++ b/src/logic/safe/transactions/send.js
@@ -109,8 +109,8 @@ export const executeTransaction = async (
.encodeABI()
const errMsg = await getErrorMessage(safeInstance.address, 0, executeDataUsedSignatures, sender)
console.log(`Error executing the TX: ${errMsg}`)
-
- throw error;
+
+ throw error
}
}
diff --git a/src/logic/tokens/utils/tokenHelpers.js b/src/logic/tokens/utils/tokenHelpers.js
index 9d714005..b97b8b6d 100644
--- a/src/logic/tokens/utils/tokenHelpers.js
+++ b/src/logic/tokens/utils/tokenHelpers.js
@@ -21,7 +21,7 @@ export const getEthAsToken = (balance: string) => {
}
export const calculateActiveErc20TokensFrom = (tokens: List) => {
- const activeTokens = List().withMutations(list => tokens.forEach((token: Token) => {
+ const activeTokens = List().withMutations((list) => tokens.forEach((token: Token) => {
const isDeactivated = isEther(token.symbol) || !token.status
if (isDeactivated) {
return
diff --git a/src/routes/open/components/Layout.stories.js b/src/routes/open/components/Layout.stories.js
index 98f4a541..08606dc3 100644
--- a/src/routes/open/components/Layout.stories.js
+++ b/src/routes/open/components/Layout.stories.js
@@ -8,7 +8,7 @@ import { getProviderInfo } from '~/logic/wallets/getWeb3'
import { sleep } from '~/utils/timer'
import Component from './Layout'
-const FrameDecorator = story => {story()}
+const FrameDecorator = (story) => {story()}
const store = new Store({
safeAddress: '',
diff --git a/src/routes/open/components/SafeOwnersConfirmationsForm/validators.js b/src/routes/open/components/SafeOwnersConfirmationsForm/validators.js
index e4697f0e..f32d20c0 100644
--- a/src/routes/open/components/SafeOwnersConfirmationsForm/validators.js
+++ b/src/routes/open/components/SafeOwnersConfirmationsForm/validators.js
@@ -1,7 +1,5 @@
// @flow
-import {
- uniqueAddress,
-} from '~/components/forms/validator'
+import { uniqueAddress } from '~/components/forms/validator'
export const getAddressValidator = (addresses: string[], position: number) => {
// thanks Rich Harris
diff --git a/src/routes/open/components/fields.js b/src/routes/open/components/fields.js
index fa280c90..a786ec73 100644
--- a/src/routes/open/components/fields.js
+++ b/src/routes/open/components/fields.js
@@ -9,7 +9,7 @@ export const getOwnerAddressBy = (index: number) => `owner${index}Address`
export const getNumOwnersFrom = (values: Object) => {
const accounts = Object.keys(values)
.sort()
- .filter(key => /^owner\d+Address$/.test(key) && !!values[key])
+ .filter((key) => /^owner\d+Address$/.test(key) && !!values[key])
return accounts.length
}
diff --git a/src/routes/open/utils/safeDataExtractor.js b/src/routes/open/utils/safeDataExtractor.js
index 5d198bfc..b20f070f 100644
--- a/src/routes/open/utils/safeDataExtractor.js
+++ b/src/routes/open/utils/safeDataExtractor.js
@@ -4,17 +4,17 @@ import { makeOwner } from '~/routes/safe/store/models/owner'
export const getAccountsFrom = (values: Object): string[] => {
const accounts = Object.keys(values)
.sort()
- .filter(key => /^owner\d+Address$/.test(key))
+ .filter((key) => /^owner\d+Address$/.test(key))
- return accounts.map(account => values[account]).slice(0, values.owners)
+ return accounts.map((account) => values[account]).slice(0, values.owners)
}
export const getNamesFrom = (values: Object): string[] => {
const accounts = Object.keys(values)
.sort()
- .filter(key => /^owner\d+Name$/.test(key))
+ .filter((key) => /^owner\d+Name$/.test(key))
- return accounts.map(account => values[account]).slice(0, values.owners)
+ return accounts.map((account) => values[account]).slice(0, values.owners)
}
export const getOwnersFrom = (names: string[], addresses: string[]): Array => {
diff --git a/src/routes/opening/Layout.stories.js b/src/routes/opening/Layout.stories.js
index 5c208cbb..940a9062 100644
--- a/src/routes/opening/Layout.stories.js
+++ b/src/routes/opening/Layout.stories.js
@@ -5,7 +5,7 @@ import styles from '~/components/layout/PageFrame/index.scss'
import { ETHEREUM_NETWORK } from '~/logic/wallets/getWeb3'
import Component from './component'
-const FrameDecorator = story => {story()}
+const FrameDecorator = (story) => {story()}
storiesOf('Routes /opening', module)
.addDecorator(FrameDecorator)
diff --git a/src/routes/safe/components/Balances/Receive/index.jsx b/src/routes/safe/components/Balances/Receive/index.jsx
index 577bad56..34f4cfb2 100644
--- a/src/routes/safe/components/Balances/Receive/index.jsx
+++ b/src/routes/safe/components/Balances/Receive/index.jsx
@@ -14,13 +14,13 @@ import Row from '~/components/layout/Row'
import Hairline from '~/components/layout/Hairline'
import Col from '~/components/layout/Col'
import {
- xxl, lg, sm, md, background, secondary,
+ lg, md, secondary, secondaryText,
} from '~/theme/variables'
import { copyToClipboard } from '~/utils/clipboard'
const styles = () => ({
heading: {
- padding: `${sm} ${lg}`,
+ padding: `${md} ${lg}`,
justifyContent: 'space-between',
maxHeight: '75px',
boxSizing: 'border-box',
@@ -32,27 +32,27 @@ const styles = () => ({
height: '35px',
width: '35px',
},
- detailsContainer: {
- backgroundColor: background,
- },
qrContainer: {
backgroundColor: '#fff',
padding: md,
- borderRadius: '3px',
- boxShadow: '0 0 5px 0 rgba(74, 85, 121, 0.5)',
+ borderRadius: '6px',
+ border: `1px solid ${secondaryText}`,
},
safeName: {
- margin: `${xxl} 0 20px`,
+ margin: `${lg} 0 ${lg}`,
},
buttonRow: {
height: '84px',
justifyContent: 'center',
- },
- button: {
- height: '42px',
+ '& > button': {
+ fontFamily: 'Averta',
+ fontSize: '16px',
+ boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
+ },
},
addressContainer: {
- marginTop: '28px',
+ marginTop: '25px',
+ marginBottom: '25px',
},
address: {
marginLeft: '6px',
@@ -84,7 +84,7 @@ const Receive = ({
-
+
{safeName}
@@ -109,7 +109,7 @@ const Receive = ({
-
+
Done
diff --git a/src/routes/safe/components/Balances/SendModal/SafeInfo/index.jsx b/src/routes/safe/components/Balances/SendModal/SafeInfo/index.jsx
index 0246c0bf..03084b4d 100644
--- a/src/routes/safe/components/Balances/SendModal/SafeInfo/index.jsx
+++ b/src/routes/safe/components/Balances/SendModal/SafeInfo/index.jsx
@@ -24,7 +24,7 @@ const styles = () => ({
letterSpacing: -0.5,
backgroundColor: border,
width: 'fit-content',
- padding: '6px',
+ padding: '5px 10px',
marginTop: xs,
borderRadius: '3px',
},
diff --git a/src/routes/safe/components/Balances/SendModal/index.jsx b/src/routes/safe/components/Balances/SendModal/index.jsx
index b20bb25f..2a5d5799 100644
--- a/src/routes/safe/components/Balances/SendModal/index.jsx
+++ b/src/routes/safe/components/Balances/SendModal/index.jsx
@@ -5,9 +5,18 @@ import cn from 'classnames'
import { withStyles } from '@material-ui/core/styles'
import { type Token } from '~/logic/tokens/store/model/token'
import Modal from '~/components/Modal'
-import ChooseTxType from './screens/ChooseTxType'
-import SendFunds from './screens/SendFunds'
-import ReviewTx from './screens/ReviewTx'
+
+const ChooseTxType = React.lazy(() => import('./screens/ChooseTxType'))
+
+const SendFunds = React.lazy(() => import('./screens/SendFunds'))
+
+const ReviewTx = React.lazy(() => import('./screens/ReviewTx'))
+
+const SendCustomTx = React.lazy(() => import('./screens/SendCustomTx'))
+
+const ReviewCustomTx = React.lazy(() => import('./screens/ReviewCustomTx'))
+
+type ActiveScreen = 'chooseTxType' | 'sendFunds' | 'reviewTx' | 'sendCustomTx' | 'reviewCustomTx'
type Props = {
onClose: () => void,
@@ -20,19 +29,23 @@ type Props = {
tokens: List,
selectedToken: string,
createTransaction: Function,
+ activeScreenType: ActiveScreen
}
-type ActiveScreen = 'chooseTxType' | 'sendFunds' | 'reviewTx'
type TxStateType =
| {
token: Token,
recipientAddress: string,
amount: string,
+ data: string,
}
| Object
const styles = () => ({
- smallerModalWindow: {
+ scalableModalWindow: {
+ height: 'auto',
+ },
+ scalableStaticModalWindow: {
height: 'auto',
position: 'static',
},
@@ -49,23 +62,27 @@ const Send = ({
tokens,
selectedToken,
createTransaction,
+ activeScreenType,
}: Props) => {
- const [activeScreen, setActiveScreen] = useState('sendFunds')
+ const [activeScreen, setActiveScreen] = useState(activeScreenType || 'chooseTxType')
const [tx, setTx] = useState({})
- const smallerModalSize = activeScreen === 'chooseTxType'
+
+ useEffect(() => {
+ setActiveScreen(activeScreenType || 'chooseTxType')
+ setTx({})
+ }, [isOpen])
+
+ const scalableModalSize = activeScreen === 'chooseTxType'
+
const handleTxCreation = (txInfo) => {
setActiveScreen('reviewTx')
setTx(txInfo)
}
- const onClickBack = () => setActiveScreen('sendFunds')
- useEffect(
- () => () => {
- setActiveScreen('sendFunds')
- setTx({})
- },
- [isOpen],
- )
+ const handleCustomTxCreation = (customTxInfo) => {
+ setActiveScreen('reviewCustomTx')
+ setTx(customTxInfo)
+ }
return (
-
+ <>
{activeScreen === 'chooseTxType' && }
{activeScreen === 'sendFunds' && (
)}
-
+ {activeScreen === 'sendCustomTx' && (
+
+ )}
+ {activeScreen === 'reviewCustomTx' && (
+
+ )}
+ >
)
}
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.jsx
index 340c89aa..3bb6e459 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.jsx
+++ b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.jsx
@@ -1,5 +1,6 @@
// @flow
import * as React from 'react'
+import classNames from 'classnames/bind'
import { withStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close'
import IconButton from '@material-ui/core/IconButton'
@@ -8,11 +9,14 @@ import Button from '~/components/layout/Button'
import Row from '~/components/layout/Row'
import Col from '~/components/layout/Col'
import Hairline from '~/components/layout/Hairline'
-import { lg, sm } from '~/theme/variables'
+import Img from '~/components/layout/Img'
+import Token from '../assets/token.svg'
+import Code from '../assets/code.svg'
+import { lg, md, sm } from '~/theme/variables'
const styles = () => ({
heading: {
- padding: `${sm} ${lg}`,
+ padding: `${md} ${lg}`,
justifyContent: 'space-between',
boxSizing: 'border-box',
maxHeight: '75px',
@@ -26,10 +30,22 @@ const styles = () => ({
},
buttonColumn: {
padding: '52px 0',
+ '& > button': {
+ fontSize: '16px',
+ fontFamily: 'Averta',
+ },
},
- secondButton: {
- marginTop: 10,
+ firstButton: {
+ boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
+ marginBottom: 15,
},
+ iconSmall: {
+ fontSize: 16,
+ },
+ leftIcon: {
+ marginRight: sm,
+ },
+
})
type Props = {
@@ -39,7 +55,7 @@ type Props = {
}
const ChooseTxType = ({ classes, onClose, setActiveScreen }: Props) => (
-
+ <>
Send
@@ -57,22 +73,24 @@ const ChooseTxType = ({ classes, onClose, setActiveScreen }: Props) => (
minHeight={52}
onClick={() => setActiveScreen('sendFunds')}
variant="contained"
+ className={classes.firstButton}
>
- SEND FUNDS
+
+ Send funds
setActiveScreen('sendCustomTx')}
variant="outlined"
>
- SEND CUSTOM TRANSACTION
+
+ Send custom transaction
-
+ >
)
export default withStyles(styles)(ChooseTxType)
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/index.jsx
new file mode 100644
index 00000000..e391ada9
--- /dev/null
+++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/index.jsx
@@ -0,0 +1,159 @@
+// @flow
+import React from 'react'
+import OpenInNew from '@material-ui/icons/OpenInNew'
+import { withStyles } from '@material-ui/core/styles'
+import Close from '@material-ui/icons/Close'
+import IconButton from '@material-ui/core/IconButton'
+import { SharedSnackbarConsumer } from '~/components/SharedSnackBar'
+import Paragraph from '~/components/layout/Paragraph'
+import Row from '~/components/layout/Row'
+import Link from '~/components/layout/Link'
+import Col from '~/components/layout/Col'
+import Button from '~/components/layout/Button'
+import Img from '~/components/layout/Img'
+import Block from '~/components/layout/Block'
+import Identicon from '~/components/Identicon'
+import { copyToClipboard } from '~/utils/clipboard'
+import Hairline from '~/components/layout/Hairline'
+import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo'
+import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils'
+import { getWeb3 } from '~/logic/wallets/getWeb3'
+import { getEthAsToken } from '~/logic/tokens/utils/tokenHelpers'
+import ArrowDown from '../assets/arrow-down.svg'
+import { secondary } from '~/theme/variables'
+import { styles } from './style'
+
+type Props = {
+ onClose: () => void,
+ setActiveScreen: Function,
+ classes: Object,
+ safeAddress: string,
+ etherScanLink: string,
+ safeName: string,
+ ethBalance: string,
+ tx: Object,
+ createTransaction: Function,
+}
+
+const openIconStyle = {
+ height: '16px',
+ color: secondary,
+}
+
+const ReviewCustomTx = ({
+ onClose,
+ setActiveScreen,
+ classes,
+ safeAddress,
+ etherScanLink,
+ safeName,
+ ethBalance,
+ tx,
+ createTransaction,
+}: Props) => (
+
+ {({ openSnackbar }) => {
+ const submitTx = async () => {
+ const web3 = getWeb3()
+ const txRecipient = tx.recipientAddress
+ const txData = tx.data
+ const txValue = tx.value ? web3.utils.toWei(tx.value, 'ether') : 0
+
+ createTransaction(safeAddress, txRecipient, txValue, txData, openSnackbar)
+ onClose()
+ }
+
+ return (
+ <>
+
+
+ Send Funds
+
+ 2 of 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Recipient
+
+
+
+
+
+
+
+
+ {tx.recipientAddress}
+
+
+
+
+
+
+
+
+ Value
+
+
+
+
+
+ {tx.value || 0}
+ {' ETH'}
+
+
+
+
+ Data (hex encoded)
+
+
+
+
+
+ {tx.data}
+
+
+
+
+
+
+ setActiveScreen('sendCustomTx')}>
+ Back
+
+
+ Submit
+
+
+ >
+ )
+ }}
+
+)
+
+export default withStyles(styles)(ReviewCustomTx)
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/style.js b/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/style.js
new file mode 100644
index 00000000..75fc858a
--- /dev/null
+++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/style.js
@@ -0,0 +1,60 @@
+// @flow
+import {
+ lg, md, sm, secondaryText, border,
+} from '~/theme/variables'
+
+export const styles = () => ({
+ heading: {
+ padding: `${md} ${lg}`,
+ justifyContent: 'flex-start',
+ boxSizing: 'border-box',
+ maxHeight: '75px',
+ },
+ annotation: {
+ letterSpacing: '-1px',
+ color: secondaryText,
+ marginRight: 'auto',
+ marginLeft: '20px',
+ },
+ headingText: {
+ fontSize: '24px',
+ },
+ closeIcon: {
+ height: '35px',
+ width: '35px',
+ },
+ container: {
+ padding: `${md} ${lg}`,
+ },
+ value: {
+ marginLeft: sm,
+ },
+ outerData: {
+ borderRadius: '5px',
+ border: `1px solid ${border}`,
+ padding: '11px',
+ minHeight: '21px',
+ },
+ data: {
+ wordBreak: 'break-all',
+ overflow: 'auto',
+ fontSize: '14px',
+ fontFamily: 'Averta',
+ maxHeight: '100px',
+ letterSpacing: 'normal',
+ fontStretch: 'normal',
+ lineHeight: '1.43',
+ },
+ buttonRow: {
+ height: '84px',
+ justifyContent: 'center',
+ '& > button': {
+ fontFamily: 'Averta',
+ fontSize: '16px',
+ },
+ },
+ submitButton: {
+ boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
+ marginLeft: '15px',
+ },
+})
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx
index 9d233a14..b301038e 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx
+++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx
@@ -27,11 +27,11 @@ import { styles } from './style'
type Props = {
onClose: () => void,
+ setActiveScreen: Function,
classes: Object,
safeAddress: string,
etherScanLink: string,
safeName: string,
- onClickBack: Function,
ethBalance: string,
tx: Object,
createTransaction: Function,
@@ -44,13 +44,13 @@ const openIconStyle = {
const ReviewTx = ({
onClose,
+ setActiveScreen,
classes,
safeAddress,
etherScanLink,
safeName,
ethBalance,
tx,
- onClickBack,
createTransaction,
}: Props) => (
@@ -138,20 +138,19 @@ const ReviewTx = ({
-
+ setActiveScreen('sendFunds')}>
Back
- SUBMIT
+ Submit
>
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/style.js b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/style.js
index 94c10bc8..6b44b167 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/style.js
+++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/style.js
@@ -5,7 +5,7 @@ import {
export const styles = () => ({
heading: {
- padding: `${sm} ${lg}`,
+ padding: `${md} ${lg}`,
justifyContent: 'flex-start',
boxSizing: 'border-box',
maxHeight: '75px',
@@ -32,8 +32,13 @@ export const styles = () => ({
buttonRow: {
height: '84px',
justifyContent: 'center',
- position: 'absolute',
- bottom: 0,
- width: '100%',
+ '& > button': {
+ fontFamily: 'Averta',
+ fontSize: '16px',
+ },
+ },
+ submitButton: {
+ boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
+ marginLeft: '15px',
},
})
diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/index.jsx
new file mode 100644
index 00000000..c1954814
--- /dev/null
+++ b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/index.jsx
@@ -0,0 +1,174 @@
+// @flow
+import React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import Close from '@material-ui/icons/Close'
+import InputAdornment from '@material-ui/core/InputAdornment'
+import IconButton from '@material-ui/core/IconButton'
+import Paragraph from '~/components/layout/Paragraph'
+import Row from '~/components/layout/Row'
+import GnoForm from '~/components/forms/GnoForm'
+import AddressInput from '~/components/forms/AddressInput'
+import Col from '~/components/layout/Col'
+import Button from '~/components/layout/Button'
+import Block from '~/components/layout/Block'
+import Hairline from '~/components/layout/Hairline'
+import ButtonLink from '~/components/layout/ButtonLink'
+import Field from '~/components/forms/Field'
+import TextField from '~/components/forms/TextField'
+import TextareaField from '~/components/forms/TextareaField'
+import {
+ composeValidators,
+ mustBeFloat,
+ maxValue,
+} from '~/components/forms/validator'
+import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo'
+import ArrowDown from '../assets/arrow-down.svg'
+import { styles } from './style'
+
+type Props = {
+ onClose: () => void,
+ classes: Object,
+ safeAddress: string,
+ etherScanLink: string,
+ safeName: string,
+ ethBalance: string,
+ onSubmit: Function,
+ initialValues: Object,
+}
+
+const SendCustomTx = ({
+ classes,
+ onClose,
+ safeAddress,
+ etherScanLink,
+ safeName,
+ ethBalance,
+ onSubmit,
+ initialValues,
+}: Props) => {
+ const handleSubmit = (values: Object) => {
+ if (values.data || values.value) {
+ onSubmit(values)
+ }
+ }
+
+ const formMutators = {
+ setMax: (args, state, utils) => {
+ utils.changeValue(state, 'value', () => ethBalance)
+ },
+ setRecipient: (args, state, utils) => {
+ utils.changeValue(state, 'recipientAddress', () => args[0])
+ },
+ }
+
+ return (
+ <>
+
+
+ Send custom transactions
+
+ 1 of 2
+
+
+
+
+
+
+ {(...args) => {
+ const mutators = args[3]
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Value
+
+
+ Send max
+
+
+
+
+
+ ETH,
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+
+
+ Review
+
+
+ >
+ )
+ }}
+
+ >
+ )
+}
+
+export default withStyles(styles)(SendCustomTx)
diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/style.js b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/style.js
new file mode 100644
index 00000000..96a1149e
--- /dev/null
+++ b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/style.js
@@ -0,0 +1,45 @@
+// @flow
+import { lg, md } from '~/theme/variables'
+
+export const styles = () => ({
+ heading: {
+ padding: `${md} ${lg}`,
+ justifyContent: 'flex-start',
+ boxSizing: 'border-box',
+ maxHeight: '75px',
+ },
+ annotation: {
+ letterSpacing: '-1px',
+ color: '#a2a8ba',
+ marginRight: 'auto',
+ marginLeft: '20px',
+ },
+ manage: {
+ fontSize: '24px',
+ },
+ closeIcon: {
+ height: '35px',
+ width: '35px',
+ },
+ formContainer: {
+ padding: `${md} ${lg}`,
+ },
+ buttonRow: {
+ height: '84px',
+ justifyContent: 'center',
+ '& > button': {
+ fontFamily: 'Averta',
+ fontSize: '16px',
+ },
+ },
+ submitButton: {
+ boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
+ marginLeft: '15px',
+ },
+ dataInput: {
+ '& TextField-root-294': {
+ lineHeight: 'auto',
+ border: 'green',
+ },
+ },
+})
diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx
index 890863c9..0d342252 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx
+++ b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx
@@ -81,24 +81,28 @@ const SendFunds = ({
-
-
-
-
-
-
-
-
-
-
-
- {(...args) => {
- const formState = args[2]
- const mutators = args[3]
- const { token } = formState.values
-
- return (
- <>
+
+ {(...args) => {
+ const formState = args[2]
+ const mutators = args[3]
+ const { token } = formState.values
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
-
-
-
- Cancel
-
-
- Review
-
-
- >
- )
- }}
-
-
+
+
+
+
+ Cancel
+
+
+ Review
+
+
+ >
+ )
+ }}
+
>
)
}
diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js
index e1d9ef62..31d3c991 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js
+++ b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js
@@ -1,11 +1,9 @@
// @flow
-import {
- lg, md, sm, secondaryText,
-} from '~/theme/variables'
+import { lg, md, secondaryText } from '~/theme/variables'
export const styles = () => ({
heading: {
- padding: `${sm} ${lg}`,
+ padding: `${md} ${lg}`,
justifyContent: 'flex-start',
boxSizing: 'border-box',
maxHeight: '75px',
@@ -29,5 +27,13 @@ export const styles = () => ({
buttonRow: {
height: '84px',
justifyContent: 'center',
+ '& > button': {
+ fontFamily: 'Averta',
+ fontSize: '16px',
+ },
+ },
+ submitButton: {
+ boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
+ marginLeft: '15px',
},
})
diff --git a/src/routes/safe/components/Balances/SendModal/screens/assets/code.svg b/src/routes/safe/components/Balances/SendModal/screens/assets/code.svg
new file mode 100644
index 00000000..ad056d55
--- /dev/null
+++ b/src/routes/safe/components/Balances/SendModal/screens/assets/code.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/routes/safe/components/Balances/SendModal/screens/assets/token.svg b/src/routes/safe/components/Balances/SendModal/screens/assets/token.svg
new file mode 100644
index 00000000..f2abb8c5
--- /dev/null
+++ b/src/routes/safe/components/Balances/SendModal/screens/assets/token.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.jsx b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.jsx
index 75b13370..dbe9cc5d 100644
--- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.jsx
+++ b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.jsx
@@ -119,10 +119,10 @@ const AddCustomToken = (props: Props) => {
}
return (
-
+ <>
{() => (
-
+ <>
Add custom token
@@ -189,17 +189,17 @@ const AddCustomToken = (props: Props) => {
-
+
Cancel
-
+
Save
-
+ >
)}
-
+ >
)
}
diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.js b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.js
index b09dcabb..3dd3b0ab 100644
--- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.js
+++ b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.js
@@ -28,7 +28,4 @@ export const styles = () => ({
height: '84px',
justifyContent: 'center',
},
- button: {
- height: '42px',
- },
})
diff --git a/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.jsx b/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.jsx
index dd68c257..ad39c8cc 100644
--- a/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.jsx
+++ b/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.jsx
@@ -124,7 +124,7 @@ class Tokens extends React.Component {
const filteredTokens = filterBy(filter, tokens)
return (
-
+ <>
@@ -179,7 +179,7 @@ class Tokens extends React.Component {
)
})}
-
+ >
)
}
}
diff --git a/src/routes/safe/components/Balances/index.jsx b/src/routes/safe/components/Balances/index.jsx
index 7edf6190..149ccc25 100644
--- a/src/routes/safe/components/Balances/index.jsx
+++ b/src/routes/safe/components/Balances/index.jsx
@@ -2,12 +2,12 @@
import * as React from 'react'
import { List } from 'immutable'
import classNames from 'classnames/bind'
-import CallMade from '@material-ui/icons/CallMade'
-import CallReceived from '@material-ui/icons/CallReceived'
import Checkbox from '@material-ui/core/Checkbox'
import TableRow from '@material-ui/core/TableRow'
import TableCell from '@material-ui/core/TableCell'
import { withStyles } from '@material-ui/core/styles'
+import CallMade from '@material-ui/icons/CallMade'
+import CallReceived from '@material-ui/icons/CallReceived'
import { type Token } from '~/logic/tokens/store/model/token'
import Col from '~/components/layout/Col'
import Row from '~/components/layout/Row'
@@ -178,7 +178,7 @@ class Balances extends React.Component {
onClick={() => this.showSendFunds(row.asset.name)}
testId="balance-send-btn"
>
-
+
Send
)}
@@ -189,7 +189,7 @@ class Balances extends React.Component {
className={classes.receive}
onClick={this.onShow('Receive')}
>
-
+
Receive
@@ -207,6 +207,7 @@ class Balances extends React.Component {
tokens={activeTokens}
selectedToken={sendFunds.selectedToken}
createTransaction={createTransaction}
+ activeScreenType="sendFunds"
/>
({
root: {
@@ -30,17 +30,25 @@ export const styles = (theme: Object) => ({
justifyContent: 'flex-end',
visibility: 'hidden',
},
- send: {
- minWidth: '0px',
- marginRight: sm,
- width: '70px',
- },
receive: {
- minWidth: '0px',
width: '95px',
+ minWidth: '95px',
+ marginLeft: sm,
+ borderRadius: '4px',
+ '& > span': {
+ fontSize: '14px',
+ },
+ },
+ send: {
+ width: '75px',
+ minWidth: '75px',
+ borderRadius: '4px',
+ '& > span': {
+ fontSize: '14px',
+ },
},
leftIcon: {
- marginRight: xs,
+ marginRight: sm,
},
links: {
textDecoration: 'underline',
diff --git a/src/routes/safe/components/Layout.jsx b/src/routes/safe/components/Layout.jsx
index d7716453..8904b636 100644
--- a/src/routes/safe/components/Layout.jsx
+++ b/src/routes/safe/components/Layout.jsx
@@ -1,27 +1,35 @@
// @flow
import * as React from 'react'
+import classNames from 'classnames/bind'
import OpenInNew from '@material-ui/icons/OpenInNew'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
+import CallMade from '@material-ui/icons/CallMade'
+import CallReceived from '@material-ui/icons/CallReceived'
import { withStyles } from '@material-ui/core/styles'
import Hairline from '~/components/layout/Hairline'
import Block from '~/components/layout/Block'
import Identicon from '~/components/Identicon'
import Heading from '~/components/layout/Heading'
import Row from '~/components/layout/Row'
+import Button from '~/components/layout/Button'
import Link from '~/components/layout/Link'
import Paragraph from '~/components/layout/Paragraph'
+import Modal from '~/components/Modal'
+import SendModal from './Balances/SendModal'
+import Receive from './Balances/Receive'
import NoSafe from '~/components/NoSafe'
import { type SelectorProps } from '~/routes/safe/container/selector'
import { getEtherScanLink } from '~/logic/wallets/getWeb3'
import {
- sm, xs, secondary, smallFontSize, border, secondaryText,
+ secondary, border,
} from '~/theme/variables'
import { copyToClipboard } from '~/utils/clipboard'
import { type Actions } from '../container/actions'
import Balances from './Balances'
import Transactions from './Transactions'
import Settings from './Settings'
+import { styles } from './style'
export const BALANCES_TAB_BTN_TEST_ID = 'balances-tab-btn'
export const SETTINGS_TAB_BTN_TEST_ID = 'settings-tab-btn'
@@ -36,6 +44,12 @@ type Props = SelectorProps &
Actions & {
classes: Object,
granted: boolean,
+ sendFunds: Object,
+ showReceive: boolean,
+ onShow: Function,
+ onHide: Function,
+ showSendFunds: Function,
+ hideSendFunds: Function
}
const openIconStyle = {
@@ -43,40 +57,6 @@ const openIconStyle = {
color: secondary,
}
-const styles = () => ({
- container: {
- display: 'flex',
- alignItems: 'center',
- },
- name: {
- marginLeft: sm,
- textOverflow: 'ellipsis',
- overflow: 'hidden',
- whiteSpace: 'nowrap',
- },
- user: {
- justifyContent: 'left',
- },
- open: {
- paddingLeft: sm,
- width: 'auto',
- '&:hover': {
- cursor: 'pointer',
- },
- },
- readonly: {
- fontSize: smallFontSize,
- letterSpacing: '0.5px',
- color: '#ffffff',
- backgroundColor: secondaryText,
- textTransform: 'uppercase',
- padding: `0 ${sm}`,
- marginLeft: sm,
- borderRadius: xs,
- lineHeight: '28px',
- },
-})
-
class Layout extends React.Component {
constructor(props) {
super(props)
@@ -112,6 +92,12 @@ class Layout extends React.Component {
updateSafe,
transactions,
userAddress,
+ sendFunds,
+ showReceive,
+ onShow,
+ onHide,
+ showSendFunds,
+ hideSendFunds,
} = this.props
const { tabIndex } = this.state
@@ -142,6 +128,32 @@ class Layout extends React.Component {
+
+
+ showSendFunds('Ether')}
+ disabled={!granted}
+ testId="balance-send-btn"
+ >
+
+ Send
+
+
+
+ Receive
+
+
+
@@ -190,6 +202,31 @@ class Layout extends React.Component {
createTransaction={createTransaction}
/>
)}
+
+
+
+
>
)
}
diff --git a/src/routes/safe/components/Layout.stories.js b/src/routes/safe/components/Layout.stories.js
index 689a1bfe..bb6ba32d 100644
--- a/src/routes/safe/components/Layout.stories.js
+++ b/src/routes/safe/components/Layout.stories.js
@@ -5,7 +5,7 @@ import { List } from 'immutable'
import styles from '~/components/layout/PageFrame/index.scss'
import Component from './Layout'
-const FrameDecorator = story => {story()}
+const FrameDecorator = (story) => {story()}
storiesOf('Routes /safe:address', module)
.addDecorator(FrameDecorator)
diff --git a/src/routes/safe/components/NoRights/index.stories.js b/src/routes/safe/components/NoRights/index.stories.js
index fbd53f8f..03d95d28 100644
--- a/src/routes/safe/components/NoRights/index.stories.js
+++ b/src/routes/safe/components/NoRights/index.stories.js
@@ -4,7 +4,7 @@ import * as React from 'react'
import styles from '~/components/layout/PageFrame/index.scss'
import Component from './index.jsx'
-const FrameDecorator = story => {story()}
+const FrameDecorator = (story) => {story()}
storiesOf('Components', module)
.addDecorator(FrameDecorator)
diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx
index 25217f5b..a952e8d6 100644
--- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx
+++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx
@@ -44,7 +44,7 @@ const ReviewAddOwner = ({
onSubmit()
}
return (
-
+ <>
Add new owner
@@ -97,7 +97,7 @@ const ReviewAddOwner = ({
- {owners.map(owner => (
+ {owners.map((owner) => (
@@ -154,22 +154,22 @@ const ReviewAddOwner = ({
-
+
Back
Submit
-
+ >
)
}
diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/ThresholdForm/index.jsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/ThresholdForm/index.jsx
index b31127d3..0c5af1de 100644
--- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/ThresholdForm/index.jsx
+++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/ThresholdForm/index.jsx
@@ -39,7 +39,7 @@ const ThresholdForm = ({
}
return (
-
+ <>
Add new owner
@@ -52,7 +52,7 @@ const ThresholdForm = ({
{() => (
-
+ <>
@@ -68,8 +68,8 @@ const ThresholdForm = ({
(
-
+ render={(props) => (
+ <>
{[...Array(Number(owners.size + 1))].map((x, index) => (
@@ -82,7 +82,7 @@ const ThresholdForm = ({
{props.meta.error}
)}
-
+ >
)}
validate={composeValidators(required, mustBeInteger, minValue(1), maxValue(owners.size + 1))}
data-testid="threshold-select-input"
@@ -101,24 +101,24 @@ owner(s)
-
+
Back
Review
-
+ >
)}
-
+ >
)
}
diff --git a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx
index 2eb99b13..6fe47db6 100644
--- a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx
+++ b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx
@@ -73,7 +73,7 @@ const EditOwnerComponent = ({
{() => (
-
+ <>
-
+
Cancel
-
+
Save
-
+ >
)}
diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx
index ff2ff84f..06946a08 100644
--- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx
+++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx
@@ -79,13 +79,14 @@ const CheckOwner = ({
-
+
Cancel
-
+
Back
-
+
Back
+ <>
Replace owner
@@ -112,7 +112,7 @@ const ReviewRemoveOwner = ({
{owners.map(
- owner => owner.address !== ownerAddress && (
+ (owner) => owner.address !== ownerAddress && (
@@ -200,14 +200,14 @@ const ReviewRemoveOwner = ({
-
+
Back
-
+ >
)
}
diff --git a/src/routes/safe/components/Settings/RemoveSafeModal/index.jsx b/src/routes/safe/components/Settings/RemoveSafeModal/index.jsx
index 8fbde0a6..19a68bde 100644
--- a/src/routes/safe/components/Settings/RemoveSafeModal/index.jsx
+++ b/src/routes/safe/components/Settings/RemoveSafeModal/index.jsx
@@ -82,7 +82,7 @@ const RemoveSafeComponent = ({
-
+
Cancel
({
+export const styles = () => ({
heading: {
padding: `${sm} ${lg}`,
justifyContent: 'space-between',
@@ -27,6 +27,7 @@ export const styles = (theme: Object) => ({
buttonRemove: {
color: '#fff',
backgroundColor: error,
+ height: '42px',
},
name: {
textOverflow: 'ellipsis',
diff --git a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx
index 1a0ee366..e980444a 100644
--- a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx
+++ b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx
@@ -103,10 +103,10 @@ owner(s)
-
+
BACK
-
+
CHANGE
diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx
index 1acc998e..f44cf32a 100644
--- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx
+++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx
@@ -104,12 +104,11 @@ const ApproveTxModal = ({
-
+
Exit
-
+
Exit
({
txDataContainer: {
padding: `${lg} ${md}`,
},
+ txData: {
+ wordBreak: 'break-all',
+ },
})
type Props = {
@@ -37,6 +41,11 @@ type DescriptionDescProps = {
newThreshold?: string,
}
+type CustomDescProps = {
+ data: String,
+ classes: Obeject,
+}
+
const TransferDescription = ({ value = '', symbol, recipient }: TransferDescProps) => (
@@ -46,7 +55,7 @@ const TransferDescription = ({ value = '', symbol, recipient }: TransferDescProp
{' '}
{symbol}
{' '}
-to:
+ to:
@@ -79,9 +88,19 @@ const SettingsDescription = ({ removedOwner, addedOwner, newThreshold }: Descrip
>
)
+const CustomDescription = ({ data, classes }: CustomDescProps) => (
+ <>
+
+ Data (hex encoded):
+
+ {data}
+
+ >
+)
+
const TxDescription = ({ tx, classes }: Props) => {
const {
- recipient, value, modifySettingsTx, removedOwner, addedOwner, newThreshold, cancellationTx,
+ recipient, value, modifySettingsTx, removedOwner, addedOwner, newThreshold, cancellationTx, customTx, data,
} = getTxData(tx)
return (
@@ -89,7 +108,10 @@ const TxDescription = ({ tx, classes }: Props) => {
{modifySettingsTx && (
)}
- {!cancellationTx && !modifySettingsTx && (
+ {customTx && (
+
+ )}
+ {!cancellationTx && !modifySettingsTx && !customTx && (
)}
diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.js b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.js
index dd9744da..affddad6 100644
--- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.js
+++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.js
@@ -10,6 +10,8 @@ type DecodedTxData = {
newThreshold?: string,
addedOwner?: string,
cancellationTx?: boolean,
+ customTx?: boolean,
+ data: string,
}
export const getTxData = (tx: Transaction): DecodedTxData => {
@@ -47,6 +49,9 @@ export const getTxData = (tx: Transaction): DecodedTxData => {
}
} else if (tx.cancellationTx) {
txData.cancellationTx = true
+ } else if (tx.customTx) {
+ txData.data = tx.data
+ txData.customTx = true
}
return txData
diff --git a/src/routes/safe/components/Transactions/TxsTable/columns.js b/src/routes/safe/components/Transactions/TxsTable/columns.js
index 2a61828d..a908e375 100644
--- a/src/routes/safe/components/Transactions/TxsTable/columns.js
+++ b/src/routes/safe/components/Transactions/TxsTable/columns.js
@@ -50,6 +50,8 @@ export const getTxTableData = (transactions: List): List ({
+ container: {
+ display: 'flex',
+ alignItems: 'center',
+ },
+ name: {
+ marginLeft: sm,
+ textOverflow: 'ellipsis',
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+ },
+ user: {
+ justifyContent: 'left',
+ },
+ open: {
+ paddingLeft: sm,
+ width: 'auto',
+ '&:hover': {
+ cursor: 'pointer',
+ },
+ },
+ readonly: {
+ fontSize: smallFontSize,
+ letterSpacing: '0.5px',
+ color: '#ffffff',
+ backgroundColor: secondaryText,
+ textTransform: 'uppercase',
+ padding: `0 ${sm}`,
+ marginLeft: sm,
+ borderRadius: xs,
+ lineHeight: '28px',
+ },
+ iconSmall: {
+ fontSize: 16,
+ },
+ balance: {
+ marginLeft: 'auto',
+ overflow: 'hidden',
+ },
+ receive: {
+ width: '95px',
+ minWidth: '95px',
+ marginLeft: sm,
+ borderRadius: '4px',
+ '& > span': {
+ fontSize: '14px',
+ },
+ },
+ send: {
+ width: '75px',
+ minWidth: '75px',
+ borderRadius: '4px',
+ '& > span': {
+ fontSize: '14px',
+ },
+ },
+ leftIcon: {
+ marginRight: sm,
+ },
+})
diff --git a/src/routes/safe/container/index.jsx b/src/routes/safe/container/index.jsx
index e6d0f2d3..ff8b4317 100644
--- a/src/routes/safe/container/index.jsx
+++ b/src/routes/safe/container/index.jsx
@@ -6,6 +6,13 @@ import Layout from '~/routes/safe/components/Layout'
import selector, { type SelectorProps } from './selector'
import actions, { type Actions } from './actions'
+type State = {
+ showReceive: boolean,
+ sendFunds: Object,
+}
+
+type Action = 'Send' | 'Receive'
+
export type Props = Actions &
SelectorProps & {
granted: boolean,
@@ -13,7 +20,15 @@ export type Props = Actions &
const TIMEOUT = process.env.NODE_ENV === 'test' ? 1500 : 5000
-class SafeView extends React.Component {
+class SafeView extends React.Component {
+ state = {
+ sendFunds: {
+ isOpen: false,
+ selectedToken: undefined,
+ },
+ showReceive: false,
+ }
+
intervalId: IntervalID
componentDidMount() {
@@ -45,6 +60,32 @@ class SafeView extends React.Component {
clearInterval(this.intervalId)
}
+ onShow = (action: Action) => () => {
+ this.setState(() => ({ [`show${action}`]: true }))
+ }
+
+ onHide = (action: Action) => () => {
+ this.setState(() => ({ [`show${action}`]: false }))
+ }
+
+ showSendFunds = (token: Token) => {
+ this.setState({
+ sendFunds: {
+ isOpen: true,
+ selectedToken: token,
+ },
+ })
+ }
+
+ hideSendFunds = () => {
+ this.setState({
+ sendFunds: {
+ isOpen: false,
+ selectedToken: undefined,
+ },
+ })
+ }
+
checkForUpdates() {
const {
safeUrl, activeTokens, fetchTokenBalances, fetchEtherBalance,
@@ -55,6 +96,7 @@ class SafeView extends React.Component {
}
render() {
+ const { sendFunds, showReceive } = this.state
const {
safe,
provider,
@@ -85,6 +127,12 @@ class SafeView extends React.Component {
fetchTransactions={fetchTransactions}
updateSafe={updateSafe}
transactions={transactions}
+ sendFunds={sendFunds}
+ showReceive={showReceive}
+ onShow={this.onShow}
+ onHide={this.onHide}
+ showSendFunds={this.showSendFunds}
+ hideSendFunds={this.hideSendFunds}
/>
)
diff --git a/src/routes/safe/store/actions/fetchTransactions.js b/src/routes/safe/store/actions/fetchTransactions.js
index 49cf414b..72f5e3d9 100644
--- a/src/routes/safe/store/actions/fetchTransactions.js
+++ b/src/routes/safe/store/actions/fetchTransactions.js
@@ -59,6 +59,7 @@ export const buildTransactionFrom = async (
)
const modifySettingsTx = tx.to === safeAddress && Number(tx.value) === 0 && !!tx.data
const cancellationTx = tx.to === safeAddress && Number(tx.value) === 0 && !tx.data
+ const customTx = tx.to !== safeAddress && !!tx.data
const isTokenTransfer = await isAddressAToken(tx.to)
let executionTxHash
@@ -82,6 +83,8 @@ export const buildTransactionFrom = async (
}
} else if (modifySettingsTx && tx.data) {
decodedParams = await decodeParamsFromSafeMethod(tx.data)
+ } else if (customTx && tx.data) {
+ decodedParams = await decodeParamsFromSafeMethod(tx.data)
}
return makeTransaction({
@@ -100,6 +103,7 @@ export const buildTransactionFrom = async (
isTokenTransfer,
decodedParams,
modifySettingsTx,
+ customTx,
cancellationTx,
})
}
diff --git a/src/routes/safe/store/actions/processTransaction.js b/src/routes/safe/store/actions/processTransaction.js
index ad5bea46..c30fd27c 100644
--- a/src/routes/safe/store/actions/processTransaction.js
+++ b/src/routes/safe/store/actions/processTransaction.js
@@ -12,7 +12,7 @@ import { approveTransaction, executeTransaction, CALL } from '~/logic/safe/trans
const generateSignaturesFromTxConfirmations = (tx: Transaction, preApprovingOwner?: string) => {
// The constant parts need to be sorted so that the recovered signers are sorted ascending
// (natural order) by address (not checksummed).
- let confirmedAdresses = tx.confirmations.map(conf => conf.owner.address)
+ let confirmedAdresses = tx.confirmations.map((conf) => conf.owner.address)
if (preApprovingOwner) {
confirmedAdresses = confirmedAdresses.push(preApprovingOwner)
diff --git a/src/routes/safe/store/models/transaction.js b/src/routes/safe/store/models/transaction.js
index ec848046..27cd179f 100644
--- a/src/routes/safe/store/models/transaction.js
+++ b/src/routes/safe/store/models/transaction.js
@@ -18,6 +18,7 @@ export type TransactionProps = {
symbol: string,
modifySettingsTx: boolean,
cancellationTx: boolean,
+ customTx: boolean,
safeTxHash: string,
executionTxHash?: string,
cancelled?: boolean,
@@ -42,6 +43,7 @@ export const makeTransaction: RecordFactory = Record({
cancelled: false,
modifySettingsTx: false,
cancellationTx: false,
+ customTx: false,
status: 'awaiting',
isTokenTransfer: false,
decodedParams: {},
diff --git a/src/routes/safe/store/reducer/safe.js b/src/routes/safe/store/reducer/safe.js
index 088ff9a6..bc942637 100644
--- a/src/routes/safe/store/reducer/safe.js
+++ b/src/routes/safe/store/reducer/safe.js
@@ -24,7 +24,7 @@ export const buildSafe = (storedSafe: SafeProps) => {
const addresses = storedSafe.owners.map((owner: OwnerProps) => owner.address)
const owners = buildOwnersFrom(Array.from(names), Array.from(addresses))
const activeTokens = List(storedSafe.activeTokens)
- const balances = storedSafe.balances.map(balance => TokenBalance(balance))
+ const balances = storedSafe.balances.map((balance) => TokenBalance(balance))
const safe: SafeProps = {
...storedSafe,
@@ -67,7 +67,7 @@ export default handleActions(
const safe = action.payload
const safeAddress = safe.address
- return state.update(safeAddress, prevSafe => prevSafe.merge(safe))
+ return state.update(safeAddress, (prevSafe) => prevSafe.merge(safe))
},
[ACTIVATE_TOKEN_FOR_ALL_SAFES]: (state: State, action: ActionType): State => {
const tokenAddress = action.payload
@@ -77,7 +77,7 @@ export default handleActions(
const safeActiveTokens = map.getIn([safeAddress, 'activeTokens'])
const activeTokens = safeActiveTokens.push(tokenAddress)
- map.update(safeAddress, prevSafe => prevSafe.merge({ activeTokens }))
+ map.update(safeAddress, (prevSafe) => prevSafe.merge({ activeTokens }))
})
})
@@ -91,7 +91,7 @@ export default handleActions(
// with initial props and it would overwrite existing ones
if (state.has(safe.address)) {
- return state.update(safe.address, prevSafe => prevSafe.merge(safe))
+ return state.update(safe.address, (prevSafe) => prevSafe.merge(safe))
}
return state.set(safe.address, SafeRecord(safe))
@@ -104,15 +104,15 @@ export default handleActions(
[ADD_SAFE_OWNER]: (state: State, action: ActionType): State => {
const { safeAddress, ownerName, ownerAddress } = action.payload
- return state.update(safeAddress, prevSafe => prevSafe.merge({
+ return state.update(safeAddress, (prevSafe) => prevSafe.merge({
owners: prevSafe.owners.push(makeOwner({ address: ownerAddress, name: ownerName })),
}))
},
[REMOVE_SAFE_OWNER]: (state: State, action: ActionType): State => {
const { safeAddress, ownerAddress } = action.payload
- return state.update(safeAddress, prevSafe => prevSafe.merge({
- owners: prevSafe.owners.filter(o => o.address.toLowerCase() !== ownerAddress.toLowerCase()),
+ return state.update(safeAddress, (prevSafe) => prevSafe.merge({
+ owners: prevSafe.owners.filter((o) => o.address.toLowerCase() !== ownerAddress.toLowerCase()),
}))
},
[REPLACE_SAFE_OWNER]: (state: State, action: ActionType): State => {
@@ -120,9 +120,9 @@ export default handleActions(
safeAddress, oldOwnerAddress, ownerName, ownerAddress,
} = action.payload
- return state.update(safeAddress, prevSafe => prevSafe.merge({
+ return state.update(safeAddress, (prevSafe) => prevSafe.merge({
owners: prevSafe.owners
- .filter(o => o.address.toLowerCase() !== oldOwnerAddress.toLowerCase())
+ .filter((o) => o.address.toLowerCase() !== oldOwnerAddress.toLowerCase())
.push(makeOwner({ address: ownerAddress, name: ownerName })),
}))
},
@@ -131,9 +131,9 @@ export default handleActions(
return state.update(safeAddress, (prevSafe) => {
const ownerToUpdateIndex = prevSafe.owners.findIndex(
- o => o.address.toLowerCase() === ownerAddress.toLowerCase(),
+ (o) => o.address.toLowerCase() === ownerAddress.toLowerCase(),
)
- const updatedOwners = prevSafe.owners.update(ownerToUpdateIndex, owner => owner.set('name', ownerName))
+ const updatedOwners = prevSafe.owners.update(ownerToUpdateIndex, (owner) => owner.set('name', ownerName))
return prevSafe.merge({ owners: updatedOwners })
})
},
diff --git a/src/routes/safeList/components/Layout.jsx b/src/routes/safeList/components/Layout.jsx
index e684accf..a7dde521 100644
--- a/src/routes/safeList/components/Layout.jsx
+++ b/src/routes/safeList/components/Layout.jsx
@@ -14,12 +14,11 @@ const SafeList = ({ safes, provider }: Props) => {
const safesAvailable = safes && safes.count() > 0
return (
-
+ <>
{ safesAvailable
?
- :
- }
-
+ : }
+ >
)
}
diff --git a/src/routes/safeList/components/Layout.stories.js b/src/routes/safeList/components/Layout.stories.js
index d8189180..391b8de6 100644
--- a/src/routes/safeList/components/Layout.stories.js
+++ b/src/routes/safeList/components/Layout.stories.js
@@ -5,7 +5,7 @@ import * as React from 'react'
import styles from '~/components/layout/PageFrame/index.scss'
import Component from './Layout'
-const FrameDecorator = story => {story()}
+const FrameDecorator = (story) => {story()}
storiesOf('Routes /safes', module)
.addDecorator(FrameDecorator)
diff --git a/src/routes/welcome/components/Layout.stories.js b/src/routes/welcome/components/Layout.stories.js
index d5b5c62c..e33a3cbf 100644
--- a/src/routes/welcome/components/Layout.stories.js
+++ b/src/routes/welcome/components/Layout.stories.js
@@ -5,7 +5,7 @@ import * as React from 'react'
import styles from '~/components/layout/PageFrame/index.scss'
import Component from './Layout'
-const FrameDecorator = story => {story()}
+const FrameDecorator = (story) => {story()}
storiesOf('Routes /welcome', module)
.addDecorator(FrameDecorator)
diff --git a/src/test/safe.dom.funds.threshold>1.test.js b/src/test/safe.dom.funds.threshold>1.test.js
index 8b355f85..a1ed2088 100644
--- a/src/test/safe.dom.funds.threshold>1.test.js
+++ b/src/test/safe.dom.funds.threshold>1.test.js
@@ -12,7 +12,10 @@ import { fillAndSubmitSendFundsForm } from './utils/transactions'
import { TRANSACTIONS_TAB_BTN_TEST_ID } from '~/routes/safe/components/Layout'
import { TRANSACTION_ROW_TEST_ID } from '~/routes/safe/components/Transactions/TxsTable'
import { useTestAccountAt, resetTestAccount } from './utils/accounts'
-import { CONFIRM_TX_BTN_TEST_ID, EXECUTE_TX_BTN_TEST_ID } from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/ButtonRow'
+import {
+ CONFIRM_TX_BTN_TEST_ID,
+ EXECUTE_TX_BTN_TEST_ID,
+} from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/ButtonRow'
import { APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID } from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal'
afterEach(resetTestAccount)
diff --git a/src/test/utils/accounts.js b/src/test/utils/accounts.js
index e2b0e925..f4c4e9a6 100644
--- a/src/test/utils/accounts.js
+++ b/src/test/utils/accounts.js
@@ -7,7 +7,4 @@ function resetTestAccount() {
delete window.testAccountIndex
}
-export {
- useTestAccountAt,
- resetTestAccount,
-}
+export { useTestAccountAt, resetTestAccount }
diff --git a/src/test/utils/logTransactions.js b/src/test/utils/logTransactions.js
index f73c4d2a..2df2e691 100644
--- a/src/test/utils/logTransactions.js
+++ b/src/test/utils/logTransactions.js
@@ -38,10 +38,7 @@ type FinsihedTx = {
finishedTransaction: boolean,
}
-export const whenExecuted = (
- SafeDom: React.Component,
- ParentComponent: React.ElementType,
-): Promise => new Promise((resolve, reject) => {
+export const whenExecuted = (SafeDom: React.Component, ParentComponent: React.ElementType): Promise => new Promise((resolve, reject) => {
let times = 0
const interval = setInterval(() => {
if (times >= MAX_TIMES_EXECUTED) {
diff --git a/src/test/utils/transactions/transactionList.helper.js b/src/test/utils/transactions/transactionList.helper.js
index 2238dce7..4a767dd4 100644
--- a/src/test/utils/transactions/transactionList.helper.js
+++ b/src/test/utils/transactions/transactionList.helper.js
@@ -34,20 +34,14 @@ export const checkRegisteredTxSend = async (
expect(txDescription).toHaveTextContent(`Send ${ethAmount} ${symbol} to:${shortVersionOf(ethAddress, 4)}`)
}
-export const checkRegisteredTxAddOwner = async (
- SafeDom: React.Component,
- ownerAddress: string,
-) => {
+export const checkRegisteredTxAddOwner = async (SafeDom: React.Component, ownerAddress: string) => {
await getLastTransaction(SafeDom)
const txDescription = SafeDom.getAllByTestId(TRANSACTIONS_DESC_ADD_OWNER_TEST_ID)[0]
expect(txDescription).toHaveTextContent(`Add owner:${shortVersionOf(ownerAddress, 4)}`)
}
-export const checkRegisteredTxRemoveOwner = async (
- SafeDom: React.Component,
- ownerAddress: string,
-) => {
+export const checkRegisteredTxRemoveOwner = async (SafeDom: React.Component, ownerAddress: string) => {
await getLastTransaction(SafeDom)
const txDescription = SafeDom.getAllByTestId(TRANSACTIONS_DESC_REMOVE_OWNER_TEST_ID)[0]
diff --git a/src/utils/timer.js b/src/utils/timer.js
index aaa8356b..bd260688 100644
--- a/src/utils/timer.js
+++ b/src/utils/timer.js
@@ -1,2 +1,2 @@
// @flow
-export const sleep: Function = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
+export const sleep: Function = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))