refactor: allow adding mocked transactions
This commit is contained in:
parent
376af39f3c
commit
6c1bc100b6
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue