Merge pull request #16 from gnosis/feature/WA-230-display-eth-funds
WA-230 Display and update ETH funds
This commit is contained in:
commit
58e935d5f3
|
@ -4,15 +4,15 @@
|
|||
}
|
||||
|
||||
.soft {
|
||||
color: #888888;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.medium {
|
||||
color: #686868;
|
||||
color: #686868;
|
||||
}
|
||||
|
||||
.dark {
|
||||
color: black;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.primary {
|
||||
|
|
|
@ -1,33 +1,18 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import Open from '~/routes/open/container/Open'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'react-router-redux'
|
||||
import { store } from '~/store'
|
||||
import { FIELD_NAME, FIELD_OWNERS, FIELD_CONFIRMATIONS, getOwnerNameBy, getOwnerAddressBy } from '~/routes/open/components/fields'
|
||||
import { DEPLOYED_COMPONENT_ID } from '~/routes/open/components/FormConfirmation'
|
||||
import { history, store } from '~/store'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { getProviderInfo } from '~/wallets/getWeb3'
|
||||
import addProvider from '~/wallets/store/actions/addProvider'
|
||||
import { makeProvider } from '~/wallets/store/model/provider'
|
||||
import { renderSafe } from '~/routes/safe/store/test/builder/deployedSafe.builder'
|
||||
|
||||
describe('React DOM TESTS > Create Safe form', () => {
|
||||
let open
|
||||
let provider
|
||||
beforeEach(async () => {
|
||||
// init app web3 instance
|
||||
provider = await getProviderInfo()
|
||||
const walletRecord = makeProvider(provider)
|
||||
store.dispatch(addProvider(walletRecord))
|
||||
|
||||
open = TestUtils.renderIntoDocument((
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Open />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
))
|
||||
open = await renderSafe(store)
|
||||
})
|
||||
|
||||
it('should create a 1 owner safe after rendering correctly the form', async () => {
|
||||
|
|
|
@ -6,10 +6,10 @@ import GnoSafe from './Safe'
|
|||
|
||||
type Props = SelectorProps
|
||||
|
||||
const Layout = ({ safe, provider }: Props) => (
|
||||
const Layout = ({ safe, balance, provider }: Props) => (
|
||||
<React.Fragment>
|
||||
{ safe
|
||||
? <GnoSafe safe={safe} />
|
||||
? <GnoSafe safe={safe} balance={balance} />
|
||||
: <NoSafe provider={provider} text="Not found safe" />
|
||||
}
|
||||
</React.Fragment>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { storiesOf } from '@storybook/react'
|
||||
import * as React from 'react'
|
||||
import styles from '~/components/layout/PageFrame/index.scss'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/index.builder'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/safe.builder'
|
||||
import Component from './Layout'
|
||||
|
||||
|
||||
|
@ -14,12 +14,31 @@ const FrameDecorator = story => (
|
|||
|
||||
storiesOf('Routes /safe:address', module)
|
||||
.addDecorator(FrameDecorator)
|
||||
.add('Safe undefined being connected', () => <Component safe={undefined} provider="METAMASK" />)
|
||||
.add('Safe undefined NOT connected', () => <Component safe={undefined} provider="" />)
|
||||
.add('Safe undefined being connected', () => (
|
||||
<Component
|
||||
safe={undefined}
|
||||
provider="METAMASK"
|
||||
balance="0"
|
||||
fetchBalance={() => {}}
|
||||
/>
|
||||
))
|
||||
.add('Safe undefined NOT connected', () => (
|
||||
<Component
|
||||
safe={undefined}
|
||||
provider=""
|
||||
balance="0"
|
||||
fetchBalance={() => {}}
|
||||
/>
|
||||
))
|
||||
.add('Safe with 2 owners', () => {
|
||||
const safe = SafeFactory.twoOwnersSafe
|
||||
|
||||
return (
|
||||
<Component safe={safe} provider="METAMASK" />
|
||||
<Component
|
||||
safe={safe}
|
||||
provider="METAMASK"
|
||||
balance="2"
|
||||
fetchBalance={() => {}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -10,9 +10,10 @@ import { type Safe } from '~/routes/safe/store/model/safe'
|
|||
|
||||
type SafeProps = {
|
||||
safe: Safe,
|
||||
balance: string,
|
||||
}
|
||||
|
||||
const GnoSafe = ({ safe }: SafeProps) => (
|
||||
const GnoSafe = ({ safe, balance }: SafeProps) => (
|
||||
<React.Fragment>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
|
@ -21,6 +22,18 @@ const GnoSafe = ({ safe }: SafeProps) => (
|
|||
</Paragraph>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Paragraph size="lg">
|
||||
<Bold>Balance</Bold>
|
||||
</Paragraph>
|
||||
</Row>
|
||||
<Row>
|
||||
<Block>
|
||||
<Paragraph>
|
||||
{balance} - ETH
|
||||
</Paragraph>
|
||||
</Block>
|
||||
</Row>
|
||||
<Row>
|
||||
<Paragraph size="lg">
|
||||
<Bold>Address</Bold>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
// @flow
|
||||
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'
|
||||
|
||||
export type Actions = {
|
||||
fetchBalance: typeof fetchBalance,
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchBalance,
|
||||
}
|
|
@ -4,19 +4,40 @@ import { connect } from 'react-redux'
|
|||
import Page from '~/components/layout/Page'
|
||||
import Layout from '~/routes/safe/component/Layout'
|
||||
import selector, { type SelectorProps } from './selector'
|
||||
import actions, { type Actions } from './actions'
|
||||
|
||||
type Props = SelectorProps
|
||||
type Props = Actions & SelectorProps
|
||||
|
||||
class SafeView extends React.PureComponent<Props> {
|
||||
componentDidMount() {
|
||||
this.intervalId = setInterval(() => {
|
||||
const { safe, fetchBalance } = this.props
|
||||
if (!safe) { return }
|
||||
|
||||
const safeAddress: string = safe.get('address')
|
||||
fetchBalance(safeAddress)
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.intervalId)
|
||||
}
|
||||
|
||||
intervalId: IntervalID
|
||||
|
||||
render() {
|
||||
const { safe, provider } = this.props
|
||||
const { safe, provider, balance } = this.props
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<Layout provider={provider} safe={safe} />
|
||||
<Layout
|
||||
balance={balance}
|
||||
provider={provider}
|
||||
safe={safe}
|
||||
/>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(selector)(SafeView)
|
||||
export default connect(selector, actions)(SafeView)
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
// @flow
|
||||
import { createStructuredSelector } from 'reselect'
|
||||
import { safeSelector, type SafeSelectorProps } from '~/routes/safe/store/selectors'
|
||||
import { balanceSelector, safeSelector, type SafeSelectorProps } from '~/routes/safe/store/selectors'
|
||||
import { providerNameSelector } from '~/wallets/store/selectors/index'
|
||||
|
||||
export type SelectorProps = {
|
||||
safe: SafeSelectorProps,
|
||||
provider: string,
|
||||
balance: string,
|
||||
}
|
||||
|
||||
export default createStructuredSelector({
|
||||
safe: safeSelector,
|
||||
provider: providerNameSelector,
|
||||
balance: balanceSelector,
|
||||
})
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// @flow
|
||||
import { createAction } from 'redux-actions'
|
||||
|
||||
export const ADD_BALANCE = 'ADD_BALANCE'
|
||||
|
||||
type BalanceProps = {
|
||||
safeAddress: string,
|
||||
funds: string,
|
||||
}
|
||||
|
||||
const addBalance = createAction(
|
||||
ADD_BALANCE,
|
||||
(safeAddress: string, funds: string): BalanceProps => ({
|
||||
safeAddress,
|
||||
funds,
|
||||
}),
|
||||
)
|
||||
|
||||
export default addBalance
|
|
@ -0,0 +1,11 @@
|
|||
// @flow
|
||||
import type { Dispatch as ReduxDispatch } from 'redux'
|
||||
import { getBalanceInEtherOf } from '~/wallets/getWeb3'
|
||||
import { type GlobalState } from '~/store/index'
|
||||
import addBalance from './addBalance'
|
||||
|
||||
export default (safeAddress: string) => async (dispatch: ReduxDispatch<GlobalState>) => {
|
||||
const balance = await getBalanceInEtherOf(safeAddress)
|
||||
|
||||
return dispatch(addBalance(safeAddress, balance))
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// @flow
|
||||
import { Map } from 'immutable'
|
||||
import { handleActions, type ActionType } from 'redux-actions'
|
||||
import addBalance, { ADD_BALANCE } from '~/routes/safe/store/actions/addBalance'
|
||||
|
||||
export const BALANCE_REDUCER_ID = 'balances'
|
||||
|
||||
export type State = Map<string, string>
|
||||
|
||||
export default handleActions({
|
||||
[ADD_BALANCE]: (state: State, action: ActionType<typeof addBalance>): State =>
|
||||
state.set(action.payload.safeAddress, action.payload.funds),
|
||||
}, Map())
|
|
@ -6,6 +6,7 @@ import { type GlobalState } from '~/store/index'
|
|||
import { SAFE_PARAM_ADDRESS } from '~/routes/routes'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { safesMapSelector } from '~/routes/safeList/store/selectors'
|
||||
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'
|
||||
|
||||
type RouterProps = {
|
||||
match: Match,
|
||||
|
@ -13,6 +14,8 @@ type RouterProps = {
|
|||
|
||||
const safeAddessSelector = (state: GlobalState, props: RouterProps) => props.match.params[SAFE_PARAM_ADDRESS] || ''
|
||||
|
||||
const balancesSelector = (state: GlobalState) => state[BALANCE_REDUCER_ID]
|
||||
|
||||
export type SafeSelectorProps = Safe | typeof undefined
|
||||
|
||||
export const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps> = createSelector(
|
||||
|
@ -27,6 +30,18 @@ export const safeSelector: Selector<GlobalState, RouterProps, SafeSelectorProps>
|
|||
},
|
||||
)
|
||||
|
||||
export const balanceSelector: Selector<GlobalState, RouterProps, string> = createSelector(
|
||||
balancesSelector,
|
||||
safeAddessSelector,
|
||||
(balances: Map<string, string>, address: string) => {
|
||||
if (!address) {
|
||||
return '0'
|
||||
}
|
||||
|
||||
return balances.get(address) || '0'
|
||||
},
|
||||
)
|
||||
|
||||
export default createStructuredSelector({
|
||||
safe: safeSelector,
|
||||
})
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// @flow
|
||||
import { BALANCE_REDUCER_ID } from '~/routes/safe/store/reducer/balances'
|
||||
import fetchBalance from '~/routes/safe/store/actions/fetchBalance'
|
||||
import { aNewStore } from '~/store'
|
||||
import { getWeb3 } from '~/wallets/getWeb3'
|
||||
import { promisify } from '~/utils/promisify'
|
||||
import { aDeployedSafe } from './builder/deployedSafe.builder'
|
||||
|
||||
const addEtherTo = async (address: string, eth: string) => {
|
||||
const web3 = getWeb3()
|
||||
const accounts = await promisify(cb => web3.eth.getAccounts(cb))
|
||||
const txData = { from: accounts[0], to: address, value: web3.toWei(eth, 'ether') }
|
||||
return promisify(cb => web3.eth.sendTransaction(txData, cb))
|
||||
}
|
||||
|
||||
const balanceReducerTests = () => {
|
||||
describe('Safe Actions[fetchBalance]', () => {
|
||||
let store
|
||||
beforeEach(async () => {
|
||||
store = aNewStore()
|
||||
})
|
||||
|
||||
it('reducer should return 0 to just deployed safe', async () => {
|
||||
// GIVEN
|
||||
const safeTx = await aDeployedSafe(store)
|
||||
const address = safeTx.contractAddress
|
||||
|
||||
// WHEN
|
||||
await store.dispatch(fetchBalance(address))
|
||||
|
||||
// THEN
|
||||
const balances = store.getState()[BALANCE_REDUCER_ID]
|
||||
expect(balances).not.toBe(undefined)
|
||||
expect(balances.get(address)).toBe('0')
|
||||
})
|
||||
|
||||
it('reducer should return 1.3456 ETH as funds to safe with 1 ETH', async () => {
|
||||
// GIVEN
|
||||
const safeTx = await aDeployedSafe(store)
|
||||
const address = safeTx.contractAddress
|
||||
|
||||
// WHEN
|
||||
await addEtherTo(address, '1.3456')
|
||||
await store.dispatch(fetchBalance(address))
|
||||
|
||||
// THEN
|
||||
const balances = store.getState()[BALANCE_REDUCER_ID]
|
||||
expect(balances).not.toBe(undefined)
|
||||
expect(balances.get(address)).toBe('1.3456')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default balanceReducerTests
|
|
@ -0,0 +1,61 @@
|
|||
// @flow
|
||||
import { type Match } from 'react-router-dom'
|
||||
import addBalance from '~/routes/safe/store/actions/addBalance'
|
||||
import { aNewStore } from '~/store'
|
||||
import { balanceSelector } from '../selectors'
|
||||
|
||||
const buildMathPropsFrom = (address): Match => ({
|
||||
params: {
|
||||
address,
|
||||
},
|
||||
isExact: true,
|
||||
path: '',
|
||||
url: '',
|
||||
})
|
||||
|
||||
const balanceSelectorTests = () => {
|
||||
describe('Safe Selector[balanceSelector]', () => {
|
||||
it('should return 0 when safe address is not found', () => {
|
||||
// GIVEN
|
||||
const safeAddress = 'foo'
|
||||
const match = buildMathPropsFrom(safeAddress)
|
||||
const store = aNewStore()
|
||||
|
||||
// WHEN
|
||||
const balance = balanceSelector(store.getState(), { match })
|
||||
|
||||
// THEN
|
||||
expect(balance).toBe('0')
|
||||
})
|
||||
|
||||
it('should return 0 when safe has no funds', async () => {
|
||||
// GIVEN
|
||||
const safeAddress = 'foo'
|
||||
const match = buildMathPropsFrom(safeAddress)
|
||||
const store = aNewStore()
|
||||
|
||||
// WHEN
|
||||
await store.dispatch(addBalance('bar', '1'))
|
||||
const balance = balanceSelector(store.getState(), { match })
|
||||
|
||||
// THEN
|
||||
expect(balance).toBe('0')
|
||||
})
|
||||
|
||||
it('should return safe funds', async () => {
|
||||
// GIVEN
|
||||
const safeAddress = 'foo'
|
||||
const match = buildMathPropsFrom(safeAddress)
|
||||
const store = aNewStore()
|
||||
|
||||
// WHEN
|
||||
await store.dispatch(addBalance(safeAddress, '1.3456'))
|
||||
const balance = balanceSelector(store.getState(), { match })
|
||||
|
||||
// THEN
|
||||
expect(balance).toBe('1.3456')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default balanceSelectorTests
|
|
@ -0,0 +1,72 @@
|
|||
// @flow
|
||||
import * as React from 'react'
|
||||
import { type Store } from 'redux'
|
||||
import TestUtils from 'react-dom/test-utils'
|
||||
import { Provider } from 'react-redux'
|
||||
import { ConnectedRouter } from 'react-router-redux'
|
||||
import { DEPLOYED_COMPONENT_ID } from '~/routes/open/components/FormConfirmation'
|
||||
import Open from '~/routes/open/container/Open'
|
||||
import { history, type GlobalState } from '~/store'
|
||||
import { sleep } from '~/utils/timer'
|
||||
import { getProviderInfo } from '~/wallets/getWeb3'
|
||||
import addProvider from '~/wallets/store/actions/addProvider'
|
||||
import { makeProvider } from '~/wallets/store/model/provider'
|
||||
|
||||
export const renderSafe = async (localStore: Store<GlobalState>) => {
|
||||
const provider = await getProviderInfo()
|
||||
const walletRecord = makeProvider(provider)
|
||||
localStore.dispatch(addProvider(walletRecord))
|
||||
|
||||
return (
|
||||
TestUtils.renderIntoDocument((
|
||||
<Provider store={localStore}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Open />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
const deploySafe = async (safe: React$Component<{}>) => {
|
||||
const inputs = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
|
||||
const fieldName = inputs[0]
|
||||
const fieldOwners = inputs[1]
|
||||
const fieldConfirmations = inputs[2]
|
||||
|
||||
TestUtils.Simulate.change(fieldOwners, { target: { value: '1' } })
|
||||
const inputsExpanded = TestUtils.scryRenderedDOMComponentsWithTag(safe, 'input')
|
||||
const ownerName = inputsExpanded[2]
|
||||
|
||||
TestUtils.Simulate.change(fieldName, { target: { value: 'Adolfo Safe' } })
|
||||
TestUtils.Simulate.change(fieldConfirmations, { target: { value: '1' } })
|
||||
TestUtils.Simulate.change(ownerName, { target: { value: 'Adolfo Eth Account' } })
|
||||
|
||||
const form = TestUtils.findRenderedDOMComponentWithTag(safe, 'form')
|
||||
|
||||
TestUtils.Simulate.submit(form) // fill the form
|
||||
TestUtils.Simulate.submit(form) // confirming data
|
||||
TestUtils.Simulate.submit(form) // Executing transaction
|
||||
|
||||
// giving some time to the component for updating its state with safe
|
||||
// before destroying its context
|
||||
await sleep(1500)
|
||||
|
||||
// THEN
|
||||
const deployed = TestUtils.findRenderedDOMComponentWithClass(safe, DEPLOYED_COMPONENT_ID)
|
||||
if (!deployed) {
|
||||
throw new Error()
|
||||
}
|
||||
|
||||
const transactionHash = JSON.parse(deployed.getElementsByTagName('pre')[0].innerHTML)
|
||||
delete transactionHash.logsBloom
|
||||
|
||||
return transactionHash
|
||||
}
|
||||
|
||||
export const aDeployedSafe = async (specificStore: Store<GlobalState>) => {
|
||||
const safe: React$Component<{}> = await renderSafe(specificStore)
|
||||
const deployedSafe = deploySafe(safe)
|
||||
|
||||
return deployedSafe
|
||||
}
|
|
@ -5,7 +5,7 @@ import safeReducer, { calculateInitialState, SAFE_REDUCER_ID } from '~/routes/sa
|
|||
import addSafe from '~/routes/safe/store/actions/addSafe'
|
||||
import * as SafeFields from '~/routes/open/components/fields'
|
||||
import { getAccountsFrom, getNamesFrom } from '~/routes/open/utils/safeDataExtractor'
|
||||
import { SafeFactory } from './builder/index.builder'
|
||||
import { SafeFactory } from './builder/safe.builder'
|
||||
|
||||
const aStore = (initState) => {
|
||||
const reducers = combineReducers({
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Map } from 'immutable'
|
|||
import { type Match } from 'react-router-dom'
|
||||
import { SAFE_REDUCER_ID } from '~/routes/safe/store/reducer/safe'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/index.builder'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/safe.builder'
|
||||
import { safeSelector } from '../selectors'
|
||||
|
||||
const buildMathPropsFrom = (address): Match => ({
|
||||
|
@ -19,7 +19,11 @@ const safeSelectorTests = () => {
|
|||
describe('Safe Selector[safeSelector]', () => {
|
||||
it('should return empty list when no safes', () => {
|
||||
// GIVEN
|
||||
const reduxStore = { [SAFE_REDUCER_ID]: Map(), providers: undefined }
|
||||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
providers: undefined,
|
||||
balances: undefined,
|
||||
}
|
||||
const match: Match = buildMathPropsFrom('fooAddress')
|
||||
|
||||
// WHEN
|
||||
|
@ -38,7 +42,11 @@ const safeSelectorTests = () => {
|
|||
const match: Match = buildMathPropsFrom('fooAddress')
|
||||
const undefMatch: Match = buildMathPropsFrom('inventedAddress')
|
||||
|
||||
const reduxStore = { [SAFE_REDUCER_ID]: map, providers: undefined }
|
||||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
providers: undefined,
|
||||
balances: undefined,
|
||||
}
|
||||
|
||||
// WHEN
|
||||
const oneOwnerSafe = safeSelector(reduxStore, { match })
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
// @flow
|
||||
import balanceReducerTests from './balance.reducer'
|
||||
import safeReducerTests from './safe.reducer'
|
||||
import balanceSelectorTests from './balance.selector'
|
||||
import safeSelectorTests from './safe.selector'
|
||||
|
||||
describe('Safe Test suite', () => {
|
||||
// ACTIONS AND REDUCERS
|
||||
safeReducerTests()
|
||||
balanceReducerTests()
|
||||
|
||||
// SAFE SELECTOR
|
||||
safeSelectorTests()
|
||||
|
||||
// BALANCE SELECTOR
|
||||
balanceSelectorTests()
|
||||
})
|
||||
|
|
|
@ -3,7 +3,7 @@ import { storiesOf } from '@storybook/react'
|
|||
import { List } from 'immutable'
|
||||
import * as React from 'react'
|
||||
import styles from '~/components/layout/PageFrame/index.scss'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/index.builder'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/safe.builder'
|
||||
import Component from './Layout'
|
||||
|
||||
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
import { List, Map } from 'immutable'
|
||||
import { SAFE_REDUCER_ID } from '~/routes/safe/store/reducer/safe'
|
||||
import { type Safe } from '~/routes/safe/store/model/safe'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/index.builder'
|
||||
import { SafeFactory } from '~/routes/safe/store/test/builder/safe.builder'
|
||||
import { safesListSelector } from '../selectors'
|
||||
|
||||
const safesListSelectorTests = () => {
|
||||
describe('Safes Selector[safesSelector]', () => {
|
||||
it('should return empty list when no safes', () => {
|
||||
// GIVEN
|
||||
const reduxStore = { [SAFE_REDUCER_ID]: Map(), providers: undefined }
|
||||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: Map(),
|
||||
providers: undefined,
|
||||
balances: undefined,
|
||||
}
|
||||
const emptyList = List([])
|
||||
|
||||
// WHEN
|
||||
|
@ -25,7 +29,11 @@ const safesListSelectorTests = () => {
|
|||
map = map.set('fooAddress', SafeFactory.oneOwnerSafe)
|
||||
map = map.set('barAddress', SafeFactory.twoOwnersSafe)
|
||||
|
||||
const reduxStore = { [SAFE_REDUCER_ID]: map, providers: undefined }
|
||||
const reduxStore = {
|
||||
[SAFE_REDUCER_ID]: map,
|
||||
providers: undefined,
|
||||
balances: undefined,
|
||||
}
|
||||
|
||||
// WHEN
|
||||
const safes = safesListSelector(reduxStore)
|
||||
|
|
|
@ -5,6 +5,7 @@ import { combineReducers, createStore, applyMiddleware, compose, type Reducer, t
|
|||
import thunk from 'redux-thunk'
|
||||
import provider, { PROVIDER_REDUCER_ID, type State as ProviderState } from '~/wallets/store/reducer/provider'
|
||||
import safe, { SAFE_REDUCER_ID, calculateInitialState, type State as SafeState } from '~/routes/safe/store/reducer/safe'
|
||||
import balances, { BALANCE_REDUCER_ID, type State as BalancesState } from '~/routes/safe/store/reducer/balances'
|
||||
|
||||
export const history = createBrowserHistory()
|
||||
|
||||
|
@ -18,14 +19,19 @@ const finalCreateStore = composeEnhancers(applyMiddleware(
|
|||
export type GlobalState = {
|
||||
providers: ProviderState,
|
||||
safes: SafeState,
|
||||
balances: BalancesState,
|
||||
}
|
||||
|
||||
const reducers: Reducer<GlobalState> = combineReducers({
|
||||
routing: routerReducer,
|
||||
[PROVIDER_REDUCER_ID]: provider,
|
||||
[SAFE_REDUCER_ID]: safe,
|
||||
[BALANCE_REDUCER_ID]: balances,
|
||||
})
|
||||
|
||||
const initialState = { [SAFE_REDUCER_ID]: calculateInitialState() }
|
||||
|
||||
export const store: Store<GlobalState> = createStore(reducers, initialState, finalCreateStore)
|
||||
|
||||
export const aNewStore = (localState?: Object): Store<GlobalState> =>
|
||||
createStore(reducers, localState, finalCreateStore)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// @flow
|
||||
export const ensureOnce = (fn: Function): Function => {
|
||||
let executed = false
|
||||
let response
|
||||
|
||||
return (...args) => {
|
||||
if (executed) { return response }
|
||||
|
||||
executed = true
|
||||
response = fn(args)
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// @flow
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import Web3 from 'web3'
|
||||
import type { ProviderProps } from '~/wallets/store/model/provider'
|
||||
import { promisify } from '~/utils/promisify'
|
||||
|
@ -42,16 +43,11 @@ export const getProviderInfo: Function = async (): Promise<ProviderProps> => {
|
|||
}
|
||||
}
|
||||
|
||||
export const ensureOnce = (fn: Function): Function => {
|
||||
let executed = false
|
||||
let response
|
||||
|
||||
return (...args) => {
|
||||
if (executed) { return response }
|
||||
|
||||
executed = true
|
||||
response = fn(args)
|
||||
|
||||
return response
|
||||
export const getBalanceInEtherOf = async (safeAddress: string) => {
|
||||
const funds: BigNumber = await promisify(cb => web3.eth.getBalance(safeAddress, cb))
|
||||
if (!funds) {
|
||||
return '0'
|
||||
}
|
||||
|
||||
return web3.fromWei(funds.toNumber(), 'ether').toString()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue