feature: WIP - add mocked tx to the list

This commit is contained in:
fernandomg 2020-05-12 00:40:49 -03:00
parent be4caa1baf
commit 141e05c2e8
7 changed files with 149 additions and 28 deletions

View File

@ -0,0 +1,6 @@
// @flow
import { createAction } from 'redux-actions'
export const ADD_CANCELLATION_TRANSACTION = 'ADD_CANCELLATION_TRANSACTION'
export const addCancellationTransaction = createAction<string, *>(ADD_CANCELLATION_TRANSACTION)

View File

@ -0,0 +1,6 @@
// @flow
import { createAction } from 'redux-actions'
export const ADD_TRANSACTION = 'ADD_TRANSACTION'
export const addTransaction = createAction<string, *>(ADD_TRANSACTION)

View File

@ -1,8 +1,9 @@
// @flow
import { push } from 'connected-react-router'
import type { RecordInstance } from 'immutable'
import { List } from 'immutable'
import type { GetState, Dispatch as ReduxDispatch } from 'redux'
import semverSatisfies from 'semver/functions/satisfies'
// import semverSatisfies from 'semver/functions/satisfies'
import { makeConfirmation } from '../../models/confirmation'
@ -20,13 +21,22 @@ import {
saveTxToHistory,
} from '~/logic/safe/transactions'
import { estimateSafeTxGas } from '~/logic/safe/transactions/gasNew'
import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from '~/logic/safe/transactions/offchainSigner'
import { getCurrentSafeVersion } from '~/logic/safe/utils/safeVersion'
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
// import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from '~/logic/safe/transactions/offchainSigner'
// import { getCurrentSafeVersion } from '~/logic/safe/utils/safeVersion'
import { TOKEN_REDUCER_ID } from '~/logic/tokens/store/reducer/tokens'
import { ZERO_ADDRESS, sameAddress } from '~/logic/wallets/ethAddresses'
import { EMPTY_DATA } from '~/logic/wallets/ethTransactions'
import { providerSelector } from '~/logic/wallets/store/selectors'
import { SAFELIST_ADDRESS } from '~/routes/routes'
import { addCancellationTransaction } from '~/routes/safe/store/actions/transactions/addCancellationTransaction'
import { addTransaction } from '~/routes/safe/store/actions/transactions/addTransaction'
import type { TxServiceModel } from '~/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions'
import { buildTransactionFrom } from '~/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions'
import { updateCancellationTransaction } from '~/routes/safe/store/actions/transactions/updateCancellationTransaction'
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from '~/routes/safe/store/actions/utils'
import type { TransactionProps } from '~/routes/safe/store/models/transaction'
import { CANCELLATION_TRANSACTIONS_REDUCER_ID } from '~/routes/safe/store/reducer/cancellationTransactions'
import { TRANSACTIONS_REDUCER_ID } from '~/routes/safe/store/reducer/transactions'
import { type GlobalState } from '~/store'
import { getErrorMessage } from '~/test/utils/ethereumErrors'
@ -44,6 +54,49 @@ export type CreateTransactionArgs = {
origin?: string | null,
}
async function mockMyTransaction(safeAddress: string, state, tx: {}) {
const submissionDate = new Date().toISOString()
const knownTokens = state[TOKEN_REDUCER_ID]
const cancellationTx = sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 && !tx.data
const existentTx: TransactionProps =
state[cancellationTx ? CANCELLATION_TRANSACTIONS_REDUCER_ID : TRANSACTIONS_REDUCER_ID]
.get(safeAddress)
.find(({ nonce }) => nonce === tx.nonce) || null
const transactionStructure: TxServiceModel = {
...tx,
value: tx.valueInWei,
blockNumber: null,
confirmations: [], // this is used to determine if a tx is pending or not. See `getTxStatus` selector
confirmationsRequired: null,
dataDecoded: {},
ethGasPrice: null,
executionDate: null,
executor: null,
fee: null,
gasUsed: null,
isExecuted: false,
isSuccessful: null,
origin: null,
safeTxHash: null,
signatures: null,
transactionHash: null,
...existentTx,
modified: submissionDate,
submissionDate,
safe: safeAddress,
}
const mockedTransaction: RecordInstance<TransactionProps> = await buildTransactionFrom(
safeAddress,
transactionStructure,
knownTokens,
null,
)
return { cancellationTx, existentTx, mockedTransaction }
}
const createTransaction = ({
safeAddress,
to,
@ -66,12 +119,12 @@ const createTransaction = ({
const ready = await onboardUser()
if (!ready) return
const { account: from, hardwareWallet, smartContractWallet } = providerSelector(state)
const { account: from /*, hardwareWallet, smartContractWallet*/ } = providerSelector(state)
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
const lastTx = await getLastTx(safeAddress)
const nonce = await getNewTxNonce(txNonce, lastTx, safeInstance)
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)
// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures
@ -105,25 +158,26 @@ const createTransaction = ({
try {
// 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
const canTryOffchainSigning =
!isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
if (false) {
const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet)
if (signature) {
closeSnackbar(beforeExecutionKey)
await saveTxToHistory({
...txArgs,
signature,
origin,
})
showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
dispatch(fetchTransactions(safeAddress))
return
}
}
// TODO: revert this
// const canTryOffchainSigning =
// !isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES)
// if (canTryOffchainSigning) {
// const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet)
//
// if (signature) {
// closeSnackbar(beforeExecutionKey)
//
// await saveTxToHistory({
// ...txArgs,
// signature,
// origin,
// })
// showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
//
// dispatch(fetchTransactions(safeAddress))
// return
// }
// }
tx = isExecution ? await getExecutionTransaction(txArgs) : await getApprovalTransaction(txArgs)
@ -143,11 +197,26 @@ const createTransaction = ({
pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
try {
await saveTxToHistory({
// TODO: let's mock the tx
const { cancellationTx, existentTx, mockedTransaction } = await mockMyTransaction(safeAddress, state, {
...txArgs,
txHash,
origin,
})
if (cancellationTx) {
if (existentTx) {
dispatch(updateCancellationTransaction({ safeAddress, transaction: mockedTransaction }))
} else {
dispatch(addCancellationTransaction({ safeAddress, transaction: mockedTransaction }))
}
} else {
if (existentTx) {
dispatch(updateTransaction({ safeAddress, transaction: mockedTransaction }))
} else {
dispatch(addTransaction({ safeAddress, transaction: mockedTransaction }))
}
}
await saveTxToHistory({ ...txArgs, txHash, origin })
await dispatch(fetchTransactions(safeAddress))
} catch (err) {
console.error(err)

View File

@ -10,6 +10,7 @@ import { decodeParamsFromSafeMethod } from '~/logic/contracts/methodIds'
import { buildTxServiceUrl } from '~/logic/safe/transactions/txHistory'
import { getTokenInfos } from '~/logic/tokens/store/actions/fetchTokens'
import { TOKEN_REDUCER_ID } from '~/logic/tokens/store/reducer/tokens'
import { ALTERNATIVE_TOKEN_ABI } from '~/logic/tokens/utils/alternativeAbi'
import {
SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH,
isMultisendTransaction,
@ -179,7 +180,7 @@ export const buildTransactionFrom = async (
})
}
const batchTxTokenRequest = (txs: any[]) => {
export const batchTxTokenRequest = (txs: any[]) => {
const batch = new web3ReadOnly.BatchRequest()
const whenTxsValues = txs.map((tx) => {

View File

@ -0,0 +1,6 @@
// @flow
import { createAction } from 'redux-actions'
export const UPDATE_CANCELLATION_TRANSACTION = 'UPDATE_CANCELLATION_TRANSACTION'
export const updateCancellationTransaction = createAction<string, *>(UPDATE_CANCELLATION_TRANSACTION)

View File

@ -2,7 +2,9 @@
import { List, Map } from 'immutable'
import { type ActionType, handleActions } from 'redux-actions'
import { ADD_CANCELLATION_TRANSACTION } from '~/routes/safe/store/actions/transactions/addCancellationTransaction'
import { ADD_CANCELLATION_TRANSACTIONS } from '~/routes/safe/store/actions/transactions/addCancellationTransactions'
import { UPDATE_CANCELLATION_TRANSACTION } from '~/routes/safe/store/actions/transactions/updateCancellationTransaction'
import { type Transaction } from '~/routes/safe/store/models/transaction'
export const CANCELLATION_TRANSACTIONS_REDUCER_ID = 'cancellationTransactions'
@ -11,7 +13,32 @@ export type CancelState = Map<string, List<Transaction>>
export default handleActions<CancelState, *>(
{
[ADD_CANCELLATION_TRANSACTION]: (state: CancelState, action: ActionType<Function>): CancelState => {
const { safeAddress, transaction } = action.payload
const transactionList = state.get(safeAddress)
return state.set(safeAddress, transactionList.push(transaction))
},
[ADD_CANCELLATION_TRANSACTIONS]: (state: CancelState, action: ActionType<Function>): CancelState => action.payload,
[UPDATE_CANCELLATION_TRANSACTION]: (state: CancelState, action: ActionType<Function>): CancelState => {
const { safeAddress, transaction } = action.payload
const transactionList = state.get(safeAddress)
if (!transaction) {
return state
}
const storedTransactionIndex = transactionList.findIndex((tx) => tx.safeTxHash === transaction.safeTxHash)
if (storedTransactionIndex === -1) {
return state
}
return state.set(
safeAddress,
transactionList.update(storedTransactionIndex, (tx) => tx.merge(transaction)),
)
},
},
Map(),
)

View File

@ -2,6 +2,7 @@
import { List, Map } from 'immutable'
import { type ActionType, handleActions } from 'redux-actions'
import { ADD_TRANSACTION } from '~/routes/safe/store/actions/transactions/addTransaction'
import { ADD_TRANSACTIONS } from '~/routes/safe/store/actions/transactions/addTransactions'
import { UPDATE_TRANSACTION } from '~/routes/safe/store/actions/transactions/updateTransaction'
import { type Transaction } from '~/routes/safe/store/models/transaction'
@ -12,6 +13,11 @@ export type State = Map<string, List<Transaction>>
export default handleActions<State, *>(
{
[ADD_TRANSACTION]: (state: State, action: ActionType<Function>): State => {
const { safeAddress, transaction } = action.payload
const transactionList = state.get(safeAddress)
return state.set(safeAddress, transactionList.push(transaction))
},
[ADD_TRANSACTIONS]: (state: State, action: ActionType<Function>): State => action.payload,
[UPDATE_TRANSACTION]: (state: State, action: ActionType<Function>): State => {
const { safeAddress, transaction } = action.payload