mirror of
https://github.com/status-im/safe-react.git
synced 2025-03-02 18:30:34 +00:00
Merge branch 'development' into darwin-arm64-support
This commit is contained in:
commit
aadf6b92f4
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "safe-react",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"description": "Allowing crypto users manage funds in a safer way",
|
||||
"website": "https://github.com/gnosis/safe-react#readme",
|
||||
"bugs": {
|
||||
|
@ -3,7 +3,7 @@ import { EnvironmentSettings, ETHEREUM_NETWORK, NetworkConfig } from 'src/config
|
||||
|
||||
const baseConfig: EnvironmentSettings = {
|
||||
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',
|
||||
gasPriceOracle: {
|
||||
url: 'https://ethgasstation.info/json/ethgasAPI.json',
|
||||
|
@ -36,8 +36,8 @@ const mainnet: NetworkConfig = {
|
||||
isTestNet: true,
|
||||
nativeCoin: {
|
||||
address: '0x0000000000000000000000000000000000000000',
|
||||
name: 'Energy web token',
|
||||
symbol: 'EWT',
|
||||
name: 'Volta Token',
|
||||
symbol: 'VT',
|
||||
decimals: 18,
|
||||
logoUri: EwcLogo,
|
||||
},
|
||||
|
@ -1,11 +1,9 @@
|
||||
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 { checksumAddress } from 'src/utils/checksumAddress'
|
||||
|
||||
import { ZERO_ADDRESS, sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
|
||||
export type TokenBalance = {
|
||||
tokenInfo: TokenProps
|
||||
balance: string
|
||||
@ -35,24 +33,5 @@ export const fetchTokenCurrenciesBalances = async ({
|
||||
checksumAddress(safeAddress),
|
||||
)}/balances/${selectedCurrency}/?trusted=${trustedTokens}&exclude_spam=${excludeSpamTokens}`
|
||||
|
||||
return axios.get(url).then(({ 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
|
||||
})
|
||||
return axios.get(url).then(({ data }) => data)
|
||||
}
|
||||
|
@ -28,10 +28,10 @@ export const useLoadSafe = (safeAddress?: string, loadedViaUrl = true): boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
dispatch(loadAddressBookFromStorage())
|
||||
|
||||
dispatch(loadAddressBookFromStorage())
|
||||
fetchData()
|
||||
}, [dispatch, safeAddress])
|
||||
}, [dispatch, safeAddress, loadedViaUrl])
|
||||
|
||||
return isSafeLoaded
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ type BaseCustom = {
|
||||
dataSize: string
|
||||
value: string
|
||||
isCancellation: boolean
|
||||
toInfo: AddressInfo
|
||||
toInfo?: AddressInfo
|
||||
}
|
||||
|
||||
type Custom = BaseCustom & {
|
||||
|
@ -17,7 +17,7 @@ const SIGNERS = {
|
||||
const getSignersByWallet = (isHW) =>
|
||||
isHW ? [SIGNERS.ETH_SIGN] : [SIGNERS.EIP712_V3, SIGNERS.EIP712_V4, SIGNERS.EIP712, SIGNERS.ETH_SIGN]
|
||||
|
||||
export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.0.0'
|
||||
export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.1.1'
|
||||
|
||||
export const tryOffchainSigning = async (safeTxHash: string, txArgs, isHW: boolean): Promise<string> => {
|
||||
let signature
|
||||
|
@ -255,7 +255,8 @@ const AddressBookTable = (): ReactElement => {
|
||||
activeScreenType="chooseTxType"
|
||||
isOpen={sendFundsModalOpen}
|
||||
onClose={() => setSendFundsModalOpen(false)}
|
||||
recipientAddress={selectedEntry && selectedEntry.entry ? selectedEntry.entry.address : undefined}
|
||||
recipientAddress={selectedEntry?.entry?.address}
|
||||
recipientName={selectedEntry?.entry?.name}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
@ -59,11 +59,12 @@ const StyledCard = styled(Card)`
|
||||
padding: 0;
|
||||
`
|
||||
|
||||
const StyledIframe = styled.iframe`
|
||||
const StyledIframe = styled.iframe<{ isLoading: boolean }>`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
display: ${({ isLoading }) => (isLoading ? 'none' : 'block')};
|
||||
`
|
||||
|
||||
const Breadcrumb = styled.div`
|
||||
@ -326,6 +327,7 @@ const AppFrame = ({ appUrl }: Props): React.ReactElement => {
|
||||
)}
|
||||
|
||||
<StyledIframe
|
||||
isLoading={appIsLoading}
|
||||
frameBorder="0"
|
||||
id={`iframe-${appUrl}`}
|
||||
ref={iframeRef}
|
||||
|
@ -34,12 +34,6 @@ export const staticAppsList: Array<StaticAppInfo> = [
|
||||
disabled: false,
|
||||
networks: [ETHEREUM_NETWORK.MAINNET],
|
||||
},
|
||||
// Aave v2
|
||||
{
|
||||
url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmVg7aXr5S8sT2iUdUwdkfTJNknmB7rcE3t92HiGoVsYDj`,
|
||||
disabled: false,
|
||||
networks: [ETHEREUM_NETWORK.MAINNET],
|
||||
},
|
||||
//Balancer Exchange
|
||||
{
|
||||
url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmRb2VfPVYBrv6gi2zDywgVgTg3A19ZCRMqwL13Ez5f5AS`,
|
||||
|
@ -56,6 +56,7 @@ type Props = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
recipientAddress?: string
|
||||
recipientName?: string
|
||||
selectedToken?: string | NFTToken | Erc721Transfer
|
||||
tokenAmount?: string
|
||||
}
|
||||
@ -65,6 +66,7 @@ const SendModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
recipientAddress,
|
||||
recipientName,
|
||||
selectedToken,
|
||||
tokenAmount,
|
||||
}: Props): React.ReactElement => {
|
||||
@ -119,7 +121,12 @@ const SendModal = ({
|
||||
}
|
||||
>
|
||||
{activeScreen === 'chooseTxType' && (
|
||||
<ChooseTxType onClose={onClose} recipientAddress={recipientAddress} setActiveScreen={setActiveScreen} />
|
||||
<ChooseTxType
|
||||
onClose={onClose}
|
||||
recipientName={recipientName}
|
||||
recipientAddress={recipientAddress}
|
||||
setActiveScreen={setActiveScreen}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeScreen === 'sendFunds' && (
|
||||
|
@ -14,20 +14,29 @@ import Row from 'src/components/layout/Row'
|
||||
import { safeFeaturesEnabledSelector } from 'src/logic/safe/store/selectors'
|
||||
import { useStyles } from 'src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style'
|
||||
import ContractInteractionIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg'
|
||||
import { EthHashInfo } from '@gnosis.pm/safe-react-components'
|
||||
|
||||
import Collectible from '../assets/collectibles.svg'
|
||||
import Token from '../assets/token.svg'
|
||||
import { FEATURES } from 'src/config/networks/network.d'
|
||||
|
||||
import { getExplorerInfo } from 'src/config'
|
||||
|
||||
type ActiveScreen = 'sendFunds' | 'sendCollectible' | 'contractInteraction'
|
||||
|
||||
interface ChooseTxTypeProps {
|
||||
onClose: () => void
|
||||
recipientAddress?: string
|
||||
recipientName?: string
|
||||
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 featuresEnabled = useSelector(safeFeaturesEnabledSelector)
|
||||
const erc721Enabled = featuresEnabled?.includes(FEATURES.ERC721)
|
||||
@ -61,11 +70,18 @@ const ChooseTxType = ({ onClose, recipientAddress, setActiveScreen }: ChooseTxTy
|
||||
</Row>
|
||||
<Hairline />
|
||||
{!!recipientAddress && (
|
||||
<Row align="center">
|
||||
<Row align="center" margin="md">
|
||||
<Col className={classes.disclaimer} layout="column" middle="xs">
|
||||
<Paragraph className={classes.disclaimerText} noMargin>
|
||||
Please select what you will send to {recipientAddress}
|
||||
Please select what you will send to
|
||||
</Paragraph>
|
||||
<EthHashInfo
|
||||
hash={recipientAddress}
|
||||
name={recipientName}
|
||||
showAvatar
|
||||
showCopyBtn
|
||||
explorerUrl={getExplorerInfo(recipientAddress)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
|
@ -19,13 +19,14 @@ export const useStyles = makeStyles(
|
||||
},
|
||||
disclaimerText: {
|
||||
fontSize: md,
|
||||
marginBottom: `${md}`,
|
||||
},
|
||||
closeIcon: {
|
||||
height: '35px',
|
||||
width: '35px',
|
||||
},
|
||||
buttonColumn: {
|
||||
padding: '52px 0',
|
||||
margin: '16px 0 44px 0',
|
||||
'& > button': {
|
||||
fontSize: md,
|
||||
fontFamily: 'Averta',
|
||||
|
@ -1,21 +1,15 @@
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import OpenInNew from '@material-ui/icons/OpenInNew'
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
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 Identicon from 'src/components/Identicon'
|
||||
import Modal from 'src/components/Modal'
|
||||
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 Link from 'src/components/layout/Link'
|
||||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import Row from 'src/components/layout/Row'
|
||||
import {
|
||||
@ -23,16 +17,11 @@ import {
|
||||
safeNameSelector,
|
||||
safeParamAddressFromStateSelector,
|
||||
} from 'src/logic/safe/store/selectors'
|
||||
import { md, secondary } from 'src/theme/variables'
|
||||
import { WELCOME_ADDRESS } from 'src/routes/routes'
|
||||
import { removeLocalSafe } from 'src/logic/safe/store/actions/removeLocalSafe'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { saveDefaultSafe } from 'src/logic/safe/utils'
|
||||
|
||||
const openIconStyle = {
|
||||
height: md,
|
||||
color: secondary,
|
||||
}
|
||||
import { getExplorerInfo } from 'src/config'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
@ -47,8 +36,6 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
|
||||
const safeName = useSelector(safeNameSelector)
|
||||
const defaultSafe = useSelector(defaultSafeSelector)
|
||||
const dispatch = useDispatch()
|
||||
const explorerInfo = getExplorerInfo(safeAddress)
|
||||
const { url } = explorerInfo()
|
||||
|
||||
const onRemoveSafeHandler = async () => {
|
||||
await dispatch(removeLocalSafe(safeAddress))
|
||||
@ -82,28 +69,16 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
|
||||
<Hairline />
|
||||
<Block className={classes.container}>
|
||||
<Row className={classes.owner}>
|
||||
<Col align="center" xs={1}>
|
||||
<Identicon address={safeAddress} diameter={32} />
|
||||
</Col>
|
||||
<Col xs={11}>
|
||||
<Block className={classNames(classes.name, classes.userName)}>
|
||||
<Paragraph noMargin size="lg" weight="bolder">
|
||||
{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>
|
||||
<EthHashInfo
|
||||
hash={safeAddress}
|
||||
name={safeName}
|
||||
showAvatar
|
||||
showCopyBtn
|
||||
explorerUrl={getExplorerInfo(safeAddress)}
|
||||
/>
|
||||
</Row>
|
||||
<Hairline />
|
||||
<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
|
||||
it back using the Safe's address.
|
||||
</Paragraph>
|
||||
@ -111,14 +86,15 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
|
||||
</Block>
|
||||
<Hairline />
|
||||
<Row align="center" className={classes.buttonRow}>
|
||||
<Button minHeight={42} minWidth={140} onClick={onClose}>
|
||||
<Button size="md" onClick={onClose} color="secondary" variant="outlined">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className={classes.buttonRemove}
|
||||
minWidth={140}
|
||||
size="md"
|
||||
onClick={onRemoveSafeHandler}
|
||||
type="submit"
|
||||
color="error"
|
||||
variant="contained"
|
||||
>
|
||||
Remove
|
||||
|
@ -1,15 +1,16 @@
|
||||
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({
|
||||
heading: {
|
||||
boxSizing: 'border-box',
|
||||
justifyContent: 'space-between',
|
||||
maxHeight: '75px',
|
||||
minHeight: '74px',
|
||||
padding: `${sm} ${lg}`,
|
||||
},
|
||||
container: {
|
||||
minHeight: '369px',
|
||||
padding: `${sm}`,
|
||||
},
|
||||
manage: {
|
||||
fontSize: lg,
|
||||
@ -35,7 +36,6 @@ export const styles = createStyles({
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
owner: {
|
||||
backgroundColor: background,
|
||||
padding: md,
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
@ -2,8 +2,10 @@ import IconButton from '@material-ui/core/IconButton'
|
||||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import { Button } from '@gnosis.pm/safe-react-components'
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import styled from 'styled-components'
|
||||
import { List } from 'immutable'
|
||||
|
||||
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 { composeValidators, differentFrom, minValue, mustBeInteger, required } from 'src/components/forms/validator'
|
||||
import Block from 'src/components/layout/Block'
|
||||
import Button from 'src/components/layout/Button'
|
||||
import Col from 'src/components/layout/Col'
|
||||
import Hairline from 'src/components/layout/Hairline'
|
||||
import 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 StyledButton = styled(Button)`
|
||||
&.Mui-disabled {
|
||||
background-color: ${({ theme }) => theme.colors.primary};
|
||||
color: ${({ theme }) => theme.colors.white};
|
||||
opacity: 0.5;
|
||||
}
|
||||
`
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
type ChangeThresholdModalProps = {
|
||||
@ -52,6 +61,7 @@ export const ChangeThresholdModal = ({
|
||||
const [manualGasPrice, setManualGasPrice] = useState<string | undefined>()
|
||||
const [manualGasLimit, setManualGasLimit] = useState<string | undefined>()
|
||||
const [editedThreshold, setEditedThreshold] = useState<number>(threshold)
|
||||
const [disabledSubmitForm, setDisabledSubmitForm] = useState<boolean>(true)
|
||||
|
||||
const {
|
||||
gasCostFormatted,
|
||||
@ -86,6 +96,12 @@ export const ChangeThresholdModal = ({
|
||||
}
|
||||
}, [safeAddress, editedThreshold])
|
||||
|
||||
const handleThreshold = ({ target }) => {
|
||||
const value = parseInt(target.value)
|
||||
setDisabledSubmitForm(value === editedThreshold || value === threshold)
|
||||
setEditedThreshold(value)
|
||||
}
|
||||
|
||||
const handleSubmit = async ({ txParameters }) => {
|
||||
await dispatch(
|
||||
createTransaction({
|
||||
@ -154,9 +170,7 @@ export const ChangeThresholdModal = ({
|
||||
<Field
|
||||
data-testid="threshold-select-input"
|
||||
name={THRESHOLD_FIELD_NAME}
|
||||
onChange={({ target }) => {
|
||||
setEditedThreshold(parseInt(target.value))
|
||||
}}
|
||||
onChange={handleThreshold}
|
||||
render={(props) => (
|
||||
<>
|
||||
<SelectField {...props} disableError>
|
||||
@ -166,11 +180,6 @@ export const ChangeThresholdModal = ({
|
||||
</MenuItem>
|
||||
))}
|
||||
</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))}
|
||||
@ -205,18 +214,18 @@ export const ChangeThresholdModal = ({
|
||||
)}
|
||||
|
||||
<Row align="center" className={classes.buttonRow}>
|
||||
<Button minWidth={140} onClick={onClose} color="secondary">
|
||||
<Button size="md" onClick={onClose} variant="outlined" color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
<StyledButton
|
||||
color="primary"
|
||||
minWidth={140}
|
||||
size="md"
|
||||
type="submit"
|
||||
variant="contained"
|
||||
disabled={txEstimationExecutionStatus === EstimationStatus.LOADING}
|
||||
disabled={txEstimationExecutionStatus === EstimationStatus.LOADING || disabledSubmitForm}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</StyledButton>
|
||||
</Row>
|
||||
</>
|
||||
)}
|
||||
|
@ -33,7 +33,7 @@ const DetailsWithTxInfo = ({ children, txData, txInfo }: DetailsWithTxInfoProps)
|
||||
let name
|
||||
let avatarUrl
|
||||
|
||||
if (isCustomTxInfo(txInfo)) {
|
||||
if (isCustomTxInfo(txInfo) && txInfo.toInfo) {
|
||||
name = txInfo.toInfo.name
|
||||
avatarUrl = txInfo.toInfo.logoUri
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user