Fix merge
This commit is contained in:
commit
bf5a24d762
|
@ -13,14 +13,14 @@ export const simpleMemoize = (fn) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const required = (value) => (value ? undefined : 'Required')
|
||||
export const required = (value?: string) => (value && value.trim() !== '' ? undefined : 'Required')
|
||||
|
||||
export const mustBeInteger = (value) =>
|
||||
export const mustBeInteger = (value: string) =>
|
||||
!Number.isInteger(Number(value)) || value.includes('.') ? 'Must be an integer' : undefined
|
||||
|
||||
export const mustBeFloat = (value) => (value && Number.isNaN(Number(value)) ? 'Must be a number' : undefined)
|
||||
export const mustBeFloat = (value: string) => (value && Number.isNaN(Number(value)) ? 'Must be a number' : undefined)
|
||||
|
||||
export const greaterThan = (min) => (value) => {
|
||||
export const greaterThan = (min: number | string) => (value: string) => {
|
||||
if (Number.isNaN(Number(value)) || Number.parseFloat(value) > Number(min)) {
|
||||
return undefined
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export const greaterThan = (min) => (value) => {
|
|||
|
||||
const regexQuery = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
|
||||
const url = new RegExp(regexQuery)
|
||||
export const mustBeUrl = (value) => {
|
||||
export const mustBeUrl = (value: string) => {
|
||||
if (url.test(value)) {
|
||||
return undefined
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ export const mustBeUrl = (value) => {
|
|||
return 'Please, provide a valid url'
|
||||
}
|
||||
|
||||
export const minValue = (min) => (value) => {
|
||||
export const minValue = (min: number | string) => (value: string) => {
|
||||
if (Number.isNaN(Number(value)) || Number.parseFloat(value) >= Number(min)) {
|
||||
return undefined
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ export const minValue = (min) => (value) => {
|
|||
return `Should be at least ${min}`
|
||||
}
|
||||
|
||||
export const maxValue = (max) => (value) => {
|
||||
if (Number.isNaN(Number(value)) || parseFloat(value) <= parseFloat(max)) {
|
||||
export const maxValue = (max: number | string) => (value: string) => {
|
||||
if (Number.isNaN(Number(value)) || parseFloat(value) <= parseFloat(max.toString())) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
@ -56,14 +56,14 @@ export const maxValue = (max) => (value) => {
|
|||
|
||||
export const ok = () => undefined
|
||||
|
||||
export const mustBeEthereumAddress = simpleMemoize((address) => {
|
||||
export const mustBeEthereumAddress = simpleMemoize((address: string) => {
|
||||
const startsWith0x = address.startsWith('0x')
|
||||
const isAddress = getWeb3().utils.isAddress(address)
|
||||
|
||||
return startsWith0x && isAddress ? undefined : 'Address should be a valid Ethereum address or ENS name'
|
||||
})
|
||||
|
||||
export const mustBeEthereumContractAddress = simpleMemoize(async (address) => {
|
||||
export const mustBeEthereumContractAddress = simpleMemoize(async (address: string) => {
|
||||
const contractCode = await getWeb3().eth.getCode(address)
|
||||
|
||||
return !contractCode || contractCode.replace('0x', '').replace(/0/g, '') === ''
|
||||
|
@ -76,8 +76,8 @@ export const minMaxLength = (minLen, maxLen) => (value) =>
|
|||
|
||||
export const ADDRESS_REPEATED_ERROR = 'Address already introduced'
|
||||
|
||||
export const uniqueAddress = (addresses) =>
|
||||
simpleMemoize((value) => {
|
||||
export const uniqueAddress = (addresses: string[]) =>
|
||||
simpleMemoize((value: string[]) => {
|
||||
const addressAlreadyExists = addresses.some((address) => sameAddress(value, address))
|
||||
return addressAlreadyExists ? ADDRESS_REPEATED_ERROR : undefined
|
||||
})
|
||||
|
@ -95,7 +95,7 @@ export const inLimit = (limit, base, baseText, symbol = 'ETH') => (value) => {
|
|||
return `Should not exceed ${max} ${symbol} (amount to reach ${baseText})`
|
||||
}
|
||||
|
||||
export const differentFrom = (diffValue) => (value) => {
|
||||
export const differentFrom = (diffValue: number | string) => (value: string) => {
|
||||
if (value === diffValue.toString()) {
|
||||
return `Value should be different than ${value}`
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
import { ensureOnce } from "src/utils/singleton"
|
||||
import { ETHEREUM_NETWORK } from "src/logic/wallets/getWeb3"
|
||||
import { ETHEREUM_NETWORK, getWeb3 } from 'src/logic/wallets/getWeb3'
|
||||
import {
|
||||
RELAY_API_URL,
|
||||
SIGNATURES_VIA_METAMASK,
|
||||
|
@ -58,6 +58,8 @@ export const getTxServiceUriFrom = (safeAddress) =>
|
|||
export const getIncomingTxServiceUriTo = (safeAddress) =>
|
||||
`safes/${safeAddress}/incoming-transfers/`
|
||||
|
||||
export const getSafeCreationTxUri = (safeAddress) => `safes/${safeAddress}/creation/`
|
||||
|
||||
export const getRelayUrl = () => getConfig()[RELAY_API_URL]
|
||||
|
||||
export const signaturesViaMetamask = () => {
|
||||
|
@ -79,3 +81,11 @@ export const getIntercomId = () =>
|
|||
export const getExchangeRatesUrl = () => 'https://api.exchangeratesapi.io/latest'
|
||||
|
||||
export const getSafeLastVersion = () => process.env.REACT_APP_LATEST_SAFE_VERSION || '1.1.1'
|
||||
|
||||
export const buildSafeCreationTxUrl = (safeAddress) => {
|
||||
const host = getTxServiceHost()
|
||||
const address = getWeb3().utils.toChecksumAddress(safeAddress)
|
||||
const base = getSafeCreationTxUri(address)
|
||||
|
||||
return `${host}${base}`
|
||||
}
|
|
@ -19,6 +19,7 @@ import { safeFeaturesEnabledSelector, safeParamAddressFromStateSelector } from '
|
|||
import { history } from 'src/store/index'
|
||||
import { wrapInSuspense } from 'src/utils/wrapInSuspense'
|
||||
import { useFetchTokens } from '../../container/hooks/useFetchTokens'
|
||||
|
||||
const Collectibles = React.lazy(() => import('src/routes/safe/components/Balances/Collectibles'))
|
||||
const Coins = React.lazy(() => import('src/routes/safe/components/Balances/Coins'))
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// @flow
|
||||
import React from 'react'
|
||||
import { formatDate } from '../../columns'
|
||||
import Bold from '../../../../../../../components/layout/Bold'
|
||||
import Paragraph from '../../../../../../../components/layout/Paragraph'
|
||||
import EtherscanLink from '../../../../../../../components/EtherscanLink'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Block from '../../../../../../../components/layout/Block'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
address: {
|
||||
height: '20px',
|
||||
},
|
||||
txData: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
lineHeight: '1.6',
|
||||
},
|
||||
txHash: {
|
||||
paddingRight: '3px',
|
||||
},
|
||||
})
|
||||
|
||||
export const CreationTx = (props) => {
|
||||
const { tx } = props
|
||||
const classes = useStyles()
|
||||
if (!tx) return null
|
||||
const isCreationTx = tx.type === 'creation'
|
||||
|
||||
console.log('Classes', classes)
|
||||
|
||||
return !isCreationTx ? null : (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
{formatDate(tx.created)}
|
||||
</Paragraph>
|
||||
<Block align="left" className={classes.txData}>
|
||||
<Bold className={classes.txHash}>Creator:</Bold>
|
||||
{tx.creator ? <EtherscanLink cut={8} type="address" value={tx.creator} /> : 'n/a'}
|
||||
</Block>
|
||||
<Block align="left" className={classes.txData}>
|
||||
<Bold className={classes.txHash}>Factory:</Bold>
|
||||
{tx.factoryAddress ? <EtherscanLink cut={8} type="address" value={tx.factoryAddress} /> : 'n/a'}
|
||||
</Block>
|
||||
<Block align="left" className={classes.txData}>
|
||||
<Bold className={classes.txHash}>Mastercopy:</Bold>
|
||||
{tx.masterCopy ? <EtherscanLink cut={8} type="address" value={tx.masterCopy} /> : 'n/a'}
|
||||
</Block>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react'
|
||||
import { INCOMING_TX_TYPES } from '../../../../../store/models/incomingTransaction'
|
||||
import { formatDate } from '../../columns'
|
||||
import Bold from '../../../../../../../components/layout/Bold'
|
||||
import Paragraph from '../../../../../../../components/layout/Paragraph'
|
||||
|
||||
export const IncomingTx = (props) => {
|
||||
const { tx } = props
|
||||
if (!tx) return null
|
||||
const isIncomingTx = !!INCOMING_TX_TYPES[tx.type]
|
||||
return !isIncomingTx ? null : (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
{formatDate(tx.executionDate)}
|
||||
</Paragraph>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
// @flow
|
||||
import React from 'react'
|
||||
import { formatDate } from '../../columns'
|
||||
import Bold from '../../../../../../../components/layout/Bold'
|
||||
import Paragraph from '../../../../../../../components/layout/Paragraph'
|
||||
|
||||
export const OutgoingTx = (props) => {
|
||||
const { tx } = props
|
||||
if (!tx || !(tx.type === 'outgoing')) return null
|
||||
return (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
{formatDate(tx.submissionDate)}
|
||||
</Paragraph>
|
||||
{tx.executionDate && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Executed: </Bold>
|
||||
{formatDate(tx.executionDate)}
|
||||
</Paragraph>
|
||||
)}
|
||||
{tx.refundParams && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Refund: </Bold>
|
||||
max. {tx.refundParams.fee} {tx.refundParams.symbol}
|
||||
</Paragraph>
|
||||
)}
|
||||
{tx.operation === 1 && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Delegate Call</Bold>
|
||||
</Paragraph>
|
||||
)}
|
||||
{tx.operation === 2 && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Contract Creation</Bold>
|
||||
</Paragraph>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -3,8 +3,6 @@ import cn from 'classnames'
|
|||
import React, { useState } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import { formatDate } from '../columns'
|
||||
|
||||
import ApproveTxModal from './ApproveTxModal'
|
||||
import OwnersColumn from './OwnersColumn'
|
||||
import RejectTxModal from './RejectTxModal'
|
||||
|
@ -23,6 +21,9 @@ import IncomingTxDescription from 'src/routes/safe/components/Transactions/TxsTa
|
|||
import { INCOMING_TX_TYPES } from 'src/routes/safe/store/models/incomingTransaction'
|
||||
|
||||
import { safeNonceSelector, safeThresholdSelector } from 'src/routes/safe/store/selectors'
|
||||
import { IncomingTx } from './IncomingTx'
|
||||
import { CreationTx } from './CreationTx'
|
||||
import { OutgoingTx } from './OutgoingTx'
|
||||
|
||||
const useStyles = makeStyles(styles as any)
|
||||
|
||||
|
@ -34,6 +35,8 @@ const ExpandedTx = ({ cancelTx, tx }) => {
|
|||
const openApproveModal = () => setOpenModal('approveTx')
|
||||
const closeModal = () => setOpenModal(null)
|
||||
const isIncomingTx = !!INCOMING_TX_TYPES[tx.type]
|
||||
const isCreationTx = tx.type === 'creation'
|
||||
|
||||
const thresholdReached = !isIncomingTx && threshold <= tx.confirmations.size
|
||||
const canExecute = !isIncomingTx && nonce === tx.nonce
|
||||
const cancelThresholdReached = !!cancelTx && threshold <= cancelTx.confirmations.size
|
||||
|
@ -52,63 +55,33 @@ const ExpandedTx = ({ cancelTx, tx }) => {
|
|||
<Block className={classes.expandedTxBlock}>
|
||||
<Row>
|
||||
<Col layout="column" xs={6}>
|
||||
<Block className={cn(classes.txDataContainer, isIncomingTx && classes.incomingTxBlock)}>
|
||||
<Block className={cn(classes.txDataContainer, (isIncomingTx || isCreationTx) && classes.incomingTxBlock)}>
|
||||
<Block align="left" className={classes.txData}>
|
||||
<Bold className={classes.txHash}>Hash:</Bold>
|
||||
{tx.executionTxHash ? <EtherScanLink cut={8} type="tx" value={tx.executionTxHash} /> : 'n/a'}
|
||||
</Block>
|
||||
{!isIncomingTx && (
|
||||
{!isIncomingTx && !isCreationTx && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Nonce: </Bold>
|
||||
<Span>{tx.nonce}</Span>
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph noMargin>
|
||||
<Bold>Fee: </Bold>
|
||||
{tx.fee ? tx.fee : 'n/a'}
|
||||
</Paragraph>
|
||||
{isIncomingTx ? (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
{formatDate(tx.executionDate)}
|
||||
</Paragraph>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Paragraph noMargin>
|
||||
<Bold>Created: </Bold>
|
||||
{formatDate(tx.submissionDate)}
|
||||
</Paragraph>
|
||||
{tx.executionDate && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Executed: </Bold>
|
||||
{formatDate(tx.executionDate)}
|
||||
</Paragraph>
|
||||
)}
|
||||
{tx.refundParams && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Refund: </Bold>
|
||||
max. {tx.refundParams.fee} {tx.refundParams.symbol}
|
||||
</Paragraph>
|
||||
)}
|
||||
{tx.operation === 1 && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Delegate Call</Bold>
|
||||
</Paragraph>
|
||||
)}
|
||||
{tx.operation === 2 && (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Contract Creation</Bold>
|
||||
</Paragraph>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isCreationTx ? (
|
||||
<Paragraph noMargin>
|
||||
<Bold>Fee: </Bold>
|
||||
{tx.fee ? tx.fee : 'n/a'}
|
||||
</Paragraph>
|
||||
) : null}
|
||||
<CreationTx tx={tx} />
|
||||
<IncomingTx tx={tx} />
|
||||
<OutgoingTx tx={tx} />
|
||||
</Block>
|
||||
<Hairline />
|
||||
{isIncomingTx ? <IncomingTxDescription tx={tx} /> : <TxDescription tx={tx} />}
|
||||
{isIncomingTx && <IncomingTxDescription tx={tx} />}
|
||||
{!isIncomingTx && !isCreationTx && <TxDescription tx={tx} />}
|
||||
{isCreationTx && <Block className={classes.emptyRowDataContainer} />}
|
||||
</Col>
|
||||
{!isIncomingTx && (
|
||||
{!isIncomingTx && !isCreationTx && (
|
||||
<OwnersColumn
|
||||
cancelThresholdReached={cancelThresholdReached}
|
||||
cancelTx={cancelTx}
|
||||
|
|
|
@ -33,4 +33,10 @@ export const styles = () => ({
|
|||
incomingTxBlock: {
|
||||
borderRight: '2px solid rgb(232, 231, 230)',
|
||||
},
|
||||
emptyRowDataContainer: {
|
||||
paddingTop: lg,
|
||||
paddingLeft: md,
|
||||
paddingBottom: md,
|
||||
borderRight: '2px solid rgb(232, 231, 230)',
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Collapse from '@material-ui/core/Collapse'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import TableCell from '@material-ui/core/TableCell'
|
||||
import TableContainer from '@material-ui/core/TableContainer'
|
||||
|
@ -21,16 +20,10 @@ import Block from 'src/components/layout/Block'
|
|||
import Row from 'src/components/layout/Row'
|
||||
import { extendedTransactionsSelector } from 'src/routes/safe/container/selector'
|
||||
import { safeCancellationTransactionsSelector } from 'src/routes/safe/store/selectors'
|
||||
import { Collapse } from '@material-ui/core'
|
||||
|
||||
export const TRANSACTION_ROW_TEST_ID = 'transaction-row'
|
||||
|
||||
const CollapseAux: any = Collapse
|
||||
|
||||
const expandCellStyle = {
|
||||
paddingLeft: 0,
|
||||
paddingRight: 15,
|
||||
}
|
||||
|
||||
const TxsTable = ({ classes }) => {
|
||||
const [expandedTx, setExpandedTx] = useState(null)
|
||||
const cancellationTransactions = useSelector(safeCancellationTransactionsSelector)
|
||||
|
@ -107,7 +100,7 @@ const TxsTable = ({ classes }) => {
|
|||
<Status status={row.status} />
|
||||
</Row>
|
||||
</TableCell>
|
||||
<TableCell style={expandCellStyle}>
|
||||
<TableCell className={classes.expandCellStyle}>
|
||||
{!row.tx.creationTx && (
|
||||
<IconButton disableRipple>
|
||||
{expandedTx === row.safeTxHash ? <ExpandLess /> : <ExpandMore />}
|
||||
|
@ -122,12 +115,30 @@ const TxsTable = ({ classes }) => {
|
|||
colSpan={6}
|
||||
style={{ paddingBottom: 0, paddingTop: 0 }}
|
||||
>
|
||||
<CollapseAux
|
||||
cancelTx={row[TX_TABLE_RAW_CANCEL_TX_ID]}
|
||||
component={ExpandedTxComponent}
|
||||
<Collapse
|
||||
component={() => (
|
||||
<ExpandedTxComponent cancelTx={row[TX_TABLE_RAW_CANCEL_TX_ID]} tx={row[TX_TABLE_RAW_TX_ID]} />
|
||||
)}
|
||||
in={expandedTx === row.tx.safeTxHash}
|
||||
timeout="auto"
|
||||
unmountOnExit
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{row.tx.creationTx && (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
className={classes.extendedTxContainer}
|
||||
colSpan={6}
|
||||
style={{ paddingBottom: 0, paddingTop: 0 }}
|
||||
>
|
||||
<Collapse
|
||||
component={() => (
|
||||
<ExpandedTxComponent cancelTx={row[TX_TABLE_RAW_CANCEL_TX_ID]} tx={row[TX_TABLE_RAW_TX_ID]} />
|
||||
)}
|
||||
in={expandedTx === row.tx.safeTxHash}
|
||||
timeout="auto"
|
||||
tx={row[TX_TABLE_RAW_TX_ID]}
|
||||
unmountOnExit
|
||||
/>
|
||||
</TableCell>
|
||||
|
|
|
@ -26,4 +26,8 @@ export const styles = () => ({
|
|||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
expandCellStyle: {
|
||||
paddingLeft: 0,
|
||||
paddingRight: 15,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -7,6 +7,7 @@ import fetchSafeTokens from 'src/logic/tokens/store/actions/fetchSafeTokens'
|
|||
import fetchLatestMasterContractVersion from 'src/routes/safe/store/actions/fetchLatestMasterContractVersion'
|
||||
import fetchSafe from 'src/routes/safe/store/actions/fetchSafe'
|
||||
import fetchTransactions from 'src/routes/safe/store/actions/fetchTransactions'
|
||||
import fetchSafeCreationTx from '../../store/actions/fetchSafeCreationTx'
|
||||
|
||||
export const useLoadSafe = (safeAddress) => {
|
||||
const dispatch = useDispatch()
|
||||
|
@ -19,6 +20,7 @@ export const useLoadSafe = (safeAddress) => {
|
|||
.then(() => {
|
||||
dispatch(fetchSafeTokens(safeAddress))
|
||||
dispatch(loadAddressBookFromStorage())
|
||||
dispatch(fetchSafeCreationTx(safeAddress))
|
||||
return dispatch(fetchTransactions(safeAddress))
|
||||
})
|
||||
.then(() => dispatch(addViewedSafe(safeAddress)))
|
||||
|
|
|
@ -5,9 +5,9 @@ import { useSelector } from 'react-redux'
|
|||
import Page from 'src/components/layout/Page'
|
||||
|
||||
import Layout from 'src/routes/safe/components/Layout'
|
||||
import { useCheckForUpdates } from 'src/routes/safe/container/hooks/useCheckForUpdates'
|
||||
import { useLoadSafe } from 'src/routes/safe/container/hooks/useLoadSafe'
|
||||
import { safeParamAddressFromStateSelector } from 'src/routes/safe/store/selectors'
|
||||
import { useLoadSafe } from './hooks/useLoadSafe'
|
||||
import { useCheckForUpdates } from './hooks/useCheckForUpdates'
|
||||
|
||||
const INITIAL_STATE = {
|
||||
sendFunds: {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// @flow
|
||||
import axios from 'axios'
|
||||
import { List } from 'immutable'
|
||||
import { buildSafeCreationTxUrl } from '../../../../config'
|
||||
import { addOrUpdateTransactions } from './transactions/addOrUpdateTransactions'
|
||||
import { makeTransaction } from '../models/transaction'
|
||||
|
||||
const getCreationTx = async (safeAddress) => {
|
||||
const url = buildSafeCreationTxUrl(safeAddress)
|
||||
const response = await axios.get(url)
|
||||
return {
|
||||
...response.data,
|
||||
creationTx: true,
|
||||
nonce: null,
|
||||
}
|
||||
}
|
||||
|
||||
const fetchSafeCreationTx = (safeAddress) => async (dispatch) => {
|
||||
if (!safeAddress) return
|
||||
const creationTxFetched = await getCreationTx(safeAddress)
|
||||
|
||||
const {
|
||||
created,
|
||||
creationTx,
|
||||
creator,
|
||||
factoryAddress,
|
||||
masterCopy,
|
||||
setupData,
|
||||
transactionHash,
|
||||
type,
|
||||
} = creationTxFetched
|
||||
const txType = type || 'creation'
|
||||
|
||||
const creationTxAsRecord = makeTransaction({
|
||||
created,
|
||||
creator,
|
||||
factoryAddress,
|
||||
masterCopy,
|
||||
setupData,
|
||||
creationTx,
|
||||
executionTxHash: transactionHash,
|
||||
type: txType,
|
||||
submissionDate: created,
|
||||
})
|
||||
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions: List([creationTxAsRecord]) }))
|
||||
}
|
||||
|
||||
export default fetchSafeCreationTx
|
|
@ -5,7 +5,6 @@ import { List, Map } from 'immutable'
|
|||
import { batch } from 'react-redux'
|
||||
|
||||
import { addIncomingTransactions } from './addIncomingTransactions'
|
||||
import { addTransactions } from './addTransactions'
|
||||
|
||||
import generateBatchRequests from 'src/logic/contracts/generateBatchRequests'
|
||||
import { decodeParamsFromSafeMethod } from 'src/logic/contracts/methodIds'
|
||||
|
@ -25,7 +24,8 @@ import { getWeb3 } from 'src/logic/wallets/getWeb3'
|
|||
import { addCancellationTransactions } from 'src/routes/safe/store/actions/addCancellationTransactions'
|
||||
import { makeConfirmation } from 'src/routes/safe/store/models/confirmation'
|
||||
import { makeIncomingTransaction } from 'src/routes/safe/store/models/incomingTransaction'
|
||||
import { makeTransaction } from 'src/routes/safe/store/models/transaction'
|
||||
import { addOrUpdateTransactions } from './transactions/addOrUpdateTransactions'
|
||||
import { makeTransaction } from '../models/transaction'
|
||||
|
||||
let web3
|
||||
|
||||
|
@ -144,33 +144,7 @@ export const buildTransactionFrom = async (
|
|||
})
|
||||
}
|
||||
|
||||
const addMockSafeCreationTx = (safeAddress) => [
|
||||
{
|
||||
blockNumber: null,
|
||||
baseGas: 0,
|
||||
confirmations: [],
|
||||
data: null,
|
||||
executionDate: null,
|
||||
gasPrice: 0,
|
||||
gasToken: '0x0000000000000000000000000000000000000000',
|
||||
isExecuted: true,
|
||||
nonce: null,
|
||||
operation: 0,
|
||||
refundReceiver: '0x0000000000000000000000000000000000000000',
|
||||
safe: safeAddress,
|
||||
safeTxGas: 0,
|
||||
safeTxHash: '',
|
||||
signatures: null,
|
||||
submissionDate: null,
|
||||
executor: '',
|
||||
to: '',
|
||||
transactionHash: null,
|
||||
value: 0,
|
||||
creationTx: true,
|
||||
},
|
||||
]
|
||||
|
||||
const batchRequestTxsData = (txs) => {
|
||||
const batchRequestTxsData = (txs: any[]) => {
|
||||
const web3Batch = new web3.BatchRequest()
|
||||
|
||||
const txsTokenInfo = txs.map((tx) => {
|
||||
|
@ -236,9 +210,9 @@ export const buildIncomingTransactionFrom = ([tx, symbol, decimals, fee]) => {
|
|||
}
|
||||
|
||||
let etagSafeTransactions = null
|
||||
let etagCachedSafeIncommingTransactions = null
|
||||
let etagCachedSafeIncomingTransactions = null
|
||||
export const loadSafeTransactions = async (safeAddress, getState) => {
|
||||
let transactions = addMockSafeCreationTx(safeAddress)
|
||||
let transactions = []
|
||||
|
||||
try {
|
||||
const config = etagSafeTransactions
|
||||
|
@ -288,7 +262,7 @@ export const loadSafeTransactions = async (safeAddress, getState) => {
|
|||
const groupedTxs = List(txsRecord).groupBy((tx) => (tx.get('cancellationTx') ? 'cancel' : 'outgoing'))
|
||||
|
||||
return {
|
||||
outgoing: Map().set(safeAddress, groupedTxs.get('outgoing')),
|
||||
outgoing: groupedTxs.get('outgoing'),
|
||||
cancel: Map().set(safeAddress, groupedTxs.get('cancel')),
|
||||
}
|
||||
}
|
||||
|
@ -296,10 +270,10 @@ export const loadSafeTransactions = async (safeAddress, getState) => {
|
|||
export const loadSafeIncomingTransactions = async (safeAddress) => {
|
||||
let incomingTransactions = []
|
||||
try {
|
||||
const config = etagCachedSafeIncommingTransactions
|
||||
const config = etagCachedSafeIncomingTransactions
|
||||
? {
|
||||
headers: {
|
||||
'If-None-Match': etagCachedSafeIncommingTransactions,
|
||||
'If-None-Match': etagCachedSafeIncomingTransactions,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
|
@ -307,11 +281,11 @@ export const loadSafeIncomingTransactions = async (safeAddress) => {
|
|||
const response = await axios.get(url, config)
|
||||
if (response.data.count > 0) {
|
||||
incomingTransactions = response.data.results
|
||||
if (etagCachedSafeIncommingTransactions === response.headers.etag) {
|
||||
if (etagCachedSafeIncomingTransactions === response.headers.etag) {
|
||||
// The txs are the same, we can return the cached ones
|
||||
return
|
||||
}
|
||||
etagCachedSafeIncommingTransactions = response.headers.etag
|
||||
etagCachedSafeIncomingTransactions = response.headers.etag
|
||||
}
|
||||
} catch (err) {
|
||||
if (err && err.response && err.response.status === 304) {
|
||||
|
@ -336,7 +310,7 @@ export default (safeAddress) => async (dispatch, getState) => {
|
|||
|
||||
batch(() => {
|
||||
dispatch(addCancellationTransactions(cancel))
|
||||
dispatch(addTransactions(outgoing))
|
||||
dispatch(addOrUpdateTransactions({ safeAddress, transactions: outgoing }))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// @flow
|
||||
import { createAction } from 'redux-actions'
|
||||
|
||||
export const ADD_OR_UPDATE_TRANSACTIONS = 'ADD_OR_UPDATE_TRANSACTIONS'
|
||||
|
||||
export const addOrUpdateTransactions = createAction(ADD_OR_UPDATE_TRANSACTIONS)
|
|
@ -12,13 +12,13 @@ import { getIncomingTxAmount } from 'src/routes/safe/components/Transactions/Txs
|
|||
import { grantedSelector } from 'src/routes/safe/container/selector'
|
||||
import { ADD_INCOMING_TRANSACTIONS } from 'src/routes/safe/store/actions/addIncomingTransactions'
|
||||
import { ADD_SAFE } from 'src/routes/safe/store/actions/addSafe'
|
||||
import { ADD_TRANSACTIONS } from 'src/routes/safe/store/actions/addTransactions'
|
||||
import updateSafe from 'src/routes/safe/store/actions/updateSafe'
|
||||
import { safeParamAddressFromStateSelector, safesMapSelector } from 'src/routes/safe/store/selectors'
|
||||
|
||||
import { loadFromStorage, saveToStorage } from 'src/utils/storage'
|
||||
import { ADD_OR_UPDATE_TRANSACTIONS } from '../actions/transactions/addOrUpdateTransactions'
|
||||
|
||||
const watchedActions = [ADD_TRANSACTIONS, ADD_INCOMING_TRANSACTIONS, ADD_SAFE]
|
||||
const watchedActions = [ADD_OR_UPDATE_TRANSACTIONS, ADD_INCOMING_TRANSACTIONS, ADD_SAFE]
|
||||
|
||||
const sendAwaitingTransactionNotification = async (
|
||||
dispatch,
|
||||
|
@ -66,16 +66,16 @@ const notificationsMiddleware = (store) => (next) => async (action) => {
|
|||
if (watchedActions.includes(action.type)) {
|
||||
const state = store.getState()
|
||||
switch (action.type) {
|
||||
case ADD_TRANSACTIONS: {
|
||||
const transactionsList = action.payload
|
||||
const userAddress = userAccountSelector(state)
|
||||
const safeAddress = action.payload.keySeq().get(0)
|
||||
case ADD_OR_UPDATE_TRANSACTIONS: {
|
||||
const { safeAddress, transactions } = action.payload
|
||||
const userAddress: string = userAccountSelector(state)
|
||||
const cancellationTransactions = state.cancellationTransactions.get(safeAddress)
|
||||
const cancellationTransactionsByNonce = cancellationTransactions
|
||||
? cancellationTransactions.reduce((acc, tx) => acc.set(tx.nonce, tx), Map())
|
||||
: Map()
|
||||
|
||||
const awaitingTransactions = getAwaitingTransactions(
|
||||
transactionsList,
|
||||
Map().set(safeAddress, transactions),
|
||||
cancellationTransactionsByNonce,
|
||||
userAddress,
|
||||
)
|
||||
|
|
|
@ -39,4 +39,10 @@ export const makeTransaction = Record({
|
|||
refundParams: null,
|
||||
type: 'outgoing',
|
||||
origin: null,
|
||||
created: false,
|
||||
creator: '',
|
||||
factoryAddress: '',
|
||||
masterCopy: '',
|
||||
setupData: '',
|
||||
transactionHash: '',
|
||||
})
|
||||
|
|
|
@ -1,13 +1,43 @@
|
|||
import { Map } from 'immutable'
|
||||
import { List, Map } from 'immutable'
|
||||
import { handleActions } from 'redux-actions'
|
||||
|
||||
import { ADD_TRANSACTIONS } from 'src/routes/safe/store/actions/addTransactions'
|
||||
import { ADD_OR_UPDATE_TRANSACTIONS } from '../actions/transactions/addOrUpdateTransactions'
|
||||
|
||||
export const TRANSACTIONS_REDUCER_ID = 'transactions'
|
||||
|
||||
export default handleActions(
|
||||
{
|
||||
[ADD_TRANSACTIONS]: (state, action) => action.payload,
|
||||
[ADD_OR_UPDATE_TRANSACTIONS]: (state, action) => {
|
||||
const { safeAddress, transactions } = action.payload
|
||||
|
||||
if (!safeAddress || !transactions) {
|
||||
return state
|
||||
}
|
||||
|
||||
const newState = state.withMutations((map) => {
|
||||
const stateTransactionsList = map.get(safeAddress)
|
||||
if (stateTransactionsList) {
|
||||
let newTxList
|
||||
transactions.forEach((updateTx) => {
|
||||
const txIndex = stateTransactionsList.findIndex((txIterator) => txIterator.nonce === updateTx.nonce)
|
||||
if (txIndex !== -1) {
|
||||
// Update
|
||||
newTxList = stateTransactionsList.update(txIndex, (oldTx) => oldTx.merge(updateTx))
|
||||
map.set(safeAddress, newTxList)
|
||||
} else {
|
||||
// Add new
|
||||
map.update(safeAddress, (oldTxList) => oldTxList.merge(List([updateTx])))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
map.set(safeAddress, transactions)
|
||||
}
|
||||
})
|
||||
|
||||
return newState
|
||||
},
|
||||
},
|
||||
Map(),
|
||||
)
|
||||
|
|
|
@ -108,6 +108,7 @@ const theme = createMuiTheme({
|
|||
MuiStepper: {
|
||||
root: {
|
||||
padding: `${lg} 0 0 15px`,
|
||||
background: 'transparent',
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
|
|
Loading…
Reference in New Issue