refactor createTRansaction method to accept safe settings txs
This commit is contained in:
parent
4b31e6130e
commit
8ec1d6f528
|
@ -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,
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue