diff --git a/src/routes/safe/component/AddOwner/AddOwnerForm/index.jsx b/src/routes/safe/component/AddOwner/AddOwnerForm/index.jsx
new file mode 100644
index 00000000..721869f2
--- /dev/null
+++ b/src/routes/safe/component/AddOwner/AddOwnerForm/index.jsx
@@ -0,0 +1,71 @@
+// @flow
+import * as React from 'react'
+import Field from '~/components/forms/Field'
+import TextField from '~/components/forms/TextField'
+import Checkbox from '~/components/forms/Checkbox'
+import { composeValidators, required, mustBeEthereumAddress, uniqueAddress } from '~/components/forms/validator'
+import Block from '~/components/layout/Block'
+import Heading from '~/components/layout/Heading'
+
+export const CONFIRMATIONS_ERROR = 'Number of confirmations can not be higher than the number of owners'
+
+export const NAME_PARAM = 'name'
+export const OWNER_ADDRESS_PARAM = 'ownerAddress'
+export const INCREASE_PARAM = 'increase'
+
+export const safeFieldsValidation = (values: Object) => {
+ const errors = {}
+
+ if (Number.parseInt(values.owners, 10) < Number.parseInt(values.confirmations, 10)) {
+ errors.confirmations = CONFIRMATIONS_ERROR
+ }
+
+ return errors
+}
+
+type Props = {
+ numOwners: number,
+ threshold: number,
+ addresses: string[]
+}
+
+const AddOwnerForm = ({ addresses, numOwners, threshold }: Props) => () => (
+
+
+ Add Owner
+
+
+ {`Actual number of owners: ${numOwners}, with threshold: ${threshold}`}
+
+
+
+
+
+
+
+
+
+ Increase owner?
+
+
+)
+
+export default AddOwnerForm
diff --git a/src/routes/safe/component/AddOwner/Review/index.jsx b/src/routes/safe/component/AddOwner/Review/index.jsx
new file mode 100644
index 00000000..6bc31b77
--- /dev/null
+++ b/src/routes/safe/component/AddOwner/Review/index.jsx
@@ -0,0 +1,43 @@
+// @flow
+import * as React from 'react'
+import { CircularProgress } from 'material-ui/Progress'
+import Block from '~/components/layout/Block'
+import Bold from '~/components/layout/Bold'
+import Heading from '~/components/layout/Heading'
+import Paragraph from '~/components/layout/Paragraph'
+import { NAME_PARAM, OWNER_ADDRESS_PARAM, INCREASE_PARAM } from '~/routes/safe/component/AddOwner/AddOwnerForm'
+
+type FormProps = {
+ values: Object,
+ submitting: boolean,
+}
+
+const spinnerStyle = {
+ minHeight: '50px',
+}
+
+const Review = () => ({ values, submitting }: FormProps) => {
+ const text = values[INCREASE_PARAM]
+ ? 'This operation will increase the threshold of the safe'
+ : 'This operation will not modify the threshold of the safe'
+
+ return (
+
+ Review the Add Owner operation
+
+ Owner Name: {values[NAME_PARAM]}
+
+
+ Owner Address: {values[OWNER_ADDRESS_PARAM]}
+
+
+ {text}
+
+
+ { submitting && }
+
+
+ )
+}
+
+export default Review
diff --git a/src/routes/safe/component/AddOwner/actions.js b/src/routes/safe/component/AddOwner/actions.js
new file mode 100644
index 00000000..e0b10a5a
--- /dev/null
+++ b/src/routes/safe/component/AddOwner/actions.js
@@ -0,0 +1,16 @@
+// @flow
+import fetchThreshold from '~/routes/safe/store/actions/fetchThreshold'
+import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
+
+type FetchThreshold = typeof fetchThreshold
+type FetchTransactions = typeof fetchTransactions
+
+export type Actions = {
+ fetchThreshold: FetchThreshold,
+ fetchTransactions: FetchTransactions,
+}
+
+export default {
+ fetchThreshold,
+ fetchTransactions,
+}
diff --git a/src/routes/safe/component/AddOwner/index.jsx b/src/routes/safe/component/AddOwner/index.jsx
new file mode 100644
index 00000000..4869314f
--- /dev/null
+++ b/src/routes/safe/component/AddOwner/index.jsx
@@ -0,0 +1,99 @@
+// @flow
+import * as React from 'react'
+import { List } from 'immutable'
+import Stepper from '~/components/Stepper'
+import { sleep } from '~/utils/timer'
+import { connect } from 'react-redux'
+import { type Safe } from '~/routes/safe/store/model/safe'
+import { type Owner } from '~/routes/safe/store/model/owner'
+import { getSafeEthereumInstance, createTransaction } from '~/routes/safe/component/AddTransaction/createTransactions'
+import AddOwnerForm, { NAME_PARAM, OWNER_ADDRESS_PARAM, INCREASE_PARAM } from './AddOwnerForm'
+import Review from './Review'
+import selector, { type SelectorProps } from './selector'
+import actions, { type Actions } from './actions'
+
+const getSteps = () => [
+ 'Fill Owner Form', 'Review Withdrawn',
+]
+
+type Props = SelectorProps & Actions & {
+ safe: Safe,
+ threshold: number,
+}
+
+type State = {
+ done: boolean,
+}
+
+export const ADD_OWNER_RESET_BUTTON_TEXT = 'RESET'
+
+const getOwnerAddressesFrom = (owners: List) => {
+ if (!owners) {
+ return []
+ }
+
+ return owners.map((owner: Owner) => owner.get('address'))
+}
+
+class AddOwner extends React.Component {
+ state = {
+ done: false,
+ }
+
+ onAddOwner = async (values: Object) => {
+ try {
+ const {
+ safe, threshold, userAddress, fetchTransactions, // fetchOwners
+ } = this.props
+ const nonce = Date.now()
+ const newThreshold = values[INCREASE_PARAM] ? threshold + 1 : threshold
+ const newOwnerAddress = values[OWNER_ADDRESS_PARAM]
+ const newOwnerName = values[NAME_PARAM]
+ const safeAddress = safe.get('address')
+ const gnosisSafe = await getSafeEthereumInstance(safeAddress)
+ const data = gnosisSafe.contract.addOwnerWithThreshold.getData(newOwnerAddress, newThreshold)
+ await createTransaction(safe, `Add Owner ${newOwnerName}`, safeAddress, 0, nonce, userAddress, data)
+ await sleep(1500)
+ fetchTransactions()
+ // fetchOwners(safeAddress)
+ this.setState({ done: true })
+ } catch (error) {
+ this.setState({ done: false })
+ // eslint-disable-next-line
+ console.log('Error while adding owner ' + error)
+ }
+ }
+
+ onReset = () => {
+ this.setState({ done: false })
+ }
+
+ render() {
+ const { safe } = this.props
+ const { done } = this.state
+ const steps = getSteps()
+ const finishedButton =
+ const addresses = getOwnerAddressesFrom(safe.get('owners'))
+
+ return (
+
+
+
+ { AddOwnerForm }
+
+
+ { Review }
+
+
+
+ )
+ }
+}
+
+export default connect(selector, actions)(AddOwner)
diff --git a/src/routes/safe/component/AddOwner/selector.js b/src/routes/safe/component/AddOwner/selector.js
new file mode 100644
index 00000000..9e7bfef1
--- /dev/null
+++ b/src/routes/safe/component/AddOwner/selector.js
@@ -0,0 +1,11 @@
+// @flow
+import { createStructuredSelector } from 'reselect'
+import { userAccountSelector } from '~/wallets/store/selectors/index'
+
+export type SelectorProps = {
+ userAddress: userAccountSelector,
+}
+
+export default createStructuredSelector({
+ userAddress: userAccountSelector,
+})
diff --git a/src/routes/safe/component/Safe/Owners.jsx b/src/routes/safe/component/Safe/Owners.jsx
index e4801fb0..4386d7ab 100644
--- a/src/routes/safe/component/Safe/Owners.jsx
+++ b/src/routes/safe/component/Safe/Owners.jsx
@@ -6,6 +6,7 @@ import Collapse from 'material-ui/transitions/Collapse'
import ListItemText from '~/components/List/ListItemText'
import List, { ListItem, ListItemIcon } from 'material-ui/List'
import Avatar from 'material-ui/Avatar'
+import Button from '~/components/layout/Button'
import Group from 'material-ui-icons/Group'
import Person from 'material-ui-icons/Person'
import ExpandLess from 'material-ui-icons/ExpandLess'
@@ -21,10 +22,13 @@ const styles = {
type Props = Open & WithStyles & {
owners: List,
+ onAddOwner: () => void,
}
+export const ADD_OWNER_BUTTON_TEXT = 'Add'
+
const Owners = openHoc(({
- open, toggle, owners, classes,
+ open, toggle, owners, classes, onAddOwner,
}: Props) => (
@@ -35,6 +39,13 @@ const Owners = openHoc(({
{open ? : }
+
diff --git a/src/routes/safe/component/Safe/index.jsx b/src/routes/safe/component/Safe/index.jsx
index 788b2561..648d315e 100644
--- a/src/routes/safe/component/Safe/index.jsx
+++ b/src/routes/safe/component/Safe/index.jsx
@@ -13,6 +13,7 @@ import Withdrawn from '~/routes/safe/component/Withdrawn'
import Transactions from '~/routes/safe/component/Transactions'
import AddTransaction from '~/routes/safe/component/AddTransaction'
import Threshold from '~/routes/safe/component/Threshold'
+import AddOwner from '~/routes/safe/component/AddOwner'
import Address from './Address'
import Balance from './Balance'
@@ -66,6 +67,12 @@ class GnoSafe extends React.PureComponent {
this.setState({ component: })
}
+ onAddOwner = () => {
+ const { safe } = this.props
+
+ this.setState({ component: })
+ }
+
render() {
const { safe, balance } = this.props
const { component } = this.state
@@ -75,7 +82,7 @@ class GnoSafe extends React.PureComponent {
-
+
diff --git a/src/routes/safe/component/Transactions/index.jsx b/src/routes/safe/component/Transactions/index.jsx
index e8a22e87..1518ed62 100644
--- a/src/routes/safe/component/Transactions/index.jsx
+++ b/src/routes/safe/component/Transactions/index.jsx
@@ -25,6 +25,7 @@ class Transactions extends React.Component {
await sleep(1200)
fetchTransactions()
fetchThreshold(safeAddress)
+ // fetchOwners(safeAddress)
}
render() {