mirror of
https://github.com/status-im/safe-react.git
synced 2025-02-17 12:07:09 +00:00
allow confirming and executing tx at the same time
This commit is contained in:
parent
c40ea98b5b
commit
f06286df2f
@ -62,7 +62,6 @@ const ReviewTx = ({
|
|||||||
let txData = EMPTY_DATA
|
let txData = EMPTY_DATA
|
||||||
let txAmount = web3.utils.toWei(tx.amount, 'ether')
|
let txAmount = web3.utils.toWei(tx.amount, 'ether')
|
||||||
|
|
||||||
|
|
||||||
if (!isSendingETH) {
|
if (!isSendingETH) {
|
||||||
const StandardToken = await getStandardTokenContract()
|
const StandardToken = await getStandardTokenContract()
|
||||||
const tokenInstance = await StandardToken.at(tx.token.address)
|
const tokenInstance = await StandardToken.at(tx.token.address)
|
||||||
@ -139,7 +138,7 @@ const ReviewTx = ({
|
|||||||
</Block>
|
</Block>
|
||||||
<Hairline style={{ position: 'absolute', bottom: 85 }} />
|
<Hairline style={{ position: 'absolute', bottom: 85 }} />
|
||||||
<Row align="center" className={classes.buttonRow}>
|
<Row align="center" className={classes.buttonRow}>
|
||||||
<Button className={classes.button} minWidth={140} onClick={onClickBack}>
|
<Button className={classes.button} minWidth={140} minHeight={42} onClick={onClickBack}>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -148,6 +147,7 @@ const ReviewTx = ({
|
|||||||
onClick={submitTx}
|
onClick={submitTx}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
minWidth={140}
|
minWidth={140}
|
||||||
|
minHeight={42}
|
||||||
color="primary"
|
color="primary"
|
||||||
data-testid="submit-tx-btn"
|
data-testid="submit-tx-btn"
|
||||||
>
|
>
|
||||||
|
@ -158,12 +158,13 @@ const SendFunds = ({
|
|||||||
</Row>
|
</Row>
|
||||||
<Hairline />
|
<Hairline />
|
||||||
<Row align="center" className={classes.buttonRow}>
|
<Row align="center" className={classes.buttonRow}>
|
||||||
<Button className={classes.button} minWidth={140} onClick={onClose}>
|
<Button className={classes.button} minWidth={140} minHeight={42} onClick={onClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
minHeight={42}
|
||||||
minWidth={140}
|
minWidth={140}
|
||||||
color="primary"
|
color="primary"
|
||||||
data-testid="review-tx-btn"
|
data-testid="review-tx-btn"
|
||||||
|
@ -26,6 +26,7 @@ type Props = {
|
|||||||
safeAddress: string,
|
safeAddress: string,
|
||||||
threshold: number,
|
threshold: number,
|
||||||
thresholdReached: boolean,
|
thresholdReached: boolean,
|
||||||
|
userAddress: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const getModalTitleAndDescription = (thresholdReached: boolean) => {
|
const getModalTitleAndDescription = (thresholdReached: boolean) => {
|
||||||
@ -51,18 +52,19 @@ const ApproveTxModal = ({
|
|||||||
safeAddress,
|
safeAddress,
|
||||||
threshold,
|
threshold,
|
||||||
thresholdReached,
|
thresholdReached,
|
||||||
|
userAddress,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [shouldExecuteTx, setShouldExecuteTx] = useState<boolean>(false)
|
const [approveAndExecute, setApproveAndExecute] = useState<boolean>(false)
|
||||||
const { title, description } = getModalTitleAndDescription(thresholdReached)
|
const { title, description } = getModalTitleAndDescription(thresholdReached)
|
||||||
const oneConfirmationLeft = tx.confirmations.size + 1 === threshold
|
const oneConfirmationLeft = tx.confirmations.size + 1 === threshold
|
||||||
|
|
||||||
const handleExecuteCheckbox = () => setShouldExecuteTx(prevShouldExecute => !prevShouldExecute)
|
const handleExecuteCheckbox = () => setApproveAndExecute(prevApproveAndExecute => !prevApproveAndExecute)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SharedSnackbarConsumer>
|
<SharedSnackbarConsumer>
|
||||||
{({ openSnackbar }) => {
|
{({ openSnackbar }) => {
|
||||||
const approveTx = () => {
|
const approveTx = () => {
|
||||||
processTransaction(safeAddress, tx, openSnackbar, thresholdReached || shouldExecuteTx)
|
processTransaction(safeAddress, tx, openSnackbar, userAddress, approveAndExecute)
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +94,7 @@ const ApproveTxModal = ({
|
|||||||
transaction right away, click on checkbox below.
|
transaction right away, click on checkbox below.
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
control={<Checkbox onChange={handleExecuteCheckbox} checked={shouldExecuteTx} color="primary" />}
|
control={<Checkbox onChange={handleExecuteCheckbox} checked={approveAndExecute} color="primary" />}
|
||||||
label="Execute transaction"
|
label="Execute transaction"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { List } from 'immutable'
|
import { List } from 'immutable'
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
import { withStyles } from '@material-ui/core/styles'
|
||||||
import Tabs from '@material-ui/core/Tabs'
|
|
||||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||||
import Tab from '@material-ui/core/Tab'
|
|
||||||
import Row from '~/components/layout/Row'
|
import Row from '~/components/layout/Row'
|
||||||
import Block from '~/components/layout/Block'
|
import Block from '~/components/layout/Block'
|
||||||
import Col from '~/components/layout/Col'
|
import Col from '~/components/layout/Col'
|
||||||
@ -16,7 +14,6 @@ import { type Transaction } from '~/routes/safe/store/models/transaction'
|
|||||||
import { type Owner } from '~/routes/safe/store/models/owner'
|
import { type Owner } from '~/routes/safe/store/models/owner'
|
||||||
import { getEtherScanLink, openTxInEtherScan, getWeb3 } from '~/logic/wallets/getWeb3'
|
import { getEtherScanLink, openTxInEtherScan, getWeb3 } from '~/logic/wallets/getWeb3'
|
||||||
import { shortVersionOf } from '~/logic/wallets/ethAddresses'
|
import { shortVersionOf } from '~/logic/wallets/ethAddresses'
|
||||||
import { TX_TYPE_CONFIRMATION } from '~/logic/safe/transactions'
|
|
||||||
import { secondary } from '~/theme/variables'
|
import { secondary } from '~/theme/variables'
|
||||||
import OwnersColumn from './OwnersColumn'
|
import OwnersColumn from './OwnersColumn'
|
||||||
import CancelTxModal from './CancelTxModal'
|
import CancelTxModal from './CancelTxModal'
|
||||||
@ -150,6 +147,7 @@ to:
|
|||||||
processTransaction={processTransaction}
|
processTransaction={processTransaction}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
tx={tx}
|
tx={tx}
|
||||||
|
userAddress={userAddress}
|
||||||
safeAddress={safeAddress}
|
safeAddress={safeAddress}
|
||||||
threshold={threshold}
|
threshold={threshold}
|
||||||
thresholdReached={thresholdReached}
|
thresholdReached={thresholdReached}
|
||||||
|
@ -9,13 +9,17 @@ import { approveTransaction, executeTransaction, CALL } from '~/logic/safe/trans
|
|||||||
|
|
||||||
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
|
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
|
||||||
// https://github.com/gnosis/safe-contracts/blob/master/test/gnosisSafeTeamEdition.js#L26
|
// https://github.com/gnosis/safe-contracts/blob/master/test/gnosisSafeTeamEdition.js#L26
|
||||||
const generateSignaturesFromTxConfirmations = (tx: Transaction) => {
|
const generateSignaturesFromTxConfirmations = (tx: Transaction, preApprovingOwner?: string) => {
|
||||||
// The constant parts need to be sorted so that the recovered signers are sorted ascending
|
// The constant parts need to be sorted so that the recovered signers are sorted ascending
|
||||||
// (natural order) by address (not checksummed).
|
// (natural order) by address (not checksummed).
|
||||||
const confirmedAdresses = tx.confirmations.map(conf => conf.owner.address).sort()
|
let confirmedAdresses = tx.confirmations.map(conf => conf.owner.address)
|
||||||
|
|
||||||
|
if (preApprovingOwner) {
|
||||||
|
confirmedAdresses = confirmedAdresses.push(preApprovingOwner)
|
||||||
|
}
|
||||||
|
|
||||||
let sigs = '0x'
|
let sigs = '0x'
|
||||||
confirmedAdresses.forEach((addr) => {
|
confirmedAdresses.sort().forEach((addr) => {
|
||||||
sigs += `000000000000000000000000${addr.replace(
|
sigs += `000000000000000000000000${addr.replace(
|
||||||
'0x',
|
'0x',
|
||||||
'',
|
'',
|
||||||
@ -28,14 +32,19 @@ const processTransaction = (
|
|||||||
safeAddress: string,
|
safeAddress: string,
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
openSnackbar: Function,
|
openSnackbar: Function,
|
||||||
shouldExecute?: boolean,
|
userAddress: string,
|
||||||
|
approveAndExecute?: boolean,
|
||||||
) => async (dispatch: ReduxDispatch<GlobalState>, getState: GetState<GlobalState>) => {
|
) => async (dispatch: ReduxDispatch<GlobalState>, getState: GetState<GlobalState>) => {
|
||||||
const state: GlobalState = getState()
|
const state: GlobalState = getState()
|
||||||
|
|
||||||
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
|
||||||
const from = userAccountSelector(state)
|
const from = userAccountSelector(state)
|
||||||
const nonce = (await safeInstance.nonce()).toString()
|
const nonce = (await safeInstance.nonce()).toString()
|
||||||
const sigs = generateSignaturesFromTxConfirmations(tx)
|
const threshold = (await safeInstance.getThreshold()).toNumber()
|
||||||
|
console.log(threshold, tx.confirmations.size)
|
||||||
|
const shouldExecute = threshold === tx.confirmations.size || approveAndExecute
|
||||||
|
const sigs = generateSignaturesFromTxConfirmations(tx, approveAndExecute && userAddress)
|
||||||
|
|
||||||
|
|
||||||
let txHash
|
let txHash
|
||||||
if (shouldExecute) {
|
if (shouldExecute) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user