Merge pull request #138 from gnosis/126-onboarding-improvements
Feature #126: Onboarding improvements
This commit is contained in:
commit
6d31f1b0e6
35
package.json
35
package.json
|
@ -31,7 +31,7 @@
|
|||
"dependencies": {
|
||||
"@gnosis.pm/safe-contracts": "^1.0.0",
|
||||
"@gnosis.pm/util-contracts": "2.0.1",
|
||||
"@material-ui/core": "4.2.0",
|
||||
"@material-ui/core": "4.2.1",
|
||||
"@material-ui/icons": "4.2.1",
|
||||
"@testing-library/jest-dom": "^4.0.0",
|
||||
"@welldone-software/why-did-you-render": "3.2.1",
|
||||
|
@ -50,8 +50,9 @@
|
|||
"react-dom": "^16.8.6",
|
||||
"react-final-form": "6.3.0",
|
||||
"react-final-form-listeners": "^1.0.2",
|
||||
"react-hot-loader": "4.12.7",
|
||||
"react-hot-loader": "4.12.8",
|
||||
"react-infinite-scroll-component": "^4.5.2",
|
||||
"react-qr-reader": "^2.2.1",
|
||||
"react-redux": "7.1.0",
|
||||
"react-router-dom": "^5.0.1",
|
||||
"recompose": "^0.30.0",
|
||||
|
@ -62,9 +63,9 @@
|
|||
"web3": "1.0.0-beta.37"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.5.0",
|
||||
"@babel/core": "7.5.4",
|
||||
"@babel/plugin-proposal-class-properties": "7.5.0",
|
||||
"@babel/cli": "7.5.5",
|
||||
"@babel/core": "7.5.5",
|
||||
"@babel/plugin-proposal-class-properties": "7.5.5",
|
||||
"@babel/plugin-proposal-decorators": "7.4.4",
|
||||
"@babel/plugin-proposal-do-expressions": "7.5.0",
|
||||
"@babel/plugin-proposal-export-default-from": "7.5.2",
|
||||
|
@ -83,7 +84,7 @@
|
|||
"@babel/plugin-transform-member-expression-literals": "^7.2.0",
|
||||
"@babel/plugin-transform-property-literals": "^7.2.0",
|
||||
"@babel/polyfill": "7.4.4",
|
||||
"@babel/preset-env": "7.5.4",
|
||||
"@babel/preset-env": "7.5.5",
|
||||
"@babel/preset-flow": "^7.0.0-beta.40",
|
||||
"@babel/preset-react": "^7.0.0-beta.40",
|
||||
"@sambego/storybook-state": "^1.0.7",
|
||||
|
@ -101,19 +102,19 @@
|
|||
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
|
||||
"babel-plugin-transform-es3-property-literals": "^6.22.0",
|
||||
"classnames": "^2.2.5",
|
||||
"css-loader": "3.0.0",
|
||||
"css-loader": "3.1.0",
|
||||
"detect-port": "^1.2.2",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-airbnb": "17.1.1",
|
||||
"eslint-plugin-flowtype": "3.11.1",
|
||||
"eslint-plugin-import": "2.18.0",
|
||||
"eslint-plugin-jest": "22.9.0",
|
||||
"eslint-plugin-flowtype": "3.12.1",
|
||||
"eslint-plugin-import": "2.18.1",
|
||||
"eslint-plugin-jest": "22.11.1",
|
||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||
"eslint-plugin-react": "7.14.2",
|
||||
"ethereumjs-abi": "^0.6.7",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"file-loader": "4.0.0",
|
||||
"flow-bin": "0.102.0",
|
||||
"file-loader": "4.1.0",
|
||||
"flow-bin": "0.103.0",
|
||||
"fs-extra": "8.1.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^3.0.4",
|
||||
|
@ -122,7 +123,7 @@
|
|||
"json-loader": "^0.5.7",
|
||||
"mini-css-extract-plugin": "0.8.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-mixins": "^6.2.0",
|
||||
"postcss-mixins": "6.2.2",
|
||||
"postcss-simple-vars": "^5.0.2",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier-eslint-cli": "5.0.0",
|
||||
|
@ -130,11 +131,11 @@
|
|||
"storybook-host": "5.1.0",
|
||||
"storybook-router": "^0.3.3",
|
||||
"style-loader": "^0.23.1",
|
||||
"truffle": "5.0.27",
|
||||
"truffle-contract": "4.0.24",
|
||||
"truffle-solidity-loader": "0.1.26",
|
||||
"truffle": "5.0.28",
|
||||
"truffle-contract": "4.0.25",
|
||||
"truffle-solidity-loader": "0.1.27",
|
||||
"uglifyjs-webpack-plugin": "2.1.3",
|
||||
"webpack": "4.35.3",
|
||||
"webpack": "4.36.1",
|
||||
"webpack-bundle-analyzer": "3.3.2",
|
||||
"webpack-cli": "3.3.6",
|
||||
"webpack-dev-server": "3.7.2",
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>Group</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Icon-/-QR-Icon" transform="translate(-11.000000, -11.000000)" fill="#a2a8ba">
|
||||
<g id="QR-Icon" transform="translate(2.000000, 2.000000)">
|
||||
<g id="Group" transform="translate(9.000000, 9.000000)">
|
||||
<path d="M2,2 L2,4 L4,4 L4,2 L2,2 Z M6,0 L6,6 L0,6 L0,0 L6,0 Z" id="Rectangle" fill-rule="nonzero"></path>
|
||||
<path d="M2,12 L2,14 L4,14 L4,12 L2,12 Z M6,10 L6,16 L0,16 L0,10 L6,10 Z" id="Rectangle-Copy-2" fill-rule="nonzero"></path>
|
||||
<path d="M12,2 L12,4 L14,4 L14,2 L12,2 Z M16,0 L16,6 L10,6 L10,0 L16,0 Z" id="Rectangle-Copy" fill-rule="nonzero"></path>
|
||||
<rect id="Rectangle" x="7" y="2" width="2" height="4"></rect>
|
||||
<path d="M7,9 L5,9 L5,7 L7,7 L9,7 L9,11 L7,11 L7,9 Z" id="Combined-Shape"></path>
|
||||
<rect id="Rectangle-Copy-4" x="0" y="7" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-Copy-5" x="10" y="7" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-Copy-6" x="14" y="7" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-Copy-7" x="12" y="9" width="2" height="2"></rect>
|
||||
<rect id="Rectangle-Copy-10" x="12" y="14" width="2" height="2"></rect>
|
||||
<path d="M9,12 L10,12 L10,11 L12,11 L12,14 L10,14 L9,14 L9,16 L7,16 L7,12 L9,12 Z" id="Combined-Shape"></path>
|
||||
<rect id="Rectangle-Copy-9" x="14" y="11" width="2" height="3"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Page from '~/components/layout/Page'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Page from '~/components/layout/Page'
|
||||
|
||||
const centerStyle = {
|
||||
margin: 'auto 0',
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Stepper from '@material-ui/core/Stepper'
|
||||
import FormStep from '@material-ui/core/Step'
|
||||
import StepLabel from '@material-ui/core/StepLabel'
|
||||
import StepContent from '@material-ui/core/StepContent'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import * as React from 'react'
|
||||
import GnoForm from '~/components/forms/GnoForm'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import Button from '~/components/layout/Button'
|
||||
import { history } from '~/store'
|
||||
import Controls from './Controls'
|
||||
|
||||
const { useState, useEffect } = React
|
||||
|
||||
export { default as Step } from './Step'
|
||||
|
||||
type Props = {
|
||||
|
@ -18,20 +19,15 @@ type Props = {
|
|||
onSubmit: (values: Object) => Promise<void>,
|
||||
children: React.Node,
|
||||
classes: Object,
|
||||
onReset?: () => void,
|
||||
initialValues?: Object,
|
||||
disabledWhenValidating?: boolean,
|
||||
mutators?: Object,
|
||||
testId?: string,
|
||||
}
|
||||
|
||||
type State = {
|
||||
page: number,
|
||||
values: Object,
|
||||
}
|
||||
|
||||
type PageProps = {
|
||||
children: Function,
|
||||
prepareNextInitialProps: (values: Object) => {},
|
||||
prepareNextInitialProps?: (values: Object) => {},
|
||||
}
|
||||
|
||||
const transitionProps = {
|
||||
|
@ -41,151 +37,124 @@ const transitionProps = {
|
|||
},
|
||||
}
|
||||
|
||||
class GnoStepper extends React.PureComponent<Props, State> {
|
||||
static Page = ({ children }: PageProps) => children
|
||||
export const StepperPage = ({ children }: PageProps) => children
|
||||
|
||||
static FinishButton = ({
|
||||
component, to, title, ...props
|
||||
}) => (
|
||||
<Button component={component} to={to} variant="contained" color="primary" {...props}>
|
||||
{title}
|
||||
</Button>
|
||||
)
|
||||
const GnoStepper = (props: Props) => {
|
||||
const [page, setPage] = useState<number>(0)
|
||||
const [values, setValues] = useState<Object>({})
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
page: 0,
|
||||
values: props.initialValues || {},
|
||||
useEffect(() => {
|
||||
if (props.initialValues) {
|
||||
setValues(props.initialValues)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const getPageProps = (pages: React.Node): PageProps => React.Children.toArray(pages)[page].props
|
||||
|
||||
const updateInitialProps = (newInitialProps) => {
|
||||
setValues(newInitialProps)
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
const { onReset, initialValues } = this.props
|
||||
if (onReset) {
|
||||
onReset()
|
||||
}
|
||||
const getActivePageFrom = (pages: React.Node) => {
|
||||
const activePageProps = getPageProps(pages)
|
||||
const { children, ...restProps } = activePageProps
|
||||
|
||||
this.setState(() => ({
|
||||
page: 0,
|
||||
values: initialValues || {},
|
||||
}))
|
||||
return children({ ...restProps, updateInitialProps })
|
||||
}
|
||||
|
||||
getPageProps = (pages: React.Node): PageProps => {
|
||||
const { page } = this.state
|
||||
|
||||
return React.Children.toArray(pages)[page].props
|
||||
}
|
||||
|
||||
getActivePageFrom = (pages: React.Node) => {
|
||||
const activePageProps = this.getPageProps(pages)
|
||||
const { children, ...props } = activePageProps
|
||||
|
||||
return children({ ...props, updateInitialProps: this.updateInitialProps })
|
||||
}
|
||||
|
||||
updateInitialProps = (values) => {
|
||||
this.setState({ values })
|
||||
}
|
||||
|
||||
validate = (values: Object) => {
|
||||
const { children } = this.props
|
||||
const { page } = this.state
|
||||
const validate = (valuesToValidate: Object) => {
|
||||
const { children } = props
|
||||
|
||||
const activePage = React.Children.toArray(children)[page]
|
||||
return activePage.props.validate ? activePage.props.validate(values) : {}
|
||||
return activePage.props.validate ? activePage.props.validate(valuesToValidate) : {}
|
||||
}
|
||||
|
||||
next = async (values: Object) => {
|
||||
const { children } = this.props
|
||||
const activePageProps = this.getPageProps(children)
|
||||
const next = async (formValues: Object) => {
|
||||
const { children } = props
|
||||
const activePageProps = getPageProps(children)
|
||||
const { prepareNextInitialProps } = activePageProps
|
||||
|
||||
let pageInitialProps
|
||||
if (prepareNextInitialProps) {
|
||||
pageInitialProps = await prepareNextInitialProps(values)
|
||||
pageInitialProps = await prepareNextInitialProps(formValues)
|
||||
}
|
||||
|
||||
const finalValues = { ...values, ...pageInitialProps }
|
||||
this.setState(state => ({
|
||||
page: Math.min(state.page + 1, React.Children.count(children) - 1),
|
||||
values: finalValues,
|
||||
}))
|
||||
const finalValues = { ...formValues, ...pageInitialProps }
|
||||
|
||||
setValues(finalValues)
|
||||
setPage(Math.min(page + 1, React.Children.count(children) - 1))
|
||||
}
|
||||
|
||||
previous = () => {
|
||||
const { page } = this.state
|
||||
|
||||
const previous = () => {
|
||||
const firstPage = page === 0
|
||||
if (firstPage) {
|
||||
return history.goBack()
|
||||
}
|
||||
|
||||
return this.setState(state => ({
|
||||
page: Math.max(state.page - 1, 0),
|
||||
}))
|
||||
return setPage(Math.max(page - 1, 0))
|
||||
}
|
||||
|
||||
handleSubmit = async (values: Object) => {
|
||||
const { children, onSubmit } = this.props
|
||||
const { page } = this.state
|
||||
const handleSubmit = async (formValues: Object) => {
|
||||
const { children, onSubmit } = props
|
||||
const isLastPage = page === React.Children.count(children) - 1
|
||||
if (isLastPage) {
|
||||
return onSubmit(values)
|
||||
return onSubmit(formValues)
|
||||
}
|
||||
|
||||
return this.next(values)
|
||||
return next(formValues)
|
||||
}
|
||||
|
||||
isLastPage = (page) => {
|
||||
const { steps } = this.props
|
||||
return page === steps.length - 1
|
||||
const isLastPage = (pageNumber) => {
|
||||
const { steps } = props
|
||||
return pageNumber === steps.length - 1
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
steps, children, classes, disabledWhenValidating = false, testId,
|
||||
} = this.props
|
||||
const { page, values } = this.state
|
||||
const activePage = this.getActivePageFrom(children)
|
||||
const lastPage = this.isLastPage(page)
|
||||
const penultimate = this.isLastPage(page + 1)
|
||||
const {
|
||||
steps, children, classes, disabledWhenValidating = false, testId, mutators,
|
||||
} = props
|
||||
const activePage = getActivePageFrom(children)
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GnoForm onSubmit={this.handleSubmit} initialValues={values} validation={this.validate} testId={testId}>
|
||||
{(submitting: boolean, validating: boolean, ...rest: any) => {
|
||||
const disabled = disabledWhenValidating ? submitting || validating : submitting
|
||||
const controls = (
|
||||
<React.Fragment>
|
||||
<Hairline />
|
||||
<Controls
|
||||
disabled={disabled}
|
||||
onPrevious={this.previous}
|
||||
firstPage={page === 0}
|
||||
lastPage={lastPage}
|
||||
penultimate={penultimate}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
const lastPage = isLastPage(page)
|
||||
const penultimate = isLastPage(page + 1)
|
||||
|
||||
return (
|
||||
<Stepper classes={{ root: classes.root }} activeStep={page} orientation="vertical">
|
||||
{steps.map(label => (
|
||||
<FormStep key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
<StepContent TransitionProps={transitionProps}>{activePage(controls, ...rest)}</StepContent>
|
||||
</FormStep>
|
||||
))}
|
||||
</Stepper>
|
||||
)
|
||||
}}
|
||||
</GnoForm>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GnoForm
|
||||
onSubmit={handleSubmit}
|
||||
initialValues={values}
|
||||
validation={validate}
|
||||
testId={testId}
|
||||
formMutators={mutators}
|
||||
>
|
||||
{(submitting: boolean, validating: boolean, ...rest: any) => {
|
||||
const disabled = disabledWhenValidating ? submitting || validating : submitting
|
||||
const controls = (
|
||||
<React.Fragment>
|
||||
<Hairline />
|
||||
<Controls
|
||||
disabled={disabled}
|
||||
onPrevious={previous}
|
||||
firstPage={page === 0}
|
||||
lastPage={lastPage}
|
||||
penultimate={penultimate}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
return (
|
||||
<Stepper classes={{ root: classes.root }} activeStep={page} orientation="vertical">
|
||||
{steps.map(label => (
|
||||
<FormStep key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
<StepContent TransitionProps={transitionProps}>{activePage(controls, ...rest)}</StepContent>
|
||||
</FormStep>
|
||||
))}
|
||||
</Stepper>
|
||||
)
|
||||
}}
|
||||
</GnoForm>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// @flow
|
||||
import classNames from 'classnames/bind'
|
||||
import React, { PureComponent } from 'react'
|
||||
import * as React from 'react'
|
||||
import { capitalize } from '~/utils/css'
|
||||
import { type Size } from '~/theme/size'
|
||||
import styles from './index.scss'
|
||||
|
||||
const { PureComponent } = React
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
||||
type Props = {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import * as React from 'react'
|
||||
import ChevronLeft from '@material-ui/icons/ChevronLeft'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import Stepper, { StepperPage } from '~/components/Stepper'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Row from '~/components/layout/Row'
|
||||
|
@ -46,11 +46,11 @@ const Layout = ({
|
|||
<Heading tag="h2">Load existing Safe</Heading>
|
||||
</Row>
|
||||
<Stepper onSubmit={onLoadSafeSubmit} steps={steps} initialValues={initialValues} testId="load-safe-form">
|
||||
<Stepper.Page validate={safeFieldsValidation}>{DetailsForm}</Stepper.Page>
|
||||
<Stepper.Page network={network}>{OwnerList}</Stepper.Page>
|
||||
<Stepper.Page network={network} userAddress={userAddress}>
|
||||
<StepperPage validate={safeFieldsValidation}>{DetailsForm}</StepperPage>
|
||||
<StepperPage network={network}>{OwnerList}</StepperPage>
|
||||
<StepperPage network={network} userAddress={userAddress}>
|
||||
{ReviewInformation}
|
||||
</Stepper.Page>
|
||||
</StepperPage>
|
||||
</Stepper>
|
||||
</Block>
|
||||
) : (
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import ChevronLeft from '@material-ui/icons/ChevronLeft'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Stepper, { StepperPage } from '~/components/Stepper'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Row from '~/components/layout/Row'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Review from '~/routes/open/components/ReviewInformation'
|
||||
import ChevronLeft from '@material-ui/icons/ChevronLeft'
|
||||
import SafeNameField from '~/routes/open/components/SafeNameForm'
|
||||
import SafeThresholdField, { safeFieldsValidation } from '~/routes/open/components/SafeThresholdForm'
|
||||
import SafeOwnersFields from '~/routes/open/components/SafeOwnersForm'
|
||||
import SafeOwnersFields from '~/routes/open/components/SafeOwnersConfirmationsForm'
|
||||
import { getOwnerNameBy, getOwnerAddressBy, FIELD_CONFIRMATIONS } from '~/routes/open/components/fields'
|
||||
import { history } from '~/store'
|
||||
import { secondary } from '~/theme/variables'
|
||||
|
||||
const getSteps = () => ['Start', 'Owners', 'Confirmations', 'Review']
|
||||
const getSteps = () => ['Start', 'Owners and confirmations', 'Review']
|
||||
|
||||
const initialValuesFrom = (userAccount: string) => ({
|
||||
[getOwnerNameBy(0)]: 'My Metamask (me)',
|
||||
|
@ -39,6 +38,12 @@ const back = () => {
|
|||
history.goBack()
|
||||
}
|
||||
|
||||
const formMutators = {
|
||||
setValue: ([field, value], state, { changeValue }) => {
|
||||
changeValue(state, field, () => value)
|
||||
},
|
||||
}
|
||||
|
||||
const Layout = ({
|
||||
provider, userAccount, onCallSafeContractSubmit, network,
|
||||
}: Props) => {
|
||||
|
@ -55,15 +60,20 @@ const Layout = ({
|
|||
</IconButton>
|
||||
<Heading tag="h2">Create New Safe</Heading>
|
||||
</Row>
|
||||
<Stepper onSubmit={onCallSafeContractSubmit} steps={steps} initialValues={initialValues} testId="create-safe-form">
|
||||
<Stepper.Page>{SafeNameField}</Stepper.Page>
|
||||
<Stepper.Page>{SafeOwnersFields}</Stepper.Page>
|
||||
<Stepper.Page validate={safeFieldsValidation}>{SafeThresholdField}</Stepper.Page>
|
||||
<Stepper.Page network={network}>{Review}</Stepper.Page>
|
||||
<Stepper
|
||||
onSubmit={onCallSafeContractSubmit}
|
||||
steps={steps}
|
||||
initialValues={initialValues}
|
||||
mutators={formMutators}
|
||||
testId="create-safe-form"
|
||||
>
|
||||
<StepperPage>{SafeNameField}</StepperPage>
|
||||
<StepperPage>{SafeOwnersFields}</StepperPage>
|
||||
<StepperPage network={network}>{Review}</StepperPage>
|
||||
</Stepper>
|
||||
</Block>
|
||||
) : (
|
||||
<div>No metamask detected</div>
|
||||
<div>No web3 provider detected</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
|
|
|
@ -129,7 +129,11 @@ const ReviewComponent = ({ values, classes, network }: Props) => {
|
|||
<Paragraph size="md" color="disabled" noMargin>
|
||||
{addresses[index]}
|
||||
</Paragraph>
|
||||
<Link className={classes.open} to={getEtherScanLink('address', addresses[index], network)} target="_blank">
|
||||
<Link
|
||||
className={classes.open}
|
||||
to={getEtherScanLink('address', addresses[index], network)}
|
||||
target="_blank"
|
||||
>
|
||||
<OpenInNew style={openIconStyle} />
|
||||
</Link>
|
||||
</Block>
|
||||
|
@ -143,10 +147,8 @@ const ReviewComponent = ({ values, classes, network }: Props) => {
|
|||
</Row>
|
||||
<Row className={classes.info} align="center">
|
||||
<Paragraph noMargin color="primary" size="md">
|
||||
{"You're about to create a new Safe."}
|
||||
</Paragraph>
|
||||
<Paragraph noMargin color="primary" size="md">
|
||||
Make sure you have enough ETH in your wallet client to fund this transaction.
|
||||
You're about to create a new Safe and will have to confirm a transaction with your currently connected
|
||||
wallet. Make sure you have ETH in this wallet to fund this transaction.
|
||||
</Paragraph>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -5,7 +5,6 @@ import Field from '~/components/forms/Field'
|
|||
import TextField from '~/components/forms/TextField'
|
||||
import { required } from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Row from '~/components/layout/Row'
|
||||
import { FIELD_NAME } from '~/routes/open/components/fields'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
|
@ -35,10 +34,25 @@ const styles = () => ({
|
|||
|
||||
const SafeName = ({ classes }: Props) => (
|
||||
<React.Fragment>
|
||||
<Block margin="lg">
|
||||
<Paragraph noMargin size="md" color="primary">
|
||||
You are about to create a new Gnosis Safe wallet with one or more owners. First, let's give your new wallet
|
||||
a name. This name is only stored locally and will never be shared with Gnosis or any third parties.
|
||||
</Paragraph>
|
||||
</Block>
|
||||
<Block margin="lg" className={classes.root}>
|
||||
<Field
|
||||
name={FIELD_NAME}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={required}
|
||||
placeholder="Name of the new Safe"
|
||||
text="Safe name"
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="lg">
|
||||
<Paragraph noMargin size="md" color="primary" className={classes.links}>
|
||||
This setup will create a Safe with one or more owners. Optionally give the Safe a local name. By continuing you
|
||||
consent with the
|
||||
By continuing you consent with the
|
||||
{' '}
|
||||
<a rel="noopener noreferrer" href="https://safe.gnosis.io/terms" target="_blank">
|
||||
terms of use
|
||||
|
@ -49,35 +63,10 @@ const SafeName = ({ classes }: Props) => (
|
|||
<a rel="noopener noreferrer" href="https://safe.gnosis.io/privacy" target="_blank">
|
||||
privacy policy
|
||||
</a>
|
||||
.
|
||||
. Most importantly, you confirm that your funds are held securely in the Gnosis Safe, a smart contract on the
|
||||
Ethereum blockchain. These funds cannot be accessed by Gnosis at any point.
|
||||
</Paragraph>
|
||||
</Block>
|
||||
<Row margin="md" className={classes.text}>
|
||||
<Paragraph noMargin className={classes.dot} color="secondary">
|
||||
●
|
||||
</Paragraph>
|
||||
<Paragraph noMargin size="md" color="primary" weight="bolder">
|
||||
I understand that my funds are held securely in my Safe. They cannot be accessed by Gnosis.
|
||||
</Paragraph>
|
||||
</Row>
|
||||
<Row margin="md">
|
||||
<Paragraph noMargin className={classes.dot} color="secondary">
|
||||
●
|
||||
</Paragraph>
|
||||
<Paragraph noMargin size="md" color="primary" weight="bolder">
|
||||
My Safe is a smart contract on the Ethereum blockchain.
|
||||
</Paragraph>
|
||||
</Row>
|
||||
<Block margin="lg" className={classes.root}>
|
||||
<Field
|
||||
name={FIELD_NAME}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={required}
|
||||
placeholder="Name of the new Safe"
|
||||
text="Safe name"
|
||||
/>
|
||||
</Block>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import QrReader from 'react-qr-reader'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import Button from '~/components/layout/Button'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import Col from '~/components/layout/Col'
|
||||
import Modal from '~/components/Modal'
|
||||
import { checkWebcam } from './utils'
|
||||
import { styles } from './style'
|
||||
|
||||
const { useEffect, useState } = React
|
||||
|
||||
type Props = {
|
||||
onClose: () => void,
|
||||
classes: Object,
|
||||
onScan: Function,
|
||||
isOpen: boolean,
|
||||
}
|
||||
|
||||
const ScanQRModal = ({
|
||||
classes, onClose, isOpen, onScan,
|
||||
}: Props) => {
|
||||
const [hasWebcam, setHasWebcam] = useState(null)
|
||||
const scannerRef: Object = React.createRef()
|
||||
const openImageDialog = () => {
|
||||
scannerRef.current.openImageDialog()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
checkWebcam(
|
||||
() => {
|
||||
setHasWebcam(true)
|
||||
},
|
||||
() => {
|
||||
setHasWebcam(false)
|
||||
},
|
||||
)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// this fires only when the hasWebcam changes to false (null > false (user doesn't have webcam)
|
||||
// , true > false (user switched from webcam to file upload))
|
||||
// Doesn't fire on re-render
|
||||
if (hasWebcam === false) {
|
||||
openImageDialog()
|
||||
}
|
||||
}, [hasWebcam])
|
||||
|
||||
return (
|
||||
<Modal title="Receive Tokens" description="Receive Tokens Form" handleClose={onClose} open={isOpen}>
|
||||
<Row align="center" grow className={classes.heading}>
|
||||
<Paragraph className={classes.manage} weight="bolder" noMargin>
|
||||
Scan QR
|
||||
</Paragraph>
|
||||
<IconButton onClick={onClose} disableRipple>
|
||||
<Close className={classes.close} />
|
||||
</IconButton>
|
||||
</Row>
|
||||
<Hairline />
|
||||
<Col layout="column" middle="xs" className={classes.detailsContainer}>
|
||||
{hasWebcam === null ? (
|
||||
<Block align="center" className={classes.loaderContainer}>
|
||||
<CircularProgress />
|
||||
</Block>
|
||||
) : (
|
||||
<QrReader
|
||||
ref={scannerRef}
|
||||
legacyMode={!hasWebcam}
|
||||
onScan={(data) => {
|
||||
if (data) onScan(data)
|
||||
}}
|
||||
onError={(err) => {
|
||||
console.error(err)
|
||||
}}
|
||||
style={{ width: '400px', height: '400px' }}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
<Hairline />
|
||||
<Row align="center" className={classes.buttonRow}>
|
||||
<Button
|
||||
color="secondary"
|
||||
className={classes.button}
|
||||
minHeight={42}
|
||||
minWidth={140}
|
||||
onClick={onClose}
|
||||
variant="contained"
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
className={classes.button}
|
||||
minWidth={140}
|
||||
minHeight={42}
|
||||
onClick={() => {
|
||||
if (hasWebcam) {
|
||||
setHasWebcam(false)
|
||||
} else {
|
||||
openImageDialog()
|
||||
}
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Upload an image
|
||||
</Button>
|
||||
</Row>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles)(ScanQRModal)
|
|
@ -0,0 +1,35 @@
|
|||
// @flow
|
||||
import { lg, sm, background } from '~/theme/variables'
|
||||
|
||||
export const styles = () => ({
|
||||
heading: {
|
||||
padding: `${sm} ${lg}`,
|
||||
justifyContent: 'space-between',
|
||||
maxHeight: '75px',
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
loaderContainer: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
manage: {
|
||||
fontSize: '24px',
|
||||
},
|
||||
close: {
|
||||
height: '35px',
|
||||
width: '35px',
|
||||
},
|
||||
detailsContainer: {
|
||||
backgroundColor: background,
|
||||
maxHeight: '420px',
|
||||
},
|
||||
buttonRow: {
|
||||
height: '84px',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
button: {
|
||||
'&:last-child': {
|
||||
marginLeft: sm,
|
||||
},
|
||||
},
|
||||
})
|
|
@ -0,0 +1,15 @@
|
|||
// @flow
|
||||
navigator.getMedia = navigator.getUserMedia // use the proper vendor prefix
|
||||
|| navigator.webkitGetUserMedia
|
||||
|| navigator.mozGetUserMedia
|
||||
|| navigator.msGetUserMedia
|
||||
|
||||
export const checkWebcam = (success: Function, err: Function) => navigator.getMedia(
|
||||
{ video: true },
|
||||
() => {
|
||||
success()
|
||||
},
|
||||
() => {
|
||||
err()
|
||||
},
|
||||
)
|
|
@ -0,0 +1,231 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import InputAdornment from '@material-ui/core/InputAdornment'
|
||||
import CheckCircle from '@material-ui/icons/CheckCircle'
|
||||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import SelectField from '~/components/forms/SelectField'
|
||||
import {
|
||||
required, composeValidators, noErrorsOn, mustBeInteger, minValue,
|
||||
} from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Button from '~/components/layout/Button'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Img from '~/components/layout/Img'
|
||||
import Col from '~/components/layout/Col'
|
||||
import {
|
||||
FIELD_CONFIRMATIONS,
|
||||
getOwnerNameBy,
|
||||
getOwnerAddressBy,
|
||||
getNumOwnersFrom,
|
||||
} from '~/routes/open/components/fields'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import trash from '~/assets/icons/trash.svg'
|
||||
import QRIcon from '~/assets/icons/qrcode.svg'
|
||||
import ScanQRModal from './ScanQRModal'
|
||||
import { getAddressValidators } from './validators'
|
||||
import { styles } from './style'
|
||||
|
||||
type Props = {
|
||||
classes: Object,
|
||||
otherAccounts: string[],
|
||||
errors: Object,
|
||||
form: Object,
|
||||
values: Object,
|
||||
}
|
||||
|
||||
const { useState } = React
|
||||
|
||||
export const ADD_OWNER_BUTTON = '+ ADD ANOTHER OWNER'
|
||||
|
||||
export const calculateValuesAfterRemoving = (index: number, notRemovedOwners: number, values: Object) => {
|
||||
const initialValues = { ...values }
|
||||
|
||||
const numOwnersAfterRemoving = notRemovedOwners - 1
|
||||
// muevo indices
|
||||
for (let i = index; i < numOwnersAfterRemoving; i += 1) {
|
||||
initialValues[getOwnerNameBy(i)] = values[getOwnerNameBy(i + 1)]
|
||||
initialValues[getOwnerAddressBy(i)] = values[getOwnerAddressBy(i + 1)]
|
||||
}
|
||||
|
||||
if (+values[FIELD_CONFIRMATIONS] === notRemovedOwners) {
|
||||
initialValues[FIELD_CONFIRMATIONS] = numOwnersAfterRemoving.toString()
|
||||
}
|
||||
|
||||
delete initialValues[getOwnerNameBy(index)]
|
||||
delete initialValues[getOwnerAddressBy(index)]
|
||||
|
||||
return initialValues
|
||||
}
|
||||
|
||||
const SafeOwners = (props: Props) => {
|
||||
const {
|
||||
classes, errors, otherAccounts, values, form,
|
||||
} = props
|
||||
|
||||
const validOwners = getNumOwnersFrom(values)
|
||||
const [numOwners, setNumOwners] = useState<number>(validOwners)
|
||||
const [qrModalOpen, setQrModalOpen] = useState<boolean>(false)
|
||||
const [scanQrForOwnerName, setScanQrForOwnerName] = useState<string | null>(null)
|
||||
|
||||
const openQrModal = (ownerName) => {
|
||||
setScanQrForOwnerName(ownerName)
|
||||
setQrModalOpen(true)
|
||||
}
|
||||
|
||||
const closeQrModal = () => {
|
||||
setQrModalOpen(false)
|
||||
}
|
||||
|
||||
const onRemoveRow = (index: number) => () => {
|
||||
const initialValues = calculateValuesAfterRemoving(index, numOwners, values)
|
||||
form.reset(initialValues)
|
||||
|
||||
setNumOwners(numOwners - 1)
|
||||
}
|
||||
|
||||
const onAddOwner = () => {
|
||||
setNumOwners(numOwners + 1)
|
||||
}
|
||||
|
||||
const handleScan = (value) => {
|
||||
let scannedAddress = value
|
||||
|
||||
if (scannedAddress.startsWith('ethereum:')) {
|
||||
scannedAddress = scannedAddress.replace('ethereum:', '')
|
||||
}
|
||||
|
||||
form.mutators.setValue(scanQrForOwnerName, scannedAddress)
|
||||
closeQrModal()
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Block className={classes.title}>
|
||||
<Paragraph noMargin size="md" color="primary">
|
||||
Specify the owners of the Safe.
|
||||
</Paragraph>
|
||||
</Block>
|
||||
<Hairline />
|
||||
<Row className={classes.header}>
|
||||
<Col xs={4}>NAME</Col>
|
||||
<Col xs={8}>ADDRESS</Col>
|
||||
</Row>
|
||||
<Hairline />
|
||||
<Block margin="md" padding="md">
|
||||
{[...Array(Number(numOwners))].map((x, index) => {
|
||||
const addressName = getOwnerAddressBy(index)
|
||||
|
||||
return (
|
||||
<Row key={`owner${index}`} className={classes.owner}>
|
||||
<Col xs={4}>
|
||||
<Field
|
||||
className={classes.name}
|
||||
name={getOwnerNameBy(index)}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={required}
|
||||
placeholder="Owner Name*"
|
||||
text="Owner Name"
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={6}>
|
||||
<Field
|
||||
name={addressName}
|
||||
component={TextField}
|
||||
inputAdornment={
|
||||
noErrorsOn(addressName, errors) && {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<CheckCircle className={classes.check} />
|
||||
</InputAdornment>
|
||||
),
|
||||
}
|
||||
}
|
||||
type="text"
|
||||
validate={getAddressValidators(otherAccounts, index)}
|
||||
placeholder="Owner Address*"
|
||||
text="Owner Address"
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={1} center="xs" middle="xs" className={classes.remove}>
|
||||
<Img
|
||||
src={QRIcon}
|
||||
height={20}
|
||||
alt="Scan QR"
|
||||
onClick={() => {
|
||||
openQrModal(addressName)
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={1} center="xs" middle="xs" className={classes.remove}>
|
||||
{index > 0 && <Img src={trash} height={20} alt="Delete" onClick={onRemoveRow(index)} />}
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
})}
|
||||
</Block>
|
||||
<Row align="center" grow className={classes.add} margin="xl">
|
||||
<Button color="secondary" onClick={onAddOwner} data-testid="add-owner-btn">
|
||||
<Paragraph weight="bold" size="md" noMargin>
|
||||
{ADD_OWNER_BUTTON}
|
||||
</Paragraph>
|
||||
</Button>
|
||||
</Row>
|
||||
<Block margin="md" padding="md" className={classes.owner}>
|
||||
<Paragraph size="md" color="primary">
|
||||
Any transaction requires the confirmation of:
|
||||
</Paragraph>
|
||||
<Row margin="xl" align="center">
|
||||
<Col xs={2}>
|
||||
<Field
|
||||
name={FIELD_CONFIRMATIONS}
|
||||
component={SelectField}
|
||||
validate={composeValidators(required, mustBeInteger, minValue(1))}
|
||||
data-testid="threshold-select-input"
|
||||
>
|
||||
{[...Array(Number(validOwners))].map((x, index) => (
|
||||
<MenuItem key={`selectOwner${index}`} value={`${index + 1}`}>
|
||||
{index + 1}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Field>
|
||||
</Col>
|
||||
<Col xs={10}>
|
||||
<Paragraph size="lg" color="primary" noMargin className={classes.owners}>
|
||||
out of
|
||||
{' '}
|
||||
{validOwners}
|
||||
{' '}
|
||||
owner(s)
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Row>
|
||||
</Block>
|
||||
{qrModalOpen && <ScanQRModal isOpen={qrModalOpen} onScan={handleScan} onClose={closeQrModal} />}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const SafeOwnersForm = withStyles(styles)(SafeOwners)
|
||||
|
||||
const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React.Node, { values, errors, form }: Object) => (
|
||||
<React.Fragment>
|
||||
<OpenPaper controls={controls} padding={false}>
|
||||
<SafeOwnersForm
|
||||
otherAccounts={getAccountsFrom(values)}
|
||||
errors={errors}
|
||||
form={form}
|
||||
updateInitialProps={updateInitialProps}
|
||||
values={values}
|
||||
/>
|
||||
</OpenPaper>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
export default SafeOwnersPage
|
|
@ -0,0 +1,44 @@
|
|||
// @flow
|
||||
import { md, lg, sm } from '~/theme/variables'
|
||||
|
||||
export const styles = () => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
},
|
||||
title: {
|
||||
padding: `${md} ${lg}`,
|
||||
},
|
||||
owner: {
|
||||
padding: `0 ${lg}`,
|
||||
marginTop: '12px',
|
||||
'&:first-child': {
|
||||
marginTop: 0,
|
||||
},
|
||||
},
|
||||
header: {
|
||||
padding: `${sm} ${lg}`,
|
||||
},
|
||||
name: {
|
||||
marginRight: `${sm}`,
|
||||
},
|
||||
trash: {
|
||||
top: '5px',
|
||||
},
|
||||
add: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
check: {
|
||||
color: '#03AE60',
|
||||
height: '20px',
|
||||
},
|
||||
remove: {
|
||||
height: '56px',
|
||||
maxWidth: '50px',
|
||||
'&:hover': {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
},
|
||||
owners: {
|
||||
paddingLeft: md,
|
||||
},
|
||||
})
|
|
@ -0,0 +1,17 @@
|
|||
// @flow
|
||||
import {
|
||||
required,
|
||||
composeValidators,
|
||||
uniqueAddress,
|
||||
mustBeEthereumAddress,
|
||||
} from '~/components/forms/validator'
|
||||
|
||||
export const getAddressValidators = (addresses: string[], position: number) => {
|
||||
// thanks Rich Harris
|
||||
// https://twitter.com/Rich_Harris/status/1125850391155965952
|
||||
const copy = addresses.slice()
|
||||
copy[position] = copy[copy.length - 1]
|
||||
copy.pop()
|
||||
|
||||
return composeValidators(required, mustBeEthereumAddress, uniqueAddress(copy))
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import InputAdornment from '@material-ui/core/InputAdornment'
|
||||
import CheckCircle from '@material-ui/icons/CheckCircle'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import {
|
||||
required,
|
||||
composeValidators,
|
||||
uniqueAddress,
|
||||
mustBeEthereumAddress,
|
||||
noErrorsOn,
|
||||
} from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Button from '~/components/layout/Button'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Img from '~/components/layout/Img'
|
||||
import Col from '~/components/layout/Col'
|
||||
import { getOwnerNameBy, getOwnerAddressBy } from '~/routes/open/components/fields'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor'
|
||||
import Hairline from '~/components/layout/Hairline'
|
||||
import { md, lg, sm } from '~/theme/variables'
|
||||
import trash from '~/assets/icons/trash.svg'
|
||||
|
||||
type Props = {
|
||||
classes: Object,
|
||||
otherAccounts: string[],
|
||||
errors: Object,
|
||||
values: Object,
|
||||
updateInitialProps: (initialValues: Object) => void,
|
||||
}
|
||||
|
||||
type State = {
|
||||
numOwners: number,
|
||||
}
|
||||
|
||||
const styles = () => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
},
|
||||
title: {
|
||||
padding: `${md} ${lg}`,
|
||||
},
|
||||
owner: {
|
||||
padding: `0 ${lg}`,
|
||||
},
|
||||
header: {
|
||||
padding: `${sm} ${lg}`,
|
||||
},
|
||||
name: {
|
||||
marginRight: `${sm}`,
|
||||
},
|
||||
trash: {
|
||||
top: '5px',
|
||||
},
|
||||
add: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
check: {
|
||||
color: '#03AE60',
|
||||
height: '20px',
|
||||
},
|
||||
remove: {
|
||||
height: '56px',
|
||||
marginTop: '12px',
|
||||
maxWidth: '50px',
|
||||
'&:hover': {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const getAddressValidators = (addresses: string[], position: number) => {
|
||||
// thanks Rich Harris
|
||||
// https://twitter.com/Rich_Harris/status/1125850391155965952
|
||||
const copy = addresses.slice()
|
||||
copy[position] = copy[copy.length - 1]
|
||||
copy.pop()
|
||||
|
||||
return composeValidators(required, mustBeEthereumAddress, uniqueAddress(copy))
|
||||
}
|
||||
|
||||
export const ADD_OWNER_BUTTON = '+ ADD ANOTHER OWNER'
|
||||
|
||||
export const calculateValuesAfterRemoving = (index: number, notRemovedOwners: number, values: Object) => {
|
||||
const initialValues = { ...values }
|
||||
const numOwnersAfterRemoving = notRemovedOwners - 1
|
||||
// muevo indices
|
||||
for (let i = index; i < numOwnersAfterRemoving; i += 1) {
|
||||
initialValues[getOwnerNameBy(i)] = values[getOwnerNameBy(i + 1)]
|
||||
initialValues[getOwnerAddressBy(i)] = values[getOwnerAddressBy(i + 1)]
|
||||
}
|
||||
|
||||
delete initialValues[getOwnerNameBy(numOwnersAfterRemoving)]
|
||||
delete initialValues[getOwnerAddressBy(numOwnersAfterRemoving)]
|
||||
|
||||
return initialValues
|
||||
}
|
||||
|
||||
class SafeOwners extends React.PureComponent<Props, State> {
|
||||
state = {
|
||||
numOwners: 1,
|
||||
}
|
||||
|
||||
onRemoveRow = (index: number) => () => {
|
||||
const { values, updateInitialProps } = this.props
|
||||
const { numOwners } = this.state
|
||||
const initialValues = calculateValuesAfterRemoving(index, numOwners, values)
|
||||
updateInitialProps(initialValues)
|
||||
|
||||
this.setState(state => ({
|
||||
numOwners: state.numOwners - 1,
|
||||
}))
|
||||
}
|
||||
|
||||
onAddOwner = () => {
|
||||
this.setState(state => ({
|
||||
numOwners: state.numOwners + 1,
|
||||
}))
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, errors, otherAccounts } = this.props
|
||||
const { numOwners } = this.state
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Block className={classes.title}>
|
||||
<Paragraph noMargin size="md" color="primary">
|
||||
Specify the owners of the Safe.
|
||||
</Paragraph>
|
||||
</Block>
|
||||
<Hairline />
|
||||
<Row className={classes.header}>
|
||||
<Col xs={4}>NAME</Col>
|
||||
<Col xs={8}>ADDRESS</Col>
|
||||
</Row>
|
||||
<Hairline />
|
||||
<Block margin="md" padding="md">
|
||||
{[...Array(Number(numOwners))].map((x, index) => {
|
||||
const addressName = getOwnerAddressBy(index)
|
||||
|
||||
return (
|
||||
<Row key={`owner${index}`} className={classes.owner}>
|
||||
<Col xs={4}>
|
||||
<Field
|
||||
className={classes.name}
|
||||
name={getOwnerNameBy(index)}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={required}
|
||||
placeholder="Owner Name*"
|
||||
text="Owner Name"
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={7}>
|
||||
<Field
|
||||
name={addressName}
|
||||
component={TextField}
|
||||
inputAdornment={
|
||||
noErrorsOn(addressName, errors) && {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<CheckCircle className={classes.check} />
|
||||
</InputAdornment>
|
||||
),
|
||||
}
|
||||
}
|
||||
type="text"
|
||||
validate={getAddressValidators(otherAccounts, index)}
|
||||
placeholder="Owner Address*"
|
||||
text="Owner Address"
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={1} center="xs" middle="xs" className={classes.remove}>
|
||||
{index > 0 && <Img src={trash} height={20} alt="Delete" onClick={this.onRemoveRow(index)} />}
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
})}
|
||||
</Block>
|
||||
<Row align="center" grow className={classes.add} margin="xl">
|
||||
<Button color="secondary" onClick={this.onAddOwner} data-testid="add-owner-btn">
|
||||
<Paragraph weight="bold" size="md" noMargin>
|
||||
{ADD_OWNER_BUTTON}
|
||||
</Paragraph>
|
||||
</Button>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const SafeOwnersForm = withStyles(styles)(SafeOwners)
|
||||
|
||||
const SafeOwnersPage = ({ updateInitialProps }: Object) => (controls: React.Node, { values, errors }: Object) => (
|
||||
<React.Fragment>
|
||||
<OpenPaper controls={controls} padding={false}>
|
||||
<SafeOwnersForm
|
||||
otherAccounts={getAccountsFrom(values)}
|
||||
errors={errors}
|
||||
updateInitialProps={updateInitialProps}
|
||||
values={values}
|
||||
/>
|
||||
</OpenPaper>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
export default SafeOwnersPage
|
|
@ -1,91 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
import Field from '~/components/forms/Field'
|
||||
import SelectField from '~/components/forms/SelectField'
|
||||
import {
|
||||
composeValidators, minValue, mustBeInteger, required,
|
||||
} from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Row from '~/components/layout/Row'
|
||||
import Col from '~/components/layout/Col'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import { FIELD_CONFIRMATIONS, getNumOwnersFrom } from '~/routes/open/components/fields'
|
||||
import { md } from '~/theme/variables'
|
||||
|
||||
type Props = {
|
||||
classes: Object,
|
||||
values: Object,
|
||||
}
|
||||
|
||||
const styles = () => ({
|
||||
owners: {
|
||||
paddingLeft: md,
|
||||
},
|
||||
})
|
||||
|
||||
export const CONFIRMATIONS_ERROR = 'Number of confirmations can not be higher than the number of owners'
|
||||
|
||||
export const safeFieldsValidation = (values: Object) => {
|
||||
const errors = {}
|
||||
|
||||
const numOwners = getNumOwnersFrom(values)
|
||||
if (numOwners < Number.parseInt(values[FIELD_CONFIRMATIONS], 10)) {
|
||||
errors[FIELD_CONFIRMATIONS] = CONFIRMATIONS_ERROR
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
const SafeThreshold = ({ classes, values }: Props) => {
|
||||
const numOwners = getNumOwnersFrom(values)
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Block margin="xs">
|
||||
<Paragraph noMargin size="md" color="primary" weight="bolder">
|
||||
Any transaction requires the confirmation of:
|
||||
</Paragraph>
|
||||
</Block>
|
||||
<Row margin="xl" align="center">
|
||||
<Col xs={2}>
|
||||
<Field
|
||||
name={FIELD_CONFIRMATIONS}
|
||||
component={SelectField}
|
||||
validate={composeValidators(required, mustBeInteger, minValue(1))}
|
||||
data-testid="threshold-select-input"
|
||||
>
|
||||
{[...Array(Number(numOwners))].map((x, index) => (
|
||||
<MenuItem key={`selectOwner${index}`} value={`${index + 1}`}>
|
||||
{index + 1}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Field>
|
||||
</Col>
|
||||
<Col xs={10}>
|
||||
<Paragraph size="lg" color="primary" noMargin className={classes.owners}>
|
||||
out of
|
||||
{' '}
|
||||
{numOwners}
|
||||
{' '}
|
||||
owner(s)
|
||||
</Paragraph>
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const SafeThresholdForm = withStyles(styles)(SafeThreshold)
|
||||
|
||||
const SafeOwnersPage = () => (controls: React.Node, { values }: Object) => (
|
||||
<React.Fragment>
|
||||
<OpenPaper controls={controls} container={450}>
|
||||
<SafeThresholdForm values={values} />
|
||||
</OpenPaper>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
export default SafeOwnersPage
|
|
@ -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+Name$/.test(key))
|
||||
.filter(key => /^owner\d+Address$/.test(key) && !!values[key])
|
||||
|
||||
return accounts.length
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import { withStyles } from '@material-ui/core/styles'
|
|||
import Close from '@material-ui/icons/Close'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||
import Link from '~/components/layout/Link'
|
||||
import QRCode from 'qrcode.react'
|
||||
import Link from '~/components/layout/Link'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import Identicon from '~/components/Identicon'
|
||||
import Button from '~/components/layout/Button'
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from '~/routes/safe/components/SendToken/SendTokenForm/index'
|
||||
|
||||
type FormProps = {
|
||||
values: Object,
|
||||
submitting: boolean,
|
||||
}
|
||||
|
||||
type Props = {
|
||||
symbol: string,
|
||||
}
|
||||
|
||||
const spinnerStyle = {
|
||||
minHeight: '50px',
|
||||
}
|
||||
|
||||
const ReviewTx = ({ symbol }: Props) => (controls: React.Node, { values, submitting }: FormProps) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2">Review the move token funds</Heading>
|
||||
<Paragraph align="left">
|
||||
<Bold>Destination: </Bold>
|
||||
{' '}
|
||||
{values[TKN_DESTINATION_PARAM]}
|
||||
</Paragraph>
|
||||
<Paragraph align="left">
|
||||
<Bold>{`Amount to transfer: ${values[TKN_VALUE_PARAM]} ${symbol}`}</Bold>
|
||||
</Paragraph>
|
||||
<Block style={spinnerStyle}>{submitting && <CircularProgress size={50} />}</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default ReviewTx
|
|
@ -1,53 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import {
|
||||
composeValidators, inLimit, mustBeFloat, required, greaterThan, mustBeEthereumAddress,
|
||||
} from '~/components/forms/validator'
|
||||
import Block from '~/components/layout/Block'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
|
||||
export const CONFIRMATIONS_ERROR = 'Number of confirmations can not be higher than the number of owners'
|
||||
|
||||
export const TKN_DESTINATION_PARAM = 'tknDestination'
|
||||
export const TKN_VALUE_PARAM = 'tknValue'
|
||||
|
||||
type Props = {
|
||||
funds: string,
|
||||
symbol: string,
|
||||
}
|
||||
|
||||
const SendTokenForm = ({ funds, symbol }: Props) => (controls: React.Node) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2" margin="lg">
|
||||
Send tokens Transaction
|
||||
</Heading>
|
||||
<Heading tag="h4" margin="lg">
|
||||
{`Available tokens: ${funds} ${symbol}`}
|
||||
</Heading>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TKN_DESTINATION_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeEthereumAddress)}
|
||||
placeholder="Destination*"
|
||||
text="Destination"
|
||||
/>
|
||||
</Block>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={TKN_VALUE_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(required, mustBeFloat, greaterThan(0), inLimit(Number(funds), 0, 'available balance', symbol))}
|
||||
placeholder="Amount of tokens*"
|
||||
text="Amount of Tokens"
|
||||
/>
|
||||
</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default SendTokenForm
|
|
@ -1,10 +0,0 @@
|
|||
// @flow
|
||||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
|
||||
export type Actions = {
|
||||
fetchTransactions: typeof fetchTransactions,
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchTransactions,
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { connect } from 'react-redux'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { type Safe } from '~/routes/safe/store/models/safe'
|
||||
import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens'
|
||||
import { type Token } from '~/logic/tokens/store/model/token'
|
||||
import { isEther } from '~/logic/tokens/utils/tokenHelpers'
|
||||
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
|
||||
import { toNative } from '~/logic/wallets/tokens'
|
||||
import { createTransaction } from '~/logic/safe/safeFrontendOperations'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import actions, { type Actions } from './actions'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import SendTokenForm, { TKN_DESTINATION_PARAM, TKN_VALUE_PARAM } from './SendTokenForm'
|
||||
import ReviewTx from './ReviewTx'
|
||||
|
||||
const getSteps = () => ['Fill Move Token form', 'Review Move Token form']
|
||||
|
||||
type Props = SelectorProps &
|
||||
Actions & {
|
||||
safe: Safe,
|
||||
token: Token,
|
||||
onReset: () => void,
|
||||
}
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const SEE_TXS_BUTTON_TEXT = 'VISIT TXS'
|
||||
|
||||
const getTransferData = async (tokenAddress: string, to: string, amount: BigNumber) => {
|
||||
const StandardToken = await getStandardTokenContract()
|
||||
const myToken = await StandardToken.at(tokenAddress)
|
||||
|
||||
return myToken.contract.transfer(to, amount).encodeABI()
|
||||
}
|
||||
|
||||
const processTokenTransfer = async (safe: Safe, token: Token, to: string, amount: string, userAddress: string) => {
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const nonce = await gnosisSafe.nonce()
|
||||
const symbol = token.get('symbol')
|
||||
const name = `Send ${amount} ${symbol} to ${to}`
|
||||
const value = isEther(symbol) ? amount : '0'
|
||||
const tokenAddress = token.get('address')
|
||||
const destination = isEther(symbol) ? to : tokenAddress
|
||||
const data = isEther(symbol)
|
||||
? EMPTY_DATA
|
||||
: await getTransferData(tokenAddress, to, toNative(amount, token.get('decimals')))
|
||||
|
||||
return createTransaction(safe, name, destination, value, nonce, userAddress, data)
|
||||
}
|
||||
|
||||
class SendToken extends React.Component<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onTransaction = async (values: Object) => {
|
||||
try {
|
||||
const {
|
||||
safe, token, userAddress, fetchTransactions,
|
||||
} = this.props
|
||||
|
||||
const amount = values[TKN_VALUE_PARAM]
|
||||
const destination = values[TKN_DESTINATION_PARAM]
|
||||
|
||||
await processTokenTransfer(safe, token, destination, amount, userAddress)
|
||||
await sleep(1500)
|
||||
fetchTransactions(safe.get('address'))
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
this.setState({ done: false })
|
||||
// eslint-disable-next-line
|
||||
console.log('Error while moving ERC20 token funds ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
const { onReset } = this.props
|
||||
|
||||
this.setState({ done: false })
|
||||
onReset() // This is for show the TX list component
|
||||
}
|
||||
|
||||
render() {
|
||||
const { done } = this.state
|
||||
const { token } = this.props
|
||||
const steps = getSteps()
|
||||
const finishedButton = <Stepper.FinishButton title={SEE_TXS_BUTTON_TEXT} />
|
||||
const symbol = token.get('symbol')
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.onTransaction}
|
||||
steps={steps}
|
||||
onReset={this.onReset}
|
||||
>
|
||||
<Stepper.Page funds={token.get('funds')} symbol={symbol}>
|
||||
{SendTokenForm}
|
||||
</Stepper.Page>
|
||||
<Stepper.Page symbol={symbol}>{ReviewTx}</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
selector,
|
||||
actions,
|
||||
)(SendToken)
|
|
@ -1,11 +0,0 @@
|
|||
// @flow
|
||||
import { createStructuredSelector } from 'reselect'
|
||||
import { userAccountSelector } from '~/logic/wallets/store/selectors'
|
||||
|
||||
export type SelectorProps = {
|
||||
userAddress: typeof userAccountSelector,
|
||||
}
|
||||
|
||||
export default createStructuredSelector<Object, *>({
|
||||
userAddress: userAccountSelector,
|
||||
})
|
|
@ -1,34 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import CircularProgress from '@material-ui/core/CircularProgress'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Bold from '~/components/layout/Bold'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import Paragraph from '~/components/layout/Paragraph'
|
||||
import { THRESHOLD_PARAM } from '~/routes/safe/components/Threshold/ThresholdForm'
|
||||
|
||||
type FormProps = {
|
||||
values: Object,
|
||||
submitting: boolean,
|
||||
}
|
||||
|
||||
const spinnerStyle = {
|
||||
minHeight: '50px',
|
||||
}
|
||||
|
||||
const Review = () => (controls: React.Node, { values, submitting }: FormProps) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2">Review the Threshold operation</Heading>
|
||||
<Paragraph align="left">
|
||||
<Bold>The new threshold will be: </Bold>
|
||||
{' '}
|
||||
{values[THRESHOLD_PARAM]}
|
||||
</Paragraph>
|
||||
<Block style={spinnerStyle}>
|
||||
{ submitting && <CircularProgress size={50} /> }
|
||||
</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default Review
|
|
@ -1,46 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Block from '~/components/layout/Block'
|
||||
import Heading from '~/components/layout/Heading'
|
||||
import OpenPaper from '~/components/Stepper/OpenPaper'
|
||||
import Field from '~/components/forms/Field'
|
||||
import TextField from '~/components/forms/TextField'
|
||||
import {
|
||||
composeValidators, minValue, maxValue, mustBeInteger, required,
|
||||
} from '~/components/forms/validator'
|
||||
import { type Safe } from '~/routes/safe/store/models/safe'
|
||||
|
||||
export const THRESHOLD_PARAM = 'threshold'
|
||||
|
||||
type ThresholdProps = {
|
||||
numOwners: number,
|
||||
safe: Safe,
|
||||
}
|
||||
|
||||
const ThresholdForm = ({ numOwners, safe }: ThresholdProps) => (controls: React.Node) => (
|
||||
<OpenPaper controls={controls}>
|
||||
<Heading tag="h2" margin="lg">
|
||||
{'Change safe\'s threshold'}
|
||||
</Heading>
|
||||
<Heading tag="h4" margin="lg">
|
||||
{`Safe's owners: ${numOwners} and Safe's threshold: ${safe.get('threshold')}`}
|
||||
</Heading>
|
||||
<Block margin="md">
|
||||
<Field
|
||||
name={THRESHOLD_PARAM}
|
||||
component={TextField}
|
||||
type="text"
|
||||
validate={composeValidators(
|
||||
required,
|
||||
mustBeInteger,
|
||||
minValue(1),
|
||||
maxValue(numOwners),
|
||||
)}
|
||||
placeholder="New threshold"
|
||||
text="Safe's threshold"
|
||||
/>
|
||||
</Block>
|
||||
</OpenPaper>
|
||||
)
|
||||
|
||||
export default ThresholdForm
|
|
@ -1,10 +0,0 @@
|
|||
// @flow
|
||||
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions'
|
||||
|
||||
export type Actions = {
|
||||
fetchTransactions: typeof fetchTransactions,
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchTransactions,
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import Stepper from '~/components/Stepper'
|
||||
import { connect } from 'react-redux'
|
||||
import { createTransaction } from '~/logic/safe/safeFrontendOperations'
|
||||
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
|
||||
import { type Safe } from '~/routes/safe/store/models/safe'
|
||||
import ThresholdForm, { THRESHOLD_PARAM } from './ThresholdForm'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import actions, { type Actions } from './actions'
|
||||
import Review from './Review'
|
||||
|
||||
type Props = SelectorProps & Actions & {
|
||||
numOwners: number,
|
||||
safe: Safe,
|
||||
onReset: () => void,
|
||||
}
|
||||
|
||||
const getSteps = () => [
|
||||
'Fill Change threshold Form', 'Review change threshold operation',
|
||||
]
|
||||
|
||||
type State = {
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
export const CHANGE_THRESHOLD_RESET_BUTTON_TEXT = 'SEE TXs'
|
||||
|
||||
class Threshold extends React.PureComponent<Props, State> {
|
||||
state = {
|
||||
done: false,
|
||||
}
|
||||
|
||||
onThreshold = async (values: Object) => {
|
||||
try {
|
||||
const { safe, userAddress, fetchTransactions } = this.props // , fetchThreshold } = this.props
|
||||
const newThreshold = values[THRESHOLD_PARAM]
|
||||
const safeAddress = safe.get('address')
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const nonce = await gnosisSafe.nonce()
|
||||
const data = gnosisSafe.contract.changeThreshold(newThreshold).encodeABI()
|
||||
await createTransaction(safe, `Change Safe's threshold [${nonce}]`, safeAddress, '0', nonce, userAddress, data)
|
||||
await fetchTransactions(safeAddress)
|
||||
this.setState({ done: true })
|
||||
} catch (error) {
|
||||
this.setState({ done: false })
|
||||
// eslint-disable-next-line
|
||||
console.log('Error while changing threshold ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
onReset = () => {
|
||||
const { onReset } = this.props
|
||||
this.setState({ done: false })
|
||||
|
||||
onReset()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { numOwners, safe } = this.props
|
||||
const { done } = this.state
|
||||
const steps = getSteps()
|
||||
const finishedButton = <Stepper.FinishButton title={CHANGE_THRESHOLD_RESET_BUTTON_TEXT} />
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Stepper
|
||||
finishedTransaction={done}
|
||||
finishedButton={finishedButton}
|
||||
onSubmit={this.onThreshold}
|
||||
steps={steps}
|
||||
onReset={this.onReset}
|
||||
>
|
||||
<Stepper.Page numOwners={numOwners} safe={safe}>
|
||||
{ ThresholdForm }
|
||||
</Stepper.Page>
|
||||
<Stepper.Page>
|
||||
{ Review }
|
||||
</Stepper.Page>
|
||||
</Stepper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(selector, actions)(Threshold)
|
|
@ -1,11 +0,0 @@
|
|||
// @flow
|
||||
import { createStructuredSelector } from 'reselect'
|
||||
import { userAccountSelector } from '~/logic/wallets/store/selectors'
|
||||
|
||||
export type SelectorProps = {
|
||||
userAddress: typeof userAccountSelector,
|
||||
}
|
||||
|
||||
export default createStructuredSelector<Object, *>({
|
||||
userAddress: userAccountSelector,
|
||||
})
|
|
@ -2,9 +2,6 @@
|
|||
import { type Transaction } from '~/routes/safe/store/models/transaction'
|
||||
import { getWeb3 } from '~/logic/wallets/getWeb3'
|
||||
|
||||
const web3 = getWeb3()
|
||||
const { toBN, fromWei } = web3.utils
|
||||
|
||||
type DecodedTxData = {
|
||||
recipient: string,
|
||||
value?: string,
|
||||
|
@ -16,6 +13,9 @@ type DecodedTxData = {
|
|||
}
|
||||
|
||||
export const getTxData = (tx: Transaction): DecodedTxData => {
|
||||
const web3 = getWeb3()
|
||||
const { toBN, fromWei } = web3.utils
|
||||
|
||||
const txData = {}
|
||||
|
||||
if (tx.isTokenTransfer && tx.decodedParams) {
|
||||
|
|
|
@ -14,9 +14,6 @@ export const TX_TABLE_STATUS_ID = 'status'
|
|||
export const TX_TABLE_RAW_TX_ID = 'tx'
|
||||
export const TX_TABLE_EXPAND_ICON = 'expand'
|
||||
|
||||
const web3 = getWeb3()
|
||||
const { toBN, fromWei } = web3.utils
|
||||
|
||||
type TxData = {
|
||||
nonce: number,
|
||||
type: string,
|
||||
|
@ -29,6 +26,9 @@ type TxData = {
|
|||
export const formatDate = (date: Date): string => format(date, 'MMM D, YYYY - HH:mm:ss')
|
||||
|
||||
export const getTxAmount = (tx: Transaction) => {
|
||||
const web3 = getWeb3()
|
||||
const { toBN, fromWei } = web3.utils
|
||||
|
||||
let txAmount = 'n/a'
|
||||
|
||||
if (tx.isTokenTransfer && tx.decodedParams) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { type Store } from 'redux'
|
|||
import { render, fireEvent, cleanup } from '@testing-library/react'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'connected-react-router'
|
||||
import { ADD_OWNER_BUTTON } from '~/routes/open/components/SafeOwnersForm'
|
||||
import { ADD_OWNER_BUTTON } from '~/routes/open/components/SafeOwnersConfirmationsForm'
|
||||
import Open from '~/routes/open/container/Open'
|
||||
import { aNewStore, history, type GlobalState } from '~/store'
|
||||
import { sleep } from '~/utils/timer'
|
||||
|
@ -80,14 +80,12 @@ const deploySafe = async (createSafeForm: any, threshold: number, numOwners: num
|
|||
fireEvent.change(ownerNameInput, { target: { value: `Owner ${i + 1}` } })
|
||||
fireEvent.change(ownerAddressInput, { target: { value: accounts[i] } })
|
||||
}
|
||||
fireEvent.submit(form)
|
||||
await sleep(600)
|
||||
|
||||
// Fill Threshold
|
||||
// The test is fragile here, MUI select btn is hard to find
|
||||
const thresholdSelect = createSafeForm.getAllByRole('button')[1]
|
||||
|
||||
const thresholdSelect = createSafeForm.getAllByRole('button')[2]
|
||||
fireEvent.click(thresholdSelect)
|
||||
|
||||
const thresholdOptions = createSafeForm.getAllByRole('option')
|
||||
fireEvent.click(thresholdOptions[numOwners - 1])
|
||||
fireEvent.submit(form)
|
||||
|
|
334
yarn.lock
334
yarn.lock
|
@ -2,16 +2,16 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/cli@7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.5.0.tgz#f403c930692e28ecfa3bf02a9e7562b474f38271"
|
||||
integrity sha512-qNH55fWbKrEsCwID+Qc/3JDPnsSGpIIiMDbppnR8Z6PxLAqMQCFNqBctkIkBrMH49Nx+qqVTrHRWUR+ho2k+qQ==
|
||||
"@babel/cli@7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.5.5.tgz#bdb6d9169e93e241a08f5f7b0265195bf38ef5ec"
|
||||
integrity sha512-UHI+7pHv/tk9g6WXQKYz+kmXTI77YtuY3vqC59KIqcoWEjsJJSG6rAxKaLsgj3LDyadsPrCB929gVOKM6Hui0w==
|
||||
dependencies:
|
||||
commander "^2.8.1"
|
||||
convert-source-map "^1.1.0"
|
||||
fs-readdir-recursive "^1.1.0"
|
||||
glob "^7.0.0"
|
||||
lodash "^4.17.11"
|
||||
lodash "^4.17.13"
|
||||
mkdirp "^0.5.1"
|
||||
output-file-sync "^2.0.0"
|
||||
slash "^2.0.0"
|
||||
|
@ -53,27 +53,7 @@
|
|||
semver "^5.4.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/core@7.5.4":
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.4.tgz#4c32df7ad5a58e9ea27ad025c11276324e0b4ddd"
|
||||
integrity sha512-+DaeBEpYq6b2+ZmHx3tHspC+ZRflrvLqwfv8E3hNr5LVQoyBnL8RPKSBCg+rK2W2My9PWlujBiqd0ZPsR9Q6zQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0"
|
||||
"@babel/generator" "^7.5.0"
|
||||
"@babel/helpers" "^7.5.4"
|
||||
"@babel/parser" "^7.5.0"
|
||||
"@babel/template" "^7.4.4"
|
||||
"@babel/traverse" "^7.5.0"
|
||||
"@babel/types" "^7.5.0"
|
||||
convert-source-map "^1.1.0"
|
||||
debug "^4.1.0"
|
||||
json5 "^2.1.0"
|
||||
lodash "^4.17.11"
|
||||
resolve "^1.3.2"
|
||||
semver "^5.4.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.4.5":
|
||||
"@babel/core@7.5.5", "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.4.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.5.5.tgz#17b2686ef0d6bc58f963dddd68ab669755582c30"
|
||||
integrity sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==
|
||||
|
@ -93,7 +73,7 @@
|
|||
semver "^5.4.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.4.0", "@babel/generator@^7.5.0", "@babel/generator@^7.5.5":
|
||||
"@babel/generator@^7.4.0", "@babel/generator@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.5.5.tgz#873a7f936a3c89491b43536d12245b626664e3cf"
|
||||
integrity sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==
|
||||
|
@ -136,7 +116,7 @@
|
|||
"@babel/traverse" "^7.4.4"
|
||||
"@babel/types" "^7.4.4"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.4.0", "@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.0", "@babel/helper-create-class-features-plugin@^7.5.5":
|
||||
"@babel/helper-create-class-features-plugin@^7.4.0", "@babel/helper-create-class-features-plugin@^7.4.4", "@babel/helper-create-class-features-plugin@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.5.5.tgz#401f302c8ddbc0edd36f7c6b2887d8fa1122e5a4"
|
||||
integrity sha512-ZsxkyYiRA7Bg+ZTRpPvB6AbOFKTFFK4LrvTet8lInm0V468MWCaSYJE+I7v2z2r8KNLtYiV+K5kTCnR7dvyZjg==
|
||||
|
@ -279,7 +259,7 @@
|
|||
"@babel/traverse" "^7.1.0"
|
||||
"@babel/types" "^7.2.0"
|
||||
|
||||
"@babel/helpers@^7.4.3", "@babel/helpers@^7.5.4", "@babel/helpers@^7.5.5":
|
||||
"@babel/helpers@^7.4.3", "@babel/helpers@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.5.5.tgz#63908d2a73942229d1e6685bc2a0e730dde3b75e"
|
||||
integrity sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==
|
||||
|
@ -297,7 +277,7 @@
|
|||
esutils "^2.0.2"
|
||||
js-tokens "^4.0.0"
|
||||
|
||||
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.0", "@babel/parser@^7.5.5":
|
||||
"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4", "@babel/parser@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b"
|
||||
integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==
|
||||
|
@ -319,15 +299,7 @@
|
|||
"@babel/helper-create-class-features-plugin" "^7.4.0"
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-proposal-class-properties@7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.0.tgz#5bc6a0537d286fcb4fd4e89975adbca334987007"
|
||||
integrity sha512-9L/JfPCT+kShiiTTzcnBJ8cOwdKVmlC1RcCf9F0F9tERVrM4iWtWnXtjWCRqNm2la2BxO1MPArWNsU9zsSJWSQ==
|
||||
dependencies:
|
||||
"@babel/helper-create-class-features-plugin" "^7.5.0"
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-proposal-class-properties@^7.3.3":
|
||||
"@babel/plugin-proposal-class-properties@7.5.5", "@babel/plugin-proposal-class-properties@^7.3.3":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4"
|
||||
integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A==
|
||||
|
@ -442,7 +414,7 @@
|
|||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/plugin-syntax-object-rest-spread" "^7.2.0"
|
||||
|
||||
"@babel/plugin-proposal-object-rest-spread@^7.3.2", "@babel/plugin-proposal-object-rest-spread@^7.4.3", "@babel/plugin-proposal-object-rest-spread@^7.5.4", "@babel/plugin-proposal-object-rest-spread@^7.5.5":
|
||||
"@babel/plugin-proposal-object-rest-spread@^7.3.2", "@babel/plugin-proposal-object-rest-spread@^7.4.3", "@babel/plugin-proposal-object-rest-spread@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58"
|
||||
integrity sha512-F2DxJJSQ7f64FyTVl5cw/9MWn6naXGdk3Q3UhDbFEEHv+EilCPoeRD3Zh/Utx1CJz4uyKlQ4uH+bJPbEhMV7Zw==
|
||||
|
@ -661,7 +633,7 @@
|
|||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-transform-block-scoping@^7.4.0", "@babel/plugin-transform-block-scoping@^7.4.4", "@babel/plugin-transform-block-scoping@^7.5.5":
|
||||
"@babel/plugin-transform-block-scoping@^7.4.0", "@babel/plugin-transform-block-scoping@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.5.5.tgz#a35f395e5402822f10d2119f6f8e045e3639a2ce"
|
||||
integrity sha512-82A3CLRRdYubkG85lKwhZB0WZoHxLGsJdux/cOVaJCJpvYFl1LVzAIFyRsa7CvXqW8rBM4Zf3Bfn8PHt5DP0Sg==
|
||||
|
@ -683,7 +655,7 @@
|
|||
"@babel/helper-split-export-declaration" "^7.4.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/plugin-transform-classes@^7.4.3", "@babel/plugin-transform-classes@^7.4.4", "@babel/plugin-transform-classes@^7.5.5":
|
||||
"@babel/plugin-transform-classes@^7.4.3", "@babel/plugin-transform-classes@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.5.5.tgz#d094299d9bd680a14a2a0edae38305ad60fb4de9"
|
||||
integrity sha512-U2htCNK/6e9K7jGyJ++1p5XRU+LJjrwtoiVn9SzRlDT2KubcZ11OOwy3s24TjHxPgxNwonCYP7U2K51uVYCMDg==
|
||||
|
@ -1050,63 +1022,7 @@
|
|||
js-levenshtein "^1.1.3"
|
||||
semver "^5.5.0"
|
||||
|
||||
"@babel/preset-env@7.5.4":
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.4.tgz#64bc15041a3cbb0798930319917e70fcca57713d"
|
||||
integrity sha512-hFnFnouyRNiH1rL8YkX1ANCNAUVC8Djwdqfev8i1415tnAG+7hlA5zhZ0Q/3Q5gkop4HioIPbCEWAalqcbxRoQ==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.0.0"
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.2.0"
|
||||
"@babel/plugin-proposal-dynamic-import" "^7.5.0"
|
||||
"@babel/plugin-proposal-json-strings" "^7.2.0"
|
||||
"@babel/plugin-proposal-object-rest-spread" "^7.5.4"
|
||||
"@babel/plugin-proposal-optional-catch-binding" "^7.2.0"
|
||||
"@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
|
||||
"@babel/plugin-syntax-async-generators" "^7.2.0"
|
||||
"@babel/plugin-syntax-dynamic-import" "^7.2.0"
|
||||
"@babel/plugin-syntax-json-strings" "^7.2.0"
|
||||
"@babel/plugin-syntax-object-rest-spread" "^7.2.0"
|
||||
"@babel/plugin-syntax-optional-catch-binding" "^7.2.0"
|
||||
"@babel/plugin-transform-arrow-functions" "^7.2.0"
|
||||
"@babel/plugin-transform-async-to-generator" "^7.5.0"
|
||||
"@babel/plugin-transform-block-scoped-functions" "^7.2.0"
|
||||
"@babel/plugin-transform-block-scoping" "^7.4.4"
|
||||
"@babel/plugin-transform-classes" "^7.4.4"
|
||||
"@babel/plugin-transform-computed-properties" "^7.2.0"
|
||||
"@babel/plugin-transform-destructuring" "^7.5.0"
|
||||
"@babel/plugin-transform-dotall-regex" "^7.4.4"
|
||||
"@babel/plugin-transform-duplicate-keys" "^7.5.0"
|
||||
"@babel/plugin-transform-exponentiation-operator" "^7.2.0"
|
||||
"@babel/plugin-transform-for-of" "^7.4.4"
|
||||
"@babel/plugin-transform-function-name" "^7.4.4"
|
||||
"@babel/plugin-transform-literals" "^7.2.0"
|
||||
"@babel/plugin-transform-member-expression-literals" "^7.2.0"
|
||||
"@babel/plugin-transform-modules-amd" "^7.5.0"
|
||||
"@babel/plugin-transform-modules-commonjs" "^7.5.0"
|
||||
"@babel/plugin-transform-modules-systemjs" "^7.5.0"
|
||||
"@babel/plugin-transform-modules-umd" "^7.2.0"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.4.5"
|
||||
"@babel/plugin-transform-new-target" "^7.4.4"
|
||||
"@babel/plugin-transform-object-super" "^7.2.0"
|
||||
"@babel/plugin-transform-parameters" "^7.4.4"
|
||||
"@babel/plugin-transform-property-literals" "^7.2.0"
|
||||
"@babel/plugin-transform-regenerator" "^7.4.5"
|
||||
"@babel/plugin-transform-reserved-words" "^7.2.0"
|
||||
"@babel/plugin-transform-shorthand-properties" "^7.2.0"
|
||||
"@babel/plugin-transform-spread" "^7.2.0"
|
||||
"@babel/plugin-transform-sticky-regex" "^7.2.0"
|
||||
"@babel/plugin-transform-template-literals" "^7.4.4"
|
||||
"@babel/plugin-transform-typeof-symbol" "^7.2.0"
|
||||
"@babel/plugin-transform-unicode-regex" "^7.4.4"
|
||||
"@babel/types" "^7.5.0"
|
||||
browserslist "^4.6.0"
|
||||
core-js-compat "^3.1.1"
|
||||
invariant "^2.2.2"
|
||||
js-levenshtein "^1.1.3"
|
||||
semver "^5.5.0"
|
||||
|
||||
"@babel/preset-env@^7.4.5":
|
||||
"@babel/preset-env@7.5.5", "@babel/preset-env@^7.4.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a"
|
||||
integrity sha512-GMZQka/+INwsMz1A5UEql8tG015h5j/qjptpKY2gJ7giy8ohzU710YciJB5rcKsWGWHiW3RUnHib0E5/m3Tp3A==
|
||||
|
@ -1227,7 +1143,7 @@
|
|||
"@babel/parser" "^7.4.4"
|
||||
"@babel/types" "^7.4.4"
|
||||
|
||||
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.0", "@babel/traverse@^7.5.5":
|
||||
"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4", "@babel/traverse@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.5.5.tgz#f664f8f368ed32988cd648da9f72d5ca70f165bb"
|
||||
integrity sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==
|
||||
|
@ -1242,7 +1158,7 @@
|
|||
globals "^11.1.0"
|
||||
lodash "^4.17.13"
|
||||
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.0", "@babel/types@^7.5.5":
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5":
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.5.5.tgz#97b9f728e182785909aa4ab56264f090a028d18a"
|
||||
integrity sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==
|
||||
|
@ -1573,10 +1489,10 @@
|
|||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^12.0.9"
|
||||
|
||||
"@material-ui/core@4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.2.0.tgz#fd57352c63a50bc28d8b0d61a779a55aba84f9c4"
|
||||
integrity sha512-kqwoCMpGaj3zJedihUuVZWjISh+T72KAXOwgk6VKNf+APMTB8yLByLSgSLDhXsliRBO/9Pda/0g/KzGY7R+irQ==
|
||||
"@material-ui/core@4.2.1":
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.2.1.tgz#18255c01d039ff856bfdb2f955fec6c9ae64a464"
|
||||
integrity sha512-hasPQUFAb9OxKng7UX2+SjUWtVZbnkVJ/jHZWXTivVcU+UzvNIpA9AyRRQvZ8SPV6swP/HD2VzUBzoMEeRR6wg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
"@material-ui/styles" "^4.2.0"
|
||||
|
@ -1585,8 +1501,8 @@
|
|||
"@material-ui/utils" "^4.1.0"
|
||||
"@types/react-transition-group" "^2.0.16"
|
||||
clsx "^1.0.2"
|
||||
convert-css-length "^2.0.0"
|
||||
deepmerge "^3.0.0"
|
||||
convert-css-length "^2.0.1"
|
||||
deepmerge "^4.0.0"
|
||||
hoist-non-react-statics "^3.2.1"
|
||||
is-plain-object "^3.0.0"
|
||||
normalize-scroll-left "^0.2.0"
|
||||
|
@ -4305,14 +4221,14 @@ bignumber.js@^7.2.1:
|
|||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f"
|
||||
integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==
|
||||
|
||||
"bignumber.js@git+https://github.com/debris/bignumber.js#master":
|
||||
version "2.0.7"
|
||||
resolved "git+https://github.com/debris/bignumber.js#c7a38de919ed75e6fb6ba38051986e294b328df9"
|
||||
|
||||
"bignumber.js@git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2":
|
||||
version "2.0.7"
|
||||
resolved "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
|
||||
|
||||
"bignumber.js@git+https://github.com/debris/bignumber.js.git#master":
|
||||
version "2.0.7"
|
||||
resolved "git+https://github.com/debris/bignumber.js.git#c7a38de919ed75e6fb6ba38051986e294b328df9"
|
||||
|
||||
"bignumber.js@git+https://github.com/frozeman/bignumber.js-nolookahead.git":
|
||||
version "2.0.7"
|
||||
resolved "git+https://github.com/frozeman/bignumber.js-nolookahead.git#57692b3ecfc98bbdd6b3a516cb2353652ea49934"
|
||||
|
@ -5452,7 +5368,7 @@ content-type@~1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
|
||||
convert-css-length@^2.0.0:
|
||||
convert-css-length@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/convert-css-length/-/convert-css-length-2.0.1.tgz#90a76bde5bfd24d72881a5b45d02249b2c1d257c"
|
||||
integrity sha512-iGpbcvhLPRKUbBc0Quxx7w/bV14AC3ItuBEGMahA5WTYqB8lq9jH0kTXFheCBASsYnqeMFZhiTruNxr1N59Axg==
|
||||
|
@ -5731,10 +5647,10 @@ css-in-js-utils@^2.0.0:
|
|||
hyphenate-style-name "^1.0.2"
|
||||
isobject "^3.0.1"
|
||||
|
||||
css-loader@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.0.0.tgz#bdd48a4921eefedf1f0a55266585944d4e5efc63"
|
||||
integrity sha512-WR6KZuCkFbnMhRrGPlkwAA7SSCtwqPwpyXJAPhotYkYsc0mKU9n/fu5wufy4jl2WhBw9Ia8gUQMIp/1w98DuPw==
|
||||
css-loader@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.1.0.tgz#6f008b993b8ce812e6bab57f3cbfdc7a7cf28685"
|
||||
integrity sha512-MuL8WsF/KSrHCBCYaozBKlx+r7vIfUaDTEreo7wR7Vv3J6N0z6fqWjRk3e/6wjneitXN1r/Y9FTK1psYNOBdJQ==
|
||||
dependencies:
|
||||
camelcase "^5.3.1"
|
||||
cssesc "^3.0.0"
|
||||
|
@ -5747,7 +5663,7 @@ css-loader@3.0.0:
|
|||
postcss-modules-scope "^2.1.0"
|
||||
postcss-modules-values "^3.0.0"
|
||||
postcss-value-parser "^4.0.0"
|
||||
schema-utils "^1.0.0"
|
||||
schema-utils "^2.0.0"
|
||||
|
||||
css-loader@^2.1.1:
|
||||
version "2.1.1"
|
||||
|
@ -6118,11 +6034,6 @@ deep-object-diff@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a"
|
||||
integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
|
||||
|
||||
deepmerge@^3.0.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7"
|
||||
integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==
|
||||
|
||||
deepmerge@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.0.0.tgz#3e3110ca29205f120d7cb064960a39c3d2087c09"
|
||||
|
@ -6828,17 +6739,17 @@ eslint-module-utils@^2.4.0:
|
|||
debug "^2.6.8"
|
||||
pkg-dir "^2.0.0"
|
||||
|
||||
eslint-plugin-flowtype@3.11.1:
|
||||
version "3.11.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.11.1.tgz#1aae15a10dbcd5aecc89897f810f2e9fcc18a5e3"
|
||||
integrity sha512-4NiaaGZuz9iEGRTK8j4lkA/scibOXSYaYoHbsTtgLOxxqQCkbWV3xt8ETqILKg7DAYDqB69z1H5U71UmtdF9hw==
|
||||
eslint-plugin-flowtype@3.12.1:
|
||||
version "3.12.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.12.1.tgz#b673c716b578c9aa66887feef3bc146f8cbe1c21"
|
||||
integrity sha512-NZqf5iRgsfHOC31HQdtX2pvzCi0n/j9pB+L7Cf9QtuYxpx0i2wObT+R3rPKhQK4KtEDzGuzPYVf75j4eg+s9ZQ==
|
||||
dependencies:
|
||||
lodash "^4.17.11"
|
||||
|
||||
eslint-plugin-import@2.18.0:
|
||||
version "2.18.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.0.tgz#7a5ba8d32622fb35eb9c8db195c2090bd18a3678"
|
||||
integrity sha512-PZpAEC4gj/6DEMMoU2Df01C5c50r7zdGIN52Yfi7CvvWaYssG7Jt5R9nFG5gmqodxNOz9vQS87xk6Izdtpdrig==
|
||||
eslint-plugin-import@2.18.1:
|
||||
version "2.18.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.1.tgz#2e4f571d13839543992ad626a18c0edffde9626b"
|
||||
integrity sha512-YEESFKOcMIXJTosb5YaepqVhQHGMb8dxkgov560GqMDP/658U5vk6FeVSR7xXLeYkPc7xPYy+uAoiYE/bKMphA==
|
||||
dependencies:
|
||||
array-includes "^3.0.3"
|
||||
contains-path "^0.1.0"
|
||||
|
@ -6847,15 +6758,15 @@ eslint-plugin-import@2.18.0:
|
|||
eslint-import-resolver-node "^0.3.2"
|
||||
eslint-module-utils "^2.4.0"
|
||||
has "^1.0.3"
|
||||
lodash "^4.17.11"
|
||||
minimatch "^3.0.4"
|
||||
object.values "^1.1.0"
|
||||
read-pkg-up "^2.0.0"
|
||||
resolve "^1.11.0"
|
||||
|
||||
eslint-plugin-jest@22.9.0:
|
||||
version "22.9.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.9.0.tgz#2573dbcb4f1066b96a6e6d3b9aa439c80b28975a"
|
||||
integrity sha512-V89BUiwf76FHlhj1mlNhNyvpzTy8VbWCh2RZpKYz/XDSl/pcuwFiE/LMt7r3q1sRKygzEMjbYeDob8MMuvakXg==
|
||||
eslint-plugin-jest@22.11.1:
|
||||
version "22.11.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.11.1.tgz#04b586e2fddd07e55900a381255d6b3d9242ae87"
|
||||
integrity sha512-kPF1Nmr5xMLz6DT7qEttz0TTeyx1x6SozIkNO9y4F2yxuWjHMp/e70fo742pR3y0MewgXQQMIIXeSKLB66iO7Q==
|
||||
|
||||
eslint-plugin-jsx-a11y@6.2.3:
|
||||
version "6.2.3"
|
||||
|
@ -7768,13 +7679,13 @@ file-entry-cache@^5.0.1:
|
|||
dependencies:
|
||||
flat-cache "^2.0.1"
|
||||
|
||||
file-loader@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.0.0.tgz#c3570783fefb6e1bc0978a856f4bf5825b966c2a"
|
||||
integrity sha512-roAbL6IdSGczwfXxhMi6Zq+jD4IfUpL0jWHD7fvmjdOVb7xBfdRUHe4LpBgO23VtVK5AW1OlWZo0p34Jvx3iWg==
|
||||
file-loader@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.1.0.tgz#3a763391bc9502da7c59612fe348e38fc1980336"
|
||||
integrity sha512-ajDk1nlByoalZAGR4b0H6oD+EGlWnyW1qbSxzaUc7RFiqmn+RbXQQRbTc72jsiUIlVusJ4Et58ltds8ZwTfnAw==
|
||||
dependencies:
|
||||
loader-utils "^1.2.2"
|
||||
schema-utils "^1.0.0"
|
||||
loader-utils "^1.2.3"
|
||||
schema-utils "^2.0.0"
|
||||
|
||||
file-loader@^3.0.1:
|
||||
version "3.0.1"
|
||||
|
@ -7967,10 +7878,10 @@ flatted@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
|
||||
integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
|
||||
|
||||
flow-bin@0.102.0:
|
||||
version "0.102.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.102.0.tgz#3d5de44bcc26d26585e932b3201988b766f9b380"
|
||||
integrity sha512-mYon6noeLO0Q5SbiWULLQeM1L96iuXnRtYMd47j3bEWXAwUW9EnwNWcn+cZg/jC/Dg4Wj/jnkdTDEuFtbeu1ww==
|
||||
flow-bin@0.103.0:
|
||||
version "0.103.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.103.0.tgz#7aec510d85e1c1b0f2b912bb988337d70035cb0f"
|
||||
integrity sha512-Y3yrnE5ICN1Kl/y10BwjA3JSuS+gt4jVPNyUNCZb0RqmkdssMrW8QNNysJYvhgAY/JBJH8Qv7NVUf11MiwfSlA==
|
||||
|
||||
flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
|
||||
version "1.1.1"
|
||||
|
@ -10636,6 +10547,11 @@ jsprim@^1.2.2:
|
|||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
jsqr@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.2.0.tgz#f93fc65fa7d1ded78b1bcb020fa044352b04261a"
|
||||
integrity sha512-wKcQS9QC2VHGk7aphWCp1RrFyC0CM6fMgC5prZZ2KV/Lk6OKNoCod9IR6bao+yx3KPY0gZFC5dc+h+KFzCI0Wg==
|
||||
|
||||
jss-plugin-camel-case@10.0.0-alpha.17:
|
||||
version "10.0.0-alpha.17"
|
||||
resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.17.tgz#6f7c9d9742e349bb061e53cd9b1c3cb006169a67"
|
||||
|
@ -11039,7 +10955,7 @@ loader-runner@^2.3.0:
|
|||
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
|
||||
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
|
||||
|
||||
loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.2, loader-utils@^1.2.3:
|
||||
loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
|
||||
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
|
||||
|
@ -13110,7 +13026,7 @@ postcss-minify-selectors@^4.0.2:
|
|||
postcss "^7.0.0"
|
||||
postcss-selector-parser "^3.0.0"
|
||||
|
||||
postcss-mixins@^6.2.0:
|
||||
postcss-mixins@6.2.2:
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-mixins/-/postcss-mixins-6.2.2.tgz#3acea63271e2c75db62fb80bc1c29e1a609a4742"
|
||||
integrity sha512-QqEZamiAMguYR6d2h73XXEHZgkxs03PlbU0PqgqtdCnbRlMLFNQgsfL/Td0rjIe2SwpLXOQyB9uoiLWa4GR7tg==
|
||||
|
@ -13965,10 +13881,10 @@ react-helmet-async@^1.0.2:
|
|||
react-fast-compare "2.0.4"
|
||||
shallowequal "1.1.0"
|
||||
|
||||
react-hot-loader@4.12.7:
|
||||
version "4.12.7"
|
||||
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.12.7.tgz#51ed57ee46c5d4d3906e58e8cdcd3f6ceeb1c0ec"
|
||||
integrity sha512-ejnGcNttqIsgaLEpCl3KHLzFfKiEKHz/VTLYv57/xKQoryDMXQ/w31+jicrOAiCStYsY+KvrulVqkOqqkRaifg==
|
||||
react-hot-loader@4.12.8:
|
||||
version "4.12.8"
|
||||
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.12.8.tgz#90ecf2ef7d4005e110292760f135c95177e804c7"
|
||||
integrity sha512-/Df2J3znMHzRzI6CW0dTOIWD2sjkVHxv56XCqujAo9mR+k2PVTiGjUgYBiGPGsix9zQzgCRfOKca93o9Zdj2vQ==
|
||||
dependencies:
|
||||
fast-levenshtein "^2.0.6"
|
||||
global "^4.3.0"
|
||||
|
@ -14053,6 +13969,15 @@ react-popper@^1.3.3:
|
|||
typed-styles "^0.0.7"
|
||||
warning "^4.0.2"
|
||||
|
||||
react-qr-reader@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.2.1.tgz#dc89046d1c1a1da837a683dd970de5926817d55b"
|
||||
integrity sha512-EL5JEj53u2yAOgtpAKAVBzD/SiKWn0Bl7AZy6ZrSf1lub7xHwtaXe6XSx36Wbhl1VMGmvmrwYMRwO1aSCT2fwA==
|
||||
dependencies:
|
||||
jsqr "^1.2.0"
|
||||
prop-types "^15.7.2"
|
||||
webrtc-adapter "^7.2.1"
|
||||
|
||||
react-redux@7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.0.tgz#72af7cf490a74acdc516ea9c1dd80e25af9ea0b2"
|
||||
|
@ -14917,6 +14842,13 @@ rsvp@^4.8.4:
|
|||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
||||
|
||||
rtcpeerconnection-shim@^1.2.15:
|
||||
version "1.2.15"
|
||||
resolved "https://registry.yarnpkg.com/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz#e7cc189a81b435324c4949aa3dfb51888684b243"
|
||||
integrity sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==
|
||||
dependencies:
|
||||
sdp "^2.6.0"
|
||||
|
||||
run-async@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
||||
|
@ -15052,6 +14984,14 @@ schema-utils@^1.0.0:
|
|||
ajv-errors "^1.0.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
schema-utils@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.0.1.tgz#1eec2e059556af841b7f3a83b61af13d7a3f9196"
|
||||
integrity sha512-HJFKJ4JixDpRur06QHwi8uu2kZbng318ahWEKgBjc0ZklcE4FDvmm2wghb448q0IRaABxIESt8vqPFvwgMB80A==
|
||||
dependencies:
|
||||
ajv "^6.1.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
scrypt-js@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4"
|
||||
|
@ -15101,6 +15041,11 @@ scryptsy@^1.2.1:
|
|||
dependencies:
|
||||
pbkdf2 "^3.0.3"
|
||||
|
||||
sdp@^2.6.0, sdp@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.9.0.tgz#2eed2d9c0b26c81ff87593107895c68d6fb9a0a6"
|
||||
integrity sha512-XAVZQO4qsfzVTHorF49zCpkdxiGmPNjA8ps8RcJGtGP3QJ/A8I9/SVg/QnkAFDMXIyGbHZBBFwYBw6WdnhT96w==
|
||||
|
||||
seamless-immutable@^7.1.3:
|
||||
version "7.1.4"
|
||||
resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8"
|
||||
|
@ -16566,7 +16511,7 @@ truffle-compile@^4.1.5:
|
|||
truffle-error "^0.0.5"
|
||||
truffle-expect "^0.0.9"
|
||||
|
||||
truffle-config@^1.0.1, truffle-config@^1.1.14, truffle-config@^1.1.15:
|
||||
truffle-config@^1.0.1, truffle-config@^1.1.15:
|
||||
version "1.1.15"
|
||||
resolved "https://registry.yarnpkg.com/truffle-config/-/truffle-config-1.1.15.tgz#9c4e647f32d7fe78017f6823a81d3048376bc460"
|
||||
integrity sha512-boLm/g36ytCtwNLaFZ7ZRv7Y1Ar0lCqgUxi2NkWwFeVoIv7V7ew27v8mziztmVvOXSSXowtkZfWNm4HAYVCWIw==
|
||||
|
@ -16627,10 +16572,10 @@ truffle-contract@4.0.0-next.0:
|
|||
web3-eth-abi "1.0.0-beta.35"
|
||||
web3-utils "1.0.0-beta.35"
|
||||
|
||||
truffle-contract@4.0.24:
|
||||
version "4.0.24"
|
||||
resolved "https://registry.yarnpkg.com/truffle-contract/-/truffle-contract-4.0.24.tgz#3e50774bb88f7552f2c01bde80fcdae4ccf9fd42"
|
||||
integrity sha512-Zcv6rr2iMJWtQo+GbSHTtih13rLh3VU1SLMiT7odDd0fNiin2lBlLnJlIU2Xpfh06CP9240LAU2r6H0g81uCAA==
|
||||
truffle-contract@4.0.25, truffle-contract@^4.0.25:
|
||||
version "4.0.25"
|
||||
resolved "https://registry.yarnpkg.com/truffle-contract/-/truffle-contract-4.0.25.tgz#dc58c3ad20a4b1654efc0d55020e3bb8b9adac66"
|
||||
integrity sha512-ngy+ljTCSs/Arv4/9xUjq6mBdAHvQjFvJGqMcBDoNZSXUdvuW4qmX9gu/cOFEt2hdsBcpt0sx7DrXGlY4O97ww==
|
||||
dependencies:
|
||||
bignumber.js "^7.2.1"
|
||||
ethers "^4.0.0-beta.1"
|
||||
|
@ -16653,23 +16598,7 @@ truffle-contract@^2.0.3:
|
|||
truffle-contract-schema "^0.0.5"
|
||||
web3 "^0.20.1"
|
||||
|
||||
truffle-contract@^4.0.25:
|
||||
version "4.0.25"
|
||||
resolved "https://registry.yarnpkg.com/truffle-contract/-/truffle-contract-4.0.25.tgz#dc58c3ad20a4b1654efc0d55020e3bb8b9adac66"
|
||||
integrity sha512-ngy+ljTCSs/Arv4/9xUjq6mBdAHvQjFvJGqMcBDoNZSXUdvuW4qmX9gu/cOFEt2hdsBcpt0sx7DrXGlY4O97ww==
|
||||
dependencies:
|
||||
bignumber.js "^7.2.1"
|
||||
ethers "^4.0.0-beta.1"
|
||||
truffle-blockchain-utils "^0.0.10"
|
||||
truffle-contract-schema "^3.0.11"
|
||||
truffle-error "^0.0.5"
|
||||
truffle-interface-adapter "^0.2.0"
|
||||
web3 "1.0.0-beta.37"
|
||||
web3-core-promievent "1.0.0-beta.37"
|
||||
web3-eth-abi "1.0.0-beta.37"
|
||||
web3-utils "1.0.0-beta.37"
|
||||
|
||||
truffle-core@^5.0.27:
|
||||
truffle-core@^5.0.28:
|
||||
version "5.0.28"
|
||||
resolved "https://registry.yarnpkg.com/truffle-core/-/truffle-core-5.0.28.tgz#0826dac6a5f8c50b498e8e6183bb5f46e8ad4ba8"
|
||||
integrity sha512-gpoXHvWcDElaH8d8JjCMogJZMtNutpb/7dAhcadZ5Ik4HnPLOVbjFFkMTaH56x3jL4p52NjcktQV3ZmQZ6/rew==
|
||||
|
@ -16911,17 +16840,17 @@ truffle-resolver@^5.0.14:
|
|||
truffle-expect "^0.0.9"
|
||||
truffle-provisioner "^0.1.5"
|
||||
|
||||
truffle-solidity-loader@0.1.26:
|
||||
version "0.1.26"
|
||||
resolved "https://registry.yarnpkg.com/truffle-solidity-loader/-/truffle-solidity-loader-0.1.26.tgz#b7b517d0d375e303211c5030642e19779715b1ee"
|
||||
integrity sha512-qa5LGNxP+l5e4kYLz8PdliZZxf0zXzAPh/g56OdX7f/RW4nAM1yYF+1aUafWLWQuhtl85X+P+iA3BSWHYXJ+4A==
|
||||
truffle-solidity-loader@0.1.27:
|
||||
version "0.1.27"
|
||||
resolved "https://registry.yarnpkg.com/truffle-solidity-loader/-/truffle-solidity-loader-0.1.27.tgz#e4c06e52d8cbb64612827652244a89ec338fdbf0"
|
||||
integrity sha512-t4DTulVIan1J7BFxgwyeF1z7S4nLo0z4n/WwyPu+9WRvAruxcCoQG45wUTGdjLyPpuNV2iBjt5Hbv/UV0uElNw==
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
find-up "^1.1.2"
|
||||
loader-utils "^1.1.0"
|
||||
schema-utils "^1.0.0"
|
||||
truffle-config "^1.1.14"
|
||||
truffle-core "^5.0.27"
|
||||
truffle-config "^1.1.15"
|
||||
truffle-core "^5.0.28"
|
||||
|
||||
truffle-solidity-utils@^1.2.3:
|
||||
version "1.2.3"
|
||||
|
@ -16942,10 +16871,10 @@ truffle-workflow-compile@^2.0.24:
|
|||
truffle-external-compile "^1.0.11"
|
||||
truffle-resolver "^5.0.14"
|
||||
|
||||
truffle@5.0.27:
|
||||
version "5.0.27"
|
||||
resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.27.tgz#2842539f69d55a7cf82d21c4ffab0605953e1942"
|
||||
integrity sha512-MN02w3TNOwVaEvvCD535pov7D/59p92yzQ/Os4hR2kiSGy1s+9V6RYtmOPucY8G/1wx8RnvOAgcIzcqHrO6tvQ==
|
||||
truffle@5.0.28:
|
||||
version "5.0.28"
|
||||
resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.28.tgz#383c6c4ae082c52fe6dda6620f924ae3a0f5ac49"
|
||||
integrity sha512-ccpvgrHW7EGfJ4cowMgh5dCAILG5Ie8q2QNBhnGtHxyPTDxb8o2/sgVIi5BZhaueijOPb47sYe2ojKlY6dk8+Q==
|
||||
dependencies:
|
||||
app-module-path "^2.2.0"
|
||||
mocha "5.2.0"
|
||||
|
@ -18546,36 +18475,7 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0:
|
|||
source-list-map "^2.0.0"
|
||||
source-map "~0.6.1"
|
||||
|
||||
webpack@4.35.3:
|
||||
version "4.35.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.35.3.tgz#66bc35ef215a7b75e8790f84d560013ffecf0ca3"
|
||||
integrity sha512-xggQPwr9ILlXzz61lHzjvgoqGU08v5+Wnut19Uv3GaTtzN4xBTcwnobodrXE142EL1tOiS5WVEButooGzcQzTA==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.8.5"
|
||||
"@webassemblyjs/helper-module-context" "1.8.5"
|
||||
"@webassemblyjs/wasm-edit" "1.8.5"
|
||||
"@webassemblyjs/wasm-parser" "1.8.5"
|
||||
acorn "^6.2.0"
|
||||
ajv "^6.1.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
chrome-trace-event "^1.0.0"
|
||||
enhanced-resolve "^4.1.0"
|
||||
eslint-scope "^4.0.0"
|
||||
json-parse-better-errors "^1.0.2"
|
||||
loader-runner "^2.3.0"
|
||||
loader-utils "^1.1.0"
|
||||
memory-fs "~0.4.1"
|
||||
micromatch "^3.1.8"
|
||||
mkdirp "~0.5.0"
|
||||
neo-async "^2.5.0"
|
||||
node-libs-browser "^2.0.0"
|
||||
schema-utils "^1.0.0"
|
||||
tapable "^1.1.0"
|
||||
terser-webpack-plugin "^1.1.0"
|
||||
watchpack "^1.5.0"
|
||||
webpack-sources "^1.3.0"
|
||||
|
||||
webpack@^4.33.0:
|
||||
webpack@4.36.1, webpack@^4.33.0:
|
||||
version "4.36.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.36.1.tgz#f546fda7a403a76faeaaa7196c50d12370ed18a9"
|
||||
integrity sha512-Ej01/N9W8DVyhEpeQnbUdGvOECw0L46FxS12cCOs8gSK7bhUlrbHRnWkjiXckGlHjUrmL89kDpTRIkUk6Y+fKg==
|
||||
|
@ -18604,6 +18504,14 @@ webpack@^4.33.0:
|
|||
watchpack "^1.5.0"
|
||||
webpack-sources "^1.3.0"
|
||||
|
||||
webrtc-adapter@^7.2.1:
|
||||
version "7.2.8"
|
||||
resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-7.2.8.tgz#1373fa874559c655aa713830c2836511588d77ab"
|
||||
integrity sha512-d/rZVIIqqPqu/1I9rabhI+hmVhNtT+MoJk0eipCJasiVM9L9ZOTBoVhZmtC/naB4G8GTvnCaassrDz5IqWZP6w==
|
||||
dependencies:
|
||||
rtcpeerconnection-shim "^1.2.15"
|
||||
sdp "^2.9.0"
|
||||
|
||||
websocket-driver@>=0.5.1:
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"
|
||||
|
|
Loading…
Reference in New Issue