Revert "Tech debt 1265: Enable strictNullChecks TS compiler option (#1301)" (#1335)

This reverts commit bfed9679f7.
This commit is contained in:
Daniel Sanchez 2020-09-09 17:35:27 +02:00 committed by GitHub
parent b7afc5caea
commit 43bc4984b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
144 changed files with 865 additions and 928 deletions

View File

@ -18,6 +18,8 @@ module.exports = {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react/prop-types': 'off',
// Remove when resolved issue with strictNullChecks
'react/display-name': 'off',
'@typescript-eslint/camelcase': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',

View File

@ -178,7 +178,7 @@
"bignumber.js": "9.0.0",
"bnc-onboard": "1.12.0",
"classnames": "^2.2.6",
"concurrently": "^5.3.0",
"concurrently": "^5.2.0",
"connected-react-router": "6.8.0",
"coveralls": "^3.1.0",
"currency-flags": "2.1.2",
@ -190,19 +190,19 @@
"eth-sig-util": "^2.5.3",
"ethereum-blockies-base64": "^1.0.2",
"ethereumjs-abi": "0.6.8",
"exponential-backoff": "^3.1.0",
"exponential-backoff": "^3.0.1",
"express": "^4.17.1",
"final-form": "^4.20.1",
"final-form-calculate": "^1.3.1",
"history": "4.10.1",
"immortal-db": "^1.1.0",
"immortal-db": "^1.0.3",
"immutable": "^4.0.0-rc.12",
"js-cookie": "^2.2.1",
"lodash.debounce": "^4.0.8",
"lodash.memoize": "^4.1.2",
"material-ui-search-bar": "^1.0.0",
"material-ui-search-bar": "^1.0.0-beta.13",
"notistack": "https://github.com/gnosis/notistack.git#v0.9.4",
"open": "^7.2.0",
"open": "^7.1.0",
"polished": "3.6.6",
"qrcode.react": "1.0.0",
"query-string": "6.13.1",
@ -215,7 +215,7 @@
"react-qr-reader": "^2.2.1",
"react-redux": "7.2.1",
"react-router-dom": "5.2.0",
"react-scripts": "^3.4.3",
"react-scripts": "^3.4.1",
"react-window": "^1.8.5",
"recompose": "^0.30.0",
"redux": "4.0.5",
@ -263,7 +263,7 @@
"eslint-plugin-import": "2.22.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react": "^7.20.5",
"eslint-plugin-sort-destructure-keys": "1.3.5",
"ethereumjs-abi": "0.6.8",
"husky": "^4.2.5",
@ -272,9 +272,9 @@
"prettier": "2.1.1",
"react-app-rewired": "^2.1.6",
"react-docgen-typescript-loader": "^3.7.2",
"truffle": "5.1.41",
"truffle": "5.1.36",
"typechain": "^2.0.0",
"typescript": "3.9.7",
"wait-on": "5.2.0"
"wait-on": "5.1.0"
}
}

View File

@ -1,8 +1,9 @@
import IconButton from '@material-ui/core/IconButton'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import { withStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close'
import QRCode from 'qrcode.react'
import * as React from 'react'
import { useSelector } from 'react-redux'
import CopyBtn from 'src/components/CopyBtn'
import EtherscanBtn from 'src/components/EtherscanBtn'
@ -13,79 +14,72 @@ 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 { safeNameSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { lg, md, screenSm, secondaryText, sm } from 'src/theme/variables'
import { copyToClipboard } from 'src/utils/clipboard'
const useStyles = makeStyles(
createStyles({
heading: {
padding: `${md} ${lg}`,
justifyContent: 'space-between',
maxHeight: '75px',
boxSizing: 'border-box',
const styles = () => ({
heading: {
padding: `${md} ${lg}`,
justifyContent: 'space-between',
maxHeight: '75px',
boxSizing: 'border-box',
},
close: {
height: lg,
width: lg,
fill: secondaryText,
},
qrContainer: {
backgroundColor: '#fff',
padding: md,
borderRadius: '6px',
border: `1px solid ${secondaryText}`,
},
annotation: {
margin: lg,
marginBottom: 0,
},
safeName: {
margin: `${md} 0`,
},
buttonRow: {
height: '84px',
justifyContent: 'center',
'& > button': {
fontFamily: 'Averta',
fontSize: md,
boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
},
close: {
height: lg,
width: lg,
fill: secondaryText,
},
qrContainer: {
backgroundColor: '#fff',
padding: md,
borderRadius: '6px',
border: `1px solid ${secondaryText}`,
},
annotation: {
margin: lg,
marginBottom: 0,
},
safeName: {
margin: `${md} 0`,
},
buttonRow: {
height: '84px',
justifyContent: 'center',
'& > button': {
fontFamily: 'Averta',
fontSize: md,
boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
},
},
addressContainer: {
flexDirection: 'column',
justifyContent: 'center',
margin: `${lg} 0`,
},
addressContainer: {
flexDirection: 'column',
justifyContent: 'center',
margin: `${lg} 0`,
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
[`@media (min-width: ${screenSm}px)`]: {
flexDirection: 'row',
},
address: {
marginLeft: sm,
marginRight: sm,
maxWidth: '70%',
overflowWrap: 'break-word',
},
address: {
marginLeft: sm,
marginRight: sm,
maxWidth: '70%',
overflowWrap: 'break-word',
[`@media (min-width: ${screenSm}px)`]: {
maxWidth: 'none',
},
[`@media (min-width: ${screenSm}px)`]: {
maxWidth: 'none',
},
}),
)
type Props = {
onClose: () => void
safeAddress: string
safeName: string
}
const ReceiveModal = ({ onClose, safeAddress, safeName }: Props) => {
const classes = useStyles()
},
})
const Receive = ({ classes, onClose }) => {
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const safeName = useSelector(safeNameSelector)
return (
<>
<Row align="center" className={classes.heading} grow>
<Paragraph noMargin size="xl" weight="bolder">
<Paragraph className={classes.manage} noMargin size="xl" weight="bolder">
Receive funds
</Paragraph>
<IconButton disableRipple onClick={onClose}>
@ -128,4 +122,4 @@ const ReceiveModal = ({ onClose, safeAddress, safeName }: Props) => {
)
}
export default ReceiveModal
export default withStyles(styles as any)(Receive)

View File

@ -30,7 +30,7 @@ import { currentCurrencySelector, safeFiatBalancesTotalSelector } from 'src/logi
import { formatAmountInUsFormat } from 'src/logic/tokens/utils/formatAmount'
import { grantedSelector } from 'src/routes/safe/container/selector'
import Receive from './ReceiveModal'
import Receive from './ModalReceive'
import { useSidebarItems } from 'src/components/AppLayout/Sidebar/useSidebarItems'
const notificationStyles = {
@ -79,8 +79,7 @@ const App: React.FC = ({ children }) => {
const sendFunds = safeActionsState.sendFunds as { isOpen: boolean; selectedToken: string }
const formattedTotalBalance = currentSafeBalance ? formatAmountInUsFormat(currentSafeBalance) : ''
const balance =
!!formattedTotalBalance && !!currentCurrency ? `${formattedTotalBalance} ${currentCurrency}` : undefined
const balance = !!formattedTotalBalance && !!currentCurrency ? `${formattedTotalBalance} ${currentCurrency}` : null
useEffect(() => {
if (matchSafe?.isExact) {
@ -134,16 +133,14 @@ const App: React.FC = ({ children }) => {
selectedToken={sendFunds.selectedToken}
/>
{safeAddress && safeName && (
<Modal
description="Receive Tokens Form"
handleClose={onReceiveHide}
open={safeActionsState.showReceive as boolean}
title="Receive Tokens"
>
<Receive onClose={onReceiveHide} safeAddress={safeAddress} safeName={safeName} />
</Modal>
)}
<Modal
description="Receive Tokens Form"
handleClose={onReceiveHide}
open={safeActionsState.showReceive as boolean}
title="Receive Tokens"
>
<Receive onClose={onReceiveHide} />
</Modal>
</>
</SnackbarProvider>
<CookiesBanner />

View File

@ -50,7 +50,7 @@ export const Base = (): React.ReactElement => {
safeAddress="0xEE63624cC4Dd2355B16b35eFaadF3F7450A9438B"
safeName="someName"
granted={true}
balance={undefined}
balance={null}
onToggleSafeList={() => console.log}
onReceiveClick={() => console.log}
onNewTransactionClick={() => console.log}

View File

@ -50,7 +50,7 @@ const HeaderComponent = (): React.ReactElement => {
}
const getProviderInfoBased = () => {
if (!loaded || !provider) {
if (!loaded) {
return <ProviderDisconnected />
}

View File

@ -79,10 +79,10 @@ const UnStyledButton = styled.button`
`
type Props = {
address: string | undefined
safeName: string | undefined
address: string | null
safeName: string
granted: boolean
balance: string | undefined
balance: string | null
onToggleSafeList: () => void
onReceiveClick: () => void
onNewTransactionClick: () => void

View File

@ -38,9 +38,9 @@ const HelpCenterLink = styled.a`
}
`
type Props = {
safeAddress?: string
safeName?: string
balance?: string
safeAddress: string | null
safeName: string | null
balance: string | null
granted: boolean
onToggleSafeList: () => void
onReceiveClick: () => void
@ -57,32 +57,34 @@ const Sidebar = ({
onToggleSafeList,
onReceiveClick,
onNewTransactionClick,
}: Props): React.ReactElement => (
<>
<SafeHeader
address={safeAddress}
safeName={safeName}
granted={granted}
balance={balance}
onToggleSafeList={onToggleSafeList}
onReceiveClick={onReceiveClick}
onNewTransactionClick={onNewTransactionClick}
/>
}: Props): React.ReactElement => {
return (
<>
<SafeHeader
address={safeAddress}
safeName={safeName}
granted={granted}
balance={balance}
onToggleSafeList={onToggleSafeList}
onReceiveClick={onReceiveClick}
onNewTransactionClick={onNewTransactionClick}
/>
{items.length ? (
<>
{items.length ? (
<>
<StyledDivider />
<List items={items} />
</>
) : null}
<HelpContainer>
<StyledDivider />
<List items={items} />
</>
) : null}
<HelpContainer>
<StyledDivider />
<HelpCenterLink href="https://help.gnosis-safe.io/en/" target="_blank" title="Help Center of Gnosis Safe">
<IconText text="HELP CENTER" iconSize="md" textSize="md" color="placeHolder" iconType="question" />
</HelpCenterLink>
</HelpContainer>
</>
)
<HelpCenterLink href="https://help.gnosis-safe.io/en/" target="_blank" title="Help Center of Gnosis Safe">
<IconText text="HELP CENTER" iconSize="md" textSize="md" color="placeHolder" iconType="question" />
</HelpCenterLink>
</HelpContainer>
</>
)
}
export default Sidebar

View File

@ -60,9 +60,9 @@ export const FooterWrapper = styled.footer`
type Props = {
sidebarItems: ListItemType[]
safeAddress: string | undefined
safeName: string | undefined
balance: string | undefined
safeAddress: string | null
safeName: string | null
balance: string | null
granted: boolean
onToggleSafeList: () => void
onReceiveClick: () => void

View File

@ -82,7 +82,7 @@ const useStyles = makeStyles({
})
type Props = {
currentSafe: string | undefined
currentSafe: string | null
defaultSafe: DefaultSafe
safes: SafeRecord[]
onSafeClick: () => void

View File

@ -8,7 +8,7 @@ interface CellWidth {
maxWidth: string
}
export const cellWidth = (width?: string | number): CellWidth | undefined => {
export const cellWidth = (width: string | number): CellWidth | undefined => {
if (!width) {
return undefined
}

View File

@ -4,7 +4,6 @@ import { createSelector } from 'reselect'
import { ADDRESS_BOOK_REDUCER_ID } from 'src/logic/addressBook/store/reducer/addressBook'
import { AddressBookMap } from 'src/logic/addressBook/store/reducer/types/addressBook.d'
import { AddressBookEntryRecord } from 'src/logic/addressBook/model/addressBook'
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
export const addressBookMapSelector = (state: AppReduxState): AddressBookMap =>
@ -14,8 +13,8 @@ export const getAddressBook = createSelector(
addressBookMapSelector,
safeParamAddressFromStateSelector,
(addressBook, safeAddress) => {
let result: List<AddressBookEntryRecord> = List([])
if (addressBook && safeAddress) {
let result = List([])
if (addressBook) {
result = addressBook.get(safeAddress, List())
}
return result

View File

@ -19,7 +19,7 @@ export const saveAddressBook = async (addressBook) => {
}
}
export const getAddressesListFromAdbk = (addressBook: List<any>) => addressBook.map((entry: any) => entry.address)
export const getAddressesListFromAdbk = (addressBook) => Array.from(addressBook).map((entry: any) => entry.address)
export const getNameFromAdbk = (addressBook, userAddress) => {
const entry = addressBook.find((addressBookItem) => addressBookItem.address === userAddress)

View File

@ -2,19 +2,14 @@ import { AbiItem } from 'web3-utils'
import { web3ReadOnly as web3 } from 'src/logic/wallets/getWeb3'
export interface AllowedAbiItem extends AbiItem {
name: string
type: 'function'
}
export interface AbiItemExtended extends AllowedAbiItem {
export interface AbiItemExtended extends AbiItem {
action: string
methodSignature: string
signatureHash: string
}
export const getMethodSignature = ({ inputs, name }: AbiItem): string => {
const params = inputs?.map((x) => x.type).join(',')
const params = inputs.map((x) => x.type).join(',')
return `${name}(${params})`
}
@ -40,17 +35,12 @@ export const isAllowedMethod = ({ name, type }: AbiItem): boolean => {
}
export const getMethodAction = ({ stateMutability }: AbiItem): 'read' | 'write' => {
if (!stateMutability) {
return 'write'
}
return ['view', 'pure'].includes(stateMutability) ? 'read' : 'write'
}
export const extractUsefulMethods = (abi: AbiItem[]): AbiItemExtended[] => {
const allowedAbiItems = abi.filter(isAllowedMethod) as AllowedAbiItem[]
return allowedAbiItems
return abi
.filter(isAllowedMethod)
.map(
(method): AbiItemExtended => ({
action: getMethodAction(method),
@ -58,11 +48,9 @@ export const extractUsefulMethods = (abi: AbiItem[]): AbiItemExtended[] => {
...method,
}),
)
.sort(({ name: a }, { name: b }) => {
return a.toLowerCase() > b.toLowerCase() ? 1 : -1
})
.sort(({ name: a }, { name: b }) => (a.toLowerCase() > b.toLowerCase() ? 1 : -1))
}
export const isPayable = (method: AbiItem | AbiItemExtended): boolean => {
return !!method?.payable
return method.payable
}

View File

@ -6,12 +6,15 @@ import { TokenProps } from 'src/logic/tokens/store/model/token'
export type BalanceEndpoint = {
balance: string
balanceUsd: string
tokenAddress: string
tokenAddress?: string
token?: TokenProps
usdConversion: string
}
const fetchTokenCurrenciesBalances = (safeAddress: string): Promise<AxiosResponse<BalanceEndpoint[]>> => {
const fetchTokenCurrenciesBalances = (safeAddress?: string): Promise<AxiosResponse<BalanceEndpoint[]>> => {
if (!safeAddress) {
return null
}
const apiUrl = getTxServiceHost()
const url = `${apiUrl}safes/${safeAddress}/balances/usd/`

View File

@ -12,7 +12,7 @@ export const fetchCurrencyValues = (safeAddress: string) => async (
dispatch: Dispatch<typeof setCurrencyBalances | typeof setSelectedCurrency | typeof setCurrencyRate>,
): Promise<void> => {
try {
const storedCurrencies = await loadCurrencyValues()
const storedCurrencies: Map<string, CurrencyRateValue> | unknown = await loadCurrencyValues()
const storedCurrency = storedCurrencies[safeAddress]
if (!storedCurrency) {
return batch(() => {

View File

@ -1,11 +1,18 @@
import fetchCurrencyRate from 'src/logic/currencyValues/store/actions/fetchCurrencyRate'
import { SET_CURRENCY_BALANCES } from 'src/logic/currencyValues/store/actions/setCurrencyBalances'
import { SET_CURRENCY_RATE } from 'src/logic/currencyValues/store/actions/setCurrencyRate'
import { SET_CURRENT_CURRENCY } from 'src/logic/currencyValues/store/actions/setSelectedCurrency'
import { currencyValuesSelector } from 'src/logic/currencyValues/store/selectors'
import { saveCurrencyValues } from 'src/logic/currencyValues/store/utils/currencyValuesStorage'
import { AVAILABLE_CURRENCIES, CurrencyRateValue } from '../model/currencyValues'
import { Map } from 'immutable'
const watchedActions = [SET_CURRENT_CURRENCY]
const watchedActions = [SET_CURRENT_CURRENCY, SET_CURRENCY_RATE, SET_CURRENCY_BALANCES]
const currencyValuesStorageMiddleware = (store) => (next) => async (action) => {
const handledAction = next(action)
if (watchedActions.includes(action.type)) {
const state = store.getState()
const { dispatch } = store
switch (action.type) {
case SET_CURRENT_CURRENCY: {
@ -13,6 +20,22 @@ const currencyValuesStorageMiddleware = (store) => (next) => async (action) => {
dispatch(fetchCurrencyRate(safeAddress, selectedCurrency))
break
}
case SET_CURRENCY_RATE:
case SET_CURRENCY_BALANCES: {
const currencyValues = currencyValuesSelector(state)
const currencyValuesWithoutBalances: Map<string, CurrencyRateValue> = currencyValues.map((currencyValue) => {
const currencyRate: number = currencyValue.get('currencyRate')
const selectedCurrency: AVAILABLE_CURRENCIES = currencyValue.get('selectedCurrency')
return {
currencyRate,
selectedCurrency,
}
})
await saveCurrencyValues(currencyValuesWithoutBalances)
break
}
default:
break

View File

@ -16,7 +16,7 @@ export const safeFiatBalancesSelector = createSelector(
currencyValuesSelector,
safeParamAddressFromStateSelector,
(currencyValues, safeAddress): CurrencyReducerMap | undefined => {
if (!currencyValues || !safeAddress) return
if (!currencyValues) return
return currencyValues.get(safeAddress)
},
)

View File

@ -11,6 +11,6 @@ export const saveCurrencyValues = async (currencyValues: Map<string, CurrencyRat
}
}
export const loadCurrencyValues = async (): Promise<Record<string, CurrencyRateValue>> => {
export const loadCurrencyValues = async (): Promise<Map<string, CurrencyRateValue> | unknown> => {
return (await loadFromStorage(CURRENCY_VALUES_STORAGE_KEY)) || {}
}

View File

@ -5,9 +5,7 @@ import { getCurrentSessionFromStorage } from 'src/logic/currentSession/utils'
const loadCurrentSessionFromStorage = () => async (dispatch) => {
const currentSession = await getCurrentSessionFromStorage()
if (currentSession) {
dispatch(loadCurrentSession(makeCurrentSession(currentSession)))
}
dispatch(loadCurrentSession(makeCurrentSession(currentSession ? currentSession : {})))
}
export default loadCurrentSessionFromStorage

View File

@ -1,9 +1,5 @@
import { Record } from 'immutable'
type SessionProps = {
viewedSafes: string[]
}
export const makeCurrentSession = Record<SessionProps>({
export const makeCurrentSession = Record({
viewedSafes: [],
})

View File

@ -7,10 +7,6 @@ import { saveCurrentSessionToStorage } from 'src/logic/currentSession/utils'
export const CURRENT_SESSION_REDUCER_ID = 'currentSession'
export type SerializedSessionState = {
viewedSafes: string[]
}
export default handleActions(
{
[LOAD_CURRENT_SESSION]: (state, action) => state.merge(Map(action.payload)),

View File

@ -1,10 +1,8 @@
import { loadFromStorage, saveToStorage } from 'src/utils/storage'
import { SerializedSessionState } from 'src/logic/currentSession/store/reducer/currentSession'
const CURRENT_SESSION_STORAGE_KEY = 'CURRENT_SESSION'
export const getCurrentSessionFromStorage = async (): Promise<SerializedSessionState | undefined> =>
loadFromStorage(CURRENT_SESSION_STORAGE_KEY)
export const getCurrentSessionFromStorage = async () => loadFromStorage(CURRENT_SESSION_STORAGE_KEY)
export const saveCurrentSessionToStorage = async (currentSession) => {
try {

View File

@ -16,7 +16,7 @@ interface DebounceOptions {
export const useDebouncedCallback = <T extends (...args: unknown[]) => unknown>(
callback: T,
delay = 0,
options?: DebounceOptions,
options: DebounceOptions,
): T & { cancel: () => void } => useCallback(debounce(callback, delay, options), [callback, delay, options])
export const useDebounce = <T extends unknown>(value: T, delay = 0, options?: DebounceOptions): T => {

View File

@ -15,7 +15,7 @@ const setNotificationOrigin = (notification: Notification, origin: string): Noti
}
const appInfo = getAppInfoFromOrigin(origin)
return { ...notification, message: `${appInfo ? appInfo.name : 'Unknown origin'}: ${notification.message}` }
return { ...notification, message: `${appInfo.name}: ${notification.message}` }
}
const getStandardTxNotificationsQueue = (

View File

@ -10,7 +10,7 @@ import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTr
import fetchSafeCreationTx from 'src/logic/safe/store/actions/fetchSafeCreationTx'
import { Dispatch } from 'src/logic/safe/store/actions/types.d'
export const useLoadSafe = (safeAddress?: string): void => {
export const useLoadSafe = (safeAddress: string): void => {
const dispatch = useDispatch<Dispatch>()
useEffect(() => {

View File

@ -8,9 +8,9 @@ import { checkAndUpdateSafe } from 'src/logic/safe/store/actions/fetchSafe'
import fetchTransactions from 'src/logic/safe/store/actions/transactions/fetchTransactions'
import { TIMEOUT } from 'src/utils/constants'
export const useSafeScheduledUpdates = (safeAddress?: string): void => {
export const useSafeScheduledUpdates = (safeAddress: string): void => {
const dispatch = useDispatch()
const timer = useRef<number>()
const timer = useRef<number>(null)
useEffect(() => {
// using this variable to prevent setting a timeout when the component is already unmounted or the effect
@ -29,7 +29,7 @@ export const useSafeScheduledUpdates = (safeAddress?: string): void => {
if (mounted) {
timer.current = setTimeout(() => {
fetchSafeData(address)
fetchSafeData(safeAddress)
}, TIMEOUT * 3)
}
}

View File

@ -30,8 +30,8 @@ const getAllTransactionsUri = (safeAddress: string): string => {
const fetchAllTransactions = async (
urlParams: ServiceUriParams,
eTag?: string,
): Promise<{ responseEtag?: string; results: Transaction[]; count?: number }> => {
eTag: string | null,
): Promise<{ responseEtag: string; results: Transaction[]; count?: number }> => {
const { safeAddress, limit, offset, orderBy, queued, trusted } = urlParams
try {
const url = getAllTransactionsUri(safeAddress)

View File

@ -100,7 +100,7 @@ interface CreateTransactionArgs {
navigateToTransactionsTab?: boolean
notifiedTransaction: string
operation?: number
origin?: string | null
origin?: string
safeAddress: string
to: string
txData?: string

View File

@ -18,7 +18,7 @@ import { Action, Dispatch } from 'redux'
import { SENTINEL_ADDRESS } from 'src/logic/contracts/safeContracts'
import { AppReduxState } from 'src/store'
const buildOwnersFrom = (safeOwners: string[], localSafe?: SafeRecordProps): List<SafeOwner> => {
const buildOwnersFrom = (safeOwners: string[], localSafe: SafeRecordProps): List<SafeOwner> => {
const ownersList = safeOwners.map((ownerAddress) => {
const convertedAdd = checksumAddress(ownerAddress)
@ -85,7 +85,7 @@ export const buildSafe = async (
needsUpdate,
featuresEnabled,
balances: Map(),
latestIncomingTxBlock: 0,
latestIncomingTxBlock: null,
activeAssets: Set(),
activeTokens: Set(),
blacklistedAssets: Set(),

View File

@ -1,15 +1,15 @@
import { Dispatch } from 'redux'
import { addSafe } from './addSafe'
import { SAFES_KEY } from 'src/logic/safe/utils'
import { SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { buildSafe } from 'src/logic/safe/store/reducer/safe'
import { loadFromStorage } from 'src/utils/storage'
import { addSafe } from './addSafe'
import { buildSafe } from 'src/logic/safe/store/reducer/safe'
import { loadFromStorage } from 'src/utils/storage'
import { Dispatch } from 'redux'
const loadSafesFromStorage = () => async (dispatch: Dispatch): Promise<void> => {
try {
const safes = await loadFromStorage<Record<string, SafeRecordProps>>(SAFES_KEY)
const safes = await loadFromStorage(SAFES_KEY)
if (safes) {
Object.values(safes).forEach((safeProps) => {

View File

@ -34,7 +34,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres
const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
const lastTx = await getLastTx(safeAddress)
const nonce = await getNewTxNonce(undefined, lastTx, safeInstance)
const nonce = await getNewTxNonce(null, lastTx, safeInstance)
const isExecution = approveAndExecute || (await shouldExecuteTransaction(safeInstance, nonce, lastTx))
const safeVersion = await getCurrentSafeVersion(safeInstance)

View File

@ -27,7 +27,7 @@ async function fetchTransactions(
txType: TransactionTypes.INCOMING | TransactionTypes.OUTGOING,
safeAddress: string,
eTag: string | null,
): Promise<{ eTag: string | null; results: TxServiceModel[] | IncomingTxServiceModel[] }> {
): Promise<{ eTag: string; results: TxServiceModel[] | IncomingTxServiceModel[] }> {
try {
const url = getServiceUrl(txType, safeAddress)
const response = await axios.get(url, eTag ? { headers: { 'If-None-Match': eTag } } : undefined)

View File

@ -39,9 +39,8 @@ export default (safeAddress: string): ThunkAction<Promise<void>, AppReduxState,
}
const incomingTransactions = await loadIncomingTransactions(safeAddress)
const safeIncomingTxs = incomingTransactions.get(safeAddress)
if (safeIncomingTxs?.size) {
if (incomingTransactions.get(safeAddress).size) {
dispatch(addIncomingTransactions(incomingTransactions))
}
} catch (error) {

View File

@ -73,8 +73,8 @@ const batchIncomingTxsTokenDataRequest = (txs: IncomingTxServiceModel[]) => {
)
}
let previousETag: string | null = null
export const loadIncomingTransactions = async (safeAddress: string): Promise<Map<string, List<any>>> => {
let previousETag = null
export const loadIncomingTransactions = async (safeAddress: string) => {
const { eTag, results } = await fetchTransactions(TransactionTypes.INCOMING, safeAddress, previousETag)
previousETag = eTag

View File

@ -27,7 +27,8 @@ export type TxServiceModel = {
blockNumber?: number | null
confirmations: ConfirmationServiceModel[]
confirmationsRequired: number
data: string | null
creationTx?: boolean | null
data?: string | null
dataDecoded?: DataDecoded
ethGasPrice: string
executionDate?: string | null
@ -39,15 +40,15 @@ export type TxServiceModel = {
isExecuted: boolean
isSuccessful: boolean
modified: string
nonce: number
nonce?: number | null
operation: number
origin: string | null
origin?: string | null
refundReceiver: string
safe: string
safeTxGas: number
safeTxHash: string
signatures: string
submissionDate: string | null
submissionDate?: string | null
to: string
transactionHash?: string | null
value: string
@ -77,7 +78,7 @@ export type BatchProcessTxsProps = OutgoingTxs & {
*/
const extractCancelAndOutgoingTxs = (safeAddress: string, outgoingTxs: TxServiceModel[]): OutgoingTxs => {
return outgoingTxs.reduce(
(acc: { cancellationTxs: Record<number, TxServiceModel>; outgoingTxs: TxServiceModel[] }, transaction) => {
(acc, transaction) => {
if (
isCancelTransaction(transaction, safeAddress) &&
outgoingTxs.find((tx) => tx.nonce === transaction.nonce && !isCancelTransaction(tx, safeAddress))
@ -163,7 +164,7 @@ const batchProcessOutgoingTransactions = async ({
// outgoing transactions
const outgoingTxsWithData = outgoingTxs.length ? await batchRequestContractCode(outgoingTxs) : []
const outgoing: Transaction[] = []
const outgoing = []
for (const [tx, txCode] of outgoingTxsWithData) {
outgoing.push(
await buildTx({
@ -181,7 +182,7 @@ const batchProcessOutgoingTransactions = async ({
return { cancel, outgoing }
}
let previousETag: string | null = null
let previousETag = null
export const loadOutgoingTransactions = async (safeAddress: string): Promise<SafeTransactionsType> => {
const defaultResponse = {
cancel: Map(),

View File

@ -19,7 +19,6 @@ import {
TransactionTypes,
TransactionTypeValues,
TxArgs,
RefundParams,
} from 'src/logic/safe/store/models/types/transaction'
import { CANCELLATION_TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/cancellationTransactions'
import { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe'
@ -66,15 +65,15 @@ export const isModifySettingsTransaction = (tx: TxServiceModel, safeAddress: str
}
export const isMultiSendTransaction = (tx: TxServiceModel): boolean => {
return !isEmptyData(tx.data) && tx.data?.substring(0, 10) === '0x8d80ff0a' && Number(tx.value) === 0
return !isEmptyData(tx.data) && tx.data.substring(0, 10) === '0x8d80ff0a' && Number(tx.value) === 0
}
export const isUpgradeTransaction = (tx: TxServiceModel): boolean => {
return (
!isEmptyData(tx.data) &&
isMultiSendTransaction(tx) &&
tx.data?.substr(308, 8) === '7de7edef' && // 7de7edef - changeMasterCopy (308, 8)
tx.data?.substr(550, 8) === 'f08a0323' // f08a0323 - setFallbackHandler (550, 8)
tx.data.substr(308, 8) === '7de7edef' && // 7de7edef - changeMasterCopy (308, 8)
tx.data.substr(550, 8) === 'f08a0323' // f08a0323 - setFallbackHandler (550, 8)
)
}
@ -84,7 +83,7 @@ export const isOutgoingTransaction = (tx: TxServiceModel, safeAddress: string):
export const isCustomTransaction = async (
tx: TxServiceModel,
txCode: string | null,
txCode: string,
safeAddress: string,
knownTokens: Map<string, Token>,
): Promise<boolean> => {
@ -99,9 +98,9 @@ export const isCustomTransaction = async (
export const getRefundParams = async (
tx: TxServiceModel,
tokenInfo: (string) => Promise<{ decimals: number; symbol: string } | null>,
): Promise<RefundParams | null> => {
): Promise<any> => {
const txGasPrice = Number(tx.gasPrice)
let refundParams: RefundParams | null = null
let refundParams = null
if (txGasPrice > 0) {
let refundSymbol = 'ETH'
@ -274,6 +273,7 @@ export const buildTx = async ({
blockNumber: tx.blockNumber,
cancelled: isTxCancelled,
confirmations,
creationTx: tx.creationTx,
customTx: isCustomTx,
data: tx.data ? tx.data : EMPTY_DATA,
dataDecoded: tx.dataDecoded,
@ -326,7 +326,7 @@ export const mockTransaction = (tx: TxToMock, safeAddress: string, state: AppRed
return buildTx({
cancellationTxs,
currentUser: undefined,
currentUser: null,
knownTokens,
outgoingTxs,
safe,
@ -345,7 +345,7 @@ export const updateStoredTransactionsStatus = (dispatch: (any) => void, walletRe
dispatch(
addOrUpdateTransactions({
safeAddress,
transactions: transactions.withMutations((list: any[]) =>
transactions: transactions.withMutations((list) =>
list.map((tx) => tx.set('status', calculateTransactionStatus(tx, safe, walletRecord.account))),
),
}),

View File

@ -4,7 +4,7 @@ import axios from 'axios'
import { buildTxServiceUrl } from 'src/logic/safe/transactions/txHistory'
export const getLastTx = async (safeAddress: string): Promise<TxServiceModel | null> => {
export const getLastTx = async (safeAddress: string): Promise<TxServiceModel> => {
try {
const url = buildTxServiceUrl(safeAddress)
const response = await axios.get(url, { params: { limit: 1 } })
@ -17,24 +17,23 @@ export const getLastTx = async (safeAddress: string): Promise<TxServiceModel | n
}
export const getNewTxNonce = async (
txNonce: string | undefined,
lastTx: TxServiceModel | null,
txNonce: string | null,
lastTx: TxServiceModel,
safeInstance: GnosisSafe,
): Promise<string> => {
if (typeof txNonce === 'string' && !Number.isInteger(Number.parseInt(txNonce, 10))) {
if (!Number.isInteger(Number.parseInt(txNonce, 10))) {
return lastTx === null
? // use current's safe nonce as fallback
(await safeInstance.methods.nonce().call()).toString()
: `${lastTx.nonce + 1}`
}
return txNonce as string
return txNonce
}
export const shouldExecuteTransaction = async (
safeInstance: GnosisSafe,
nonce: string,
lastTx: TxServiceModel | null,
lastTx: TxServiceModel,
): Promise<boolean> => {
const threshold = await safeInstance.methods.getThreshold().call()
@ -46,7 +45,7 @@ export const shouldExecuteTransaction = async (
// by the user using the exec button.
const canExecuteCurrentTransaction = lastTx && lastTx.isExecuted
return isFirstTransaction || !!canExecuteCurrentTransaction
return isFirstTransaction || canExecuteCurrentTransaction
}
return false

View File

@ -85,7 +85,7 @@ const notificationsMiddleware = (store) => (next) => async (action) => {
const safes = safesMapSelector(state)
const currentSafe = safes.get(safeAddress)
if (!currentSafe || !isUserAnOwner(currentSafe, userAddress) || awaitingTransactions.size === 0) {
if (!isUserAnOwner(currentSafe, userAddress) || awaitingTransactions.size === 0) {
break
}

View File

@ -33,7 +33,6 @@ export enum PendingActionType {
REJECT = 'reject',
}
export type PendingActionValues = PendingActionType[keyof PendingActionType]
export type RefundParams = { fee: string; symbol: string }
export type TransactionProps = {
baseGas: number
@ -44,7 +43,7 @@ export type TransactionProps = {
creator: string
creationTx: boolean
customTx: boolean
data: string | null
data?: string | null
dataDecoded: DataDecoded | null
decimals?: (number | string) | null
decodedParams: DecodedParams | null
@ -52,7 +51,7 @@ export type TransactionProps = {
executionTxHash?: string | null
executor: string
factoryAddress: string
fee: string | null // It will be replace with the new TXs types.
fee?: string // It will be replace with the new TXs types.
gasPrice: string
gasToken: string
isCancellationTx: boolean
@ -64,18 +63,18 @@ export type TransactionProps = {
masterCopy: string
modifySettingsTx: boolean
multiSendTx: boolean
nonce: number
nonce?: number | null
operation: number
origin: string | null
ownersWithPendingActions: Map<PendingActionValues, List<any>>
recipient: string
refundParams: RefundParams | null
refundParams: any
refundReceiver: string
safeTxGas: number
safeTxHash: string
setupData: string
status: TransactionStatus
submissionDate: string | null
status?: TransactionStatus
submissionDate?: string | null
symbol?: string | null
transactionHash: string | null
transfers?: Transfer[]
@ -88,7 +87,7 @@ export type Transaction = RecordOf<TransactionProps>
export type TxArgs = {
baseGas: number
data: string
data?: string | null
gasPrice: string
gasToken: string
nonce: number

View File

@ -37,7 +37,7 @@ export const buildSafe = (storedSafe: SafeRecordProps): SafeRecordProps => {
blacklistedTokens,
activeAssets,
blacklistedAssets,
latestIncomingTxBlock: 0,
latestIncomingTxBlock: null,
modules: null,
}
}

View File

@ -12,11 +12,11 @@ export const allTransactionsSelector = createSelector(getTransactionsStateSelect
export const safeAllTransactionsSelector = createSelector(
safeParamAddressFromStateSelector,
allTransactionsSelector,
(safeAddress, transactions) => (safeAddress ? transactions[safeAddress]?.transactions : []),
(safeAddress, transactions) => transactions[safeAddress]?.transactions || [],
)
export const safeTotalTransactionsAmountSelector = createSelector(
safeParamAddressFromStateSelector,
allTransactionsSelector,
(safeAddress, transactions) => (safeAddress ? transactions[safeAddress]?.totalTransactionsCount : 0),
(safeAddress, transactions) => transactions[safeAddress]?.totalTransactionsCount || 0,
)

View File

@ -36,7 +36,7 @@ const cancellationTransactionsSelector = (state: AppReduxState) => state[CANCELL
const incomingTransactionsSelector = (state: AppReduxState) => state[INCOMING_TRANSACTIONS_REDUCER_ID]
export const safeParamAddressFromStateSelector = (state: AppReduxState): string | undefined => {
export const safeParamAddressFromStateSelector = (state: AppReduxState): string | null => {
const match = matchPath<{ safeAddress: string }>(state.router.location.pathname, {
path: `${SAFELIST_ADDRESS}/:safeAddress`,
})
@ -45,7 +45,7 @@ export const safeParamAddressFromStateSelector = (state: AppReduxState): string
return checksumAddress(match.params.safeAddress)
}
return undefined
return null
}
export const safeParamAddressSelector = (
@ -177,16 +177,16 @@ export const safeBlacklistedAssetsSelector = createSelector(
)
export const safeActiveAssetsSelectorBySafe = (safeAddress: string, safes: SafesMap): Set<string> =>
safes.get(safeAddress)?.get('activeAssets') || Set()
safes.get(safeAddress).get('activeAssets')
export const safeBlacklistedAssetsSelectorBySafe = (safeAddress: string, safes: SafesMap): Set<string> =>
safes.get(safeAddress)?.get('blacklistedAssets') || Set()
safes.get(safeAddress).get('blacklistedAssets')
const baseSafe = makeSafe()
export const safeFieldSelector = <K extends keyof SafeRecordProps>(field: K) => (
safe: SafeRecord,
): SafeRecordProps[K] | undefined => (safe ? safe.get(field, baseSafe.get(field)) : undefined)
): SafeRecordProps[K] | null => (safe ? safe.get(field, baseSafe.get(field)) : null)
export const safeNameSelector = createSelector(safeSelector, safeFieldSelector('name'))

View File

@ -1,13 +1,8 @@
import { List } from 'immutable'
import { isPendingTransaction } from 'src/logic/safe/store/actions/transactions/utils/transactionHelpers'
import { Transaction } from 'src/logic/safe/store/models/types/transaction'
export const getAwaitingTransactions = (
allTransactions: List<Transaction>,
cancellationTxs,
userAccount: string,
): List<Transaction> => {
export const getAwaitingTransactions = (allTransactions = List([]), cancellationTxs, userAccount: string) => {
return allTransactions.filter((tx) => {
const cancelTx = !!tx.nonce && !isNaN(Number(tx.nonce)) ? cancellationTxs.get(`${tx.nonce}`) : null

View File

@ -25,7 +25,7 @@ const estimateDataGasCosts = (data: string): number => {
return accumulator + 16
}
return data.match(/.{2}/g)?.reduce(reducer, 0)
return data.match(/.{2}/g).reduce(reducer, 0)
}
export const estimateTxGasCosts = async (
@ -38,11 +38,6 @@ export const estimateTxGasCosts = async (
try {
const web3 = getWeb3()
const from = await getAccountFrom(web3)
if (!from) {
return 0
}
const safeInstance = (new web3.eth.Contract(GnosisSafeSol.abi as AbiItem[], safeAddress) as unknown) as GnosisSafe
const nonce = await safeInstance.methods.nonce().call()
const threshold = await safeInstance.methods.getThreshold().call()

View File

@ -39,7 +39,7 @@ export const ethSigner = async ({
return reject(err)
}
if (signature?.result == null) {
if (signature.result == null) {
reject(new Error(ETH_SIGN_NOT_SUPPORTED_ERROR_MSG))
return
}

View File

@ -2,18 +2,11 @@ import { loadFromStorage, saveToStorage } from 'src/utils/storage'
import { SafeRecordProps } from 'src/logic/safe/store/models/safe'
export const SAFES_KEY = 'SAFES'
export const TX_KEY = 'TX'
export const DEFAULT_SAFE_KEY = 'DEFAULT_SAFE'
type StoredSafes = Record<string, SafeRecordProps>
export const loadStoredSafes = async (): Promise<StoredSafes | undefined> => {
const safes = await loadFromStorage<StoredSafes>(SAFES_KEY)
return safes
}
export const getSafeName = async (safeAddress: string): Promise<string | undefined> => {
const safes = await loadStoredSafes()
const safes = await loadFromStorage(SAFES_KEY)
if (!safes) {
return undefined
}
@ -30,9 +23,9 @@ export const saveSafes = async (safes) => {
}
}
export const getLocalSafe = async (safeAddress: string): Promise<SafeRecordProps | undefined> => {
const storedSafes = await loadStoredSafes()
return storedSafes?.[safeAddress]
export const getLocalSafe = async (safeAddress: string): Promise<SafeRecordProps | null> => {
const storedSafes = (await loadFromStorage(SAFES_KEY)) || {}
return storedSafes[safeAddress] || null
}
export const getDefaultSafe = async (): Promise<string> => {

View File

@ -11,15 +11,13 @@ export const FEATURES = [
{ name: 'ERC1155', validVersion: '>=1.1.1' },
]
type Feature = typeof FEATURES[number]
export const safeNeedsUpdate = (currentVersion?: string, latestVersion?: string): boolean => {
export const safeNeedsUpdate = (currentVersion: string, latestVersion: string): boolean => {
if (!currentVersion || !latestVersion) {
return false
}
const current = semverValid(currentVersion) as string
const latest = semverValid(latestVersion) as string
const current = semverValid(currentVersion)
const latest = semverValid(latestVersion)
return latest ? semverLessThan(current, latest) : false
}
@ -28,7 +26,7 @@ export const getCurrentSafeVersion = (gnosisSafeInstance: GnosisSafe): Promise<s
gnosisSafeInstance.methods.VERSION().call()
export const enabledFeatures = (version: string): string[] =>
FEATURES.reduce((acc: string[], feature: Feature) => {
FEATURES.reduce((acc, feature) => {
if (semverSatisfies(version, feature.validVersion)) {
acc.push(feature.name)
}
@ -46,11 +44,11 @@ export const checkIfSafeNeedsUpdate = async (
lastSafeVersion: string,
): Promise<SafeVersionInfo> => {
if (!gnosisSafeInstance || !lastSafeVersion) {
throw new Error('checkIfSafeNeedsUpdate: No Safe Instance or version provided')
return null
}
const safeMasterVersion = await getCurrentSafeVersion(gnosisSafeInstance)
const current = semverValid(safeMasterVersion) as string
const latest = semverValid(lastSafeVersion) as string
const current = semverValid(safeMasterVersion)
const latest = semverValid(lastSafeVersion)
const needUpdate = safeNeedsUpdate(safeMasterVersion, lastSafeVersion)
return { current, latest, needUpdate }

View File

@ -48,7 +48,7 @@ const extractDataFromResult = (currentTokens: TokenState) => (
if (tokenAddress === null) {
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(token.decimals)) })
if (currentTokens && !currentTokens.get(tokenAddress)) {
acc.tokens = acc.tokens.push(makeToken({ address: tokenAddress, ...token }))
@ -57,7 +57,7 @@ const extractDataFromResult = (currentTokens: TokenState) => (
acc.currencyList = acc.currencyList.push(
makeBalanceCurrency({
currencyName: balanceUsd ? AVAILABLE_CURRENCIES.USD : undefined,
currencyName: balanceUsd ? AVAILABLE_CURRENCIES.USD : null,
tokenAddress,
balanceInBaseCurrency: balanceUsd,
balanceInSelectedCurrency: balanceUsd,

View File

@ -57,7 +57,11 @@ const getTokenValues = (tokenAddress) =>
methods: ['decimals', 'name', 'symbol'],
})
export const getTokenInfos = async (tokenAddress: string): Promise<Token | undefined> => {
export const getTokenInfos = async (tokenAddress: string): Promise<Token> => {
if (!tokenAddress) {
return null
}
const { tokens } = store.getState()
const localToken = tokens.get(tokenAddress)
@ -70,7 +74,7 @@ export const getTokenInfos = async (tokenAddress: string): Promise<Token | undef
const [tokenDecimals, tokenName, tokenSymbol] = await getTokenValues(tokenAddress)
if (tokenDecimals === null) {
return undefined
return null
}
const token = makeToken({

View File

@ -5,7 +5,7 @@ export type TokenProps = {
name: string
symbol: string
decimals: number | string
logoUri: string
logoUri?: string | null
balance?: number | string
}

View File

@ -35,18 +35,18 @@ export const isAddressAToken = async (tokenAddress: string): Promise<boolean> =>
// } catch {
// return 'Not a token address'
// }
const call = await web3.eth.call({ to: tokenAddress, data: web3.utils.sha3('totalSupply()') as string })
const call = await web3.eth.call({ to: tokenAddress, data: web3.utils.sha3('totalSupply()') })
return call !== '0x'
}
export const isTokenTransfer = (tx: TxServiceModel): boolean => {
return !isEmptyData(tx.data) && tx.data?.substring(0, 10) === '0xa9059cbb' && Number(tx.value) === 0
return !isEmptyData(tx.data) && tx.data.substring(0, 10) === '0xa9059cbb' && Number(tx.value) === 0
}
export const isSendERC721Transaction = (
tx: TxServiceModel,
txCode: string | null,
txCode: string,
knownTokens: Map<string, Token>,
): boolean => {
// "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85" - ens token contract, includes safeTransferFrom
@ -78,7 +78,7 @@ export const getERC20DecimalsAndSymbol = async (
try {
const storedTokenInfo = await getTokenInfos(tokenAddress)
if (!storedTokenInfo) {
if (storedTokenInfo === null) {
const [tokenDecimals, tokenSymbol] = await generateBatchRequests({
abi: ALTERNATIVE_TOKEN_ABI,
address: tokenAddress,
@ -96,7 +96,7 @@ export const getERC20DecimalsAndSymbol = async (
export const isSendERC20Transaction = async (
tx: TxServiceModel,
txCode: string | null,
txCode: string,
knownTokens: Map<string, Token>,
): Promise<boolean> => {
let isSendTokenTx = !isSendERC721Transaction(tx, txCode, knownTokens) && isTokenTransfer(tx)

View File

@ -2,7 +2,7 @@ import { List } from 'immutable'
import { SafeRecord } from 'src/logic/safe/store/models/safe'
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
export const sameAddress = (firstAddress: string | undefined, secondAddress: string | undefined): boolean => {
export const sameAddress = (firstAddress: string, secondAddress: string): boolean => {
if (!firstAddress) {
return false
}

View File

@ -96,7 +96,7 @@ const isSmartContractWallet = async (web3Provider: Web3, account: string): Promi
}
export const getProviderInfo = async (web3Instance: Web3, providerName = 'Wallet'): Promise<ProviderProps> => {
const account = (await getAccountFrom(web3Instance)) || ''
const account = await getAccountFrom(web3Instance)
const network = await getNetworkIdFrom(web3Instance)
const smartContractWallet = await isSmartContractWallet(web3Instance, account)
const hardwareWallet = isHardwareWallet(providerName)

View File

@ -16,7 +16,8 @@ export const loadLastUsedProvider = async (): Promise<string | undefined> => {
return lastUsedProvider
}
let watcherInterval
let watcherInterval = null
const providerWatcherMware = (store) => (next) => async (action) => {
const handledAction = next(action)

View File

@ -120,8 +120,6 @@ const DetailsForm = ({ errors, form }: DetailsFormProps): React.ReactElement =>
fieldMutator={(val) => {
form.mutators.setValue(FIELD_LOAD_ADDRESS, val)
}}
// eslint-disable-next-line
// @ts-ignore
inputAdornment={
noErrorsOn(FIELD_LOAD_ADDRESS, errors) && {
endAdornment: (
@ -158,15 +156,12 @@ const DetailsForm = ({ errors, form }: DetailsFormProps): React.ReactElement =>
)
}
const DetailsPage = () =>
function LoadSafeDetails(controls: React.ReactNode, { errors, form }: StepperPageFormProps): React.ReactElement {
return (
<>
<OpenPaper controls={controls}>
<DetailsForm errors={errors} form={form} />
</OpenPaper>
</>
)
}
const DetailsPage = () => (controls: React.ReactNode, { errors, form }: StepperPageFormProps): React.ReactElement => (
<>
<OpenPaper controls={controls}>
<DetailsForm errors={errors} form={form} />
</OpenPaper>
</>
)
export default DetailsPage

View File

@ -79,7 +79,7 @@ const calculateSafeValues = (owners, threshold, values) => {
}
const OwnerListComponent = (props) => {
const [owners, setOwners] = useState<string[]>([])
const [owners, setOwners] = useState([])
const { classes, updateInitialProps, values } = props
useEffect(() => {
@ -156,15 +156,12 @@ const OwnerListComponent = (props) => {
const OwnerListPage = withStyles(styles as any)(OwnerListComponent)
const OwnerList = ({ updateInitialProps }, network) =>
function LoadSafeOwnerList(controls, { values }): React.ReactElement {
return (
<>
<OpenPaper controls={controls} padding={false}>
<OwnerListPage network={network} updateInitialProps={updateInitialProps} values={values} />
</OpenPaper>
</>
)
}
const OwnerList = ({ updateInitialProps }, network) => (controls, { values }) => (
<>
<OpenPaper controls={controls} padding={false}>
<OwnerListPage network={network} updateInitialProps={updateInitialProps} values={values} />
</OpenPaper>
</>
)
export default OwnerList

View File

@ -6,11 +6,12 @@ import { FIELD_LOAD_ADDRESS, FIELD_LOAD_NAME } from '../components/fields'
import Page from 'src/components/layout/Page'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
import { saveSafes, loadStoredSafes } from 'src/logic/safe/utils'
import { SAFES_KEY, saveSafes } from 'src/logic/safe/utils'
import { getNamesFrom, getOwnersFrom } from 'src/routes/open/utils/safeDataExtractor'
import { SAFELIST_ADDRESS } from 'src/routes/routes'
import { buildSafe } from 'src/logic/safe/store/actions/fetchSafe'
import { history } from 'src/store'
import { loadFromStorage } from 'src/utils/storage'
import { SafeOwner, SafeRecordProps } from 'src/logic/safe/store/models/safe'
import { List } from 'immutable'
import { checksumAddress } from 'src/utils/checksumAddress'
@ -26,7 +27,7 @@ export const loadSafe = async (
const safeProps = await buildSafe(safeAddress, safeName)
safeProps.owners = owners
const storedSafes = (await loadStoredSafes()) || {}
const storedSafes = (await loadFromStorage(SAFES_KEY)) || {}
storedSafes[safeAddress] = safeProps

View File

@ -138,8 +138,6 @@ const SafeOwners = (props) => {
fieldMutator={(val) => {
form.mutators.setValue(addressName, val)
}}
// eslint-disable-next-line
// @ts-ignore
inputAdornment={
noErrorsOn(addressName, errors) && {
endAdornment: (
@ -219,21 +217,18 @@ const SafeOwners = (props) => {
const SafeOwnersForm = withStyles(styles as any)(withRouter(SafeOwners))
const SafeOwnersPage = ({ updateInitialProps }) =>
function OpenSafeOwnersPage(controls, { errors, form, values }) {
return (
<>
<OpenPaper controls={controls} padding={false}>
<SafeOwnersForm
errors={errors}
form={form}
otherAccounts={getAccountsFrom(values)}
updateInitialProps={updateInitialProps}
values={values}
/>
</OpenPaper>
</>
)
}
const SafeOwnersPage = ({ updateInitialProps }) => (controls, { errors, form, values }) => (
<>
<OpenPaper controls={controls} padding={false}>
<SafeOwnersForm
errors={errors}
form={form}
otherAccounts={getAccountsFrom(values)}
updateInitialProps={updateInitialProps}
values={values}
/>
</OpenPaper>
</>
)
export default SafeOwnersPage

View File

@ -161,7 +161,7 @@ const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.Reac
pathname: `${SAFELIST_ADDRESS}/${safeProps.address}/balances`,
state: {
name,
tx: pendingCreation?.txHash,
tx: pendingCreation.txHash,
},
}
@ -177,7 +177,7 @@ const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.Reac
const onRetry = async () => {
const values = await loadFromStorage<{ txHash: string }>(SAFE_PENDING_CREATION_STORAGE_KEY)
delete values?.txHash
delete values.txHash
await saveToStorage(SAFE_PENDING_CREATION_STORAGE_KEY, values)
setSafeCreationPendingInfo(values)
createSafeProxy()

View File

@ -105,7 +105,7 @@ const BackButton = styled(Button)`
const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, provider, submittedPromise }: any) => {
const [loading, setLoading] = useState(true)
const [stepIndex, setStepIndex] = useState(0)
const [safeCreationTxHash, setSafeCreationTxHash] = useState('')
const [safeCreationTxHash, setSafeCreationTxHash] = useState()
const [createdSafeAddress, setCreatedSafeAddress] = useState()
const [error, setError] = useState(false)
@ -242,7 +242,7 @@ const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, provider
useEffect(() => {
let interval
const awaitUntilSafeIsDeployed = async (safeCreationTxHash: string) => {
const awaitUntilSafeIsDeployed = async () => {
try {
const web3 = getWeb3()
const receipt = await web3.eth.getTransactionReceipt(safeCreationTxHash)
@ -283,9 +283,7 @@ const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, provider
return
}
if (typeof safeCreationTxHash === 'string') {
awaitUntilSafeIsDeployed(safeCreationTxHash)
}
awaitUntilSafeIsDeployed()
return () => {
clearInterval(interval)
@ -296,7 +294,7 @@ const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, provider
return <Loader size="sm" />
}
let FooterComponent
let FooterComponent = null
if (error) {
FooterComponent = ErrorFooter
} else if (steps[stepIndex].footerComponent) {

View File

@ -50,7 +50,7 @@ const AddressBookTable = ({ classes }) => {
const safesList = useSelector(safesListSelector)
const entryAddressToEditOrCreateNew = useSelector(addressBookQueryParamsSelector)
const addressBook = useSelector(getAddressBook)
const [selectedEntry, setSelectedEntry] = useState<any>(null)
const [selectedEntry, setSelectedEntry] = useState(null)
const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false)
const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false)
const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false)
@ -70,7 +70,7 @@ const AddressBookTable = ({ classes }) => {
if (entryAddressToEditOrCreateNew) {
const checksumEntryAdd = checksumAddress(entryAddressToEditOrCreateNew)
const key = addressBook.findKey((entry) => entry.address === checksumEntryAdd)
if (key && key >= 0) {
if (key >= 0) {
// Edit old entry
const value = addressBook.get(key)
setSelectedEntry({ entry: value, index: key })

View File

@ -38,7 +38,7 @@ const Transactions = (): React.ReactElement => {
{transactionsByPage.map((tx: Transaction, index) => {
let txHash = ''
if ('transactionHash' in tx) {
txHash = tx.transactionHash as string
txHash = tx.transactionHash
}
if ('txHash' in tx) {
txHash = tx.txHash

View File

@ -14,7 +14,7 @@ const AppAgreement = (): React.ReactElement => {
const { visited } = useFormState({ subscription: { visited: true } })
// trick to prevent having the field validated by default. Not sure why this happens in this form
const validate = !visited?.agreementAccepted ? undefined : required
const validate = !visited.agreementAccepted ? undefined : required
return (
<Field

View File

@ -28,7 +28,7 @@ export const appUrlResolver = createDecorator({
},
})
export const AppInfoUpdater = ({ onAppInfo }: { onAppInfo: (appInfo: SafeApp) => void }): null => {
export const AppInfoUpdater = ({ onAppInfo }: { onAppInfo: (appInfo: SafeApp) => void }): React.ReactElement => {
const {
input: { value: appUrl },
} = useField('appUrl', { subscription: { value: true } })
@ -52,7 +52,7 @@ const AppUrl = ({ appList }: { appList: SafeApp[] }): React.ReactElement => {
const { visited } = useFormState({ subscription: { visited: true } })
// trick to prevent having the field validated by default. Not sure why this happens in this form
const validate = !visited?.appUrl ? undefined : composeValidators(required, validateUrl, uniqueApp(appList))
const validate = !visited.appUrl ? undefined : composeValidators(required, validateUrl, uniqueApp(appList))
return (
<Field label="App URL" name="appUrl" placeholder="App URL" type="text" component={TextField} validate={validate} />

View File

@ -9,14 +9,14 @@ interface SubmitButtonStatusProps {
onSubmitButtonStatusChange: (disabled: boolean) => void
}
const SubmitButtonStatus = ({ appInfo, onSubmitButtonStatusChange }: SubmitButtonStatusProps): null => {
const SubmitButtonStatus = ({ appInfo, onSubmitButtonStatusChange }: SubmitButtonStatusProps): React.ReactElement => {
const { valid, validating, visited } = useFormState({
subscription: { valid: true, validating: true, visited: true },
})
React.useEffect(() => {
// if non visited, fields were not evaluated yet. Then, the default value is considered invalid
const fieldsVisited = visited?.agreementAccepted && visited.appUrl
const fieldsVisited = visited.agreementAccepted && visited.appUrl
onSubmitButtonStatusChange(validating || !valid || !fieldsVisited || !isAppManifestValid(appInfo))
}, [validating, valid, visited, onSubmitButtonStatusChange, appInfo])

View File

@ -40,7 +40,7 @@ const INITIAL_VALUES: AddAppFormValues = {
}
const APP_INFO: SafeApp = {
id: '',
id: undefined,
url: '',
name: '',
iconUrl: appsIconSvg,

View File

@ -54,7 +54,7 @@ const AppFrame = forwardRef<HTMLIFrameElement, AppFrameProps>(function AppFrameC
const redirectToBalance = () => history.push(`${SAFELIST_ADDRESS}/${safeAddress}/balances`)
if (!selectedApp) {
return <div />
return null
}
if (!consentReceived) {

View File

@ -31,7 +31,7 @@ const isTxValid = (t: Transaction): boolean => {
}
const isAddressValid = mustBeEthereumAddress(t.to) === undefined
return isAddressValid && !!t.data && typeof t.data === 'string'
return isAddressValid && t.data && typeof t.data === 'string'
}
const Wrapper = styled.div`
@ -84,7 +84,7 @@ const ConfirmTransactionModal = ({
onCancel,
onUserConfirm,
onClose,
}: OwnProps): React.ReactElement | null => {
}: OwnProps): React.ReactElement => {
const dispatch = useDispatch()
if (!isOpen) {
return null

View File

@ -14,8 +14,6 @@ type Props = {
onAppRemoved: (appId: string) => void
}
type AppListItem = SafeApp & { checked: boolean }
const ManageApps = ({ appList, onAppAdded, onAppToggle, onAppRemoved }: Props): React.ReactElement => {
const [isOpen, setIsOpen] = useState(false)
const [isSubmitDisabled, setIsSubmitDisabled] = useState(true)
@ -30,7 +28,7 @@ const ManageApps = ({ appList, onAppAdded, onAppToggle, onAppRemoved }: Props):
const closeModal = () => setIsOpen(false)
const getItemList = (): AppListItem[] =>
const getItemList = () =>
appList.map((a) => {
return { ...a, checked: !a.disabled }
})

View File

@ -42,7 +42,7 @@ const useAppList = (): UseAppListReturnType => {
}
})
let apps: SafeApp[] = []
let apps = []
// using the appURL to recover app info
for (let index = 0; index < list.length; index++) {
try {

View File

@ -44,7 +44,7 @@ const useIframeMessageHandler = (
selectedApp: SafeApp | undefined,
openConfirmationModal: (txs: Transaction[], requestId: RequestId) => void,
closeModal: () => void,
iframeRef: MutableRefObject<HTMLIFrameElement | null>,
iframeRef: MutableRefObject<HTMLIFrameElement>,
): ReturnType => {
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
const safeName = useSelector(safeNameSelector)
@ -60,8 +60,8 @@ const useIframeMessageHandler = (
requestId: requestId || Math.trunc(window.performance.now()),
}
if (iframeRef && selectedApp) {
iframeRef.current?.contentWindow?.postMessage(requestWithMessage, selectedApp.url)
if (iframeRef?.current && selectedApp) {
iframeRef.current.contentWindow.postMessage(requestWithMessage, selectedApp.url)
}
},
[iframeRef, selectedApp],
@ -77,9 +77,7 @@ const useIframeMessageHandler = (
switch (msg.data.messageId) {
case SDK_MESSAGES.SEND_TRANSACTIONS: {
if (msg.data.data) {
openConfirmationModal(msg.data.data, requestId)
}
openConfirmationModal(msg.data.data, requestId)
break
}
@ -87,9 +85,9 @@ const useIframeMessageHandler = (
const message = {
messageId: INTERFACE_MESSAGES.ON_SAFE_INFO,
data: {
safeAddress: safeAddress as string,
network,
ethBalance: ethBalance as string,
safeAddress,
network: network,
ethBalance,
},
}
@ -106,7 +104,7 @@ const useIframeMessageHandler = (
if (message.origin === window.origin) {
return
}
if (!selectedApp?.url.includes(message.origin)) {
if (!selectedApp.url.includes(message.origin)) {
console.error(`ThirdPartyApp: A message was received from an unknown origin ${message.origin}`)
return
}

View File

@ -7,7 +7,6 @@ import styled, { css } from 'styled-components'
import ManageApps from './components/ManageApps'
import AppFrame from './components/AppFrame'
import { useAppList } from './hooks/useAppList'
import { SafeApp } from './types.d'
import LCL from 'src/components/ListContentLayout'
import { networkSelector } from 'src/logic/wallets/store/selectors'
@ -64,7 +63,7 @@ const Apps = (): React.ReactElement => {
const [confirmTransactionModal, setConfirmTransactionModal] = useState<ConfirmTransactionModalState>(
INITIAL_CONFIRM_TX_MODAL_STATE,
)
const iframeRef = useRef<HTMLIFrameElement>(null)
const iframeRef = useRef<HTMLIFrameElement>()
const { trackEvent } = useAnalytics()
const granted = useSelector(grantedSelector)
@ -147,14 +146,14 @@ const Apps = (): React.ReactElement => {
sendMessageToIframe({
messageId: INTERFACE_MESSAGES.ON_SAFE_INFO,
data: {
safeAddress: safeAddress as string,
safeAddress,
network,
ethBalance: ethBalance as string,
ethBalance,
},
})
}, [ethBalance, network, safeAddress, selectedApp, sendMessageToIframe])
if (loadingAppList || !appList.length || !safeAddress) {
if (loadingAppList || !appList.length) {
return (
<LoadingContainer>
<Loader size="md" />
@ -200,10 +199,10 @@ const Apps = (): React.ReactElement => {
</CenteredMT>
<ConfirmTransactionModal
isOpen={confirmTransactionModal.isOpen}
app={selectedApp as SafeApp}
app={selectedApp}
safeAddress={safeAddress}
ethBalance={ethBalance as string}
safeName={safeName as string}
ethBalance={ethBalance}
safeName={safeName}
txs={confirmTransactionModal.txs}
onCancel={closeConfirmationModal}
onClose={closeConfirmationModal}

View File

@ -1,5 +1,5 @@
export type SafeApp = {
id: string
id: string | undefined
url: string
name: string
iconUrl: string

View File

@ -1,7 +1,7 @@
import axios from 'axios'
import memoize from 'lodash.memoize'
import { SafeApp } from './types.d'
import { SafeApp } from './types'
import { getGnosisSafeAppsUrl } from 'src/config/index'
import { getContentFromENS } from 'src/logic/wallets/getWeb3'
@ -62,8 +62,8 @@ export const isAppManifestValid = (appInfo: SafeApp): boolean =>
!appInfo.error
export const getAppInfoFromUrl = memoize(
async (appUrl: string): Promise<SafeApp> => {
let res = { id: '', url: appUrl, name: 'unknown', iconUrl: appsIconSvg, error: true, description: '' }
async (appUrl?: string): Promise<SafeApp> => {
let res = { id: undefined, url: appUrl, name: 'unknown', iconUrl: appsIconSvg, error: true, description: '' }
if (!appUrl?.length) {
return res

View File

@ -79,7 +79,7 @@ const Collectibles = (): React.ReactElement => {
const classes = useStyles()
const [selectedToken, setSelectedToken] = React.useState({})
const [sendNFTsModalOpen, setSendNFTsModalOpen] = React.useState(false)
const { address, ethBalance, name } = useSelector(safeSelector) || {}
const { address, ethBalance, name } = useSelector(safeSelector)
const nftTokens = useSelector(nftTokensSelector)
const activeAssetsList = useSelector(activeNftAssetsListSelector)
const { trackEvent } = useAnalytics()

View File

@ -5,7 +5,7 @@ import AddressInfo from 'src/components/AddressInfo'
import { safeSelector } from 'src/logic/safe/store/selectors'
const SafeInfo = () => {
const { address: safeAddress = '', ethBalance, name: safeName } = useSelector(safeSelector) || {}
const { address: safeAddress = '', ethBalance, name: safeName } = useSelector(safeSelector)
return <AddressInfo ethBalance={ethBalance} safeAddress={safeAddress} safeName={safeName} />
}

View File

@ -22,9 +22,9 @@ export interface AddressBookProps {
pristine: boolean
recipientAddress?: string
setSelectedEntry: (
entry: { address?: string; name?: string } | React.SetStateAction<{ address: string; name: string }> | null,
entry: { address?: string; name?: string } | React.SetStateAction<{ address: string; name: string }>,
) => void
setIsValidAddress: (valid: boolean) => void
setIsValidAddress: (valid?: boolean) => void
}
const useStyles = makeStyles(styles)
@ -157,7 +157,7 @@ const AddressBookInput = ({
optionsArray.filter((item) => {
const inputLowerCase = inputValue.toLowerCase()
const foundName = item.name.toLowerCase().includes(inputLowerCase)
const foundAddress = item.address?.toLowerCase().includes(inputLowerCase)
const foundAddress = item.address.toLowerCase().includes(inputLowerCase)
return foundName || foundAddress
})
}
@ -212,11 +212,6 @@ const AddressBookInput = ({
)}
renderOption={(adbkEntry) => {
const { address, name } = adbkEntry
if (!address) {
return
}
return (
<div className={classes.itemOptionList}>
<div className={classes.identicon}>

View File

@ -62,8 +62,8 @@ const useStyles = makeStyles({
const ChooseTxType = ({ onClose, recipientAddress, setActiveScreen }) => {
const classes = useStyles()
const { featuresEnabled } = useSelector(safeSelector) || {}
const erc721Enabled = featuresEnabled?.includes('ERC721')
const { featuresEnabled } = useSelector(safeSelector)
const erc721Enabled = featuresEnabled.includes('ERC721')
const [disableContractInteraction, setDisableContractInteraction] = React.useState(!!recipientAddress)
React.useEffect(() => {

View File

@ -11,7 +11,6 @@ import {
mustBeEthereumAddress,
mustBeEthereumContractAddress,
required,
Validator,
} from 'src/components/forms/validator'
import Col from 'src/components/layout/Col'
import Row from 'src/components/layout/Row'
@ -35,12 +34,8 @@ const EthAddressInput = ({
text,
}: EthAddressInputProps): React.ReactElement => {
const classes = useStyles()
const validatorsList = [
isRequired && required,
mustBeEthereumAddress,
isContract && mustBeEthereumContractAddress,
] as Validator[]
const validate = composeValidators(...validatorsList.filter((validator) => validator))
const validatorsList = [isRequired && required, mustBeEthereumAddress, isContract && mustBeEthereumContractAddress]
const validate = composeValidators(...validatorsList.filter((_) => _))
const { pristine } = useFormState({ subscription: { pristine: true } })
const {
input: { value },

View File

@ -20,18 +20,14 @@ const useStyles = makeStyles(styles)
interface EthValueProps {
onSetMax: (ethBalance: string) => void
}
const EthValue = ({ onSetMax }: EthValueProps): React.ReactElement | null => {
const EthValue = ({ onSetMax }: EthValueProps) => {
const classes = useStyles()
const { ethBalance } = useSelector(safeSelector) || {}
const { ethBalance } = useSelector(safeSelector)
const {
input: { value: method },
} = useField('selectedMethod', { subscription: { value: true } })
const disabled = !isPayable(method)
if (!ethBalance) {
return null
}
return disabled ? null : (
<>
<Row className={classes.fullWidth} margin="xs">

View File

@ -16,7 +16,7 @@ import { NO_CONTRACT } from 'src/routes/safe/components/Balances/SendModal/scree
import CheckIcon from 'src/routes/safe/components/CurrencyDropdown/img/check.svg'
import { useDropdownStyles } from 'src/routes/safe/components/CurrencyDropdown/style'
import { DropdownListTheme } from 'src/theme/mui'
import { extractUsefulMethods, AbiItemExtended } from 'src/logic/contractInteraction/sources/ABIService'
import { extractUsefulMethods } from 'src/logic/contractInteraction/sources/ABIService'
const MENU_WIDTH = '452px'
@ -24,7 +24,7 @@ interface MethodsDropdownProps {
onChange: (method: AbiItem) => void
}
const MethodsDropdown = ({ onChange }: MethodsDropdownProps): React.ReactElement | null => {
const MethodsDropdown = ({ onChange }: MethodsDropdownProps) => {
const classes = useDropdownStyles({ buttonWidth: MENU_WIDTH })
const {
input: { value: abi },
@ -34,8 +34,8 @@ const MethodsDropdown = ({ onChange }: MethodsDropdownProps): React.ReactElement
initialValues: { selectedMethod: selectedMethodByDefault },
} = useFormState({ subscription: { initialValues: true } })
const [selectedMethod, setSelectedMethod] = React.useState(selectedMethodByDefault ? selectedMethodByDefault : {})
const [methodsList, setMethodsList] = React.useState<AbiItemExtended[]>([])
const [methodsListFiltered, setMethodsListFiltered] = React.useState<AbiItemExtended[]>([])
const [methodsList, setMethodsList] = React.useState([])
const [methodsListFiltered, setMethodsListFiltered] = React.useState([])
const [anchorEl, setAnchorEl] = React.useState(null)
const [searchParams, setSearchParams] = React.useState('')
@ -50,7 +50,7 @@ const MethodsDropdown = ({ onChange }: MethodsDropdownProps): React.ReactElement
}, [abi])
React.useEffect(() => {
setMethodsListFiltered(methodsList.filter(({ name }) => name?.toLowerCase().includes(searchParams.toLowerCase())))
setMethodsListFiltered(methodsList.filter(({ name }) => name.toLowerCase().includes(searchParams.toLowerCase())))
}, [methodsList, searchParams])
const handleClick = (event) => {

View File

@ -15,7 +15,7 @@ type Props = {
placeholder: string
}
const InputComponent = ({ type, keyValue, placeholder }: Props): React.ReactElement | null => {
const InputComponent = ({ type, keyValue, placeholder }: Props): React.ReactElement => {
if (!type) {
return null
}

View File

@ -7,18 +7,18 @@ import InputComponent from './InputComponent'
import { generateFormFieldKey } from '../utils'
import { AbiItemExtended } from 'src/logic/contractInteraction/sources/ABIService'
const RenderInputParams = (): React.ReactElement | null => {
const RenderInputParams = (): React.ReactElement => {
const {
meta: { valid: validABI },
} = useField('abi', { subscription: { valid: true, value: true } })
const {
input: { value: method },
}: { input: { value: AbiItemExtended } } = useField('selectedMethod', { subscription: { value: true } })
const renderInputs = validABI && !!method && method.inputs?.length
const renderInputs = validABI && !!method && method.inputs.length
return !renderInputs ? null : (
<>
{method.inputs?.map(({ name, type }, index) => {
{method.inputs.map(({ name, type }, index) => {
const placeholder = name ? `${name} (${type})` : type
const key = generateFormFieldKey(type, method.signatureHash, index)

View File

@ -40,11 +40,11 @@ type Props = {
tx: TransactionReviewType
}
const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
const ContractInteractionReview = ({ onClose, onPrev, tx }: Props) => {
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
const classes = useStyles()
const dispatch = useDispatch()
const { address: safeAddress } = useSelector(safeSelector) || {}
const { address: safeAddress } = useSelector(safeSelector)
const [gasCosts, setGasCosts] = useState('< 0.001')
useEffect(() => {
@ -54,7 +54,7 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
const { fromWei, toBN } = getWeb3().utils
const txData = tx.data ? tx.data.trim() : ''
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, tx.contractAddress as string, txData)
const estimatedGasCosts = await estimateTxGasCosts(safeAddress, tx.contractAddress, txData)
const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether')
const formattedGasCosts = formatAmount(gasCostsAsEth)
@ -102,7 +102,7 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
</Paragraph>
</Row>
<Row align="center" margin="md">
<AddressInfo safeAddress={tx.contractAddress as string} />
<AddressInfo safeAddress={tx.contractAddress} />
</Row>
<Row margin="xs">
<Paragraph color="disabled" noMargin size="md" style={{ letterSpacing: '-0.5px' }}>
@ -129,11 +129,11 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
</Row>
<Row align="center" margin="md">
<Paragraph className={classes.value} size="md" style={{ margin: 0 }}>
{tx.selectedMethod?.name}
{tx.selectedMethod.name}
</Paragraph>
</Row>
{tx.selectedMethod?.inputs?.map(({ name, type }, index) => {
const key = generateFormFieldKey(type, tx.selectedMethod?.signatureHash || '', index)
{tx.selectedMethod.inputs.map(({ name, type }, index) => {
const key = generateFormFieldKey(type, tx.selectedMethod.signatureHash, index)
const value: string = getValueFromTxInputs(key, type, tx)
return (

View File

@ -1,6 +1,7 @@
import IconButton from '@material-ui/core/IconButton'
import { makeStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close'
import { useSnackbar } from 'notistack'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
@ -38,9 +39,10 @@ type Props = {
const useStyles = makeStyles(styles)
const ReviewCustomTx = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
const classes = useStyles()
const dispatch = useDispatch()
const { address: safeAddress } = useSelector(safeSelector) || {}
const { address: safeAddress } = useSelector(safeSelector)
const [gasCosts, setGasCosts] = useState<string>('< 0.001')
useEffect(() => {
@ -50,7 +52,7 @@ const ReviewCustomTx = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
const { fromWei, toBN } = getWeb3().utils
const txData = tx.data ? tx.data.trim() : ''
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, tx.contractAddress as string, txData)
const estimatedGasCosts = await estimateTxGasCosts(safeAddress, tx.contractAddress, txData)
const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether')
const formattedGasCosts = formatAmount(gasCostsAsEth)
@ -74,12 +76,14 @@ const ReviewCustomTx = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
dispatch(
createTransaction({
safeAddress: safeAddress as string,
to: txRecipient as string,
safeAddress,
to: txRecipient,
valueInWei: txValue,
txData,
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
}),
enqueueSnackbar,
closeSnackbar,
} as any),
)
onClose()
@ -114,15 +118,15 @@ const ReviewCustomTx = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
</Row>
<Row align="center" margin="md">
<Col xs={1}>
<Identicon address={tx.contractAddress as string} diameter={32} />
<Identicon address={tx.contractAddress} diameter={32} />
</Col>
<Col layout="column" xs={11}>
<Block justify="left">
<Paragraph noMargin weight="bolder">
{tx.contractAddress}
</Paragraph>
<CopyBtn content={tx.contractAddress as string} />
<EtherscanBtn type="address" value={tx.contractAddress as string} />
<CopyBtn content={tx.contractAddress} />
<EtherscanBtn type="address" value={tx.contractAddress} />
</Block>
</Col>
</Row>

View File

@ -52,7 +52,7 @@ const useStyles = makeStyles(styles)
const SendCustomTx: React.FC<Props> = ({ initialValues, onClose, onNext, contractAddress, switchMethod, isABI }) => {
const classes = useStyles()
const { ethBalance } = useSelector(safeSelector) || {}
const { ethBalance } = useSelector(safeSelector)
const [qrModalOpen, setQrModalOpen] = useState<boolean>(false)
const [selectedEntry, setSelectedEntry] = useState<{ address?: string; name?: string } | null>({
address: contractAddress || initialValues.contractAddress,
@ -230,7 +230,7 @@ const SendCustomTx: React.FC<Props> = ({ initialValues, onClose, onNext, contrac
placeholder="Value*"
text="Value*"
type="text"
validate={composeValidators(mustBeFloat, maxValue(ethBalance || '0'), minValue(0))}
validate={composeValidators(mustBeFloat, maxValue(ethBalance), minValue(0))}
/>
</Col>
</Row>

View File

@ -49,7 +49,7 @@ const ContractInteraction: React.FC<ContractInteractionProps> = ({
isABI,
}) => {
const classes = useStyles()
const { address: safeAddress = '' } = useSelector(safeSelector) || {}
const { address: safeAddress = '' } = useSelector(safeSelector)
let setCallResults
React.useMemo(() => {

View File

@ -59,7 +59,7 @@ export const formMutators: Record<string, Mutator<{ selectedMethod: { name: stri
},
setSelectedMethod: (args, state, utils) => {
const modified =
state.lastFormState?.values.selectedMethod && state.lastFormState.values.selectedMethod.name !== args[0].name
state.lastFormState.values.selectedMethod && state.lastFormState.values.selectedMethod.name !== args[0].name
if (modified) {
utils.changeValue(state, 'callResults', () => '')
@ -115,8 +115,8 @@ export const createTxObject = (
): ContractSendMethod => {
const web3 = getWeb3()
const contract: any = new web3.eth.Contract([method], contractAddress)
const { inputs, name = '', signatureHash } = method
const args = inputs?.map(extractMethodArgs(signatureHash, values)) || []
const { inputs, name, signatureHash } = method
const args = inputs.map(extractMethodArgs(signatureHash, values))
return contract.methods[name](...args)
}

View File

@ -43,7 +43,7 @@ const ReviewCollectible = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx
const classes = useStyles()
const shortener = textShortener()
const dispatch = useDispatch()
const { address: safeAddress } = useSelector(safeSelector) || {}
const { address: safeAddress } = useSelector(safeSelector)
const nftTokens = useSelector(nftTokensSelector)
const [gasCosts, setGasCosts] = useState('< 0.001')
const txToken = nftTokens.find(
@ -66,7 +66,7 @@ const ReviewCollectible = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx
const tokenInstance = await ERC721Token.at(tx.assetAddress)
const txData = tokenInstance.contract.methods[methodToCall](...params).encodeABI()
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, tx.recipientAddress, txData)
const estimatedGasCosts = await estimateTxGasCosts(safeAddress, tx.recipientAddress, txData)
const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether')
const formattedGasCosts = formatAmount(gasCostsAsEth)
@ -148,7 +148,7 @@ const ReviewCollectible = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx
<Row align="center" margin="md">
<Img alt={txToken.name} height={28} onError={setImageToPlaceholder} src={txToken.image} />
<Paragraph className={classes.amount} noMargin size="md">
{shortener(txToken.name)} (Token ID: {shortener(txToken.tokenId as string)})
{shortener(txToken.name)} (Token ID: {shortener(txToken.tokenId)})
</Paragraph>
</Row>
)}

View File

@ -3,7 +3,7 @@ import { makeStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close'
import { BigNumber } from 'bignumber.js'
import { withSnackbar } from 'notistack'
import React, { useEffect, useState, useMemo } from 'react'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import ArrowDown from '../assets/arrow-down.svg'
@ -39,14 +39,14 @@ const useStyles = makeStyles(styles as any)
const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
const classes = useStyles()
const dispatch = useDispatch()
const { address: safeAddress } = useSelector(safeSelector) || {}
const { address: safeAddress } = useSelector(safeSelector)
const tokens = useSelector(extendedSafeTokensSelector)
const [gasCosts, setGasCosts] = useState('< 0.001')
const [data, setData] = useState('')
const txToken = useMemo(() => tokens.find((token) => token.address === tx.token), [tokens, tx.token])
const isSendingETH = txToken?.address === ETH_ADDRESS
const txRecipient = isSendingETH ? tx.recipientAddress : txToken?.address
const txToken = tokens.find((token) => token.address === tx.token)
const isSendingETH = txToken.address === ETH_ADDRESS
const txRecipient = isSendingETH ? tx.recipientAddress : txToken.address
useEffect(() => {
let isCurrent = true
@ -54,22 +54,18 @@ const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
const estimateGas = async () => {
const { fromWei, toBN } = getWeb3().utils
if (!txToken) {
return
}
let txData = EMPTY_DATA
if (!isSendingETH) {
const StandardToken = await getHumanFriendlyToken()
const tokenInstance = await StandardToken.at(txToken.address as string)
const tokenInstance = await StandardToken.at(txToken.address)
const decimals = await tokenInstance.decimals()
const txAmount = new BigNumber(tx.amount).times(10 ** decimals.toNumber()).toString()
txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, txAmount).encodeABI()
}
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, txRecipient, txData)
const estimatedGasCosts = await estimateTxGasCosts(safeAddress, txRecipient, txData)
const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether')
const formattedGasCosts = formatAmount(gasCostsAsEth)
@ -84,7 +80,7 @@ const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
return () => {
isCurrent = false
}
}, [isSendingETH, safeAddress, tx.amount, tx.recipientAddress, txRecipient, txToken])
}, [isSendingETH, safeAddress, tx.amount, tx.recipientAddress, txRecipient, txToken.address])
const submitTx = async () => {
const web3 = getWeb3()
@ -159,14 +155,9 @@ const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
</Paragraph>
</Row>
<Row align="center" margin="md">
<Img alt={txToken?.name as string} height={28} onError={setImageToPlaceholder} src={txToken?.logoUri} />
<Paragraph
className={classes.amount}
noMargin
size="md"
data-testid={`amount-${txToken?.symbol as string}-review-step`}
>
{tx.amount} {txToken?.symbol}
<Img alt={txToken.name} height={28} onError={setImageToPlaceholder} src={txToken.logoUri} />
<Paragraph className={classes.amount} noMargin size="md" data-testid={`amount-${txToken.symbol}-review-step`}>
{tx.amount} {txToken.symbol}
</Paragraph>
</Row>
<Row>

View File

@ -53,7 +53,7 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel
name: '',
})
const [pristine, setPristine] = useState(true)
const [isValidAddress, setIsValidAddress] = useState(false)
const [isValidAddress, setIsValidAddress] = useState(true)
React.useMemo(() => {
if (selectedEntry === null && pristine) {
@ -129,7 +129,7 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel
<div
onKeyDown={(e) => {
if (e.keyCode !== 9) {
setSelectedEntry({ address: '', name: 'string' })
setSelectedEntry(null)
}
}}
role="listbox"
@ -150,7 +150,7 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel
<Paragraph
className={classes.selectAddress}
noMargin
onClick={() => setSelectedEntry({ address: '', name: 'string' })}
onClick={() => setSelectedEntry(null)}
weight="bolder"
>
{selectedEntry.name}
@ -158,7 +158,7 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel
<Paragraph
className={classes.selectAddress}
noMargin
onClick={() => setSelectedEntry({ address: '', name: 'string' })}
onClick={() => setSelectedEntry(null)}
weight="bolder"
>
{selectedEntry.address}

View File

@ -58,7 +58,7 @@ const SendFunds = ({ initialValues, onClose, onNext, recipientAddress, selectedT
})
const [pristine, setPristine] = useState(true)
const [isValidAddress, setIsValidAddress] = useState(false)
const [isValidAddress, setIsValidAddress] = useState(true)
React.useMemo(() => {
if (selectedEntry === null && pristine) {
@ -130,7 +130,7 @@ const SendFunds = ({ initialValues, onClose, onNext, recipientAddress, selectedT
<div
onKeyDown={(e) => {
if (e.keyCode !== 9) {
setSelectedEntry({ address: '', name: 'string' })
setSelectedEntry(null)
}
}}
role="listbox"
@ -151,7 +151,7 @@ const SendFunds = ({ initialValues, onClose, onNext, recipientAddress, selectedT
<Paragraph
className={classes.selectAddress}
noMargin
onClick={() => setSelectedEntry({ address: '', name: 'string' })}
onClick={() => setSelectedEntry(null)}
weight="bolder"
>
{selectedEntry.name}
@ -159,7 +159,7 @@ const SendFunds = ({ initialValues, onClose, onNext, recipientAddress, selectedT
<Paragraph
className={classes.selectAddress}
noMargin
onClick={() => setSelectedEntry({ address: '', name: 'string' })}
onClick={() => setSelectedEntry(null)}
weight="bolder"
>
{selectedEntry.address}
@ -204,7 +204,7 @@ const SendFunds = ({ initialValues, onClose, onNext, recipientAddress, selectedT
Amount
</Paragraph>
<ButtonLink
onClick={() => mutators.setMax(selectedTokenRecord?.balance)}
onClick={() => mutators.setMax(selectedTokenRecord.balance)}
weight="bold"
testId="send-max-btn"
>
@ -230,7 +230,7 @@ const SendFunds = ({ initialValues, onClose, onNext, recipientAddress, selectedT
required,
mustBeFloat,
minValue(0, false),
maxValue(selectedTokenRecord?.balance || 0),
maxValue(selectedTokenRecord?.balance),
)}
/>
<OnChange name="token">

View File

@ -61,7 +61,7 @@ export const getBalanceData = (
symbol: token.symbol,
},
assetOrder: token.name,
[BALANCE_TABLE_BALANCE_ID]: `${formatAmountInUsFormat(token.balance?.toString() || '0')} ${token.symbol}`,
[BALANCE_TABLE_BALANCE_ID]: `${formatAmountInUsFormat(token.balance.toString())} ${token.symbol}`,
balanceOrder: Number(token.balance),
[FIXED]: token.symbol === 'ETH',
[BALANCE_TABLE_VALUE_ID]: getTokenPriceInCurrency(token, currencySelected, currencyValues, currencyRate),

View File

@ -2,7 +2,7 @@ import { makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import Receive from 'src/components/App/ReceiveModal'
import Receive from 'src/components/App/ModalReceive'
import Tokens from './Tokens'
import { styles } from './style'
@ -15,11 +15,7 @@ import Row from 'src/components/layout/Row'
import { SAFELIST_ADDRESS } from 'src/routes/routes'
import SendModal from 'src/routes/safe/components/Balances/SendModal'
import CurrencyDropdown from 'src/routes/safe/components/CurrencyDropdown'
import {
safeFeaturesEnabledSelector,
safeParamAddressFromStateSelector,
safeNameSelector,
} from 'src/logic/safe/store/selectors'
import { safeFeaturesEnabledSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { wrapInSuspense } from 'src/utils/wrapInSuspense'
import { useFetchTokens } from 'src/logic/safe/hooks/useFetchTokens'
@ -37,7 +33,7 @@ const INITIAL_STATE = {
showManageCollectibleModal: false,
sendFunds: {
isOpen: false,
selectedToken: '',
selectedToken: undefined,
},
showReceive: false,
}
@ -53,12 +49,11 @@ const Balances = (): React.ReactElement => {
const address = useSelector(safeParamAddressFromStateSelector)
const featuresEnabled = useSelector(safeFeaturesEnabledSelector)
const safeName = useSelector(safeNameSelector)
useFetchTokens(address as string)
useFetchTokens(address)
useEffect(() => {
const erc721Enabled = Boolean(featuresEnabled?.includes('ERC721'))
const erc721Enabled = featuresEnabled && featuresEnabled.includes('ERC721')
setState((prevState) => ({
...prevState,
@ -89,7 +84,7 @@ const Balances = (): React.ReactElement => {
...prevState,
sendFunds: {
isOpen: false,
selectedToken: '',
selectedToken: undefined,
},
}))
}
@ -229,7 +224,7 @@ const Balances = (): React.ReactElement => {
paperClassName={receiveModal}
title="Receive Tokens"
>
<Receive safeAddress={address as string} safeName={safeName as string} onClose={() => onHide('Receive')} />
<Receive onClose={() => onHide('Receive')} />
</Modal>
</>
)

View File

@ -22,7 +22,7 @@ import { setImageToPlaceholder } from '../Balances/utils'
import Img from 'src/components/layout/Img/index'
import etherIcon from 'src/assets/icons/icon_etherTokens.svg'
const CurrencyDropdown = (): React.ReactElement | null => {
const CurrencyDropdown = (): React.ReactElement => {
const currenciesList = Object.values(AVAILABLE_CURRENCIES)
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const dispatch = useDispatch()
@ -48,11 +48,7 @@ const CurrencyDropdown = (): React.ReactElement | null => {
handleClose()
}
if (!selectedCurrency) {
return null
}
return (
return !selectedCurrency ? null : (
<MuiThemeProvider theme={DropdownListTheme}>
<>
<button className={classes.button} onClick={handleClick} type="button">

View File

@ -50,7 +50,7 @@ const ModulesTable = ({ moduleData }: ModulesTableProps): React.ReactElement =>
const [viewRemoveModuleModal, setViewRemoveModuleModal] = React.useState(false)
const hideRemoveModuleModal = () => setViewRemoveModuleModal(false)
const [selectedModule, setSelectedModule] = React.useState<ModulePair>()
const [selectedModule, setSelectedModule] = React.useState(null)
const triggerRemoveSelectedModule = (module: ModulePair): void => {
setSelectedModule(module)
setViewRemoveModuleModal(true)
@ -67,7 +67,7 @@ const ModulesTable = ({ moduleData }: ModulesTableProps): React.ReactElement =>
disablePagination
label="Modules"
noBorder
size={moduleData?.length}
size={moduleData.length}
>
{(sortedData) =>
sortedData.map((row, index) => (
@ -117,9 +117,7 @@ const ModulesTable = ({ moduleData }: ModulesTableProps): React.ReactElement =>
}
</Table>
</TableContainer>
{viewRemoveModuleModal && selectedModule && (
<RemoveModuleModal onClose={hideRemoveModuleModal} selectedModule={selectedModule} />
)}
{viewRemoveModuleModal && <RemoveModuleModal onClose={hideRemoveModuleModal} selectedModule={selectedModule} />}
</>
)
}

View File

@ -46,7 +46,7 @@ interface RemoveModuleModal {
const RemoveModuleModal = ({ onClose, selectedModule }: RemoveModuleModal): React.ReactElement => {
const classes = useStyles()
const safeAddress = useSelector(safeParamAddressFromStateSelector) as string
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const dispatch = useDispatch()
const removeSelectedModule = async (): Promise<void> => {

View File

@ -42,7 +42,7 @@ const Advanced = (): React.ReactElement => {
const classes = useStyles()
const nonce = useSelector(safeNonceSelector)
const modules = useSelector(safeModulesSelector)
const moduleData = modules ? getModuleData(modules) ?? null : null
const moduleData = getModuleData(modules) ?? null
const { trackEvent } = useAnalytics()
useEffect(() => {

View File

@ -35,7 +35,7 @@ const OwnerForm = ({ classes, onClose, onSubmit }) => {
onSubmit(values)
}
const owners = useSelector(safeOwnersSelector)
const ownerDoesntExist = uniqueAddress(owners?.map((o) => o.address) || [])
const ownerDoesntExist = uniqueAddress(owners.map((o) => o.address))
return (
<>

Some files were not shown because too many files have changed in this diff Show More