refactor: allow adding mocked transactions

This commit is contained in:
fernandomg 2020-05-22 16:50:17 -03:00
parent 376af39f3c
commit 6c1bc100b6
2 changed files with 132 additions and 40 deletions

View File

@ -1,4 +1,5 @@
import { push } from 'connected-react-router' import { push } from 'connected-react-router'
import { List, Map, fromJS } from 'immutable'
import semverSatisfies from 'semver/functions/satisfies' import semverSatisfies from 'semver/functions/satisfies'
import { onboardUser } from 'src/components/ConnectButton' import { onboardUser } from 'src/components/ConnectButton'
@ -12,10 +13,41 @@ import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
import { providerSelector } from 'src/logic/wallets/store/selectors' import { providerSelector } from 'src/logic/wallets/store/selectors'
import { SAFELIST_ADDRESS } from 'src/routes/routes' import { SAFELIST_ADDRESS } from 'src/routes/routes'
import fetchTransactions from 'src/routes/safe/store/actions/fetchTransactions' import { addOrUpdateCancellationTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateCancellationTransactions'
import { addOrUpdateTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateTransactions'
import { removeCancellationTransaction } from 'src/routes/safe/store/actions/transactions/removeCancellationTransaction'
import { removeTransaction } from 'src/routes/safe/store/actions/transactions/removeTransaction'
import { mockTransaction } from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers'
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils' import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils'
import { getErrorMessage } from 'src/test/utils/ethereumErrors' import { getErrorMessage } from 'src/test/utils/ethereumErrors'
import { makeConfirmation } from '../models/confirmation'
import fetchTransactions from './transactions/fetchTransactions'
export const removeTxFromStore = (tx, safeAddress, dispatch) => {
if (tx.isCancellationTx) {
dispatch(removeCancellationTransaction({ safeAddress, transaction: tx }))
} else {
dispatch(removeTransaction({ safeAddress, transaction: tx }))
}
}
export const storeTx = async (tx, safeAddress, dispatch) => {
if (tx.isCancellationTx) {
dispatch(
addOrUpdateCancellationTransactions({
safeAddress,
transactions: Map({ [`${tx.nonce}`]: tx }),
}),
)
} else {
dispatch(
addOrUpdateTransactions({
safeAddress,
transactions: List([tx]),
}),
)
}
}
const createTransaction = ({ const createTransaction = ({
safeAddress, safeAddress,
@ -42,7 +74,7 @@ const createTransaction = ({
const { account: from, hardwareWallet, smartContractWallet } = providerSelector(state) const { account: from, hardwareWallet, smartContractWallet } = providerSelector(state)
const safeInstance = await getGnosisSafeInstanceAt(safeAddress) const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
const lastTx = await getLastTx(safeAddress) const lastTx = await getLastTx(safeAddress)
const nonce = await getNewTxNonce(txNonce, lastTx, safeInstance) const nonce = Number(await getNewTxNonce(txNonce, lastTx, safeInstance))
const isExecution = await shouldExecuteTransaction(safeInstance, nonce, lastTx) const isExecution = await shouldExecuteTransaction(safeInstance, nonce, lastTx)
const safeVersion = await getCurrentSafeVersion(safeInstance) const safeVersion = await getCurrentSafeVersion(safeInstance)
const safeTxGas = await estimateSafeTxGas(safeInstance, safeAddress, txData, to, valueInWei, operation) const safeTxGas = await estimateSafeTxGas(safeInstance, safeAddress, txData, to, valueInWei, operation)
@ -78,9 +110,6 @@ const createTransaction = ({
try { try {
// Here we're checking that safe contract version is greater or equal 1.1.1, but // Here we're checking that safe contract version is greater or equal 1.1.1, but
// theoretically EIP712 should also work for 1.0.0 contracts // theoretically EIP712 should also work for 1.0.0 contracts
// Also, offchain signatures are not working for ledger/trezor wallet because of a bug in their library:
// https://github.com/LedgerHQ/ledgerjs/issues/378
// Couldn't find an issue for trezor but the error is almost the same
const canTryOffchainSigning = const canTryOffchainSigning =
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) !isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
if (canTryOffchainSigning) { if (canTryOffchainSigning) {
@ -89,11 +118,7 @@ const createTransaction = ({
if (signature) { if (signature) {
closeSnackbar(beforeExecutionKey) closeSnackbar(beforeExecutionKey)
await saveTxToHistory({ await saveTxToHistory({ ...txArgs, signature, origin })
...txArgs,
signature,
origin,
} as any)
showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar) showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
@ -110,30 +135,39 @@ const createTransaction = ({
sendParams.gas = '7000000' sendParams.gas = '7000000'
} }
const txToMock = {
...txArgs,
confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper
value: txArgs.valueInWei,
}
const mockedTx = await mockTransaction(txToMock, safeAddress, state)
await tx await tx
.send(sendParams) .send(sendParams)
.once('transactionHash', async (hash) => { .once('transactionHash', async (hash) => {
try {
txHash = hash txHash = hash
closeSnackbar(beforeExecutionKey) closeSnackbar(beforeExecutionKey)
pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar) pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
try { await saveTxToHistory({ ...txArgs, txHash, origin })
await saveTxToHistory({ await storeTx(mockedTx, safeAddress, dispatch)
...txArgs,
txHash,
origin,
} as any)
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
} catch (err) { } catch (e) {
console.error(err) removeTxFromStore(mockedTx, safeAddress, dispatch)
} }
}) })
.on('error', (error) => { .on('error', (error) => {
closeSnackbar(pendingExecutionKey)
removeTxFromStore(mockedTx, safeAddress, dispatch)
console.error('Tx error: ', error) console.error('Tx error: ', error)
}) })
.then((receipt) => { .then(async (receipt) => {
if (pendingExecutionKey) {
closeSnackbar(pendingExecutionKey) closeSnackbar(pendingExecutionKey)
}
showSnackbar( showSnackbar(
isExecution isExecution
? notificationsQueue.afterExecution.noMoreConfirmationsNeeded ? notificationsQueue.afterExecution.noMoreConfirmationsNeeded
@ -142,6 +176,24 @@ const createTransaction = ({
closeSnackbar, closeSnackbar,
) )
const toStoreTx = isExecution
? mockedTx.withMutations((record) => {
record
.set('executionTxHash', receipt.events.ExecutionSuccess.transactionHash)
.set('safeTxHash', receipt.events.ExecutionSuccess.returnValues.txHash)
.set('executor', from)
.set('isExecuted', true)
.set('isSuccessful', true)
.set('status', 'success')
})
: mockedTx.set('status', 'awaiting_confirmations')
await storeTx(
toStoreTx.set('confirmations', fromJS([makeConfirmation({ owner: from })])),
safeAddress,
dispatch,
)
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
return receipt.transactionHash return receipt.transactionHash
@ -149,7 +201,11 @@ const createTransaction = ({
} catch (err) { } catch (err) {
console.error(err) console.error(err)
closeSnackbar(beforeExecutionKey) closeSnackbar(beforeExecutionKey)
if (pendingExecutionKey) {
closeSnackbar(pendingExecutionKey) closeSnackbar(pendingExecutionKey)
}
showSnackbar(notificationsQueue.afterExecutionError, enqueueSnackbar, closeSnackbar) showSnackbar(notificationsQueue.afterExecutionError, enqueueSnackbar, closeSnackbar)
const executeDataUsedSignatures = safeInstance.contract.methods const executeDataUsedSignatures = safeInstance.contract.methods

View File

@ -1,3 +1,4 @@
import { fromJS } from 'immutable'
import semverSatisfies from 'semver/functions/satisfies' import semverSatisfies from 'semver/functions/satisfies'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
@ -8,10 +9,16 @@ import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from 'src/lo
import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion' import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion'
import { providerSelector } from 'src/logic/wallets/store/selectors' import { providerSelector } from 'src/logic/wallets/store/selectors'
import fetchSafe from 'src/routes/safe/store/actions/fetchSafe' import fetchSafe from 'src/routes/safe/store/actions/fetchSafe'
import fetchTransactions from 'src/routes/safe/store/actions/fetchTransactions' import fetchTransactions from 'src/routes/safe/store/actions/transactions/fetchTransactions'
import {
isCancelTransaction,
mockTransaction,
} from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers'
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils' import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils'
import { getErrorMessage } from 'src/test/utils/ethereumErrors' import { getErrorMessage } from 'src/test/utils/ethereumErrors'
import { makeConfirmation } from '../models/confirmation'
import { storeTx } from './createTransaction'
const processTransaction = ({ const processTransaction = ({
approveAndExecute, approveAndExecute,
@ -47,6 +54,7 @@ const processTransaction = ({
let txHash let txHash
let transaction let transaction
const txArgs = { const txArgs = {
...tx.toJS(), // merge the previous tx with new data
safeInstance, safeInstance,
to: tx.recipient, to: tx.recipient,
valueInWei: tx.value, valueInWei: tx.value,
@ -76,11 +84,7 @@ const processTransaction = ({
if (signature) { if (signature) {
closeSnackbar(beforeExecutionKey) closeSnackbar(beforeExecutionKey)
await saveTxToHistory({ await saveTxToHistory({ ...txArgs, signature })
...txArgs,
signature,
origin,
} as any)
showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar) showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
@ -97,6 +101,13 @@ const processTransaction = ({
sendParams.gas = '7000000' sendParams.gas = '7000000'
} }
const txToMock = {
...txArgs,
confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper
value: txArgs.valueInWei,
}
const mockedTx = await mockTransaction(txToMock, safeAddress, state)
await transaction await transaction
.send(sendParams) .send(sendParams)
.once('transactionHash', async (hash) => { .once('transactionHash', async (hash) => {
@ -106,20 +117,24 @@ const processTransaction = ({
pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar) pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
try { try {
await saveTxToHistory({ await saveTxToHistory({ ...txArgs, txHash })
...txArgs, await storeTx(mockedTx, safeAddress, dispatch)
txHash,
})
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
} catch (err) { } catch (e) {
console.error(err) closeSnackbar(pendingExecutionKey)
await storeTx(tx, safeAddress, dispatch)
console.error(e)
} }
}) })
.on('error', (error) => { .on('error', (error) => {
closeSnackbar(pendingExecutionKey)
storeTx(mockedTx.set('isSuccessful', false), safeAddress, dispatch)
console.error('Processing transaction error: ', error) console.error('Processing transaction error: ', error)
}) })
.then((receipt) => { .then(async (receipt) => {
if (pendingExecutionKey) {
closeSnackbar(pendingExecutionKey) closeSnackbar(pendingExecutionKey)
}
showSnackbar( showSnackbar(
isExecution isExecution
@ -128,6 +143,27 @@ const processTransaction = ({
enqueueSnackbar, enqueueSnackbar,
closeSnackbar, closeSnackbar,
) )
const toStoreTx = isExecution
? mockedTx.withMutations((record) => {
record
.set('executionTxHash', receipt.events.ExecutionSuccess.transactionHash)
.set('safeTxHash', receipt.events.ExecutionSuccess.returnValues.txHash)
.set('blockNumber', receipt.blockNumber)
.set('executionDate', record.submissionDate)
.set('executor', from)
.set('isExecuted', true)
.set('isSuccessful', true)
.set('status', isCancelTransaction(record, safeAddress) ? 'cancelled' : 'success')
})
: mockedTx.set('status', 'awaiting_confirmations')
await storeTx(
toStoreTx.set('confirmations', fromJS([...tx.confirmations, makeConfirmation({ owner: from })])),
safeAddress,
dispatch,
)
dispatch(fetchTransactions(safeAddress)) dispatch(fetchTransactions(safeAddress))
if (isExecution) { if (isExecution) {