From 5c923ceffa76d217d785fd381937fe7d37a2ba11 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Thu, 4 Mar 2021 11:24:45 -0300 Subject: [PATCH 01/23] (Feature) Estimate gas for safe creation (#1825) * Types in safeDataExtractor.ts * Adds useEstimateSafeCreationGas hook * Refactor Layout.tsx * Remove gas price in estimateGasForDeployingSafe * Types in createSafe and adds gasLimit to deployment transaction * Uses useEstimateSafeCreationGas in ReviewComponent for transaction creation * Add types * Fix types * Fix types of ReviewInformation modal on safe load * Fix safe creation for multiple owners * fix unit tests * Remove unnecesary minimum tx gas Co-authored-by: Mati Dastugue Co-authored-by: nicolas Co-authored-by: nicosampler Co-authored-by: Daniel Sanchez --- src/components/Stepper/index.tsx | 3 +- src/logic/contracts/safeContracts.ts | 11 ++-- .../hooks/useEstimateSafeCreationGas.tsx | 59 +++++++++++++++++++ .../components/ReviewInformation/index.tsx | 7 ++- src/routes/load/container/Load.tsx | 14 ++++- src/routes/open/components/Layout.tsx | 55 ++++++++--------- .../components/ReviewInformation/index.tsx | 51 ++++++++-------- src/routes/open/components/fields.ts | 10 +++- src/routes/open/container/Open.tsx | 30 ++++++---- .../open/utils/safeDataExtractor.spec.ts | 27 +++++++-- src/routes/open/utils/safeDataExtractor.ts | 30 +++++++--- 11 files changed, 200 insertions(+), 97 deletions(-) create mode 100644 src/logic/hooks/useEstimateSafeCreationGas.tsx diff --git a/src/components/Stepper/index.tsx b/src/components/Stepper/index.tsx index 07ee8497..824a1c77 100644 --- a/src/components/Stepper/index.tsx +++ b/src/components/Stepper/index.tsx @@ -11,6 +11,7 @@ import Controls from './Controls' import GnoForm from 'src/components/forms/GnoForm' import Hairline from 'src/components/layout/Hairline' import { history } from 'src/store' +import { LoadFormValues } from 'src/routes/load/container/Load' const transitionProps = { timeout: { @@ -20,7 +21,7 @@ const transitionProps = { } export interface StepperPageFormProps { - values: Record + values: LoadFormValues errors: Record form: FormApi } diff --git a/src/logic/contracts/safeContracts.ts b/src/logic/contracts/safeContracts.ts index 384fcc8e..17345da0 100644 --- a/src/logic/contracts/safeContracts.ts +++ b/src/logic/contracts/safeContracts.ts @@ -5,7 +5,7 @@ import Web3 from 'web3' import { ETHEREUM_NETWORK } from 'src/config/networks/network.d' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' -import { calculateGasOf, calculateGasPrice } from 'src/logic/wallets/ethTransactions' +import { calculateGasOf, EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { getWeb3, getNetworkIdFrom } from 'src/logic/wallets/getWeb3' import { GnosisSafe } from 'src/types/contracts/GnosisSafe.d' import { GnosisSafeProxyFactory } from 'src/types/contracts/GnosisSafeProxyFactory.d' @@ -99,7 +99,7 @@ export const getSafeDeploymentTransaction = ( safeAccounts, numConfirmations, ZERO_ADDRESS, - '0x', + EMPTY_DATA, DEFAULT_FALLBACK_HANDLER_ADDRESS, ZERO_ADDRESS, 0, @@ -120,7 +120,7 @@ export const estimateGasForDeployingSafe = async ( safeAccounts, numConfirmations, ZERO_ADDRESS, - '0x', + EMPTY_DATA, DEFAULT_FALLBACK_HANDLER_ADDRESS, ZERO_ADDRESS, 0, @@ -130,14 +130,11 @@ export const estimateGasForDeployingSafe = async ( const proxyFactoryData = proxyFactoryMaster.methods .createProxyWithNonce(safeMaster.options.address, gnosisSafeData, safeCreationSalt) .encodeABI() - const gas = await calculateGasOf({ + return calculateGasOf({ data: proxyFactoryData, from: userAccount, to: proxyFactoryMaster.options.address, }) - const gasPrice = await calculateGasPrice() - - return gas * parseInt(gasPrice, 10) } export const getGnosisSafeInstanceAt = (safeAddress: string): GnosisSafe => { diff --git a/src/logic/hooks/useEstimateSafeCreationGas.tsx b/src/logic/hooks/useEstimateSafeCreationGas.tsx new file mode 100644 index 00000000..40a712d0 --- /dev/null +++ b/src/logic/hooks/useEstimateSafeCreationGas.tsx @@ -0,0 +1,59 @@ +import { useEffect, useState } from 'react' +import { useSelector } from 'react-redux' +import { userAccountSelector } from '../wallets/store/selectors' +import { estimateGasForDeployingSafe } from 'src/logic/contracts/safeContracts' +import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue' +import { formatAmount } from 'src/logic/tokens/utils/formatAmount' +import { getNetworkInfo } from 'src/config' +import { calculateGasPrice } from 'src/logic/wallets/ethTransactions' + +type EstimateSafeCreationGasProps = { + addresses: string[] + numOwners: number + safeCreationSalt: number +} + +type SafeCreationEstimationResult = { + gasEstimation: number // Amount of gas needed for execute or approve the transaction + gasCostFormatted: string // Cost of gas in format '< | > 100' + gasLimit: number // Minimum gas requited to execute the Tx +} + +const { nativeCoin } = getNetworkInfo() + +export const useEstimateSafeCreationGas = ({ + addresses, + numOwners, + safeCreationSalt, +}: EstimateSafeCreationGasProps): SafeCreationEstimationResult => { + const [gasEstimation, setGasEstimation] = useState({ + gasEstimation: 0, + gasCostFormatted: '< 0.001', + gasLimit: 0, + }) + const userAccount = useSelector(userAccountSelector) + + useEffect(() => { + const estimateGas = async () => { + if (!addresses.length || !numOwners || !userAccount) { + return + } + + const gasEstimation = await estimateGasForDeployingSafe(addresses, numOwners, userAccount, safeCreationSalt) + const gasPrice = await calculateGasPrice() + const estimatedGasCosts = gasEstimation * parseInt(gasPrice, 10) + const gasCost = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals) + const gasCostFormatted = formatAmount(gasCost) + + setGasEstimation({ + gasEstimation, + gasCostFormatted, + gasLimit: gasEstimation, + }) + } + + estimateGas() + }, [numOwners, userAccount, safeCreationSalt, addresses]) + + return gasEstimation +} diff --git a/src/routes/load/components/ReviewInformation/index.tsx b/src/routes/load/components/ReviewInformation/index.tsx index edf41823..ae729f36 100644 --- a/src/routes/load/components/ReviewInformation/index.tsx +++ b/src/routes/load/components/ReviewInformation/index.tsx @@ -17,8 +17,9 @@ import { getAccountsFrom } from 'src/routes/open/utils/safeDataExtractor' import { useStyles } from './styles' import { getExplorerInfo } from 'src/config' import { ExplorerButton } from '@gnosis.pm/safe-react-components' +import { LoadFormValues } from 'src/routes/load/container/Load' -const checkIfUserAddressIsAnOwner = (values: Record, userAddress: string): boolean => { +const checkIfUserAddressIsAnOwner = (values: LoadFormValues, userAddress: string): boolean => { let isOwner = false for (let i = 0; i < getNumOwnersFrom(values); i += 1) { @@ -33,7 +34,7 @@ const checkIfUserAddressIsAnOwner = (values: Record, userAddress interface Props { userAddress: string - values: Record + values: LoadFormValues } const ReviewComponent = ({ userAddress, values }: Props): React.ReactElement => { @@ -138,7 +139,7 @@ const ReviewComponent = ({ userAddress, values }: Props): React.ReactElement => } const Review = ({ userAddress }: { userAddress: string }) => - function ReviewPage(controls: React.ReactNode, { values }: { values: Record }): React.ReactElement { + function ReviewPage(controls: React.ReactNode, { values }: { values: LoadFormValues }): React.ReactElement { return ( <> diff --git a/src/routes/load/container/Load.tsx b/src/routes/load/container/Load.tsx index 6e4b5305..1557ba3a 100644 --- a/src/routes/load/container/Load.tsx +++ b/src/routes/load/container/Load.tsx @@ -35,12 +35,24 @@ export const loadSafe = async ( await addSafe(safeProps) } -export interface LoadFormValues { +interface ReviewSafeCreationValues { + confirmations: string + name: string + owner0Address: string + owner0Name: string + safeCreationSalt: number +} + +interface LoadForm { name: string address: string threshold: string + owner0Address: string + owner0Name: string } +export type LoadFormValues = ReviewSafeCreationValues | LoadForm + const Load = (): React.ReactElement => { const dispatch = useDispatch() const provider = useSelector(providerNameSelector) diff --git a/src/routes/open/components/Layout.tsx b/src/routes/open/components/Layout.tsx index 9620c05b..e7e1689d 100644 --- a/src/routes/open/components/Layout.tsx +++ b/src/routes/open/components/Layout.tsx @@ -20,7 +20,7 @@ import { import { WelcomeLayout } from 'src/routes/welcome/components/index' import { history } from 'src/store' import { secondary, sm } from 'src/theme/variables' -import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' +import { providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' import { useSelector } from 'react-redux' import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { getNameFromAddressBook } from 'src/logic/addressBook/utils' @@ -94,7 +94,6 @@ export const Layout = (props: LayoutProps): React.ReactElement => { const { onCallSafeContractSubmit, safeProps } = props const provider = useSelector(providerNameSelector) - const network = useSelector(networkSelector) const userAccount = useSelector(userAccountSelector) useEffect(() => { @@ -107,33 +106,31 @@ export const Layout = (props: LayoutProps): React.ReactElement => { const initialValues = useInitialValuesFrom(userAccount, safeProps) + if (!provider) { + return + } + return ( - <> - {provider ? ( - - - - - - - Create New Safe - - - - - - - - - ) : ( - - )} - + + + + + + + Create New Safe + + + + + + + + ) } diff --git a/src/routes/open/components/ReviewInformation/index.tsx b/src/routes/open/components/ReviewInformation/index.tsx index 4f08ff61..8e899bee 100644 --- a/src/routes/open/components/ReviewInformation/index.tsx +++ b/src/routes/open/components/ReviewInformation/index.tsx @@ -1,7 +1,6 @@ import TableContainer from '@material-ui/core/TableContainer' import classNames from 'classnames' -import React, { useEffect, useState } from 'react' -import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue' +import React, { ReactElement, useEffect, useMemo } from 'react' import { getExplorerInfo, getNetworkInfo } from 'src/config' import CopyBtn from 'src/components/CopyBtn' import Identicon from 'src/components/Identicon' @@ -11,45 +10,41 @@ import Hairline from 'src/components/layout/Hairline' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' import OpenPaper from 'src/components/Stepper/OpenPaper' -import { estimateGasForDeployingSafe } from 'src/logic/contracts/safeContracts' -import { formatAmount } from 'src/logic/tokens/utils/formatAmount' -import { getAccountsFrom, getNamesFrom, getSafeCreationSaltFrom } from 'src/routes/open/utils/safeDataExtractor' +import { + CreateSafeValues, + getAccountsFrom, + getNamesFrom, + getSafeCreationSaltFrom, +} from 'src/routes/open/utils/safeDataExtractor' import { FIELD_CONFIRMATIONS, FIELD_NAME, getNumOwnersFrom } from '../fields' import { useStyles } from './styles' import { ExplorerButton } from '@gnosis.pm/safe-react-components' +import { useEstimateSafeCreationGas } from 'src/logic/hooks/useEstimateSafeCreationGas' +import { FormApi } from 'final-form' +import { StepperPageFormProps } from 'src/components/Stepper' +import { LoadFormValues } from 'src/routes/load/container/Load' type ReviewComponentProps = { - userAccount: string - values: any + values: LoadFormValues + form: FormApi } const { nativeCoin } = getNetworkInfo() -const ReviewComponent = ({ userAccount, values }: ReviewComponentProps) => { +const ReviewComponent = ({ values, form }: ReviewComponentProps): ReactElement => { const classes = useStyles() - const [gasCosts, setGasCosts] = useState('< 0.001') const names = getNamesFrom(values) - const addresses = getAccountsFrom(values) + const addresses = useMemo(() => getAccountsFrom(values), [values]) + const numOwners = getNumOwnersFrom(values) - const safeCreationSalt = getSafeCreationSaltFrom(values) + const safeCreationSalt = getSafeCreationSaltFrom(values as CreateSafeValues) + const { gasCostFormatted, gasLimit } = useEstimateSafeCreationGas({ addresses, numOwners, safeCreationSalt }) useEffect(() => { - const estimateGas = async () => { - if (!addresses.length || !numOwners || !userAccount) { - return - } - const estimatedGasCosts = ( - await estimateGasForDeployingSafe(addresses, numOwners, userAccount, safeCreationSalt) - ).toString() - const gasCosts = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals) - const formattedGasCosts = formatAmount(gasCosts) - setGasCosts(formattedGasCosts) - } - - estimateGas() - }, [addresses, numOwners, safeCreationSalt, userAccount]) + form.mutators.setValue('gasLimit', gasLimit) + }, [gasLimit, form.mutators]) return ( <> @@ -135,8 +130,8 @@ const ReviewComponent = ({ userAccount, values }: ReviewComponentProps) => { You're about to create a new Safe and will have to confirm a transaction with your currently connected - wallet. The creation will cost approximately {gasCosts} {nativeCoin.name}. The exact amount will be determined - by your wallet. + wallet. The creation will cost approximately {gasCostFormatted} {nativeCoin.name}. The exact amount will be + determined by your wallet. @@ -144,7 +139,7 @@ const ReviewComponent = ({ userAccount, values }: ReviewComponentProps) => { } export const Review = () => - function ReviewPage(controls, props): React.ReactElement { + function ReviewPage(controls: React.ReactNode, props: StepperPageFormProps): React.ReactElement { return ( <> diff --git a/src/routes/open/components/fields.ts b/src/routes/open/components/fields.ts index 4df7b36e..87d0f0b7 100644 --- a/src/routes/open/components/fields.ts +++ b/src/routes/open/components/fields.ts @@ -4,13 +4,17 @@ export const FIELD_OWNERS = 'owners' export const FIELD_SAFE_NAME = 'safeName' export const FIELD_CREATION_PROXY_SALT = 'safeCreationSalt' -export const getOwnerNameBy = (index) => `owner${index}Name` -export const getOwnerAddressBy = (index) => `owner${index}Address` +export const getOwnerNameBy = (index: number): string => `owner${index}Name` +export const getOwnerAddressBy = (index: number): string => `owner${index}Address` export const getNumOwnersFrom = (values) => { const accounts = Object.keys(values) .sort() - .filter((key) => /^owner\d+Address$/.test(key) && !!values[key]) + .filter((key) => { + const res = /^owner\d+Address$/.test(key) + + return res && !!values[key] + }) return accounts.length } diff --git a/src/routes/open/container/Open.tsx b/src/routes/open/container/Open.tsx index 9744f4f3..51136910 100644 --- a/src/routes/open/container/Open.tsx +++ b/src/routes/open/container/Open.tsx @@ -6,11 +6,12 @@ import { useLocation } from 'react-router-dom' import { PromiEvent, TransactionReceipt } from 'web3-core' import { SafeDeployment } from 'src/routes/opening' -import { InitialValuesForm, Layout } from 'src/routes/open/components/Layout' +import { Layout } from 'src/routes/open/components/Layout' import Page from 'src/components/layout/Page' import { getSafeDeploymentTransaction } from 'src/logic/contracts/safeContracts' import { checkReceiptStatus } from 'src/logic/wallets/ethTransactions' import { + CreateSafeValues, getAccountsFrom, getNamesFrom, getOwnersFrom, @@ -29,6 +30,8 @@ import { useAnalytics } from 'src/utils/googleAnalytics' const SAFE_PENDING_CREATION_STORAGE_KEY = 'SAFE_PENDING_CREATION_STORAGE_KEY' +type LoadedSafeType = CreateSafeValues & { txHash: string } + interface SafeCreationQueryParams { ownerAddresses: string | string[] | null ownerNames: string | string[] | null @@ -85,7 +88,7 @@ export const getSafeProps = async ( return safeProps } -export const createSafe = (values: InitialValuesForm, userAccount: string): PromiEvent => { +export const createSafe = (values: CreateSafeValues, userAccount: string): PromiEvent => { const confirmations = getThresholdFrom(values) const name = getSafeNameFrom(values) const ownersNames = getNamesFrom(values) @@ -93,7 +96,10 @@ export const createSafe = (values: InitialValuesForm, userAccount: string): Prom const safeCreationSalt = getSafeCreationSaltFrom(values) const deploymentTx = getSafeDeploymentTransaction(ownerAddresses, confirmations, safeCreationSalt) - const promiEvent = deploymentTx.send({ from: userAccount }) + const promiEvent = deploymentTx.send({ + from: userAccount, + gas: values?.gasLimit, + }) promiEvent .once('transactionHash', (txHash) => { @@ -155,28 +161,28 @@ const Open = (): React.ReactElement => { load() }, []) - const createSafeProxy = async (formValues?: InitialValuesForm) => { + const createSafeProxy = async (formValues?: CreateSafeValues) => { let values = formValues // save form values, used when the user rejects the TX and wants to retry - if (formValues) { - const copy = { ...formValues } + if (values) { + const copy = { ...values } saveToStorage(SAFE_PENDING_CREATION_STORAGE_KEY, copy) } else { - values = await loadFromStorage(SAFE_PENDING_CREATION_STORAGE_KEY) + values = (await loadFromStorage(SAFE_PENDING_CREATION_STORAGE_KEY)) as CreateSafeValues } - const promiEvent = createSafe(values as InitialValuesForm, userAccount) + const promiEvent = createSafe(values, userAccount) setCreationTxPromise(promiEvent) setShowProgress(true) } const onSafeCreated = async (safeAddress): Promise => { - const pendingCreation = await loadFromStorage<{ txHash: string }>(SAFE_PENDING_CREATION_STORAGE_KEY) + const pendingCreation = await loadFromStorage(SAFE_PENDING_CREATION_STORAGE_KEY) - const name = getSafeNameFrom(pendingCreation) - const ownersNames = getNamesFrom(pendingCreation) - const ownerAddresses = getAccountsFrom(pendingCreation) + const name = pendingCreation ? getSafeNameFrom(pendingCreation) : '' + const ownersNames = getNamesFrom(pendingCreation as CreateSafeValues) + const ownerAddresses = pendingCreation ? getAccountsFrom(pendingCreation) : [] const safeProps = await getSafeProps(safeAddress, name, ownersNames, ownerAddresses) await dispatch(addOrUpdateSafe(safeProps)) diff --git a/src/routes/open/utils/safeDataExtractor.spec.ts b/src/routes/open/utils/safeDataExtractor.spec.ts index ff1946b3..a4add102 100644 --- a/src/routes/open/utils/safeDataExtractor.spec.ts +++ b/src/routes/open/utils/safeDataExtractor.spec.ts @@ -8,6 +8,9 @@ describe('Test JS', () => { owner1Address: 'bar', owner2Address: 'baz', owners: 3, + confirmations: '0', + name: '', + safeCreationSalt: 0, } expect(getAccountsFrom(safe)).toEqual(['foo', 'bar', 'baz']) @@ -15,9 +18,15 @@ describe('Test JS', () => { it('return the names of owners', () => { const safe = { owner0Name: 'foo', + owner0Address: '0x', owner1Name: 'bar', + owner1Address: '0x', owner2Name: 'baz', + owner2Address: '0x', owners: 3, + confirmations: '0', + name: '', + safeCreationSalt: 0, } expect(getNamesFrom(safe)).toEqual(['foo', 'bar', 'baz']) @@ -31,12 +40,15 @@ describe('Test JS', () => { owner2Name: 'bazName', owner2Address: 'bazAddress', owners: 1, + confirmations: '0', + name: '', + safeCreationSalt: 0, } - expect(getNamesFrom(safe)).toEqual(['fooName']) - expect(getAccountsFrom(safe)).toEqual(['fooAddress']) + expect(getNamesFrom(safe)).toEqual(['fooName', 'barName', 'bazName']) + expect(getAccountsFrom(safe)).toEqual(['fooAddress', 'barAddress', 'bazAddress']) }) - it('return name and address ordered alphabetically', () => { + it('return name and address keys ordered alphabetically', () => { const safe = { owner1Name: 'barName', owner1Address: 'barAddress', @@ -45,14 +57,19 @@ describe('Test JS', () => { owner2Address: 'bazAddress', owner0Address: 'fooAddress', owners: 1, + confirmations: '0', + name: '', + safeCreationSalt: 0, } - expect(getNamesFrom(safe)).toEqual(['fooName']) - expect(getAccountsFrom(safe)).toEqual(['fooAddress']) + expect(getNamesFrom(safe)).toEqual(['fooName', 'barName', 'bazName']) + expect(getAccountsFrom(safe)).toEqual(['fooAddress', 'barAddress', 'bazAddress']) }) it('return the number of required confirmations', () => { const safe = { confirmations: '1', + name: '', + safeCreationSalt: 0, } expect(getThresholdFrom(safe)).toEqual(1) diff --git a/src/routes/open/utils/safeDataExtractor.ts b/src/routes/open/utils/safeDataExtractor.ts index fd380228..e44452c8 100644 --- a/src/routes/open/utils/safeDataExtractor.ts +++ b/src/routes/open/utils/safeDataExtractor.ts @@ -2,31 +2,45 @@ import { List } from 'immutable' import { makeOwner } from 'src/logic/safe/store/models/owner' import { SafeOwner } from 'src/logic/safe/store/models/safe' +import { LoadFormValues } from 'src/routes/load/container/Load' +import { getNumOwnersFrom } from 'src/routes/open/components/fields' -export const getAccountsFrom = (values) => { +export type CreateSafeValues = { + confirmations: string + name: string + owner0Address?: string + owner0Name?: string + safeCreationSalt: number + gasLimit?: number + owners?: number | string +} + +export const getAccountsFrom = (values: CreateSafeValues | LoadFormValues): string[] => { const accounts = Object.keys(values) .sort() .filter((key) => /^owner\d+Address$/.test(key)) - return accounts.map((account) => values[account]).slice(0, values.owners) + const numOwners = getNumOwnersFrom(values) + return accounts.map((account) => values[account]).slice(0, numOwners) } -export const getNamesFrom = (values) => { +export const getNamesFrom = (values: CreateSafeValues | LoadFormValues): string[] => { const accounts = Object.keys(values) .sort() .filter((key) => /^owner\d+Name$/.test(key)) - return accounts.map((account) => values[account]).slice(0, values.owners) + const numOwners = getNumOwnersFrom(values) + return accounts.map((account) => values[account]).slice(0, numOwners) } -export const getOwnersFrom = (names, addresses): List => { +export const getOwnersFrom = (names: string[], addresses: string[]): List => { const owners = names.map((name, index) => makeOwner({ name, address: addresses[index] })) return List(owners) } -export const getThresholdFrom = (values) => Number(values.confirmations) +export const getThresholdFrom = (values: CreateSafeValues): number => Number(values.confirmations) -export const getSafeNameFrom = (values) => values.name +export const getSafeNameFrom = (values: CreateSafeValues): string => values.name -export const getSafeCreationSaltFrom = (values) => values.safeCreationSalt +export const getSafeCreationSaltFrom = (values: CreateSafeValues): number => values.safeCreationSalt From a9f1130a9f572dc4a303c223f8a84ef9176c0bcd Mon Sep 17 00:00:00 2001 From: Mati Dastugue Date: Thu, 4 Mar 2021 16:29:49 -0300 Subject: [PATCH 02/23] [EWC] Enable Wallet connect (#1952) * Enable Wallet connect on EWC --- .github/workflows/deploy-ewc.yml | 2 +- src/config/networks/energy_web_chain.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy-ewc.yml b/.github/workflows/deploy-ewc.yml index 6fa71020..2081c697 100644 --- a/.github/workflows/deploy-ewc.yml +++ b/.github/workflows/deploy-ewc.yml @@ -11,7 +11,7 @@ on: env: REPO_NAME_ALPHANUMERIC: safereact - REACT_APP_NETWORK: 'ewc' + REACT_APP_NETWORK: 'energy_web_chain' STAGING_BUCKET_NAME: ${{ secrets.STAGING_EWC_BUCKET_NAME }} REACT_APP_SENTRY_DSN: ${{ secrets.SENTRY_DSN_EWC }} REACT_APP_GOOGLE_ANALYTICS: ${{ secrets.REACT_APP_GOOGLE_ANALYTICS_ID_EWC }} diff --git a/src/config/networks/energy_web_chain.ts b/src/config/networks/energy_web_chain.ts index 8a340c81..1f908757 100644 --- a/src/config/networks/energy_web_chain.ts +++ b/src/config/networks/energy_web_chain.ts @@ -55,7 +55,6 @@ const mainnet: NetworkConfig = { WALLETS.PORTIS, WALLETS.TORUS, WALLETS.TRUST, - WALLETS.WALLET_CONNECT, WALLETS.WALLET_LINK, WALLETS.AUTHEREUM, WALLETS.LATTICE, From 4fb77297e62f92d7e73c5855daf5388712cdd0d0 Mon Sep 17 00:00:00 2001 From: Mati Dastugue Date: Fri, 5 Mar 2021 00:57:54 -0300 Subject: [PATCH 03/23] Bump safe components version with copy to clipboard fix --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index cde7198c..1709674b 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "@gnosis.pm/safe-apps-sdk": "1.0.3", "@gnosis.pm/safe-apps-sdk-v1": "npm:@gnosis.pm/safe-apps-sdk@0.4.2", "@gnosis.pm/safe-contracts": "1.1.1-dev.2", - "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#fb1a523", + "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#f610327", "@gnosis.pm/util-contracts": "2.0.6", "@ledgerhq/hw-transport-node-hid-singleton": "5.41.0", "@material-ui/core": "^4.11.0", diff --git a/yarn.lock b/yarn.lock index 1962f9c7..2295a735 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1561,9 +1561,9 @@ solc "0.5.14" truffle "^5.1.21" -"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#fb1a523": +"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#f610327": version "0.5.0" - resolved "https://github.com/gnosis/safe-react-components.git#fb1a523ece12aa54e7e6a1169c7cd13da5bf5b61" + resolved "https://github.com/gnosis/safe-react-components.git#f610327c109810547513079196514b05cda63844" dependencies: classnames "^2.2.6" react-media "^1.10.0" @@ -15713,7 +15713,7 @@ polished@^3.3.1: resolved "https://registry.yarnpkg.com/polished/-/polished-3.6.7.tgz#44cbd0047f3187d83db0c479ef0c7d5583af5fb6" integrity sha512-b4OViUOihwV0icb9PHmWbR+vPqaSzSAEbgLskvb7ANPATVXGiYv/TQFHQo65S53WU9i5EQ1I03YDOJW7K0bmYg== dependencies: - "@babel/runtime" "^7.12.5" + "@babel/runtime" "^7.9.2" popper.js@1.16.1-lts: version "1.16.1-lts" From 92a5f7d21d2832235a9c9069d1478701cfd02fd6 Mon Sep 17 00:00:00 2001 From: nicolas Date: Fri, 5 Mar 2021 14:03:30 -0300 Subject: [PATCH 04/23] Set owners and threshold properly (#1979) * set owners and threshold properly * Take threshold from step 2 --- .../Settings/ManageOwners/RemoveOwnerModal/index.tsx | 9 ++++----- .../RemoveOwnerModal/screens/Review/index.tsx | 5 ++++- .../RemoveOwnerModal/screens/ThresholdForm/index.tsx | 11 +++++++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx index a86ab0b4..436e233a 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx @@ -80,7 +80,7 @@ export const RemoveOwnerModal = ({ }: RemoveOwnerProps): React.ReactElement => { const classes = useStyles() const [activeScreen, setActiveScreen] = useState('checkOwner') - const [values, setValues] = useState({}) + const [values, setValues] = useState({ ownerAddress, ownerName, threshold: '' }) const dispatch = useDispatch() const safeAddress = useSelector(safeParamAddressFromStateSelector) const threshold = useSelector(safeThresholdSelector) || 1 @@ -88,7 +88,6 @@ export const RemoveOwnerModal = ({ useEffect( () => () => { setActiveScreen('checkOwner') - setValues({}) }, [isOpen], ) @@ -106,8 +105,8 @@ export const RemoveOwnerModal = ({ } const thresholdSubmitted = (newValues) => { - values.threshold = newValues.threshold - setValues(values) + const cpValues = { ...values, threshold: newValues.threshold } + setValues(cpValues) setActiveScreen('reviewRemoveOwner') } @@ -138,7 +137,7 @@ export const RemoveOwnerModal = ({ onSubmit={onRemoveOwner} ownerAddress={ownerAddress} ownerName={ownerName} - threshold={threshold} + threshold={Number(values.threshold)} /> )} diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx index 5a088416..c69aba52 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx @@ -27,6 +27,7 @@ import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionPara import { styles } from './style' import { TransactionFees } from 'src/components/TransactionsFees' import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters' +import { sameAddress } from 'src/logic/wallets/ethAddresses' export const REMOVE_OWNER_REVIEW_BTN_TEST_ID = 'remove-owner-review-btn' @@ -87,7 +88,7 @@ export const ReviewRemoveOwnerModal = ({ try { const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress) const safeOwners = await gnosisSafe.methods.getOwners().call() - const index = safeOwners.findIndex((owner) => owner.toLowerCase() === ownerAddress.toLowerCase()) + const index = safeOwners.findIndex((owner) => sameAddress(owner, ownerAddress)) const prevAddress = index === 0 ? SENTINEL_ADDRESS : safeOwners[index - 1] const txData = gnosisSafe.methods.removeOwner(prevAddress, ownerAddress, threshold).encodeABI() @@ -141,6 +142,7 @@ export const ReviewRemoveOwnerModal = ({ + {/* Details */} @@ -166,6 +168,7 @@ export const ReviewRemoveOwnerModal = ({ + {/* Owners */} diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/ThresholdForm/index.tsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/ThresholdForm/index.tsx index f58a75c0..a7d66ca3 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/ThresholdForm/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/ThresholdForm/index.tsx @@ -2,7 +2,7 @@ import IconButton from '@material-ui/core/IconButton' import MenuItem from '@material-ui/core/MenuItem' import { makeStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' -import React from 'react' +import React, { ReactElement } from 'react' import { useSelector } from 'react-redux' import { styles } from './style' @@ -18,12 +18,19 @@ import Hairline from 'src/components/layout/Hairline' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' import { safeOwnersSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' +import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters' export const REMOVE_OWNER_THRESHOLD_NEXT_BTN_TEST_ID = 'remove-owner-threshold-next-btn' const useStyles = makeStyles(styles) -const ThresholdForm = ({ onClickBack, onClose, onSubmit }) => { +type Props = { + onClickBack: () => void + onClose: () => void + onSubmit: (txParameters: TxParameters) => void +} + +const ThresholdForm = ({ onClickBack, onClose, onSubmit }: Props): ReactElement => { const classes = useStyles() const owners = useSelector(safeOwnersSelector) const threshold = useSelector(safeThresholdSelector) as number From 0fb02c73332672f4ee8ebdfad01eb5a1d5ae82e8 Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Mon, 8 Mar 2021 09:44:39 +0100 Subject: [PATCH 05/23] Revert "Fix - Copy to clipboard button prevents submit" (#2001) --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1709674b..cde7198c 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "@gnosis.pm/safe-apps-sdk": "1.0.3", "@gnosis.pm/safe-apps-sdk-v1": "npm:@gnosis.pm/safe-apps-sdk@0.4.2", "@gnosis.pm/safe-contracts": "1.1.1-dev.2", - "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#f610327", + "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#fb1a523", "@gnosis.pm/util-contracts": "2.0.6", "@ledgerhq/hw-transport-node-hid-singleton": "5.41.0", "@material-ui/core": "^4.11.0", diff --git a/yarn.lock b/yarn.lock index 2295a735..1962f9c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1561,9 +1561,9 @@ solc "0.5.14" truffle "^5.1.21" -"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#f610327": +"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#fb1a523": version "0.5.0" - resolved "https://github.com/gnosis/safe-react-components.git#f610327c109810547513079196514b05cda63844" + resolved "https://github.com/gnosis/safe-react-components.git#fb1a523ece12aa54e7e6a1169c7cd13da5bf5b61" dependencies: classnames "^2.2.6" react-media "^1.10.0" @@ -15713,7 +15713,7 @@ polished@^3.3.1: resolved "https://registry.yarnpkg.com/polished/-/polished-3.6.7.tgz#44cbd0047f3187d83db0c479ef0c7d5583af5fb6" integrity sha512-b4OViUOihwV0icb9PHmWbR+vPqaSzSAEbgLskvb7ANPATVXGiYv/TQFHQo65S53WU9i5EQ1I03YDOJW7K0bmYg== dependencies: - "@babel/runtime" "^7.9.2" + "@babel/runtime" "^7.12.5" popper.js@1.16.1-lts: version "1.16.1-lts" From 760a2a2b0fb429f22b5281578f6b481caaa9a5a5 Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Mon, 8 Mar 2021 09:46:40 +0100 Subject: [PATCH 06/23] Revert "Revert "Fix - Copy to clipboard button prevents submit" (#2001)" (#2002) This reverts commit 0fb02c73332672f4ee8ebdfad01eb5a1d5ae82e8. --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index cde7198c..1709674b 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "@gnosis.pm/safe-apps-sdk": "1.0.3", "@gnosis.pm/safe-apps-sdk-v1": "npm:@gnosis.pm/safe-apps-sdk@0.4.2", "@gnosis.pm/safe-contracts": "1.1.1-dev.2", - "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#fb1a523", + "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#f610327", "@gnosis.pm/util-contracts": "2.0.6", "@ledgerhq/hw-transport-node-hid-singleton": "5.41.0", "@material-ui/core": "^4.11.0", diff --git a/yarn.lock b/yarn.lock index 1962f9c7..2295a735 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1561,9 +1561,9 @@ solc "0.5.14" truffle "^5.1.21" -"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#fb1a523": +"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#f610327": version "0.5.0" - resolved "https://github.com/gnosis/safe-react-components.git#fb1a523ece12aa54e7e6a1169c7cd13da5bf5b61" + resolved "https://github.com/gnosis/safe-react-components.git#f610327c109810547513079196514b05cda63844" dependencies: classnames "^2.2.6" react-media "^1.10.0" @@ -15713,7 +15713,7 @@ polished@^3.3.1: resolved "https://registry.yarnpkg.com/polished/-/polished-3.6.7.tgz#44cbd0047f3187d83db0c479ef0c7d5583af5fb6" integrity sha512-b4OViUOihwV0icb9PHmWbR+vPqaSzSAEbgLskvb7ANPATVXGiYv/TQFHQo65S53WU9i5EQ1I03YDOJW7K0bmYg== dependencies: - "@babel/runtime" "^7.12.5" + "@babel/runtime" "^7.9.2" popper.js@1.16.1-lts: version "1.16.1-lts" From f4f79f0303d603874499911ba27539eed430f7be Mon Sep 17 00:00:00 2001 From: Richard Meissner Date: Mon, 8 Mar 2021 09:58:02 +0100 Subject: [PATCH 07/23] Allow off-chain signing and collectibles with 1.0.0 safes (#1966) --- src/logic/safe/transactions/offchainSigner/index.ts | 2 +- src/logic/safe/utils/safeVersion.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logic/safe/transactions/offchainSigner/index.ts b/src/logic/safe/transactions/offchainSigner/index.ts index 08e04803..4a1e3b6b 100644 --- a/src/logic/safe/transactions/offchainSigner/index.ts +++ b/src/logic/safe/transactions/offchainSigner/index.ts @@ -17,7 +17,7 @@ const SIGNERS = { const getSignersByWallet = (isHW) => isHW ? [SIGNERS.ETH_SIGN] : [SIGNERS.EIP712_V3, SIGNERS.EIP712_V4, SIGNERS.EIP712, SIGNERS.ETH_SIGN] -export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.1.1' +export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.0.0' export const tryOffchainSigning = async (safeTxHash: string, txArgs, isHW: boolean): Promise => { let signature diff --git a/src/logic/safe/utils/safeVersion.ts b/src/logic/safe/utils/safeVersion.ts index 1c085225..116d2068 100644 --- a/src/logic/safe/utils/safeVersion.ts +++ b/src/logic/safe/utils/safeVersion.ts @@ -14,7 +14,7 @@ type FeatureConfigByVersion = { } const FEATURES_BY_VERSION: FeatureConfigByVersion[] = [ - { name: FEATURES.ERC721, validVersion: '>=1.1.1' }, + { name: FEATURES.ERC721 }, { name: FEATURES.ERC1155, validVersion: '>=1.1.1' }, { name: FEATURES.SAFE_APPS }, { name: FEATURES.CONTRACT_INTERACTION }, From 4c37a2b7c1bb87527a795add14dd57ff6bbb9772 Mon Sep 17 00:00:00 2001 From: nicolas Date: Mon, 8 Mar 2021 06:08:28 -0300 Subject: [PATCH 08/23] Handle 404 responses for txs queue and history (#1990) --- .../loadGatewayTransactions.ts | 47 +++++++++++++------ .../HistoryTransactions.tsx | 17 +++++-- .../GatewayTransactions/QueueTransactions.tsx | 11 +---- .../GatewayTransactions/styled.tsx | 5 ++ 4 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/logic/safe/store/actions/transactions/fetchTransactions/loadGatewayTransactions.ts b/src/logic/safe/store/actions/transactions/fetchTransactions/loadGatewayTransactions.ts index 15c002c5..1530da4b 100644 --- a/src/logic/safe/store/actions/transactions/fetchTransactions/loadGatewayTransactions.ts +++ b/src/logic/safe/store/actions/transactions/fetchTransactions/loadGatewayTransactions.ts @@ -42,15 +42,25 @@ export const loadPagedHistoryTransactions = async ( export const loadHistoryTransactions = async (safeAddress: string): Promise => { const historyTransactionsUrl = getHistoryTransactionsUrl(safeAddress) - const { - data: { results, ...pointers }, - } = await axios.get>(historyTransactionsUrl) + try { + const { + data: { results, ...pointers }, + } = await axios.get>(historyTransactionsUrl) - if (!historyPointers[safeAddress]) { - historyPointers[safeAddress] = pointers + if (!historyPointers[safeAddress]) { + historyPointers[safeAddress] = pointers + } + + return results + } catch (error) { + // When the safe is just created there is a delay until the gateway recognize the + // safe address, when that happens it returns 404. + if (error.response.status === 404) { + return [] + } + + throw Error(`There was an error trying to fetch history txs from safeAddress ${safeAddress}`) } - - return results } /************/ @@ -90,14 +100,23 @@ export const loadPagedQueuedTransactions = async ( export const loadQueuedTransactions = async (safeAddress: string): Promise => { const queuedTransactionsUrl = getQueuedTransactionsUrl(safeAddress) + try { + const { + data: { results, ...pointers }, + } = await axios.get>(queuedTransactionsUrl) - const { - data: { results, ...pointers }, - } = await axios.get>(queuedTransactionsUrl) + if (!queuedPointers[safeAddress] || queuedPointers[safeAddress].next === null) { + queuedPointers[safeAddress] = pointers + } - if (!queuedPointers[safeAddress] || queuedPointers[safeAddress].next === null) { - queuedPointers[safeAddress] = pointers + return results + } catch (error) { + // When the safe is just created there is a delay until the gateway recognize the + // safe address, when that happens it returns 404. + if (error.response.status === 404) { + return [] + } + + throw Error(`There was an error trying to fetch queued txs from safeAddress ${safeAddress}`) } - - return results } diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/HistoryTransactions.tsx b/src/routes/safe/components/Transactions/GatewayTransactions/HistoryTransactions.tsx index 00be4d7b..9bba9e11 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/HistoryTransactions.tsx +++ b/src/routes/safe/components/Transactions/GatewayTransactions/HistoryTransactions.tsx @@ -1,15 +1,17 @@ -import { Loader } from '@gnosis.pm/safe-react-components' +import { Loader, Title } from '@gnosis.pm/safe-react-components' import React, { ReactElement } from 'react' import { usePagedHistoryTransactions } from './hooks/usePagedHistoryTransactions' -import { Centered } from './styled' +import { Centered, NoTransactions } from './styled' import { HistoryTxList } from './HistoryTxList' import { TxsInfiniteScroll } from './TxsInfiniteScroll' +import Img from 'src/components/layout/Img' +import NoTransactionsImage from './assets/no-transactions.svg' export const HistoryTransactions = (): ReactElement => { const { count, hasMore, next, transactions, isLoading } = usePagedHistoryTransactions() - if (count === 0) { + if (isLoading || !transactions) { return ( @@ -17,6 +19,15 @@ export const HistoryTransactions = (): ReactElement => { ) } + if (count === 0) { + return ( + + No Transactions yet + History transactions will appear here + + ) + } + return ( diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/QueueTransactions.tsx b/src/routes/safe/components/Transactions/GatewayTransactions/QueueTransactions.tsx index f5e55f62..7c6ee471 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/QueueTransactions.tsx +++ b/src/routes/safe/components/Transactions/GatewayTransactions/QueueTransactions.tsx @@ -1,23 +1,16 @@ import { Loader, Title } from '@gnosis.pm/safe-react-components' import React, { ReactElement } from 'react' -import style from 'styled-components' import Img from 'src/components/layout/Img' import { ActionModal } from './ActionModal' import NoTransactionsImage from './assets/no-transactions.svg' import { usePagedQueuedTransactions } from './hooks/usePagedQueuedTransactions' import { QueueTxList } from './QueueTxList' -import { Centered } from './styled' +import { Centered, NoTransactions } from './styled' import { TxActionProvider } from './TxActionProvider' import { TxsInfiniteScroll } from './TxsInfiniteScroll' import { TxLocationContext } from './TxLocationProvider' -const NoTransactions = style.div` - display: flex; - flex-direction: column; - margin-top: 60px; -` - export const QueueTransactions = (): ReactElement => { const { count, isLoading, hasMore, next, transactions } = usePagedQueuedTransactions() @@ -35,7 +28,7 @@ export const QueueTransactions = (): ReactElement => { return ( No Transactions yet - Transactions will appear here + Queue transactions will appear here ) } diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/styled.tsx b/src/routes/safe/components/Transactions/GatewayTransactions/styled.tsx index 10afad0f..de8d7f19 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/styled.tsx +++ b/src/routes/safe/components/Transactions/GatewayTransactions/styled.tsx @@ -503,3 +503,8 @@ export const AlignItemsWithMargin = styled.div` margin-right: 6px; } ` +export const NoTransactions = styled.div` + display: flex; + flex-direction: column; + margin-top: 60px; +` From 7312c99cdb966c856e88836ced3258bc14ff4c16 Mon Sep 17 00:00:00 2001 From: nicolas Date: Mon, 8 Mar 2021 11:44:39 -0300 Subject: [PATCH 09/23] set gas to 0 when rejecting a tx. (#1993) --- .../Transactions/GatewayTransactions/modals/RejectTxModal.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/modals/RejectTxModal.tsx b/src/routes/safe/components/Transactions/GatewayTransactions/modals/RejectTxModal.tsx index 97637461..3426ba5a 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/modals/RejectTxModal.tsx +++ b/src/routes/safe/components/Transactions/GatewayTransactions/modals/RejectTxModal.tsx @@ -46,7 +46,6 @@ export const RejectTxModal = ({ isOpen, onClose, gwTransaction }: Props): React. isOffChainSignature, isCreation, gasLimit, - gasEstimation, gasPriceFormatted, } = useEstimateTransactionGas({ txData: EMPTY_DATA, @@ -85,7 +84,7 @@ export const RejectTxModal = ({ isOpen, onClose, gwTransaction }: Props): React. From 4b9e6c7bfe32bc19977f25e2a0a82ea0f81db66e Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Tue, 9 Mar 2021 09:27:35 +0100 Subject: [PATCH 10/23] [Chore] Cleanup old tx list leftovers (#1996) * Cleanup old transaction list files * Rename view folder from GatewayTransactions to TxList * Remove all reducer logic for incomingTransactions * Delete allTransactionsLogic * Cleanup cancel transaction legacy logic * Remove fetch creation transaction * Remove moduleTransactions logic * Remove unused transfer methods * Cleanup unused types --- .../collectibles/store/selectors/index.ts | 4 - src/logic/collectibles/utils/index.ts | 23 - src/logic/safe/hooks/useLoadSafe.tsx | 2 - .../__tests__/transactionHelpers.test.ts | 782 ------------------ .../store/actions/__tests__/utils.test.ts | 237 ++++-- .../store/actions/addIncomingTransactions.ts | 5 - .../store/actions/addModuleTransactions.ts | 13 - .../allTransactions/loadAllTransactions.ts | 86 -- .../actions/allTransactions/pagination.ts | 14 - .../safe/store/actions/fetchSafeCreationTx.ts | 55 -- .../addOrUpdateCancellationTransactions.ts | 5 - .../transactions/addOrUpdateTransactions.ts | 5 - .../fetchTransactions/fetchTransactions.ts | 69 -- .../loadIncomingTransactions.ts | 103 --- .../loadModuleTransactions.ts | 35 - .../loadOutgoingTransactions.ts | 177 +--- .../transactions/pendingTransactions.ts | 169 ---- .../removeCancellationTransaction.ts | 5 - .../actions/transactions/removeTransaction.ts | 5 - .../utils/addMockSafeCreationTx.ts | 27 - .../utils/newTransactionsHelpers.ts | 11 - .../transactions/utils/transactionHelpers.ts | 315 +------ .../middleware/notificationsMiddleware.ts | 64 +- .../safe/store/models/incomingTransaction.ts | 45 - src/logic/safe/store/models/transaction.ts | 57 -- .../safe/store/models/types/transaction.ts | 25 +- .../safe/store/models/types/transactions.d.ts | 67 -- .../safe/store/reducer/allTransactions.ts | 37 - .../store/reducer/cancellationTransactions.ts | 68 -- .../store/reducer/incomingTransactions.ts | 14 - .../safe/store/reducer/moduleTransactions.ts | 32 - src/logic/safe/store/reducer/transactions.ts | 78 -- src/logic/safe/store/reducer/types/safe.d.ts | 0 .../safe/store/selectors/allTransactions.ts | 22 - src/logic/safe/store/selectors/index.ts | 62 +- .../safe/store/selectors/transactions.ts | 18 - .../safe/transactions/awaitingTransactions.ts | 26 +- .../safe/transactions/incomingTxHistory.ts | 7 - .../safe/transactions/moduleTxHistory.ts | 9 - .../safe/utils/buildSafeCreationTxUrl.ts | 7 - src/logic/tokens/utils/tokenHelpers.ts | 18 +- .../wallets/store/actions/fetchProvider.ts | 2 - .../SendModal/screens/ChooseTxType/index.tsx | 2 +- .../GatewayTransactions/HexEncodedData.tsx | 13 - .../ActionModal.tsx | 0 .../AddressInfo.tsx | 0 .../Transactions/TxList/HexEncodedData.tsx | 68 ++ .../HistoryTransactions.tsx | 0 .../HistoryTxList.tsx | 2 +- .../InfoDetails.tsx | 0 .../MethodDetails.tsx | 2 +- .../Value.tsx => TxList/MethodValue.tsx} | 0 .../MultiSendDetails.tsx | 0 .../OwnerRow.tsx | 0 .../QueueTransactions.tsx | 0 .../QueueTxList.tsx | 0 .../SpendingLimitDetails.tsx | 0 .../TokenTransferAmount.tsx | 0 .../TxActionProvider.tsx | 0 .../TxCollapsed.tsx | 0 .../TxCollapsedActions.tsx | 0 .../TxData.tsx | 0 .../TxDetails.tsx | 0 .../TxExpandedActions.tsx | 2 +- .../TxHistoryCollapsed.tsx | 0 .../TxHistoryRow.tsx | 0 .../TxHoverProvider.tsx | 0 .../TxInfo.tsx | 0 .../TxInfoCreation.tsx | 0 .../TxInfoDetails.tsx | 0 .../TxInfoSettings.tsx | 0 .../TxInfoTransfer.tsx | 0 .../TxLocationProvider.tsx | 0 .../TxOwners.tsx | 0 .../TxQueueCollapsed.tsx | 0 .../TxQueueRow.tsx | 2 +- .../TxSummary.tsx | 0 .../TxsInfiniteScroll.tsx | 0 .../assets/check-circle-green.svg | 0 .../assets/custom.svg | 0 .../assets/incoming.svg | 0 .../assets/no-transactions.svg | 0 .../assets/outgoing.svg | 0 .../assets/plus-circle-green.svg | 0 .../assets/settings.svg | 0 .../assets/transactions-list-active.svg | 0 .../assets/transactions-list-inactive.svg | 0 .../hooks/useActionButtonsHandlers.ts | 8 +- .../hooks/useAssetInfo.ts | 3 +- .../hooks/useHistoryTransactions.ts | 0 .../hooks/usePagedHistoryTransactions.ts | 2 +- .../hooks/usePagedQueuedTransactions.ts | 0 .../hooks/useQueueTransactions.ts | 0 .../hooks/useTransactionActions.ts | 4 +- .../hooks/useTransactionDetails.ts | 2 +- .../hooks/useTransactionStatus.ts | 2 +- .../hooks/useTransactionType.ts | 10 +- .../{GatewayTransactions => TxList}/index.tsx | 0 .../modals/ApproveTxModal.tsx | 2 +- .../modals/RejectTxModal.tsx | 0 .../modals/style.ts | 0 .../styled.tsx | 0 .../{GatewayTransactions => TxList}/utils.ts | 21 - .../ExpandedTx/ApproveTxModal/index.tsx | 242 ------ .../ExpandedTx/ApproveTxModal/style.ts | 37 - .../TxsTable/ExpandedTx/CreationTx/index.tsx | 71 -- .../TxsTable/ExpandedTx/IncomingTx/index.tsx | 20 - .../IncomingTxDescription/index.tsx | 46 -- .../TxsTable/ExpandedTx/OutgoingTx/index.tsx | 52 -- .../OwnersColumn/OwnerComponent.tsx | 194 ----- .../ExpandedTx/OwnersColumn/OwnersList.tsx | 42 - .../assets/cancel-small-filled.svg | 3 - .../assets/check-large-filled-green.svg | 6 - .../assets/check-large-filled-red.svg | 6 - .../assets/confirm-large-green.svg | 3 - .../assets/confirm-large-grey.svg | 3 - .../OwnersColumn/assets/confirm-large-red.svg | 3 - .../assets/confirm-small-filled.svg | 3 - .../assets/confirm-small-green.svg | 3 - .../assets/confirm-small-grey.svg | 3 - .../OwnersColumn/assets/confirm-small-red.svg | 3 - .../assets/confirm-small-yellow.svg | 3 - .../ExpandedTx/OwnersColumn/index.tsx | 271 ------ .../TxsTable/ExpandedTx/OwnersColumn/style.ts | 111 --- .../ExpandedTx/RejectTxModal/index.tsx | 151 ---- .../ExpandedTx/RejectTxModal/style.ts | 37 - .../TxDescription/CustomDescription.tsx | 335 -------- .../TxDescription/SettingsDescription.tsx | 153 ---- .../TxDescription/TransferDescription.tsx | 65 -- .../ExpandedTx/TxDescription/index.tsx | 56 -- .../ExpandedTx/TxDescription/styles.ts | 43 - .../ExpandedTx/TxDescription/utils.ts | 180 ---- .../TxsTable/ExpandedTx/index.tsx | 217 ----- .../Transactions/TxsTable/ExpandedTx/style.ts | 29 - .../TxsTable/TxType/assets/custom.svg | 3 - .../TxsTable/TxType/assets/incoming.svg | 3 - .../TxsTable/TxType/assets/outgoing.svg | 3 - .../TxsTable/TxType/assets/settings.svg | 3 - .../Transactions/TxsTable/TxType/index.tsx | 80 -- .../Transactions/TxsTable/columns.tsx | 252 ------ .../Transactions/TxsTable/test/column.test.ts | 30 - .../safe/container/hooks/useTransactions.ts | 35 - src/routes/safe/container/index.tsx | 10 +- src/routes/safe/container/selector.ts | 67 +- .../transactions/__tests__/utils.test.ts | 158 ---- .../utils/multiSendDecodedDetails.ts | 64 -- .../utils/newTransactionHelpers.ts | 25 - .../transactions/utils/transferDetails.d.ts | 50 -- .../transactions/utils/transferDetails.ts | 85 -- .../safe/store/models/types/transactions.d.ts | 255 ------ src/store/index.ts | 26 +- src/test/safe.dom.funds.thresholdGt1.ts | 78 -- src/test/safe.dom.settings.owners.ts | 242 ------ src/test/utils/historyServiceHelper.ts | 3 - src/test/utils/safeHelper.ts | 3 +- src/test/utils/transactions/index.ts | 3 +- .../transactions/transactionList.helper.ts | 64 -- 157 files changed, 258 insertions(+), 6625 deletions(-) delete mode 100644 src/logic/safe/store/actions/addIncomingTransactions.ts delete mode 100644 src/logic/safe/store/actions/addModuleTransactions.ts delete mode 100644 src/logic/safe/store/actions/allTransactions/loadAllTransactions.ts delete mode 100644 src/logic/safe/store/actions/allTransactions/pagination.ts delete mode 100644 src/logic/safe/store/actions/fetchSafeCreationTx.ts delete mode 100644 src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions.ts delete mode 100644 src/logic/safe/store/actions/transactions/addOrUpdateTransactions.ts delete mode 100644 src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions.ts delete mode 100644 src/logic/safe/store/actions/transactions/fetchTransactions/loadIncomingTransactions.ts delete mode 100644 src/logic/safe/store/actions/transactions/fetchTransactions/loadModuleTransactions.ts delete mode 100644 src/logic/safe/store/actions/transactions/pendingTransactions.ts delete mode 100644 src/logic/safe/store/actions/transactions/removeCancellationTransaction.ts delete mode 100644 src/logic/safe/store/actions/transactions/removeTransaction.ts delete mode 100644 src/logic/safe/store/actions/transactions/utils/addMockSafeCreationTx.ts delete mode 100644 src/logic/safe/store/actions/transactions/utils/newTransactionsHelpers.ts delete mode 100644 src/logic/safe/store/models/incomingTransaction.ts delete mode 100644 src/logic/safe/store/models/transaction.ts delete mode 100644 src/logic/safe/store/reducer/allTransactions.ts delete mode 100644 src/logic/safe/store/reducer/cancellationTransactions.ts delete mode 100644 src/logic/safe/store/reducer/incomingTransactions.ts delete mode 100644 src/logic/safe/store/reducer/moduleTransactions.ts delete mode 100644 src/logic/safe/store/reducer/transactions.ts delete mode 100644 src/logic/safe/store/reducer/types/safe.d.ts delete mode 100644 src/logic/safe/store/selectors/allTransactions.ts delete mode 100644 src/logic/safe/store/selectors/transactions.ts delete mode 100644 src/logic/safe/transactions/incomingTxHistory.ts delete mode 100644 src/logic/safe/transactions/moduleTxHistory.ts delete mode 100644 src/logic/safe/utils/buildSafeCreationTxUrl.ts delete mode 100644 src/routes/safe/components/Transactions/GatewayTransactions/HexEncodedData.tsx rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/ActionModal.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/AddressInfo.tsx (100%) create mode 100644 src/routes/safe/components/Transactions/TxList/HexEncodedData.tsx rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/HistoryTransactions.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/HistoryTxList.tsx (96%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/InfoDetails.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/MethodDetails.tsx (93%) rename src/routes/safe/components/Transactions/{TxsTable/ExpandedTx/TxDescription/Value.tsx => TxList/MethodValue.tsx} (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/MultiSendDetails.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/OwnerRow.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/QueueTransactions.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/QueueTxList.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/SpendingLimitDetails.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TokenTransferAmount.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxActionProvider.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxCollapsed.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxCollapsedActions.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxData.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxDetails.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxExpandedActions.tsx (95%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxHistoryCollapsed.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxHistoryRow.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxHoverProvider.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxInfo.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxInfoCreation.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxInfoDetails.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxInfoSettings.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxInfoTransfer.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxLocationProvider.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxOwners.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxQueueCollapsed.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxQueueRow.tsx (95%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxSummary.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/TxsInfiniteScroll.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/check-circle-green.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/custom.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/incoming.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/no-transactions.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/outgoing.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/plus-circle-green.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/settings.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/transactions-list-active.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/assets/transactions-list-inactive.svg (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useActionButtonsHandlers.ts (94%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useAssetInfo.ts (93%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useHistoryTransactions.ts (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/usePagedHistoryTransactions.ts (96%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/usePagedQueuedTransactions.ts (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useQueueTransactions.ts (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useTransactionActions.ts (97%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useTransactionDetails.ts (96%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useTransactionStatus.ts (98%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/hooks/useTransactionType.ts (92%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/index.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/modals/ApproveTxModal.tsx (99%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/modals/RejectTxModal.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/modals/style.ts (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/styled.tsx (100%) rename src/routes/safe/components/Transactions/{GatewayTransactions => TxList}/utils.ts (85%) delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/style.ts delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/CreationTx/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTx/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OutgoingTx/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnersList.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/cancel-small-filled.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-green.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-red.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-green.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-grey.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-red.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-filled.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-green.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-grey.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-red.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-yellow.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/style.ts delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/style.ts delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/styles.ts delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts delete mode 100644 src/routes/safe/components/Transactions/TxsTable/TxType/assets/custom.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/TxType/assets/incoming.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/TxType/assets/outgoing.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/TxType/assets/settings.svg delete mode 100644 src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/columns.tsx delete mode 100644 src/routes/safe/components/Transactions/TxsTable/test/column.test.ts delete mode 100644 src/routes/safe/container/hooks/useTransactions.ts delete mode 100644 src/routes/safe/store/actions/transactions/__tests__/utils.test.ts delete mode 100644 src/routes/safe/store/actions/transactions/utils/multiSendDecodedDetails.ts delete mode 100644 src/routes/safe/store/actions/transactions/utils/newTransactionHelpers.ts delete mode 100644 src/routes/safe/store/actions/transactions/utils/transferDetails.d.ts delete mode 100644 src/routes/safe/store/actions/transactions/utils/transferDetails.ts delete mode 100644 src/routes/safe/store/models/types/transactions.d.ts delete mode 100644 src/test/safe.dom.funds.thresholdGt1.ts delete mode 100644 src/test/safe.dom.settings.owners.ts delete mode 100644 src/test/utils/transactions/transactionList.helper.ts diff --git a/src/logic/collectibles/store/selectors/index.ts b/src/logic/collectibles/store/selectors/index.ts index d6737a6a..1c5e6412 100644 --- a/src/logic/collectibles/store/selectors/index.ts +++ b/src/logic/collectibles/store/selectors/index.ts @@ -16,10 +16,6 @@ export const nftAssetsListSelector = createSelector(nftAssets, (assets): NFTAsse return assets ? Object.values(assets) : [] }) -export const nftAssetsListAddressesSelector = createSelector(nftAssetsListSelector, (assets): string[] => { - return Array.from(new Set(assets.map((nftAsset) => nftAsset.address))) -}) - export const availableNftAssetsAddresses = createSelector(nftTokensSelector, (userNftTokens): string[] => { return Array.from(new Set(userNftTokens.map((nftToken) => nftToken.assetAddress))) }) diff --git a/src/logic/collectibles/utils/index.ts b/src/logic/collectibles/utils/index.ts index 5eae89c2..830c17aa 100644 --- a/src/logic/collectibles/utils/index.ts +++ b/src/logic/collectibles/utils/index.ts @@ -1,13 +1,8 @@ import { getNetworkId, getNetworkInfo } from 'src/config' import { ETHEREUM_NETWORK } from 'src/config/networks/network.d' -import { nftAssetsListAddressesSelector } from 'src/logic/collectibles/store/selectors' -import { BuildTx, ServiceTx } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' -import { TOKEN_TRANSFER_METHODS_NAMES } from 'src/logic/safe/store/models/types/transactions.d' import { getERC721TokenContract, getStandardTokenContract } from 'src/logic/tokens/store/actions/fetchTokens' import { sameAddress } from 'src/logic/wallets/ethAddresses' import { CollectibleTx } from 'src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible' -import { store } from 'src/store' -import { sameString } from 'src/utils/strings' // CryptoKitties Contract Addresses by network // This is an exception made for a popular NFT that's not ERC721 standard-compatible, @@ -29,24 +24,6 @@ const ENS_CONTRACT_ADDRESS = { // safeTransferFrom(address,address,uint256) export const SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH = '42842e0e' -/** - * Verifies that a tx received by the transaction service is an ERC721 token-related transaction - * @param {BuildTx['tx']} tx - * @returns boolean - */ -export const isSendERC721Transaction = (tx: BuildTx['tx']): boolean => { - let hasERC721Transfer = false - - if (tx.dataDecoded && sameString(tx.dataDecoded.method, TOKEN_TRANSFER_METHODS_NAMES.SAFE_TRANSFER_FROM)) { - hasERC721Transfer = tx.dataDecoded.parameters.findIndex((param) => sameString(param.name, 'tokenId')) !== -1 - } - - // Note: this is only valid with our current case (client rendering), if we move to server side rendering we need to refactor this - const state = store.getState() - const knownAssets = nftAssetsListAddressesSelector(state) - return knownAssets.includes((tx as ServiceTx).to) || hasERC721Transfer -} - /** * Returns the symbol of the provided ERC721 contract * @param {string} contractAddress diff --git a/src/logic/safe/hooks/useLoadSafe.tsx b/src/logic/safe/hooks/useLoadSafe.tsx index d847d2a9..116bf24c 100644 --- a/src/logic/safe/hooks/useLoadSafe.tsx +++ b/src/logic/safe/hooks/useLoadSafe.tsx @@ -7,7 +7,6 @@ import fetchSafeTokens from 'src/logic/tokens/store/actions/fetchSafeTokens' import fetchLatestMasterContractVersion from 'src/logic/safe/store/actions/fetchLatestMasterContractVersion' import fetchSafe from 'src/logic/safe/store/actions/fetchSafe' import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTransactions' -import fetchSafeCreationTx from 'src/logic/safe/store/actions/fetchSafeCreationTx' import { Dispatch } from 'src/logic/safe/store/actions/types.d' export const useLoadSafe = (safeAddress?: string): boolean => { @@ -21,7 +20,6 @@ export const useLoadSafe = (safeAddress?: string): boolean => { await dispatch(fetchSafe(safeAddress)) setIsSafeLoaded(true) await dispatch(fetchSafeTokens(safeAddress)) - dispatch(fetchSafeCreationTx(safeAddress)) dispatch(fetchTransactions(safeAddress)) dispatch(addViewedSafe(safeAddress)) } diff --git a/src/logic/safe/store/actions/__tests__/transactionHelpers.test.ts b/src/logic/safe/store/actions/__tests__/transactionHelpers.test.ts index 027f6cd8..36b84fff 100644 --- a/src/logic/safe/store/actions/__tests__/transactionHelpers.test.ts +++ b/src/logic/safe/store/actions/__tests__/transactionHelpers.test.ts @@ -1,275 +1,15 @@ import { getMockedSafeInstance, getMockedTxServiceModel } from 'src/test/utils/safeHelper' -import { makeTransaction } from 'src/logic/safe/store/models/transaction' -import { TransactionStatus, TransactionTypes } from 'src/logic/safe/store/models/types/transaction' -import makeSafe from 'src/logic/safe/store/models/safe' -import { List, Map, Record } from 'immutable' -import { makeToken, TokenProps } from 'src/logic/tokens/store/model/token' -import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' -import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { - buildTx, - calculateTransactionStatus, - calculateTransactionType, generateSafeTxHash, getRefundParams, - isCancelTransaction, - isCustomTransaction, - isInnerTransaction, - isModifySettingsTransaction, isMultiSendTransaction, - isOutgoingTransaction, - isPendingTransaction, isUpgradeTransaction, } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' import { getERC20DecimalsAndSymbol } from 'src/logic/tokens/utils/tokenHelpers' -import { DELEGATE_CALL } from 'src/logic/safe/transactions' const safeAddress = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf' const safeAddress2 = '0x344B941b1aAE2e4Be73987212FC4741687Bf0503' -describe('isInnerTransaction', () => { - it('It should return true if the transaction recipient is our given safeAddress and the txValue is 0', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0' }) - - // when - const result = isInnerTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - }) - it('It should return false if the transaction recipient is our given safeAddress and the txValue is >0', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '100' }) - - // when - const result = isInnerTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction recipient is not our given safeAddress', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0' }) - - // when - const result = isInnerTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return true if the transaction recipient is the given safeAddress and the txValue is 0', () => { - // given - const transaction = makeTransaction({ recipient: safeAddress, value: '0' }) - - // when - const result = isInnerTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - }) - it('It should return false if the transaction recipient is the given safeAddress and the txValue is >0', () => { - // given - const transaction = makeTransaction({ recipient: safeAddress, value: '100' }) - - // when - const result = isInnerTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction recipient is not the given safeAddress', () => { - // given - const transaction = makeTransaction({ recipient: safeAddress2, value: '100' }) - - // when - const result = isInnerTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) -}) - -describe.skip('isCancelTransaction', () => { - const safeAddress = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf' - const mockedETHAccount = '0xd76e0B566e218a80F4c96458FE09a322EBAa9aF2' - it('It should return false if given a inner transaction with empty data', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: null }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - }) - it('It should return false if given a inner transaction without empty data', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: 'test' }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction recipient is not the safeAddress', () => { - // given - const transaction = getMockedTxServiceModel({ to: mockedETHAccount, value: '0', data: null }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction value is not empty', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '100', data: null }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction data is not empty', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: mockedETHAccount }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction operation is not call', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: null, operation: DELEGATE_CALL }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction baseGas is not empty', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: null, baseGas: 10 }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction gasPrice is not empty', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: null, gasPrice: '10' }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction gasToken is not empty', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: null, gasToken: mockedETHAccount }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the refundReceiver is not empty', () => { - // given - const transaction = getMockedTxServiceModel({ - to: safeAddress, - value: '0', - data: null, - refundReceiver: mockedETHAccount, - }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return true for a transaction with everything empty except for to parameter equals to the safeAddress,', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress }) - - // when - const result = isCancelTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - }) -}) - -describe('isPendingTransaction', () => { - it('It should return true if the transaction is on pending status', () => { - // given - const transaction = makeTransaction({ status: TransactionStatus.PENDING }) - const cancelTx = makeTransaction({ data: null }) - - // when - const result = isPendingTransaction(transaction, cancelTx) - - // then - expect(result).toBe(true) - }) - it('It should return true If the transaction is not pending status but the cancellation transaction is', () => { - // given - const transaction = makeTransaction({ status: TransactionStatus.AWAITING_CONFIRMATIONS }) - const cancelTx = makeTransaction({ status: TransactionStatus.PENDING }) - - // when - const result = isPendingTransaction(transaction, cancelTx) - - // then - expect(result).toBe(true) - }) - it('It should return true If the transaction and a cancellation transaction are not pending', () => { - // given - const transaction = makeTransaction({ status: TransactionStatus.CANCELLED }) - const cancelTx = makeTransaction({ status: TransactionStatus.AWAITING_CONFIRMATIONS }) - - // when - const result = isPendingTransaction(transaction, cancelTx) - - // then - expect(result).toBe(false) - }) -}) - -describe('isModifySettingsTransaction', () => { - const safeAddress = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf' - it('It should return true if given an inner transaction without empty data', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: 'test' }) - - // when - const result = isModifySettingsTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - }) - it('It should return false if given an inner transaction with empty data', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: null }) - - // when - const result = isModifySettingsTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) -}) - describe('isMultiSendTransaction', () => { it('It should return true if given a transaction without value, the data has multisend data', () => { // given @@ -337,160 +77,6 @@ describe('isUpgradeTransaction', () => { }) }) -describe('isOutgoingTransaction', () => { - it('It should return true if the transaction recipient is not a safe address and data is not empty', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0', data: 'test' }) - - // when - const result = isOutgoingTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - }) - it('It should return true if the transaction has an address equal to the safe address', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: 'test' }) - - // when - const result = isOutgoingTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) - it('It should return false if the transaction recipient is not a safe address and data is empty', () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress, value: '0', data: null }) - - // when - const result = isOutgoingTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - }) -}) - -jest.mock('src/logic/collectibles/utils') -jest.mock('src/logic/tokens/utils/tokenHelpers') -describe('isCustomTransaction', () => { - afterAll(() => { - jest.unmock('src/logic/collectibles/utils') - jest.unmock('src/logic/tokens/utils/tokenHelpers') - }) - it('It should return true if Is outgoing transaction, is not an erc20 transaction, not an upgrade transaction and not and erc721 transaction', async () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0', data: 'test' }) - const knownTokens = Map & Readonly>() - const token = makeToken({ - address: '0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', - name: 'OmiseGo', - symbol: 'OMG', - decimals: 18, - logoUri: - 'https://github.com/TrustWallet/tokens/blob/master/images/0x6810e776880c02933d47db1b9fc05908e5386b96.png?raw=true', - }) - knownTokens.set('0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', token) - - const collectiblesHelpers = require('src/logic/collectibles/utils') - const txHelpers = require('src/logic/tokens/utils/tokenHelpers') - - txHelpers.isSendERC20Transaction.mockImplementationOnce(() => false) - collectiblesHelpers.isSendERC721Transaction.mockImplementationOnce(() => false) - - // when - const result = await isCustomTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - expect(txHelpers.isSendERC20Transaction).toHaveBeenCalled() - expect(collectiblesHelpers.isSendERC721Transaction).toHaveBeenCalled() - }) - it('It should return true if is outgoing transaction, is not SendERC20Transaction, is not isUpgradeTransaction and not isSendERC721Transaction', async () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0', data: 'test' }) - const knownTokens = Map & Readonly>() - const token = makeToken({ - address: '0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', - name: 'OmiseGo', - symbol: 'OMG', - decimals: 18, - logoUri: - 'https://github.com/TrustWallet/tokens/blob/master/images/0x6810e776880c02933d47db1b9fc05908e5386b96.png?raw=true', - }) - knownTokens.set('0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', token) - - const collectiblesHelpers = require('src/logic/collectibles/utils') - const txHelpers = require('src/logic/tokens/utils/tokenHelpers') - - txHelpers.isSendERC20Transaction.mockImplementationOnce(() => false) - collectiblesHelpers.isSendERC721Transaction.mockImplementationOnce(() => false) - - // when - const result = await isCustomTransaction(transaction, safeAddress) - - // then - expect(result).toBe(true) - expect(txHelpers.isSendERC20Transaction).toHaveBeenCalled() - expect(collectiblesHelpers.isSendERC721Transaction).toHaveBeenCalled() - }) - it('It should return false if is outgoing transaction, not SendERC20Transaction, isUpgradeTransaction and not isSendERC721Transaction', async () => { - // given - const upgradeTxData = `0x8d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f200dfa693da0d16f5e7e78fdcbede8fc6ebea44f1cf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000247de7edef000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cdbdf4400dfa693da0d16f5e7e78fdcbede8fc6ebea44f1cf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f08a032300000000000000000000000034cfac646f301356faa8b21e94227e3583fe3f5f0000000000000000000000000000` - const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0', data: upgradeTxData }) - const knownTokens = Map & Readonly>() - const token = makeToken({ - address: '0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', - name: 'OmiseGo', - symbol: 'OMG', - decimals: 18, - logoUri: - 'https://github.com/TrustWallet/tokens/blob/master/images/0x6810e776880c02933d47db1b9fc05908e5386b96.png?raw=true', - }) - knownTokens.set('0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', token) - - const collectiblesHelpers = require('src/logic/collectibles/utils') - const txHelpers = require('src/logic/tokens/utils/tokenHelpers') - - txHelpers.isSendERC20Transaction.mockImplementationOnce(() => true) - collectiblesHelpers.isSendERC721Transaction.mockImplementationOnce(() => false) - - // when - const result = await isCustomTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - expect(txHelpers.isSendERC20Transaction).toHaveBeenCalled() - }) - it('It should return false if is outgoing transaction, is not SendERC20Transaction, not isUpgradeTransaction and isSendERC721Transaction', async () => { - // given - const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0', data: 'test' }) - const knownTokens = Map & Readonly>() - const token = makeToken({ - address: '0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', - name: 'OmiseGo', - symbol: 'OMG', - decimals: 18, - logoUri: - 'https://github.com/TrustWallet/tokens/blob/master/images/0x6810e776880c02933d47db1b9fc05908e5386b96.png?raw=true', - }) - knownTokens.set('0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', token) - - const collectiblesHelpers = require('src/logic/collectibles/utils') - const txHelpers = require('src/logic/tokens/utils/tokenHelpers') - - txHelpers.isSendERC20Transaction.mockImplementationOnce(() => false) - collectiblesHelpers.isSendERC721Transaction.mockImplementationOnce(() => true) - - // when - const result = await isCustomTransaction(transaction, safeAddress) - - // then - expect(result).toBe(false) - expect(txHelpers.isSendERC20Transaction).toHaveBeenCalled() - expect(collectiblesHelpers.isSendERC721Transaction).toHaveBeenCalled() - }) -}) - describe('getRefundParams', () => { it('It should return null if given a transaction with the gasPrice == 0', async () => { // given @@ -588,374 +174,6 @@ describe('getRefundParams', () => { }) }) -describe('getDecodedParams', () => { - it('', () => { - // given - // when - // then - }) -}) - -describe('isTransactionCancelled', () => { - it('', () => { - // given - // when - // then - }) -}) - -describe('calculateTransactionStatus', () => { - it('It should return SUCCESS if the tx is executed and successful', () => { - // given - const transaction = makeTransaction({ isExecuted: true, isSuccessful: true }) - const safe = makeSafe() - const currentUser = safeAddress - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.SUCCESS) - }) - it('It should return CANCELLED if the tx is cancelled and successful', () => { - // given - const transaction = makeTransaction({ cancelled: true }) - const safe = makeSafe() - const currentUser = safeAddress - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.CANCELLED) - }) - it('It should return AWAITING_EXECUTION if the tx has an amount of confirmations equal to the safe threshold', () => { - // given - const makeUser = Record({ - owner: '', - type: '', - hash: '', - signature: '', - }) - const transaction = makeTransaction({ cancelled: true, confirmations: List([makeUser(), makeUser(), makeUser()]) }) - const safe = makeSafe({ threshold: 3 }) - const currentUser = safeAddress - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.CANCELLED) - }) - it('It should return SUCCESS if the tx is the creation transaction', () => { - // given - const transaction = makeTransaction({ creationTx: true, confirmations: List() }) - const safe = makeSafe({ threshold: 3 }) - const currentUser = safeAddress - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.SUCCESS) - }) - it('It should return PENDING if the tx is pending', () => { - // given - const transaction = makeTransaction({ confirmations: List(), isPending: true }) - const safe = makeSafe({ threshold: 3 }) - const currentUser = safeAddress - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.PENDING) - }) - it('It should return AWAITING_CONFIRMATIONS if the tx has confirmations bellow the threshold, the user is owner and signed', () => { - // given - const userAddress = 'address1' - const userAddress2 = 'address2' - const makeUser = Record({ - owner: '', - type: '', - hash: '', - signature: '', - }) - const transaction = makeTransaction({ confirmations: List([makeUser({ owner: userAddress })]) }) - const safe = makeSafe({ - threshold: 3, - owners: List([ - { name: '', address: userAddress }, - { name: '', address: userAddress2 }, - ]), - }) - const currentUser = userAddress - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.AWAITING_CONFIRMATIONS) - }) - it('It should return AWAITING_YOUR_CONFIRMATION if the tx has confirmations bellow the threshold, the user is owner and not signed', () => { - // given - const userAddress = 'address1' - const userAddress2 = 'address2' - const makeUser = Record({ - owner: '', - type: '', - hash: '', - signature: '', - }) - - const transaction = makeTransaction({ confirmations: List([makeUser({ owner: userAddress })]) }) - const safe = makeSafe({ - threshold: 3, - owners: List([ - { name: '', address: userAddress }, - { name: '', address: userAddress2 }, - ]), - }) - const currentUser = userAddress2 - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.AWAITING_YOUR_CONFIRMATION) - }) - it('It should return AWAITING_CONFIRMATIONS if the tx has confirmations bellow the threshold, the user is not owner', () => { - // given - const userAddress = 'address1' - const userAddress2 = 'address2' - const makeUser = Record({ - owner: '', - type: '', - hash: '', - signature: '', - }) - - const transaction = makeTransaction({ confirmations: List([makeUser({ owner: userAddress })]) }) - const safe = makeSafe({ threshold: 3, owners: List([{ name: '', address: userAddress }]) }) - const currentUser = userAddress2 - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.AWAITING_CONFIRMATIONS) - }) - it('It should return FAILED if the tx is not successful', () => { - // given - const userAddress = 'address1' - const userAddress2 = 'address2' - const makeUser = Record({ - owner: '', - type: '', - hash: '', - signature: '', - }) - - const transaction = makeTransaction({ - confirmations: List([makeUser({ owner: userAddress })]), - isSuccessful: false, - }) - const safe = makeSafe({ threshold: 3, owners: List([{ name: '', address: userAddress }]) }) - const currentUser = userAddress2 - - // when - const result = calculateTransactionStatus(transaction, safe, currentUser) - - // then - expect(result).toBe(TransactionStatus.FAILED) - }) -}) - -describe('calculateTransactionType', () => { - it('It should return TOKEN If the tx is a token transfer transaction', () => { - // given - const transaction = makeTransaction({ isTokenTransfer: true }) - - // when - const result = calculateTransactionType(transaction) - - // then - expect(result).toBe(TransactionTypes.TOKEN) - }) - it('It should return COLLECTIBLE If the tx is a collectible transfer transaction', () => { - // given - const transaction = makeTransaction({ isCollectibleTransfer: true }) - - // when - const result = calculateTransactionType(transaction) - - // then - expect(result).toBe(TransactionTypes.COLLECTIBLE) - }) - it('It should return SETTINGS If the tx is a modifySettings transaction', () => { - // given - const transaction = makeTransaction({ modifySettingsTx: true }) - - // when - const result = calculateTransactionType(transaction) - - // then - expect(result).toBe(TransactionTypes.SETTINGS) - }) - - it('It should return CANCELLATION If the tx is a cancellation transaction', () => { - // given - const transaction = makeTransaction({ isCancellationTx: true }) - - // when - const result = calculateTransactionType(transaction) - - // then - expect(result).toBe(TransactionTypes.CANCELLATION) - }) - - it('It should return CUSTOM If the tx is a custom transaction', () => { - // given - const transaction = makeTransaction({ customTx: true }) - - // when - const result = calculateTransactionType(transaction) - - // then - expect(result).toBe(TransactionTypes.CUSTOM) - }) - it('It should return CUSTOM If the tx is a creation transaction', () => { - // given - const transaction = makeTransaction({ creationTx: true }) - - // when - const result = calculateTransactionType(transaction) - - // then - expect(result).toBe(TransactionTypes.CREATION) - }) - it('It should return UPGRADE If the tx is an upgrade transaction', () => { - // given - const transaction = makeTransaction({ upgradeTx: true }) - - // when - const result = calculateTransactionType(transaction) - - // then - expect(result).toBe(TransactionTypes.UPGRADE) - }) -}) - -describe('buildTx', () => { - it('Returns a valid transaction', async () => { - // given - const cancelTx1 = { - baseGas: 0, - blockNumber: 0, - confirmations: [], - confirmationsRequired: 2, - data: null, - dataDecoded: undefined, - ethGasPrice: '0', - executionDate: null, - executor: '', - fee: '', - gasPrice: '', - gasToken: '', - gasUsed: 0, - isExecuted: false, - isSuccessful: true, - modified: '', - nonce: 0, - operation: 0, - origin: null, - refundReceiver: '', - safe: '', - safeTxGas: 0, - safeTxHash: '', - signatures: '', - submissionDate: null, - to: '', - transactionHash: null, - value: '', - } - const transaction = getMockedTxServiceModel({ to: safeAddress2, value: '0' }) - const userAddress = 'address1' - const cancellationTxs = List([cancelTx1]) - const token = makeToken({ - address: '0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', - name: 'OmiseGo', - symbol: 'OMG', - decimals: 18, - logoUri: - 'https://github.com/TrustWallet/tokens/blob/master/images/0x6810e776880c02933d47db1b9fc05908e5386b96.png?raw=true', - }) - const knownTokens = Map & Readonly>() - knownTokens.set('0x00Df91984582e6e96288307E9c2f20b38C8FeCE9', token) - const outgoingTxs = [cancelTx1] - const safeInstance = makeSafe({ name: 'LOADED SAFE', address: safeAddress }) - const expectedTx = makeTransaction({ - baseGas: 0, - blockNumber: 0, - cancelled: false, - confirmations: List([]), - creationTx: false, - customTx: false, - data: EMPTY_DATA, - dataDecoded: null, - decimals: 18, - decodedParams: null, - executionDate: '', - executionTxHash: '', - executor: '', - gasPrice: '', - gasToken: ZERO_ADDRESS, - isCancellationTx: false, - isCollectibleTransfer: false, - isExecuted: false, - isSuccessful: false, - isTokenTransfer: false, - modifySettingsTx: false, - multiSendTx: false, - nonce: 0, - operation: 0, - origin: '', - recipient: safeAddress2, - refundParams: null, - refundReceiver: ZERO_ADDRESS, - safeTxGas: 0, - safeTxHash: '', - setupData: '', - status: TransactionStatus.FAILED, - submissionDate: '', - symbol: 'ETH', - upgradeTx: false, - value: '0', - fee: '', - }) - - // when - const txResult = await buildTx({ - cancellationTxs, - currentUser: userAddress, - outgoingTxs, - safe: safeInstance, - tx: transaction, - }) - - // then - expect(txResult).toStrictEqual(expectedTx) - }) -}) - -describe('updateStoredTransactionsStatus', () => { - it('', () => { - // given - // when - // then - }) -}) - describe('generateSafeTxHash', () => { it('It should return a safe transaction hash', () => { // given diff --git a/src/logic/safe/store/actions/__tests__/utils.test.ts b/src/logic/safe/store/actions/__tests__/utils.test.ts index 549b93eb..82579f99 100644 --- a/src/logic/safe/store/actions/__tests__/utils.test.ts +++ b/src/logic/safe/store/actions/__tests__/utils.test.ts @@ -1,99 +1,158 @@ -import { getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils' -import { GnosisSafe } from 'src/types/contracts/GnosisSafe.d' -import { TxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' -import { getMockedSafeInstance } from 'src/test/utils/safeHelper' -import { NonPayableTransactionObject } from 'src/types/contracts/types' +import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils' +import { getMockedSafeInstance, getMockedTxServiceModel } from 'src/test/utils/safeHelper' +import axios from 'axios' +import { buildTxServiceUrl } from 'src/logic/safe/transactions' -describe('Store actions utils > getNewTxNonce', () => { - it(`Should return nonce of a last transaction + 1 if passed nonce is less than last transaction or invalid`, async () => { - // Given - const lastTx = { nonce: 44 } as TxServiceModel - const safeInstance = { - methods: { - nonce: () => ({ - call: () => Promise.resolve('45'), - }), - }, - } +describe('shouldExecuteTransaction', () => { + it('It should return false if given a safe with a threshold > 1', async () => { + // given + const nonce = '0' + const threshold = '2' + const safeInstance = getMockedSafeInstance({ threshold }) + const lastTx = getMockedTxServiceModel({}) - // When - const nonce = await getNewTxNonce(lastTx, safeInstance as GnosisSafe) + // when + const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) - // Then - expect(nonce).toBe('45') + // then + expect(result).toBe(false) }) + it('It should return true if given a safe with a threshold === 1 and the previous transaction is already executed', async () => { + // given + const nonce = '1' + const threshold = '1' + const safeInstance = getMockedSafeInstance({ threshold, nonce }) + const lastTx = getMockedTxServiceModel({}) - it(`Should retrieve contract's instance nonce value as a fallback, if txNonce and lastTx are not valid`, async () => { - // Given + // when + const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) + + // then + expect(result).toBe(true) + }) + it('It should return true if given a safe with a threshold === 1 and the previous transaction is already executed', async () => { + // given + const nonce = '10' + const threshold = '1' + const safeInstance = getMockedSafeInstance({ threshold, nonce }) + const lastTx = getMockedTxServiceModel({ isExecuted: true }) + + // when + const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) + + // then + expect(result).toBe(true) + }) + it('It should return false if given a safe with a threshold === 1 and the previous transaction is not yet executed', async () => { + // given + const nonce = '10' + const threshold = '1' + const safeInstance = getMockedSafeInstance({ threshold }) + const lastTx = getMockedTxServiceModel({ isExecuted: false }) + + // when + const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) + + // then + expect(result).toBe(false) + }) +}) + +describe('getNewTxNonce', () => { + it('It should return 2 if given the last transaction with nonce 1', async () => { + // given + const safeInstance = getMockedSafeInstance({}) + const lastTx = getMockedTxServiceModel({ nonce: 1 }) + const expectedResult = '2' + + // when + const result = await getNewTxNonce(lastTx, safeInstance) + + // then + expect(result).toBe(expectedResult) + }) + it('It should return 0 if given a safe with nonce 0 and no transactions should use safe contract instance for retrieving nonce', async () => { + // given + const safeNonce = '0' + const safeInstance = getMockedSafeInstance({ nonce: safeNonce }) + const expectedResult = '0' + const mockFnCall = jest.fn().mockImplementation(() => safeNonce) + const mockFnNonce = jest.fn().mockImplementation(() => ({ call: mockFnCall })) + + safeInstance.methods.nonce = mockFnNonce + + // when + const result = await getNewTxNonce(null, safeInstance) + + // then + expect(result).toBe(expectedResult) + expect(mockFnNonce).toHaveBeenCalled() + expect(mockFnCall).toHaveBeenCalled() + mockFnNonce.mockRestore() + mockFnCall.mockRestore() + }) + it('Given a Safe and the last transaction, should return nonce of the last transaction + 1', async () => { + // given + const safeInstance = getMockedSafeInstance({}) + const expectedResult = '11' + const lastTx = getMockedTxServiceModel({ nonce: 10 }) + + // when + const result = await getNewTxNonce(lastTx, safeInstance) + + // then + expect(result).toBe(expectedResult) + }) +}) + +jest.mock('axios') +jest.mock('console') +describe('getLastTx', () => { + afterAll(() => { + jest.unmock('axios') + jest.unmock('console') + }) + const safeAddress = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf' + it('It should return the last transaction for a given a safe address', async () => { + // given + const lastTx = getMockedTxServiceModel({ nonce: 1 }) + const url = buildTxServiceUrl(safeAddress) + + // when + // @ts-ignore + axios.get.mockImplementationOnce(() => { + return { + data: { + results: [lastTx], + }, + } + }) + + const result = await getLastTx(safeAddress) + + // then + expect(result).toStrictEqual(lastTx) + expect(axios.get).toHaveBeenCalled() + expect(axios.get).toBeCalledWith(url, { params: { limit: 1 } }) + }) + it('If should return null If catches an error getting last transaction', async () => { + // given const lastTx = null - const safeInstance = { - methods: { - nonce: () => ({ - call: () => Promise.resolve('45'), - }), - }, - } + const url = buildTxServiceUrl(safeAddress) - // When - const nonce = await getNewTxNonce(lastTx, safeInstance as GnosisSafe) + // when + // @ts-ignore + axios.get.mockImplementationOnce(() => { + throw new Error() + }) + console.error = jest.fn() + const result = await getLastTx(safeAddress) + const spyConsole = jest.spyOn(console, 'error').mockImplementation() - // Then - expect(nonce).toBe('45') - }) -}) - -describe('Store actions utils > shouldExecuteTransaction', () => { - it(`should return false if there's a previous tx pending to be executed`, async () => { - // Given - const safeInstance = getMockedSafeInstance({}) - safeInstance.methods.getThreshold = () => - ({ - call: () => Promise.resolve('1'), - } as NonPayableTransactionObject) - - const nonce = '1' - const lastTx = { isExecuted: false } as TxServiceModel - - // When - const isExecution = await shouldExecuteTransaction(safeInstance as GnosisSafe, nonce, lastTx) - - // Then - expect(isExecution).toBeFalsy() - }) - - it(`should return false if threshold is greater than 1`, async () => { - // Given - const safeInstance = getMockedSafeInstance({}) - safeInstance.methods.getThreshold = () => - ({ - call: () => Promise.resolve('2'), - } as NonPayableTransactionObject) - - const nonce = '1' - const lastTx = { isExecuted: true } as TxServiceModel - - // When - const isExecution = await shouldExecuteTransaction(safeInstance as GnosisSafe, nonce, lastTx) - - // Then - expect(isExecution).toBeFalsy() - }) - - it(`should return true is threshold is 1 and previous tx is executed`, async () => { - // Given - const safeInstance = getMockedSafeInstance({ nonce: '1' }) - safeInstance.methods.getThreshold = () => - ({ - call: () => Promise.resolve('1'), - } as NonPayableTransactionObject) - - const nonce = '1' - const lastTx = { isExecuted: true } as TxServiceModel - - // When - const isExecution = await shouldExecuteTransaction(safeInstance as GnosisSafe, nonce, lastTx) - - // Then - expect(isExecution).toBeTruthy() + // then + expect(result).toStrictEqual(lastTx) + expect(axios.get).toHaveBeenCalled() + expect(axios.get).toBeCalledWith(url, { params: { limit: 1 } }) + expect(spyConsole).toHaveBeenCalled() }) }) diff --git a/src/logic/safe/store/actions/addIncomingTransactions.ts b/src/logic/safe/store/actions/addIncomingTransactions.ts deleted file mode 100644 index 012d6455..00000000 --- a/src/logic/safe/store/actions/addIncomingTransactions.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createAction } from 'redux-actions' - -export const ADD_INCOMING_TRANSACTIONS = 'ADD_INCOMING_TRANSACTIONS' - -export const addIncomingTransactions = createAction(ADD_INCOMING_TRANSACTIONS) diff --git a/src/logic/safe/store/actions/addModuleTransactions.ts b/src/logic/safe/store/actions/addModuleTransactions.ts deleted file mode 100644 index 6c26cb59..00000000 --- a/src/logic/safe/store/actions/addModuleTransactions.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createAction } from 'redux-actions' -import { ModuleTxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadModuleTransactions' - -export const ADD_MODULE_TRANSACTIONS = 'ADD_MODULE_TRANSACTIONS' - -export type AddModuleTransactionsAction = { - payload: { - safeAddress: string - modules: ModuleTxServiceModel[] - } -} - -export const addModuleTransactions = createAction(ADD_MODULE_TRANSACTIONS) diff --git a/src/logic/safe/store/actions/allTransactions/loadAllTransactions.ts b/src/logic/safe/store/actions/allTransactions/loadAllTransactions.ts deleted file mode 100644 index bbb4c25b..00000000 --- a/src/logic/safe/store/actions/allTransactions/loadAllTransactions.ts +++ /dev/null @@ -1,86 +0,0 @@ -import axios, { AxiosResponse } from 'axios' - -import { getSafeServiceBaseUrl } from 'src/config' -import { checksumAddress } from 'src/utils/checksumAddress' -import { Transaction } from 'src/logic/safe/store/models/types/transactions.d' - -export type ServiceUriParams = { - safeAddress: string - limit: number - offset: number - orderBy?: string // todo: maybe this should be key of MultiSigTransaction | keyof EthereumTransaction - queued?: boolean - trusted?: boolean -} - -type TransactionDTO = { - count: number - next?: string - previous?: string - results: Transaction[] -} - -const getAllTransactionsUri = (safeAddress: string): string => { - const address = checksumAddress(safeAddress) - return `${getSafeServiceBaseUrl(address)}/all-transactions/` -} - -const fetchAllTransactions = async ( - urlParams: ServiceUriParams, - eTag?: string, -): Promise<{ responseEtag?: string; results: Transaction[]; count?: number }> => { - const { safeAddress, limit, offset, orderBy, queued, trusted } = urlParams - try { - const url = getAllTransactionsUri(safeAddress) - - const config = { - params: { - limit, - offset, - orderBy, - queued, - trusted, - }, - headers: eTag ? { 'If-None-Match': eTag } : undefined, - } - - const response: AxiosResponse = await axios.get(url, config) - - if (response.data.count > 0) { - const { etag } = response.headers - - if (eTag !== etag) { - return { - responseEtag: etag, - results: response.data.results, - count: response.data.count, - } - } - } - } catch (err) { - if (!(err && err.response && err.response.status === 304)) { - console.error(`Requests for outgoing transactions for ${safeAddress || 'unknown'} failed with 404`, err) - } else { - // NOTE: this is the expected implementation, currently the backend is not returning 304. - // So I check if the returned etag is the same instead (see above) - } - } - return { responseEtag: eTag, results: [] } -} - -const etagsByPage = {} -export const loadAllTransactions = async ( - uriParams: ServiceUriParams, -): Promise<{ - transactions: Transaction[] - totalTransactionsAmount?: number -}> => { - const previousEtag = etagsByPage && etagsByPage[uriParams.offset] - const { responseEtag, results, count } = await fetchAllTransactions(uriParams, previousEtag) - etagsByPage[uriParams.offset] = responseEtag - - return { - transactions: results, - totalTransactionsAmount: count, - } -} diff --git a/src/logic/safe/store/actions/allTransactions/pagination.ts b/src/logic/safe/store/actions/allTransactions/pagination.ts deleted file mode 100644 index a1b2b237..00000000 --- a/src/logic/safe/store/actions/allTransactions/pagination.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createAction } from 'redux-actions' -import { Transaction } from '../../models/types/transactions.d' - -export const LOAD_MORE_TRANSACTIONS = 'LOAD_MORE_TRANSACTIONS' - -export type LoadMoreTransactionsAction = { - payload: { - safeAddress: string - transactions: Transaction[] - totalTransactionsAmount: number - } -} - -export const loadMore = createAction(LOAD_MORE_TRANSACTIONS) diff --git a/src/logic/safe/store/actions/fetchSafeCreationTx.ts b/src/logic/safe/store/actions/fetchSafeCreationTx.ts deleted file mode 100644 index 6504d226..00000000 --- a/src/logic/safe/store/actions/fetchSafeCreationTx.ts +++ /dev/null @@ -1,55 +0,0 @@ -import axios from 'axios' -import { List } from 'immutable' - -import { buildSafeCreationTxUrl } from 'src/logic/safe/utils/buildSafeCreationTxUrl' -import { addOrUpdateTransactions } from './transactions/addOrUpdateTransactions' -import { makeTransaction } from 'src/logic/safe/store/models/transaction' -import { TransactionTypes, TransactionStatus } from 'src/logic/safe/store/models/types/transaction' -import { web3ReadOnly } from 'src/logic/wallets/getWeb3' - -const getCreationTx = async (safeAddress) => { - const url = buildSafeCreationTxUrl(safeAddress) - const response = await axios.get(url) - return { - ...response.data, - creationTx: true, - nonce: null, - } -} - -const fetchSafeCreationTx = (safeAddress) => async (dispatch) => { - if (!safeAddress) return - const creationTxFetched = await getCreationTx(safeAddress) - - const { - created, - creationTx, - creator, - factoryAddress, - masterCopy, - setupData, - transactionHash, - type, - } = creationTxFetched - const txType = type || TransactionTypes.CREATION - const safeTxHash = web3ReadOnly.utils.toHex('this is the creation transaction') - - const creationTxAsRecord = makeTransaction({ - created, - creator, - factoryAddress, - masterCopy, - nonce: -1, - setupData, - creationTx, - executionTxHash: transactionHash, - type: txType, - safeTxHash, - status: TransactionStatus.SUCCESS, - submissionDate: created, - }) - - dispatch(addOrUpdateTransactions({ safeAddress, transactions: List([creationTxAsRecord]) })) -} - -export default fetchSafeCreationTx diff --git a/src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions.ts b/src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions.ts deleted file mode 100644 index cf156eed..00000000 --- a/src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createAction } from 'redux-actions' - -export const ADD_OR_UPDATE_CANCELLATION_TRANSACTIONS = 'ADD_OR_UPDATE_CANCELLATION_TRANSACTIONS' - -export const addOrUpdateCancellationTransactions = createAction(ADD_OR_UPDATE_CANCELLATION_TRANSACTIONS) diff --git a/src/logic/safe/store/actions/transactions/addOrUpdateTransactions.ts b/src/logic/safe/store/actions/transactions/addOrUpdateTransactions.ts deleted file mode 100644 index 8d6cb275..00000000 --- a/src/logic/safe/store/actions/transactions/addOrUpdateTransactions.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createAction } from 'redux-actions' - -export const ADD_OR_UPDATE_TRANSACTIONS = 'ADD_OR_UPDATE_TRANSACTIONS' - -export const addOrUpdateTransactions = createAction(ADD_OR_UPDATE_TRANSACTIONS) diff --git a/src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions.ts b/src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions.ts deleted file mode 100644 index a1cf32df..00000000 --- a/src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions.ts +++ /dev/null @@ -1,69 +0,0 @@ -import axios from 'axios' - -import { buildTxServiceUrl } from 'src/logic/safe/transactions' -import { buildIncomingTxServiceUrl } from 'src/logic/safe/transactions/incomingTxHistory' -import { buildModuleTxServiceUrl } from 'src/logic/safe/transactions/moduleTxHistory' -import { TxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' -import { IncomingTxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadIncomingTransactions' -import { TransactionTypes } from 'src/logic/safe/store/models/types/transaction' -import { ModuleTxServiceModel } from './loadModuleTransactions' - -const getServiceUrl = (txType: string, safeAddress: string): string => { - return { - [TransactionTypes.INCOMING]: buildIncomingTxServiceUrl, - [TransactionTypes.OUTGOING]: buildTxServiceUrl, - [TransactionTypes.MODULE]: buildModuleTxServiceUrl, - }[txType](safeAddress) -} - -// TODO: Remove this magic -/* eslint-disable */ -async function fetchTransactions( - txType: TransactionTypes.MODULE, - safeAddress: string, - eTag: string | null, -): Promise<{ eTag: string | null; results: ModuleTxServiceModel[] }> -async function fetchTransactions( - txType: TransactionTypes.INCOMING, - safeAddress: string, - eTag: string | null, -): Promise<{ eTag: string | null; results: IncomingTxServiceModel[] }> -async function fetchTransactions( - txType: TransactionTypes.OUTGOING, - safeAddress: string, - eTag: string | null, -): Promise<{ eTag: string | null; results: TxServiceModel[] }> -async function fetchTransactions( - txType: TransactionTypes.MODULE | TransactionTypes.INCOMING | TransactionTypes.OUTGOING, - safeAddress: string, - eTag: string | null, -): Promise<{ eTag: string | null; results: ModuleTxServiceModel[] | TxServiceModel[] | IncomingTxServiceModel[] }> { - /* eslint-enable */ - try { - const url = getServiceUrl(txType, safeAddress) - const response = await axios.get(url, eTag ? { headers: { 'If-None-Match': eTag } } : undefined) - - if (response.data.count > 0) { - const { etag } = response.headers - - if (eTag !== etag) { - return { - eTag: etag, - results: response.data.results, - } - } - } - } catch (err) { - if (!(err && err.response && err.response.status === 304)) { - console.error(`Requests for outgoing transactions for ${safeAddress || 'unknown'} failed with 404`, err) - } else { - // NOTE: this is the expected implementation, currently the backend is not returning 304. - // So I check if the returned etag is the same instead (see above) - } - } - - // defaults to an empty array to avoid type errors - return { eTag, results: [] } -} - -export default fetchTransactions diff --git a/src/logic/safe/store/actions/transactions/fetchTransactions/loadIncomingTransactions.ts b/src/logic/safe/store/actions/transactions/fetchTransactions/loadIncomingTransactions.ts deleted file mode 100644 index 2a8d8bab..00000000 --- a/src/logic/safe/store/actions/transactions/fetchTransactions/loadIncomingTransactions.ts +++ /dev/null @@ -1,103 +0,0 @@ -import bn from 'bignumber.js' -import { List, Map } from 'immutable' -import { Transaction, TransactionReceipt } from 'web3-core' -import { AbiItem } from 'web3-utils' - -import { getNetworkInfo } from 'src/config' -import generateBatchRequests from 'src/logic/contracts/generateBatchRequests' -import { ALTERNATIVE_TOKEN_ABI } from 'src/logic/tokens/utils/alternativeAbi' -import { web3ReadOnly } from 'src/logic/wallets/getWeb3' -import { makeIncomingTransaction } from 'src/logic/safe/store/models/incomingTransaction' -import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions' -import { TransactionTypes } from 'src/logic/safe/store/models/types/transaction' -import { isENSContract } from 'src/logic/collectibles/utils' - -export type IncomingTxServiceModel = { - blockNumber: number - transactionHash: string - to: string - value: number - tokenAddress: string - from: string -} - -const buildIncomingTransactionFrom = ([tx, symbol, decimals, fee]: [ - IncomingTxServiceModel, - string, - number, - string, -]) => { - // this is a particular treatment for the DCD token, as it seems to lack of symbol and decimal methods - if (tx.tokenAddress && tx.tokenAddress.toLowerCase() === '0xe0b7927c4af23765cb51314a0e0521a9645f0e2a') { - symbol = 'DCD' - decimals = 9 - } - - const { transactionHash, ...incomingTx } = tx - - return makeIncomingTransaction({ - ...incomingTx, - symbol, - decimals, - fee, - executionTxHash: transactionHash, - safeTxHash: transactionHash, - }) -} - -const batchIncomingTxsTokenDataRequest = (txs: IncomingTxServiceModel[]) => { - const batch = new web3ReadOnly.BatchRequest() - const { nativeCoin } = getNetworkInfo() - - const whenTxsValues = txs.map((tx) => { - const methods = [ - 'symbol', - 'decimals', - { method: 'getTransaction', args: [tx.transactionHash], type: 'eth' }, - { method: 'getTransactionReceipt', args: [tx.transactionHash], type: 'eth' }, - ] - - return generateBatchRequests< - [ - IncomingTxServiceModel, - string | undefined, - string | undefined, - Transaction | undefined, - TransactionReceipt | undefined, - ] - >({ - abi: ALTERNATIVE_TOKEN_ABI as AbiItem[], - address: tx.tokenAddress, - batch, - context: tx, - methods, - }) - }) - - batch.execute() - - return Promise.all(whenTxsValues).then((txsValues) => - txsValues.map(([tx, symbolFetched, decimals, ethTx, ethTxReceipt]) => { - let symbol = symbolFetched - if (!symbolFetched) { - symbol = isENSContract(tx.tokenAddress) ? 'ENS' : nativeCoin.symbol - } - return [ - tx, - symbol, - decimals ? decimals : nativeCoin.decimals, - new bn(ethTx?.gasPrice ?? 0).times(ethTxReceipt?.gasUsed ?? 0), - ] - }), - ) -} - -let previousETag: string | null = null -export const loadIncomingTransactions = async (safeAddress: string): Promise>> => { - const { eTag, results } = await fetchTransactions(TransactionTypes.INCOMING, safeAddress, previousETag) - previousETag = eTag - - const incomingTxsWithData = await batchIncomingTxsTokenDataRequest(results) - const incomingTxsRecord = incomingTxsWithData.map(buildIncomingTransactionFrom) - return Map({ [safeAddress]: List(incomingTxsRecord) }) -} diff --git a/src/logic/safe/store/actions/transactions/fetchTransactions/loadModuleTransactions.ts b/src/logic/safe/store/actions/transactions/fetchTransactions/loadModuleTransactions.ts deleted file mode 100644 index 7dd1b7b7..00000000 --- a/src/logic/safe/store/actions/transactions/fetchTransactions/loadModuleTransactions.ts +++ /dev/null @@ -1,35 +0,0 @@ -import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions' -import { TransactionTypes } from 'src/logic/safe/store/models/types/transaction' -import { DataDecoded, Operation } from 'src/logic/safe/store/models/types/transactions.d' - -export type ModuleTxServiceModel = { - created: string - executionDate: string - blockNumber: number - transactionHash: string - safe: string - module: string - to: string - value: string - data: string - operation: Operation - dataDecoded: DataDecoded -} - -type ETag = string | null - -let previousETag: ETag = null -export const loadModuleTransactions = async (safeAddress: string): Promise => { - if (!safeAddress) { - return [] - } - - const { eTag, results }: { eTag: ETag; results: ModuleTxServiceModel[] } = await fetchTransactions( - TransactionTypes.MODULE, - safeAddress, - previousETag, - ) - previousETag = eTag - - return results -} diff --git a/src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts b/src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts index 299d6205..0dc51f08 100644 --- a/src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts +++ b/src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions.ts @@ -1,14 +1,6 @@ -import { fromJS, List, Map } from 'immutable' +import { List } from 'immutable' -import generateBatchRequests from 'src/logic/contracts/generateBatchRequests' -import { CancellationTransactions } from 'src/logic/safe/store/reducer/cancellationTransactions' -import { web3ReadOnly } from 'src/logic/wallets/getWeb3' -import { PROVIDER_REDUCER_ID } from 'src/logic/wallets/store/reducer/provider' -import { buildTx, isCancelTransaction } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' -import { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe' -import { store } from 'src/store' -import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTransactions/fetchTransactions' -import { Transaction, TransactionTypes } from 'src/logic/safe/store/models/types/transaction' +import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { SafeRecord } from 'src/logic/safe/store/models/safe' import { DataDecoded } from 'src/logic/safe/store/models/types/transactions.d' @@ -52,13 +44,8 @@ export type TxServiceModel = { value: string } -export type SafeTransactionsType = { - cancel: any - outgoing: any -} - export type OutgoingTxs = { - cancellationTxs: Record | CancellationTransactions + cancellationTxs: Record outgoingTxs: TxServiceModel[] | List } @@ -66,161 +53,3 @@ export type BatchProcessTxsProps = OutgoingTxs & { currentUser?: string safe: SafeRecord } - -/** - * Differentiates outgoing transactions from its cancel ones and returns a split map - * @param {string} safeAddress - safe's Ethereum Address - * @param {TxServiceModel[]} outgoingTxs - collection of transactions (usually, returned by the /transactions service) - * @returns {any|{cancellationTxs: {}, outgoingTxs: []}} - */ -const extractCancelAndOutgoingTxs = (safeAddress: string, outgoingTxs: TxServiceModel[]): OutgoingTxs => { - return outgoingTxs.reduce( - (acc: { cancellationTxs: Record; outgoingTxs: TxServiceModel[] }, transaction) => { - if ( - isCancelTransaction(transaction, safeAddress) && - outgoingTxs.find((tx) => tx.nonce === transaction.nonce && !isCancelTransaction(tx, safeAddress)) - ) { - if (!isNaN(Number(transaction.nonce))) { - acc.cancellationTxs[transaction.nonce] = transaction - } - } else { - acc.outgoingTxs = [...acc.outgoingTxs, transaction] - } - return acc - }, - { - cancellationTxs: {}, - outgoingTxs: [], - }, - ) -} - -type BatchRequestReturnValues = [TxServiceModel | Transaction, string | undefined] - -/** - * Requests Contract's code for all the Contracts the Safe has interacted with - * @param transactions - * @returns {Promise<[Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>, Promise<*[]>]>} - */ -const batchRequestContractCode = ( - transactions: (TxServiceModel | Transaction)[], -): Promise => { - if (!transactions || !Array.isArray(transactions)) { - throw new Error('`transactions` must be provided in order to lookup information') - } - - const batch = new web3ReadOnly.BatchRequest() - - // this will no longer be used when txs-list-v2 feature is finished - // that's why I'm doing this to move forward - const whenTxsValues = (transactions as any[]).map((tx) => { - return generateBatchRequests({ - abi: [], - address: tx.to, - batch, - context: tx, - methods: [{ method: 'getCode', type: 'eth', args: [tx.to] }], - }) - }) - - batch.execute() - - return Promise.all(whenTxsValues) -} - -/** - * Receives a list of outgoing and its cancellation transactions and builds the tx object that will be store - * @param cancellationTxs - * @param currentUser - * @param knownTokens - * @param outgoingTxs - * @param safe - * @returns {Promise<{cancel: {}, outgoing: []}>} - */ -const batchProcessOutgoingTransactions = async ({ - cancellationTxs, - currentUser, - outgoingTxs, - safe, -}: BatchProcessTxsProps): Promise<{ - cancel: Record - outgoing: Transaction[] -}> => { - // cancellation transactions - const cancelTxsValues = List(Object.values(cancellationTxs)) - const cancellationTxsWithData = cancelTxsValues.size ? await batchRequestContractCode(cancelTxsValues.toArray()) : [] - - const cancel = {} - for (const [tx] of cancellationTxsWithData) { - cancel[`${tx.nonce}`] = await buildTx({ - cancellationTxs, - currentUser, - outgoingTxs, - safe, - tx, - }) - } - - // outgoing transactions - const outgoingTxsList: List = - (outgoingTxs as TxServiceModel[]).length !== undefined - ? List(outgoingTxs as TxServiceModel[]) - : (outgoingTxs as List) - const outgoingTxsWithData = outgoingTxsList.size ? await batchRequestContractCode(outgoingTxsList.toArray()) : [] - - const outgoing: Transaction[] = [] - for (const [tx] of outgoingTxsWithData) { - outgoing.push( - await buildTx({ - cancellationTxs, - currentUser, - outgoingTxs, - safe, - tx, - }), - ) - } - - return { cancel, outgoing } -} - -let previousETag: string | null = null -export const loadOutgoingTransactions = async (safeAddress: string): Promise => { - const defaultResponse = { - cancel: Map(), - outgoing: List(), - } - const state = store.getState() - - if (!safeAddress) { - return defaultResponse - } - - const currentUser: string = state[PROVIDER_REDUCER_ID].get('account') - const safe: SafeRecord = state[SAFE_REDUCER_ID].getIn(['safes', safeAddress]) - - if (!safe) { - return defaultResponse - } - - const { eTag, results }: { eTag: string | null; results: TxServiceModel[] } = await fetchTransactions( - TransactionTypes.OUTGOING, - safeAddress, - previousETag, - ) - previousETag = eTag - const { cancellationTxs, outgoingTxs } = extractCancelAndOutgoingTxs(safeAddress, results) - - // this should be only used for the initial load or when paginating - const { cancel, outgoing } = await batchProcessOutgoingTransactions({ - cancellationTxs, - currentUser, - outgoingTxs, - safe, - }) - - return { - cancel: fromJS(cancel), - outgoing: fromJS(outgoing), - } -} diff --git a/src/logic/safe/store/actions/transactions/pendingTransactions.ts b/src/logic/safe/store/actions/transactions/pendingTransactions.ts deleted file mode 100644 index edb383fb..00000000 --- a/src/logic/safe/store/actions/transactions/pendingTransactions.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { List, Map } from 'immutable' -import { batch } from 'react-redux' -import { TransactionReceipt } from 'web3-core' - -import { addOrUpdateCancellationTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions' -import { addOrUpdateTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateTransactions' -import { removeCancellationTransaction } from 'src/logic/safe/store/actions/transactions/removeCancellationTransaction' -import { removeTransaction } from 'src/logic/safe/store/actions/transactions/removeTransaction' -import { Dispatch } from 'src/logic/safe/store/actions/types.d' -import { makeConfirmation } from 'src/logic/safe/store/models/confirmation' -import { Transaction, TransactionStatus } from 'src/logic/safe/store/models/types/transaction' -import { safeTransactionsSelector } from 'src/logic/safe/store/selectors' -import { sameAddress } from 'src/logic/wallets/ethAddresses' -import { web3ReadOnly } from 'src/logic/wallets/getWeb3' -import { AppReduxState } from 'src/store' - -type SetPendingTransactionParams = { - transaction: Transaction - from: string -} - -const setTxStatusAsPending = ({ transaction, from }: SetPendingTransactionParams): Transaction => - transaction.withMutations((transaction) => { - transaction - // setting user as the one who has triggered the tx - // this allows to display the owner's "pending" status - .updateIn(['ownersWithPendingActions', transaction.isCancellationTx ? 'reject' : 'confirm'], (previous) => - previous.push(from), - ) - // global transaction status - .set('status', TransactionStatus.PENDING) - }) - -type SetOptimisticTransactionParams = { - transaction: Transaction - from: string - isExecution: boolean - receipt: TransactionReceipt -} - -const updateTxBasedOnReceipt = ({ - transaction, - from, - isExecution, - receipt, -}: SetOptimisticTransactionParams): Transaction => { - const txToStore = isExecution - ? transaction.withMutations((tx) => { - tx.set('executionTxHash', receipt.transactionHash) - .set('blockNumber', receipt.blockNumber) - .set('executionDate', tx.submissionDate) - .set('fee', web3ReadOnly.utils.toWei(`${receipt.gasUsed}`, 'gwei')) - .set('executor', from) - .set('isExecuted', true) - .set('isSuccessful', receipt.status) - .set('status', receipt.status ? TransactionStatus.SUCCESS : TransactionStatus.FAILED) - }) - : transaction.set('status', TransactionStatus.AWAITING_CONFIRMATIONS) - - return txToStore.withMutations((tx) => { - const senderHasAlreadyConfirmed = tx.confirmations.findIndex(({ owner }) => sameAddress(owner, from)) !== -1 - - if (!senderHasAlreadyConfirmed) { - // updates confirmations status - tx.update('confirmations', (confirmations) => confirmations.push(makeConfirmation({ owner: from }))) - } - - tx.updateIn(['ownersWithPendingActions', 'reject'], (prev) => prev.clear()).updateIn( - ['ownersWithPendingActions', 'confirm'], - (prev) => prev.clear(), - ) - }) -} - -type StoreTxParams = { - transaction: Transaction - safeAddress: string - dispatch: Dispatch - state: AppReduxState -} - -export const storeTx = async ({ transaction, safeAddress, dispatch, state }: StoreTxParams): Promise => { - if (transaction.isCancellationTx) { - // `transaction` is the Cancellation tx - // So we need to decide the `status` for the main transaction this `transaction` is cancelling - let status: TransactionStatus = TransactionStatus.AWAITING_YOUR_CONFIRMATION - // `cancelled`, will become true if its corresponding Cancellation tx was successfully executed - let cancelled = false - - switch (transaction.status) { - case TransactionStatus.SUCCESS: - status = TransactionStatus.CANCELLED - cancelled = true - break - case TransactionStatus.PENDING: - status = TransactionStatus.PENDING - break - default: - break - } - - const safeTransactions = safeTransactionsSelector(state) - - const transactions = safeTransactions.withMutations((txs) => { - const txIndex = txs.findIndex(({ nonce }) => Number(nonce) === Number(transaction.nonce)) - txs.update(txIndex, (tx) => tx.set('status', status).set('cancelled', cancelled)) - }) - - batch(() => { - dispatch( - addOrUpdateCancellationTransactions({ - safeAddress, - transactions: Map({ [`${transaction.nonce}`]: transaction }), - }), - ) - dispatch(addOrUpdateTransactions({ safeAddress, transactions })) - }) - } else { - dispatch(addOrUpdateTransactions({ safeAddress, transactions: List([transaction]) })) - } -} - -type StoreSignedTxParams = StoreTxParams & { - from: string - isExecution: boolean -} - -export const storeSignedTx = ({ transaction, from, isExecution, ...rest }: StoreSignedTxParams): Promise => - storeTx({ - transaction: isExecution ? setTxStatusAsPending({ transaction, from }) : transaction, - ...rest, - }) - -type StoreExecParams = StoreTxParams & { - from: string - isExecution: boolean - safeAddress: string - receipt: TransactionReceipt -} - -export const storeExecutedTx = ({ safeAddress, dispatch, state, ...rest }: StoreExecParams): Promise => - storeTx({ - transaction: updateTxBasedOnReceipt({ ...rest }), - safeAddress, - dispatch, - state, - }) - -export const removeTxFromStore = ( - transaction: Transaction, - safeAddress: string, - dispatch: Dispatch, - state: AppReduxState, -): void => { - if (transaction.isCancellationTx) { - const safeTransactions = safeTransactionsSelector(state) - const transactions = safeTransactions.withMutations((txs) => { - const txIndex = txs.findIndex(({ nonce }) => Number(nonce) === Number(transaction.nonce)) - txs[txIndex].set('status', TransactionStatus.AWAITING_YOUR_CONFIRMATION) - }) - - batch(() => { - dispatch(addOrUpdateTransactions({ safeAddress, transactions })) - dispatch(removeCancellationTransaction({ safeAddress, transaction })) - }) - } else { - dispatch(removeTransaction({ safeAddress, transaction })) - } -} diff --git a/src/logic/safe/store/actions/transactions/removeCancellationTransaction.ts b/src/logic/safe/store/actions/transactions/removeCancellationTransaction.ts deleted file mode 100644 index 985f313a..00000000 --- a/src/logic/safe/store/actions/transactions/removeCancellationTransaction.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createAction } from 'redux-actions' - -export const REMOVE_CANCELLATION_TRANSACTION = 'REMOVE_CANCELLATION_TRANSACTION' - -export const removeCancellationTransaction = createAction(REMOVE_CANCELLATION_TRANSACTION) diff --git a/src/logic/safe/store/actions/transactions/removeTransaction.ts b/src/logic/safe/store/actions/transactions/removeTransaction.ts deleted file mode 100644 index 3a84f3d4..00000000 --- a/src/logic/safe/store/actions/transactions/removeTransaction.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createAction } from 'redux-actions' - -export const REMOVE_TRANSACTION = 'REMOVE_TRANSACTION' - -export const removeTransaction = createAction(REMOVE_TRANSACTION) diff --git a/src/logic/safe/store/actions/transactions/utils/addMockSafeCreationTx.ts b/src/logic/safe/store/actions/transactions/utils/addMockSafeCreationTx.ts deleted file mode 100644 index 8543f5cb..00000000 --- a/src/logic/safe/store/actions/transactions/utils/addMockSafeCreationTx.ts +++ /dev/null @@ -1,27 +0,0 @@ -const addMockSafeCreationTx = (safeAddress: string) => [ - { - blockNumber: null, - baseGas: 0, - confirmations: [], - data: null, - executionDate: null, - gasPrice: 0, - gasToken: '0x0000000000000000000000000000000000000000', - isExecuted: true, - nonce: null, - operation: 0, - refundReceiver: '0x0000000000000000000000000000000000000000', - safe: safeAddress, - safeTxGas: 0, - safeTxHash: '', - signatures: null, - submissionDate: null, - executor: '', - to: '', - transactionHash: null, - value: 0, - creationTx: true, - }, -] - -export default addMockSafeCreationTx diff --git a/src/logic/safe/store/actions/transactions/utils/newTransactionsHelpers.ts b/src/logic/safe/store/actions/transactions/utils/newTransactionsHelpers.ts deleted file mode 100644 index 9a93ed00..00000000 --- a/src/logic/safe/store/actions/transactions/utils/newTransactionsHelpers.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Transaction, TxType } from 'src/logic/safe/store/models/types/transactions.d' - -export const isMultiSigTx = (tx: Transaction): boolean => { - return TxType[tx.txType] === TxType.MULTISIG_TRANSACTION -} -export const isModuleTx = (tx: Transaction): boolean => { - return TxType[tx.txType] === TxType.MODULE_TRANSACTION -} -export const isEthereumTx = (tx: Transaction): boolean => { - return TxType[tx.txType] === TxType.ETHEREUM_TRANSACTION -} diff --git a/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts b/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts index e14a0f3b..62238891 100644 --- a/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts +++ b/src/logic/safe/store/actions/transactions/utils/transactionHelpers.ts @@ -1,105 +1,17 @@ -import { List } from 'immutable' import { getNetworkInfo } from 'src/config' -import { getERC20DecimalsAndSymbol, isSendERC20Transaction } from 'src/logic/tokens/utils/tokenHelpers' -import { getERC721Symbol, isSendERC721Transaction } from 'src/logic/collectibles/utils' -import { isEmptyAddress, sameAddress, ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' +import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' -import { makeConfirmation } from 'src/logic/safe/store/models/confirmation' -import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' -import { makeTransaction } from 'src/logic/safe/store/models/transaction' -import { - Transaction, - TransactionStatus, - TransactionStatusValues, - TransactionTypes, - TransactionTypeValues, - TxArgs, - RefundParams, - isStoredTransaction, -} from 'src/logic/safe/store/models/types/transaction' -import { AppReduxState, store } from 'src/store' -import { - safeSelector, - safeTransactionsSelector, - safeCancellationTransactionsSelector, -} from 'src/logic/safe/store/selectors' -import { addOrUpdateTransactions } from 'src/logic/safe/store/actions/transactions/addOrUpdateTransactions' +import { Transaction, TxArgs, RefundParams } from 'src/logic/safe/store/models/types/transaction' import { BatchProcessTxsProps, TxServiceModel, } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' import { TypedDataUtils } from 'eth-sig-util' -import { ProviderRecord } from 'src/logic/wallets/store/model/provider' -import { SafeRecord } from 'src/logic/safe/store/models/safe' -import { DecodedParams } from 'src/routes/safe/store/models/types/transactions.d' -import { CALL } from 'src/logic/safe/transactions' export const isEmptyData = (data?: string | null): boolean => { return !data || data === EMPTY_DATA } -export const isInnerTransaction = (tx: BuildTx['tx'] | 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: BuildTx['tx'], safeAddress: string): boolean => { - if (isStoredTransaction(tx)) { - if (!sameAddress(tx.recipient, safeAddress)) { - return false - } - } else { - if (!sameAddress(tx.to, safeAddress)) { - return false - } - } - - if (Number(tx.value)) { - return false - } - - if (tx.data && !isEmptyData(tx.data)) { - return false - } - - if (tx.operation !== CALL) { - return false - } - - if (tx.baseGas) { - return false - } - - if (Number(tx.gasPrice)) { - return false - } - - if (tx.gasToken && !isEmptyAddress(tx.gasToken)) { - return false - } - - if (tx.refundReceiver && !isEmptyAddress(tx.refundReceiver)) { - return false - } - - return true -} - -export const isPendingTransaction = (tx: Transaction, cancelTx: Transaction): boolean => { - return (!!cancelTx && cancelTx.status === 'pending') || tx.status === 'pending' -} - -export const isModifySettingsTransaction = (tx: BuildTx['tx'], safeAddress: string): boolean => { - return isInnerTransaction(tx, safeAddress) && !isEmptyData(tx.data) -} - export const isMultiSendTransaction = (tx: BuildTx['tx']): boolean => { return !isEmptyData(tx.data) && tx.data?.substring(0, 10) === '0x8d80ff0a' && Number(tx.value) === 0 } @@ -113,19 +25,6 @@ export const isUpgradeTransaction = (tx: BuildTx['tx']): boolean => { ) } -export const isOutgoingTransaction = (tx: BuildTx['tx'], safeAddress?: string): boolean => { - return !sameAddress((tx as ServiceTx).to, safeAddress) && !isEmptyData(tx.data) -} - -export const isCustomTransaction = async (tx: BuildTx['tx'], safeAddress?: string): Promise => { - const isOutgoing = isOutgoingTransaction(tx, safeAddress) - const isErc20 = await isSendERC20Transaction(tx) - const isUpgrade = isUpgradeTransaction(tx) - const isErc721 = isSendERC721Transaction(tx) - - return isOutgoing && !isErc20 && !isUpgrade && !isErc721 -} - export const getRefundParams = async ( tx: BuildTx['tx'], tokenInfo: (string) => Promise<{ decimals: number; symbol: string } | null>, @@ -162,224 +61,14 @@ export const getRefundParams = async ( return refundParams } -export const getDecodedParams = (tx: BuildTx['tx']): DecodedParams | null => { - if (tx.dataDecoded) { - return { - [tx.dataDecoded.method]: tx.dataDecoded.parameters.reduce( - (acc, param) => ({ - ...acc, - [param.name]: param.value, - }), - {}, - ), - } - } - return null -} - -export const getConfirmations = (tx: BuildTx['tx']): List => { - return List( - (tx.confirmations as ServiceTx['confirmations'])?.map((conf) => - makeConfirmation({ - owner: conf.owner, - hash: conf.transactionHash, - signature: conf.signature, - }), - ) ?? [], - ) -} - -export const isTransactionCancelled = ( - tx: BuildTx['tx'], - outgoingTxs: BuildTx['outgoingTxs'], - cancellationTxs: BuildTx['cancellationTxs'], -): boolean => { - return ( - // not executed - !tx.isExecuted && - // there's an executed cancel tx, with same nonce - ((tx.nonce && !!cancellationTxs[tx.nonce] && cancellationTxs[tx.nonce].isExecuted) || - // there's an executed tx, with same nonce - outgoingTxs.some((outgoingTx) => tx.nonce === outgoingTx.nonce && outgoingTx.isExecuted)) - ) -} - -export const calculateTransactionStatus = ( - tx: Transaction, - { owners, threshold, nonce }: SafeRecord, - currentUser?: string | null, -): TransactionStatusValues => { - let txStatus - - if (tx.isExecuted && tx.isSuccessful) { - txStatus = TransactionStatus.SUCCESS - } else if (tx.creationTx) { - txStatus = TransactionStatus.SUCCESS - } else if (tx.cancelled || nonce > tx.nonce) { - txStatus = TransactionStatus.CANCELLED - } else if (tx.confirmations.size === threshold) { - txStatus = TransactionStatus.AWAITING_EXECUTION - } else if (!!tx.isPending) { - txStatus = TransactionStatus.PENDING - } else { - const userConfirmed = tx.confirmations.filter((conf) => conf.owner === currentUser).size === 1 - const userIsSafeOwner = owners.filter((owner) => owner.address === currentUser).size === 1 - txStatus = - !userConfirmed && userIsSafeOwner - ? TransactionStatus.AWAITING_YOUR_CONFIRMATION - : TransactionStatus.AWAITING_CONFIRMATIONS - } - - if (tx.isSuccessful === false) { - txStatus = TransactionStatus.FAILED - } - - return txStatus -} - -export const calculateTransactionType = (tx: Transaction): TransactionTypeValues => { - let txType = TransactionTypes.OUTGOING - - if (tx.isTokenTransfer) { - txType = TransactionTypes.TOKEN - } else if (tx.isCollectibleTransfer) { - txType = TransactionTypes.COLLECTIBLE - } else if (tx.modifySettingsTx) { - txType = TransactionTypes.SETTINGS - } else if (tx.isCancellationTx) { - txType = TransactionTypes.CANCELLATION - } else if (tx.customTx) { - txType = TransactionTypes.CUSTOM - } else if (tx.creationTx) { - txType = TransactionTypes.CREATION - } else if (tx.upgradeTx) { - txType = TransactionTypes.UPGRADE - } - - return txType -} - export type ServiceTx = TxServiceModel | TxToMock export type BuildTx = BatchProcessTxsProps & { tx: ServiceTx | Transaction } -export const buildTx = async ({ - cancellationTxs, - currentUser, - outgoingTxs, - safe, - tx, -}: BuildTx): Promise => { - const safeAddress = safe.address - const { nativeCoin } = getNetworkInfo() - const isModifySettingsTx = isModifySettingsTransaction(tx, safeAddress) - const isTxCancelled = isTransactionCancelled(tx, outgoingTxs, cancellationTxs) - const isSendERC721Tx = isSendERC721Transaction(tx) - const isSendERC20Tx = await isSendERC20Transaction(tx) - const isMultiSendTx = isMultiSendTransaction(tx) - const isUpgradeTx = isUpgradeTransaction(tx) - const isCustomTx = await isCustomTransaction(tx, safeAddress) - const isCancellationTx = isCancelTransaction(tx, safeAddress) - const refundParams = await getRefundParams(tx, getERC20DecimalsAndSymbol) - const decodedParams = getDecodedParams(tx) - const confirmations = getConfirmations(tx) - - let tokenDecimals = nativeCoin.decimals - let tokenSymbol = nativeCoin.symbol - try { - if (isSendERC20Tx) { - const { decimals, symbol } = await getERC20DecimalsAndSymbol((tx as ServiceTx).to) - tokenDecimals = decimals - tokenSymbol = symbol - } else if (isSendERC721Tx) { - tokenSymbol = await getERC721Symbol((tx as ServiceTx).to) - } - } catch (err) { - console.log(`Failed to retrieve token data from ${(tx as ServiceTx).to}`) - } - - const txToStore = makeTransaction({ - baseGas: tx.baseGas, - blockNumber: (tx as ServiceTx).blockNumber, - cancelled: isTxCancelled, - confirmations, - customTx: isCustomTx, - data: tx.data ? tx.data : EMPTY_DATA, - dataDecoded: tx.dataDecoded, - decimals: tokenDecimals, - decodedParams, - executionDate: (tx as ServiceTx).executionDate, - executionTxHash: (tx as ServiceTx).transactionHash, - executor: (tx as ServiceTx).executor, - fee: (tx as ServiceTx).fee, - gasPrice: tx.gasPrice, - gasToken: tx.gasToken || ZERO_ADDRESS, - isCancellationTx, - isCollectibleTransfer: isSendERC721Tx, - isExecuted: tx.isExecuted, - isSuccessful: (tx as ServiceTx).isSuccessful, - isTokenTransfer: isSendERC20Tx, - modifySettingsTx: isModifySettingsTx, - multiSendTx: isMultiSendTx, - nonce: tx.nonce, - operation: tx.operation, - origin: (tx as ServiceTx).origin, - recipient: (tx as ServiceTx).to, - refundParams, - refundReceiver: tx.refundReceiver || ZERO_ADDRESS, - safeTxGas: tx.safeTxGas, - safeTxHash: tx.safeTxHash, - submissionDate: tx.submissionDate, - symbol: tokenSymbol, - upgradeTx: isUpgradeTx, - value: tx.value?.toString(), - }) - - return txToStore - .set('status', calculateTransactionStatus(txToStore, safe, currentUser)) - .set('type', calculateTransactionType(txToStore)) -} - export type TxToMock = TxArgs & Partial -export const mockTransaction = (tx: TxToMock, safeAddress: string, state: AppReduxState): Promise => { - const safe = safeSelector(state) - const cancellationTxs = safeCancellationTransactionsSelector(state) - const outgoingTxs = safeTransactionsSelector(state) - - if (!safe) { - throw new Error('Failed to recover Safe from the store') - } - - return buildTx({ - cancellationTxs, - currentUser: undefined, - outgoingTxs, - safe, - tx, - }) -} - -export const updateStoredTransactionsStatus = (dispatch: (any) => void, walletRecord: ProviderRecord): void => { - const state = store.getState() - const safe = safeSelector(state) - - if (safe) { - const safeAddress = safe.address - const transactions = safeTransactionsSelector(state) - dispatch( - addOrUpdateTransactions({ - safeAddress, - transactions: transactions.withMutations((list) => - list.map((tx) => tx.set('status', calculateTransactionStatus(tx, safe, walletRecord.account))), - ), - }), - ) - } -} - export function generateSafeTxHash(safeAddress: string, txArgs: TxArgs): string { const messageTypes = { EIP712Domain: [{ type: 'address', name: 'verifyingContract' }], diff --git a/src/logic/safe/store/middleware/notificationsMiddleware.ts b/src/logic/safe/store/middleware/notificationsMiddleware.ts index 3f7972a3..f516a141 100644 --- a/src/logic/safe/store/middleware/notificationsMiddleware.ts +++ b/src/logic/safe/store/middleware/notificationsMiddleware.ts @@ -3,34 +3,19 @@ import { push } from 'connected-react-router' import { NOTIFICATIONS, enhanceSnackbarForAction } from 'src/logic/notifications' import closeSnackbarAction from 'src/logic/notifications/store/actions/closeSnackbar' import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar' -import { - getAwaitingTransactions, - getAwaitingGatewayTransactions, -} from 'src/logic/safe/transactions/awaitingTransactions' +import { getAwaitingGatewayTransactions } from 'src/logic/safe/transactions/awaitingTransactions' import { getSafeVersionInfo } from 'src/logic/safe/utils/safeVersion' import { isUserAnOwner } from 'src/logic/wallets/ethAddresses' import { userAccountSelector } from 'src/logic/wallets/store/selectors' import { grantedSelector } from 'src/routes/safe/container/selector' -import { ADD_INCOMING_TRANSACTIONS } from 'src/logic/safe/store/actions/addIncomingTransactions' -import { ADD_OR_UPDATE_TRANSACTIONS } from 'src/logic/safe/store/actions/transactions/addOrUpdateTransactions' import { ADD_QUEUED_TRANSACTIONS } from 'src/logic/safe/store/actions/transactions/gatewayTransactions' -import updateSafe from 'src/logic/safe/store/actions/updateSafe' -import { - safeParamAddressFromStateSelector, - safesMapSelector, - safeCancellationTransactionsSelector, -} from 'src/logic/safe/store/selectors' +import { safeParamAddressFromStateSelector, safesMapSelector } from 'src/logic/safe/store/selectors' import { isTransactionSummary } from 'src/logic/safe/store/models/types/gateway.d' import { loadFromStorage, saveToStorage } from 'src/utils/storage' import { ADD_OR_UPDATE_SAFE } from '../actions/addOrUpdateSafe' -const watchedActions = [ - ADD_OR_UPDATE_TRANSACTIONS, - ADD_INCOMING_TRANSACTIONS, - ADD_OR_UPDATE_SAFE, - ADD_QUEUED_TRANSACTIONS, -] +const watchedActions = [ADD_OR_UPDATE_SAFE, ADD_QUEUED_TRANSACTIONS] const LAST_TIME_USED_LOGGED_IN_ID = 'LAST_TIME_USED_LOGGED_IN_ID' @@ -85,32 +70,6 @@ const notificationsMiddleware = (store) => (next) => async (action) => { const state = store.getState() switch (action.type) { - case ADD_OR_UPDATE_TRANSACTIONS: { - const { safeAddress, transactions } = action.payload - const userAddress: string = userAccountSelector(state) - const cancellationTransactions = safeCancellationTransactionsSelector(state) - const awaitingTransactions = getAwaitingTransactions(transactions, cancellationTransactions, userAddress) - const awaitingTxsSubmissionDateList = awaitingTransactions.map((tx) => tx.submissionDate) - - const safes = safesMapSelector(state) - const currentSafe = safes.get(safeAddress) - - if (!currentSafe || !isUserAnOwner(currentSafe, userAddress) || awaitingTransactions.size === 0) { - break - } - - const notificationKey = `${safeAddress}-awaiting` - - await sendAwaitingTransactionNotification( - dispatch, - safeAddress, - awaitingTxsSubmissionDateList, - notificationKey, - onNotificationClicked(dispatch, notificationKey, safeAddress), - ) - - break - } case ADD_QUEUED_TRANSACTIONS: { const { safeAddress, values } = action.payload const transactions = values.filter((tx) => isTransactionSummary(tx)).map((item) => item.transaction) @@ -138,23 +97,6 @@ const notificationsMiddleware = (store) => (next) => async (action) => { break } - case ADD_INCOMING_TRANSACTIONS: { - action.payload.forEach((incomingTransactions, safeAddress) => { - const { latestIncomingTxBlock } = state.safes.get('safes').get(safeAddress, {}) - - const newIncomingTransactions = incomingTransactions.filter((tx) => tx.blockNumber > latestIncomingTxBlock) - - dispatch( - updateSafe({ - address: safeAddress, - latestIncomingTxBlock: newIncomingTransactions.size - ? newIncomingTransactions.first().blockNumber - : latestIncomingTxBlock, - }), - ) - }) - break - } case ADD_OR_UPDATE_SAFE: { const state = store.getState() const { safe } = action.payload diff --git a/src/logic/safe/store/models/incomingTransaction.ts b/src/logic/safe/store/models/incomingTransaction.ts deleted file mode 100644 index 22e00f62..00000000 --- a/src/logic/safe/store/models/incomingTransaction.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Record } from 'immutable' - -export const INCOMING_TX_TYPES = { - INCOMING: 'INCOMING', - ERC721_TRANSFER: 'ERC721_TRANSFER', - ERC20_TRANSFER: 'ERC20_TRANSFER', - ETHER_TRANSFER: 'ETHER_TRANSFER', -} - -export const makeIncomingTransaction = Record({ - blockNumber: 0, - executionTxHash: '', - safeTxHash: '', - to: '', - value: 0, - tokenAddress: '', - from: '', - symbol: '', - decimals: 18, - fee: '', - executionDate: '', - type: 'INCOMING', - status: 'success', - nonce: null, - confirmations: null, - recipient: null, - data: null, - operation: null, - safeTxGas: null, - baseGas: null, - gasPrice: null, - gasToken: null, - refundReceiver: null, - isExecuted: null, - submissionDate: null, - executor: null, - cancelled: null, - modifySettingsTx: null, - cancellationTx: null, - customTx: null, - creationTx: null, - isTokenTransfer: null, - decodedParams: null, - refundParams: null, -}) diff --git a/src/logic/safe/store/models/transaction.ts b/src/logic/safe/store/models/transaction.ts deleted file mode 100644 index a0d3e21c..00000000 --- a/src/logic/safe/store/models/transaction.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { List, Map, Record } from 'immutable' - -import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' -import { - PendingActionType, - TransactionProps, - TransactionStatus, - TransactionTypes, -} from 'src/logic/safe/store/models/types/transaction' - -export const makeTransaction = Record({ - baseGas: 0, - blockNumber: 0, - cancelled: false, - confirmations: List([]), - created: '', - creator: '', - creationTx: false, - customTx: false, - data: null, - dataDecoded: null, - decimals: 18, - decodedParams: {}, - executionDate: '', - executionTxHash: undefined, - executor: '', - factoryAddress: '', - fee: null, - gasPrice: '0', - gasToken: ZERO_ADDRESS, - isCancellationTx: false, - isCollectibleTransfer: false, - isExecuted: false, - isPending: false, - isSuccessful: true, - isTokenTransfer: false, - masterCopy: '', - modifySettingsTx: false, - multiSendTx: false, - nonce: 0, - operation: 0, - origin: null, - ownersWithPendingActions: Map({ [PendingActionType.CONFIRM]: List([]), [PendingActionType.REJECT]: List([]) }), - recipient: '', - refundParams: null, - refundReceiver: ZERO_ADDRESS, - safeTxGas: 0, - safeTxHash: '', - setupData: '', - status: TransactionStatus.PENDING, - submissionDate: '', - symbol: '', - transactionHash: '', - type: TransactionTypes.OUTGOING, - upgradeTx: false, - value: '0', -}) diff --git a/src/logic/safe/store/models/types/transaction.ts b/src/logic/safe/store/models/types/transaction.ts index c69b6cf1..005e113c 100644 --- a/src/logic/safe/store/models/types/transaction.ts +++ b/src/logic/safe/store/models/types/transaction.ts @@ -1,15 +1,10 @@ import { List, Map, RecordOf } from 'immutable' -import { ModuleTxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadModuleTransactions' -import { Token } from 'src/logic/tokens/store/model/token' import { Confirmation } from './confirmation' import { GnosisSafe } from 'src/types/contracts/GnosisSafe.d' -import { DataDecoded, Transfer } from './transactions' -import { DecodedParams } from 'src/routes/safe/store/models/types/transactions.d' -import { BuildTx } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' +import { DataDecoded, DecodedParams, Transfer } from './transactions.d' export enum TransactionTypes { - INCOMING = 'incoming', OUTGOING = 'outgoing', SETTINGS = 'settings', CUSTOM = 'custom', @@ -92,10 +87,6 @@ export type TransactionProps = { export type Transaction = RecordOf & Readonly -export const isStoredTransaction = (tx: BuildTx['tx']): tx is Transaction => { - return typeof (tx as Transaction).recipient !== 'undefined' -} - export type TxArgs = { baseGas: number data: string @@ -111,17 +102,3 @@ export type TxArgs = { to: string valueInWei: string } - -type SafeModuleCompatibilityTypes = { - nonce?: string // not required for this tx: added for compatibility - fee?: number // not required for this tx: added for compatibility - executionTxHash?: string // not required for this tx: added for compatibility - safeTxHash: string // table uses this key as a unique row identifier, added for compatibility -} - -export type SafeModuleTransaction = ModuleTxServiceModel & - SafeModuleCompatibilityTypes & { - status: TransactionStatus - type: TransactionTypes - tokenInfo?: Token - } diff --git a/src/logic/safe/store/models/types/transactions.d.ts b/src/logic/safe/store/models/types/transactions.d.ts index 4d753d61..335ca7a1 100644 --- a/src/logic/safe/store/models/types/transactions.d.ts +++ b/src/logic/safe/store/models/types/transactions.d.ts @@ -116,73 +116,6 @@ export interface Transfer { from: string } -export enum TxType { - MULTISIG_TRANSACTION = 'MULTISIG_TRANSACTION', - ETHEREUM_TRANSACTION = 'ETHEREUM_TRANSACTION', - MODULE_TRANSACTION = 'MODULE_TRANSACTION', -} - -export interface MultiSigTransaction { - safe: string - to: string - value: string - data: string | null - operation: number - gasToken: string - safeTxGas: number - baseGas: number - gasPrice: string - refundReceiver: string - nonce: number - executionDate: string | null - submissionDate: string - modified: string - blockNumber: number | null - transactionHash: string | null - safeTxHash: string - executor: string | null - isExecuted: boolean - isSuccessful: boolean | null - ethGasPrice: string | null - gasUsed: number | null - fee: string | null - origin: string | null - dataDecoded: DataDecoded | null - confirmationsRequired: number | null - confirmations: Confirmation[] - signatures: string | null - transfers: Transfer[] - txType: TxType.MULTISIG_TRANSACTION -} - -export interface ModuleTransaction { - created: string - executionDate: string - blockNumber: number - transactionHash: string - safe: string - module: string - to: string - value: string - data: string - operation: Operation - transfers: Transfer[] - txType: TxType.MODULE_TRANSACTION -} - -export interface EthereumTransaction { - executionDate: string - to: string - data: string | null - txHash: string - blockNumber: number - transfers: Transfer[] - txType: TxType.ETHEREUM_TRANSACTION - from: string -} - -export type Transaction = MultiSigTransaction | ModuleTransaction | EthereumTransaction - // SAFE METHODS TO ITS ID // https://github.com/gnosis/safe-contracts/blob/development/test/safeMethodNaming.js // https://github.com/gnosis/safe-contracts/blob/development/contracts/GnosisSafe.sol diff --git a/src/logic/safe/store/reducer/allTransactions.ts b/src/logic/safe/store/reducer/allTransactions.ts deleted file mode 100644 index dcfcb3e5..00000000 --- a/src/logic/safe/store/reducer/allTransactions.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { handleActions } from 'redux-actions' - -import { Transaction } from 'src/logic/safe/store/models/types/transactions.d' -import { - LOAD_MORE_TRANSACTIONS, - LoadMoreTransactionsAction, -} from 'src/logic/safe/store/actions/allTransactions/pagination' - -export const TRANSACTIONS = 'allTransactions' - -export interface TransactionsState { - [safeAddress: string]: { - totalTransactionsCount: number - transactions: Transaction[] - } -} - -export default handleActions( - { - // todo: because we are thinking in remove immutableJS, I will implement this without it so it can be easier removed in future - [LOAD_MORE_TRANSACTIONS]: (state: TransactionsState, action: LoadMoreTransactionsAction) => { - const { safeAddress, transactions, totalTransactionsAmount } = action.payload - const oldState = state[safeAddress] - - return { - ...state, - [safeAddress]: { - ...oldState, - transactions: [...(oldState?.transactions || []), ...transactions], - totalTransactionsCount: - totalTransactionsAmount > 0 ? totalTransactionsAmount : state[safeAddress].totalTransactionsCount, - }, - } - }, - }, - {}, -) diff --git a/src/logic/safe/store/reducer/cancellationTransactions.ts b/src/logic/safe/store/reducer/cancellationTransactions.ts deleted file mode 100644 index 52258daa..00000000 --- a/src/logic/safe/store/reducer/cancellationTransactions.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Map } from 'immutable' -import { Action, handleActions } from 'redux-actions' - -import { Transaction } from 'src/logic/safe/store/models/types/transaction' -import { ADD_OR_UPDATE_CANCELLATION_TRANSACTIONS } from 'src/logic/safe/store/actions/transactions/addOrUpdateCancellationTransactions' -import { REMOVE_CANCELLATION_TRANSACTION } from 'src/logic/safe/store/actions/transactions/removeCancellationTransaction' -import { AppReduxState } from 'src/store' - -export const CANCELLATION_TRANSACTIONS_REDUCER_ID = 'cancellationTransactions' - -export type CancellationTransactions = Map -export type CancellationTxState = Map - -type CancellationTransactionsPayload = { safeAddress: string; transactions: CancellationTransactions } -type CancellationTransactionPayload = { safeAddress: string; transaction: Transaction } - -export default handleActions< - AppReduxState['cancellationTransactions'], - CancellationTransactionsPayload | CancellationTransactionPayload ->( - { - [ADD_OR_UPDATE_CANCELLATION_TRANSACTIONS]: (state, action: Action) => { - const { safeAddress, transactions } = action.payload - - if (!safeAddress || !transactions || !transactions.size) { - return state - } - - return state.withMutations((map) => { - const stateTransactionsMap = map.get(safeAddress) - - if (stateTransactionsMap) { - transactions.forEach((updateTx) => { - const keyPath = [safeAddress, `${updateTx.nonce}`] - - if (updateTx.confirmations.size) { - // if there are confirmations then we replace what's stored with the new tx - // as we assume that this is the newest tx returned by the server - map.setIn(keyPath, updateTx) - } else { - // if there are no confirmations, we assume this is a mocked tx - // as txs without confirmation are not being returned by the server (?has_confirmations=true) - map.mergeDeepIn(keyPath, updateTx) - } - }) - } else { - map.set(safeAddress, transactions) - } - }) - }, - [REMOVE_CANCELLATION_TRANSACTION]: (state, action: Action) => { - const { safeAddress, transaction } = action.payload - - if (!safeAddress || !transaction) { - return state - } - - return state.withMutations((map) => { - const stateTransactionsMap = map.get(safeAddress) - - if (stateTransactionsMap) { - map.deleteIn([safeAddress, `${transaction.nonce}`]) - } - }) - }, - }, - Map(), -) diff --git a/src/logic/safe/store/reducer/incomingTransactions.ts b/src/logic/safe/store/reducer/incomingTransactions.ts deleted file mode 100644 index a5273385..00000000 --- a/src/logic/safe/store/reducer/incomingTransactions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Map } from 'immutable' -import { handleActions } from 'redux-actions' - -import { ADD_INCOMING_TRANSACTIONS } from 'src/logic/safe/store/actions/addIncomingTransactions' -import { AppReduxState } from 'src/store' - -export const INCOMING_TRANSACTIONS_REDUCER_ID = 'incomingTransactions' - -export default handleActions( - { - [ADD_INCOMING_TRANSACTIONS]: (state, action) => action.payload, - }, - Map(), -) diff --git a/src/logic/safe/store/reducer/moduleTransactions.ts b/src/logic/safe/store/reducer/moduleTransactions.ts deleted file mode 100644 index 27b310b2..00000000 --- a/src/logic/safe/store/reducer/moduleTransactions.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { handleActions } from 'redux-actions' - -import { - ADD_MODULE_TRANSACTIONS, - AddModuleTransactionsAction, -} from 'src/logic/safe/store/actions/addModuleTransactions' -import { ModuleTxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadModuleTransactions' - -export const MODULE_TRANSACTIONS_REDUCER_ID = 'moduleTransactions' - -export interface ModuleTransactionsState { - [safeAddress: string]: ModuleTxServiceModel[] -} - -export default handleActions( - { - [ADD_MODULE_TRANSACTIONS]: (state: ModuleTransactionsState, action: AddModuleTransactionsAction) => { - const { modules, safeAddress } = action.payload - const oldModuleTxs = state[safeAddress] ?? [] - const oldModuleTxsHashes = oldModuleTxs.map(({ transactionHash }) => transactionHash) - // As backend is returning the whole list of txs on every request, - // to avoid duplicates, filtering happens in this level. - const newModuleTxs = modules.filter((moduleTx) => !oldModuleTxsHashes.includes(moduleTx.transactionHash)) - - return { - ...state, - [safeAddress]: [...oldModuleTxs, ...newModuleTxs], - } - }, - }, - {}, -) diff --git a/src/logic/safe/store/reducer/transactions.ts b/src/logic/safe/store/reducer/transactions.ts deleted file mode 100644 index fe19bb18..00000000 --- a/src/logic/safe/store/reducer/transactions.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { List, Map } from 'immutable' -import { Action, handleActions } from 'redux-actions' - -import { ADD_OR_UPDATE_TRANSACTIONS } from 'src/logic/safe/store/actions/transactions/addOrUpdateTransactions' -import { REMOVE_TRANSACTION } from 'src/logic/safe/store/actions/transactions/removeTransaction' -import { Transaction } from 'src/logic/safe/store/models/types/transaction' -import { AppReduxState } from 'src/store' - -export const TRANSACTIONS_REDUCER_ID = 'transactions' - -type TransactionBasePayload = { safeAddress: string } -type TransactionsPayload = TransactionBasePayload & { transactions: List } -type TransactionPayload = TransactionBasePayload & { transaction: Transaction } - -type Payload = TransactionsPayload | TransactionPayload - -export default handleActions( - { - [ADD_OR_UPDATE_TRANSACTIONS]: (state, action: Action) => { - const { safeAddress, transactions } = action.payload - - if (!safeAddress || !transactions || !transactions.size) { - return state - } - - return state.withMutations((map) => { - const stateTransactionsList = map.get(safeAddress) - - if (stateTransactionsList) { - const txsToStore = stateTransactionsList.withMutations((txsList) => { - transactions.forEach((updateTx) => { - const storedTxIndex = txsList.findIndex((txIterator) => txIterator.safeTxHash === updateTx.safeTxHash) - - if (storedTxIndex !== -1) { - // Update - if (updateTx.confirmations.size) { - // if there are confirmations then we replace what's stored with the new tx - // as we assume that this is the newest tx returned by the server - txsList.update(storedTxIndex, () => updateTx) - } else { - // if there are no confirmations, we assume this is a mocked tx - // as txs without confirmation are not being returned by the server (?has_confirmations=true) - txsList.update(storedTxIndex, (storedTx) => storedTx.mergeDeep(updateTx)) - } - } else { - // Add new - txsList.unshift(updateTx) - } - }) - }) - map.set(safeAddress, txsToStore) - } else { - map.set(safeAddress, transactions) - } - }) - }, - [REMOVE_TRANSACTION]: (state, action: Action) => { - const { safeAddress, transaction } = action.payload - - if (!safeAddress || !transaction) { - return state - } - - return state.withMutations((map) => { - const stateTransactionsList = map.get(safeAddress) - - if (stateTransactionsList) { - const storedTxIndex = stateTransactionsList.findIndex((storedTx) => storedTx.equals(transaction)) - - if (storedTxIndex !== -1) { - map.deleteIn([safeAddress, storedTxIndex]) - } - } - }) - }, - }, - Map(), -) diff --git a/src/logic/safe/store/reducer/types/safe.d.ts b/src/logic/safe/store/reducer/types/safe.d.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/logic/safe/store/selectors/allTransactions.ts b/src/logic/safe/store/selectors/allTransactions.ts deleted file mode 100644 index e8729e61..00000000 --- a/src/logic/safe/store/selectors/allTransactions.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { TransactionsState, TRANSACTIONS } from '../reducer/allTransactions' -import { createSelector } from 'reselect' -import { safeParamAddressFromStateSelector } from './index' -import { AppReduxState } from 'src/store' - -export const getTransactionsStateSelector = (state: AppReduxState): TransactionsState => state[TRANSACTIONS] - -export const allTransactionsSelector = createSelector(getTransactionsStateSelector, (transactionsState) => { - return transactionsState -}) - -export const safeAllTransactionsSelector = createSelector( - safeParamAddressFromStateSelector, - allTransactionsSelector, - (safeAddress, transactions) => (safeAddress ? transactions[safeAddress]?.transactions : []), -) - -export const safeTotalTransactionsAmountSelector = createSelector( - safeParamAddressFromStateSelector, - allTransactionsSelector, - (safeAddress, transactions) => (safeAddress ? transactions[safeAddress]?.totalTransactionsCount : 0), -) diff --git a/src/logic/safe/store/selectors/index.ts b/src/logic/safe/store/selectors/index.ts index 95c2ec12..7675d0a9 100644 --- a/src/logic/safe/store/selectors/index.ts +++ b/src/logic/safe/store/selectors/index.ts @@ -1,15 +1,9 @@ -import { List, Map, Set } from 'immutable' +import { List, Set } from 'immutable' import { matchPath, RouteComponentProps } from 'react-router-dom' import { createSelector } from 'reselect' import { SAFELIST_ADDRESS, SAFE_PARAM_ADDRESS } from 'src/routes/routes' -import { - CANCELLATION_TRANSACTIONS_REDUCER_ID, - CancellationTransactions, -} from 'src/logic/safe/store/reducer/cancellationTransactions' -import { INCOMING_TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/incomingTransactions' import { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe' -import { TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/transactions' import { AppReduxState } from 'src/store' import { checksumAddress } from 'src/utils/checksumAddress' @@ -30,12 +24,6 @@ export const latestMasterContractVersionSelector = createSelector(safesStateSele safeState.get('latestMasterContractVersion'), ) -const transactionsSelector = (state: AppReduxState) => state[TRANSACTIONS_REDUCER_ID] - -const cancellationTransactionsSelector = (state: AppReduxState) => state[CANCELLATION_TRANSACTIONS_REDUCER_ID] - -const incomingTransactionsSelector = (state: AppReduxState) => state[INCOMING_TRANSACTIONS_REDUCER_ID] - export const safeParamAddressFromStateSelector = (state: AppReduxState): string => { const match = matchPath<{ safeAddress: string }>(state.router.location.pathname, { path: `${SAFELIST_ADDRESS}/:safeAddress`, @@ -56,22 +44,6 @@ export const safeParamAddressSelector = ( return urlAdd ? checksumAddress(urlAdd) : '' } -export const safeTransactionsSelector = createSelector( - transactionsSelector, - safeParamAddressFromStateSelector, - (transactions, address) => { - if (!transactions) { - return List([]) - } - - if (!address) { - return List([]) - } - - return transactions.get(address, List([])) - }, -) - export const addressBookQueryParamsSelector = (state: AppReduxState): string | undefined => { const { location } = state.router @@ -81,38 +53,6 @@ export const addressBookQueryParamsSelector = (state: AppReduxState): string | u } } -export const safeCancellationTransactionsSelector = createSelector( - cancellationTransactionsSelector, - safeParamAddressFromStateSelector, - (cancellationTransactions, address): CancellationTransactions => { - if (!cancellationTransactions) { - return Map() - } - - if (!address) { - return Map() - } - - return cancellationTransactions.get(address, Map()) - }, -) - -export const safeIncomingTransactionsSelector = createSelector( - incomingTransactionsSelector, - safeParamAddressFromStateSelector, - (incomingTransactions, address) => { - if (!incomingTransactions) { - return List([]) - } - - if (!address) { - return List([]) - } - - return incomingTransactions.get(address, List()) - }, -) - export const safeSelector = createSelector( safesMapSelector, safeParamAddressFromStateSelector, diff --git a/src/logic/safe/store/selectors/transactions.ts b/src/logic/safe/store/selectors/transactions.ts deleted file mode 100644 index 7fc6e3eb..00000000 --- a/src/logic/safe/store/selectors/transactions.ts +++ /dev/null @@ -1,18 +0,0 @@ -// import { List } from 'immutable' -// import { createSelector } from 'reselect' -// -// import { safeIncomingTransactionsSelector, safeTransactionsSelector } from 'src/logic/safe/store/selectors' -// import { Transaction, SafeModuleTransaction } from 'src/logic/safe/store/models/types/transaction' -// import { safeModuleTransactionsSelector } from 'src/routes/safe/container/selector' - -// export const extendedTransactionsSelector = createSelector( -// safeTransactionsSelector, -// safeIncomingTransactionsSelector, -// safeModuleTransactionsSelector, -// (transactions, incomingTransactions, moduleTransactions): List => -// List().withMutations((list) => { -// list.concat(transactions).concat(incomingTransactions).concat(moduleTransactions) -// }), -// ) - -export {} diff --git a/src/logic/safe/transactions/awaitingTransactions.ts b/src/logic/safe/transactions/awaitingTransactions.ts index 7b1345de..a6ff1212 100644 --- a/src/logic/safe/transactions/awaitingTransactions.ts +++ b/src/logic/safe/transactions/awaitingTransactions.ts @@ -1,30 +1,6 @@ -import { List } from 'immutable' - -import { isPendingTransaction } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' import { isStatusAwaitingConfirmation } from 'src/logic/safe/store/models/types/gateway.d' -import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { Transaction as GatewayTransaction } from 'src/logic/safe/store/models/types/gateway' -import { addressInList } from 'src/routes/safe/components/Transactions/GatewayTransactions/utils' -import { CancellationTransactions } from 'src/logic/safe/store/reducer/cancellationTransactions' - -export const getAwaitingTransactions = ( - allTransactions: List, - cancellationTxs: CancellationTransactions, - userAccount: string, -): List => { - return allTransactions.filter((tx) => { - const cancelTx = !!tx.nonce && !isNaN(Number(tx.nonce)) ? cancellationTxs.get(`${tx.nonce}`) : null - - // The transaction is not executed and is not cancelled, nor pending, so it's still waiting confirmations - if (!tx.executionTxHash && !tx.cancelled && cancelTx && !isPendingTransaction(tx, cancelTx)) { - // Then we check if the waiting confirmations are not from the current user, otherwise, filters this transaction - const transactionWaitingUser = tx.confirmations.filter(({ owner }) => owner !== userAccount) - return transactionWaitingUser.size > 0 - } - - return false - }) -} +import { addressInList } from 'src/routes/safe/components/Transactions/TxList/utils' export const getAwaitingGatewayTransactions = ( allTransactions: GatewayTransaction[], diff --git a/src/logic/safe/transactions/incomingTxHistory.ts b/src/logic/safe/transactions/incomingTxHistory.ts deleted file mode 100644 index f4cbf582..00000000 --- a/src/logic/safe/transactions/incomingTxHistory.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { getSafeServiceBaseUrl } from 'src/config' -import { checksumAddress } from 'src/utils/checksumAddress' - -export const buildIncomingTxServiceUrl = (safeAddress: string): string => { - const address = checksumAddress(safeAddress) - return `${getSafeServiceBaseUrl(address)}/incoming-transfers/` -} diff --git a/src/logic/safe/transactions/moduleTxHistory.ts b/src/logic/safe/transactions/moduleTxHistory.ts deleted file mode 100644 index 656b4381..00000000 --- a/src/logic/safe/transactions/moduleTxHistory.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { getSafeServiceBaseUrl } from 'src/config' -import { checksumAddress } from 'src/utils/checksumAddress' - -export const buildModuleTxServiceUrl = (safeAddress: string): string => { - const address = checksumAddress(safeAddress) - const url = getSafeServiceBaseUrl(address) - - return `${url}/module-transactions/` -} diff --git a/src/logic/safe/utils/buildSafeCreationTxUrl.ts b/src/logic/safe/utils/buildSafeCreationTxUrl.ts deleted file mode 100644 index 624bc608..00000000 --- a/src/logic/safe/utils/buildSafeCreationTxUrl.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { getSafeServiceBaseUrl } from 'src/config' -import { checksumAddress } from 'src/utils/checksumAddress' - -export const buildSafeCreationTxUrl = (safeAddress: string): string => { - const address = checksumAddress(safeAddress) - return `${getSafeServiceBaseUrl(address)}/creation/` -} diff --git a/src/logic/tokens/utils/tokenHelpers.ts b/src/logic/tokens/utils/tokenHelpers.ts index 7fbdcd0a..9dc7d305 100644 --- a/src/logic/tokens/utils/tokenHelpers.ts +++ b/src/logic/tokens/utils/tokenHelpers.ts @@ -4,11 +4,10 @@ import { AbiItem } from 'web3-utils' import { getNetworkInfo } from 'src/config' import generateBatchRequests from 'src/logic/contracts/generateBatchRequests' import { getTokenInfos } from 'src/logic/tokens/store/actions/fetchTokens' -import { isSendERC721Transaction } from 'src/logic/collectibles/utils' import { makeToken, Token } from 'src/logic/tokens/store/model/token' import { ALTERNATIVE_TOKEN_ABI } from 'src/logic/tokens/utils/alternativeAbi' import { web3ReadOnly as web3 } from 'src/logic/wallets/getWeb3' -import { BuildTx, isEmptyData, ServiceTx } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' +import { BuildTx, isEmptyData } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' import { CALL } from 'src/logic/safe/transactions' import { sameAddress } from 'src/logic/wallets/ethAddresses' @@ -69,21 +68,6 @@ export const getERC20DecimalsAndSymbol = async ( return tokenInfo } -export const isSendERC20Transaction = async (tx: BuildTx['tx']): Promise => { - let isSendTokenTx = !isSendERC721Transaction(tx) && isTokenTransfer(tx) - - if (isSendTokenTx) { - const { decimals, symbol } = await getERC20DecimalsAndSymbol((tx as ServiceTx).to) - - // some contracts may implement the same methods as in ERC20 standard - // we may falsely treat them as tokens, so in case we get any errors when getting token info - // we fallback to displaying custom transaction - isSendTokenTx = decimals !== null && symbol !== null - } - - return isSendTokenTx -} - export type GetTokenByAddress = { tokenAddress: string tokens: List diff --git a/src/logic/wallets/store/actions/fetchProvider.ts b/src/logic/wallets/store/actions/fetchProvider.ts index e6e40153..f75794b1 100644 --- a/src/logic/wallets/store/actions/fetchProvider.ts +++ b/src/logic/wallets/store/actions/fetchProvider.ts @@ -8,12 +8,10 @@ import { NOTIFICATIONS, enhanceSnackbarForAction } from 'src/logic/notifications import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar' import { getProviderInfo, getWeb3 } from 'src/logic/wallets/getWeb3' import { makeProvider } from 'src/logic/wallets/store/model/provider' -import { updateStoredTransactionsStatus } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers' export const processProviderResponse = (dispatch, provider) => { const walletRecord = makeProvider(provider) dispatch(addProvider(walletRecord)) - updateStoredTransactionsStatus(dispatch, walletRecord) } const handleProviderNotification = (provider, dispatch) => { diff --git a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx index 6bf71077..4a9c90f9 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx @@ -13,7 +13,7 @@ import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' import { safeFeaturesEnabledSelector } from 'src/logic/safe/store/selectors' import { useStyles } from 'src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style' -import ContractInteractionIcon from 'src/routes/safe/components/Transactions/TxsTable/TxType/assets/custom.svg' +import ContractInteractionIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg' import Collectible from '../assets/collectibles.svg' import Token from '../assets/token.svg' diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/HexEncodedData.tsx b/src/routes/safe/components/Transactions/GatewayTransactions/HexEncodedData.tsx deleted file mode 100644 index 8f5d4a7e..00000000 --- a/src/routes/safe/components/Transactions/GatewayTransactions/HexEncodedData.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Text } from '@gnosis.pm/safe-react-components' -import React, { ReactElement } from 'react' - -import { TxData as LegacyTxData } from 'src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription' - -export const HexEncodedData = ({ hexData }: { hexData: string }): ReactElement => ( -
- - Data (hex encoded): - - -
-) diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/ActionModal.tsx b/src/routes/safe/components/Transactions/TxList/ActionModal.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/ActionModal.tsx rename to src/routes/safe/components/Transactions/TxList/ActionModal.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/AddressInfo.tsx b/src/routes/safe/components/Transactions/TxList/AddressInfo.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/AddressInfo.tsx rename to src/routes/safe/components/Transactions/TxList/AddressInfo.tsx diff --git a/src/routes/safe/components/Transactions/TxList/HexEncodedData.tsx b/src/routes/safe/components/Transactions/TxList/HexEncodedData.tsx new file mode 100644 index 00000000..81955e08 --- /dev/null +++ b/src/routes/safe/components/Transactions/TxList/HexEncodedData.tsx @@ -0,0 +1,68 @@ +import { Text } from '@gnosis.pm/safe-react-components' +import { createStyles, makeStyles } from '@material-ui/core/styles' +import React, { ReactElement, useState } from 'react' + +import Paragraph from 'src/components/layout/Paragraph' +import LinkWithRef from 'src/components/layout/Link' +import { shortVersionOf } from 'src/logic/wallets/ethAddresses' + +export const styles = createStyles({ + txDataParagraph: { + whiteSpace: 'normal', + }, + linkTxData: { + textDecoration: 'underline', + cursor: 'pointer', + }, +}) + +const useStyles = makeStyles(styles) + +export const HexEncodedData = ({ hexData }: { hexData: string }): ReactElement => { + const classes = useStyles() + const [showTxData, setShowTxData] = useState(false) + const showExpandBtn = hexData.length > 20 + + return ( +
+ + Data (hex encoded): + + + {showExpandBtn ? ( + <> + {showTxData ? ( + <> + {hexData}{' '} + setShowTxData(false)} + rel="noopener noreferrer" + target="_blank" + > + Show Less + + + ) : ( + <> + {shortVersionOf(hexData, 20)}{' '} + setShowTxData(true)} + rel="noopener noreferrer" + target="_blank" + > + Show More + + + )} + + ) : ( + hexData + )} + +
+ ) +} diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/HistoryTransactions.tsx b/src/routes/safe/components/Transactions/TxList/HistoryTransactions.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/HistoryTransactions.tsx rename to src/routes/safe/components/Transactions/TxList/HistoryTransactions.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/HistoryTxList.tsx b/src/routes/safe/components/Transactions/TxList/HistoryTxList.tsx similarity index 96% rename from src/routes/safe/components/Transactions/GatewayTransactions/HistoryTxList.tsx rename to src/routes/safe/components/Transactions/TxList/HistoryTxList.tsx index b57f7347..edba291f 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/HistoryTxList.tsx +++ b/src/routes/safe/components/Transactions/TxList/HistoryTxList.tsx @@ -1,7 +1,7 @@ import React, { ReactElement, useContext } from 'react' import { TransactionDetails } from 'src/logic/safe/store/models/types/gateway.d' -import { TxsInfiniteScrollContext } from 'src/routes/safe/components/Transactions/GatewayTransactions/TxsInfiniteScroll' +import { TxsInfiniteScrollContext } from 'src/routes/safe/components/Transactions/TxList/TxsInfiniteScroll' import { formatWithSchema } from 'src/utils/date' import { sameString } from 'src/utils/strings' import { StyledTransactions, StyledTransactionsGroup, SubTitle } from './styled' diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/InfoDetails.tsx b/src/routes/safe/components/Transactions/TxList/InfoDetails.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/InfoDetails.tsx rename to src/routes/safe/components/Transactions/TxList/InfoDetails.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/MethodDetails.tsx b/src/routes/safe/components/Transactions/TxList/MethodDetails.tsx similarity index 93% rename from src/routes/safe/components/Transactions/GatewayTransactions/MethodDetails.tsx rename to src/routes/safe/components/Transactions/TxList/MethodDetails.tsx index d849aa8a..d4b38121 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/MethodDetails.tsx +++ b/src/routes/safe/components/Transactions/TxList/MethodDetails.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components' import { DataDecoded } from 'src/logic/safe/store/models/types/gateway.d' import { isArrayParameter } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils' -import Value from 'src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/Value' +import Value from 'src/routes/safe/components/Transactions/TxList/MethodValue' const TxDetailsMethodParam = styled.div<{ isArrayParameter: boolean }>` padding-left: 24px; diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/Value.tsx b/src/routes/safe/components/Transactions/TxList/MethodValue.tsx similarity index 100% rename from src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/Value.tsx rename to src/routes/safe/components/Transactions/TxList/MethodValue.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/MultiSendDetails.tsx b/src/routes/safe/components/Transactions/TxList/MultiSendDetails.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/MultiSendDetails.tsx rename to src/routes/safe/components/Transactions/TxList/MultiSendDetails.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/OwnerRow.tsx b/src/routes/safe/components/Transactions/TxList/OwnerRow.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/OwnerRow.tsx rename to src/routes/safe/components/Transactions/TxList/OwnerRow.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/QueueTransactions.tsx b/src/routes/safe/components/Transactions/TxList/QueueTransactions.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/QueueTransactions.tsx rename to src/routes/safe/components/Transactions/TxList/QueueTransactions.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/QueueTxList.tsx b/src/routes/safe/components/Transactions/TxList/QueueTxList.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/QueueTxList.tsx rename to src/routes/safe/components/Transactions/TxList/QueueTxList.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/SpendingLimitDetails.tsx b/src/routes/safe/components/Transactions/TxList/SpendingLimitDetails.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/SpendingLimitDetails.tsx rename to src/routes/safe/components/Transactions/TxList/SpendingLimitDetails.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TokenTransferAmount.tsx b/src/routes/safe/components/Transactions/TxList/TokenTransferAmount.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TokenTransferAmount.tsx rename to src/routes/safe/components/Transactions/TxList/TokenTransferAmount.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxActionProvider.tsx b/src/routes/safe/components/Transactions/TxList/TxActionProvider.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxActionProvider.tsx rename to src/routes/safe/components/Transactions/TxList/TxActionProvider.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxCollapsed.tsx b/src/routes/safe/components/Transactions/TxList/TxCollapsed.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxCollapsed.tsx rename to src/routes/safe/components/Transactions/TxList/TxCollapsed.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxCollapsedActions.tsx b/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxCollapsedActions.tsx rename to src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxData.tsx b/src/routes/safe/components/Transactions/TxList/TxData.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxData.tsx rename to src/routes/safe/components/Transactions/TxList/TxData.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxDetails.tsx b/src/routes/safe/components/Transactions/TxList/TxDetails.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxDetails.tsx rename to src/routes/safe/components/Transactions/TxList/TxDetails.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxExpandedActions.tsx b/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx similarity index 95% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxExpandedActions.tsx rename to src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx index 37175329..71e0c28c 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/TxExpandedActions.tsx +++ b/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx @@ -2,7 +2,7 @@ import { Button } from '@gnosis.pm/safe-react-components' import React, { ReactElement } from 'react' import { Transaction } from 'src/logic/safe/store/models/types/gateway.d' -import { useActionButtonsHandlers } from 'src/routes/safe/components/Transactions/GatewayTransactions/hooks/useActionButtonsHandlers' +import { useActionButtonsHandlers } from 'src/routes/safe/components/Transactions/TxList/hooks/useActionButtonsHandlers' type TxExpandedActionsProps = { transaction: Transaction diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxHistoryCollapsed.tsx b/src/routes/safe/components/Transactions/TxList/TxHistoryCollapsed.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxHistoryCollapsed.tsx rename to src/routes/safe/components/Transactions/TxList/TxHistoryCollapsed.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxHistoryRow.tsx b/src/routes/safe/components/Transactions/TxList/TxHistoryRow.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxHistoryRow.tsx rename to src/routes/safe/components/Transactions/TxList/TxHistoryRow.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxHoverProvider.tsx b/src/routes/safe/components/Transactions/TxList/TxHoverProvider.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxHoverProvider.tsx rename to src/routes/safe/components/Transactions/TxList/TxHoverProvider.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxInfo.tsx b/src/routes/safe/components/Transactions/TxList/TxInfo.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxInfo.tsx rename to src/routes/safe/components/Transactions/TxList/TxInfo.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxInfoCreation.tsx b/src/routes/safe/components/Transactions/TxList/TxInfoCreation.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxInfoCreation.tsx rename to src/routes/safe/components/Transactions/TxList/TxInfoCreation.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxInfoDetails.tsx b/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxInfoDetails.tsx rename to src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxInfoSettings.tsx b/src/routes/safe/components/Transactions/TxList/TxInfoSettings.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxInfoSettings.tsx rename to src/routes/safe/components/Transactions/TxList/TxInfoSettings.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxInfoTransfer.tsx b/src/routes/safe/components/Transactions/TxList/TxInfoTransfer.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxInfoTransfer.tsx rename to src/routes/safe/components/Transactions/TxList/TxInfoTransfer.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxLocationProvider.tsx b/src/routes/safe/components/Transactions/TxList/TxLocationProvider.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxLocationProvider.tsx rename to src/routes/safe/components/Transactions/TxList/TxLocationProvider.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxOwners.tsx b/src/routes/safe/components/Transactions/TxList/TxOwners.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxOwners.tsx rename to src/routes/safe/components/Transactions/TxList/TxOwners.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxQueueCollapsed.tsx b/src/routes/safe/components/Transactions/TxList/TxQueueCollapsed.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxQueueCollapsed.tsx rename to src/routes/safe/components/Transactions/TxList/TxQueueCollapsed.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxQueueRow.tsx b/src/routes/safe/components/Transactions/TxList/TxQueueRow.tsx similarity index 95% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxQueueRow.tsx rename to src/routes/safe/components/Transactions/TxList/TxQueueRow.tsx index 5405ddaf..f72f8eb6 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/TxQueueRow.tsx +++ b/src/routes/safe/components/Transactions/TxList/TxQueueRow.tsx @@ -2,7 +2,7 @@ import { AccordionDetails } from '@gnosis.pm/safe-react-components' import React, { ReactElement, useContext, useEffect, useState } from 'react' import { Transaction } from 'src/logic/safe/store/models/types/gateway.d' -import { useTransactionActions } from 'src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionActions' +import { useTransactionActions } from 'src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions' import { NoPaddingAccordion, StyledAccordionSummary } from './styled' import { TxDetails } from './TxDetails' import { TxHoverContext } from './TxHoverProvider' diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxSummary.tsx b/src/routes/safe/components/Transactions/TxList/TxSummary.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxSummary.tsx rename to src/routes/safe/components/Transactions/TxList/TxSummary.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/TxsInfiniteScroll.tsx b/src/routes/safe/components/Transactions/TxList/TxsInfiniteScroll.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/TxsInfiniteScroll.tsx rename to src/routes/safe/components/Transactions/TxList/TxsInfiniteScroll.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/check-circle-green.svg b/src/routes/safe/components/Transactions/TxList/assets/check-circle-green.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/check-circle-green.svg rename to src/routes/safe/components/Transactions/TxList/assets/check-circle-green.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/custom.svg b/src/routes/safe/components/Transactions/TxList/assets/custom.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/custom.svg rename to src/routes/safe/components/Transactions/TxList/assets/custom.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/incoming.svg b/src/routes/safe/components/Transactions/TxList/assets/incoming.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/incoming.svg rename to src/routes/safe/components/Transactions/TxList/assets/incoming.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/no-transactions.svg b/src/routes/safe/components/Transactions/TxList/assets/no-transactions.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/no-transactions.svg rename to src/routes/safe/components/Transactions/TxList/assets/no-transactions.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/outgoing.svg b/src/routes/safe/components/Transactions/TxList/assets/outgoing.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/outgoing.svg rename to src/routes/safe/components/Transactions/TxList/assets/outgoing.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/plus-circle-green.svg b/src/routes/safe/components/Transactions/TxList/assets/plus-circle-green.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/plus-circle-green.svg rename to src/routes/safe/components/Transactions/TxList/assets/plus-circle-green.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/settings.svg b/src/routes/safe/components/Transactions/TxList/assets/settings.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/settings.svg rename to src/routes/safe/components/Transactions/TxList/assets/settings.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/transactions-list-active.svg b/src/routes/safe/components/Transactions/TxList/assets/transactions-list-active.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/transactions-list-active.svg rename to src/routes/safe/components/Transactions/TxList/assets/transactions-list-active.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/assets/transactions-list-inactive.svg b/src/routes/safe/components/Transactions/TxList/assets/transactions-list-inactive.svg similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/assets/transactions-list-inactive.svg rename to src/routes/safe/components/Transactions/TxList/assets/transactions-list-inactive.svg diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useActionButtonsHandlers.ts b/src/routes/safe/components/Transactions/TxList/hooks/useActionButtonsHandlers.ts similarity index 94% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useActionButtonsHandlers.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useActionButtonsHandlers.ts index 3921d215..2226c29e 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useActionButtonsHandlers.ts +++ b/src/routes/safe/components/Transactions/TxList/hooks/useActionButtonsHandlers.ts @@ -3,11 +3,11 @@ import { useSelector } from 'react-redux' import { Transaction } from 'src/logic/safe/store/models/types/gateway.d' import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { addressInList } from 'src/routes/safe/components/Transactions/GatewayTransactions/utils' +import { addressInList } from 'src/routes/safe/components/Transactions/TxList/utils' import { useTransactionActions } from './useTransactionActions' -import { TransactionActionStateContext } from 'src/routes/safe/components/Transactions/GatewayTransactions/TxActionProvider' -import { TxHoverContext } from 'src/routes/safe/components/Transactions/GatewayTransactions/TxHoverProvider' -import { TxLocationContext } from 'src/routes/safe/components/Transactions/GatewayTransactions/TxLocationProvider' +import { TransactionActionStateContext } from 'src/routes/safe/components/Transactions/TxList/TxActionProvider' +import { TxHoverContext } from 'src/routes/safe/components/Transactions/TxList/TxHoverProvider' +import { TxLocationContext } from 'src/routes/safe/components/Transactions/TxList/TxLocationProvider' type ActionButtonsHandlers = { canCancel: boolean diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useAssetInfo.ts b/src/routes/safe/components/Transactions/TxList/hooks/useAssetInfo.ts similarity index 93% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useAssetInfo.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useAssetInfo.ts index dc6b3467..55ac92c8 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useAssetInfo.ts +++ b/src/routes/safe/components/Transactions/TxList/hooks/useAssetInfo.ts @@ -9,8 +9,7 @@ import { SettingsChange, TransactionInfo, } from 'src/logic/safe/store/models/types/gateway.d' -import { getTxAmount } from 'src/routes/safe/components/Transactions/GatewayTransactions/utils' -import { NOT_AVAILABLE } from 'src/routes/safe/components/Transactions/TxsTable/columns' +import { getTxAmount, NOT_AVAILABLE } from 'src/routes/safe/components/Transactions/TxList/utils' export type TokenTransferAsset = { type: 'Transfer' diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useHistoryTransactions.ts b/src/routes/safe/components/Transactions/TxList/hooks/useHistoryTransactions.ts similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useHistoryTransactions.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useHistoryTransactions.ts diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/usePagedHistoryTransactions.ts b/src/routes/safe/components/Transactions/TxList/hooks/usePagedHistoryTransactions.ts similarity index 96% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/usePagedHistoryTransactions.ts rename to src/routes/safe/components/Transactions/TxList/hooks/usePagedHistoryTransactions.ts index 9f685957..44ef7310 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/usePagedHistoryTransactions.ts +++ b/src/routes/safe/components/Transactions/TxList/hooks/usePagedHistoryTransactions.ts @@ -5,7 +5,7 @@ import { loadPagedHistoryTransactions } from 'src/logic/safe/store/actions/trans import { addHistoryTransactions } from 'src/logic/safe/store/actions/transactions/gatewayTransactions' import { TransactionDetails } from 'src/logic/safe/store/models/types/gateway.d' import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' -import { useHistoryTransactions } from 'src/routes/safe/components/Transactions/GatewayTransactions/hooks/useHistoryTransactions' +import { useHistoryTransactions } from 'src/routes/safe/components/Transactions/TxList/hooks/useHistoryTransactions' type PagedTransactions = { count: number diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/usePagedQueuedTransactions.ts b/src/routes/safe/components/Transactions/TxList/hooks/usePagedQueuedTransactions.ts similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/usePagedQueuedTransactions.ts rename to src/routes/safe/components/Transactions/TxList/hooks/usePagedQueuedTransactions.ts diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useQueueTransactions.ts b/src/routes/safe/components/Transactions/TxList/hooks/useQueueTransactions.ts similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useQueueTransactions.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useQueueTransactions.ts diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionActions.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts similarity index 97% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionActions.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts index 1f3042fb..5e3b7566 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionActions.ts +++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts @@ -6,8 +6,8 @@ import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selector import { getQueuedTransactionsByNonce } from 'src/logic/safe/store/selectors/gatewayTransactions' import { sameAddress } from 'src/logic/wallets/ethAddresses' import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { TxLocationContext } from 'src/routes/safe/components/Transactions/GatewayTransactions/TxLocationProvider' -import { isCancelTransaction } from 'src/routes/safe/components/Transactions/GatewayTransactions/utils' +import { TxLocationContext } from 'src/routes/safe/components/Transactions/TxList/TxLocationProvider' +import { isCancelTransaction } from 'src/routes/safe/components/Transactions/TxList/utils' import { grantedSelector } from 'src/routes/safe/container/selector' import { AppReduxState } from 'src/store' diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionDetails.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionDetails.ts similarity index 96% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionDetails.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useTransactionDetails.ts index 060d4f2a..8c569093 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionDetails.ts +++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionDetails.ts @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from 'react-redux' import { ExpandedTxDetails } from 'src/logic/safe/store/models/types/gateway.d' import { fetchTransactionDetails } from 'src/logic/safe/store/actions/fetchTransactionDetails' -import { TxLocationContext } from 'src/routes/safe/components/Transactions/GatewayTransactions/TxLocationProvider' +import { TxLocationContext } from 'src/routes/safe/components/Transactions/TxList/TxLocationProvider' import { getTransactionDetails } from 'src/logic/safe/store/selectors/gatewayTransactions' import { AppReduxState } from 'src/store' diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionStatus.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts similarity index 98% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionStatus.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts index 299bb0b2..e64637b7 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionStatus.ts +++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts @@ -10,7 +10,7 @@ import { Transaction, } from 'src/logic/safe/store/models/types/gateway.d' import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { addressInList } from 'src/routes/safe/components/Transactions/GatewayTransactions/utils' +import { addressInList } from 'src/routes/safe/components/Transactions/TxList/utils' export type TransactionStatusProps = { color: ThemeColors diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionType.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts similarity index 92% rename from src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionType.ts rename to src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts index 61e54268..7d15b560 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionType.ts +++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts @@ -3,11 +3,11 @@ import { useSelector } from 'react-redux' import { Transaction } from 'src/logic/safe/store/models/types/gateway.d' import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' -import CustomTxIcon from 'src/routes/safe/components/Transactions/GatewayTransactions/assets/custom.svg' -import IncomingTxIcon from 'src/routes/safe/components/Transactions/GatewayTransactions/assets/incoming.svg' -import OutgoingTxIcon from 'src/routes/safe/components/Transactions/GatewayTransactions/assets/outgoing.svg' -import SettingsTxIcon from 'src/routes/safe/components/Transactions/GatewayTransactions/assets/settings.svg' -import { isCancelTransaction } from 'src/routes/safe/components/Transactions/GatewayTransactions/utils' +import CustomTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg' +import IncomingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/incoming.svg' +import OutgoingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/outgoing.svg' +import SettingsTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/settings.svg' +import { isCancelTransaction } from 'src/routes/safe/components/Transactions/TxList/utils' export type TxTypeProps = { icon: string diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/index.tsx b/src/routes/safe/components/Transactions/TxList/index.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/index.tsx rename to src/routes/safe/components/Transactions/TxList/index.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/modals/ApproveTxModal.tsx b/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx similarity index 99% rename from src/routes/safe/components/Transactions/GatewayTransactions/modals/ApproveTxModal.tsx rename to src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx index d6b1694a..eca47c1d 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/modals/ApproveTxModal.tsx +++ b/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx @@ -26,7 +26,7 @@ import { TxParametersDetail } from 'src/routes/safe/components/Transactions/help import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { isThresholdReached } from 'src/routes/safe/components/Transactions/GatewayTransactions/hooks/useTransactionActions' +import { isThresholdReached } from 'src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions' import { Overwrite } from 'src/types/helpers' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { makeConfirmation } from 'src/logic/safe/store/models/confirmation' diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/modals/RejectTxModal.tsx b/src/routes/safe/components/Transactions/TxList/modals/RejectTxModal.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/modals/RejectTxModal.tsx rename to src/routes/safe/components/Transactions/TxList/modals/RejectTxModal.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/modals/style.ts b/src/routes/safe/components/Transactions/TxList/modals/style.ts similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/modals/style.ts rename to src/routes/safe/components/Transactions/TxList/modals/style.ts diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/styled.tsx b/src/routes/safe/components/Transactions/TxList/styled.tsx similarity index 100% rename from src/routes/safe/components/Transactions/GatewayTransactions/styled.tsx rename to src/routes/safe/components/Transactions/TxList/styled.tsx diff --git a/src/routes/safe/components/Transactions/GatewayTransactions/utils.ts b/src/routes/safe/components/Transactions/TxList/utils.ts similarity index 85% rename from src/routes/safe/components/Transactions/GatewayTransactions/utils.ts rename to src/routes/safe/components/Transactions/TxList/utils.ts index 7bd9a5d9..d461e8cc 100644 --- a/src/routes/safe/components/Transactions/GatewayTransactions/utils.ts +++ b/src/routes/safe/components/Transactions/TxList/utils.ts @@ -9,21 +9,11 @@ import { TransactionInfo, Transfer, } from 'src/logic/safe/store/models/types/gateway.d' -import { SafeModuleTransaction } from 'src/logic/safe/store/models/types/transaction' import { formatAmount } from 'src/logic/tokens/utils/formatAmount' import { sameAddress } from 'src/logic/wallets/ethAddresses' import { sameString } from 'src/utils/strings' -export const TX_TABLE_ID = 'id' -export const TX_TABLE_TYPE_ID = 'type' -export const TX_TABLE_DATE_ID = 'date' -export const TX_TABLE_AMOUNT_ID = 'amount' -export const TX_TABLE_STATUS_ID = 'status' -export const TX_TABLE_RAW_TX_ID = 'tx' -export const TX_TABLE_RAW_CANCEL_TX_ID = 'cancelTx' -export const TX_TABLE_EXPAND_ICON = 'expand' - export const NOT_AVAILABLE = 'n/a' interface AmountData { @@ -100,17 +90,6 @@ export const getTxTokenData = (txInfo: Transfer): txTokenData => { } } -export interface TableData { - amount: string - cancelTx?: Transaction - date: string - dateOrder?: number - id: string - status: string - tx: Transaction | SafeModuleTransaction - type: any -} - // TODO: isCancel // how can we be sure that it's a cancel tx without asking for tx-details? // can the client-gateway service provide info about the tx, Like: `isCancelTransaction: boolean`? diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx deleted file mode 100644 index 9b9431b5..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx +++ /dev/null @@ -1,242 +0,0 @@ -import Checkbox from '@material-ui/core/Checkbox' -import FormControlLabel from '@material-ui/core/FormControlLabel' -import IconButton from '@material-ui/core/IconButton' -import { makeStyles } from '@material-ui/core/styles' -import Close from '@material-ui/icons/Close' -import React, { useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' - -import { styles } from './style' - -import Modal from 'src/components/Modal' -import Block from 'src/components/layout/Block' -import Bold from 'src/components/layout/Bold' -import Button from 'src/components/layout/Button' -import Hairline from 'src/components/layout/Hairline' -import Paragraph from 'src/components/layout/Paragraph' -import Row from 'src/components/layout/Row' -import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions' -import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { processTransaction } from 'src/logic/safe/store/actions/processTransaction' - -import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' -import { Transaction } from 'src/logic/safe/store/models/types/transaction' -import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFees } from 'src/components/TransactionsFees' -import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters' -import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail' -import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters' - -const useStyles = makeStyles(styles) - -export const APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID = 'approve-tx-modal-submit-btn' -export const REJECT_TX_MODAL_SUBMIT_BTN_TEST_ID = 'reject-tx-modal-submit-btn' - -const getModalTitleAndDescription = (thresholdReached, isCancelTx) => { - const modalInfo = { - title: 'Execute Transaction Rejection', - description: 'This action will execute this transaction.', - } - - if (isCancelTx) { - return modalInfo - } - - if (thresholdReached) { - modalInfo.title = 'Execute Transaction' - modalInfo.description = - 'This action will execute this transaction. A separate Transaction will be performed to submit the execution.' - } else { - modalInfo.title = 'Approve Transaction' - modalInfo.description = - 'This action will approve this transaction. A separate Transaction will be performed to submit the approval.' - } - - return modalInfo -} - -type Props = { - onClose: () => void - canExecute: boolean - isCancelTx?: boolean - isOpen: boolean - thresholdReached: boolean - tx: Transaction - txParameters: TxParameters -} - -export const ApproveTxModal = ({ - onClose, - canExecute, - isCancelTx = false, - isOpen, - thresholdReached, - tx, -}: Props): React.ReactElement => { - const dispatch = useDispatch() - const userAddress = useSelector(userAccountSelector) - const classes = useStyles() - const threshold = useSelector(safeThresholdSelector) || 1 - const safeAddress = useSelector(safeParamAddressFromStateSelector) - const [approveAndExecute, setApproveAndExecute] = useState(canExecute) - const { description, title } = getModalTitleAndDescription(thresholdReached, isCancelTx) - const oneConfirmationLeft = !thresholdReached && tx.confirmations.size + 1 === threshold - const isTheTxReadyToBeExecuted = oneConfirmationLeft ? true : thresholdReached - const [manualGasPrice, setManualGasPrice] = useState() - - const { - gasLimit, - gasPriceFormatted, - gasCostFormatted, - txEstimationExecutionStatus, - isExecution, - isOffChainSignature, - isCreation, - } = useEstimateTransactionGas({ - txRecipient: tx.recipient, - txData: tx.data || '', - txConfirmations: tx.confirmations, - txAmount: tx.value, - preApprovingOwner: approveAndExecute ? userAddress : undefined, - safeTxGas: tx.safeTxGas, - operation: tx.operation, - manualGasPrice, - }) - - const handleExecuteCheckbox = () => setApproveAndExecute((prevApproveAndExecute) => !prevApproveAndExecute) - - const approveTx = (txParameters: TxParameters) => { - dispatch( - processTransaction({ - safeAddress, - tx: tx as any, - userAddress, - notifiedTransaction: TX_NOTIFICATION_TYPES.CONFIRMATION_TX, - approveAndExecute: canExecute && approveAndExecute && isTheTxReadyToBeExecuted, - ethParameters: txParameters, - thresholdReached, - }), - ) - onClose() - } - - const getParametersStatus = () => { - if (canExecute || approveAndExecute) { - return 'SAFE_DISABLED' - } - - return 'DISABLED' - } - - const closeEditModalCallback = (txParameters: TxParameters) => { - const oldGasPrice = Number(gasPriceFormatted) - const newGasPrice = Number(txParameters.ethGasPrice) - - if (newGasPrice && oldGasPrice !== newGasPrice) { - setManualGasPrice(txParameters.ethGasPrice) - } - } - - return ( - - - {(txParameters, toggleEditMode) => { - return ( - <> - {/* Header */} - - - {title} - - - - - - - - - {/* Tx info */} - - - {description} - - Transaction nonce: -
- {tx.nonce} -
- - {oneConfirmationLeft && canExecute && ( - <> - - Approving this transaction executes it right away. - {!isCancelTx && - ' If you want approve but execute the transaction manually later, click on the checkbox below.'} - - - {!isCancelTx && ( - - } - label="Execute transaction" - data-testid="execute-checkbox" - /> - )} - - )} - - {/* Tx Parameters */} - {approveAndExecute && ( - - )} -
-
- {txEstimationExecutionStatus === EstimationStatus.LOADING ? null : ( - - - - )} - {/* Footer */} - - - - - - ) - }} -
-
- ) -} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/style.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/style.ts deleted file mode 100644 index 19a99e1e..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/style.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createStyles } from '@material-ui/core' -import { background, border, lg, md, sm } from 'src/theme/variables' - -export const styles = createStyles({ - heading: { - padding: `${sm} ${lg}`, - justifyContent: 'space-between', - boxSizing: 'border-box', - height: '74px', - }, - headingText: { - fontSize: lg, - }, - closeIcon: { - height: '35px', - width: '35px', - }, - container: { - padding: `${md} ${lg}`, - }, - buttonRow: { - height: '84px', - justifyContent: 'center', - position: 'relative', - bottom: 0, - width: '100%', - borderTop: `1px solid ${border}`, - }, - nonceNumber: { - marginTop: sm, - fontSize: md, - }, - gasCostsContainer: { - backgroundColor: background, - padding: `0 ${lg}`, - }, -}) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/CreationTx/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/CreationTx/index.tsx deleted file mode 100644 index bae4e469..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/CreationTx/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { makeStyles } from '@material-ui/core/styles' -import React from 'react' -import { EthHashInfo } from '@gnosis.pm/safe-react-components' - -import { Transaction } from 'src/logic/safe/store/models/types/transaction' - -import { formatDate } from 'src/routes/safe/components/Transactions/TxsTable/columns' -import Bold from 'src/components/layout/Bold' -import Paragraph from 'src/components/layout/Paragraph' -import Block from 'src/components/layout/Block' -import { TransactionTypes } from 'src/logic/safe/store/models/types/transaction' -import { getExplorerInfo } from 'src/config' - -const useStyles = makeStyles({ - address: { - height: '20px', - }, - txData: { - alignItems: 'center', - display: 'flex', - lineHeight: '1.6', - }, - txHash: { - paddingRight: '3px', - }, -}) - -type Props = { - tx?: Transaction -} - -export const CreationTx = ({ tx }: Props): React.ReactElement | null => { - const classes = useStyles() - const isCreationTx = tx?.type === TransactionTypes.CREATION - - if (!tx || !isCreationTx) { - return null - } - const explorerUrl = getExplorerInfo(tx.creator) - const scanBlockFactoryAddressUrl = getExplorerInfo(tx.factoryAddress) - const scanBlockMasterCopyUrl = getExplorerInfo(tx.masterCopy) - - return isCreationTx ? ( - <> - - Created: - {formatDate(tx.created)} - - - Creator: - {tx.creator ? : 'n/a'} - - - Factory: - {tx.factoryAddress ? ( - - ) : ( - 'n/a' - )} - - - Mastercopy: - {tx.masterCopy ? ( - - ) : ( - 'n/a' - )} - - - ) : null -} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTx/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTx/index.tsx deleted file mode 100644 index 7d9e1ccd..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTx/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react' -import { INCOMING_TX_TYPES } from 'src/logic/safe/store/models/incomingTransaction' -import { formatDate } from 'src/routes/safe/components/Transactions/TxsTable/columns' -import Bold from 'src/components/layout/Bold' -import Paragraph from 'src/components/layout/Paragraph' - -export const IncomingTx = ({ tx }) => { - if (!tx) { - return null - } - - const isIncomingTx = !!INCOMING_TX_TYPES[tx.type] - - return isIncomingTx ? ( - - Created: - {formatDate(tx.executionDate)} - - ) : null -} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx deleted file mode 100644 index 3243d867..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { makeStyles } from '@material-ui/core/styles' -import React from 'react' -import { useSelector } from 'react-redux' - -import { EtherscanLink } from 'src/components/EtherscanLink' -import Block from 'src/components/layout/Block' -import Bold from 'src/components/layout/Bold' -import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' -import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' -import { getIncomingTxAmount } from 'src/routes/safe/components/Transactions/TxsTable/columns' -import { lg, md } from 'src/theme/variables' - -export const TRANSACTIONS_DESC_INCOMING_TEST_ID = 'tx-description-incoming' - -const useStyles = makeStyles({ - txDataContainer: { - paddingTop: lg, - paddingLeft: md, - paddingBottom: md, - borderRight: '2px solid rgb(232, 231, 230)', - }, -}) - -const TransferDescription = ({ from, txFromName, value = '' }) => ( - - Received {value} from: -
- {txFromName ? ( - - ) : ( - - )} -
-) - -const IncomingTxDescription = ({ tx }) => { - const classes = useStyles() - const txFromName = useSelector((state) => getNameFromAddressBookSelector(state, tx.from)) - return ( - - - - ) -} - -export default IncomingTxDescription diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OutgoingTx/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OutgoingTx/index.tsx deleted file mode 100644 index 674fb8f7..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OutgoingTx/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react' - -import { formatDate } from 'src/routes/safe/components/Transactions/TxsTable/columns' -import Bold from 'src/components/layout/Bold' -import Paragraph from 'src/components/layout/Paragraph' -import { TransactionTypes } from 'src/logic/safe/store/models/types/transaction' - -export const OutgoingTx = ({ tx }) => { - if (!tx) { - return null - } - - const isOutgoingTx = [ - TransactionTypes.OUTGOING, - TransactionTypes.UPGRADE, - TransactionTypes.CUSTOM, - TransactionTypes.SETTINGS, - TransactionTypes.COLLECTIBLE, - TransactionTypes.TOKEN, - ].includes(tx.type) - - return isOutgoingTx ? ( - <> - - Created: - {formatDate(tx.submissionDate)} - - {tx.executionDate && ( - - Executed: - {formatDate(tx.executionDate)} - - )} - {tx.refundParams && ( - - Refund: - max. {tx.refundParams.fee} {tx.refundParams.symbol} - - )} - {tx.operation === 1 && ( - - Delegate Call - - )} - {tx.operation === 2 && ( - - Contract Creation - - )} - - ) : null -} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx deleted file mode 100644 index 06bbaec6..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import { makeStyles } from '@material-ui/core/styles' -import cn from 'classnames' -import React from 'react' -import { useSelector } from 'react-redux' -import { EthHashInfo } from '@gnosis.pm/safe-react-components' - -import CancelSmallFilledCircle from './assets/cancel-small-filled.svg' -import ConfirmSmallFilledCircle from './assets/confirm-small-filled.svg' -import ConfirmSmallGreenCircle from './assets/confirm-small-green.svg' -import ConfirmSmallGreyCircle from './assets/confirm-small-grey.svg' -import ConfirmSmallRedCircle from './assets/confirm-small-red.svg' -import PendingSmallYellowCircle from './assets/confirm-small-yellow.svg' -import { styles } from './style' - -import Block from 'src/components/layout/Block' -import Button from 'src/components/layout/Button' -import Img from 'src/components/layout/Img' -import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' -import { OwnersWithoutConfirmations } from './index' -import { getExplorerInfo } from 'src/config' - -export const CONFIRM_TX_BTN_TEST_ID = 'confirm-btn' -export const EXECUTE_TX_BTN_TEST_ID = 'execute-btn' -export const REJECT_TX_BTN_TEST_ID = 'reject-btn' -export const EXECUTE_REJECT_TX_BTN_TEST_ID = 'execute-reject-btn' - -type OwnerComponentProps = { - executor: string - isCancelTx?: boolean - onTxConfirm?: () => void - onTxExecute?: () => void - onTxReject?: () => void - ownersUnconfirmed: OwnersWithoutConfirmations - ownersWhoConfirmed: string[] - showConfirmBtn?: boolean - showExecuteBtn?: boolean - showExecuteRejectBtn?: boolean - showRejectBtn?: boolean - thresholdReached: boolean - userAddress: string - confirmed?: boolean - owner: string - pendingAcceptAction?: boolean - pendingRejectAction?: boolean -} - -const useStyles = makeStyles(styles) - -const OwnerComponent = (props: OwnerComponentProps): React.ReactElement => { - const { - owner, - pendingAcceptAction, - pendingRejectAction, - isCancelTx, - thresholdReached, - executor, - showConfirmBtn, - onTxConfirm, - onTxExecute, - showExecuteBtn, - showRejectBtn, - userAddress, - onTxReject, - showExecuteRejectBtn, - confirmed, - } = props - const nameInAdbk = useSelector((state) => getNameFromAddressBookSelector(state, owner)) - const classes = useStyles() - const [imgCircle, setImgCircle] = React.useState(ConfirmSmallGreyCircle) - - React.useMemo(() => { - if (pendingAcceptAction || pendingRejectAction) { - setImgCircle(PendingSmallYellowCircle) - return - } - if (confirmed) { - setImgCircle(isCancelTx ? CancelSmallFilledCircle : ConfirmSmallFilledCircle) - return - } - if (thresholdReached || executor) { - setImgCircle(isCancelTx ? ConfirmSmallRedCircle : ConfirmSmallGreenCircle) - return - } - setImgCircle(ConfirmSmallGreyCircle) - }, [confirmed, thresholdReached, executor, isCancelTx, pendingAcceptAction, pendingRejectAction]) - - const getTimelineLine = () => { - if (pendingAcceptAction || pendingRejectAction) { - return classes.verticalPendingAction - } - if (isCancelTx) { - return classes.verticalLineCancel - } - return classes.verticalLineDone - } - - const confirmButton = () => { - if (pendingRejectAction) { - return null - } - if (pendingAcceptAction) { - return Pending - } - return ( - <> - {showConfirmBtn && ( - - )} - {showExecuteBtn && ( - - )} - - ) - } - - const rejectButton = () => { - if (pendingRejectAction) { - return Pending - } - if (pendingAcceptAction) { - return null - } - return ( - <> - {showRejectBtn && ( - - )} - {showExecuteRejectBtn && ( - - )} - - ) - } - const explorerUrl = getExplorerInfo(owner) - return ( - -
-
- -
- - - {owner === userAddress && {isCancelTx ? rejectButton() : confirmButton()}} - {owner === executor && Executor} - - ) -} - -export default OwnerComponent diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnersList.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnersList.tsx deleted file mode 100644 index 0d0b4058..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnersList.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' - -import OwnerComponent from './OwnerComponent' -import { OwnersWithoutConfirmations } from './index' - -type OwnersListProps = { - executor: string - isCancelTx?: boolean - onTxConfirm?: () => void - onTxExecute?: () => void - onTxReject?: () => void - ownersUnconfirmed: OwnersWithoutConfirmations - ownersWhoConfirmed: string[] - showConfirmBtn?: boolean - showExecuteBtn?: boolean - showExecuteRejectBtn?: boolean - showRejectBtn?: boolean - thresholdReached: boolean - userAddress: string -} - -const OwnersList = (props: OwnersListProps): React.ReactElement => { - const { ownersUnconfirmed, ownersWhoConfirmed } = props - return ( - <> - {ownersWhoConfirmed.map((owner) => ( - - ))} - {ownersUnconfirmed.map(({ hasPendingAcceptActions, hasPendingRejectActions, owner }) => ( - - ))} - - ) -} - -export default OwnersList diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/cancel-small-filled.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/cancel-small-filled.svg deleted file mode 100644 index 8c2b45dd..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/cancel-small-filled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-green.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-green.svg deleted file mode 100644 index ed4fbbb7..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-green.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-red.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-red.svg deleted file mode 100644 index 191eb63a..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/check-large-filled-red.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-green.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-green.svg deleted file mode 100644 index 353553ca..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-green.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-grey.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-grey.svg deleted file mode 100644 index 1abe86e5..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-grey.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-red.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-red.svg deleted file mode 100644 index e5ddd508..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-large-red.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-filled.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-filled.svg deleted file mode 100644 index 7f170aec..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-filled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-green.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-green.svg deleted file mode 100644 index c4bb04c8..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-green.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-grey.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-grey.svg deleted file mode 100644 index 45a7fd9a..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-grey.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-red.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-red.svg deleted file mode 100644 index 4e9f8350..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-red.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-yellow.svg b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-yellow.svg deleted file mode 100644 index fbd9928b..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/assets/confirm-small-yellow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx deleted file mode 100644 index c23827af..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import cn from 'classnames' -import React from 'react' -import { useSelector } from 'react-redux' - -import OwnersList from './OwnersList' -import CheckLargeFilledGreenCircle from './assets/check-large-filled-green.svg' -import CheckLargeFilledRedCircle from './assets/check-large-filled-red.svg' -import ConfirmLargeGreenCircle from './assets/confirm-large-green.svg' -import ConfirmLargeGreyCircle from './assets/confirm-large-grey.svg' -import ConfirmLargeRedCircle from './assets/confirm-large-red.svg' - -import Block from 'src/components/layout/Block' -import Col from 'src/components/layout/Col' -import Img from 'src/components/layout/Img' -import Paragraph from 'src/components/layout/Paragraph/index' -import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import { Transaction } from 'src/logic/safe/store/models/types/transaction' -import { List } from 'immutable' -import { makeStyles } from '@material-ui/core/styles' -import { styles } from './style' -import { makeTransaction } from 'src/logic/safe/store/models/transaction' -import { safeOwnersSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' -import { TransactionStatus } from 'src/logic/safe/store/models/types/transaction' -import { SafeOwner } from 'src/logic/safe/store/models/safe' - -export type OwnersWithoutConfirmations = { - hasPendingAcceptActions: boolean - hasPendingRejectActions: boolean - owner: string -}[] - -function getOwnersConfirmations(tx: Transaction, userAddress: string): [string[], boolean] { - const ownersWhoConfirmed: string[] = [] - let currentUserAlreadyConfirmed = false - - tx.confirmations?.forEach((conf) => { - if (conf.owner === userAddress) { - currentUserAlreadyConfirmed = true - } - - ownersWhoConfirmed.push(conf.owner) - }) - return [ownersWhoConfirmed, currentUserAlreadyConfirmed] -} - -function getPendingOwnersConfirmations( - owners: List<{ name: string; address: string }>, - tx: Transaction, - userAddress: string, -): [OwnersWithoutConfirmations, boolean] { - const ownersWithNoConfirmations: string[] = [] - let currentUserNotConfirmed = true - - owners.forEach((owner) => { - const confirmationsEntry = tx.confirmations.find((conf) => conf.owner === owner.address) - if (!confirmationsEntry) { - ownersWithNoConfirmations.push(owner.address) - } - if (confirmationsEntry && confirmationsEntry.owner === userAddress) { - currentUserNotConfirmed = false - } - }) - - const confirmationPendingActions = tx.ownersWithPendingActions.get('confirm') - const confirmationRejectActions = tx.ownersWithPendingActions.get('reject') - - const ownersWithNoConfirmationsSorted = ownersWithNoConfirmations - .map((owner) => ({ - hasPendingAcceptActions: !!confirmationPendingActions?.includes(owner), - hasPendingRejectActions: !!confirmationRejectActions?.includes(owner), - owner, - })) - // Reorders the list of unconfirmed owners, owners with pendingActions should be first - .sort((ownerA, ownerB) => { - // If the first owner has pending actions, A should be before B - if (ownerA.hasPendingRejectActions || ownerA.hasPendingAcceptActions) { - return -1 - } - // The first owner has not pending actions but the second yes, B should be before A - if (ownerB.hasPendingRejectActions || ownerB.hasPendingAcceptActions) { - return 1 - } - // Otherwise do not change order - return 0 - }) - - return [ownersWithNoConfirmationsSorted, currentUserNotConfirmed] -} - -const useStyles = makeStyles(styles) - -type ownersColumnProps = { - tx: Transaction - cancelTx?: Transaction - thresholdReached: boolean - cancelThresholdReached: boolean - onTxConfirm: () => void - onTxExecute: () => void - onTxReject: () => void - canExecute: boolean - canExecuteCancel: boolean -} - -const OwnersColumn = ({ - tx, - cancelTx = makeTransaction({ isCancellationTx: true, status: TransactionStatus.AWAITING_YOUR_CONFIRMATION }), - thresholdReached, - cancelThresholdReached, - onTxConfirm, - onTxExecute, - onTxReject, - canExecute, - canExecuteCancel, -}: ownersColumnProps): React.ReactElement => { - const classes = useStyles() - let showOlderTxAnnotation - if (tx.isExecuted || cancelTx.isExecuted) { - showOlderTxAnnotation = false - } else { - showOlderTxAnnotation = (thresholdReached && !canExecute) || (cancelThresholdReached && !canExecuteCancel) - } - const owners = useSelector(safeOwnersSelector) as List - const threshold = useSelector(safeThresholdSelector) - const userAddress = useSelector(userAccountSelector) - const [ownersWhoConfirmed, currentUserAlreadyConfirmed] = getOwnersConfirmations(tx, userAddress) - const [ownersUnconfirmed, userIsUnconfirmedOwner] = getPendingOwnersConfirmations(owners, tx, userAddress) - const [ownersWhoConfirmedCancel, currentUserAlreadyConfirmedCancel] = getOwnersConfirmations(cancelTx, userAddress) - const [ownersUnconfirmedCancel, userIsUnconfirmedCancelOwner] = getPendingOwnersConfirmations( - owners, - cancelTx, - userAddress, - ) - - let displayButtonRow = true - if (tx.executionTxHash) { - // One of owners already executed the tx - displayButtonRow = false - } else if (tx.status === 'cancelled') { - // tx is cancelled (replaced) by another one - displayButtonRow = false - } else if (currentUserAlreadyConfirmedCancel) { - displayButtonRow = false - } - - // TODO: simplify this whole logic around tx status, it's getting hard to maintain and follow - const showConfirmBtn = - !tx.isExecuted && - tx.status !== 'pending' && - cancelTx.status !== 'pending' && - !tx.cancelled && - userIsUnconfirmedOwner && - !currentUserAlreadyConfirmed && - !thresholdReached - - const showExecuteBtn = - canExecute && !tx.isExecuted && thresholdReached && tx.status !== 'pending' && cancelTx.status !== 'pending' - - const showRejectBtn = - !cancelTx.isExecuted && - !tx.isExecuted && - tx.status !== 'pending' && - cancelTx.status !== 'pending' && - userIsUnconfirmedCancelOwner && - !currentUserAlreadyConfirmedCancel && - !cancelThresholdReached && - displayButtonRow - - const showExecuteRejectBtn = - !cancelTx.isExecuted && - !tx.isExecuted && - canExecuteCancel && - cancelThresholdReached && - tx.status !== 'pending' && - cancelTx.status !== 'pending' - - const txThreshold = cancelTx.isExecuted ? tx.confirmations.size : threshold - const cancelThreshold = tx.isExecuted ? cancelTx.confirmations.size : threshold - - return ( - - -
- -
- {tx.isExecuted - ? `Confirmed [${tx.confirmations.size}/${tx.confirmations.size}]` - : `Confirmed [${tx.confirmations.size}/${txThreshold}]`} -
- - {/* Cancel TX thread - START */} - -
-
- -
- {cancelTx.isExecuted - ? `Rejected [${cancelTx.confirmations.size}/${cancelTx.confirmations.size}]` - : `Rejected [${cancelTx.confirmations.size}/${cancelThreshold}]`} - - - {/* Cancel TX thread - END */} - -
-
- {!tx.isExecuted && !cancelTx.isExecuted && Confirm / Execute tx} - {tx.isExecuted && TX Executed icon} - {cancelTx.isExecuted && TX Executed icon} -
- Executed - - - {showOlderTxAnnotation && ( - - There are older transactions that need to be executed first - - )} - - ) -} - -export default OwnersColumn diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/style.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/style.ts deleted file mode 100644 index 36e8fdf2..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/style.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { boldFont, border, error, primary, secondary, secondaryText, sm, warning } from 'src/theme/variables' -import { createStyles } from '@material-ui/core/styles' - -export const styles = createStyles({ - ownersList: { - height: '192px', - overflowY: 'scroll', - padding: 0, - width: '100%', - }, - rightCol: { - borderLeft: `2px solid ${border}`, - boxSizing: 'border-box', - }, - verticalLine: { - backgroundColor: secondaryText, - height: '55px', - left: '27px', - position: 'absolute', - top: '-27px', - width: '2px', - zIndex: 12, - }, - verticalLinePending: { - backgroundColor: secondaryText, - }, - verticalLineDone: { - backgroundColor: secondary, - }, - verticalLineCancel: { - backgroundColor: error, - }, - verticalPendingAction: { - backgroundColor: warning, - }, - icon: { - marginRight: sm, - }, - owner: { - borderBottom: `1px solid ${border}`, - }, - container: { - alignItems: 'center', - display: 'flex', - padding: '13px 15px 13px 18px', - position: 'relative', - }, - ownerListTitle: { - alignItems: 'center', - display: 'flex', - fontSize: '11px', - fontWeight: boldFont, - letterSpacing: '1px', - lineHeight: 1.3, - padding: '15px 15px 15px 18px', - position: 'relative', - textTransform: 'uppercase', - }, - olderTxAnnotation: { - textAlign: 'center', - }, - ownerListTitleDone: { - color: secondary, - }, - ownerListTitleCancelDone: { - color: error, - }, - name: { - height: '15px', - overflow: 'hidden', - textOverflow: 'ellipsis', - }, - address: { - height: '20px', - }, - spacer: { - flex: 'auto', - }, - circleState: { - display: 'flex', - justifyContent: 'center', - marginRight: '18px', - width: '20px', - zIndex: 13, - - '& > img': { - display: 'block', - }, - }, - button: { - alignSelf: 'center', - flexGrow: 0, - fontSize: '16px', - justifyContent: 'center', - paddingLeft: '14px', - paddingRight: '14px', - }, - lastButton: { - marginLeft: '10px', - }, - executor: { - alignSelf: 'center', - background: border, - borderRadius: '3px', - color: primary, - fontSize: '11px', - height: '24px', - lineHeight: '24px', - padding: '0 12px', - }, -}) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/index.tsx deleted file mode 100644 index 8198ee61..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/index.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import IconButton from '@material-ui/core/IconButton' -import { makeStyles } from '@material-ui/core/styles' -import Close from '@material-ui/icons/Close' -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' - -import { styles } from './style' - -import Modal from 'src/components/Modal' -import Block from 'src/components/layout/Block' -import Bold from 'src/components/layout/Bold' -import Button from 'src/components/layout/Button' -import Hairline from 'src/components/layout/Hairline' -import Paragraph from 'src/components/layout/Paragraph' -import Row from 'src/components/layout/Row' -import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions' -import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' -import { createTransaction } from 'src/logic/safe/store/actions/createTransaction' - -import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' -import { Transaction } from 'src/logic/safe/store/models/types/transaction' -import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFees } from 'src/components/TransactionsFees' -import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail' -import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters' -import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters' -import { ParametersStatus } from 'src/routes/safe/components/Transactions/helpers/utils' - -const useStyles = makeStyles(styles) - -type Props = { - isOpen: boolean - onClose: () => void - tx: Transaction -} - -export const RejectTxModal = ({ isOpen, onClose, tx }: Props): React.ReactElement => { - const dispatch = useDispatch() - const safeAddress = useSelector(safeParamAddressFromStateSelector) - const classes = useStyles() - - const { - gasCostFormatted, - txEstimationExecutionStatus, - isExecution, - isOffChainSignature, - isCreation, - gasLimit, - gasEstimation, - gasPriceFormatted, - } = useEstimateTransactionGas({ - txData: EMPTY_DATA, - txRecipient: safeAddress, - }) - - const sendReplacementTransaction = (txParameters: TxParameters) => { - dispatch( - createTransaction({ - safeAddress, - to: safeAddress, - valueInWei: '0', - txNonce: tx.nonce, - origin: tx.origin, - safeTxGas: txParameters.safeTxGas ? Number(txParameters.safeTxGas) : undefined, - ethParameters: txParameters, - notifiedTransaction: TX_NOTIFICATION_TYPES.CANCELLATION_TX, - }), - ) - onClose() - } - - const getParametersStatus = (): ParametersStatus => { - return 'CANCEL_TRANSACTION' - } - - return ( - - - {(txParameters, toggleEditMode) => { - return ( - <> - - - Reject transaction - - - - - - - - - - This action will cancel this transaction. A separate transaction will be performed to submit the - rejection. - - - Transaction nonce: -
- {tx.nonce} -
-
- {/* Tx Parameters */} - -
- {txEstimationExecutionStatus === EstimationStatus.LOADING ? null : ( - - - - )} - - - - - - ) - }} -
-
- ) -} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/style.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/style.ts deleted file mode 100644 index 19a99e1e..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/RejectTxModal/style.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createStyles } from '@material-ui/core' -import { background, border, lg, md, sm } from 'src/theme/variables' - -export const styles = createStyles({ - heading: { - padding: `${sm} ${lg}`, - justifyContent: 'space-between', - boxSizing: 'border-box', - height: '74px', - }, - headingText: { - fontSize: lg, - }, - closeIcon: { - height: '35px', - width: '35px', - }, - container: { - padding: `${md} ${lg}`, - }, - buttonRow: { - height: '84px', - justifyContent: 'center', - position: 'relative', - bottom: 0, - width: '100%', - borderTop: `1px solid ${border}`, - }, - nonceNumber: { - marginTop: sm, - fontSize: md, - }, - gasCostsContainer: { - backgroundColor: background, - padding: `0 ${lg}`, - }, -}) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx deleted file mode 100644 index ed2a11d9..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx +++ /dev/null @@ -1,335 +0,0 @@ -import { IconText, Text, EthHashInfo } from '@gnosis.pm/safe-react-components' -import { makeStyles } from '@material-ui/core/styles' -import React from 'react' - -import styled from 'styled-components' - -import { styles } from './styles' -import Value from './Value' - -import Col from 'src/components/layout/Col' -import { RESET_TIME_OPTIONS } from 'src/routes/safe/components/Settings/SpendingLimit/FormFields/ResetTime' -import useTokenInfo from 'src/logic/safe/hooks/useTokenInfo' -import { AddressInfo, ResetTimeInfo, TokenInfo } from 'src/routes/safe/components/Settings/SpendingLimit/InfoDisplay' -import Block from 'src/components/layout/Block' -import { - extractMultiSendDataDecoded, - MultiSendDetails, -} from 'src/routes/safe/store/actions/transactions/utils/multiSendDecodedDetails' -import Bold from 'src/components/layout/Bold' -import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue' -import Collapse from 'src/components/Collapse' -import { useSelector } from 'react-redux' -import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' -import Paragraph from 'src/components/layout/Paragraph' -import LinkWithRef from 'src/components/layout/Link' -import { shortVersionOf } from 'src/logic/wallets/ethAddresses' -import { Transaction, SafeModuleTransaction } from 'src/logic/safe/store/models/types/transaction' -import { DataDecoded } from 'src/logic/safe/store/models/types/transactions.d' -import DividerLine from 'src/components/DividerLine' -import { isArrayParameter } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils' - -import { getExplorerInfo, getNetworkInfo } from 'src/config' -import { decodeMethods, isDeleteAllowanceMethod, isSetAllowanceMethod } from 'src/logic/contracts/methodIds' - -export const TRANSACTIONS_DESC_CUSTOM_VALUE_TEST_ID = 'tx-description-custom-value' -export const TRANSACTIONS_DESC_CUSTOM_DATA_TEST_ID = 'tx-description-custom-data' -export const TRANSACTION_DESC_ACTION_TEST_ID = 'tx-description-action-data' - -const useStyles = makeStyles(styles) - -const TxDetailsMethodName = styled(Text)` - text-indent: 4px; -` -const TxDetailsMethodParam = styled.div<{ isArrayParameter: boolean }>` - padding-left: 8px; - display: ${({ isArrayParameter }) => (isArrayParameter ? 'block' : 'flex')}; - align-items: center; - - p:first-of-type { - margin-right: ${({ isArrayParameter }) => (isArrayParameter ? '0' : '4px')}; - } -` -const TxDetailsContent = styled.div` - padding: 8px 8px 8px 16px; - overflow-wrap: break-word; -` - -const TxInfo = styled.div` - padding: 8px 8px 8px 16px; -` - -const StyledMethodName = styled(Text)` - white-space: nowrap; -` - -const { nativeCoin } = getNetworkInfo() - -const TxInfoDetails = ({ data }: { data: DataDecoded }): React.ReactElement => ( - - - {data.method} - - - {data.parameters.map((param, index) => ( - - - {param.name}({param.type}): - - - - ))} - -) - -const SpendingLimitDetailsContainer = styled.div` - padding-left: 24px; -` - -const spendingLimitTxType = (data: string | null): { isSetSpendingLimit: boolean; isDeleteSpendingLimit: boolean } => ({ - isSetSpendingLimit: !!data && isSetAllowanceMethod(data), - isDeleteSpendingLimit: !!data && isDeleteAllowanceMethod(data), -}) - -interface NewSpendingLimitDetailsProps { - data: DataDecoded -} - -const ModifySpendingLimitDetails = ({ data }: NewSpendingLimitDetailsProps): React.ReactElement => { - const [beneficiary, tokenAddress, amount, resetTimeMin] = React.useMemo( - () => data.parameters.map(({ value }) => value), - [data.parameters], - ) - - const resetTimeLabel = React.useMemo( - () => RESET_TIME_OPTIONS.find(({ value }) => +value === +resetTimeMin / 24 / 60)?.label ?? '', - [resetTimeMin], - ) - - const tokenInfo = useTokenInfo(tokenAddress) - - return ( - <> - - Modify Spending Limit: - - - - - - - {tokenInfo && ( - - )} - - - - - - - ) -} - -const DeleteSpendingLimitDetails = ({ data }: NewSpendingLimitDetailsProps): React.ReactElement => { - const [beneficiary, tokenAddress] = React.useMemo(() => data.parameters.map(({ value }) => value), [data.parameters]) - const tokenInfo = useTokenInfo(tokenAddress) - - return ( - <> - - Delete Spending Limit: - - - - - - {tokenInfo && } - - - ) -} - -const MultiSendCustomDataAction = ({ tx, order }: { tx: MultiSendDetails; order: number }): React.ReactElement => { - const classes = useStyles() - const methodName = tx.dataDecoded?.method ? ` (${tx.dataDecoded.method})` : '' - const data = tx.dataDecoded ?? decodeMethods(tx.data) - const explorerUrl = getExplorerInfo(tx.to) - const { isSetSpendingLimit, isDeleteSpendingLimit } = spendingLimitTxType(tx.data) - - return ( - } - > - {isSetSpendingLimit || isDeleteSpendingLimit ? ( - - {isSetSpendingLimit && } - {isDeleteSpendingLimit && } - - ) : ( - - - - Send {fromTokenUnit(tx.value, nativeCoin.decimals)} {nativeCoin.name} to: - - - - - {!!data ? : tx.data && } - - )} - - ) -} - -const MultiSendCustomData = ({ txDetails }: { txDetails: MultiSendDetails[] }): React.ReactElement => { - const classes = useStyles() - - return ( - - {txDetails.map((tx, index) => ( - - ))} - - ) -} - -export const TxData = ({ data }: { data: string }): React.ReactElement => { - const classes = useStyles() - const [showTxData, setShowTxData] = React.useState(false) - const showExpandBtn = data.length > 20 - - return ( - - {showExpandBtn ? ( - <> - {showTxData ? ( - <> - {data}{' '} - setShowTxData(false)} - rel="noopener noreferrer" - target="_blank" - > - Show Less - - - ) : ( - <> - {shortVersionOf(data, 20)}{' '} - setShowTxData(true)} - rel="noopener noreferrer" - target="_blank" - > - Show More - - - )} - - ) : ( - data - )} - - ) -} - -const TxActionData = ({ dataDecoded }: { dataDecoded: DataDecoded }): React.ReactElement => { - const classes = useStyles() - - return ( - <> - - - - Action - - - - - - ) -} - -interface HexEncodedDataProps { - data: string -} - -const HexEncodedData = ({ data }: HexEncodedDataProps): React.ReactElement => { - const classes = useStyles() - - return ( - - Data (hex encoded): - - - ) -} - -interface GenericCustomDataProps { - amount?: string - data?: string | null - recipient?: string - storedTx: Transaction | SafeModuleTransaction -} - -const GenericCustomData = ({ - amount = '0', - data = null, - recipient, - storedTx, -}: GenericCustomDataProps): React.ReactElement => { - const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, recipient)) - const explorerUrl = recipient ? getExplorerInfo(recipient) : '' - const txData = storedTx?.dataDecoded ?? decodeMethods(data) - const { isSetSpendingLimit, isDeleteSpendingLimit } = spendingLimitTxType(data) - - return isSetSpendingLimit || isDeleteSpendingLimit ? ( - <> - {isSetSpendingLimit && } - {isDeleteSpendingLimit && } - - ) : ( - - {recipient && ( - - Send {amount} to: - - - - )} - - {!!txData ? : data && } - - ) -} - -interface CustomDescriptionProps { - amount?: string - data?: string | null - recipient?: string - storedTx: Transaction -} - -const CustomDescription = ({ amount, data, recipient, storedTx }: CustomDescriptionProps): React.ReactElement => { - const txDetails = (storedTx.multiSendTx && extractMultiSendDataDecoded(storedTx).txDetails) ?? undefined - - return txDetails ? ( - - ) : ( - - ) -} - -export default CustomDescription diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx deleted file mode 100644 index 9c2b70a2..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import React, { ReactElement } from 'react' -import { useSelector } from 'react-redux' -import { EtherscanLink } from 'src/components/EtherscanLink' -import Block from 'src/components/layout/Block' -import Bold from 'src/components/layout/Bold' -import Paragraph from 'src/components/layout/Paragraph' - -import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' -import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' -import { SAFE_METHODS_NAMES, SafeMethods } from 'src/routes/safe/store/models/types/transactions.d' - -export const TRANSACTIONS_DESC_ADD_OWNER_TEST_ID = 'tx-description-add-owner' -export const TRANSACTIONS_DESC_REMOVE_OWNER_TEST_ID = 'tx-description-remove-owner' -export const TRANSACTIONS_DESC_CHANGE_THRESHOLD_TEST_ID = 'tx-description-change-threshold' -export const TRANSACTIONS_DESC_ADD_MODULE_TEST_ID = 'tx-description-add-module' -export const TRANSACTIONS_DESC_REMOVE_MODULE_TEST_ID = 'tx-description-remove-module' -export const TRANSACTIONS_DESC_NO_DATA = 'tx-description-no-data' - -interface RemovedOwnerProps { - removedOwner: string -} - -const RemovedOwner = ({ removedOwner }: RemovedOwnerProps): ReactElement => { - const ownerChangedName = useSelector((state) => getNameFromAddressBookSelector(state, removedOwner)) - - return ( - - Remove owner: - {ownerChangedName ? ( - - ) : ( - - )} - - ) -} - -interface AddedOwnerProps { - addedOwner: string -} - -const AddedOwner = ({ addedOwner }: AddedOwnerProps): ReactElement => { - const ownerChangedName = useSelector((state) => getNameFromAddressBookSelector(state, addedOwner)) - - return ( - - Add owner: - {ownerChangedName ? ( - - ) : ( - - )} - - ) -} - -interface NewThresholdProps { - newThreshold: string -} - -const NewThreshold = ({ newThreshold }: NewThresholdProps): ReactElement => ( - - Change required confirmations: - - {newThreshold} - - -) - -interface AddModuleProps { - module: string -} - -const AddModule = ({ module }: AddModuleProps): ReactElement => ( - - Add module: - - -) - -interface RemoveModuleProps { - module: string -} - -const RemoveModule = ({ module }: RemoveModuleProps): ReactElement => ( - - Remove module: - - -) - -interface SettingsDescriptionProps { - action?: SafeMethods - addedOwner?: string - newThreshold?: string - removedOwner?: string - module?: string -} - -const SettingsDescription = ({ - action, - addedOwner, - newThreshold, - removedOwner, - module, -}: SettingsDescriptionProps): ReactElement => { - if (action === SAFE_METHODS_NAMES.REMOVE_OWNER && removedOwner && newThreshold) { - return ( - <> - - - - ) - } - - if (action === SAFE_METHODS_NAMES.CHANGE_THRESHOLD && newThreshold) { - return - } - - if (action === SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD && addedOwner && newThreshold) { - return ( - <> - - - - ) - } - - if (action === SAFE_METHODS_NAMES.SWAP_OWNER && removedOwner && addedOwner) { - return ( - <> - - - - ) - } - - if (action === SAFE_METHODS_NAMES.ENABLE_MODULE && module) { - return - } - - if (action === SAFE_METHODS_NAMES.DISABLE_MODULE && module) { - return - } - - return ( - - No data available for current transaction - - ) -} - -export default SettingsDescription diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx deleted file mode 100644 index 58a7c2a6..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { ReactElement } from 'react' -import { useSelector } from 'react-redux' -import { EtherscanLink } from 'src/components/EtherscanLink' -import Block from 'src/components/layout/Block' -import Bold from 'src/components/layout/Bold' -import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' -import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' - -import { TRANSACTIONS_DESC_SEND_TEST_ID } from './index' -import SendModal from 'src/routes/safe/components/Balances/SendModal' - -interface TransferDescriptionProps { - amountWithSymbol: string - recipient?: string - tokenAddress?: string - rawAmount?: string - isTokenTransfer: boolean -} - -const TransferDescription = ({ - amountWithSymbol = '', - recipient, - tokenAddress, - rawAmount, - isTokenTransfer, -}: TransferDescriptionProps): ReactElement | null => { - const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, recipient)) - const [sendModalOpen, setSendModalOpen] = React.useState(false) - const sendModalOpenHandler = () => { - setSendModalOpen(true) - } - - return recipient ? ( - <> - - Send {amountWithSymbol} to: - {recipientName ? ( - - ) : ( - - )} - - setSendModalOpen(false)} - recipientAddress={recipient} - selectedToken={tokenAddress} - tokenAmount={rawAmount} - /> - - ) : null -} - -export default TransferDescription diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/index.tsx deleted file mode 100644 index 789aa29d..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { makeStyles } from '@material-ui/core/styles' -import React from 'react' - -import { styles } from './styles' -import { getTxData } from './utils' -import SettingsDescription from './SettingsDescription' -import CustomDescription from './CustomDescription' -import TransferDescription from './TransferDescription' - -import { getRawTxAmount, getTxAmount } from 'src/routes/safe/components/Transactions/TxsTable/columns' -import Block from 'src/components/layout/Block' -import { Transaction, TransactionTypes } from 'src/logic/safe/store/models/types/transaction' - -export const TRANSACTIONS_DESC_SEND_TEST_ID = 'tx-description-send' - -const useStyles = makeStyles(styles) - -const SettingsDescriptionTx = ({ tx }: { tx: Transaction }): React.ReactElement => { - const { action, addedOwner, module, newThreshold, removedOwner } = getTxData(tx) - return -} - -const CustomDescriptionTx = ({ tx }: { tx: Transaction }): React.ReactElement => { - const amount = getTxAmount(tx, false) - const { data, recipient } = getTxData(tx) - return -} - -const UpgradeDescriptionTx = ({ tx }: { tx: Transaction }): React.ReactElement => { - const { data } = getTxData(tx) - return
{data}
-} - -const TransferDescriptionTx = ({ tx }: { tx: Transaction }): React.ReactElement => { - const amountWithSymbol = getTxAmount(tx, false) - const rawAmount = getRawTxAmount(tx) - const { recipient, isTokenTransfer = false, tokenAddress } = getTxData(tx) - return -} - -const TxDescription = ({ tx }: { tx: Transaction }): React.ReactElement => { - const classes = useStyles() - - return ( - - {tx.type === TransactionTypes.SETTINGS && } - {tx.type === TransactionTypes.CUSTOM && } - {tx.type === TransactionTypes.UPGRADE && } - {[TransactionTypes.TOKEN, TransactionTypes.COLLECTIBLE, TransactionTypes.OUTGOING].includes(tx.type) && ( - - )} - - ) -} - -export default TxDescription diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/styles.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/styles.ts deleted file mode 100644 index d1c1e8d7..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/styles.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { createStyles } from '@material-ui/core/styles' -import { lg, md } from 'src/theme/variables' - -export const styles = createStyles({ - txDataContainer: { - paddingTop: lg, - paddingLeft: md, - paddingBottom: md, - }, - txData: { - wordBreak: 'break-all', - }, - txDataParagraph: { - whiteSpace: 'normal', - }, - linkTxData: { - textDecoration: 'underline', - cursor: 'pointer', - }, - multiSendTxData: { - marginTop: `-${lg}`, - marginLeft: `-${md}`, - }, - collapse: { - borderBottom: `2px solid rgb(232, 231, 230)`, - }, - collapseHeaderWrapper: { - display: 'flex', - flexDirection: 'row', - alignContent: 'center', - alignItems: 'center', - justifyContent: 'space-between', - padding: '8px 8px 8px 16px', - borderBottom: '2px solid rgb(232, 231, 230)', - - '&:hover': { - cursor: 'pointer', - }, - }, - address: { - display: 'inline-flex', - }, -}) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts deleted file mode 100644 index 5a702409..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/utils.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { Transaction } from 'src/logic/safe/store/models/types/transaction' -import { SAFE_METHODS_NAMES, SafeMethods, TokenDecodedParams } from 'src/logic/safe/store/models/types/transactions.d' -import { sameString } from 'src/utils/strings' -import { getNetworkInfo } from 'src/config' - -const getSafeVersion = (data) => { - const contractAddress = data.substr(340, 40).toLowerCase() - - return ( - { - '34cfac646f301356faa8b21e94227e3583fe3f5f': '1.1.1', - }[contractAddress] || 'X.x.x' - ) -} - -interface TxData { - data?: string | null - recipient?: string - module?: string - action?: SafeMethods - addedOwner?: string - removedOwner?: string - newThreshold?: string - tokenId?: string - isTokenTransfer?: boolean - isCollectibleTransfer?: boolean - modifySettingsTx?: boolean - customTx?: boolean - cancellationTx?: boolean - creationTx?: boolean - upgradeTx?: boolean - tokenAddress?: string -} - -const getTxDataForModifySettingsTxs = (tx: Transaction): TxData => { - const txData: TxData = {} - - if (!tx.modifySettingsTx || !tx.decodedParams) { - return txData - } - - txData.recipient = tx.recipient - txData.modifySettingsTx = true - - if (tx.decodedParams[SAFE_METHODS_NAMES.REMOVE_OWNER]) { - const { _threshold, owner } = tx.decodedParams[SAFE_METHODS_NAMES.REMOVE_OWNER] - txData.action = SAFE_METHODS_NAMES.REMOVE_OWNER - txData.removedOwner = owner - txData.newThreshold = _threshold - - return txData - } - if (tx.decodedParams[SAFE_METHODS_NAMES.CHANGE_THRESHOLD]) { - const { _threshold } = tx.decodedParams[SAFE_METHODS_NAMES.CHANGE_THRESHOLD] - txData.action = SAFE_METHODS_NAMES.CHANGE_THRESHOLD - txData.newThreshold = _threshold - return txData - } - if (tx.decodedParams[SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD]) { - const { _threshold, owner } = tx.decodedParams[SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD] - txData.action = SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD - txData.addedOwner = owner - txData.newThreshold = _threshold - return txData - } - - if (tx.decodedParams[SAFE_METHODS_NAMES.SWAP_OWNER]) { - const { newOwner, oldOwner } = tx.decodedParams[SAFE_METHODS_NAMES.SWAP_OWNER] - txData.action = SAFE_METHODS_NAMES.SWAP_OWNER - txData.removedOwner = oldOwner - txData.addedOwner = newOwner - return txData - } - - if (tx.decodedParams[SAFE_METHODS_NAMES.ENABLE_MODULE]) { - const { module } = tx.decodedParams[SAFE_METHODS_NAMES.ENABLE_MODULE] - txData.action = SAFE_METHODS_NAMES.ENABLE_MODULE - txData.module = module - return txData - } - - if (tx.decodedParams[SAFE_METHODS_NAMES.DISABLE_MODULE]) { - const { module } = tx.decodedParams[SAFE_METHODS_NAMES.DISABLE_MODULE] - txData.action = SAFE_METHODS_NAMES.DISABLE_MODULE - txData.module = module - return txData - } - - return txData -} - -const getTxDataForTxsWithDecodedParams = (tx: Transaction): TxData => { - const txData: TxData = {} - - if (!tx.decodedParams) { - return txData - } - - if (tx.isTokenTransfer) { - const { to } = (tx.decodedParams as TokenDecodedParams).transfer || {} - txData.recipient = to - txData.isTokenTransfer = true - txData.tokenAddress = tx.recipient - return txData - } - - if (tx.isCollectibleTransfer) { - const { safeTransferFrom, transfer, transferFrom } = tx.decodedParams as TokenDecodedParams - const { to, value } = safeTransferFrom || transferFrom || transfer || {} - txData.recipient = to - txData.tokenId = value - txData.isCollectibleTransfer = true - - return txData - } - - if (tx.modifySettingsTx) { - return getTxDataForModifySettingsTxs(tx) - } - - if (tx.multiSendTx) { - txData.recipient = tx.recipient - txData.data = tx.data - txData.customTx = true - return txData - } - - txData.recipient = tx.recipient - txData.data = tx.data - txData.customTx = true - - return txData -} - -// @todo (agustin) this function does not makes much sense -// it should be refactored to simplify unnecessary if's checks and re-asigning props to the txData object -export const getTxData = (tx: Transaction): TxData => { - const txData: TxData = {} - - const { nativeCoin } = getNetworkInfo() - if (sameString(tx.type, 'outgoing') && tx.symbol && sameString(tx.symbol, nativeCoin.symbol)) { - txData.isTokenTransfer = true - txData.tokenAddress = nativeCoin.address - } - - if (tx.decodedParams) { - return getTxDataForTxsWithDecodedParams(tx) - } - - if (tx.customTx) { - txData.recipient = tx.recipient - txData.data = tx.data - txData.customTx = true - return txData - } - if (Number(tx.value) > 0) { - txData.recipient = tx.recipient - return txData - } - - if (tx.isCancellationTx) { - txData.cancellationTx = true - return txData - } - - if (tx.creationTx) { - txData.creationTx = true - return txData - } - - if (tx.upgradeTx) { - txData.upgradeTx = true - txData.data = `The contract of this Safe is upgraded to Version ${getSafeVersion(tx.data)}` - - return txData - } - txData.recipient = tx.recipient - - return txData -} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx deleted file mode 100644 index 31c6458c..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import cn from 'classnames' -import React, { ReactElement, useMemo, useState } from 'react' -import { useSelector } from 'react-redux' -import { EthHashInfo } from '@gnosis.pm/safe-react-components' - -import { ApproveTxModal } from './ApproveTxModal' -import OwnersColumn from './OwnersColumn' -import { RejectTxModal } from './RejectTxModal' -import TxDescription from './TxDescription' -import { IncomingTx } from './IncomingTx' -import { CreationTx } from './CreationTx' -import { OutgoingTx } from './OutgoingTx' -import useStyles from './style' - -import { - getModuleAmount, - NOT_AVAILABLE, - TableData, - TX_TABLE_RAW_CANCEL_TX_ID, - TX_TABLE_RAW_TX_ID, -} from 'src/routes/safe/components/Transactions/TxsTable/columns' -import Block from 'src/components/layout/Block' -import Bold from 'src/components/layout/Bold' -import Col from 'src/components/layout/Col' -import Hairline from 'src/components/layout/Hairline' -import Paragraph from 'src/components/layout/Paragraph' -import Row from 'src/components/layout/Row' -import Span from 'src/components/layout/Span' -import { getWeb3 } from 'src/logic/wallets/getWeb3' -import { INCOMING_TX_TYPES } from 'src/logic/safe/store/models/incomingTransaction' -import { Transaction, TransactionTypes, SafeModuleTransaction } from 'src/logic/safe/store/models/types/transaction' -import IncomingTxDescription from './IncomingTxDescription' -import { getExplorerInfo, getNetworkInfo } from 'src/config' -import TransferDescription from './TxDescription/TransferDescription' -import { sameAddress } from 'src/logic/wallets/ethAddresses' -import { safeNonceSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' -import { useTransactionParameters } from 'src/routes/safe/container/hooks/useTransactionParameters' - -const ExpandedModuleTx = ({ tx }: { tx: SafeModuleTransaction }): ReactElement => { - const classes = useStyles() - - const recipient = useMemo(() => { - if (tx.type === TransactionTypes.SPENDING_LIMIT) { - if (tx.dataDecoded) { - // if `dataDecoded` is defined, then it's a token transfer - return tx.dataDecoded?.parameters[0].value - } else { - // if `data` is not defined, then it's an ETH transfer - return tx.to - } - } - }, [tx.dataDecoded, tx.to, tx.type]) - - const amountWithSymbol = getModuleAmount(tx) - - return ( - - - - -
- Hash: - {tx.executionTxHash ? ( - - ) : ( - 'n/a' - )} -
-
- - - - - -
-
- ) -} - -interface ExpandedSafeTxProps { - cancelTx?: Transaction - tx: Transaction -} - -const { nativeCoin } = getNetworkInfo() - -type ScreenType = 'approveTx' | 'executeRejectTx' | 'rejectTx' - -const ExpandedSafeTx = ({ cancelTx, tx }: ExpandedSafeTxProps): ReactElement => { - const { fromWei, toBN } = getWeb3().utils - const classes = useStyles() - const nonce = useSelector(safeNonceSelector) - const threshold = useSelector(safeThresholdSelector) as number - const [openModal, setOpenModal] = useState() - const txParameters = useTransactionParameters() - - const isIncomingTx = !!INCOMING_TX_TYPES[tx.type] - const isCreationTx = tx.type === TransactionTypes.CREATION - const thresholdReached = !isIncomingTx && threshold <= tx.confirmations.size - const canExecute = !isIncomingTx && nonce === tx.nonce - const cancelThresholdReached = !!cancelTx && threshold <= cancelTx.confirmations?.size - const canExecuteCancel = nonce === tx.nonce - - const openApproveModal = () => setOpenModal('approveTx') - - const closeModal = () => setOpenModal(undefined) - - const openRejectModal = () => { - if (!!cancelTx && nonce === cancelTx.nonce) { - setOpenModal('executeRejectTx') - } else { - setOpenModal('rejectTx') - } - } - - const explorerUrl = tx.executionTxHash ? getExplorerInfo(tx.executionTxHash) : null - - return ( - <> - - - - -
- Hash: - {tx.executionTxHash ? ( - - ) : ( - 'n/a' - )} -
- {!isIncomingTx && !isCreationTx && ( - - Nonce: - {tx.nonce} - - )} - {!isCreationTx ? ( - - Fee: - {tx.fee ? fromWei(toBN(tx.fee)) + ` ${nativeCoin.name}` : 'n/a'} - - ) : null} - - - -
- - {isIncomingTx && } - {!isIncomingTx && !isCreationTx && } - {isCreationTx && } - - {!isIncomingTx && !isCreationTx && ( - - )} -
-
- - {/* Approve TX */} - {openModal === 'approveTx' && ( - - )} - - {/* Reject TX */} - {openModal === 'rejectTx' && } - - {/* Execute the rejection TX */} - {openModal === 'executeRejectTx' && cancelTx && ( - - )} - - ) -} - -export const ExpandedTx = ({ row }: { row: TableData }): ReactElement => { - const isModuleTx = [TransactionTypes.SPENDING_LIMIT, TransactionTypes.MODULE].includes(row.tx.type) - - if (isModuleTx) { - return - } - - return -} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts deleted file mode 100644 index 7e85f743..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createStyles, makeStyles } from '@material-ui/core' -import { border, lg, md } from 'src/theme/variables' - -export default makeStyles( - createStyles({ - col: { - wordBreak: 'break-word', - whiteSpace: 'normal', - }, - expandedTxBlock: { - borderBottom: `2px solid ${border}`, - }, - txDataContainer: { - padding: `${lg} ${md}`, - }, - txHash: { - paddingRight: '3px', - }, - incomingTxBlock: { - borderRight: '2px solid rgb(232, 231, 230)', - }, - emptyRowDataContainer: { - paddingTop: lg, - paddingLeft: md, - paddingBottom: md, - borderRight: '2px solid rgb(232, 231, 230)', - }, - }), -) diff --git a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/custom.svg b/src/routes/safe/components/Transactions/TxsTable/TxType/assets/custom.svg deleted file mode 100644 index ff8de3f4..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/custom.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/incoming.svg b/src/routes/safe/components/Transactions/TxsTable/TxType/assets/incoming.svg deleted file mode 100644 index c08ef2a1..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/incoming.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/outgoing.svg b/src/routes/safe/components/Transactions/TxsTable/TxType/assets/outgoing.svg deleted file mode 100644 index 89b07111..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/outgoing.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/settings.svg b/src/routes/safe/components/Transactions/TxsTable/TxType/assets/settings.svg deleted file mode 100644 index eb64eda0..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/TxType/assets/settings.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx b/src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx deleted file mode 100644 index bfe52d2e..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/TxType/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { Loader } from '@gnosis.pm/safe-react-components' -import React, { useEffect, useState } from 'react' - -import CustomTxIcon from './assets/custom.svg' -import IncomingTxIcon from './assets/incoming.svg' -import OutgoingTxIcon from './assets/outgoing.svg' -import SettingsTxIcon from './assets/settings.svg' - -import CustomIconText from 'src/components/CustomIconText' -import { getAppInfoFromOrigin, getAppInfoFromUrl } from 'src/routes/safe/components/Apps/utils' -import { SafeApp } from 'src/routes/safe/components/Apps/types.d' - -const typeToIcon = { - outgoing: OutgoingTxIcon, - token: OutgoingTxIcon, - collectible: OutgoingTxIcon, - incoming: IncomingTxIcon, - custom: CustomTxIcon, - settings: SettingsTxIcon, - creation: SettingsTxIcon, - cancellation: SettingsTxIcon, - upgrade: SettingsTxIcon, - module: SettingsTxIcon, - spendingLimit: SettingsTxIcon, -} - -const typeToLabel = { - outgoing: 'Outgoing transfer', - token: 'Outgoing transfer', - collectible: 'Outgoing transfer', - incoming: 'Incoming transfer', - custom: 'Contract Interaction', - settings: 'Modify settings', - creation: 'Safe created', - cancellation: 'Cancellation transaction', - upgrade: 'Contract Upgrade', - module: 'Module transaction', - spendingLimit: 'Spending Limit', -} - -interface TxTypeProps { - origin: string | null - txType: keyof typeof typeToLabel -} - -const TxType = ({ origin, txType }: TxTypeProps): React.ReactElement => { - const [loading, setLoading] = useState(true) - const [appInfo, setAppInfo] = useState() - const [forceCustom, setForceCustom] = useState(false) - - useEffect(() => { - const getAppInfo = async (origin: string | null) => { - if (!origin) { - return - } - - const parsedOrigin = getAppInfoFromOrigin(origin) - - if (!parsedOrigin) { - setForceCustom(true) - setLoading(false) - return - } - - const appInfo = await getAppInfoFromUrl(parsedOrigin.url) - - setAppInfo(appInfo) - setLoading(false) - } - - getAppInfo(origin) - }, [origin, txType]) - - if (forceCustom || !origin) { - return - } - - return loading ? : -} -export default TxType diff --git a/src/routes/safe/components/Transactions/TxsTable/columns.tsx b/src/routes/safe/components/Transactions/TxsTable/columns.tsx deleted file mode 100644 index 713fc32c..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/columns.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import { BigNumber } from 'bignumber.js' -import format from 'date-fns/format' -import getTime from 'date-fns/getTime' -import parseISO from 'date-fns/parseISO' -import { List } from 'immutable' -import React from 'react' - -import TxType from './TxType' - -import { buildOrderFieldFrom } from 'src/components/Table/sorting' -import { TableColumn } from 'src/components/Table/types.d' -import { formatAmount } from 'src/logic/tokens/utils/formatAmount' -import { INCOMING_TX_TYPES } from 'src/logic/safe/store/models/incomingTransaction' -import { SafeModuleTransaction, Transaction, TransactionTypes } from 'src/logic/safe/store/models/types/transaction' -import { TokenDecodedParams } from 'src/logic/safe/store/models/types/transactions.d' -import { CancellationTransactions } from 'src/logic/safe/store/reducer/cancellationTransactions' -import { getNetworkInfo } from 'src/config' - -export const TX_TABLE_ID = 'id' -export const TX_TABLE_TYPE_ID = 'type' -export const TX_TABLE_DATE_ID = 'date' -export const TX_TABLE_AMOUNT_ID = 'amount' -export const TX_TABLE_STATUS_ID = 'status' -export const TX_TABLE_RAW_TX_ID = 'tx' -export const TX_TABLE_RAW_CANCEL_TX_ID = 'cancelTx' -export const TX_TABLE_EXPAND_ICON = 'expand' - -export const formatDate = (date: string): string => format(parseISO(date), 'MMM d, yyyy - HH:mm:ss') - -export const NOT_AVAILABLE = 'n/a' - -interface AmountData { - decimals?: number | string - symbol?: string - value: number | string -} - -const getAmountWithSymbol = ( - { decimals = 0, symbol = NOT_AVAILABLE, value }: AmountData, - formatted = false, -): string => { - const nonFormattedValue = new BigNumber(value).times(`1e-${decimals}`).toFixed() - const finalValue = formatted ? formatAmount(nonFormattedValue).toString() : nonFormattedValue - const txAmount = finalValue === 'NaN' ? NOT_AVAILABLE : finalValue - - return `${txAmount} ${symbol}` -} - -export const getIncomingTxAmount = (tx: Transaction, formatted = true): string => { - // simple workaround to avoid displaying unexpected values for incoming NFT transfer - if (INCOMING_TX_TYPES[tx.type] === INCOMING_TX_TYPES.ERC721_TRANSFER) { - return `1 ${tx.symbol}` - } - - return getAmountWithSymbol( - { decimals: tx.decimals as string, symbol: tx.symbol as string, value: tx.value }, - formatted, - ) -} - -export const getTxAmount = (tx: Transaction, formatted = true): string => { - const { decimals = 18, decodedParams, isTokenTransfer, symbol } = tx - const tokenDecodedTransfer = isTokenTransfer && (decodedParams as TokenDecodedParams)?.transfer - const { value } = tokenDecodedTransfer || tx - - if (tx.isCollectibleTransfer) { - return `1 ${tx.symbol}` - } - - if (!isTokenTransfer && !(Number(value) > 0)) { - return NOT_AVAILABLE - } - - return getAmountWithSymbol({ decimals: decimals as string, symbol: symbol as string, value }, formatted) -} - -export const getModuleAmount = (tx: SafeModuleTransaction, formatted = true): string => { - if (tx.type === TransactionTypes.SPENDING_LIMIT && tx.tokenInfo) { - const { decimals, symbol } = tx.tokenInfo - - let value - - if (tx.dataDecoded) { - // if `dataDecoded` is defined, then it's a token transfer - const [, amount] = tx.dataDecoded.parameters - value = amount.value - } else { - // if `dataDecoded` is not defined, then it's an ETH transfer - value = tx.value - } - - return getAmountWithSymbol({ decimals, symbol, value }, formatted) - } - - return NOT_AVAILABLE -} - -export const getRawTxAmount = (tx: Transaction): string => { - const { decimals, decodedParams, isTokenTransfer } = tx - const { nativeCoin } = getNetworkInfo() - const tokenDecodedTransfer = isTokenTransfer && (decodedParams as TokenDecodedParams)?.transfer - const { value } = tokenDecodedTransfer || tx - - if (tx.isCollectibleTransfer) { - return '1' - } - - if (!isTokenTransfer && !(Number(value) > 0)) { - return NOT_AVAILABLE - } - - const tokenDecimals = decimals ?? nativeCoin.decimals - const finalValue = new BigNumber(value).times(`1e-${tokenDecimals}`).toFixed() - - return finalValue === 'NaN' ? NOT_AVAILABLE : finalValue -} - -export interface TableData { - amount: string - cancelTx?: Transaction - date: string - dateOrder?: number - id: string - status: string - tx: Transaction | SafeModuleTransaction - type: any -} - -const getModuleTxTableData = (tx: SafeModuleTransaction): TableData => ({ - [TX_TABLE_ID]: tx.blockNumber?.toString() ?? '', - [TX_TABLE_TYPE_ID]: , - [TX_TABLE_DATE_ID]: formatDate(tx.executionDate), - [buildOrderFieldFrom(TX_TABLE_DATE_ID)]: getTime(parseISO(tx.executionDate)), - [TX_TABLE_AMOUNT_ID]: getModuleAmount(tx), - [TX_TABLE_STATUS_ID]: tx.status, - [TX_TABLE_RAW_TX_ID]: tx, -}) - -const getIncomingTxTableData = (tx: Transaction): TableData => ({ - [TX_TABLE_ID]: tx.blockNumber?.toString() ?? '', - [TX_TABLE_TYPE_ID]: , - [TX_TABLE_DATE_ID]: formatDate(tx.executionDate || '0'), - [buildOrderFieldFrom(TX_TABLE_DATE_ID)]: getTime(parseISO(tx.executionDate || '0')), - [TX_TABLE_AMOUNT_ID]: getIncomingTxAmount(tx), - [TX_TABLE_STATUS_ID]: tx.status, - [TX_TABLE_RAW_TX_ID]: tx, -}) - -// This follows the approach of calculating the tx information closest to the presentation components. -// Instead of populating tx in the store with another flag, Spending Limit tx is inferred here. -const getTxType = (tx: Transaction): TransactionTypes => { - const SET_ALLOWANCE_HASH = 'beaeb388' - const DELETE_ALLOWANCE_HASH = '885133e3' - - return tx.data?.includes(SET_ALLOWANCE_HASH) || tx.data?.includes(DELETE_ALLOWANCE_HASH) - ? TransactionTypes.SPENDING_LIMIT - : tx.type -} - -const getTransactionTableData = (tx: Transaction, cancelTx?: Transaction): TableData => { - const txDate = tx.submissionDate - const txType = getTxType(tx) - - return { - [TX_TABLE_ID]: tx.blockNumber?.toString() ?? '', - [TX_TABLE_TYPE_ID]: , - [TX_TABLE_DATE_ID]: txDate ? formatDate(txDate) : '', - [buildOrderFieldFrom(TX_TABLE_DATE_ID)]: txDate ? getTime(parseISO(txDate)) : null, - [TX_TABLE_AMOUNT_ID]: getTxAmount(tx), - [TX_TABLE_STATUS_ID]: tx.status, - [TX_TABLE_RAW_TX_ID]: tx, - [TX_TABLE_RAW_CANCEL_TX_ID]: cancelTx, - } -} - -export const getTxTableData = ( - transactions: List, - cancelTxs: CancellationTransactions, -): List => { - return transactions.map((tx) => { - const isModuleTx = [TransactionTypes.SPENDING_LIMIT, TransactionTypes.MODULE].includes(tx.type) - const isIncomingTx = INCOMING_TX_TYPES[tx.type] !== undefined - - if (isModuleTx) { - return getModuleTxTableData(tx as SafeModuleTransaction) - } - - if (isIncomingTx) { - return getIncomingTxTableData(tx as Transaction) - } - - return getTransactionTableData(tx as Transaction, cancelTxs.get(`${tx.nonce}`)) - }) -} - -export const generateColumns = (): List => { - const nonceColumn = { - id: TX_TABLE_ID, - disablePadding: false, - label: 'Id', - custom: false, - order: false, - width: 50, - } - - const typeColumn = { - id: TX_TABLE_TYPE_ID, - order: false, - disablePadding: false, - label: 'Type', - custom: false, - width: 200, - } - - const valueColumn = { - id: TX_TABLE_AMOUNT_ID, - order: false, - disablePadding: false, - label: 'Amount', - custom: false, - width: 120, - } - - const dateColumn = { - id: TX_TABLE_DATE_ID, - disablePadding: false, - order: true, - label: 'Date', - custom: false, - } - - const statusColumn = { - id: TX_TABLE_STATUS_ID, - order: false, - disablePadding: false, - label: 'Status', - custom: true, - align: 'right', - } - - const expandIconColumn = { - id: TX_TABLE_EXPAND_ICON, - order: false, - disablePadding: true, - label: '', - custom: true, - width: 50, - static: true, - } - - return List([nonceColumn, typeColumn, valueColumn, dateColumn, statusColumn, expandIconColumn]) -} diff --git a/src/routes/safe/components/Transactions/TxsTable/test/column.test.ts b/src/routes/safe/components/Transactions/TxsTable/test/column.test.ts deleted file mode 100644 index 75f9fffb..00000000 --- a/src/routes/safe/components/Transactions/TxsTable/test/column.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { List, Map } from 'immutable' -import { makeTransaction } from 'src/logic/safe/store/models/transaction' -import { getTxTableData, TX_TABLE_RAW_CANCEL_TX_ID, TableData } from 'src/routes/safe/components/Transactions/TxsTable/columns' - -describe('TxsTable Columns > getTxTableData', () => { - it('should include CancelTx object inside TxTableData', () => { - // Given - const mockedTransaction = makeTransaction({ nonce: 1, blockNumber: 100 }) - const mockedCancelTransaction = makeTransaction({ nonce: 1, blockNumber: 123 }) - - // When - const txTableData = getTxTableData(List([mockedTransaction]), Map( { '1': mockedCancelTransaction })) - const txRow = txTableData.first() as TableData - - // Then - expect(txRow[TX_TABLE_RAW_CANCEL_TX_ID]).toEqual(mockedCancelTransaction) - }) - it('should not include CancelTx object inside TxTableData', () => { - // Given - const mockedTransaction = makeTransaction({ nonce: 1, blockNumber: 100 }) - const mockedCancelTransaction = makeTransaction({ nonce: 2, blockNumber: 123 }) - - // When - const txTableData = getTxTableData(List([mockedTransaction]), Map( { '2': mockedCancelTransaction })) - const txRow = txTableData.first() as TableData - - // Then - expect(txRow[TX_TABLE_RAW_CANCEL_TX_ID]).toBeUndefined() - }) -}) diff --git a/src/routes/safe/container/hooks/useTransactions.ts b/src/routes/safe/container/hooks/useTransactions.ts deleted file mode 100644 index e9699ab6..00000000 --- a/src/routes/safe/container/hooks/useTransactions.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { loadAllTransactions } from 'src/logic/safe/store/actions/allTransactions/loadAllTransactions' - -import { useEffect } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { loadMore } from 'src/logic/safe/store/actions/allTransactions/pagination' -import { - safeAllTransactionsSelector, - safeTotalTransactionsAmountSelector, -} from 'src/logic/safe/store/selectors/allTransactions' -import { Transaction } from 'src/logic/safe/store/models/types/transactions.d' -import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' - -type Props = { - offset: number - limit: number -} - -export const useTransactions = (props: Props): { transactions: Transaction[]; totalTransactionsCount: number } => { - const { offset, limit } = props - const dispatch = useDispatch() - const transactions = useSelector(safeAllTransactionsSelector) - const safeAddress = useSelector(safeParamAddressFromStateSelector) as string - const totalTransactionsCount = useSelector(safeTotalTransactionsAmountSelector) - useEffect(() => { - async function loadNewTxs() { - const { transactions, totalTransactionsAmount } = await loadAllTransactions({ safeAddress, offset, limit }) - if (transactions.length) { - dispatch(loadMore({ transactions, safeAddress, totalTransactionsAmount })) - } - } - loadNewTxs() - }, [dispatch, safeAddress, offset, limit]) - - return { transactions, totalTransactionsCount } -} diff --git a/src/routes/safe/container/index.tsx b/src/routes/safe/container/index.tsx index 6783ca00..2114d7d7 100644 --- a/src/routes/safe/container/index.tsx +++ b/src/routes/safe/container/index.tsx @@ -17,10 +17,10 @@ export const ADDRESS_BOOK_TAB_BTN_TEST_ID = 'address-book-tab-btn' export const SAFE_VIEW_NAME_HEADING_TEST_ID = 'safe-name-heading' export const TRANSACTIONS_TAB_NEW_BTN_TEST_ID = 'transactions-tab-new-btn' -const Apps = React.lazy(() => import('../components/Apps')) -const Settings = React.lazy(() => import('../components/Settings')) -const Balances = React.lazy(() => import('../components/Balances')) -const TxsTable = React.lazy(() => import('src/routes/safe/components/Transactions/GatewayTransactions')) +const Apps = React.lazy(() => import('src/routes/safe/components/Apps')) +const Settings = React.lazy(() => import('src/routes/safe/components/Settings')) +const Balances = React.lazy(() => import('src/routes/safe/components/Balances')) +const TxList = React.lazy(() => import('src/routes/safe/components/Transactions/TxList')) const AddressBookTable = React.lazy(() => import('src/routes/safe/components/AddressBook')) const Container = (): React.ReactElement => { @@ -68,7 +68,7 @@ const Container = (): React.ReactElement => { wrapInSuspense(, null)} + render={() => wrapInSuspense(, null)} /> state[MODULE_TRANSACTIONS_REDUCER_ID] - -export const modulesTransactionsBySafeSelector = createSelector( - moduleTransactionsSelector, - safeParamAddressFromStateSelector, - (moduleTransactions, safeAddress): ModuleTxServiceModel[] => { - // no module tx for the current safe so far - if (!moduleTransactions || !safeAddress || !moduleTransactions[safeAddress]) { - return [] - } - - return moduleTransactions[safeAddress] - }, -) - -// export const safeModuleTransactionsSelector = createSelector( -// tokenListSelector, -// modulesTransactionsBySafeSelector, -// (tokens, safeModuleTransactions): SafeModuleTransaction[] => { -// return safeModuleTransactions.map((moduleTx) => { -// // if not spendingLimit module tx, then it's an generic module tx -// const type = sameAddress(moduleTx.module, SPENDING_LIMIT_MODULE_ADDRESS) -// ? TransactionTypes.SPENDING_LIMIT -// : TransactionTypes.MODULE -// -// // TODO: this is strictly attached to Spending Limit Module. -// // This has to be moved nearest the module info rendering. -// // add token info to the model, so data can be properly displayed in the UI -// let tokenInfo -// if (type === TransactionTypes.SPENDING_LIMIT) { -// if (moduleTx.data) { -// // if `data` is defined, then it's a token transfer -// tokenInfo = tokens.find(({ address }) => sameAddress(address, moduleTx.to)) -// } else { -// // if `data` is not defined, then it's an ETH transfer -// // ETH does not exist in the list of tokens, so we recreate the record here -// tokenInfo = getEthAsToken(0) -// } -// } -// -// return { -// ...moduleTx, -// safeTxHash: moduleTx.transactionHash, -// executionTxHash: moduleTx.transactionHash, -// status: TransactionStatus.SUCCESS, -// tokenInfo, -// type, -// } -// }) -// }, -// ) diff --git a/src/routes/safe/store/actions/transactions/__tests__/utils.test.ts b/src/routes/safe/store/actions/transactions/__tests__/utils.test.ts deleted file mode 100644 index 82579f99..00000000 --- a/src/routes/safe/store/actions/transactions/__tests__/utils.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/logic/safe/store/actions/utils' -import { getMockedSafeInstance, getMockedTxServiceModel } from 'src/test/utils/safeHelper' -import axios from 'axios' -import { buildTxServiceUrl } from 'src/logic/safe/transactions' - -describe('shouldExecuteTransaction', () => { - it('It should return false if given a safe with a threshold > 1', async () => { - // given - const nonce = '0' - const threshold = '2' - const safeInstance = getMockedSafeInstance({ threshold }) - const lastTx = getMockedTxServiceModel({}) - - // when - const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) - - // then - expect(result).toBe(false) - }) - it('It should return true if given a safe with a threshold === 1 and the previous transaction is already executed', async () => { - // given - const nonce = '1' - const threshold = '1' - const safeInstance = getMockedSafeInstance({ threshold, nonce }) - const lastTx = getMockedTxServiceModel({}) - - // when - const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) - - // then - expect(result).toBe(true) - }) - it('It should return true if given a safe with a threshold === 1 and the previous transaction is already executed', async () => { - // given - const nonce = '10' - const threshold = '1' - const safeInstance = getMockedSafeInstance({ threshold, nonce }) - const lastTx = getMockedTxServiceModel({ isExecuted: true }) - - // when - const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) - - // then - expect(result).toBe(true) - }) - it('It should return false if given a safe with a threshold === 1 and the previous transaction is not yet executed', async () => { - // given - const nonce = '10' - const threshold = '1' - const safeInstance = getMockedSafeInstance({ threshold }) - const lastTx = getMockedTxServiceModel({ isExecuted: false }) - - // when - const result = await shouldExecuteTransaction(safeInstance, nonce, lastTx) - - // then - expect(result).toBe(false) - }) -}) - -describe('getNewTxNonce', () => { - it('It should return 2 if given the last transaction with nonce 1', async () => { - // given - const safeInstance = getMockedSafeInstance({}) - const lastTx = getMockedTxServiceModel({ nonce: 1 }) - const expectedResult = '2' - - // when - const result = await getNewTxNonce(lastTx, safeInstance) - - // then - expect(result).toBe(expectedResult) - }) - it('It should return 0 if given a safe with nonce 0 and no transactions should use safe contract instance for retrieving nonce', async () => { - // given - const safeNonce = '0' - const safeInstance = getMockedSafeInstance({ nonce: safeNonce }) - const expectedResult = '0' - const mockFnCall = jest.fn().mockImplementation(() => safeNonce) - const mockFnNonce = jest.fn().mockImplementation(() => ({ call: mockFnCall })) - - safeInstance.methods.nonce = mockFnNonce - - // when - const result = await getNewTxNonce(null, safeInstance) - - // then - expect(result).toBe(expectedResult) - expect(mockFnNonce).toHaveBeenCalled() - expect(mockFnCall).toHaveBeenCalled() - mockFnNonce.mockRestore() - mockFnCall.mockRestore() - }) - it('Given a Safe and the last transaction, should return nonce of the last transaction + 1', async () => { - // given - const safeInstance = getMockedSafeInstance({}) - const expectedResult = '11' - const lastTx = getMockedTxServiceModel({ nonce: 10 }) - - // when - const result = await getNewTxNonce(lastTx, safeInstance) - - // then - expect(result).toBe(expectedResult) - }) -}) - -jest.mock('axios') -jest.mock('console') -describe('getLastTx', () => { - afterAll(() => { - jest.unmock('axios') - jest.unmock('console') - }) - const safeAddress = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf' - it('It should return the last transaction for a given a safe address', async () => { - // given - const lastTx = getMockedTxServiceModel({ nonce: 1 }) - const url = buildTxServiceUrl(safeAddress) - - // when - // @ts-ignore - axios.get.mockImplementationOnce(() => { - return { - data: { - results: [lastTx], - }, - } - }) - - const result = await getLastTx(safeAddress) - - // then - expect(result).toStrictEqual(lastTx) - expect(axios.get).toHaveBeenCalled() - expect(axios.get).toBeCalledWith(url, { params: { limit: 1 } }) - }) - it('If should return null If catches an error getting last transaction', async () => { - // given - const lastTx = null - const url = buildTxServiceUrl(safeAddress) - - // when - // @ts-ignore - axios.get.mockImplementationOnce(() => { - throw new Error() - }) - console.error = jest.fn() - const result = await getLastTx(safeAddress) - const spyConsole = jest.spyOn(console, 'error').mockImplementation() - - // then - expect(result).toStrictEqual(lastTx) - expect(axios.get).toHaveBeenCalled() - expect(axios.get).toBeCalledWith(url, { params: { limit: 1 } }) - expect(spyConsole).toHaveBeenCalled() - }) -}) diff --git a/src/routes/safe/store/actions/transactions/utils/multiSendDecodedDetails.ts b/src/routes/safe/store/actions/transactions/utils/multiSendDecodedDetails.ts deleted file mode 100644 index 3573c275..00000000 --- a/src/routes/safe/store/actions/transactions/utils/multiSendDecodedDetails.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { TransferDetails } from './transferDetails.d' -import { - DataDecoded, - Operation, - Parameter, - Transfer, - TransferType, -} from 'src/logic/safe/store/models/types/transactions.d' - -import { - extractERC20TransferDetails, - extractERC721TransferDetails, - extractETHTransferDetails, - extractUnknownTransferDetails, -} from './transferDetails' -import { isMultiSendParameter } from './newTransactionHelpers' -import { Transaction } from 'src/logic/safe/store/models/types/transaction' - -export type MultiSendDetails = { - operation: Operation - to: string - data: string | null - dataDecoded: DataDecoded | null - value: number -} - -export type MultiSendDataDecoded = { - txDetails?: MultiSendDetails[] - transfersDetails?: TransferDetails[] -} - -export const extractTransferDetails = (transfer: Transfer): TransferDetails => { - switch (TransferType[transfer.type]) { - case TransferType.ERC20_TRANSFER: - return extractERC20TransferDetails(transfer) - case TransferType.ERC721_TRANSFER: - return extractERC721TransferDetails(transfer) - case TransferType.ETHER_TRANSFER: - return extractETHTransferDetails(transfer) - default: - return extractUnknownTransferDetails(transfer) - } -} - -export const extractMultiSendDetails = (parameter: Parameter): MultiSendDetails[] | undefined => { - if (isMultiSendParameter(parameter)) { - return parameter.valueDecoded.map((valueDecoded) => { - return { - operation: valueDecoded.operation, - to: valueDecoded.to, - value: valueDecoded.value, - dataDecoded: valueDecoded?.dataDecoded ?? null, - data: valueDecoded?.data ?? null, - } - }) - } -} - -export const extractMultiSendDataDecoded = (tx: Transaction): MultiSendDataDecoded => { - const transfersDetails = tx.transfers?.map(extractTransferDetails) - const txDetails = tx.dataDecoded?.parameters[0] ? extractMultiSendDetails(tx.dataDecoded?.parameters[0]) : undefined - - return { txDetails, transfersDetails } -} diff --git a/src/routes/safe/store/actions/transactions/utils/newTransactionHelpers.ts b/src/routes/safe/store/actions/transactions/utils/newTransactionHelpers.ts deleted file mode 100644 index 0b1ddeab..00000000 --- a/src/routes/safe/store/actions/transactions/utils/newTransactionHelpers.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - EthereumTransaction, - ModuleTransaction, - MultiSendMethodParameter, - MultiSigTransaction, - Parameter, - Transaction, - TxType, -} from 'src/routes/safe/store/models/types/transactions.d' - -export const isMultiSigTx = (tx: Transaction): tx is MultiSigTransaction => { - return TxType[tx.txType] === TxType.MULTISIG_TRANSACTION -} - -export const isModuleTx = (tx: Transaction): tx is ModuleTransaction => { - return TxType[tx.txType] === TxType.MODULE_TRANSACTION -} - -export const isEthereumTx = (tx: Transaction): tx is EthereumTransaction => { - return TxType[tx.txType] === TxType.ETHEREUM_TRANSACTION -} - -export const isMultiSendParameter = (parameter: Parameter): parameter is MultiSendMethodParameter => { - return !!(parameter as MultiSendMethodParameter)?.valueDecoded -} diff --git a/src/routes/safe/store/actions/transactions/utils/transferDetails.d.ts b/src/routes/safe/store/actions/transactions/utils/transferDetails.d.ts deleted file mode 100644 index a95f1795..00000000 --- a/src/routes/safe/store/actions/transactions/utils/transferDetails.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -export interface IncomingTransferDetails { - from: string -} - -export interface OutgoingTransferDetails { - to: string -} - -export interface CommonERC20TransferDetails { - tokenAddress: string - value: string - name: string - txHash: string | null -} - -export interface IncomingERC20TransferDetails extends CommonERC20TransferDetails, IncomingTransferDetails {} - -export interface OutgoingERC20TransferDetails extends CommonERC20TransferDetails, OutgoingTransferDetails {} - -export type ERC20TransferDetails = IncomingERC20TransferDetails | OutgoingERC20TransferDetails - -export interface CommonERC721TransferDetails { - tokenAddress: string - tokenId: string | null - txHash: string | null -} - -export interface IncomingERC721TransferDetails extends CommonERC721TransferDetails, IncomingTransferDetails {} - -export interface OutgoingERC721TransferDetails extends CommonERC721TransferDetails, OutgoingTransferDetails {} - -export type ERC721TransferDetails = IncomingERC721TransferDetails | OutgoingERC721TransferDetails - -export interface CommonETHTransferDetails { - value: string - txHash: string | null -} - -export interface IncomingETHTransferDetails extends CommonETHTransferDetails, IncomingTransferDetails {} - -export interface OutgoingETHTransferDetails extends CommonETHTransferDetails, OutgoingTransferDetails {} - -export type ETHTransferDetails = IncomingETHTransferDetails | OutgoingETHTransferDetails - -export interface UnknownTransferDetails extends IncomingTransferDetails, OutgoingTransferDetails { - value: string - txHash: string -} - -export type TransferDetails = ERC20TransferDetails | ERC721TransferDetails | ETHTransferDetails | UnknownTransferDetails diff --git a/src/routes/safe/store/actions/transactions/utils/transferDetails.ts b/src/routes/safe/store/actions/transactions/utils/transferDetails.ts deleted file mode 100644 index 12363dff..00000000 --- a/src/routes/safe/store/actions/transactions/utils/transferDetails.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Transfer, TxConstants } from 'src/routes/safe/store/models/types/transactions.d' -import { sameAddress } from 'src/logic/wallets/ethAddresses' -import { store } from 'src/store' -import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' -import { - ERC20TransferDetails, - ERC721TransferDetails, - ETHTransferDetails, - UnknownTransferDetails, -} from './transferDetails.d' -import { humanReadableValue } from 'src/logic/tokens/utils/humanReadableValue' - -const isIncomingTransfer = (transfer: Transfer): boolean => { - // TODO: prevent using `store` here and receive `safeAddress` as a param - const state = store.getState() - const safeAddress = safeParamAddressFromStateSelector(state) - return sameAddress(transfer.to, safeAddress) -} - -export const extractERC20TransferDetails = (transfer: Transfer): ERC20TransferDetails => { - const erc20TransferDetails = { - tokenAddress: transfer.tokenInfo?.address || TxConstants.UNKNOWN, - value: humanReadableValue(transfer.value || 0, transfer.tokenInfo?.decimals), - name: transfer.tokenInfo?.name || transfer.tokenInfo?.symbol || TxConstants.UNKNOWN, - txHash: transfer.transactionHash, - } - - if (isIncomingTransfer(transfer)) { - return { - ...erc20TransferDetails, - from: transfer.from, - } - } - - return { - ...erc20TransferDetails, - to: transfer.to, - } -} - -export const extractERC721TransferDetails = (transfer: Transfer): ERC721TransferDetails => { - const erc721TransferDetails = { - tokenAddress: transfer.tokenAddress, - tokenId: transfer.tokenId, - txHash: transfer.transactionHash, - } - if (isIncomingTransfer(transfer)) { - return { - ...erc721TransferDetails, - from: transfer.from, - } - } - - return { - ...erc721TransferDetails, - to: transfer.to, - } -} - -export const extractETHTransferDetails = (transfer: Transfer): ETHTransferDetails => { - const ethTransferDetails = { - value: humanReadableValue(transfer.value || 0), - txHash: transfer.transactionHash, - } - if (isIncomingTransfer(transfer)) { - return { - ...ethTransferDetails, - from: transfer.from, - } - } - - return { - ...ethTransferDetails, - to: transfer.to, - } -} - -export const extractUnknownTransferDetails = (transfer: Transfer): UnknownTransferDetails => { - return { - value: transfer?.value || TxConstants.UNKNOWN, - txHash: transfer?.transactionHash || TxConstants.UNKNOWN, - to: transfer?.to || TxConstants.UNKNOWN, - from: transfer?.from || TxConstants.UNKNOWN, - } -} diff --git a/src/routes/safe/store/models/types/transactions.d.ts b/src/routes/safe/store/models/types/transactions.d.ts deleted file mode 100644 index 0ee07282..00000000 --- a/src/routes/safe/store/models/types/transactions.d.ts +++ /dev/null @@ -1,255 +0,0 @@ -// TODO this file is duplicated with src/logic/safe/store/model/types/transaction.d.ts, we should remove it -export enum TxConstants { - MULTI_SEND = 'multiSend', - UNKNOWN = 'UNKNOWN', -} - -export enum Operation { - CALL, - DELEGATE_CALL, - CREATE, -} - -// types comes from: https://github.com/gnosis/safe-client-gateway/blob/752e76b6d1d475791dbd7917b174bb41d2d9d8be/src/utils.rs -export enum TransferMethods { - TRANSFER = 'transfer', - TRANSFER_FROM = 'transferFrom', - SAFE_TRANSFER_FROM = 'safeTransferFrom', -} - -export enum SettingsChangeMethods { - SETUP = 'setup', - SET_FALLBACK_HANDLER = 'setFallbackHandler', - ADD_OWNER_WITH_THRESHOLD = 'addOwnerWithThreshold', - REMOVE_OWNER = 'removeOwner', - REMOVE_OWNER_WITH_THRESHOLD = 'removeOwnerWithThreshold', - SWAP_OWNER = 'swapOwner', - CHANGE_THRESHOLD = 'changeThreshold', - CHANGE_MASTER_COPY = 'changeMasterCopy', - ENABLE_MODULE = 'enableModule', - DISABLE_MODULE = 'disableModule', - EXEC_TRANSACTION_FROM_MODULE = 'execTransactionFromModule', - APPROVE_HASH = 'approveHash', - EXEC_TRANSACTION = 'execTransaction', -} - -// note: this extends SAFE_METHODS_NAMES in /logic/contracts/methodIds.ts, we need to figure out which one we are going to use -export type DataDecodedMethod = TransferMethods | SettingsChangeMethods | string - -export interface ValueDecoded { - operation: Operation - to: string - value: number - data: string - dataDecoded: DataDecoded -} - -export interface SingleTransactionMethodParameter { - name: string - type: string - value: string -} - -export interface MultiSendMethodParameter extends SingleTransactionMethodParameter { - valueDecoded: ValueDecoded[] -} - -export type Parameter = MultiSendMethodParameter | SingleTransactionMethodParameter - -export interface DataDecoded { - method: DataDecodedMethod - parameters: Parameter[] -} - -export enum ConfirmationType { - CONFIRMATION = 'CONFIRMATION', - EXECUTION = 'EXECUTION', -} - -export enum SignatureType { - CONTRACT_SIGNATURE = 'CONTRACT_SIGNATURE', - APPROVED_HASH = 'APPROVED_HASH', - EOA = 'EOA', - ETH_SIGN = 'ETH_SIGN', -} - -export interface Confirmation { - owner: string - submissionDate: string - transactionHash: string | null - confirmationType: ConfirmationType - signature: string - signatureType: SignatureType -} - -export enum TokenType { - ERC20 = 'ERC20', - ERC721 = 'ERC721', - OTHER = 'OTHER', -} - -export interface TokenInfo { - type: TokenType - address: string - name: string - symbol: string - decimals: number - logoUri: string -} - -export enum TransferType { - ETHER_TRANSFER = 'ETHER_TRANSFER', - ERC20_TRANSFER = 'ERC20_TRANSFER', - ERC721_TRANSFER = 'ERC721_TRANSFER', - UNKNOWN = 'UNKNOWN', -} - -export interface Transfer { - type: TransferType - executionDate: string - blockNumber: number - transactionHash: string | null - to: string - value: string | null - tokenId: string | null - tokenAddress: string - tokenInfo: TokenInfo | null - from: string -} - -export enum TxType { - MULTISIG_TRANSACTION = 'MULTISIG_TRANSACTION', - ETHEREUM_TRANSACTION = 'ETHEREUM_TRANSACTION', - MODULE_TRANSACTION = 'MODULE_TRANSACTION', -} - -export interface MultiSigTransaction { - safe: string - to: string - value: string - data: string | null - operation: number - gasToken: string - safeTxGas: number - baseGas: number - gasPrice: string - refundReceiver: string - nonce: number - executionDate: string | null - submissionDate: string - modified: string - blockNumber: number | null - transactionHash: string | null - safeTxHash: string - executor: string | null - isExecuted: boolean - isSuccessful: boolean | null - ethGasPrice: string | null - gasUsed: number | null - fee: string | null - origin: string | null - dataDecoded: DataDecoded | null - confirmationsRequired: number | null - confirmations: Confirmation[] - signatures: string | null - transfers: Transfer[] - txType: TxType.MULTISIG_TRANSACTION -} - -export interface ModuleTransaction { - created: string - executionDate: string - blockNumber: number - transactionHash: string - safe: string - module: string - to: string - value: string - data: string - operation: Operation - transfers: Transfer[] - txType: TxType.MODULE_TRANSACTION -} - -export interface EthereumTransaction { - executionDate: string - to: string - data: string | null - txHash: string - blockNumber: number - transfers: Transfer[] - txType: TxType.ETHEREUM_TRANSACTION - from: string -} - -export type Transaction = MultiSigTransaction | ModuleTransaction | EthereumTransaction - -// SAFE METHODS TO ITS ID -// https://github.com/gnosis/safe-contracts/blob/development/test/safeMethodNaming.js -// https://github.com/gnosis/safe-contracts/blob/development/contracts/GnosisSafe.sol -// [ -// { name: "addOwnerWithThreshold", id: "0x0d582f13" }, -// { name: "DOMAIN_SEPARATOR_TYPEHASH", id: "0x1db61b54" }, -// { name: "isOwner", id: "0x2f54bf6e" }, -// { name: "execTransactionFromModule", id: "0x468721a7" }, -// { name: "signedMessages", id: "0x5ae6bd37" }, -// { name: "enableModule", id: "0x610b5925" }, -// { name: "changeThreshold", id: "0x694e80c3" }, -// { name: "approvedHashes", id: "0x7d832974" }, -// { name: "changeMasterCopy", id: "0x7de7edef" }, -// { name: "SENTINEL_MODULES", id: "0x85e332cd" }, -// { name: "SENTINEL_OWNERS", id: "0x8cff6355" }, -// { name: "getOwners", id: "0xa0e67e2b" }, -// { name: "NAME", id: "0xa3f4df7e" }, -// { name: "nonce", id: "0xaffed0e0" }, -// { name: "getModules", id: "0xb2494df3" }, -// { name: "SAFE_MSG_TYPEHASH", id: "0xc0856ffc" }, -// { name: "SAFE_TX_TYPEHASH", id: "0xccafc387" }, -// { name: "disableModule", id: "0xe009cfde" }, -// { name: "swapOwner", id: "0xe318b52b" }, -// { name: "getThreshold", id: "0xe75235b8" }, -// { name: "domainSeparator", id: "0xf698da25" }, -// { name: "removeOwner", id: "0xf8dc5dd9" }, -// { name: "VERSION", id: "0xffa1ad74" }, -// { name: "setup", id: "0xa97ab18a" }, -// { name: "execTransaction", id: "0x6a761202" }, -// { name: "requiredTxGas", id: "0xc4ca3a9c" }, -// { name: "approveHash", id: "0xd4d9bdcd" }, -// { name: "signMessage", id: "0x85a5affe" }, -// { name: "isValidSignature", id: "0x20c13b0b" }, -// { name: "getMessageHash", id: "0x0a1028c4" }, -// { name: "encodeTransactionData", id: "0xe86637db" }, -// { name: "getTransactionHash", id: "0xd8d11f78" } -// ] - -export const SAFE_METHODS_NAMES = { - ADD_OWNER_WITH_THRESHOLD: 'addOwnerWithThreshold', - CHANGE_THRESHOLD: 'changeThreshold', - REMOVE_OWNER: 'removeOwner', - SWAP_OWNER: 'swapOwner', - ENABLE_MODULE: 'enableModule', - DISABLE_MODULE: 'disableModule', -} - -export const METHOD_TO_ID = { - '0xe318b52b': SAFE_METHODS_NAMES.SWAP_OWNER, - '0x0d582f13': SAFE_METHODS_NAMES.ADD_OWNER_WITH_THRESHOLD, - '0xf8dc5dd9': SAFE_METHODS_NAMES.REMOVE_OWNER, - '0x694e80c3': SAFE_METHODS_NAMES.CHANGE_THRESHOLD, - '0x610b5925': SAFE_METHODS_NAMES.ENABLE_MODULE, - '0xe009cfde': SAFE_METHODS_NAMES.DISABLE_MODULE, -} - -export type SafeMethods = typeof SAFE_METHODS_NAMES[keyof typeof SAFE_METHODS_NAMES] - -type TokenMethods = 'transfer' | 'transferFrom' | 'safeTransferFrom' - -type SafeDecodedParams = { - [key in SafeMethods]?: Record -} - -type TokenDecodedParams = { - [key in TokenMethods]?: Record -} - -export type DecodedParams = SafeDecodedParams | TokenDecodedParams | null diff --git a/src/store/index.ts b/src/store/index.ts index 037d71df..cb3dba2e 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,4 +1,4 @@ -import { List, Map } from 'immutable' +import { Map } from 'immutable' import { connectRouter, routerMiddleware, RouterState } from 'connected-react-router' import { createHashHistory } from 'history' import { applyMiddleware, combineReducers, compose, createStore, CombinedState, PreloadedState, Store } from 'redux' @@ -24,7 +24,6 @@ import currentSession, { } from 'src/logic/currentSession/store/reducer/currentSession' import { Notification } from 'src/logic/notifications' import notifications, { NOTIFICATIONS_REDUCER_ID } from 'src/logic/notifications/store/reducer/notifications' -import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { StoreStructure } from 'src/logic/safe/store/models/types/gateway' import { gatewayTransactions, GATEWAY_TRANSACTIONS_ID } from 'src/logic/safe/store/reducer/gatewayTransactions' import tokens, { TOKEN_REDUCER_ID, TokenState } from 'src/logic/tokens/store/reducer/tokens' @@ -32,23 +31,10 @@ import providerWatcher from 'src/logic/wallets/store/middlewares/providerWatcher import provider, { PROVIDER_REDUCER_ID, ProviderState } from 'src/logic/wallets/store/reducer/provider' import notificationsMiddleware from 'src/logic/safe/store/middleware/notificationsMiddleware' import safeStorage from 'src/logic/safe/store/middleware/safeStorage' -import cancellationTransactions, { - CANCELLATION_TRANSACTIONS_REDUCER_ID, - CancellationTxState, -} from 'src/logic/safe/store/reducer/cancellationTransactions' -import incomingTransactions, { - INCOMING_TRANSACTIONS_REDUCER_ID, -} from 'src/logic/safe/store/reducer/incomingTransactions' import safe, { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe' -import transactions, { TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/transactions' import { NFTAssets, NFTTokens } from 'src/logic/collectibles/sources/collectibles.d' import { SafeReducerMap } from 'src/routes/safe/store/reducer/types/safe' -import allTransactions, { TRANSACTIONS, TransactionsState } from '../logic/safe/store/reducer/allTransactions' import { AddressBookState } from 'src/logic/addressBook/model/addressBook' -import moduleTransactions, { - MODULE_TRANSACTIONS_REDUCER_ID, - ModuleTransactionsState, -} from 'src/logic/safe/store/reducer/moduleTransactions' export const history = createHashHistory() @@ -74,16 +60,11 @@ const reducers = combineReducers({ [NFT_TOKENS_REDUCER_ID]: nftTokensReducer, [TOKEN_REDUCER_ID]: tokens, [GATEWAY_TRANSACTIONS_ID]: gatewayTransactions, - [TRANSACTIONS_REDUCER_ID]: transactions, - [CANCELLATION_TRANSACTIONS_REDUCER_ID]: cancellationTransactions, - [INCOMING_TRANSACTIONS_REDUCER_ID]: incomingTransactions, - [MODULE_TRANSACTIONS_REDUCER_ID]: moduleTransactions, [NOTIFICATIONS_REDUCER_ID]: notifications, [CURRENCY_VALUES_KEY]: currencyValues, [COOKIES_REDUCER_ID]: cookies, [ADDRESS_BOOK_REDUCER_ID]: addressBook, [CURRENT_SESSION_REDUCER_ID]: currentSession, - [TRANSACTIONS]: allTransactions, }) export type AppReduxState = CombinedState<{ @@ -93,16 +74,11 @@ export type AppReduxState = CombinedState<{ [NFT_TOKENS_REDUCER_ID]: NFTTokens [TOKEN_REDUCER_ID]: TokenState [GATEWAY_TRANSACTIONS_ID]: Record - [TRANSACTIONS_REDUCER_ID]: Map> - [CANCELLATION_TRANSACTIONS_REDUCER_ID]: CancellationTxState - [INCOMING_TRANSACTIONS_REDUCER_ID]: Map> - [MODULE_TRANSACTIONS_REDUCER_ID]: ModuleTransactionsState [NOTIFICATIONS_REDUCER_ID]: Map [CURRENCY_VALUES_KEY]: CurrencyValuesState [COOKIES_REDUCER_ID]: Map [ADDRESS_BOOK_REDUCER_ID]: AddressBookState [CURRENT_SESSION_REDUCER_ID]: CurrentSessionState - [TRANSACTIONS]: TransactionsState router: RouterState }> diff --git a/src/test/safe.dom.funds.thresholdGt1.ts b/src/test/safe.dom.funds.thresholdGt1.ts deleted file mode 100644 index af45d955..00000000 --- a/src/test/safe.dom.funds.thresholdGt1.ts +++ /dev/null @@ -1,78 +0,0 @@ -// -import { fireEvent, waitForElement } from '@testing-library/react' -import { aNewStore } from 'src/store' -import { aMinedSafe } from 'src/test/builder/safe.redux.builder' -import { sendEtherTo } from 'src/test/utils/tokenMovements' -import { renderSafeView } from 'src/test/builder/safe.dom.utils' -import { getWeb3, getBalanceInEtherOf } from 'src/logic/wallets/getWeb3' -import { sleep } from 'src/utils/timer' -import '@testing-library/jest-dom/extend-expect' -import { BALANCE_ROW_TEST_ID } from 'src/routes/safe/components/Balances' -import { fillAndSubmitSendFundsForm, TRANSACTION_ROW_TEST_ID } from './utils/transactions' -import { TRANSACTIONS_TAB_BTN_TEST_ID } from 'src/routes/safe/container' -import { useTestAccountAt, resetTestAccount } from './utils/accounts' -import { APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID } from 'src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal' -import { CONFIRM_TX_BTN_TEST_ID } from 'src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent' - -afterEach(resetTestAccount) - -describe('DOM > Feature > Sending Funds', () => { - let store - let safeAddress - let accounts - beforeEach(async () => { - store = aNewStore() - // using 4th account because other accounts were used in other tests and paid gas - safeAddress = await aMinedSafe(store, 2, 2) - accounts = await getWeb3().eth.getAccounts() - }) - - it('Sends ETH with threshold = 2', async () => { - // GIVEN - const ethAmount = '5' - await sendEtherTo(safeAddress, ethAmount) - const balanceAfterSendingEthToSafe = await getBalanceInEtherOf(accounts[0]) - - // WHEN - const SafeDom = renderSafeView(store, safeAddress) - await sleep(1300) - - // Open send funds modal - const balanceRows = SafeDom.getAllByTestId(BALANCE_ROW_TEST_ID) - expect(balanceRows[0]).toHaveTextContent(`${ethAmount} ETH`) - const sendButton = SafeDom.getByTestId('balance-send-btn') - fireEvent.click(sendButton) - - await fillAndSubmitSendFundsForm(SafeDom, sendButton, ethAmount, accounts[0]) - - // CONFIRM TX - fireEvent.click(SafeDom.getByTestId(TRANSACTIONS_TAB_BTN_TEST_ID)) - await sleep(200) - - useTestAccountAt(1) - await sleep(2200) - const txRows = SafeDom.getAllByTestId(TRANSACTION_ROW_TEST_ID) - expect(txRows.length).toBe(1) - - fireEvent.click(txRows[0]) - - const confirmBtn = await waitForElement(() => SafeDom.getByTestId(CONFIRM_TX_BTN_TEST_ID)) - fireEvent.click(confirmBtn) - - // Travel confirm modal - const approveTxBtn = await waitForElement(() => SafeDom.getByTestId(APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID)) - fireEvent.click(approveTxBtn) - - await sleep(1000) - - // THEN - const safeFunds = await getBalanceInEtherOf(safeAddress) - expect(Number(safeFunds)).toBe(0) - - const receiverFunds = await getBalanceInEtherOf(accounts[0]) - const ESTIMATED_GASCOSTS = 0.3 - expect(Number(parseInt(receiverFunds, 10) - parseInt(balanceAfterSendingEthToSafe, 10))).toBeGreaterThan( - parseInt(ethAmount, 10) - ESTIMATED_GASCOSTS, - ) - }) -}) diff --git a/src/test/safe.dom.settings.owners.ts b/src/test/safe.dom.settings.owners.ts deleted file mode 100644 index 7e8287b8..00000000 --- a/src/test/safe.dom.settings.owners.ts +++ /dev/null @@ -1,242 +0,0 @@ -// -import { fireEvent, waitForElement } from '@testing-library/react' -import { aNewStore } from 'src/store' -import { aMinedSafe } from 'src/test/builder/safe.redux.builder' -import { renderSafeView } from 'src/test/builder/safe.dom.utils' -import { sleep } from 'src/utils/timer' -import '@testing-library/jest-dom/extend-expect' -import { - checkRegisteredTxAddOwner, - checkRegisteredTxRemoveOwner, - checkRegisteredTxReplaceOwner, -} from './utils/transactions' -import { SETTINGS_TAB_BTN_TEST_ID } from 'src/routes/safe/container' -import { OWNERS_SETTINGS_TAB_TEST_ID } from 'src/routes/safe/components/Settings' -import { - RENAME_OWNER_BTN_TEST_ID, - OWNERS_ROW_TEST_ID, - REMOVE_OWNER_BTN_TEST_ID, - ADD_OWNER_BTN_TEST_ID, - REPLACE_OWNER_BTN_TEST_ID, -} from 'src/routes/safe/components/Settings/ManageOwners' -import { - RENAME_OWNER_INPUT_TEST_ID, - SAVE_OWNER_CHANGES_BTN_TEST_ID, -} from 'src/routes/safe/components/Settings/ManageOwners/EditOwnerModal' -import { REMOVE_OWNER_MODAL_NEXT_BTN_TEST_ID } from 'src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner' -import { REMOVE_OWNER_THRESHOLD_NEXT_BTN_TEST_ID } from 'src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/ThresholdForm' -import { REMOVE_OWNER_REVIEW_BTN_TEST_ID } from 'src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review' -import { ADD_OWNER_THRESHOLD_NEXT_BTN_TEST_ID } from 'src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/ThresholdForm' -import { - ADD_OWNER_NAME_INPUT_TEST_ID, - ADD_OWNER_ADDRESS_INPUT_TEST_ID, - ADD_OWNER_NEXT_BTN_TEST_ID, -} from 'src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/OwnerForm' -import { ADD_OWNER_SUBMIT_BTN_TEST_ID } from 'src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review' -import { - REPLACE_OWNER_NEXT_BTN_TEST_ID, - REPLACE_OWNER_NAME_INPUT_TEST_ID, - REPLACE_OWNER_ADDRESS_INPUT_TEST_ID, -} from 'src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm' -import { REPLACE_OWNER_SUBMIT_BTN_TEST_ID } from 'src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review' - -const originalError = console.error -beforeAll(() => { - console.error = (...args) => { - if (/Warning.*not wrapped in act/.test(args[0])) { - return - } - originalError.call(console, ...args) - } -}) - -afterAll(() => { - console.error = originalError -}) - -const travelToOwnerSettings = async (dom) => { - const settingsBtn = await waitForElement(() => dom.getByTestId(SETTINGS_TAB_BTN_TEST_ID)) - fireEvent.click(settingsBtn) - - // click on owners settings - const ownersSettingsBtn = await waitForElement(() => dom.getByTestId(OWNERS_SETTINGS_TAB_TEST_ID)) - fireEvent.click(ownersSettingsBtn) - - await sleep(500) -} - -describe('DOM > Feature > Settings - Manage owners', () => { - let store - let safeAddress - beforeEach(async () => { - store = aNewStore() - safeAddress = await aMinedSafe(store) - }) - - it("Changes owner's name", async () => { - const NEW_OWNER_NAME = 'NEW OWNER NAME' - - const SafeDom = renderSafeView(store, safeAddress) - await sleep(1300) - - // Travel to settings - await travelToOwnerSettings(SafeDom) - - // open rename owner modal - const renameOwnerBtn = await waitForElement(() => SafeDom.getByTestId(RENAME_OWNER_BTN_TEST_ID)) - fireEvent.click(renameOwnerBtn) - - // rename owner - const ownerNameInput = SafeDom.getByTestId(RENAME_OWNER_INPUT_TEST_ID) - const saveOwnerChangesBtn = SafeDom.getByTestId(SAVE_OWNER_CHANGES_BTN_TEST_ID) - fireEvent.change(ownerNameInput, { target: { value: NEW_OWNER_NAME } }) - fireEvent.click(saveOwnerChangesBtn) - await sleep(200) - - // check if the name updated - const ownerRow = SafeDom.getByTestId(OWNERS_ROW_TEST_ID) - expect(ownerRow).toHaveTextContent(NEW_OWNER_NAME) - }) - - it('Removes an owner', async () => { - const twoOwnersSafeAddress = await aMinedSafe(store, 2) - - const SafeDom = renderSafeView(store, twoOwnersSafeAddress) - await sleep(1300) - - // Travel to settings - await travelToOwnerSettings(SafeDom) - - // check if there are 2 owners - let ownerRows = SafeDom.getAllByTestId(OWNERS_ROW_TEST_ID) - expect(ownerRows.length).toBe(2) - expect(ownerRows[0]).toHaveTextContent('Adol 1 Eth Account') - expect(ownerRows[0]).toHaveTextContent('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1') - expect(ownerRows[1]).toHaveTextContent('Adol 2 Eth Account') - expect(ownerRows[1]).toHaveTextContent('0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0') - - // click remove owner btn which opens the modal - const removeOwnerBtn = SafeDom.getAllByTestId(REMOVE_OWNER_BTN_TEST_ID)[1] - fireEvent.click(removeOwnerBtn) - - // modal navigation - const nextBtnStep1 = SafeDom.getByTestId(REMOVE_OWNER_MODAL_NEXT_BTN_TEST_ID) - fireEvent.click(nextBtnStep1) - - const nextBtnStep2 = SafeDom.getByTestId(REMOVE_OWNER_THRESHOLD_NEXT_BTN_TEST_ID) - fireEvent.click(nextBtnStep2) - - const nextBtnStep3 = SafeDom.getByTestId(REMOVE_OWNER_REVIEW_BTN_TEST_ID) - fireEvent.click(nextBtnStep3) - await sleep(1300) - - // check if owner was removed - await travelToOwnerSettings(SafeDom) - - ownerRows = SafeDom.getAllByTestId(OWNERS_ROW_TEST_ID) - expect(ownerRows.length).toBe(1) - expect(ownerRows[0]).toHaveTextContent('Adol 1 Eth Account') - expect(ownerRows[0]).toHaveTextContent('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1') - - // Check that the transaction was registered - await checkRegisteredTxRemoveOwner(SafeDom, '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0') - }) - - it('Adds a new owner', async () => { - const NEW_OWNER_NAME = 'I am a new owner' - const NEW_OWNER_ADDRESS = '0x0E329Fa8d6fCd1BA0cDA495431F1F7ca24F442c3' - - const SafeDom = renderSafeView(store, safeAddress) - await sleep(1300) - - // Travel to settings - await travelToOwnerSettings(SafeDom) - - // check if there is 1 owner - let ownerRows = SafeDom.getAllByTestId(OWNERS_ROW_TEST_ID) - expect(ownerRows.length).toBe(1) - expect(ownerRows[0]).toHaveTextContent('Adol 1 Eth Account') - expect(ownerRows[0]).toHaveTextContent('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1') - - // click add owner btn - fireEvent.click(SafeDom.getByTestId(ADD_OWNER_BTN_TEST_ID)) - await sleep(200) - - // fill and travel add owner modal - const ownerNameInput = SafeDom.getByTestId(ADD_OWNER_NAME_INPUT_TEST_ID) - const ownerAddressInput = SafeDom.getByTestId(ADD_OWNER_ADDRESS_INPUT_TEST_ID) - const nextBtn = SafeDom.getByTestId(ADD_OWNER_NEXT_BTN_TEST_ID) - fireEvent.change(ownerNameInput, { target: { value: NEW_OWNER_NAME } }) - fireEvent.change(ownerAddressInput, { target: { value: NEW_OWNER_ADDRESS } }) - fireEvent.click(nextBtn) - await sleep(200) - - fireEvent.click(SafeDom.getByTestId(ADD_OWNER_THRESHOLD_NEXT_BTN_TEST_ID)) - await sleep(200) - fireEvent.click(SafeDom.getByTestId(ADD_OWNER_SUBMIT_BTN_TEST_ID)) - await sleep(1500) - - // check if owner was added - await travelToOwnerSettings(SafeDom) - - ownerRows = SafeDom.getAllByTestId(OWNERS_ROW_TEST_ID) - expect(ownerRows.length).toBe(2) - expect(ownerRows[0]).toHaveTextContent('Adol 1 Eth Account') - expect(ownerRows[0]).toHaveTextContent('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1') - expect(ownerRows[1]).toHaveTextContent(NEW_OWNER_NAME) - expect(ownerRows[1]).toHaveTextContent(NEW_OWNER_ADDRESS) - // Check that the transaction was registered - await checkRegisteredTxAddOwner(SafeDom, NEW_OWNER_ADDRESS) - }) - - it('Replaces an owner', async () => { - const NEW_OWNER_NAME = 'I replaced an old owner' - const NEW_OWNER_ADDRESS = '0x1dF62f291b2E969fB0849d99D9Ce41e2F137006e' - - const twoOwnersSafeAddress = await aMinedSafe(store, 2) - - const SafeDom = renderSafeView(store, twoOwnersSafeAddress) - await sleep(1300) - - // Travel to settings - await travelToOwnerSettings(SafeDom) - - // check if there are 2 owners - let ownerRows = SafeDom.getAllByTestId(OWNERS_ROW_TEST_ID) - expect(ownerRows.length).toBe(2) - expect(ownerRows[0]).toHaveTextContent('Adol 1 Eth Account') - expect(ownerRows[0]).toHaveTextContent('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1') - expect(ownerRows[1]).toHaveTextContent('Adol 2 Eth Account') - expect(ownerRows[1]).toHaveTextContent('0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0') - - // click replace owner btn which opens the modal - const replaceOwnerBtn = SafeDom.getAllByTestId(REPLACE_OWNER_BTN_TEST_ID)[1] - fireEvent.click(replaceOwnerBtn) - - // fill and travel add owner modal - const ownerNameInput = SafeDom.getByTestId(REPLACE_OWNER_NAME_INPUT_TEST_ID) - const ownerAddressInput = SafeDom.getByTestId(REPLACE_OWNER_ADDRESS_INPUT_TEST_ID) - const nextBtn = SafeDom.getByTestId(REPLACE_OWNER_NEXT_BTN_TEST_ID) - fireEvent.change(ownerNameInput, { target: { value: NEW_OWNER_NAME } }) - fireEvent.change(ownerAddressInput, { target: { value: NEW_OWNER_ADDRESS } }) - fireEvent.click(nextBtn) - await sleep(200) - - const replaceSubmitBtn = SafeDom.getByTestId(REPLACE_OWNER_SUBMIT_BTN_TEST_ID) - fireEvent.click(replaceSubmitBtn) - await sleep(1000) - - // check if the owner was replaced - await travelToOwnerSettings(SafeDom) - - ownerRows = SafeDom.getAllByTestId(OWNERS_ROW_TEST_ID) - expect(ownerRows.length).toBe(2) - expect(ownerRows[0]).toHaveTextContent('Adol 1 Eth Account') - expect(ownerRows[0]).toHaveTextContent('0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1') - expect(ownerRows[1]).toHaveTextContent(NEW_OWNER_NAME) - expect(ownerRows[1]).toHaveTextContent(NEW_OWNER_ADDRESS) - - // Check that the transaction was registered - await checkRegisteredTxReplaceOwner(SafeDom, '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0', NEW_OWNER_ADDRESS) - }) -}) diff --git a/src/test/utils/historyServiceHelper.ts b/src/test/utils/historyServiceHelper.ts index 93f36fc7..39e7a4a8 100644 --- a/src/test/utils/historyServiceHelper.ts +++ b/src/test/utils/historyServiceHelper.ts @@ -1,7 +1,4 @@ -// -import { List, Map } from 'immutable' import { } from 'src/logic/safe/store/models/confirmation' -import { } from 'src/logic/safe/store/models/transaction' import { sameAddress } from 'src/logic/wallets/ethAddresses' export const testSizeOfSafesWith = (transactions, size) => { diff --git a/src/test/utils/safeHelper.ts b/src/test/utils/safeHelper.ts index c260537c..ed0956a3 100644 --- a/src/test/utils/safeHelper.ts +++ b/src/test/utils/safeHelper.ts @@ -7,9 +7,8 @@ import { ConfirmationServiceModel, TxServiceModel, } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' -import { DataDecoded } from 'src/routes/safe/store/models/types/transactions.d' import { List, Map } from 'immutable' -import { PendingActionValues } from 'src/logic/safe/store/models/types/transaction' +import { DataDecoded, PendingActionValues } from 'src/logic/safe/store/models/types/transaction' const mockNonPayableTransactionObject = (callResult?: string): NonPayableTransactionObject => { return { diff --git a/src/test/utils/transactions/index.ts b/src/test/utils/transactions/index.ts index 31ebbccf..53bc5607 100644 --- a/src/test/utils/transactions/index.ts +++ b/src/test/utils/transactions/index.ts @@ -1,4 +1,3 @@ // export * from './moveFunds.helper' -export * from './moveTokens.helper' -export * from './transactionList.helper' +export * from './moveTokens.helper' \ No newline at end of file diff --git a/src/test/utils/transactions/transactionList.helper.ts b/src/test/utils/transactions/transactionList.helper.ts deleted file mode 100644 index c9e47df4..00000000 --- a/src/test/utils/transactions/transactionList.helper.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { fireEvent } from '@testing-library/react' -import { sleep } from 'src/utils/timer' -import { shortVersionOf } from 'src/logic/wallets/ethAddresses' -import { TRANSACTIONS_TAB_BTN_TEST_ID } from 'src/routes/safe/container' -import { - TRANSACTIONS_DESC_SEND_TEST_ID, -} from 'src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription' -import { - TRANSACTIONS_DESC_ADD_OWNER_TEST_ID, - TRANSACTIONS_DESC_REMOVE_OWNER_TEST_ID, -} from 'src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription' - -export const TRANSACTION_ROW_TEST_ID = 'transaction-row' - -export const getLastTransaction = async (SafeDom) => { - // Travel to transactions - const transactionsBtn = SafeDom.getByTestId(TRANSACTIONS_TAB_BTN_TEST_ID) - fireEvent.click(transactionsBtn) - await sleep(200) - - // Check the last transaction was registered - const transactionRows = SafeDom.getAllByTestId(TRANSACTION_ROW_TEST_ID) - expect(transactionRows.length).toBe(1) - fireEvent.click(transactionRows[0]) -} - -export const checkRegisteredTxSend = async ( - SafeDom, - ethAmount, - symbol, - ethAddress, -) => { - await getLastTransaction(SafeDom) - - const txDescription = SafeDom.getAllByTestId(TRANSACTIONS_DESC_SEND_TEST_ID)[0] - expect(txDescription).toHaveTextContent(`Send ${ethAmount} ${symbol} to:${shortVersionOf(ethAddress, 4)}`) -} - -export const checkRegisteredTxAddOwner = async (SafeDom, ownerAddress) => { - await getLastTransaction(SafeDom) - - const txDescription = SafeDom.getAllByTestId(TRANSACTIONS_DESC_ADD_OWNER_TEST_ID)[0] - expect(txDescription).toHaveTextContent(`Add owner:${shortVersionOf(ownerAddress, 4)}`) -} - -export const checkRegisteredTxRemoveOwner = async (SafeDom, ownerAddress) => { - await getLastTransaction(SafeDom) - - const txDescription = SafeDom.getAllByTestId(TRANSACTIONS_DESC_REMOVE_OWNER_TEST_ID)[0] - expect(txDescription).toHaveTextContent(`Remove owner:${shortVersionOf(ownerAddress, 4)}`) -} - -export const checkRegisteredTxReplaceOwner = async ( - SafeDom, - oldOwnerAddress, - newOwnerAddress, -) => { - await getLastTransaction(SafeDom) - - const txDescriptionRemove = SafeDom.getAllByTestId(TRANSACTIONS_DESC_REMOVE_OWNER_TEST_ID)[0] - expect(txDescriptionRemove).toHaveTextContent(`Remove owner:${shortVersionOf(oldOwnerAddress, 4)}`) - const txDescriptionAdd = SafeDom.getAllByTestId(TRANSACTIONS_DESC_ADD_OWNER_TEST_ID)[0] - expect(txDescriptionAdd).toHaveTextContent(`Add owner:${shortVersionOf(newOwnerAddress, 4)}`) -} From b733cb1c85c262619d1e8c4b98437b642e03ad10 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Tue, 9 Mar 2021 05:39:18 -0300 Subject: [PATCH 11/23] (Feature) - V1 fetch assets from client gateway (#1992) * Replace collectibles fetch with client gateway * Replaces balance endpoint with client gateway * Remove default export of tokens list * Set the default rows per page to 100 * Fix ether price load * Remove Add custom token button * Remove add custom asset and add custom token modals --- .../__tests__/fetchSafeTokens.test.ts | 6 +- .../api/fetchCurrenciesRates.ts | 6 +- .../api/fetchTokenCurrenciesBalances.ts | 21 +- .../api/fetchErc20AndErc721AssetsList.ts | 3 +- src/logic/tokens/api/fetchSafeCollectibles.ts | 6 +- .../tokens/store/actions/fetchSafeTokens.ts | 33 ++- src/logic/tokens/store/model/token.ts | 2 + .../safe/components/Balances/Coins/index.tsx | 2 +- .../safe/components/Balances/Tokens/index.tsx | 29 +-- .../Tokens/screens/AddCustomAsset/index.tsx | 187 --------------- .../Tokens/screens/AddCustomAsset/style.ts | 33 --- .../Tokens/screens/AddCustomAsset/utils.ts | 16 -- .../screens/AddCustomAsset/validators.ts | 21 -- .../Tokens/screens/AddCustomToken/index.tsx | 214 ------------------ .../Tokens/screens/AddCustomToken/style.ts | 34 --- .../Tokens/screens/AddCustomToken/utils.ts | 16 -- .../screens/AddCustomToken/validators.ts | 31 --- .../Tokens/screens/AssetsList/index.tsx | 30 +-- .../Tokens/screens/AssetsList/style.ts | 2 +- .../Tokens/screens/TokenList/index.tsx | 23 +- .../Tokens/screens/TokenList/style.ts | 2 +- .../safe/components/Balances/dataFetcher.ts | 22 +- src/routes/safe/components/Balances/index.tsx | 4 +- 23 files changed, 69 insertions(+), 674 deletions(-) delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/index.tsx delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/style.ts delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/utils.ts delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/validators.ts delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.tsx delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.ts delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/utils.ts delete mode 100644 src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/validators.ts diff --git a/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts b/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts index e127acee..adc85654 100644 --- a/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts +++ b/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts @@ -1,6 +1,6 @@ import axios from 'axios' -import { getSafeServiceBaseUrl } from 'src/config' +import { getSafeClientGatewayBaseUrl } from 'src/config' import { fetchTokenCurrenciesBalances, BalanceEndpoint, @@ -46,7 +46,7 @@ describe('fetchTokenCurrenciesBalances', () => { }, ] - const apiUrl = getSafeServiceBaseUrl(safeAddress) + const apiUrl = getSafeClientGatewayBaseUrl(safeAddress) // @ts-ignore axios.get.mockImplementationOnce(() => Promise.resolve({ data: expectedResult })) @@ -57,6 +57,6 @@ describe('fetchTokenCurrenciesBalances', () => { // then expect(result).toStrictEqual(expectedResult) expect(axios.get).toHaveBeenCalled() - expect(axios.get).toBeCalledWith(`${apiUrl}/balances/usd/?exclude_spam=${excludeSpamTokens}`) + expect(axios.get).toBeCalledWith(`${apiUrl}/balances/usd/?trusted=false&exclude_spam=${excludeSpamTokens}`) }) }) diff --git a/src/logic/currencyValues/api/fetchCurrenciesRates.ts b/src/logic/currencyValues/api/fetchCurrenciesRates.ts index d61924f6..20d1d6b2 100644 --- a/src/logic/currencyValues/api/fetchCurrenciesRates.ts +++ b/src/logic/currencyValues/api/fetchCurrenciesRates.ts @@ -4,7 +4,7 @@ import BigNumber from 'bignumber.js' import { EXCHANGE_RATE_URL } from 'src/utils/constants' import { fetchTokenCurrenciesBalances } from './fetchTokenCurrenciesBalances' import { sameString } from 'src/utils/strings' -import { AVAILABLE_CURRENCIES } from '../store/model/currencyValues' +import { AVAILABLE_CURRENCIES } from 'src/logic/currencyValues/store/model/currencyValues' const fetchCurrenciesRates = async ( baseCurrency: string, @@ -15,8 +15,8 @@ const fetchCurrenciesRates = async ( if (sameString(targetCurrencyValue, AVAILABLE_CURRENCIES.NETWORK)) { try { const tokenCurrenciesBalances = await fetchTokenCurrenciesBalances(safeAddress) - if (tokenCurrenciesBalances?.length) { - rate = new BigNumber(1).div(tokenCurrenciesBalances[0].fiatConversion).toNumber() + if (tokenCurrenciesBalances.items.length) { + rate = new BigNumber(1).div(tokenCurrenciesBalances.items[0].fiatConversion).toNumber() } } catch (error) { console.error(`Fetching ${AVAILABLE_CURRENCIES.NETWORK} data from the relayer errored`, error) diff --git a/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.ts b/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.ts index d9855f85..fd21f689 100644 --- a/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.ts +++ b/src/logic/currencyValues/api/fetchTokenCurrenciesBalances.ts @@ -1,22 +1,29 @@ import axios from 'axios' -import { getSafeServiceBaseUrl } from 'src/config' +import { getSafeClientGatewayBaseUrl } from 'src/config' import { TokenProps } from 'src/logic/tokens/store/model/token' +import { checksumAddress } from 'src/utils/checksumAddress' -export type BalanceEndpoint = { - tokenAddress: string - token?: TokenProps +export type TokenBalance = { + tokenInfo: TokenProps balance: string fiatBalance: string fiatConversion: string - fiatCode: string +} + +export type BalanceEndpoint = { + fiatTotal: string + items: TokenBalance[] } export const fetchTokenCurrenciesBalances = ( safeAddress: string, excludeSpamTokens = true, -): Promise => { - const url = `${getSafeServiceBaseUrl(safeAddress)}/balances/usd/?exclude_spam=${excludeSpamTokens}` + trustedTokens = false, +): Promise => { + const url = `${getSafeClientGatewayBaseUrl( + checksumAddress(safeAddress), + )}/balances/usd/?trusted=${trustedTokens}&exclude_spam=${excludeSpamTokens}` return axios.get(url).then(({ data }) => data) } diff --git a/src/logic/tokens/api/fetchErc20AndErc721AssetsList.ts b/src/logic/tokens/api/fetchErc20AndErc721AssetsList.ts index 6321f706..c547c8e7 100644 --- a/src/logic/tokens/api/fetchErc20AndErc721AssetsList.ts +++ b/src/logic/tokens/api/fetchErc20AndErc721AssetsList.ts @@ -1,6 +1,7 @@ import axios, { AxiosResponse } from 'axios' import { getTokensServiceBaseUrl } from 'src/config' +import { TokenType } from 'src/logic/safe/store/models/types/gateway' export type TokenResult = { address: string @@ -8,7 +9,7 @@ export type TokenResult = { logoUri: string name: string symbol: string - type: string + type: TokenType } export const fetchErc20AndErc721AssetsList = (): Promise> => { diff --git a/src/logic/tokens/api/fetchSafeCollectibles.ts b/src/logic/tokens/api/fetchSafeCollectibles.ts index f847a2c0..4f0a5bfd 100644 --- a/src/logic/tokens/api/fetchSafeCollectibles.ts +++ b/src/logic/tokens/api/fetchSafeCollectibles.ts @@ -1,6 +1,6 @@ import axios, { AxiosResponse } from 'axios' -import { getSafeServiceBaseUrl } from 'src/config' +import { getSafeClientGatewayBaseUrl } from 'src/config' import { checksumAddress } from 'src/utils/checksumAddress' export type CollectibleResult = { @@ -17,8 +17,6 @@ export type CollectibleResult = { } export const fetchSafeCollectibles = async (safeAddress: string): Promise> => { - const address = checksumAddress(safeAddress) - const url = `${getSafeServiceBaseUrl(address)}/collectibles/` - + const url = `${getSafeClientGatewayBaseUrl(checksumAddress(safeAddress))}/collectibles/` return axios.get>(url) } diff --git a/src/logic/tokens/store/actions/fetchSafeTokens.ts b/src/logic/tokens/store/actions/fetchSafeTokens.ts index 6957080d..fb585e94 100644 --- a/src/logic/tokens/store/actions/fetchSafeTokens.ts +++ b/src/logic/tokens/store/actions/fetchSafeTokens.ts @@ -2,12 +2,13 @@ import { backOff } from 'exponential-backoff' import { List, Map } from 'immutable' import { Dispatch } from 'redux' +import { fetchTokenCurrenciesBalances, TokenBalance } from 'src/logic/currencyValues/api/fetchTokenCurrenciesBalances' + import { - fetchTokenCurrenciesBalances, - BalanceEndpoint, -} from 'src/logic/currencyValues/api/fetchTokenCurrenciesBalances' -import { setCurrencyBalances } from 'src/logic/currencyValues/store/actions/setCurrencyBalances' -import { CurrencyRateValueRecord, makeBalanceCurrency } from 'src/logic/currencyValues/store/model/currencyValues' + AVAILABLE_CURRENCIES, + CurrencyRateValueRecord, + makeBalanceCurrency, +} from 'src/logic/currencyValues/store/model/currencyValues' import addTokens from 'src/logic/tokens/store/actions/saveTokens' import { makeToken, Token } from 'src/logic/tokens/store/model/token' import { TokenState } from 'src/logic/tokens/store/reducer/tokens' @@ -16,6 +17,10 @@ import { AppReduxState } from 'src/store' import { humanReadableValue } from 'src/logic/tokens/utils/humanReadableValue' import { safeActiveTokensSelector, safeBlacklistedTokensSelector, safeSelector } from 'src/logic/safe/store/selectors' import { tokensSelector } from 'src/logic/tokens/store/selectors' +import { currentCurrencySelector } from 'src/logic/currencyValues/store/selectors' +import { sameAddress, ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' +import { setCurrencyBalances } from 'src/logic/currencyValues/store/actions/setCurrencyBalances' +import { getNetworkInfo } from 'src/config' interface ExtractedData { balances: Map @@ -24,17 +29,20 @@ interface ExtractedData { tokens: List } -const extractDataFromResult = (currentTokens: TokenState) => ( +const { nativeCoin } = getNetworkInfo() + +const extractDataFromResult = (currentTokens: TokenState, fiatCode: string) => ( acc: ExtractedData, - { balance, fiatBalance, fiatCode, token, tokenAddress }: BalanceEndpoint, + { balance, fiatBalance, tokenInfo }: TokenBalance, ): ExtractedData => { - if (tokenAddress === null) { + const { address: tokenAddress, decimals } = tokenInfo + if (sameAddress(tokenAddress, ZERO_ADDRESS) || sameAddress(tokenAddress, nativeCoin.address)) { acc.ethBalance = humanReadableValue(balance, 18) } else { - acc.balances = acc.balances.merge({ [tokenAddress]: humanReadableValue(balance, Number(token?.decimals)) }) + acc.balances = acc.balances.merge({ [tokenAddress]: humanReadableValue(balance, Number(decimals)) }) if (currentTokens && !currentTokens.get(tokenAddress)) { - acc.tokens = acc.tokens.push(makeToken({ address: tokenAddress, ...token })) + acc.tokens = acc.tokens.push(makeToken({ ...tokenInfo })) } } @@ -58,6 +66,7 @@ const fetchSafeTokens = (safeAddress: string) => async ( const state = getState() const safe = safeSelector(state) const currentTokens = tokensSelector(state) + const currencySelected = currentCurrencySelector(state) if (!safe) { return @@ -67,8 +76,8 @@ const fetchSafeTokens = (safeAddress: string) => async ( const alreadyActiveTokens = safeActiveTokensSelector(state) const blacklistedTokens = safeBlacklistedTokensSelector(state) - const { balances, currencyList, ethBalance, tokens } = tokenCurrenciesBalances.reduce( - extractDataFromResult(currentTokens), + const { balances, currencyList, ethBalance, tokens } = tokenCurrenciesBalances.items.reduce( + extractDataFromResult(currentTokens, currencySelected || AVAILABLE_CURRENCIES.USD), { balances: Map(), currencyList: List(), diff --git a/src/logic/tokens/store/model/token.ts b/src/logic/tokens/store/model/token.ts index 1657359c..e0c612bb 100644 --- a/src/logic/tokens/store/model/token.ts +++ b/src/logic/tokens/store/model/token.ts @@ -1,4 +1,5 @@ import { Record, RecordOf } from 'immutable' +import { TokenType } from 'src/logic/safe/store/models/types/gateway' export type TokenProps = { address: string @@ -7,6 +8,7 @@ export type TokenProps = { decimals: number | string logoUri: string balance: number | string + type?: TokenType } export const makeToken = Record({ diff --git a/src/routes/safe/components/Balances/Coins/index.tsx b/src/routes/safe/components/Balances/Coins/index.tsx index 603960af..d15e532a 100644 --- a/src/routes/safe/components/Balances/Coins/index.tsx +++ b/src/routes/safe/components/Balances/Coins/index.tsx @@ -91,7 +91,7 @@ const Coins = (props: Props): React.ReactElement => { data={filteredData} defaultFixed defaultOrderBy={BALANCE_TABLE_ASSET_ID} - defaultRowsPerPage={10} + defaultRowsPerPage={100} label="Balances" size={filteredData.size} > diff --git a/src/routes/safe/components/Balances/Tokens/index.tsx b/src/routes/safe/components/Balances/Tokens/index.tsx index a25c8762..b7c01a4a 100644 --- a/src/routes/safe/components/Balances/Tokens/index.tsx +++ b/src/routes/safe/components/Balances/Tokens/index.tsx @@ -2,7 +2,7 @@ import IconButton from '@material-ui/core/IconButton' import { makeStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' -import React, { useState } from 'react' +import React from 'react' import { useSelector } from 'react-redux' import { styles } from './style' @@ -12,9 +12,7 @@ import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' import { orderedTokenListSelector } from 'src/logic/tokens/store/selectors' -import AddCustomAssetComponent from 'src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset' -import AddCustomToken from 'src/routes/safe/components/Balances/Tokens/screens/AddCustomToken' -import AssetsList from 'src/routes/safe/components/Balances/Tokens/screens/AssetsList' +import { AssetsList } from 'src/routes/safe/components/Balances/Tokens/screens/AssetsList' import { extendedSafeTokensSelector } from 'src/routes/safe/container/selector' import { safeBlacklistedTokensSelector } from 'src/logic/safe/store/selectors' @@ -30,13 +28,12 @@ type Props = { onClose: () => void } -const Tokens = (props: Props): React.ReactElement => { +export const Tokens = (props: Props): React.ReactElement => { const { modalScreen, onClose, safeAddress } = props const tokens = useSelector(orderedTokenListSelector) const activeTokens = useSelector(extendedSafeTokensSelector) const blacklistedTokens = useSelector(safeBlacklistedTokensSelector) const classes = useStyles() - const [activeScreen, setActiveScreen] = useState(modalScreen) return ( <> @@ -49,31 +46,15 @@ const Tokens = (props: Props): React.ReactElement => { - {activeScreen === 'tokenList' && ( + {modalScreen === 'tokenList' && ( )} - {activeScreen === 'assetsList' && } - {activeScreen === 'addCustomToken' && ( - - )} - {activeScreen === 'addCustomAsset' && ( - - )} + {modalScreen === 'assetsList' && } ) } - -export default Tokens diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/index.tsx b/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/index.tsx deleted file mode 100644 index 268af01a..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/index.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import { withStyles } from '@material-ui/core/styles' -import React, { useState } from 'react' -import { FormSpy } from 'react-final-form' -import { useSelector } from 'react-redux' - -import { styles } from './style' -import { getSymbolAndDecimalsFromContract } from './utils' - -import Field from 'src/components/forms/Field' -import GnoForm from 'src/components/forms/GnoForm' -import TextField from 'src/components/forms/TextField' -import AddressInput from 'src/components/forms/AddressInput' -import { composeValidators, minMaxLength, required } from 'src/components/forms/validator' -import Block from 'src/components/layout/Block' -import Button from 'src/components/layout/Button' -import Col from 'src/components/layout/Col' -import Hairline from 'src/components/layout/Hairline' -import Img from 'src/components/layout/Img' -import Paragraph from 'src/components/layout/Paragraph' -import Row from 'src/components/layout/Row' -import { nftAssetsListSelector } from 'src/logic/collectibles/store/selectors' - -import { - addressIsAssetContract, - doesntExistInAssetsList, -} from 'src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/validators' -import TokenPlaceholder from 'src/routes/safe/components/Balances/assets/token_placeholder.svg' -import { Checkbox } from '@gnosis.pm/safe-react-components' - -export const ADD_CUSTOM_ASSET_ADDRESS_INPUT_TEST_ID = 'add-custom-asset-address-input' -export const ADD_CUSTOM_ASSET_SYMBOLS_INPUT_TEST_ID = 'add-custom-asset-symbols-input' -export const ADD_CUSTOM_ASSET_DECIMALS_INPUT_TEST_ID = 'add-custom-asset-decimals-input' -export const ADD_CUSTOM_ASSET_FORM = 'add-custom-asset-form' - -const INITIAL_FORM_STATE = { - address: '', - decimals: '', - symbol: '', - logoUri: '', -} - -const AddCustomAsset = (props) => { - const { classes, onClose, parentList, setActiveScreen } = props - - const nftAssetsList = useSelector(nftAssetsListSelector) - const [formValues, setFormValues] = useState(INITIAL_FORM_STATE) - - const handleSubmit = () => { - onClose() - } - - const populateFormValuesFromAddress = async (tokenAddress) => { - const tokenData = await getSymbolAndDecimalsFromContract(tokenAddress) - - if (tokenData.length) { - const [symbol, decimals] = tokenData - - setFormValues({ - address: tokenAddress, - symbol, - decimals, - name: symbol, - } as any) - } - } - - const formSpyOnChangeHandler = async (state) => { - const { dirty, errors, submitSucceeded, validating, values } = state - // for some reason this is called after submitting, we don't need to update the values - // after submit - if (submitSucceeded) { - return - } - - if (dirty && !validating && errors.address) { - setFormValues(INITIAL_FORM_STATE) - } - - if (!errors.address && !validating && dirty) { - await populateFormValuesFromAddress(values.address) - } - } - - const formMutators = { - setAssetAddress: (args, state, utils) => { - utils.changeValue(state, 'address', () => args[0]) - }, - } - - const goBack = () => { - setActiveScreen(parentList) - } - - return ( - <> - - {(...args) => { - const mutators = args[3] - - return ( - <> - - - Add custom asset - - - - - - - - - - - - - Token Image - Token image - - - - - - - - - - ) - }} - - - ) -} - -const AddCustomAssetComponent = withStyles(styles as any)(AddCustomAsset) - -export default AddCustomAssetComponent diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/style.ts b/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/style.ts deleted file mode 100644 index 6c8f0b35..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/style.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { lg, md } from 'src/theme/variables' - -export const styles = () => ({ - title: { - padding: `${lg} 0 20px`, - fontSize: md, - }, - formContainer: { - padding: '0 20px', - minHeight: '369px', - }, - addressInput: { - marginBottom: '15px', - display: 'flex', - flexGrow: 1, - backgroundColor: 'red', - }, - tokenImageHeading: { - margin: '0 0 15px', - }, - checkbox: { - padding: '0 7px 0 0', - width: '18px', - height: '18px', - }, - checkboxLabel: { - letterSpacing: '-0.5px', - }, - buttonRow: { - height: '84px', - justifyContent: 'center', - }, -}) diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/utils.ts b/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/utils.ts deleted file mode 100644 index 58bde5fe..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/utils.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getHumanFriendlyToken } from 'src/logic/tokens/store/actions/fetchTokens' - -export const getSymbolAndDecimalsFromContract = async (tokenAddress) => { - const tokenContract = await getHumanFriendlyToken() - const token = await tokenContract.at(tokenAddress) - let values - - try { - const [symbol, decimals] = await Promise.all([token.symbol(), token.decimals()]) - values = [symbol, decimals.toString()] - } catch { - values = [] - } - - return values -} diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/validators.ts b/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/validators.ts deleted file mode 100644 index 95dcbcb4..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomAsset/validators.ts +++ /dev/null @@ -1,21 +0,0 @@ -import memoize from 'lodash.memoize' -import { isERC721Contract } from 'src/logic/collectibles/utils' -import { sameAddress } from 'src/logic/wallets/ethAddresses' - -// eslint-disable-next-line -export const addressIsAssetContract = memoize(async (tokenAddress) => { - const isAsset = await isERC721Contract(tokenAddress) - if (!isAsset) { - return 'Not a asset address' - } -}) - -// eslint-disable-next-line -export const doesntExistInAssetsList = (assetsList) => - memoize((tokenAddress) => { - const tokenIndex = assetsList.findIndex(({ address }) => sameAddress(address, tokenAddress)) - - if (tokenIndex !== -1) { - return 'Token already exists in your token list' - } - }) diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.tsx b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.tsx deleted file mode 100644 index c5053040..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/index.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { makeStyles } from '@material-ui/core/styles' -import React, { useState } from 'react' -import { FormSpy } from 'react-final-form' - -import { styles } from './style' -import { getSymbolAndDecimalsFromContract } from './utils' -import { addressIsTokenContract, doesntExistInTokenList } from './validators' - -import Field from 'src/components/forms/Field' -import GnoForm from 'src/components/forms/GnoForm' -import TextField from 'src/components/forms/TextField' -import AddressInput from 'src/components/forms/AddressInput' -import { composeValidators, minMaxLength, required } from 'src/components/forms/validator' -import Block from 'src/components/layout/Block' -import Button from 'src/components/layout/Button' -import Col from 'src/components/layout/Col' -import Hairline from 'src/components/layout/Hairline' -import Img from 'src/components/layout/Img' -import Paragraph from 'src/components/layout/Paragraph' -import Row from 'src/components/layout/Row' - -import TokenPlaceholder from 'src/routes/safe/components/Balances/assets/token_placeholder.svg' -import { checksumAddress } from 'src/utils/checksumAddress' -import { Checkbox } from '@gnosis.pm/safe-react-components' -import { useDispatch } from 'react-redux' -import { addToken } from 'src/logic/tokens/store/actions/addToken' -import updateActiveTokens from 'src/logic/safe/store/actions/updateActiveTokens' -import activateTokenForAllSafes from 'src/logic/safe/store/actions/activateTokenForAllSafes' -import { Token } from 'src/logic/tokens/store/model/token' -import { List, Set } from 'immutable' - -export const ADD_CUSTOM_TOKEN_ADDRESS_INPUT_TEST_ID = 'add-custom-token-address-input' -export const ADD_CUSTOM_TOKEN_SYMBOLS_INPUT_TEST_ID = 'add-custom-token-symbols-input' -export const ADD_CUSTOM_TOKEN_DECIMALS_INPUT_TEST_ID = 'add-custom-token-decimals-input' -export const ADD_CUSTOM_TOKEN_FORM = 'add-custom-token-form' - -const INITIAL_FORM_STATE = { - address: '', - decimals: '', - symbol: '', - logoUri: '', -} - -const useStyles = makeStyles(styles) - -type Props = { - activeTokens: List - onClose: () => void - parentList: string - safeAddress: string - setActiveScreen: (screen: string) => void - tokens: List -} - -const AddCustomToken = (props: Props): React.ReactElement => { - const { activeTokens, onClose, parentList, safeAddress, setActiveScreen, tokens } = props - const [formValues, setFormValues] = useState(INITIAL_FORM_STATE) - const classes = useStyles() - const dispatch = useDispatch() - - const handleSubmit = (values) => { - const address = checksumAddress(values.address) - const token = { - address, - decimals: values.decimals, - symbol: values.symbol, - name: values.symbol, - } - - dispatch(addToken(token)) - if (values.showForAllSafes) { - dispatch(activateTokenForAllSafes(token.address)) - } else { - const activeTokensAddresses = Set(activeTokens.map(({ address }) => address)) - dispatch(updateActiveTokens(safeAddress, activeTokensAddresses.add(token.address))) - } - - onClose() - } - - const populateFormValuesFromAddress = async (tokenAddress) => { - const tokenData = await getSymbolAndDecimalsFromContract(tokenAddress) - - if (tokenData.length) { - const [symbol, decimals] = tokenData - - setFormValues({ - address: tokenAddress, - symbol, - decimals, - name: symbol, - } as any) - } - } - - const formSpyOnChangeHandler = async (state) => { - const { dirty, errors, submitSucceeded, validating, values } = state - // for some reason this is called after submitting, we don't need to update the values - // after submit - if (submitSucceeded) { - return - } - - if (dirty && !validating && errors.address) { - setFormValues(INITIAL_FORM_STATE) - } - - if (!errors.address && !validating && dirty) { - await populateFormValuesFromAddress(values.address) - } - } - - const formMutators = { - setTokenAddress: (args, state, utils) => { - utils.changeValue(state, 'address', () => args[0]) - }, - } - - const goBack = () => { - setActiveScreen(parentList) - } - - return ( - <> - - {(...args) => { - const mutators = args[3] - - return ( - <> - - - Add custom token - - - - - - - - - - - - - Token Image - Token image - - - - - - - - - - ) - }} - - - ) -} - -export default AddCustomToken diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.ts b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.ts deleted file mode 100644 index 793edae9..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/style.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { lg, md } from 'src/theme/variables' -import { createStyles } from '@material-ui/core' - -export const styles = createStyles({ - title: { - padding: `${lg} 0 20px`, - fontSize: md, - }, - formContainer: { - padding: '0 20px', - minHeight: '369px', - }, - addressInput: { - marginBottom: '15px', - display: 'flex', - flexGrow: 1, - backgroundColor: 'red', - }, - tokenImageHeading: { - margin: '0 0 15px', - }, - checkbox: { - padding: '0 7px 0 0', - width: '18px', - height: '18px', - }, - checkboxLabel: { - letterSpacing: '-0.5px', - }, - buttonRow: { - height: '84px', - justifyContent: 'center', - }, -}) diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/utils.ts b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/utils.ts deleted file mode 100644 index 58bde5fe..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/utils.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getHumanFriendlyToken } from 'src/logic/tokens/store/actions/fetchTokens' - -export const getSymbolAndDecimalsFromContract = async (tokenAddress) => { - const tokenContract = await getHumanFriendlyToken() - const token = await tokenContract.at(tokenAddress) - let values - - try { - const [symbol, decimals] = await Promise.all([token.symbol(), token.decimals()]) - values = [symbol, decimals.toString()] - } catch { - values = [] - } - - return values -} diff --git a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/validators.ts b/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/validators.ts deleted file mode 100644 index 636a1454..00000000 --- a/src/routes/safe/components/Balances/Tokens/screens/AddCustomToken/validators.ts +++ /dev/null @@ -1,31 +0,0 @@ -import memoize from 'lodash.memoize' - -import { isAddressAToken } from 'src/logic/tokens/utils/tokenHelpers' -import { sameAddress } from 'src/logic/wallets/ethAddresses' - -export const addressIsTokenContract = memoize(async (tokenAddress) => { - // SECOND APPROACH: - // They both seem to work the same - // const tokenContract = await getStandardTokenContract() - // try { - // await tokenContract.at(tokenAddress) - // } catch { - // return 'Not a token address' - // } - - const isToken = await isAddressAToken(tokenAddress) - - if (!isToken) { - return 'Not a token address' - } -}) - -// eslint-disable-next-line -export const doesntExistInTokenList = (tokenList) => - memoize((tokenAddress: string) => { - const tokenIndex = tokenList.findIndex(({ address }) => sameAddress(address, tokenAddress)) - - if (tokenIndex !== -1) { - return 'Token already exists in your token list' - } - }) diff --git a/src/routes/safe/components/Balances/Tokens/screens/AssetsList/index.tsx b/src/routes/safe/components/Balances/Tokens/screens/AssetsList/index.tsx index 6f4f4e93..5671a662 100644 --- a/src/routes/safe/components/Balances/Tokens/screens/AssetsList/index.tsx +++ b/src/routes/safe/components/Balances/Tokens/screens/AssetsList/index.tsx @@ -10,10 +10,7 @@ import Paragraph from 'src/components/layout/Paragraph' import { useStyles } from './style' -import Spacer from 'src/components/Spacer' import Block from 'src/components/layout/Block' -import Button from 'src/components/layout/Button' -import Divider from 'src/components/layout/Divider' import Hairline from 'src/components/layout/Hairline' import Row from 'src/components/layout/Row' import { nftAssetsListSelector } from 'src/logic/collectibles/store/selectors' @@ -26,12 +23,6 @@ import { safeParamAddressFromStateSelector, } from 'src/logic/safe/store/selectors' -export const ADD_CUSTOM_ASSET_BUTTON_TEST_ID = 'add-custom-asset-btn' - -type Props = { - setActiveScreen: (newScreen: string) => void -} - const filterBy = (filter, nfts) => nfts.filter( (asset) => @@ -41,7 +32,7 @@ const filterBy = (filter, nfts) => asset.symbol.toLowerCase().includes(filter.toLowerCase()), ) -const AssetsList = (props: Props): React.ReactElement => { +export const AssetsList = (): React.ReactElement => { const classes = useStyles() const searchClasses = { input: classes.searchInput, @@ -99,7 +90,7 @@ const AssetsList = (props: Props): React.ReactElement => { const nftAssetsFilteredList = filterBy(filterValue, nftAssetsList) const itemData = createItemData(nftAssetsFilteredList) - const switchToAddCustomAssetScreen = () => props.setActiveScreen('addCustomAsset') + return ( <> @@ -113,21 +104,6 @@ const AssetsList = (props: Props): React.ReactElement => { searchIcon={
} value={filterValue} /> - - - - @@ -154,5 +130,3 @@ const AssetsList = (props: Props): React.ReactElement => { ) } - -export default AssetsList diff --git a/src/routes/safe/components/Balances/Tokens/screens/AssetsList/style.ts b/src/routes/safe/components/Balances/Tokens/screens/AssetsList/style.ts index 52b060b2..65c0ed1b 100644 --- a/src/routes/safe/components/Balances/Tokens/screens/AssetsList/style.ts +++ b/src/routes/safe/components/Balances/Tokens/screens/AssetsList/style.ts @@ -57,7 +57,6 @@ export const useStyles = makeStyles( alignItems: 'center', }, searchContainer: { - width: '180px', marginLeft: xs, marginRight: xs, }, @@ -69,6 +68,7 @@ export const useStyles = makeStyles( '& > button': { display: 'none', }, + flex: 1, }, searchIcon: { '&:hover': { diff --git a/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.tsx b/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.tsx index 7cd838cb..aa7e12cc 100644 --- a/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.tsx +++ b/src/routes/safe/components/Balances/Tokens/screens/TokenList/index.tsx @@ -9,11 +9,7 @@ import { FixedSizeList } from 'react-window' import TokenRow from './TokenRow' import { useStyles } from './style' - -import Spacer from 'src/components/Spacer' import Block from 'src/components/layout/Block' -import Button from 'src/components/layout/Button' -import Divider from 'src/components/layout/Divider' import Hairline from 'src/components/layout/Hairline' import Row from 'src/components/layout/Row' import { Token } from 'src/logic/tokens/store/model/token' @@ -32,7 +28,6 @@ const filterBy = (filter: string, tokens: List): List => ) type Props = { - setActiveScreen: (newScreen: string) => void tokens: List activeTokens: List blacklistedTokens: Set @@ -47,7 +42,7 @@ export type ItemData = { export const TokenList = (props: Props): React.ReactElement => { const classes = useStyles() - const { setActiveScreen, tokens, activeTokens, blacklistedTokens, safeAddress } = props + const { tokens, activeTokens, blacklistedTokens, safeAddress } = props const [activeTokensAddresses, setActiveTokensAddresses] = useState(Set(activeTokens.map(({ address }) => address))) const [blacklistedTokensAddresses, setBlacklistedTokensAddresses] = useState>(blacklistedTokens) const [filter, setFilter] = useState('') @@ -93,8 +88,6 @@ export const TokenList = (props: Props): React.ReactElement => { onSwitch, }) - const switchToAddCustomTokenScreen = () => setActiveScreen('addCustomToken') - const getItemKey = (index: number, { tokens }): string => { return tokens.get(index).address } @@ -115,20 +108,6 @@ export const TokenList = (props: Props): React.ReactElement => { searchIcon={
} value={filter} /> - - - - diff --git a/src/routes/safe/components/Balances/Tokens/screens/TokenList/style.ts b/src/routes/safe/components/Balances/Tokens/screens/TokenList/style.ts index 18631a78..dbf3500b 100644 --- a/src/routes/safe/components/Balances/Tokens/screens/TokenList/style.ts +++ b/src/routes/safe/components/Balances/Tokens/screens/TokenList/style.ts @@ -65,7 +65,6 @@ export const useStyles = makeStyles( alignItems: 'center', }, searchContainer: { - width: '180px', marginLeft: xs, marginRight: xs, }, @@ -77,6 +76,7 @@ export const useStyles = makeStyles( '& > button': { display: 'none', }, + flex: 1, }, searchIcon: { '&:hover': { diff --git a/src/routes/safe/components/Balances/dataFetcher.ts b/src/routes/safe/components/Balances/dataFetcher.ts index e950560e..7d48d54a 100644 --- a/src/routes/safe/components/Balances/dataFetcher.ts +++ b/src/routes/safe/components/Balances/dataFetcher.ts @@ -6,6 +6,7 @@ import { formatAmountInUsFormat } from 'src/logic/tokens/utils/formatAmount' import { TableColumn } from 'src/components/Table/types.d' import { BalanceCurrencyList } from 'src/logic/currencyValues/store/model/currencyValues' import { Token } from 'src/logic/tokens/store/model/token' +import { sameAddress } from 'src/logic/wallets/ethAddresses' export const BALANCE_TABLE_ASSET_ID = 'asset' export const BALANCE_TABLE_BALANCE_ID = 'balance' @@ -13,23 +14,17 @@ export const BALANCE_TABLE_VALUE_ID = 'value' const { nativeCoin } = getNetworkInfo() -const getTokenValue = (token: Token, currencyValues?: BalanceCurrencyList, currencyRate?: number): string => { - const currencyValue = currencyValues?.find(({ tokenAddress }) => { - if (token.address === nativeCoin.address && !tokenAddress) { - return true - } +const getTokenValue = (token: Token, currencyValues: BalanceCurrencyList, currencyRate: number): string => { + const currencyValue = currencyValues.find( + ({ tokenAddress }) => sameAddress(token.address, tokenAddress) || sameAddress(token.address, nativeCoin.address), + ) - return token.address === tokenAddress - }) - - if (!currencyValue || !currencyRate) { + if (!currencyValue) { return '' } const { balanceInBaseCurrency } = currencyValue - const balance = new BigNumber(balanceInBaseCurrency).times(currencyRate).toString() - - return balance + return new BigNumber(balanceInBaseCurrency).times(currencyRate).toString() } const getTokenPriceInCurrency = (balance: string, currencySelected?: string): string => { @@ -57,7 +52,8 @@ export const getBalanceData = ( ): List => { const { nativeCoin } = getNetworkInfo() return activeTokens.map((token) => { - const balance = getTokenValue(token, currencyValues, currencyRate) + const balance = currencyRate && currencyValues ? getTokenValue(token, currencyValues, currencyRate) : '0' + return { [BALANCE_TABLE_ASSET_ID]: { name: token.name, diff --git a/src/routes/safe/components/Balances/index.tsx b/src/routes/safe/components/Balances/index.tsx index 49bd4d8b..be80a8ba 100644 --- a/src/routes/safe/components/Balances/index.tsx +++ b/src/routes/safe/components/Balances/index.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react' import { useSelector } from 'react-redux' import ReceiveModal from 'src/components/App/ReceiveModal' -import Tokens from './Tokens' +import { Tokens } from './Tokens' import { styles } from './style' import Modal from 'src/components/Modal' @@ -56,7 +56,7 @@ const Balances = (): React.ReactElement => { const featuresEnabled = useSelector(safeFeaturesEnabledSelector) const safeName = useSelector(safeNameSelector) ?? '' - useFetchTokens(address as string) + useFetchTokens(address) useEffect(() => { const erc721Enabled = Boolean(featuresEnabled?.includes(FEATURES.ERC721)) From 69f0e346a04b1f7a0846414b50237d4967947761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Longoni?= Date: Tue, 9 Mar 2021 05:56:09 -0300 Subject: [PATCH 12/23] (feature) Wording and styles improvements in modals (#1974) * add external icon in safe version (settings) * change wording in receive modal * fix Modal titles and color on back/cancel button * move toggle to the left (contract interaction modal) * add title "Method" for drop down * review lettercase in titles and buttons * add contract address label in contract interaction * fix label for input "Contract address" * fix label when use custom data (Contract address) * fix external link component on Safe version * change Label "Recipient" to "Contract address" --- src/components/App/ReceiveModal.tsx | 4 +-- .../AppLayout/Sidebar/SafeHeader/index.tsx | 2 +- .../SendModal/screens/ChooseTxType/index.tsx | 2 +- .../ContractInteraction/Buttons/index.tsx | 2 +- .../EthAddressInput/index.tsx | 1 + .../MethodsDropdown/index.tsx | 16 +++++++++-- .../ContractInteraction/Review/index.tsx | 4 +-- .../ReviewCustomTx/index.tsx | 4 +-- .../SendCustomTx/index.tsx | 11 ++++---- .../screens/ContractInteraction/index.tsx | 8 +++--- .../screens/ReviewCollectible/index.tsx | 4 +-- .../screens/ReviewSendFundsTx/index.tsx | 2 +- .../screens/SendCollectible/index.tsx | 4 +-- .../SendModal/screens/SendFunds/index.tsx | 4 +-- .../components/Settings/SafeDetails/index.tsx | 28 +++++++++++++------ .../components/Settings/SafeDetails/style.ts | 12 +------- 16 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/components/App/ReceiveModal.tsx b/src/components/App/ReceiveModal.tsx index 0574c485..98edb0f4 100644 --- a/src/components/App/ReceiveModal.tsx +++ b/src/components/App/ReceiveModal.tsx @@ -94,7 +94,7 @@ const ReceiveModal = ({ onClose, safeAddress, safeName }: Props): ReactElement = <> - Receive funds + Receive assets @@ -106,7 +106,7 @@ const ReceiveModal = ({ onClose, safeAddress, safeName }: Props): ReactElement = This is the address of your Safe. Deposit funds by scanning the QR code or copying the address below. Only send{' '} - {networkInfo.nativeCoin.name} and ERC-20 tokens to this address! + {networkInfo.nativeCoin.name} and assets to this address (e.g. ETH, ERC20, ERC721)! diff --git a/src/components/AppLayout/Sidebar/SafeHeader/index.tsx b/src/components/AppLayout/Sidebar/SafeHeader/index.tsx index 5f7a5bc0..149f7d84 100644 --- a/src/components/AppLayout/Sidebar/SafeHeader/index.tsx +++ b/src/components/AppLayout/Sidebar/SafeHeader/index.tsx @@ -171,7 +171,7 @@ const SafeHeader = ({ - New Transaction + New transaction diff --git a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx index 4a9c90f9..1f584b24 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx @@ -116,7 +116,7 @@ const ChooseTxType = ({ onClose, recipientAddress, setActiveScreen }: ChooseTxTy className={classNames(classes.leftIcon, classes.iconSmall)} src={ContractInteractionIcon} /> - Contract Interaction + Contract interaction )} diff --git a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Buttons/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Buttons/index.tsx index 9022b85d..6fecb6f4 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Buttons/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Buttons/index.tsx @@ -30,7 +30,7 @@ const Buttons = ({ onClose }: ButtonProps) => { return ( - {(txParameters, toggleEditMode) => ( <> -
+
@@ -223,7 +223,7 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
-
-