Skip to content
This repository was archived by the owner on Nov 10, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/components/forms/validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export const required = (value: Field) => (value ? undefined : 'Required')
export const mustBeNumber = (value: number) =>
(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)) {
return undefined
}

return `Should be greater than ${min}`
}

export const minValue = (min: number) => (value: string) => {
if (Number.isNaN(Number(value)) || Number.parseInt(value, 10) >= Number(min)) {
return undefined
Expand Down
13 changes: 12 additions & 1 deletion src/routes/open/components/Layout.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
// @flow
import TestUtils from 'react-dom/test-utils'
import { store } from '~/store'
import { FIELD_NAME, FIELD_OWNERS, FIELD_CONFIRMATIONS, getOwnerNameBy, getOwnerAddressBy } from '~/routes/open/components/fields'
import {
FIELD_NAME,
FIELD_OWNERS,
FIELD_CONFIRMATIONS,
FIELD_DAILY_LIMIT,
getOwnerNameBy,
getOwnerAddressBy,
} from '~/routes/open/components/fields'
import { DEPLOYED_COMPONENT_ID } from '~/routes/open/components/FormConfirmation'
import { sleep } from '~/utils/timer'
import { getProviderInfo } from '~/wallets/getWeb3'
Expand All @@ -26,6 +33,9 @@ describe('React DOM TESTS > Create Safe form', () => {
const fieldConfirmations = inputs[2]
expect(fieldConfirmations.name).toEqual(FIELD_CONFIRMATIONS)

const dailyLimitConfirmations = inputs[3]
expect(dailyLimitConfirmations.name).toEqual(FIELD_DAILY_LIMIT)

TestUtils.Simulate.change(fieldOwners, { target: { value: '1' } })
const inputsExpanded = TestUtils.scryRenderedDOMComponentsWithTag(open, 'input')

Expand All @@ -39,6 +49,7 @@ describe('React DOM TESTS > Create Safe form', () => {
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
TestUtils.Simulate.change(fieldConfirmations, { target: { value: '1' } })
TestUtils.Simulate.change(ownerName, { target: { value: 'Adolfo Eth Account' } })
TestUtils.Simulate.change(dailyLimitConfirmations, { target: { value: '10' } })

const form = TestUtils.findRenderedDOMComponentWithTag(open, 'form')
// One submit per step when creating a safe
Expand Down
8 changes: 6 additions & 2 deletions src/routes/open/components/ReviewInformation/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Col from '~/components/layout/Col'
import Heading from '~/components/layout/Heading'
import Row from '~/components/layout/Row'
import Paragraph from '~/components/layout/Paragraph'
import { FIELD_NAME, FIELD_CONFIRMATIONS, FIELD_DAILY_LIMIT } from '../fields'

type FormProps = {
values: Object,
Expand All @@ -20,10 +21,13 @@ const ReviewInformation = () => ({ values }: FormProps) => {
<Block>
<Heading tag="h2">Review the Safe information</Heading>
<Paragraph>
<Bold>Safe Name: </Bold> {values.name}
<Bold>Safe Name: </Bold> {values[FIELD_NAME]}
</Paragraph>
<Paragraph>
<Bold>Required confirmations: </Bold> {values.confirmations}
<Bold>Required confirmations: </Bold> {values[FIELD_CONFIRMATIONS]}
</Paragraph>
<Paragraph>
<Bold>Daily limit: </Bold> {values[FIELD_DAILY_LIMIT]} ETH
</Paragraph>
<Heading tag="h3">Owners</Heading>
{ names.map((name, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('React DOM TESTS > Create Safe form', () => {

// THEN
const muiFields = TestUtils.scryRenderedComponentsWithType(open, TextField)
expect(5).toEqual(muiFields.length)
expect(6).toEqual(muiFields.length)
const confirmationsField = muiFields[4]

expect(confirmationsField.props.meta.valid).toBe(false)
Expand All @@ -64,7 +64,7 @@ describe('React DOM TESTS > Create Safe form', () => {

// THEN
const muiFields = TestUtils.scryRenderedComponentsWithType(open, TextField)
expect(7).toEqual(muiFields.length)
expect(8).toEqual(muiFields.length)
const confirmationsField = muiFields[6]

expect(confirmationsField.props.meta.valid).toBe(false)
Expand Down
22 changes: 22 additions & 0 deletions src/routes/open/components/SafeForm/DailyLimit/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @flow
import * as React from 'react'
import Field from '~/components/forms/Field'
import TextField from '~/components/forms/TextField'
import { composeValidators, mustBeNumber, required, greaterThan } from '~/components/forms/validator'
import Block from '~/components/layout/Block'
import { FIELD_DAILY_LIMIT } from '~/routes/open/components/fields'

const DailyLimit = () => (
<Block margin="md">
<Field
name={FIELD_DAILY_LIMIT}
component={TextField}
type="text"
validate={composeValidators(required, mustBeNumber, greaterThan(0))}
placeholder="Daily Limit*"
text="Daily Limit"
/>
</Block>
)

export default DailyLimit
2 changes: 2 additions & 0 deletions src/routes/open/components/SafeForm/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
import Name from './Name'
import Owners from './Owners'
import Confirmations from './Confirmations'
import DailyLimit from './DailyLimit'

export const CONFIRMATIONS_ERROR = 'Number of confirmations can not be higher than the number of owners'

Expand All @@ -25,5 +26,6 @@ export default () => ({ values }: Object) => (
<Name />
<Owners numOwners={values.owners} otherAccounts={getAccountsFrom(values)} />
<Confirmations />
<DailyLimit />
</Block>
)
1 change: 1 addition & 0 deletions src/routes/open/components/fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export const FIELD_NAME: string = 'name'
export const FIELD_CONFIRMATIONS: string = 'confirmations'
export const FIELD_OWNERS: string = 'owners'
export const FIELD_DAILY_LIMIT: string = 'limit'

export const getOwnerNameBy = (index: number) => `owner${index}Name`
export const getOwnerAddressBy = (index: number) => `owner${index}Address`
5 changes: 3 additions & 2 deletions src/routes/open/container/Open.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from 'react'
import { connect } from 'react-redux'
import contract from 'truffle-contract'
import Page from '~/components/layout/Page'
import { getAccountsFrom, getThresholdFrom, getNamesFrom, getSafeNameFrom } from '~/routes/open/utils/safeDataExtractor'
import { getAccountsFrom, getThresholdFrom, getNamesFrom, getSafeNameFrom, getDailyLimitFrom } from '~/routes/open/utils/safeDataExtractor'
import { getWeb3 } from '~/wallets/getWeb3'
import { promisify } from '~/utils/promisify'
import Safe from '#/GnosisSafe.json'
Expand All @@ -26,12 +26,13 @@ const createSafe = async (safeContract, values, userAccount, addSafe) => {
const numConfirmations = getThresholdFrom(values)
const name = getSafeNameFrom(values)
const owners = getNamesFrom(values)
const dailyLimit = getDailyLimitFrom(values)

const web3 = getWeb3()
safeContract.setProvider(web3.currentProvider)

const safe = await safeContract.new(accounts, numConfirmations, 0, 0, { from: userAccount, gas: '5000000' })
addSafe(name, safe.address, numConfirmations, owners, accounts)
addSafe(name, safe.address, numConfirmations, dailyLimit, owners, accounts)
return safe
}

Expand Down
2 changes: 2 additions & 0 deletions src/routes/open/utils/safeDataExtractor.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// @flow
export const getDailyLimitFrom = (values: Object): number => Number(values.limit)

export const getAccountsFrom = (values: Object): string[] => {
const accounts = Object.keys(values).sort().filter(key => /^owner\d+Address$/.test(key))

Expand Down
21 changes: 21 additions & 0 deletions src/routes/safe/component/Safe/DailyLimit.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// @flow
import * as React from 'react'
import { ListItem } from 'material-ui/List'
import Avatar from 'material-ui/Avatar'
import NotificationsPaused from 'material-ui-icons/NotificationsPaused'
import ListItemText from '~/components/List/ListItemText'

type Props = {
limit: number,
}

const DailyLimit = ({ limit }: Props) => (
<ListItem>
<Avatar>
<NotificationsPaused />
</Avatar>
<ListItemText primary="Daily Limit" secondary={`${limit} ETH`} />
</ListItem>
)

export default DailyLimit
2 changes: 1 addition & 1 deletion src/routes/safe/component/Safe/Owners.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const Owners = openHoc(({
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{owners.map(owner => (
<ListItem key={owner.address} button className={classes.nested}>
<ListItem key={owner.address} className={classes.nested}>
<ListItemIcon>
<Person />
</ListItemIcon>
Expand Down
3 changes: 3 additions & 0 deletions src/routes/safe/component/Safe/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Address from './Address'
import Balance from './Balance'
import Owners from './Owners'
import Confirmations from './Confirmations'
import DailyLimit from './DailyLimit'

type SafeProps = {
safe: Safe,
Expand All @@ -20,6 +21,7 @@ type SafeProps = {

const listStyle = {
width: '100%',
minWidth: '485px',
}

class GnoSafe extends React.PureComponent<SafeProps> {
Expand All @@ -34,6 +36,7 @@ class GnoSafe extends React.PureComponent<SafeProps> {
<Owners owners={safe.owners} />
<Confirmations confirmations={safe.get('confirmations')} />
<Address address={safe.get('address')} />
<DailyLimit limit={safe.get('dailyLimit')} />
</List>
</Col>
<Col xs={12} center="xs" sm={8} margin="xl" layout="block">
Expand Down
5 changes: 3 additions & 2 deletions src/routes/safe/store/actions/addSafe.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ export const buildOwnersFrom = (names: string[], addresses: string[]) => {
const addSafe = createAction(
ADD_SAFE,
(
name: string, address: string, confirmations: number,
name: string, address: string,
confirmations: number, dailyLimit: number,
ownersName: string[], ownersAddress: string[],
): SafeProps => {
const owners: List<Owner> = buildOwnersFrom(ownersName, ownersAddress)

return ({
address, name, confirmations, owners,
address, name, confirmations, owners, dailyLimit,
})
},
)
Expand Down
2 changes: 2 additions & 0 deletions src/routes/safe/store/model/safe.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ export type SafeProps = {
address: string,
confirmations: number,
owners: List<Owner>,
dailyLimit: number,
}

export const makeSafe: RecordFactory<SafeProps> = Record({
name: '',
address: '',
confirmations: 0,
owners: List([]),
dailyLimit: 0,
})

export type Safe = RecordOf<SafeProps>
Expand Down
2 changes: 2 additions & 0 deletions src/routes/safe/store/test/builder/deployedSafe.builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const deploySafe = async (safe: React$Component<{}>) => {
const fieldName = inputs[0]
const fieldOwners = inputs[1]
const fieldConfirmations = inputs[2]
const fieldDailyLimit = inputs[3]

TestUtils.Simulate.change(fieldOwners, { target: { value: '1' } })
const inputsExpanded = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
Expand All @@ -41,6 +42,7 @@ const deploySafe = async (safe: React$Component<{}>) => {
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
TestUtils.Simulate.change(fieldConfirmations, { target: { value: '1' } })
TestUtils.Simulate.change(ownerName, { target: { value: 'Adolfo Eth Account' } })
TestUtils.Simulate.change(fieldDailyLimit, { target: { value: '10' } })

const form = TestUtils.findRenderedDOMComponentWithTag(safe, 'form')

Expand Down
6 changes: 6 additions & 0 deletions src/routes/safe/store/test/builder/safe.builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class SafeBuilder {
return this
}

withDailyLimit(limit: number) {
this.safe = this.safe.set('dailyLimit', limit)
return this
}

withOwner(names: string[], adresses: string[]) {
const owners = buildOwnersFrom(names, adresses)
this.safe = this.safe.set('owners', owners)
Expand All @@ -42,6 +47,7 @@ export class SafeFactory {
.withAddress('0x03db1a8b26d08df23337e9276a36b474510f0025')
.withName('Adol ICO Safe')
.withConfirmations(1)
.withDailyLimit(10)
.withOwner(['Adol Metamask'], ['0x03db1a8b26d08df23337e9276a36b474510f0023'])
.get()

Expand Down
28 changes: 12 additions & 16 deletions src/routes/safe/store/test/safe.reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,31 @@ const aStore = (initState) => {
const providerReducerTests = () => {
describe('Safe Actions[addSafe]', () => {
let store
let address
let formValues
beforeEach(() => {
store = aStore()
})

it('reducer should return SafeRecord from form values', () => {
// GIVEN
const address = '0x03db1a8b26d08df23337e9276a36b474510f0025'
const formValues = {
address = '0x03db1a8b26d08df23337e9276a36b474510f0025'
formValues = {
[SafeFields.FIELD_NAME]: 'Adol ICO Safe',
[SafeFields.FIELD_CONFIRMATIONS]: 1,
[SafeFields.FIELD_OWNERS]: 1,
[SafeFields.FIELD_DAILY_LIMIT]: 10,
[SafeFields.getOwnerAddressBy(0)]: '0x03db1a8b26d08df23337e9276a36b474510f0023',
[SafeFields.getOwnerNameBy(0)]: 'Adol Metamask',
address,
}
})

it('reducer should return SafeRecord from form values', () => {
// GIVEN in beforeEach method

// WHEN
store.dispatch(addSafe(
formValues[SafeFields.FIELD_NAME],
formValues.address,
formValues[SafeFields.FIELD_CONFIRMATIONS],
formValues[SafeFields.FIELD_DAILY_LIMIT],
getNamesFrom(formValues),
getAccountsFrom(formValues),
))
Expand All @@ -54,22 +58,14 @@ const providerReducerTests = () => {
})

it('reducer loads information from localStorage', async () => {
// GIVEN
const address = '0x03db1a8b26d08df23337e9276a36b474510f0025'
const formValues = {
[SafeFields.FIELD_NAME]: 'Adol ICO Safe',
[SafeFields.FIELD_CONFIRMATIONS]: 1,
[SafeFields.FIELD_OWNERS]: 1,
[SafeFields.getOwnerAddressBy(0)]: '0x03db1a8b26d08df23337e9276a36b474510f0023',
[SafeFields.getOwnerNameBy(0)]: 'Adol Metamask',
address,
}
// GIVEN in beforeEach method

// WHEN
store.dispatch(addSafe(
formValues[SafeFields.FIELD_NAME],
formValues.address,
formValues[SafeFields.FIELD_CONFIRMATIONS],
formValues[SafeFields.FIELD_DAILY_LIMIT],
getNamesFrom(formValues),
getAccountsFrom(formValues),
))
Expand Down
10 changes: 6 additions & 4 deletions src/routes/safeList/components/SafeTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const SafeTable = ({ safes }: Props) => (
<TableCell>Deployed Address</TableCell>
<TableCell numeric>Confirmations</TableCell>
<TableCell numeric>Number of owners</TableCell>
<TableCell numeric>Daily Limit</TableCell>
</TableRow>
</TableHead>
<TableBody>
Expand All @@ -29,10 +30,11 @@ const SafeTable = ({ safes }: Props) => (
<Button variant="raised" size="small" color="primary">Open</Button>
</Link>
</TableCell>
<TableCell padding="none">{safe.name}</TableCell>
<TableCell padding="none">{safe.address}</TableCell>
<TableCell padding="none" numeric>{safe.confirmations}</TableCell>
<TableCell padding="none" numeric>{safe.owners.count()}</TableCell>
<TableCell padding="none">{safe.get('name')}</TableCell>
<TableCell padding="none">{safe.get('address')}</TableCell>
<TableCell padding="none" numeric>{safe.get('confirmations')}</TableCell>
<TableCell padding="none" numeric>{safe.get('owners').count()}</TableCell>
<TableCell padding="none" numeric>{`${safe.get('dailyLimit')} ETH`}</TableCell>
</TableRow>
))}
</TableBody>
Expand Down