Merge branch 'development' into darwin-arm64-support

This commit is contained in:
Daniel Sanchez 2021-04-13 09:38:37 +02:00 committed by GitHub
commit aadf6b92f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 84 additions and 99 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "safe-react", "name": "safe-react",
"version": "3.3.1", "version": "3.4.0",
"description": "Allowing crypto users manage funds in a safer way", "description": "Allowing crypto users manage funds in a safer way",
"website": "https://github.com/gnosis/safe-react#readme", "website": "https://github.com/gnosis/safe-react#readme",
"bugs": { "bugs": {

View File

@ -3,7 +3,7 @@ import { EnvironmentSettings, ETHEREUM_NETWORK, NetworkConfig } from 'src/config
const baseConfig: EnvironmentSettings = { const baseConfig: EnvironmentSettings = {
clientGatewayUrl: 'https://safe-client.rinkeby.staging.gnosisdev.com/v1', clientGatewayUrl: 'https://safe-client.rinkeby.staging.gnosisdev.com/v1',
txServiceUrl: 'https://safe-transaction.staging.gnosisdev.com/api/v1', txServiceUrl: 'https://safe-transaction.rinkeby.staging.gnosisdev.com/api/v1',
safeAppsUrl: 'https://safe-apps.dev.gnosisdev.com', safeAppsUrl: 'https://safe-apps.dev.gnosisdev.com',
gasPriceOracle: { gasPriceOracle: {
url: 'https://ethgasstation.info/json/ethgasAPI.json', url: 'https://ethgasstation.info/json/ethgasAPI.json',

View File

@ -36,8 +36,8 @@ const mainnet: NetworkConfig = {
isTestNet: true, isTestNet: true,
nativeCoin: { nativeCoin: {
address: '0x0000000000000000000000000000000000000000', address: '0x0000000000000000000000000000000000000000',
name: 'Energy web token', name: 'Volta Token',
symbol: 'EWT', symbol: 'VT',
decimals: 18, decimals: 18,
logoUri: EwcLogo, logoUri: EwcLogo,
}, },

View File

@ -1,11 +1,9 @@
import axios from 'axios' import axios from 'axios'
import { getSafeClientGatewayBaseUrl, getNetworkInfo } from 'src/config' import { getSafeClientGatewayBaseUrl } from 'src/config'
import { TokenProps } from 'src/logic/tokens/store/model/token' import { TokenProps } from 'src/logic/tokens/store/model/token'
import { checksumAddress } from 'src/utils/checksumAddress' import { checksumAddress } from 'src/utils/checksumAddress'
import { ZERO_ADDRESS, sameAddress } from 'src/logic/wallets/ethAddresses'
export type TokenBalance = { export type TokenBalance = {
tokenInfo: TokenProps tokenInfo: TokenProps
balance: string balance: string
@ -35,24 +33,5 @@ export const fetchTokenCurrenciesBalances = async ({
checksumAddress(safeAddress), checksumAddress(safeAddress),
)}/balances/${selectedCurrency}/?trusted=${trustedTokens}&exclude_spam=${excludeSpamTokens}` )}/balances/${selectedCurrency}/?trusted=${trustedTokens}&exclude_spam=${excludeSpamTokens}`
return axios.get(url).then(({ data }) => { return axios.get(url).then(({ data }) => data)
// Currently the client-gateway is not returning the balance using network token symbol and name
// FIXME remove this logic and return data directly once this is fixed
const { nativeCoin } = getNetworkInfo()
if (data.items && data.items.length) {
data.items = data.items.map((element) => {
const { tokenInfo } = element
if (sameAddress(ZERO_ADDRESS, tokenInfo.address)) {
// If it's native coin we swap symbol and name
tokenInfo.symbol = nativeCoin.symbol
tokenInfo.name = nativeCoin.name
}
return element
})
}
return data
})
} }

View File

@ -28,10 +28,10 @@ export const useLoadSafe = (safeAddress?: string, loadedViaUrl = true): boolean
} }
} }
} }
dispatch(loadAddressBookFromStorage())
dispatch(loadAddressBookFromStorage())
fetchData() fetchData()
}, [dispatch, safeAddress]) }, [dispatch, safeAddress, loadedViaUrl])
return isSafeLoaded return isSafeLoaded
} }

View File

@ -133,7 +133,7 @@ type BaseCustom = {
dataSize: string dataSize: string
value: string value: string
isCancellation: boolean isCancellation: boolean
toInfo: AddressInfo toInfo?: AddressInfo
} }
type Custom = BaseCustom & { type Custom = BaseCustom & {

View File

@ -17,7 +17,7 @@ const SIGNERS = {
const getSignersByWallet = (isHW) => const getSignersByWallet = (isHW) =>
isHW ? [SIGNERS.ETH_SIGN] : [SIGNERS.EIP712_V3, SIGNERS.EIP712_V4, SIGNERS.EIP712, SIGNERS.ETH_SIGN] isHW ? [SIGNERS.ETH_SIGN] : [SIGNERS.EIP712_V3, SIGNERS.EIP712_V4, SIGNERS.EIP712, SIGNERS.ETH_SIGN]
export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.0.0' export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.1.1'
export const tryOffchainSigning = async (safeTxHash: string, txArgs, isHW: boolean): Promise<string> => { export const tryOffchainSigning = async (safeTxHash: string, txArgs, isHW: boolean): Promise<string> => {
let signature let signature

View File

@ -255,7 +255,8 @@ const AddressBookTable = (): ReactElement => {
activeScreenType="chooseTxType" activeScreenType="chooseTxType"
isOpen={sendFundsModalOpen} isOpen={sendFundsModalOpen}
onClose={() => setSendFundsModalOpen(false)} onClose={() => setSendFundsModalOpen(false)}
recipientAddress={selectedEntry && selectedEntry.entry ? selectedEntry.entry.address : undefined} recipientAddress={selectedEntry?.entry?.address}
recipientName={selectedEntry?.entry?.name}
/> />
</> </>
) )

View File

@ -59,11 +59,12 @@ const StyledCard = styled(Card)`
padding: 0; padding: 0;
` `
const StyledIframe = styled.iframe` const StyledIframe = styled.iframe<{ isLoading: boolean }>`
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow: auto; overflow: auto;
box-sizing: border-box; box-sizing: border-box;
display: ${({ isLoading }) => (isLoading ? 'none' : 'block')};
` `
const Breadcrumb = styled.div` const Breadcrumb = styled.div`
@ -326,6 +327,7 @@ const AppFrame = ({ appUrl }: Props): React.ReactElement => {
)} )}
<StyledIframe <StyledIframe
isLoading={appIsLoading}
frameBorder="0" frameBorder="0"
id={`iframe-${appUrl}`} id={`iframe-${appUrl}`}
ref={iframeRef} ref={iframeRef}

View File

@ -34,12 +34,6 @@ export const staticAppsList: Array<StaticAppInfo> = [
disabled: false, disabled: false,
networks: [ETHEREUM_NETWORK.MAINNET], networks: [ETHEREUM_NETWORK.MAINNET],
}, },
// Aave v2
{
url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmVg7aXr5S8sT2iUdUwdkfTJNknmB7rcE3t92HiGoVsYDj`,
disabled: false,
networks: [ETHEREUM_NETWORK.MAINNET],
},
//Balancer Exchange //Balancer Exchange
{ {
url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmRb2VfPVYBrv6gi2zDywgVgTg3A19ZCRMqwL13Ez5f5AS`, url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmRb2VfPVYBrv6gi2zDywgVgTg3A19ZCRMqwL13Ez5f5AS`,

View File

@ -56,6 +56,7 @@ type Props = {
isOpen: boolean isOpen: boolean
onClose: () => void onClose: () => void
recipientAddress?: string recipientAddress?: string
recipientName?: string
selectedToken?: string | NFTToken | Erc721Transfer selectedToken?: string | NFTToken | Erc721Transfer
tokenAmount?: string tokenAmount?: string
} }
@ -65,6 +66,7 @@ const SendModal = ({
isOpen, isOpen,
onClose, onClose,
recipientAddress, recipientAddress,
recipientName,
selectedToken, selectedToken,
tokenAmount, tokenAmount,
}: Props): React.ReactElement => { }: Props): React.ReactElement => {
@ -119,7 +121,12 @@ const SendModal = ({
} }
> >
{activeScreen === 'chooseTxType' && ( {activeScreen === 'chooseTxType' && (
<ChooseTxType onClose={onClose} recipientAddress={recipientAddress} setActiveScreen={setActiveScreen} /> <ChooseTxType
onClose={onClose}
recipientName={recipientName}
recipientAddress={recipientAddress}
setActiveScreen={setActiveScreen}
/>
)} )}
{activeScreen === 'sendFunds' && ( {activeScreen === 'sendFunds' && (

View File

@ -14,20 +14,29 @@ import Row from 'src/components/layout/Row'
import { safeFeaturesEnabledSelector } from 'src/logic/safe/store/selectors' import { safeFeaturesEnabledSelector } from 'src/logic/safe/store/selectors'
import { useStyles } from 'src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style' import { useStyles } from 'src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style'
import ContractInteractionIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg' import ContractInteractionIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg'
import { EthHashInfo } from '@gnosis.pm/safe-react-components'
import Collectible from '../assets/collectibles.svg' import Collectible from '../assets/collectibles.svg'
import Token from '../assets/token.svg' import Token from '../assets/token.svg'
import { FEATURES } from 'src/config/networks/network.d' import { FEATURES } from 'src/config/networks/network.d'
import { getExplorerInfo } from 'src/config'
type ActiveScreen = 'sendFunds' | 'sendCollectible' | 'contractInteraction' type ActiveScreen = 'sendFunds' | 'sendCollectible' | 'contractInteraction'
interface ChooseTxTypeProps { interface ChooseTxTypeProps {
onClose: () => void onClose: () => void
recipientAddress?: string recipientAddress?: string
recipientName?: string
setActiveScreen: React.Dispatch<React.SetStateAction<ActiveScreen>> setActiveScreen: React.Dispatch<React.SetStateAction<ActiveScreen>>
} }
const ChooseTxType = ({ onClose, recipientAddress, setActiveScreen }: ChooseTxTypeProps): React.ReactElement => { const ChooseTxType = ({
onClose,
recipientAddress,
recipientName,
setActiveScreen,
}: ChooseTxTypeProps): React.ReactElement => {
const classes = useStyles() const classes = useStyles()
const featuresEnabled = useSelector(safeFeaturesEnabledSelector) const featuresEnabled = useSelector(safeFeaturesEnabledSelector)
const erc721Enabled = featuresEnabled?.includes(FEATURES.ERC721) const erc721Enabled = featuresEnabled?.includes(FEATURES.ERC721)
@ -61,11 +70,18 @@ const ChooseTxType = ({ onClose, recipientAddress, setActiveScreen }: ChooseTxTy
</Row> </Row>
<Hairline /> <Hairline />
{!!recipientAddress && ( {!!recipientAddress && (
<Row align="center"> <Row align="center" margin="md">
<Col className={classes.disclaimer} layout="column" middle="xs"> <Col className={classes.disclaimer} layout="column" middle="xs">
<Paragraph className={classes.disclaimerText} noMargin> <Paragraph className={classes.disclaimerText} noMargin>
Please select what you will send to {recipientAddress} Please select what you will send to
</Paragraph> </Paragraph>
<EthHashInfo
hash={recipientAddress}
name={recipientName}
showAvatar
showCopyBtn
explorerUrl={getExplorerInfo(recipientAddress)}
/>
</Col> </Col>
</Row> </Row>
)} )}

View File

@ -19,13 +19,14 @@ export const useStyles = makeStyles(
}, },
disclaimerText: { disclaimerText: {
fontSize: md, fontSize: md,
marginBottom: `${md}`,
}, },
closeIcon: { closeIcon: {
height: '35px', height: '35px',
width: '35px', width: '35px',
}, },
buttonColumn: { buttonColumn: {
padding: '52px 0', margin: '16px 0 44px 0',
'& > button': { '& > button': {
fontSize: md, fontSize: md,
fontFamily: 'Averta', fontFamily: 'Averta',

View File

@ -1,21 +1,15 @@
import IconButton from '@material-ui/core/IconButton' import IconButton from '@material-ui/core/IconButton'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close' import Close from '@material-ui/icons/Close'
import OpenInNew from '@material-ui/icons/OpenInNew'
import classNames from 'classnames'
import React from 'react' import React from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { getExplorerInfo } from 'src/config' import { EthHashInfo, Button } from '@gnosis.pm/safe-react-components'
import { styles } from './style' import { styles } from './style'
import Identicon from 'src/components/Identicon'
import Modal from 'src/components/Modal' import Modal from 'src/components/Modal'
import Block from 'src/components/layout/Block' 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 Hairline from 'src/components/layout/Hairline'
import Link from 'src/components/layout/Link'
import Paragraph from 'src/components/layout/Paragraph' import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row' import Row from 'src/components/layout/Row'
import { import {
@ -23,16 +17,11 @@ import {
safeNameSelector, safeNameSelector,
safeParamAddressFromStateSelector, safeParamAddressFromStateSelector,
} from 'src/logic/safe/store/selectors' } from 'src/logic/safe/store/selectors'
import { md, secondary } from 'src/theme/variables'
import { WELCOME_ADDRESS } from 'src/routes/routes' import { WELCOME_ADDRESS } from 'src/routes/routes'
import { removeLocalSafe } from 'src/logic/safe/store/actions/removeLocalSafe' import { removeLocalSafe } from 'src/logic/safe/store/actions/removeLocalSafe'
import { sameAddress } from 'src/logic/wallets/ethAddresses' import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { saveDefaultSafe } from 'src/logic/safe/utils' import { saveDefaultSafe } from 'src/logic/safe/utils'
import { getExplorerInfo } from 'src/config'
const openIconStyle = {
height: md,
color: secondary,
}
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
@ -47,8 +36,6 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
const safeName = useSelector(safeNameSelector) const safeName = useSelector(safeNameSelector)
const defaultSafe = useSelector(defaultSafeSelector) const defaultSafe = useSelector(defaultSafeSelector)
const dispatch = useDispatch() const dispatch = useDispatch()
const explorerInfo = getExplorerInfo(safeAddress)
const { url } = explorerInfo()
const onRemoveSafeHandler = async () => { const onRemoveSafeHandler = async () => {
await dispatch(removeLocalSafe(safeAddress)) await dispatch(removeLocalSafe(safeAddress))
@ -82,28 +69,16 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
<Hairline /> <Hairline />
<Block className={classes.container}> <Block className={classes.container}>
<Row className={classes.owner}> <Row className={classes.owner}>
<Col align="center" xs={1}> <EthHashInfo
<Identicon address={safeAddress} diameter={32} /> hash={safeAddress}
</Col> name={safeName}
<Col xs={11}> showAvatar
<Block className={classNames(classes.name, classes.userName)}> showCopyBtn
<Paragraph noMargin size="lg" weight="bolder"> explorerUrl={getExplorerInfo(safeAddress)}
{safeName} />
</Paragraph>
<Block className={classes.user} justify="center">
<Paragraph color="disabled" noMargin size="md">
{safeAddress}
</Paragraph>
<Link className={classes.open} target="_blank" to={url}>
<OpenInNew style={openIconStyle} />
</Link>
</Block>
</Block>
</Col>
</Row> </Row>
<Hairline />
<Row className={classes.description}> <Row className={classes.description}>
<Paragraph noMargin> <Paragraph size="lg" noMargin>
Removing a Safe only removes it from your interface. <b>It does not delete the Safe</b>. You can always add Removing a Safe only removes it from your interface. <b>It does not delete the Safe</b>. You can always add
it back using the Safe&apos;s address. it back using the Safe&apos;s address.
</Paragraph> </Paragraph>
@ -111,14 +86,15 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
</Block> </Block>
<Hairline /> <Hairline />
<Row align="center" className={classes.buttonRow}> <Row align="center" className={classes.buttonRow}>
<Button minHeight={42} minWidth={140} onClick={onClose}> <Button size="md" onClick={onClose} color="secondary" variant="outlined">
Cancel Cancel
</Button> </Button>
<Button <Button
className={classes.buttonRemove} className={classes.buttonRemove}
minWidth={140} size="md"
onClick={onRemoveSafeHandler} onClick={onRemoveSafeHandler}
type="submit" type="submit"
color="error"
variant="contained" variant="contained"
> >
Remove Remove

View File

@ -1,15 +1,16 @@
import { createStyles } from '@material-ui/core/styles' import { createStyles } from '@material-ui/core/styles'
import { background, error, lg, md, sm } from 'src/theme/variables' import { error, lg, md, sm } from 'src/theme/variables'
export const styles = createStyles({ export const styles = createStyles({
heading: { heading: {
boxSizing: 'border-box', boxSizing: 'border-box',
justifyContent: 'space-between', justifyContent: 'space-between',
maxHeight: '75px', minHeight: '74px',
padding: `${sm} ${lg}`, padding: `${sm} ${lg}`,
}, },
container: { container: {
minHeight: '369px', minHeight: '369px',
padding: `${sm}`,
}, },
manage: { manage: {
fontSize: lg, fontSize: lg,
@ -35,7 +36,6 @@ export const styles = createStyles({
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
}, },
owner: { owner: {
backgroundColor: background,
padding: md, padding: md,
alignItems: 'center', alignItems: 'center',
}, },

View File

@ -2,8 +2,10 @@ import IconButton from '@material-ui/core/IconButton'
import MenuItem from '@material-ui/core/MenuItem' import MenuItem from '@material-ui/core/MenuItem'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import Close from '@material-ui/icons/Close' import Close from '@material-ui/icons/Close'
import { Button } from '@gnosis.pm/safe-react-components'
import React, { ReactElement, useEffect, useState } from 'react' import React, { ReactElement, useEffect, useState } from 'react'
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
import styled from 'styled-components'
import { List } from 'immutable' import { List } from 'immutable'
import Field from 'src/components/forms/Field' import Field from 'src/components/forms/Field'
@ -11,7 +13,6 @@ import GnoForm from 'src/components/forms/GnoForm'
import SelectField from 'src/components/forms/SelectField' import SelectField from 'src/components/forms/SelectField'
import { composeValidators, differentFrom, minValue, mustBeInteger, required } from 'src/components/forms/validator' import { composeValidators, differentFrom, minValue, mustBeInteger, required } from 'src/components/forms/validator'
import Block from 'src/components/layout/Block' import Block from 'src/components/layout/Block'
import Button from 'src/components/layout/Button'
import Col from 'src/components/layout/Col' import Col from 'src/components/layout/Col'
import Hairline from 'src/components/layout/Hairline' import Hairline from 'src/components/layout/Hairline'
import Paragraph from 'src/components/layout/Paragraph' import Paragraph from 'src/components/layout/Paragraph'
@ -30,6 +31,14 @@ import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionPara
const THRESHOLD_FIELD_NAME = 'threshold' const THRESHOLD_FIELD_NAME = 'threshold'
const StyledButton = styled(Button)`
&.Mui-disabled {
background-color: ${({ theme }) => theme.colors.primary};
color: ${({ theme }) => theme.colors.white};
opacity: 0.5;
}
`
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
type ChangeThresholdModalProps = { type ChangeThresholdModalProps = {
@ -52,6 +61,7 @@ export const ChangeThresholdModal = ({
const [manualGasPrice, setManualGasPrice] = useState<string | undefined>() const [manualGasPrice, setManualGasPrice] = useState<string | undefined>()
const [manualGasLimit, setManualGasLimit] = useState<string | undefined>() const [manualGasLimit, setManualGasLimit] = useState<string | undefined>()
const [editedThreshold, setEditedThreshold] = useState<number>(threshold) const [editedThreshold, setEditedThreshold] = useState<number>(threshold)
const [disabledSubmitForm, setDisabledSubmitForm] = useState<boolean>(true)
const { const {
gasCostFormatted, gasCostFormatted,
@ -86,6 +96,12 @@ export const ChangeThresholdModal = ({
} }
}, [safeAddress, editedThreshold]) }, [safeAddress, editedThreshold])
const handleThreshold = ({ target }) => {
const value = parseInt(target.value)
setDisabledSubmitForm(value === editedThreshold || value === threshold)
setEditedThreshold(value)
}
const handleSubmit = async ({ txParameters }) => { const handleSubmit = async ({ txParameters }) => {
await dispatch( await dispatch(
createTransaction({ createTransaction({
@ -154,9 +170,7 @@ export const ChangeThresholdModal = ({
<Field <Field
data-testid="threshold-select-input" data-testid="threshold-select-input"
name={THRESHOLD_FIELD_NAME} name={THRESHOLD_FIELD_NAME}
onChange={({ target }) => { onChange={handleThreshold}
setEditedThreshold(parseInt(target.value))
}}
render={(props) => ( render={(props) => (
<> <>
<SelectField {...props} disableError> <SelectField {...props} disableError>
@ -166,11 +180,6 @@ export const ChangeThresholdModal = ({
</MenuItem> </MenuItem>
))} ))}
</SelectField> </SelectField>
{props.meta.error && props.meta.touched && (
<Paragraph className={classes.errorText} color="error" noMargin>
{props.meta.error}
</Paragraph>
)}
</> </>
)} )}
validate={composeValidators(required, mustBeInteger, minValue(1), differentFrom(threshold))} validate={composeValidators(required, mustBeInteger, minValue(1), differentFrom(threshold))}
@ -205,18 +214,18 @@ export const ChangeThresholdModal = ({
)} )}
<Row align="center" className={classes.buttonRow}> <Row align="center" className={classes.buttonRow}>
<Button minWidth={140} onClick={onClose} color="secondary"> <Button size="md" onClick={onClose} variant="outlined" color="primary">
Cancel Cancel
</Button> </Button>
<Button <StyledButton
color="primary" color="primary"
minWidth={140} size="md"
type="submit" type="submit"
variant="contained" variant="contained"
disabled={txEstimationExecutionStatus === EstimationStatus.LOADING} disabled={txEstimationExecutionStatus === EstimationStatus.LOADING || disabledSubmitForm}
> >
Submit Submit
</Button> </StyledButton>
</Row> </Row>
</> </>
)} )}

View File

@ -33,7 +33,7 @@ const DetailsWithTxInfo = ({ children, txData, txInfo }: DetailsWithTxInfoProps)
let name let name
let avatarUrl let avatarUrl
if (isCustomTxInfo(txInfo)) { if (isCustomTxInfo(txInfo) && txInfo.toInfo) {
name = txInfo.toInfo.name name = txInfo.toInfo.name
avatarUrl = txInfo.toInfo.logoUri avatarUrl = txInfo.toInfo.logoUri
} }