From 41ec0786b75cf445940f0a1c93ce9ff66c7c107b Mon Sep 17 00:00:00 2001 From: Fernando Date: Tue, 9 Jun 2020 16:15:31 -0300 Subject: [PATCH] (Refactor) Update `dataDecoded` structure (#995) --- src/logic/contracts/methodIds.ts | 47 +++++++---- .../store/actions/fetchCurrencyValues.ts | 2 +- .../store/utils/currencyValuesStorage.ts | 2 +- src/logic/safe/transactions/send.ts | 5 +- src/logic/wallets/store/model/provider.ts | 18 +++- src/routes/opening/components/Footer.tsx | 12 ++- .../safe/components/Apps/AddAppForm.tsx | 2 +- .../screens/ContractInteraction/index.tsx | 3 +- src/routes/safe/components/Layout/index.tsx | 8 +- .../ExpandedTx/TxDescription/utils.ts | 5 ++ .../safe/store/actions/createTransaction.ts | 13 +-- .../safe/store/actions/processTransaction.ts | 12 ++- .../loadOutgoingTransactions.ts | 31 ++++--- .../transactions/utils/transactionHelpers.ts | 82 ++++++++++++------- src/routes/safe/store/models/safe.ts | 27 +++++- src/routes/safe/store/models/transaction.ts | 4 +- .../safe/store/models/types/transaction.ts | 32 ++++---- src/routes/safe/store/reducer/safe.ts | 4 +- src/test/builder/safe.redux.builder.ts | 11 +-- src/test/utils/safeHelper.ts | 2 +- 20 files changed, 213 insertions(+), 109 deletions(-) diff --git a/src/logic/contracts/methodIds.ts b/src/logic/contracts/methodIds.ts index 351df109..851c0273 100644 --- a/src/logic/contracts/methodIds.ts +++ b/src/logic/contracts/methodIds.ts @@ -57,6 +57,7 @@ type TokenMethods = 'transfer' | 'transferFrom' | 'safeTransferFrom' type DecodedValues = Array<{ name: string + type?: string value: string }> @@ -70,7 +71,12 @@ type TokenDecodedParams = { export type DecodedMethods = SafeDecodedParams | TokenDecodedParams | null -export const decodeParamsFromSafeMethod = (data: string): SafeDecodedParams | null => { +export interface DataDecoded { + method: SafeMethods | TokenMethods + parameters: DecodedValues +} + +export const decodeParamsFromSafeMethod = (data: string): DataDecoded | null => { const [methodId, params] = [data.slice(0, 10) as keyof typeof METHOD_TO_ID | string, data.slice(10)] switch (methodId) { @@ -78,10 +84,11 @@ export const decodeParamsFromSafeMethod = (data: string): SafeDecodedParams | nu case '0xe318b52b': { const decodedParameters = web3.eth.abi.decodeParameters(['uint', 'address', 'address'], params) return { - [METHOD_TO_ID[methodId]]: [ + method: METHOD_TO_ID[methodId], + parameters: [ { name: 'oldOwner', value: decodedParameters[1] }, { name: 'newOwner', value: decodedParameters[2] }, - ] + ], } } @@ -89,10 +96,11 @@ export const decodeParamsFromSafeMethod = (data: string): SafeDecodedParams | nu case '0x0d582f13': { const decodedParameters = web3.eth.abi.decodeParameters(['address', 'uint'], params) return { - [METHOD_TO_ID[methodId]]: [ + method: METHOD_TO_ID[methodId], + parameters: [ { name: 'owner', value: decodedParameters[0] }, { name: '_threshold', value: decodedParameters[1] }, - ] + ], } } @@ -100,10 +108,11 @@ export const decodeParamsFromSafeMethod = (data: string): SafeDecodedParams | nu case '0xf8dc5dd9': { const decodedParameters = web3.eth.abi.decodeParameters(['address', 'address', 'uint'], params) return { - [METHOD_TO_ID[methodId]]: [ + method: METHOD_TO_ID[methodId], + parameters: [ { name: 'oldOwner', value: decodedParameters[1] }, { name: '_threshold', value: decodedParameters[2] }, - ] + ], } } @@ -111,9 +120,10 @@ export const decodeParamsFromSafeMethod = (data: string): SafeDecodedParams | nu case '0x694e80c3': { const decodedParameters = web3.eth.abi.decodeParameters(['uint'], params) return { - [METHOD_TO_ID[methodId]]: [ + method: METHOD_TO_ID[methodId], + parameters: [ { name: '_threshold', value: decodedParameters[0] }, - ] + ], } } @@ -126,7 +136,7 @@ const isSafeMethod = (methodId: string): boolean => { return !!METHOD_TO_ID[methodId] } -export const decodeMethods = (data: string): DecodedMethods => { +export const decodeMethods = (data: string): DataDecoded | null => { const [methodId, params] = [data.slice(0, 10), data.slice(10)] if (isSafeMethod(methodId)) { @@ -138,10 +148,11 @@ export const decodeMethods = (data: string): DecodedMethods => { case '0xa9059cbb': { const decodeParameters = web3.eth.abi.decodeParameters(['address', 'uint'], params) return { - transfer: [ + method: 'transfer', + parameters: [ { name: 'to', value: decodeParameters[0] }, { name: 'value', value: decodeParameters[1] }, - ] + ], } } @@ -149,23 +160,25 @@ export const decodeMethods = (data: string): DecodedMethods => { case '0x23b872dd': { const decodeParameters = web3.eth.abi.decodeParameters(['address', 'address', 'uint'], params) return { - transferFrom: [ + method: 'transferFrom', + parameters: [ { name: 'from', value: decodeParameters[0] }, { name: 'to', value: decodeParameters[1] }, { name: 'value', value: decodeParameters[2] }, - ] + ], } } // 42842e0e - safeTransferFrom(address,address,uint256) - case '0x42842e0e':{ + case '0x42842e0e': { const decodedParameters = web3.eth.abi.decodeParameters(['address', 'address', 'uint'], params) return { - safeTransferFrom: [ + method: 'safeTransferFrom', + parameters: [ { name: 'from', value: decodedParameters[0] }, { name: 'to', value: decodedParameters[1] }, { name: 'value', value: decodedParameters[2] }, - ] + ], } } diff --git a/src/logic/currencyValues/store/actions/fetchCurrencyValues.ts b/src/logic/currencyValues/store/actions/fetchCurrencyValues.ts index 6d92ff3e..2c098cbf 100644 --- a/src/logic/currencyValues/store/actions/fetchCurrencyValues.ts +++ b/src/logic/currencyValues/store/actions/fetchCurrencyValues.ts @@ -9,7 +9,7 @@ import { loadCurrencyValues } from 'src/logic/currencyValues/store/utils/currenc export const fetchCurrencyValues = (safeAddress: string) => async (dispatch) => { try { - const storedCurrencies: Map | {} = await loadCurrencyValues() + const storedCurrencies: Map | any = await loadCurrencyValues() const storedCurrency = storedCurrencies[safeAddress] if (!storedCurrency) { return batch(() => { diff --git a/src/logic/currencyValues/store/utils/currencyValuesStorage.ts b/src/logic/currencyValues/store/utils/currencyValuesStorage.ts index 280169d2..002c8884 100644 --- a/src/logic/currencyValues/store/utils/currencyValuesStorage.ts +++ b/src/logic/currencyValues/store/utils/currencyValuesStorage.ts @@ -11,6 +11,6 @@ export const saveCurrencyValues = async (currencyValues: Map | {}> => { +export const loadCurrencyValues = async (): Promise | any> => { return (await loadFromStorage(CURRENCY_VALUES_STORAGE_KEY)) || {} } diff --git a/src/logic/safe/transactions/send.ts b/src/logic/safe/transactions/send.ts index 3b372111..7063e93c 100644 --- a/src/logic/safe/transactions/send.ts +++ b/src/logic/safe/transactions/send.ts @@ -1,6 +1,7 @@ import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json' import { getWeb3 } from 'src/logic/wallets/getWeb3' +import { TxArgs } from 'src/routes/safe/store/models/types/transaction' export const CALL = 0 export const DELEGATE_CALL = 1 @@ -20,7 +21,7 @@ export const getApprovalTransaction = async ({ sender, to, valueInWei, -}) => { +}: TxArgs) => { const txHash = await safeInstance.getTransactionHash( to, valueInWei, @@ -61,7 +62,7 @@ export const getExecutionTransaction = async ({ sigs, to, valueInWei, -}) => { +}: TxArgs) => { try { const web3 = getWeb3() const contract: any = new web3.eth.Contract(GnosisSafeSol.abi as any, safeInstance.address) diff --git a/src/logic/wallets/store/model/provider.ts b/src/logic/wallets/store/model/provider.ts index 4b07702a..1e8ad09a 100644 --- a/src/logic/wallets/store/model/provider.ts +++ b/src/logic/wallets/store/model/provider.ts @@ -1,6 +1,16 @@ -import { Record } from 'immutable' +import { Record, RecordOf } from 'immutable' -export const makeProvider = Record({ +export type ProviderProps = { + name: string + loaded: boolean + available: boolean + account: string + network: number + smartContractWallet: boolean + hardwareWallet: boolean +} + +export const makeProvider = Record({ name: '', loaded: false, available: false, @@ -10,4 +20,6 @@ export const makeProvider = Record({ hardwareWallet: false, }) -// Useage const someProvider: Provider = makeProvider({ name: 'METAMASK', loaded: false, available: false }) +// Usage const someProvider: Provider = makeProvider({ name: 'METAMASK', loaded: false, available: false }) + +export type ProviderRecord = RecordOf diff --git a/src/routes/opening/components/Footer.tsx b/src/routes/opening/components/Footer.tsx index 36aca11d..9e007b65 100644 --- a/src/routes/opening/components/Footer.tsx +++ b/src/routes/opening/components/Footer.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { SyntheticEvent } from 'react' import styled from 'styled-components' import Button from 'src/components/layout/Button' @@ -36,14 +36,20 @@ export const ContinueFooter = ({ onContinue, }: { continueButtonDisabled: boolean - onContinue: () => void + onContinue: (event: SyntheticEvent) => void }) => ( ) -export const ErrorFooter = ({ onCancel, onRetry }: { onCancel: () => void; onRetry: () => void }) => ( +export const ErrorFooter = ({ + onCancel, + onRetry, +}: { + onCancel: (event: SyntheticEvent) => void + onRetry: (event: SyntheticEvent) => void +}) => ( <> Cancel diff --git a/src/routes/safe/components/Apps/AddAppForm.tsx b/src/routes/safe/components/Apps/AddAppForm.tsx index fd5f2609..5eafad7b 100644 --- a/src/routes/safe/components/Apps/AddAppForm.tsx +++ b/src/routes/safe/components/Apps/AddAppForm.tsx @@ -102,7 +102,7 @@ const curriedSafeAppValidator = memoize((appList) => async (value: string) => { } }) -const composeValidatorsApps = (...validators: Function[]) => (value, values, meta) => { +const composeValidatorsApps = (...validators) => (value, values, meta) => { if (!meta.modified) { return } diff --git a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/index.tsx index 1d200145..48dba963 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/index.tsx @@ -19,13 +19,14 @@ import MethodsDropdown from './MethodsDropdown' import RenderInputParams from './RenderInputParams' import RenderOutputParams from './RenderOutputParams' import { abiExtractor, createTxObject, formMutators, handleSubmitError, isReadMethod, ensResolver } from './utils' +import { TransactionReviewType } from './Review' const useStyles = makeStyles(styles) export interface CreatedTx { contractAddress: string data: string - selectedMethod: any + selectedMethod: TransactionReviewType value: string | number } diff --git a/src/routes/safe/components/Layout/index.tsx b/src/routes/safe/components/Layout/index.tsx index d2c85e94..81aaccce 100644 --- a/src/routes/safe/components/Layout/index.tsx +++ b/src/routes/safe/components/Layout/index.tsx @@ -34,10 +34,10 @@ const AddressBookTable = React.lazy(() => import('src/routes/safe/components/Add interface Props { sendFunds: Record showReceive: boolean - onShow: Function - onHide: Function - showSendFunds: Function - hideSendFunds: Function + onShow: (value: string) => void + onHide: (value: string) => void + showSendFunds: (value: string) => void + hideSendFunds: () => void match: Record location: Record history: Record diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts index 01159987..cd810666 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts @@ -51,6 +51,11 @@ export const getTxData = (tx) => { txData.addedOwner = newOwner } } + if (tx.multiSendTx) { + txData.recipient = tx.recipient + txData.data = tx.data + txData.customTx = true + } } else if (tx.customTx) { txData.recipient = tx.recipient txData.data = tx.data diff --git a/src/routes/safe/store/actions/createTransaction.ts b/src/routes/safe/store/actions/createTransaction.ts index be558b0e..a6cb1b57 100644 --- a/src/routes/safe/store/actions/createTransaction.ts +++ b/src/routes/safe/store/actions/createTransaction.ts @@ -27,13 +27,14 @@ import { removeTransaction } from 'src/routes/safe/store/actions/transactions/re import { generateSafeTxHash, mockTransaction, + TxToMock, } from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers' import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils' import { getErrorMessage } from 'src/test/utils/ethereumErrors' import { makeConfirmation } from '../models/confirmation' import fetchTransactions from './transactions/fetchTransactions' import { safeTransactionsSelector } from 'src/routes/safe/store/selectors' -import { TransactionStatus } from 'src/routes/safe/store/models/types/transaction' +import { TransactionStatus, TxArgs } from 'src/routes/safe/store/models/types/transaction' export const removeTxFromStore = (tx, safeAddress, dispatch, state) => { if (tx.isCancellationTx) { @@ -120,7 +121,7 @@ const createTransaction = ({ let txHash let tx - const txArgs = { + const txArgs: TxArgs = { safeInstance, to, valueInWei, @@ -129,7 +130,7 @@ const createTransaction = ({ nonce, safeTxGas, baseGas: 0, - gasPrice: 0, + gasPrice: '0', gasToken: ZERO_ADDRESS, refundReceiver: ZERO_ADDRESS, sender: from, @@ -164,7 +165,7 @@ const createTransaction = ({ sendParams.gas = '7000000' } - const txToMock = { + const txToMock: TxToMock = { ...txArgs, confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper value: txArgs.valueInWei, @@ -223,9 +224,9 @@ const createTransaction = ({ .set('executor', from) .set('isExecuted', true) .set('isSuccessful', receipt.status) - .set('status', receipt.status ? 'success' : 'failed') + .set('status', receipt.status ? TransactionStatus.SUCCESS : TransactionStatus.FAILED) }) - : mockedTx.set('status', 'awaiting_confirmations') + : mockedTx.set('status', TransactionStatus.AWAITING_CONFIRMATIONS) await storeTx( toStoreTx.withMutations((record) => { diff --git a/src/routes/safe/store/actions/processTransaction.ts b/src/routes/safe/store/actions/processTransaction.ts index 1d9556db..a1bc4f47 100644 --- a/src/routes/safe/store/actions/processTransaction.ts +++ b/src/routes/safe/store/actions/processTransaction.ts @@ -13,12 +13,14 @@ import fetchTransactions from 'src/routes/safe/store/actions/transactions/fetchT import { isCancelTransaction, mockTransaction, + TxToMock, } from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers' import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils' import { getErrorMessage } from 'src/test/utils/ethereumErrors' import { makeConfirmation } from '../models/confirmation' import { storeTx } from './createTransaction' +import { TransactionStatus } from '../models/types/transaction' const processTransaction = ({ approveAndExecute, @@ -101,7 +103,7 @@ const processTransaction = ({ sendParams.gas = '7000000' } - const txToMock = { + const txToMock: TxToMock = { ...txArgs, confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper value: txArgs.valueInWei, @@ -165,11 +167,15 @@ const processTransaction = ({ .set('isSuccessful', receipt.status) .set( 'status', - receipt.status ? (isCancelTransaction(record, safeAddress) ? 'cancelled' : 'success') : 'failed', + receipt.status + ? isCancelTransaction(record, safeAddress) + ? TransactionStatus.CANCELLED + : TransactionStatus.SUCCESS + : TransactionStatus.FAILED, ) .updateIn(['ownersWithPendingActions', 'reject'], (prev) => prev.clear()) }) - : mockedTx.set('status', 'awaiting_confirmations') + : mockedTx.set('status', TransactionStatus.AWAITING_CONFIRMATIONS) await storeTx( toStoreTx.withMutations((record) => { diff --git a/src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts b/src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts index 409453f8..9b2f8000 100644 --- a/src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts +++ b/src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts @@ -7,14 +7,18 @@ import { PROVIDER_REDUCER_ID } from 'src/logic/wallets/store/reducer/provider' import { buildTx, isCancelTransaction } from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers' import { SAFE_REDUCER_ID } from 'src/routes/safe/store/reducer/safe' import { store } from 'src/store' -import { DecodedMethods } from 'src/logic/contracts/methodIds' +import { DataDecoded } from 'src/logic/contracts/methodIds' import fetchTransactions from 'src/routes/safe/store/actions/transactions/fetchTransactions/fetchTransactions' -import { TransactionTypes } from 'src/routes/safe/store/models/types/transaction' +import { Transaction, TransactionTypes } from 'src/routes/safe/store/models/types/transaction' +import { Token } from 'src/logic/tokens/store/model/token' +import { SafeRecord } from 'src/routes/safe/store/models/safe' export type ConfirmationServiceModel = { + confirmationType: string owner: string - submissionDate: Date + submissionDate: string signature: string + signatureType: string transactionHash: string } @@ -22,25 +26,32 @@ export type TxServiceModel = { baseGas: number blockNumber?: number | null confirmations: ConfirmationServiceModel[] + confirmationsRequired: number creationTx?: boolean | null data?: string | null - dataDecoded?: DecodedMethods + dataDecoded?: DataDecoded + ethGasPrice: string executionDate?: string | null executor: string - gasPrice: number + fee: string + gasPrice: string gasToken: string + gasUsed: number isExecuted: boolean isSuccessful: boolean + modified: string nonce?: number | null operation: number origin?: string | null refundReceiver: string + safe: string safeTxGas: number safeTxHash: string + signatures: string submissionDate?: string | null to: string transactionHash?: string | null - value: number + value: string } export type SafeTransactionsType = { @@ -55,8 +66,8 @@ export type OutgoingTxs = { export type BatchProcessTxsProps = OutgoingTxs & { currentUser?: string - knownTokens: any - safe: any + knownTokens: Record + safe: SafeRecord } /** @@ -127,8 +138,8 @@ const batchProcessOutgoingTransactions = async ({ outgoingTxs, safe, }: BatchProcessTxsProps): Promise<{ - cancel: any - outgoing: any + cancel: Record + outgoing: Array }> => { // cancellation transactions const cancelTxsValues = Object.values(cancellationTxs) diff --git a/src/routes/safe/store/actions/transactions/utils/transactionHelpers.ts b/src/routes/safe/store/actions/transactions/utils/transactionHelpers.ts index dae640a1..76fa16a6 100644 --- a/src/routes/safe/store/actions/transactions/utils/transactionHelpers.ts +++ b/src/routes/safe/store/actions/transactions/utils/transactionHelpers.ts @@ -26,18 +26,32 @@ import { TRANSACTIONS_REDUCER_ID } from 'src/routes/safe/store/reducer/transacti import { store } from 'src/store' import { safeSelector, safeTransactionsSelector } from 'src/routes/safe/store/selectors' import { addOrUpdateTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateTransactions' -import { TxServiceModel } from 'src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' +import { + BatchProcessTxsProps, + TxServiceModel, +} from 'src/routes/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' import { TypedDataUtils } from 'eth-sig-util' +import { Token } from 'src/logic/tokens/store/model/token' +import { ProviderRecord } from 'src/logic/wallets/store/model/provider' +import { SafeRecord } from 'src/routes/safe/store/models/safe' export const isEmptyData = (data?: string | null): boolean => { return !data || data === EMPTY_DATA } -export const isInnerTransaction = (tx: TxServiceModel, safeAddress: string): boolean => { - return sameAddress(tx.to, safeAddress) && Number(tx.value) === 0 +export const isInnerTransaction = (tx: TxServiceModel | Transaction, safeAddress: string): boolean => { + let isSameAddress = false + + if ((tx as TxServiceModel).to !== undefined) { + isSameAddress = sameAddress((tx as TxServiceModel).to, safeAddress) + } else if ((tx as Transaction).recipient !== undefined) { + isSameAddress = sameAddress((tx as Transaction).recipient, safeAddress) + } + + return isSameAddress && Number(tx.value) === 0 } -export const isCancelTransaction = (tx: TxServiceModel, safeAddress: string): boolean => { +export const isCancelTransaction = (tx: TxServiceModel | Transaction, safeAddress: string): boolean => { return isInnerTransaction(tx, safeAddress) && isEmptyData(tx.data) } @@ -70,7 +84,7 @@ export const isCustomTransaction = async ( tx: TxServiceModel, txCode: string, safeAddress: string, - knownTokens: any, + knownTokens: Record, ): Promise => { return ( isOutgoingTransaction(tx, safeAddress) && @@ -81,12 +95,13 @@ export const isCustomTransaction = async ( } export const getRefundParams = async ( - tx: any, + tx: TxServiceModel, tokenInfo: (string) => Promise<{ decimals: number; symbol: string } | null>, ): Promise => { + const txGasPrice = Number(tx.gasPrice) let refundParams = null - if (tx.gasPrice > 0) { + if (txGasPrice > 0) { let refundSymbol = 'ETH' let refundDecimals = 18 @@ -99,7 +114,9 @@ export const getRefundParams = async ( } } - const feeString = (tx.gasPrice * (tx.baseGas + tx.safeTxGas)).toString().padStart(refundDecimals, '0') + const feeString = (txGasPrice * (Number(tx.baseGas) + Number(tx.safeTxGas))) + .toString() + .padStart(refundDecimals, '0') const whole = feeString.slice(0, feeString.length - refundDecimals) || '0' const fraction = feeString.slice(feeString.length - refundDecimals) @@ -114,18 +131,15 @@ export const getRefundParams = async ( export const getDecodedParams = (tx: TxServiceModel): DecodedMethods => { if (tx.dataDecoded) { - return Object.keys(tx.dataDecoded).reduce((acc, key) => { - acc[key] = { - ...tx.dataDecoded[key].reduce( - (acc, param) => ({ - ...acc, - [param.name]: param.value, - }), - {}, - ), - } - return acc - }, {}) + return { + [tx.dataDecoded.method]: tx.dataDecoded.parameters.reduce( + (acc, param) => ({ + ...acc, + [param.name]: param.value, + }), + {}, + ), + } } return null } @@ -159,7 +173,7 @@ export const isTransactionCancelled = ( export const calculateTransactionStatus = ( tx: Transaction, - { owners, threshold }: any, + { owners, threshold }: SafeRecord, currentUser?: string | null, ): TransactionStatusValues => { let txStatus @@ -212,6 +226,11 @@ export const calculateTransactionType = (tx: Transaction): TransactionTypeValues return txType } +export type BuildTx = BatchProcessTxsProps & { + tx: TxServiceModel + txCode: string | null +} + export const buildTx = async ({ cancellationTxs, currentUser, @@ -220,7 +239,7 @@ export const buildTx = async ({ safe, tx, txCode, -}): Promise => { +}: BuildTx): Promise => { const safeAddress = safe.address const isModifySettingsTx = isModifySettingsTransaction(tx, safeAddress) const isTxCancelled = isTransactionCancelled(tx, outgoingTxs, cancellationTxs) @@ -235,7 +254,7 @@ export const buildTx = async ({ const confirmations = getConfirmations(tx) const { decimals = 18, symbol = 'ETH' } = isSendERC20Tx ? await getERC20DecimalsAndSymbol(tx.to) : {} - const txToStore: Transaction = makeTransaction({ + const txToStore = makeTransaction({ baseGas: tx.baseGas, blockNumber: tx.blockNumber, cancelled: isTxCancelled, @@ -276,7 +295,13 @@ export const buildTx = async ({ .set('type', calculateTransactionType(txToStore)) } -export const mockTransaction = (tx, safeAddress: string, state): Promise => { +export type TxToMock = TxArgs & { + confirmations: [] + safeTxHash: string + value: string +} + +export const mockTransaction = (tx: TxToMock, safeAddress: string, state): Promise => { const submissionDate = new Date().toISOString() const transactionStructure: TxServiceModel = { @@ -301,8 +326,8 @@ export const mockTransaction = (tx, safeAddress: string, state): Promise => ...tx, } - const knownTokens = state[TOKEN_REDUCER_ID] - const safe = state[SAFE_REDUCER_ID].getIn([SAFE_REDUCER_ID, safeAddress]) + const knownTokens: Record = state[TOKEN_REDUCER_ID] + const safe: SafeRecord = state[SAFE_REDUCER_ID].getIn([SAFE_REDUCER_ID, safeAddress]) const cancellationTxs = state[CANCELLATION_TRANSACTIONS_REDUCER_ID].get(safeAddress) || Map() const outgoingTxs = state[TRANSACTIONS_REDUCER_ID].get(safeAddress) || List() @@ -317,7 +342,7 @@ export const mockTransaction = (tx, safeAddress: string, state): Promise => }) } -export const updateStoredTransactionsStatus = (dispatch, walletRecord): void => { +export const updateStoredTransactionsStatus = (dispatch: (any) => void, walletRecord: ProviderRecord): void => { const state = store.getState() const safe = safeSelector(state) @@ -351,7 +376,8 @@ export function generateSafeTxHash(safeAddress: string, txArgs: TxArgs): string { type: 'uint256', name: 'nonce' }, ], } - const primaryType = 'SafeTx' as const + + const primaryType: 'SafeTx' = 'SafeTx' as const const typedData = { types: messageTypes, diff --git a/src/routes/safe/store/models/safe.ts b/src/routes/safe/store/models/safe.ts index 72064803..40eb1621 100644 --- a/src/routes/safe/store/models/safe.ts +++ b/src/routes/safe/store/models/safe.ts @@ -1,6 +1,25 @@ -import { List, Map, Record, Set } from 'immutable' +import { List, Map, Record, RecordOf, Set } from 'immutable' -const SafeRecord = Record({ +export type SafeRecordProps = { + name: string + address: string + threshold: number + ethBalance: number + owners: List<{ name: string; address: string }> + activeTokens: Set + activeAssets: Set + blacklistedTokens: Set + blacklistedAssets: Set + balances: Map + nonce: number + latestIncomingTxBlock: number + recurringUser?: boolean + currentVersion: string + needsUpdate: boolean + featuresEnabled: Array +} + +const makeSafe = Record({ name: '', address: '', threshold: 0, @@ -19,4 +38,6 @@ const SafeRecord = Record({ featuresEnabled: [], }) -export default SafeRecord +export type SafeRecord = RecordOf + +export default makeSafe diff --git a/src/routes/safe/store/models/transaction.ts b/src/routes/safe/store/models/transaction.ts index 214fd111..4db6cb0e 100644 --- a/src/routes/safe/store/models/transaction.ts +++ b/src/routes/safe/store/models/transaction.ts @@ -2,8 +2,8 @@ import { List, Map, Record } from 'immutable' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { - TransactionProps, PendingActionType, + TransactionProps, TransactionStatus, TransactionTypes, } from 'src/routes/safe/store/models/types/transaction' @@ -24,7 +24,7 @@ export const makeTransaction = Record({ executionTxHash: undefined, executor: '', factoryAddress: '', - gasPrice: 0, + gasPrice: '0', gasToken: ZERO_ADDRESS, isCancellationTx: false, isCollectibleTransfer: false, diff --git a/src/routes/safe/store/models/types/transaction.ts b/src/routes/safe/store/models/types/transaction.ts index 83c7bd9c..83815f60 100644 --- a/src/routes/safe/store/models/types/transaction.ts +++ b/src/routes/safe/store/models/types/transaction.ts @@ -1,3 +1,7 @@ +import { List, Map, RecordOf } from 'immutable' +import { DecodedMethods } from 'src/logic/contracts/methodIds' +import { Confirmation } from './confirmation' + export enum TransactionTypes { INCOMING = 'incoming', OUTGOING = 'outgoing', @@ -32,19 +36,19 @@ export type TransactionProps = { baseGas: number blockNumber?: number | null cancelled?: boolean - confirmations: import('immutable').List + confirmations: List created: boolean creator: string creationTx: boolean customTx: boolean data?: string | null decimals?: (number | string) | null - decodedParams: import('src/logic/contracts/methodIds').DecodedMethods + decodedParams: DecodedMethods executionDate?: string | null executionTxHash?: string | null executor: string factoryAddress: string - gasPrice: number + gasPrice: string gasToken: string isCancellationTx: boolean isCollectibleTransfer: boolean @@ -58,7 +62,7 @@ export type TransactionProps = { nonce?: number | null operation: number origin: string | null - ownersWithPendingActions: import('immutable').Map> + ownersWithPendingActions: Map> recipient: string refundParams: any refundReceiver: string @@ -68,26 +72,26 @@ export type TransactionProps = { status?: TransactionStatus submissionDate?: string | null symbol?: string | null - transactionHash: string + transactionHash: string | null type: TransactionTypes upgradeTx: boolean value: string } -export type Transaction = import('immutable').RecordOf +export type Transaction = RecordOf export type TxArgs = { - data: any baseGas: number + data?: string | null + gasPrice: string gasToken: string - safeInstance: any nonce: number - valueInWei: any - safeTxGas: number + operation: number refundReceiver: string - sender: any + safeInstance: any + safeTxGas: number + sender?: string sigs: string - to: any - operation: any - gasPrice: number + to: string + valueInWei: string } diff --git a/src/routes/safe/store/reducer/safe.ts b/src/routes/safe/store/reducer/safe.ts index 45323c04..73e9c55e 100644 --- a/src/routes/safe/store/reducer/safe.ts +++ b/src/routes/safe/store/reducer/safe.ts @@ -12,7 +12,7 @@ import { SET_DEFAULT_SAFE } from 'src/routes/safe/store/actions/setDefaultSafe' import { SET_LATEST_MASTER_CONTRACT_VERSION } from 'src/routes/safe/store/actions/setLatestMasterContractVersion' import { UPDATE_SAFE } from 'src/routes/safe/store/actions/updateSafe' import { makeOwner } from 'src/routes/safe/store/models/owner' -import SafeRecord from 'src/routes/safe/store/models/safe' +import makeSafe from 'src/routes/safe/store/models/safe' import { checksumAddress } from 'src/utils/checksumAddress' export const SAFE_REDUCER_ID = 'safes' @@ -74,7 +74,7 @@ export default handleActions( return state.updateIn([SAFE_REDUCER_ID, safe.address], (prevSafe) => prevSafe.merge(safe)) } - return state.setIn([SAFE_REDUCER_ID, safe.address], SafeRecord(safe)) + return state.setIn([SAFE_REDUCER_ID, safe.address], makeSafe(safe)) }, [REMOVE_SAFE]: (state, action) => { const safeAddress = action.payload diff --git a/src/test/builder/safe.redux.builder.ts b/src/test/builder/safe.redux.builder.ts index f7efb91c..a6b6e50a 100644 --- a/src/test/builder/safe.redux.builder.ts +++ b/src/test/builder/safe.redux.builder.ts @@ -1,7 +1,5 @@ -// -/* eslint-disable max-classes-per-file */ -import SafeRecord, { } from 'src/routes/safe/store/models/safe' -import addSafe, { buildOwnersFrom } from 'src/routes/safe/store/actions/addSafe' +import makeSafe from 'src/routes/safe/store/models/safe' +import { buildOwnersFrom } from 'src/routes/safe/store/actions/addSafe' import { FIELD_NAME, FIELD_CONFIRMATIONS, @@ -11,7 +9,6 @@ import { } from 'src/routes/open/components/fields' import { getWeb3, getProviderInfo } from 'src/logic/wallets/getWeb3' import { createSafe, } from 'src/routes/open/container/Open' -import { } from 'src/store/index' import { makeProvider } from 'src/logic/wallets/store/model/provider' import addProvider from 'src/logic/wallets/store/actions/addProvider' @@ -19,7 +16,7 @@ class SafeBuilder { safe constructor() { - this.safe = SafeRecord() + this.safe = makeSafe() } withAddress(address) { @@ -90,7 +87,7 @@ export const aMinedSafe = async ( form[getOwnerNameBy(i)] = `Adol ${i + 1} Eth Account` form[getOwnerAddressBy(i)] = accounts[i] } - + const openSafeProps = await createSafe(form, accounts[0]) return openSafeProps.safeAddress diff --git a/src/test/utils/safeHelper.ts b/src/test/utils/safeHelper.ts index af091c18..8f34e51e 100644 --- a/src/test/utils/safeHelper.ts +++ b/src/test/utils/safeHelper.ts @@ -1,4 +1,4 @@ -// +// import { } from 'react-router-dom' import { buildMatchPropsFrom } from 'src/test/utils/buildReactRouterProps' import { safeSelector } from 'src/routes/safe/store/selectors/index'