refactor createTRansaction method to accept safe settings txs

This commit is contained in:
mmv 2019-06-18 15:55:53 +04:00
parent 4b31e6130e
commit 8ec1d6f528
5 changed files with 108 additions and 104 deletions

View File

@ -12,7 +12,7 @@ const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
export const executeTransaction = async ( export const executeTransaction = async (
safeInstance: any, safeInstance: any,
to: string, to: string,
valueInWei: number, valueInWei: number | string,
data: string, data: string,
operation: number | string, operation: number | string,
nonce: string | number, nonce: string | number,

View File

@ -17,8 +17,12 @@ import { copyToClipboard } from '~/utils/clipboard'
import Hairline from '~/components/layout/Hairline' import Hairline from '~/components/layout/Hairline'
import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo' import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo'
import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils' import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils'
import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens'
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
import { getWeb3 } from '~/logic/wallets/getWeb3'
import ArrowDown from '../assets/arrow-down.svg' import ArrowDown from '../assets/arrow-down.svg'
import { secondary } from '~/theme/variables' import { secondary } from '~/theme/variables'
import { isEther } from '~/logic/tokens/utils/tokenHelpers'
import { styles } from './style' import { styles } from './style'
type Props = { type Props = {
@ -50,87 +54,109 @@ const ReviewTx = ({
createTransaction, createTransaction,
}: Props) => ( }: Props) => (
<SharedSnackbarConsumer> <SharedSnackbarConsumer>
{({ openSnackbar }) => ( {({ openSnackbar }) => {
<React.Fragment> const submitTx = async () => {
<Row align="center" grow className={classes.heading}> const web3 = getWeb3()
<Paragraph weight="bolder" className={classes.headingText} noMargin> const isSendingETH = isEther(tx.token.symbol)
Send Funds const txRecipient = isSendingETH ? tx.recipientAddress : tx.token.address
</Paragraph> let txData = EMPTY_DATA
<Paragraph className={classes.annotation}>2 of 2</Paragraph> let txAmount = web3.utils.toWei(tx.amount, 'ether')
<IconButton onClick={onClose} disableRipple>
<Close className={classes.closeIcon} />
</IconButton> if (!isSendingETH) {
</Row> const StandardToken = await getStandardTokenContract()
<Hairline /> const tokenInstance = await StandardToken.at(tx.token.address)
<Block className={classes.container}>
<SafeInfo txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, txAmount).encodeABI()
safeAddress={safeAddress} // txAmount should be 0 if we send tokens
etherScanLink={etherScanLink} // the real value is encoded in txData and will be used by the contract
safeName={safeName} // if txAmount > 0 it would send ETH from the safe
ethBalance={ethBalance} txAmount = 0
/> }
<Row margin="md">
<Col xs={1}> createTransaction(safeAddress, txRecipient, txAmount, txData, openSnackbar)
<img src={ArrowDown} alt="Arrow Down" style={{ marginLeft: '8px' }} /> onClose()
</Col> }
<Col xs={11} center="xs" layout="column">
<Hairline /> return (
</Col> <React.Fragment>
</Row> <Row align="center" grow className={classes.heading}>
<Row margin="xs"> <Paragraph weight="bolder" className={classes.headingText} noMargin>
<Paragraph size="md" color="disabled" style={{ letterSpacing: '-0.5px' }} noMargin> Send Funds
Recipient
</Paragraph> </Paragraph>
<Paragraph className={classes.annotation}>2 of 2</Paragraph>
<IconButton onClick={onClose} disableRipple>
<Close className={classes.closeIcon} />
</IconButton>
</Row> </Row>
<Row margin="md" align="center"> <Hairline />
<Col xs={1}> <Block className={classes.container}>
<Identicon address={tx.recipientAddress} diameter={32} /> <SafeInfo
</Col> safeAddress={safeAddress}
<Col xs={11} layout="column"> etherScanLink={etherScanLink}
<Paragraph weight="bolder" onClick={copyToClipboard} noMargin> safeName={safeName}
{tx.recipientAddress} ethBalance={ethBalance}
<Link to={etherScanLink} target="_blank"> />
<OpenInNew style={openIconStyle} /> <Row margin="md">
</Link> <Col xs={1}>
<img src={ArrowDown} alt="Arrow Down" style={{ marginLeft: '8px' }} />
</Col>
<Col xs={11} center="xs" layout="column">
<Hairline />
</Col>
</Row>
<Row margin="xs">
<Paragraph size="md" color="disabled" style={{ letterSpacing: '-0.5px' }} noMargin>
Recipient
</Paragraph> </Paragraph>
</Col> </Row>
<Row margin="md" align="center">
<Col xs={1}>
<Identicon address={tx.recipientAddress} diameter={32} />
</Col>
<Col xs={11} layout="column">
<Paragraph weight="bolder" onClick={copyToClipboard} noMargin>
{tx.recipientAddress}
<Link to={etherScanLink} target="_blank">
<OpenInNew style={openIconStyle} />
</Link>
</Paragraph>
</Col>
</Row>
<Row margin="xs">
<Paragraph size="md" color="disabled" style={{ letterSpacing: '-0.5px' }} noMargin>
Amount
</Paragraph>
</Row>
<Row margin="md" align="center">
<Img src={tx.token.logoUri} height={28} alt={tx.token.name} onError={setImageToPlaceholder} />
<Paragraph size="md" noMargin className={classes.amount}>
{tx.amount}
{' '}
{tx.token.symbol}
</Paragraph>
</Row>
</Block>
<Hairline style={{ position: 'absolute', bottom: 85 }} />
<Row align="center" className={classes.buttonRow}>
<Button className={classes.button} minWidth={140} onClick={onClickBack}>
Back
</Button>
<Button
type="submit"
className={classes.button}
onClick={submitTx}
variant="contained"
minWidth={140}
color="primary"
data-testid="submit-tx-btn"
>
SUBMIT
</Button>
</Row> </Row>
<Row margin="xs"> </React.Fragment>
<Paragraph size="md" color="disabled" style={{ letterSpacing: '-0.5px' }} noMargin> )
Amount }}
</Paragraph>
</Row>
<Row margin="md" align="center">
<Img src={tx.token.logoUri} height={28} alt={tx.token.name} onError={setImageToPlaceholder} />
<Paragraph size="md" noMargin className={classes.amount}>
{tx.amount}
{' '}
{tx.token.symbol}
</Paragraph>
</Row>
</Block>
<Hairline style={{ position: 'absolute', bottom: 85 }} />
<Row align="center" className={classes.buttonRow}>
<Button className={classes.button} minWidth={140} onClick={onClickBack}>
Back
</Button>
<Button
type="submit"
className={classes.button}
onClick={() => {
createTransaction(safeAddress, tx.recipientAddress, tx.amount, tx.token, openSnackbar)
onClose()
}}
variant="contained"
minWidth={140}
color="primary"
data-testid="submit-tx-btn"
>
SUBMIT
</Button>
</Row>
</React.Fragment>
)}
</SharedSnackbarConsumer> </SharedSnackbarConsumer>
) )

View File

@ -18,7 +18,6 @@ import {
sm, xs, secondary, smallFontSize, sm, xs, secondary, smallFontSize,
} from '~/theme/variables' } from '~/theme/variables'
import { copyToClipboard } from '~/utils/clipboard' import { copyToClipboard } from '~/utils/clipboard'
import type { Safe } from '~/routes/safe/store/models/safe'
import Balances from './Balances' import Balances from './Balances'
import Settings from './Settings' import Settings from './Settings'

View File

@ -11,7 +11,7 @@ export type Props = Actions &
granted: boolean, granted: boolean,
} }
const TIMEOUT = process.env.NODE_ENV === 'test' ? 1500 : 15000 const TIMEOUT = process.env.NODE_ENV === 'test' ? 1500 : 5000
class SafeView extends React.Component<Props> { class SafeView extends React.Component<Props> {
componentDidMount() { componentDidMount() {

View File

@ -1,15 +1,11 @@
// @flow // @flow
import type { Dispatch as ReduxDispatch, GetState } from 'redux' import type { Dispatch as ReduxDispatch, GetState } from 'redux'
import { createAction } from 'redux-actions' import { createAction } from 'redux-actions'
import { getWeb3 } from '~/logic/wallets/getWeb3'
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
import { type Token } from '~/logic/tokens/store/model/token'
import { userAccountSelector } from '~/logic/wallets/store/selectors' import { userAccountSelector } from '~/logic/wallets/store/selectors'
import { type GlobalState } from '~/store' import { type GlobalState } from '~/store'
import { isEther } from '~/logic/tokens/utils/tokenHelpers'
import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts' import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts'
import { executeTransaction, CALL } from '~/logic/safe/transactions' import { executeTransaction, CALL } from '~/logic/safe/transactions'
import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens'
export const ADD_TRANSACTIONS = 'ADD_TRANSACTIONS' export const ADD_TRANSACTIONS = 'ADD_TRANSACTIONS'
export const addTransactions = createAction<string, *>(ADD_TRANSACTIONS) export const addTransactions = createAction<string, *>(ADD_TRANSACTIONS)
@ -17,40 +13,23 @@ export const addTransactions = createAction<string, *>(ADD_TRANSACTIONS)
const createTransaction = ( const createTransaction = (
safeAddress: string, safeAddress: string,
to: string, to: string,
valueInEth: string, valueInWei: string,
token: Token,
openSnackbar: Function,
txData: string = EMPTY_DATA, txData: string = EMPTY_DATA,
openSnackbar: Function,
) => async (dispatch: ReduxDispatch<GlobalState>, getState: GetState<GlobalState>) => { ) => async (dispatch: ReduxDispatch<GlobalState>, getState: GetState<GlobalState>) => {
const isSendingETH = isEther(token.symbol)
const state: GlobalState = getState() const state: GlobalState = getState()
const safeInstance = await getGnosisSafeInstanceAt(safeAddress) const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
const web3 = getWeb3()
const from = userAccountSelector(state) const from = userAccountSelector(state)
const threshold = await safeInstance.getThreshold() const threshold = await safeInstance.getThreshold()
const nonce = await safeInstance.nonce() const nonce = await safeInstance.nonce()
const txRecipient = isSendingETH ? to : token.address
const valueInWei = web3.utils.toWei(valueInEth, 'ether')
let txAmount = valueInWei
const isExecution = threshold.toNumber() === 1 const isExecution = threshold.toNumber() === 1
let txData = EMPTY_DATA
if (!isSendingETH) {
const StandardToken = await getStandardTokenContract()
const sendToken = await StandardToken.at(token.address)
txData = sendToken.contract.methods.transfer(to, valueInWei).encodeABI()
// txAmount should be 0 if we send tokens
// the real value is encoded in txData and will be used by the contract
// if txAmount > 0 it would send ETH from the safe
txAmount = 0
}
let txHash let txHash
if (isExecution) { if (isExecution) {
openSnackbar('Transaction has been submitted', 'success') openSnackbar('Transaction has been submitted', 'success')
txHash = await executeTransaction(safeInstance, txRecipient, txAmount, txData, CALL, nonce, from) txHash = await executeTransaction(safeInstance, to, valueInWei, txData, CALL, nonce, from)
openSnackbar('Transaction has been confirmed', 'success') openSnackbar('Transaction has been confirmed', 'success')
} else { } else {
// txHash = await approveTransaction(safeAddress, to, valueInWei, txData, CALL, nonce) // txHash = await approveTransaction(safeAddress, to, valueInWei, txData, CALL, nonce)