diff --git a/.env.example b/.env.example
index 266bbc27..ba240349 100644
--- a/.env.example
+++ b/.env.example
@@ -5,7 +5,6 @@ REACT_APP_NETWORK=
REACT_APP_GOOGLE_ANALYTICS=
REACT_APP_INFURA_TOKEN=
REACT_APP_IPFS_GATEWAY=https://ipfs.io/ipfs
-PUBLIC_URL=/app/
REACT_APP_SENTRY_DSN=
# For production environments
diff --git a/package.json b/package.json
index 61d94146..5c7b64f5 100644
--- a/package.json
+++ b/package.json
@@ -161,7 +161,7 @@
"@gnosis.pm/safe-apps-sdk": "1.0.3",
"@gnosis.pm/safe-apps-sdk-v1": "npm:@gnosis.pm/safe-apps-sdk@0.4.2",
"@gnosis.pm/safe-contracts": "1.1.1-dev.2",
- "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#80f5db6",
+ "@gnosis.pm/safe-react-components": "https://github.com/gnosis/safe-react-components.git#2e427ee",
"@gnosis.pm/util-contracts": "2.0.6",
"@ledgerhq/hw-transport-node-hid-singleton": "5.45.0",
"@material-ui/core": "^4.11.0",
diff --git a/readme.md b/readme.md
index dbd62775..a7bf34d3 100644
--- a/readme.md
+++ b/readme.md
@@ -4,16 +4,14 @@ The most trusted platform to store digital assets on Ethereum
## Getting Started
-These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.
+These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See [Deployment](#deployment) for notes on how to deploy the project on a live system.
### Prerequisites
-What things you need to install the software and how to install them
+What things you need to install globally:
```
-yarn add truffle // recommended usage of -g flag
-yarn add ganache-cli // recommended usage of -g flag
-yarn add flow-type // recommended usage of -g flag
+yarn global add truffle ganache-cli
```
We use [yarn](https://yarnpkg.com) in our infrastacture, so we decided to go with yarn in the README
@@ -37,6 +35,18 @@ If you prefer using Mainnet ones:
yarn start-mainnet
```
+### Environment variables
+The app grabs environment variables from the `.env` file. Copy our template to your own local file:
+```
+cp .env.example .env
+```
+
+To execute transactions, you'll need to create an [Infura](https://infura.io) project and set the project ID in the `.env` you've just created:
+```
+REACT_APP_INFURA_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+```
+Once done, you'll need to restart the app.
+
### Building
For Rinkeby:
```
@@ -120,7 +130,7 @@ Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c6
## Versioning
-We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/gnosis/gnosis-team-safe/tags).
+We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/gnosis/gnosis-team-safe/tags).
## Authors
diff --git a/src/components/AddressInfo/index.tsx b/src/components/AddressInfo/index.tsx
deleted file mode 100644
index 8e08f2b7..00000000
--- a/src/components/AddressInfo/index.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React from 'react'
-import { getExplorerInfo, getNetworkInfo } from 'src/config'
-import CopyBtn from 'src/components/CopyBtn'
-import Identicon from 'src/components/Identicon'
-import Block from 'src/components/layout/Block'
-import Bold from 'src/components/layout/Bold'
-import Paragraph from 'src/components/layout/Paragraph'
-import { border, xs } from 'src/theme/variables'
-import styled from 'styled-components'
-import { ExplorerButton } from '@gnosis.pm/safe-react-components'
-
-const Wrapper = styled.div`
- display: flex;
- align-items: center;
-
- .icon-section {
- margin-right: 10px;
- }
-
- .data-section {
- display: flex;
- flex-direction: column;
-
- .address {
- display: flex;
- }
- }
-`
-const StyledBlock = styled(Block)`
- font-size: 12px;
- line-height: 1.08;
- letter-spacing: -0.5;
- background-color: ${border};
- width: fit-content;
- padding: 5px 10px;
- margin-top: ${xs};
- border-radius: 3px;
-`
-interface Props {
- safeName?: string
- safeAddress: string
- ethBalance?: string
-}
-
-const { nativeCoin } = getNetworkInfo()
-
-const AddressInfo = ({ ethBalance, safeAddress, safeName }: Props): React.ReactElement => {
- return (
-
-
-
-
-
- {safeName && (
-
- {safeName}
-
- )}
-
-
- {safeAddress}
-
-
-
-
- {ethBalance && (
-
-
- Balance: {`${ethBalance} ${nativeCoin.symbol}`}
-
-
- )}
-
-
- )
-}
-
-export default AddressInfo
diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx
index ae64a97d..158725fe 100644
--- a/src/components/App/index.tsx
+++ b/src/components/App/index.tsx
@@ -50,12 +50,6 @@ const notificationStyles = {
info: {
background: '#fff',
},
- receiveModal: {
- height: 'auto',
- maxWidth: 'calc(100% - 30px)',
- minHeight: '544px',
- overflow: 'hidden',
- },
}
const Frame = styled.div`
@@ -149,7 +143,7 @@ const App: React.FC = ({ children }) => {
description="Receive Tokens Form"
handleClose={onReceiveHide}
open={safeActionsState.showReceive}
- paperClassName={classes.receiveModal}
+ paperClassName="receive-modal"
title="Receive Tokens"
>
diff --git a/src/components/AppLayout/Header/components/Layout.tsx b/src/components/AppLayout/Header/components/Layout.tsx
index 85051334..fae9690d 100644
--- a/src/components/AppLayout/Header/components/Layout.tsx
+++ b/src/components/AppLayout/Header/components/Layout.tsx
@@ -12,7 +12,7 @@ import Spacer from 'src/components/Spacer'
import Col from 'src/components/layout/Col'
import Img from 'src/components/layout/Img'
import Row from 'src/components/layout/Row'
-import { border, headerHeight, md, screenSm, sm } from 'src/theme/variables'
+import { headerHeight, md, screenSm, sm } from 'src/theme/variables'
import { useStateHandler } from 'src/logic/hooks/useStateHandler'
import SafeLogo from '../assets/gnosis-safe-multisig-logo.svg'
@@ -29,8 +29,6 @@ const styles = () => ({
summary: {
alignItems: 'center',
backgroundColor: 'white',
- borderBottom: `solid 2px ${border}`,
- boxShadow: '0 2px 4px 0 rgba(212, 212, 211, 0.59)',
flexWrap: 'nowrap',
height: headerHeight,
position: 'fixed',
diff --git a/src/components/AppLayout/Header/components/ProviderDetails/ConnectDetails.tsx b/src/components/AppLayout/Header/components/ProviderDetails/ConnectDetails.tsx
index 0b52d4e5..e3ebe602 100644
--- a/src/components/AppLayout/Header/components/ProviderDetails/ConnectDetails.tsx
+++ b/src/components/AppLayout/Header/components/ProviderDetails/ConnectDetails.tsx
@@ -6,13 +6,11 @@ import ConnectButton from 'src/components/ConnectButton'
import Block from 'src/components/layout/Block'
import Paragraph from 'src/components/layout/Paragraph'
import Row from 'src/components/layout/Row'
-import { lg, md } from 'src/theme/variables'
import { KeyRing } from 'src/components/AppLayout/Header/components/KeyRing'
+import { Card } from '@gnosis.pm/safe-react-components'
+import styled from 'styled-components'
const styles = () => ({
- container: {
- padding: `${md} 12px`,
- },
logo: {
justifyContent: 'center',
},
@@ -22,8 +20,8 @@ const styles = () => ({
textAlign: 'center',
},
connect: {
- padding: `${md} ${lg}`,
textAlign: 'center',
+ marginTop: '60px',
},
connectText: {
letterSpacing: '1px',
@@ -33,22 +31,24 @@ const styles = () => ({
},
})
+const StyledCard = styled(Card)`
+ padding: 20px;
+`
const ConnectDetails = ({ classes }) => (
- <>
-
-
-
- Connect a Wallet
-
-
-
-
-
+
+
+
+ Connect a Wallet
+
+
+
+
+
- >
+
)
export default withStyles(styles as any)(ConnectDetails)
diff --git a/src/components/AppLayout/Header/components/ProviderDetails/UserDetails.tsx b/src/components/AppLayout/Header/components/ProviderDetails/UserDetails.tsx
index d50fa6cf..1598904f 100644
--- a/src/components/AppLayout/Header/components/ProviderDetails/UserDetails.tsx
+++ b/src/components/AppLayout/Header/components/ProviderDetails/UserDetails.tsx
@@ -2,7 +2,7 @@ import { makeStyles } from '@material-ui/core/styles'
import Dot from '@material-ui/icons/FiberManualRecord'
import classNames from 'classnames'
import * as React from 'react'
-import { EthHashInfo, Identicon } from '@gnosis.pm/safe-react-components'
+import { EthHashInfo, Identicon, Card } from '@gnosis.pm/safe-react-components'
import Spacer from 'src/components/Spacer'
import Block from 'src/components/layout/Block'
@@ -18,6 +18,7 @@ import { getExplorerInfo } from 'src/config'
import { KeyRing } from 'src/components/AppLayout/Header/components/KeyRing'
import { CircleDot } from '../CircleDot'
import { createStyles } from '@material-ui/core'
+import styled from 'styled-components'
import WalletIcon from '../../assets/wallet.svg'
@@ -91,6 +92,9 @@ const styles = createStyles({
},
})
+const StyledCard = styled(Card)`
+ padding: 0px;
+`
type Props = {
connected: boolean
network: ETHEREUM_NETWORK
@@ -116,11 +120,11 @@ export const UserDetails = ({
const classes = useStyles()
return (
- <>
+
{connected ? (
-
+
) : (
)}
@@ -185,11 +189,11 @@ export const UserDetails = ({
variant="contained"
data-testid="disconnect-btn"
>
-
+
Disconnect
- >
+
)
}
diff --git a/src/components/AppLayout/Header/components/ProviderInfo/ProviderAccessible.tsx b/src/components/AppLayout/Header/components/ProviderInfo/ProviderAccessible.tsx
index 563c3abb..e4720113 100644
--- a/src/components/AppLayout/Header/components/ProviderInfo/ProviderAccessible.tsx
+++ b/src/components/AppLayout/Header/components/ProviderInfo/ProviderAccessible.tsx
@@ -89,8 +89,8 @@ const ProviderInfo = ({ connected, provider, userAddress }: ProviderInfoProps):
diff --git a/src/components/AppLayout/Sidebar/SafeHeader/index.tsx b/src/components/AppLayout/Sidebar/SafeHeader/index.tsx
index 149f7d84..fad57d55 100644
--- a/src/components/AppLayout/Sidebar/SafeHeader/index.tsx
+++ b/src/components/AppLayout/Sidebar/SafeHeader/index.tsx
@@ -45,7 +45,7 @@ const IconContainer = styled.div`
`
const StyledButton = styled(Button)`
&&.MuiButton-root {
- padding: 0 16px;
+ padding: 0 12px;
}
*:first-child {
margin: 0 4px 0 0;
@@ -170,7 +170,7 @@ const SafeHeader = ({
{balance}
-
+
New transaction
diff --git a/src/components/AppLayout/index.tsx b/src/components/AppLayout/index.tsx
index e70a31b4..72fb20a0 100644
--- a/src/components/AppLayout/index.tsx
+++ b/src/components/AppLayout/index.tsx
@@ -17,16 +17,16 @@ const Container = styled.div`
`
const HeaderWrapper = styled.nav`
- height: 54px;
+ height: 52px;
width: 100%;
- z-index: 1;
+ z-index: 2;
background-color: white;
- box-shadow: 0 0 4px 0 rgba(212, 212, 211, 0.59);
+ box-shadow: 0 2px 4px 0 rgba(40, 54, 61, 0.18);
`
const BodyWrapper = styled.div`
- height: calc(100% - 54px);
+ height: calc(100% - 52px);
width: 100%;
display: flex;
flex-direction: row;
@@ -41,7 +41,7 @@ const SidebarWrapper = styled.aside`
padding: 8px 8px 0 8px;
background-color: ${({ theme }) => theme.colors.white};
- border-right: 2px solid ${({ theme }) => theme.colors.separator};
+ box-shadow: 0 2px 4px 0 rgba(40, 54, 61, 0.18);
`
const ContentWrapper = styled.section`
diff --git a/src/components/ConnectButton/index.tsx b/src/components/ConnectButton/index.tsx
index 4d3c23dd..dc9bbd2e 100644
--- a/src/components/ConnectButton/index.tsx
+++ b/src/components/ConnectButton/index.tsx
@@ -73,7 +73,7 @@ export const onConnectButtonClick = async () => {
}
const ConnectButton = (props): React.ReactElement => (
-
+
Connect
)
diff --git a/src/components/CookiesBanner/index.tsx b/src/components/CookiesBanner/index.tsx
index 9219aab6..781ae6de 100644
--- a/src/components/CookiesBanner/index.tsx
+++ b/src/components/CookiesBanner/index.tsx
@@ -21,7 +21,7 @@ const useStyles = makeStyles({
container: {
backgroundColor: '#fff',
bottom: '0',
- boxShadow: '0 2px 4px 0 rgba(212, 212, 211, 0.59)',
+ boxShadow: '1px 2px 10px 0 rgba(40, 54, 61, 0.18)',
boxSizing: 'border-box',
display: 'flex',
justifyContent: 'center',
diff --git a/src/components/CustomIconText/index.tsx b/src/components/CustomIconText/index.tsx
index 31fc13ba..5cbb94ab 100644
--- a/src/components/CustomIconText/index.tsx
+++ b/src/components/CustomIconText/index.tsx
@@ -1,5 +1,5 @@
import { Text } from '@gnosis.pm/safe-react-components'
-import React from 'react'
+import React, { ReactElement } from 'react'
import styled from 'styled-components'
const Wrapper = styled.div`
@@ -12,11 +12,11 @@ const Icon = styled.img`
margin-right: 9px;
`
-const CustomIconText = ({ iconUrl, text }: { iconUrl: string; text?: string }) => (
+type Props = { iconUrl: string | null | undefined; text?: string }
+
+export const CustomIconText = ({ iconUrl, text }: Props): ReactElement => (
-
+ {iconUrl && }
{text && {text} }
)
-
-export default CustomIconText
diff --git a/src/components/DecodeTxs/index.tsx b/src/components/DecodeTxs/index.tsx
index 8b242e99..703ef001 100644
--- a/src/components/DecodeTxs/index.tsx
+++ b/src/components/DecodeTxs/index.tsx
@@ -69,7 +69,7 @@ export const BasicTxInfo = ({
,
+ parameters: {
+ title: 'The title that will be displayed in the modal',
+ titleNote: 'An annotation for the title, like "1 of 2"',
+ onClose: 'Callback to be called when attempt to close the modal',
+ },
+ compositionElements: [
+ {
+ title: 'Modal.Header.Title',
+ component: {} ,
+ description: 'safe-react-component exposed with a few styles added to personalize the modal header',
+ },
+ ],
+ },
+ {
+ title: 'Modal.Header',
+ component: {} ,
+ parameters: {
+ children: 'whatever is required to be rendered in the footer. Usually buttons.',
+ noPadding: 'a flag that will set padding to 0 (zero) in case it is needed',
+ },
+ },
+ {
+ title: 'Modal.Footer',
+ component: {} ,
+ parameters: {
+ children: 'whatever is required to be rendered in the footer. Usually buttons.',
+ },
+ compositionElements: [
+ {
+ title: 'Modal.Footer.Buttons',
+ component: ,
+ description: 'standard two buttons wrapped implementation. One "Cancel" and one "Submit" button.',
+ },
+ ],
+ },
+ ],
+}
+
+const SimpleFormModal = ({ title, description, handleClose, handleSubmit, isOpen, children }) => (
+
+ {/* header */}
+
+ {title}
+
+
+
+ {() => (
+ <>
+ {/* body */}
+ {children}
+
+ {/* footer */}
+
+
+
+ >
+ )}
+
+
+)
+
+const Username = () => (
+
+
+ Username
+
+
+
+)
+
+export const FormModal = (): ReactElement => {
+ const [isOpen, setIsOpen] = useState(false)
+
+ const handleClose = () => {
+ setIsOpen(false)
+ console.log('modal closed')
+ }
+
+ const handleSubmit = (values) => {
+ alert(JSON.stringify(values, null, 2))
+ console.log('form submitted', values)
+ handleClose()
+ }
+
+ return (
+
+ setIsOpen(true)}>Open Modal
+ {/* Modal with Form */}
+
+ {/* Form Fields */}
+
+
+
+ )
+}
+
+export const RemoveSomething = (): ReactElement => {
+ const [isOpen, setIsOpen] = useState(false)
+ const title = 'Remove Something'
+
+ const handleClose = () => {
+ setIsOpen(false)
+ console.log('modal closed')
+ }
+
+ const handleSubmit = () => {
+ alert('Something was removed')
+ handleClose()
+ }
+
+ return (
+
+ setIsOpen(true)}>Open Modal
+ {/* Modal */}
+
+ {/* Header */}
+
+ {title}
+
+
+ {/* Body */}
+
+ You are about to remove something
+
+
+ {/* Footer */}
+
+
+
+
+
+ )
+}
diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx
index d9a2ea46..5a6dcc33 100644
--- a/src/components/Modal/index.tsx
+++ b/src/components/Modal/index.tsx
@@ -1,69 +1,267 @@
-import Modal from '@material-ui/core/Modal'
-import { makeStyles, createStyles } from '@material-ui/core/styles'
+import { Button, Icon, theme, Title as TitleSRC } from '@gnosis.pm/safe-react-components'
+import { ButtonProps as ButtonPropsMUI, Modal as ModalMUI } from '@material-ui/core'
import cn from 'classnames'
-import React, { ReactElement, ReactNode } from 'react'
+import React, { ReactElement, ReactNode, ReactNodeArray } from 'react'
+import styled from 'styled-components'
-import { sm } from 'src/theme/variables'
+type Theme = typeof theme
-const useStyles = makeStyles(
- createStyles({
- root: {
- alignItems: 'center',
- flexDirection: 'column',
- display: 'flex',
- overflowY: 'scroll',
- },
- paper: {
- position: 'relative',
- top: '68px',
- width: '500px',
- borderRadius: sm,
- backgroundColor: '#ffffff',
- boxShadow: '0 0 5px 0 rgba(74, 85, 121, 0.5)',
- '&:focus': {
- outline: 'none',
- },
- display: 'flex',
- flexDirection: 'column',
- },
- }),
-)
+const ModalStyled = styled(ModalMUI)`
+ & {
+ align-items: center;
+ flex-direction: column;
+ display: flex;
+ overflow-y: scroll;
+ }
+
+ .overlay {
+ background-color: rgba(232, 231, 230, 0.75) !important;
+ }
+
+ .paper {
+ position: relative;
+ top: 68px;
+ width: 500px;
+ border-radius: 8px;
+ background-color: #ffffff;
+ box-shadow: 1px 2px 10px 0 rgba(40, 54, 61, 0.18);
+ display: flex;
+ flex-direction: column;
+
+ &:focus {
+ outline: none;
+ }
+
+ // TODO: replace class-based styles by params
+ &.receive-modal {
+ height: auto;
+ max-width: calc(100% - 130px);
+ min-height: 544px;
+ overflow: hidden;
+ }
+
+ &.bigger-modal-window {
+ width: 775px;
+ height: auto;
+ }
+
+ &.smaller-modal-window {
+ height: auto;
+ }
+
+ &.modal {
+ height: auto;
+ max-width: calc(100% - 130px);
+ }
+ }
+`
interface GnoModalProps {
children: ReactNode
description: string
// type copied from Material-UI Modal's `close` prop
- handleClose?: {
- bivarianceHack(event: Record, reason: 'backdropClick' | 'escapeKeyDown'): void
- }['bivarianceHack']
- modalClassName?: string
+ handleClose?: (event: Record, reason: 'backdropClick' | 'escapeKeyDown') => void
open: boolean
paperClassName?: string
title: string
}
-const GnoModal = ({
- children,
- description,
- handleClose,
- modalClassName,
- open,
- paperClassName,
- title,
-}: GnoModalProps): ReactElement => {
- const classes = useStyles()
-
+const GnoModal = ({ children, description, handleClose, open, paperClassName, title }: GnoModalProps): ReactElement => {
return (
-
- {children}
-
+ {children}
+
)
}
export default GnoModal
+
+/*****************/
+/* Generic Modal */
+/*****************/
+
+/*** Header ***/
+const HeaderSection = styled.div`
+ display: flex;
+ padding: 24px 18px 24px 24px;
+ border-bottom: 2px solid ${({ theme }) => theme.colors.separator};
+
+ h5 {
+ color: ${({ theme }) => theme.colors.text};
+ }
+
+ .close-button {
+ align-self: flex-end;
+ background: none;
+ border: none;
+ padding: 5px;
+ width: 26px;
+ height: 26px;
+
+ span {
+ margin-right: 0;
+ }
+
+ :hover {
+ background: ${({ theme }) => theme.colors.separator};
+ border-radius: 16px;
+ cursor: pointer;
+ }
+ }
+`
+
+const TitleStyled = styled(TitleSRC)`
+ display: flex;
+ align-items: center;
+ flex-basis: 100%;
+
+ .image,
+ img {
+ width: 20px;
+ margin-right: 10px;
+ }
+
+ .note,
+ span {
+ margin-left: 12px;
+ }
+`
+
+interface TitleProps {
+ children: string | ReactNode
+ size?: keyof Theme['title']['size']
+ withoutMargin?: boolean
+ strong?: boolean
+}
+
+const Title = ({ children, ...props }: TitleProps): ReactElement => (
+
+ {children}
+
+)
+
+interface HeaderProps {
+ children?: ReactNode
+ onClose?: (event: any) => void
+}
+
+const Header = ({ children, onClose }: HeaderProps): ReactElement => {
+ return (
+
+ {children}
+
+ {onClose && (
+
+
+
+ )}
+
+ )
+}
+
+Header.Title = Title
+
+/*** Body ***/
+const BodySection = styled.div<{ withoutPadding: BodyProps['withoutPadding'] }>`
+ padding: ${({ withoutPadding }) => (withoutPadding ? 0 : '24px')};
+`
+
+interface BodyProps {
+ children: ReactNode | ReactNodeArray
+ withoutPadding?: boolean
+}
+
+const Body = ({ children, withoutPadding = false }: BodyProps): ReactElement => (
+
+ {children}
+
+)
+
+/*** Footer ***/
+const FooterSection = styled.div`
+ display: flex;
+ justify-content: center;
+ border-top: 2px solid ${({ theme }) => theme.colors.separator};
+ padding: 24px;
+`
+
+const ButtonStyled = styled(Button)`
+ &.MuiButtonBase-root {
+ margin: 0 10px;
+ }
+`
+
+type CustomButtonMUIProps = Omit & {
+ to?: string
+ component?: ReactNode
+}
+
+interface ButtonProps extends CustomButtonMUIProps {
+ text?: string
+ size?: keyof Theme['buttons']['size']
+ color?: 'primary' | 'secondary' | 'error'
+ variant?: 'bordered' | 'contained' | 'outlined'
+}
+
+interface ButtonsProps {
+ cancelButtonProps?: ButtonProps
+ confirmButtonProps?: ButtonProps
+}
+
+const Buttons = ({ cancelButtonProps = {}, confirmButtonProps = {} }: ButtonsProps): ReactElement => {
+ const { text: cancelText = 'Cancel' } = cancelButtonProps
+ const { text: confirmText = 'Submit' } = confirmButtonProps
+
+ return (
+ <>
+
+ {cancelText}
+
+
+ {confirmText}
+
+ >
+ )
+}
+
+interface FooterProps {
+ children: ReactNode | ReactNodeArray
+}
+
+const Footer = ({ children }: FooterProps): ReactElement => (
+ {children}
+)
+
+Footer.Buttons = Buttons
+
+interface ModalProps {
+ children: ReactNode
+ description: string
+ handleClose: () => void
+ open: boolean
+ title: string
+}
+
+export const Modal = ({ children, ...props }: ModalProps): ReactElement => {
+ return (
+
+ {children}
+
+ )
+}
+
+Modal.Header = Header
+Modal.Body = Body
+Modal.Footer = Footer
diff --git a/src/components/SafeListSidebar/SafeList/AddressWrapper.tsx b/src/components/SafeListSidebar/SafeList/AddressWrapper.tsx
index f21874aa..0be0668d 100644
--- a/src/components/SafeListSidebar/SafeList/AddressWrapper.tsx
+++ b/src/components/SafeListSidebar/SafeList/AddressWrapper.tsx
@@ -62,7 +62,7 @@ export const AddressWrapper = (props: Props): React.ReactElement => {
return (
-
+
{`${formatAmount(safe.ethBalance)} ${nativeCoin.name}`}
diff --git a/src/components/TransactionFailText/index.tsx b/src/components/TransactionFailText/index.tsx
index d61664cc..25c6b56b 100644
--- a/src/components/TransactionFailText/index.tsx
+++ b/src/components/TransactionFailText/index.tsx
@@ -41,7 +41,7 @@ export const TransactionFailText = ({
if (isExecution) {
errorMessage =
threshold && threshold > 1
- ? `To save gas costs, cancel this transaction`
+ ? `To save gas costs, reject this transaction`
: `To save gas costs, avoid executing the transaction.`
}
diff --git a/src/config/networks/rinkeby.ts b/src/config/networks/rinkeby.ts
index 3ce8179f..fddbf2af 100644
--- a/src/config/networks/rinkeby.ts
+++ b/src/config/networks/rinkeby.ts
@@ -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',
diff --git a/src/config/networks/volta.ts b/src/config/networks/volta.ts
index 154ba9e8..5450752b 100644
--- a/src/config/networks/volta.ts
+++ b/src/config/networks/volta.ts
@@ -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,
},
diff --git a/src/logic/contracts/safeContracts.ts b/src/logic/contracts/safeContracts.ts
index 17345da0..8001d894 100644
--- a/src/logic/contracts/safeContracts.ts
+++ b/src/logic/contracts/safeContracts.ts
@@ -52,17 +52,6 @@ const getProxyFactoryContract = (web3: Web3, networkId: ETHEREUM_NETWORK): Gnosi
return (new web3.eth.Contract(ProxyFactorySol.abi as AbiItem[], contractAddress) as unknown) as GnosisSafeProxyFactory
}
-/**
- * Creates a Contract instance of the GnosisSafeProxyFactory contract
- */
-export const getSpendingLimitContract = () => {
- const web3 = getWeb3()
- return (new web3.eth.Contract(
- SpendingLimitModule.abi as AbiItem[],
- SPENDING_LIMIT_MODULE_ADDRESS,
- ) as unknown) as AllowanceModule
-}
-
export const getMasterCopyAddressFromProxyAddress = async (proxyAddress: string): Promise
=> {
const res = await getSafeInfo(proxyAddress)
const masterCopyAddress = (res as SafeInfo)?.masterCopy
@@ -115,7 +104,7 @@ export const estimateGasForDeployingSafe = async (
userAccount: string,
safeCreationSalt: number,
) => {
- const gnosisSafeData = await safeMaster.methods
+ const gnosisSafeData = safeMaster.methods
.setup(
safeAccounts,
numConfirmations,
@@ -134,10 +123,22 @@ export const estimateGasForDeployingSafe = async (
data: proxyFactoryData,
from: userAccount,
to: proxyFactoryMaster.options.address,
- })
+ }).then((value) => value * 2)
}
export const getGnosisSafeInstanceAt = (safeAddress: string): GnosisSafe => {
const web3 = getWeb3()
return (new web3.eth.Contract(GnosisSafeSol.abi as AbiItem[], safeAddress) as unknown) as GnosisSafe
}
+
+/**
+ * Creates a Contract instance of the SpendingLimitModule contract
+ */
+export const getSpendingLimitContract = () => {
+ const web3 = getWeb3()
+
+ return (new web3.eth.Contract(
+ SpendingLimitModule.abi as AbiItem[],
+ SPENDING_LIMIT_MODULE_ADDRESS,
+ ) as unknown) as AllowanceModule
+}
diff --git a/src/logic/hooks/useEstimateTransactionGas.tsx b/src/logic/hooks/useEstimateTransactionGas.tsx
index 567d7659..6e9799ae 100644
--- a/src/logic/hooks/useEstimateTransactionGas.tsx
+++ b/src/logic/hooks/useEstimateTransactionGas.tsx
@@ -1,17 +1,16 @@
+import { List } from 'immutable'
import { useEffect, useState } from 'react'
+import { useSelector } from 'react-redux'
+import { getNetworkInfo } from 'src/config'
import {
- estimateGasForTransactionApproval,
- estimateGasForTransactionCreation,
- estimateGasForTransactionExecution,
- getFixedGasCosts,
- SAFE_TX_GAS_DATA_COST,
+ checkTransactionExecution,
+ estimateSafeTxGas,
+ estimateTransactionGasLimit,
} from 'src/logic/safe/transactions/gas'
import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
import { calculateGasPrice } from 'src/logic/wallets/ethTransactions'
-import { getNetworkInfo } from 'src/config'
-import { useSelector } from 'react-redux'
import {
safeCurrentVersionSelector,
safeParamAddressFromStateSelector,
@@ -21,7 +20,6 @@ import { CALL } from 'src/logic/safe/transactions'
import { web3ReadOnly as web3 } from 'src/logic/wallets/getWeb3'
import { providerSelector } from 'src/logic/wallets/store/selectors'
-import { List } from 'immutable'
import { Confirmation } from 'src/logic/safe/store/models/types/confirmation'
import { checkIfOffChainSignatureIsPossible } from 'src/logic/safe/safeTxSigner'
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
@@ -64,90 +62,16 @@ export const checkIfTxIsApproveAndExecution = (
return txConfirmations + 1 === threshold || sameString(txType, 'spendingLimit')
}
+ if (threshold === 1) {
+ return true
+ }
+
return false
}
export const checkIfTxIsCreation = (txConfirmations: number, txType?: string): boolean =>
txConfirmations === 0 && !sameString(txType, 'spendingLimit')
-type TransactionEstimationProps = {
- txData: string
- safeAddress: string
- txRecipient: string
- txConfirmations?: List
- txAmount?: string
- operation?: number
- gasPrice?: string
- gasToken?: string
- refundReceiver?: string // Address of receiver of gas payment (or 0 if tx.origin).
- safeTxGas?: number
- from?: string
- isExecution: boolean
- isCreation: boolean
- isOffChainSignature?: boolean
- approvalAndExecution?: boolean
-}
-
-const estimateTransactionGas = async ({
- txData,
- safeAddress,
- txRecipient,
- txConfirmations,
- txAmount,
- operation,
- gasPrice,
- gasToken,
- refundReceiver,
- safeTxGas,
- from,
- isExecution,
- isCreation,
- isOffChainSignature = false,
- approvalAndExecution,
-}: TransactionEstimationProps): Promise => {
- if (isCreation) {
- return estimateGasForTransactionCreation(
- safeAddress,
- txData,
- txRecipient,
- txAmount || '0',
- operation || CALL,
- safeTxGas,
- )
- }
-
- if (!from) {
- throw new Error('No from provided for approving or execute transaction')
- }
-
- if (isExecution) {
- return estimateGasForTransactionExecution({
- safeAddress,
- txRecipient,
- txConfirmations,
- txAmount: txAmount || '0',
- txData,
- operation: operation || CALL,
- from,
- gasPrice: gasPrice || '0',
- gasToken: gasToken || ZERO_ADDRESS,
- refundReceiver: refundReceiver || ZERO_ADDRESS,
- safeTxGas: safeTxGas || 0,
- approvalAndExecution,
- })
- }
-
- return estimateGasForTransactionApproval({
- safeAddress,
- operation: operation || CALL,
- txData,
- txAmount: txAmount || '0',
- txRecipient,
- from,
- isOffChainSignature,
- })
-}
-
type UseEstimateTransactionGasProps = {
txData: string
txRecipient: string
@@ -158,6 +82,7 @@ type UseEstimateTransactionGasProps = {
safeTxGas?: number
txType?: string
manualGasPrice?: string
+ manualGasLimit?: string
}
export type TransactionGasEstimationResult = {
@@ -183,6 +108,7 @@ export const useEstimateTransactionGas = ({
safeTxGas,
txType,
manualGasPrice,
+ manualGasLimit,
}: UseEstimateTransactionGasProps): TransactionGasEstimationResult => {
const [gasEstimation, setGasEstimation] = useState({
txEstimationExecutionStatus: EstimationStatus.LOADING,
@@ -208,51 +134,79 @@ export const useEstimateTransactionGas = ({
return
}
- const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations?.size, txType)
const isCreation = checkIfTxIsCreation(txConfirmations?.size || 0, txType)
+ const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations?.size, txType)
const approvalAndExecution = checkIfTxIsApproveAndExecution(
Number(threshold),
txConfirmations?.size || 0,
txType,
preApprovingOwner,
)
-
- const fixedGasCosts = getFixedGasCosts(Number(threshold))
const isOffChainSignature = checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion)
try {
- const gasEstimation = await estimateTransactionGas({
- safeAddress,
- txRecipient,
- txData,
- txAmount,
- txConfirmations,
- isExecution,
- isCreation,
- isOffChainSignature,
- operation,
- from,
- safeTxGas,
- approvalAndExecution,
- })
+ let safeTxGasEstimation = safeTxGas || 0
+ let ethGasLimitEstimation = 0
+ let transactionCallSuccess = true
+ let txEstimationExecutionStatus = EstimationStatus.LOADING
+
+ if (isCreation) {
+ safeTxGasEstimation = await estimateSafeTxGas({
+ safeAddress,
+ txData,
+ txRecipient,
+ txAmount: txAmount || '0',
+ operation: operation || CALL,
+ safeTxGas,
+ })
+ }
+ if (isExecution || approvalAndExecution) {
+ ethGasLimitEstimation = await estimateTransactionGasLimit({
+ safeAddress,
+ txRecipient,
+ txData,
+ txAmount: txAmount || '0',
+ txConfirmations,
+ isExecution,
+ isOffChainSignature,
+ operation: operation || CALL,
+ from,
+ safeTxGas: safeTxGasEstimation,
+ approvalAndExecution,
+ })
+ }
- const totalGasEstimation = (gasEstimation + fixedGasCosts) * 2
const gasPrice = manualGasPrice ? web3.utils.toWei(manualGasPrice, 'gwei') : await calculateGasPrice()
const gasPriceFormatted = web3.utils.fromWei(gasPrice, 'gwei')
- const estimatedGasCosts = totalGasEstimation * parseInt(gasPrice, 10)
+ const estimatedGasCosts = ethGasLimitEstimation * parseInt(gasPrice, 10)
const gasCost = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals)
const gasCostFormatted = formatAmount(gasCost)
- const gasLimit = totalGasEstimation.toString()
+ const gasLimit = manualGasLimit || ethGasLimitEstimation.toString()
- let txEstimationExecutionStatus = EstimationStatus.SUCCESS
-
- if (gasEstimation <= 0) {
- txEstimationExecutionStatus = isOffChainSignature ? EstimationStatus.SUCCESS : EstimationStatus.FAILURE
+ txEstimationExecutionStatus = EstimationStatus.SUCCESS
+ if (isExecution) {
+ transactionCallSuccess = await checkTransactionExecution({
+ safeAddress,
+ txRecipient,
+ txData,
+ txAmount: txAmount || '0',
+ txConfirmations,
+ operation: operation || CALL,
+ from,
+ gasPrice: '0',
+ gasToken: ZERO_ADDRESS,
+ gasLimit,
+ refundReceiver: ZERO_ADDRESS,
+ safeTxGas: safeTxGasEstimation,
+ approvalAndExecution,
+ })
}
+ txEstimationExecutionStatus = transactionCallSuccess ? EstimationStatus.SUCCESS : EstimationStatus.FAILURE
+
setGasEstimation({
txEstimationExecutionStatus,
- gasEstimation,
+ gasEstimation: safeTxGasEstimation,
gasCost,
gasCostFormatted,
gasPrice,
@@ -264,15 +218,12 @@ export const useEstimateTransactionGas = ({
})
} catch (error) {
console.warn(error.message)
- // We put a fixed the amount of gas to let the user try to execute the tx, but it's not accurate so it will probably fail
- const gasEstimation = fixedGasCosts + SAFE_TX_GAS_DATA_COST
- const gasCost = fromTokenUnit(gasEstimation, nativeCoin.decimals)
- const gasCostFormatted = formatAmount(gasCost)
+ // If safeTxGas estimation fail we set this value to 0 (so up to all gasLimit can be used)
setGasEstimation({
txEstimationExecutionStatus: EstimationStatus.FAILURE,
- gasEstimation,
- gasCost,
- gasCostFormatted,
+ gasEstimation: 0,
+ gasCost: '0',
+ gasCostFormatted: '< 0.001',
gasPrice: '1',
gasPriceFormatted: '1',
gasLimit: '0',
@@ -301,6 +252,7 @@ export const useEstimateTransactionGas = ({
txType,
providerName,
manualGasPrice,
+ manualGasLimit,
])
return gasEstimation
diff --git a/src/logic/safe/api/fetchSafeTxGasEstimation.ts b/src/logic/safe/api/fetchSafeTxGasEstimation.ts
new file mode 100644
index 00000000..b0a4759e
--- /dev/null
+++ b/src/logic/safe/api/fetchSafeTxGasEstimation.ts
@@ -0,0 +1,25 @@
+import axios from 'axios'
+
+import { getSafeServiceBaseUrl } from 'src/config'
+import { checksumAddress } from 'src/utils/checksumAddress'
+
+export type GasEstimationResponse = {
+ safeTxGas: string
+}
+
+type FetchSafeTxGasEstimationProps = {
+ safeAddress: string
+ to: string
+ value: string
+ data?: string
+ operation: number
+}
+
+export const fetchSafeTxGasEstimation = async ({
+ safeAddress,
+ ...body
+}: FetchSafeTxGasEstimationProps): Promise => {
+ const url = `${getSafeServiceBaseUrl(checksumAddress(safeAddress))}/multisig-transactions/estimations/`
+
+ return axios.post(url, body).then(({ data }) => data.safeTxGas)
+}
diff --git a/src/logic/safe/api/fetchTokenCurrenciesBalances.ts b/src/logic/safe/api/fetchTokenCurrenciesBalances.ts
index 2fcc4a93..d1185a3d 100644
--- a/src/logic/safe/api/fetchTokenCurrenciesBalances.ts
+++ b/src/logic/safe/api/fetchTokenCurrenciesBalances.ts
@@ -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)
}
diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts
index b2e6b347..1b11c715 100644
--- a/src/logic/safe/store/actions/createTransaction.ts
+++ b/src/logic/safe/store/actions/createTransaction.ts
@@ -11,7 +11,7 @@ import {
saveTxToHistory,
tryOffchainSigning,
} from 'src/logic/safe/transactions'
-import { estimateGasForTransactionCreation } from 'src/logic/safe/transactions/gas'
+import { estimateSafeTxGas } from 'src/logic/safe/transactions/gas'
import * as aboutToExecuteTx from 'src/logic/safe/utils/aboutToExecuteTx'
import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion'
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
@@ -78,7 +78,7 @@ export const createTransaction = (
if (!ready) return
const { account: from, hardwareWallet, smartContractWallet } = providerSelector(state)
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const lastTx = await getLastTx(safeAddress)
const nextNonce = await getNewTxNonce(lastTx, safeInstance)
const nonce = txNonce !== undefined ? txNonce.toString() : nextNonce
@@ -88,7 +88,7 @@ export const createTransaction = (
let safeTxGas = safeTxGasArg || 0
try {
if (safeTxGasArg === undefined) {
- safeTxGas = await estimateGasForTransactionCreation(safeAddress, txData, to, valueInWei, operation)
+ safeTxGas = await estimateSafeTxGas({ safeAddress, txData, txRecipient: to, txAmount: valueInWei, operation })
}
} catch (error) {
safeTxGas = safeTxGasArg || 0
diff --git a/src/logic/safe/store/actions/processTransaction.ts b/src/logic/safe/store/actions/processTransaction.ts
index 28934b23..709805ef 100644
--- a/src/logic/safe/store/actions/processTransaction.ts
+++ b/src/logic/safe/store/actions/processTransaction.ts
@@ -73,7 +73,7 @@ export const processTransaction = ({
const state = getState()
const { account: from, hardwareWallet, smartContractWallet } = providerSelector(state)
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const lastTx = await getLastTx(safeAddress)
const nonce = await getNewTxNonce(lastTx, safeInstance)
diff --git a/src/logic/safe/store/models/types/gateway.d.ts b/src/logic/safe/store/models/types/gateway.d.ts
index c462971a..1212328b 100644
--- a/src/logic/safe/store/models/types/gateway.d.ts
+++ b/src/logic/safe/store/models/types/gateway.d.ts
@@ -133,7 +133,7 @@ type BaseCustom = {
dataSize: string
value: string
isCancellation: boolean
- toInfo: AddressInfo
+ toInfo?: AddressInfo
}
type Custom = BaseCustom & {
@@ -236,7 +236,7 @@ type MultiSigExecutionDetails = {
type DetailedExecutionInfo = ModuleExecutionDetails | MultiSigExecutionDetails
type ExpandedTxDetails = {
- executedAt: number
+ executedAt: number | null
txStatus: TransactionStatus
txInfo: TransactionInfo
txData: TransactionData | null
diff --git a/src/logic/safe/transactions/gas.ts b/src/logic/safe/transactions/gas.ts
index 9c79dd1d..94773b2b 100644
--- a/src/logic/safe/transactions/gas.ts
+++ b/src/logic/safe/transactions/gas.ts
@@ -1,42 +1,18 @@
+import axios from 'axios'
import { BigNumber } from 'bignumber.js'
+import { List } from 'immutable'
+import { getRpcServiceUrl, usesInfuraRPC } from 'src/config'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
import { calculateGasOf, EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
import { getWeb3, web3ReadOnly } from 'src/logic/wallets/getWeb3'
import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
import { generateSignaturesFromTxConfirmations } from 'src/logic/safe/safeTxSigner'
-import { List } from 'immutable'
+import { fetchSafeTxGasEstimation } from 'src/logic/safe/api/fetchSafeTxGasEstimation'
import { Confirmation } from 'src/logic/safe/store/models/types/confirmation'
-import axios from 'axios'
-import { getRpcServiceUrl, usesInfuraRPC } from 'src/config'
+import { checksumAddress } from 'src/utils/checksumAddress'
import { sameString } from 'src/utils/strings'
-// 21000 - additional gas costs (e.g. base tx costs, transfer costs)
-export const MINIMUM_TRANSACTION_GAS = 21000
-// Estimation of gas required for each signature (aproximately 7800, roundup to 8000)
-export const GAS_REQUIRED_PER_SIGNATURE = 8000
-// We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
-// We also add 3k pay when processing safeTxGas value. We don't know this value when creating the transaction
-// Hex values different than 0 has some gas cost
-export const SAFE_TX_GAS_DATA_COST = 6000
-
-// Receives the response data of the safe method requiredTxGas() and parses it to get the gas amount
-const parseRequiredTxGasResponse = (data: string): number => {
- const reducer = (accumulator, currentValue) => {
- if (currentValue === EMPTY_DATA) {
- return accumulator + 0
- }
-
- if (currentValue === '00') {
- return accumulator + 4
- }
-
- return accumulator + 16
- }
-
- return data.match(/.{2}/g)?.reduce(reducer, 0)
-}
-
interface ErrorDataJson extends JSON {
originalError?: {
data?: string
@@ -178,94 +154,113 @@ export const getGasEstimationTxResponse = async (txConfig: {
return estimateGasWithWeb3Provider(txConfig)
}
-const calculateMinimumGasForTransaction = async (
- additionalGasBatches: number[],
- safeAddress: string,
- estimateData: string,
- safeTxGasEstimation: number,
- fixedGasCosts: number,
-): Promise => {
- for (const additionalGas of additionalGasBatches) {
- const batchedSafeTxGas = safeTxGasEstimation + additionalGas
- // To simulate if safeTxGas is enough we need to send an estimated gasLimit that will be the sum
- // of the safeTxGasEstimation and fixedGas costs for ethereum transaction
- const gasLimit = batchedSafeTxGas + fixedGasCosts
- console.info(`Estimating safeTxGas with gas amount: ${batchedSafeTxGas}`)
- try {
- const estimation = await getGasEstimationTxResponse({
- to: safeAddress,
- from: safeAddress,
- data: estimateData,
- gasPrice: 0,
- gas: gasLimit,
- })
- if (estimation > 0) {
- console.info(`Gas estimation successfully finished with gas amount: ${batchedSafeTxGas}`)
- return batchedSafeTxGas
- }
- } catch (error) {
- console.log(`Error trying to estimate gas with amount: ${batchedSafeTxGas}`)
- }
- }
-
- return 0
+type SafeTxGasEstimationProps = {
+ safeAddress: string
+ txData: string
+ txRecipient: string
+ txAmount: string
+ operation: number
+ safeTxGas?: number
}
-export const getFixedGasCosts = (threshold: number): number => {
- // There are some minimum gas costs to execute an Ethereum transaction
- // We add this fixed network minimum gas, the gas required to check each signature
- return MINIMUM_TRANSACTION_GAS + (threshold || 1) * GAS_REQUIRED_PER_SIGNATURE
-}
-
-export const estimateGasForTransactionCreation = async (
- safeAddress: string,
- data: string,
- to: string,
- valueInWei: string,
- operation: number,
- safeTxGas?: number,
-): Promise => {
+export const estimateSafeTxGas = async ({
+ safeAddress,
+ txData,
+ txRecipient,
+ txAmount,
+ operation,
+ safeTxGas,
+}: SafeTxGasEstimationProps): Promise => {
try {
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
-
- const estimateData = safeInstance.methods.requiredTxGas(to, valueInWei, data, operation).encodeABI()
- const threshold = await safeInstance.methods.getThreshold().call()
-
- const fixedGasCosts = getFixedGasCosts(Number(threshold))
-
- const gasEstimationResponse = await getGasEstimationTxResponse({
- to: safeAddress,
- from: safeAddress,
- data: estimateData,
- gas: safeTxGas ? safeTxGas + fixedGasCosts : undefined,
+ const safeTxGasEstimation = await fetchSafeTxGasEstimation({
+ safeAddress,
+ to: checksumAddress(txRecipient),
+ value: txAmount,
+ data: txData,
+ operation,
})
+ console.log('Backend gas estimation', safeTxGasEstimation)
+
if (safeTxGas) {
- // When we execute we get a more precise estimate value, we log for debug purposes
- console.info('This is the smart contract minimum expected safeTxGas', gasEstimationResponse)
+ // If safeTxGas was already defined we leave it but log our estimation for debug purposes
+ console.info('This is the smart contract minimum expected safeTxGas', safeTxGasEstimation)
// We return set safeTxGas
return safeTxGas
}
- const dataGasEstimation = parseRequiredTxGasResponse(estimateData)
- // Adding this values we should get the full safeTxGas value
- const safeTxGasEstimation = gasEstimationResponse + dataGasEstimation + SAFE_TX_GAS_DATA_COST
- // We will add gas batches in case is not enough
- const additionalGasBatches = [0, 10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000, 5120000]
-
- return await calculateMinimumGasForTransaction(
- additionalGasBatches,
- safeAddress,
- estimateData,
- safeTxGasEstimation,
- fixedGasCosts,
- )
+ return parseInt(safeTxGasEstimation)
} catch (error) {
console.info('Error calculating tx gas estimation', error.message)
throw error
}
}
+type TransactionEstimationProps = {
+ txData: string
+ safeAddress: string
+ txRecipient: string
+ txConfirmations?: List
+ txAmount: string
+ operation: number
+ gasPrice?: string
+ gasToken?: string
+ refundReceiver?: string // Address of receiver of gas payment (or 0 if tx.origin).
+ safeTxGas?: number
+ from?: string
+ isExecution: boolean
+ isOffChainSignature?: boolean
+ approvalAndExecution?: boolean
+}
+
+export const estimateTransactionGasLimit = async ({
+ txData,
+ safeAddress,
+ txRecipient,
+ txConfirmations,
+ txAmount,
+ operation,
+ gasPrice,
+ gasToken,
+ refundReceiver,
+ safeTxGas,
+ from,
+ isExecution,
+ isOffChainSignature = false,
+ approvalAndExecution,
+}: TransactionEstimationProps): Promise => {
+ if (!from) {
+ throw new Error('No from provided for approving or execute transaction')
+ }
+
+ if (isExecution) {
+ return estimateGasForTransactionExecution({
+ safeAddress,
+ txRecipient,
+ txConfirmations,
+ txAmount,
+ txData,
+ operation,
+ from,
+ gasPrice: gasPrice || '0',
+ gasToken: gasToken || ZERO_ADDRESS,
+ refundReceiver: refundReceiver || ZERO_ADDRESS,
+ safeTxGas: safeTxGas || 0,
+ approvalAndExecution,
+ })
+ }
+
+ return estimateGasForTransactionApproval({
+ safeAddress,
+ operation,
+ txData,
+ txAmount,
+ txRecipient,
+ from,
+ isOffChainSignature,
+ })
+}
+
type TransactionExecutionEstimationProps = {
txData: string
safeAddress: string
@@ -275,65 +270,75 @@ type TransactionExecutionEstimationProps = {
operation: number
gasPrice: string
gasToken: string
+ gasLimit?: string
refundReceiver: string // Address of receiver of gas payment (or 0 if tx.origin).
safeTxGas: number
from: string
approvalAndExecution?: boolean
}
-export const estimateGasForTransactionExecution = async ({
+const estimateGasForTransactionExecution = async ({
safeAddress,
txRecipient,
txConfirmations,
txAmount,
txData,
operation,
+ from,
gasPrice,
gasToken,
refundReceiver,
safeTxGas,
approvalAndExecution,
}: TransactionExecutionEstimationProps): Promise => {
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
- try {
- let gasEstimation
- // If safeTxGas === 0 we still have to estimate the gas limit to execute the transaction so we need to get an estimation
- if (approvalAndExecution || safeTxGas === 0) {
- console.info(`Estimating transaction necessary gas...`)
- // @todo (agustin) once we solve the problem with the preApprovingOwner, we need to use the method bellow (execTransaction) with sigs = generateSignaturesFromTxConfirmations(txConfirmations,from)
- gasEstimation = await estimateGasForTransactionCreation(
- safeAddress,
- txData,
- txRecipient,
- txAmount,
- operation,
- safeTxGas,
- )
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
+ // If it's approvalAndExecution we have to add a preapproved signature else we have all signatures
+ const sigs = generateSignaturesFromTxConfirmations(txConfirmations, approvalAndExecution ? from : undefined)
- if (approvalAndExecution) {
- // If it's approve and execute we don't have all the signatures to do a complete simulation, we return the gas estimation
- console.info(`Gas estimation successfully finished with gas amount: ${gasEstimation}`)
- return gasEstimation
- }
- }
- // If we have all signatures we can do a call to ensure the transaction will be successful or fail
- const sigs = generateSignaturesFromTxConfirmations(txConfirmations)
- console.info(`Check transaction success with gas amount: ${safeTxGas}...`)
- await safeInstance.methods
- .execTransaction(txRecipient, txAmount, txData, operation, safeTxGas, 0, gasPrice, gasToken, refundReceiver, sigs)
- .call()
- console.info(`Gas estimation successfully finished with gas amount: ${safeTxGas}`)
- return safeTxGas || gasEstimation
- } catch (error) {
- throw new Error(`Gas estimation failed with gas amount: ${safeTxGas}`)
- }
+ const estimationData = safeInstance.methods
+ .execTransaction(txRecipient, txAmount, txData, operation, safeTxGas, 0, gasPrice, gasToken, refundReceiver, sigs)
+ .encodeABI()
+
+ return calculateGasOf({
+ data: estimationData,
+ from,
+ to: safeAddress,
+ })
+}
+
+export const checkTransactionExecution = async ({
+ safeAddress,
+ txRecipient,
+ txConfirmations,
+ txAmount,
+ txData,
+ operation,
+ from,
+ gasPrice,
+ gasToken,
+ gasLimit,
+ refundReceiver,
+ safeTxGas,
+ approvalAndExecution,
+}: TransactionExecutionEstimationProps): Promise => {
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
+ // If it's approvalAndExecution we have to add a preapproved signature else we have all signatures
+ const sigs = generateSignaturesFromTxConfirmations(txConfirmations, approvalAndExecution ? from : undefined)
+
+ return safeInstance.methods
+ .execTransaction(txRecipient, txAmount, txData, operation, safeTxGas, 0, gasPrice, gasToken, refundReceiver, sigs)
+ .call({
+ from,
+ gas: gasLimit,
+ })
+ .catch(() => false)
}
type TransactionApprovalEstimationProps = {
- txData: string
safeAddress: string
txRecipient: string
txAmount: string
+ txData: string
operation: number
from: string
isOffChainSignature: boolean
@@ -352,7 +357,7 @@ export const estimateGasForTransactionApproval = async ({
return 0
}
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const nonce = await safeInstance.methods.nonce().call()
const txHash = await safeInstance.methods
@@ -360,7 +365,7 @@ export const estimateGasForTransactionApproval = async ({
.call({
from,
})
- const approveTransactionTxData = await safeInstance.methods.approveHash(txHash).encodeABI()
+ const approveTransactionTxData = safeInstance.methods.approveHash(txHash).encodeABI()
return calculateGasOf({
data: approveTransactionTxData,
from,
diff --git a/src/logic/safe/utils/upgradeSafe.ts b/src/logic/safe/utils/upgradeSafe.ts
index 53caa747..3bbe6336 100644
--- a/src/logic/safe/utils/upgradeSafe.ts
+++ b/src/logic/safe/utils/upgradeSafe.ts
@@ -49,7 +49,7 @@ export const getEncodedMultiSendCallData = (txs: MultiSendTx[], web3: Web3): str
}
export const getUpgradeSafeTransactionHash = async (safeAddress: string): Promise => {
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const fallbackHandlerTxData = safeInstance.methods.setFallbackHandler(DEFAULT_FALLBACK_HANDLER_ADDRESS).encodeABI()
const updateSafeTxData = safeInstance.methods.changeMasterCopy(SAFE_MASTER_COPY_ADDRESS).encodeABI()
const txs = [
diff --git a/src/logic/wallets/ethTransactions.ts b/src/logic/wallets/ethTransactions.ts
index bfedd1a0..bf1fd7b3 100644
--- a/src/logic/wallets/ethTransactions.ts
+++ b/src/logic/wallets/ethTransactions.ts
@@ -6,7 +6,7 @@ import { getGasPrice, getGasPriceOracle } from 'src/config'
export const EMPTY_DATA = '0x'
-export const checkReceiptStatus = async (hash) => {
+export const checkReceiptStatus = async (hash: string): Promise => {
if (!hash) {
return Promise.reject(new Error('No valid Tx hash to get receipt from'))
}
@@ -27,10 +27,6 @@ export const checkReceiptStatus = async (hash) => {
}
export const calculateGasPrice = async (): Promise => {
- if (process.env.NODE_ENV === 'test') {
- return '20000000000'
- }
-
const gasPrice = getGasPrice()
const gasPriceOracle = getGasPriceOracle()
@@ -61,7 +57,7 @@ export const calculateGasOf = async (txConfig: {
try {
const gas = await web3.eth.estimateGas(txConfig)
- return gas * 2
+ return gas
} catch (err) {
return Promise.reject(err)
}
diff --git a/src/routes/load/components/OwnerList/index.tsx b/src/routes/load/components/OwnerList/index.tsx
index d6f6fc74..7888d8d5 100644
--- a/src/routes/load/components/OwnerList/index.tsx
+++ b/src/routes/load/components/OwnerList/index.tsx
@@ -55,7 +55,7 @@ const OwnerListComponent = (props) => {
const fetchSafe = async () => {
const safeAddress = values[FIELD_LOAD_ADDRESS]
- const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
+ const gnosisSafe = getGnosisSafeInstanceAt(safeAddress)
const safeOwners = await gnosisSafe.methods.getOwners().call()
const threshold = await gnosisSafe.methods.getThreshold().call()
diff --git a/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx b/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx
index bb7aa3e8..cc37955f 100644
--- a/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx
+++ b/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx
@@ -67,7 +67,7 @@ export const CreateEditEntryModal = ({
description={isNew ? 'Create new addressBook entry' : 'Edit addressBook entry'}
handleClose={onClose}
open={isOpen}
- paperClassName={classes.smallerModalWindow}
+ paperClassName="smaller-modal-window"
title={isNew ? 'Create new entry' : 'Edit entry'}
>
diff --git a/src/routes/safe/components/AddressBook/CreateEditEntryModal/style.ts b/src/routes/safe/components/AddressBook/CreateEditEntryModal/style.ts
index 839697a1..8aca604c 100644
--- a/src/routes/safe/components/AddressBook/CreateEditEntryModal/style.ts
+++ b/src/routes/safe/components/AddressBook/CreateEditEntryModal/style.ts
@@ -23,8 +23,5 @@ export const useStyles = makeStyles(
height: '84px',
justifyContent: 'center',
},
- smallerModalWindow: {
- height: 'auto',
- },
}),
)
diff --git a/src/routes/safe/components/AddressBook/DeleteEntryModal/index.tsx b/src/routes/safe/components/AddressBook/DeleteEntryModal/index.tsx
index 9a1432a7..91390839 100644
--- a/src/routes/safe/components/AddressBook/DeleteEntryModal/index.tsx
+++ b/src/routes/safe/components/AddressBook/DeleteEntryModal/index.tsx
@@ -25,7 +25,7 @@ const DeleteEntryModalComponent = ({ classes, deleteEntryModalHandler, entryToDe
description="Delete entry"
handleClose={onClose}
open={isOpen}
- paperClassName={classes.smallerModalWindow}
+ paperClassName="smaller-modal-window"
title="Delete entry"
>
diff --git a/src/routes/safe/components/AddressBook/DeleteEntryModal/style.ts b/src/routes/safe/components/AddressBook/DeleteEntryModal/style.ts
index 3ea5fab9..4952235f 100644
--- a/src/routes/safe/components/AddressBook/DeleteEntryModal/style.ts
+++ b/src/routes/safe/components/AddressBook/DeleteEntryModal/style.ts
@@ -27,7 +27,4 @@ export const styles = () => ({
buttonCancel: {
color: '#008c73',
},
- smallerModalWindow: {
- height: 'auto',
- },
})
diff --git a/src/routes/safe/components/AddressBook/index.tsx b/src/routes/safe/components/AddressBook/index.tsx
index 7460f663..38570218 100644
--- a/src/routes/safe/components/AddressBook/index.tsx
+++ b/src/routes/safe/components/AddressBook/index.tsx
@@ -11,7 +11,6 @@ import { styles } from './style'
import Table from 'src/components/Table'
import { cellWidth } from 'src/components/Table/TableHead'
import Block from 'src/components/layout/Block'
-import Button from 'src/components/layout/Button'
import ButtonLink from 'src/components/layout/ButtonLink'
import Col from 'src/components/layout/Col'
import Img from 'src/components/layout/Img'
@@ -40,7 +39,19 @@ import { addressBookQueryParamsSelector, safesListSelector } from 'src/logic/saf
import { checksumAddress } from 'src/utils/checksumAddress'
import { grantedSelector } from 'src/routes/safe/container/selector'
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
+import { FixedIcon, Text, Button } from '@gnosis.pm/safe-react-components'
+import styled from 'styled-components'
+const StyledButton = styled(Button)`
+ &&.MuiButton-root {
+ margin: 4px 12px 4px 0px;
+ padding: 0 12px;
+ min-width: auto;
+ }
+ svg {
+ margin: 0 6px 0 0;
+ }
+`
const useStyles = makeStyles(styles)
interface AddressBookSelectedEntry extends AddressBookEntry {
@@ -202,19 +213,21 @@ const AddressBookTable = (): ReactElement => {
testId={REMOVE_ENTRY_BUTTON}
/>
{granted ? (
- {
setSelectedEntry({ entry: row })
setSendFundsModalOpen(true)
}}
- size="small"
- testId={SEND_ENTRY_BUTTON}
+ size="md"
variant="contained"
+ data-testid={SEND_ENTRY_BUTTON}
>
- Send
-
+
+
+ Send
+
+
) : null}
diff --git a/src/routes/safe/components/AddressBook/style.ts b/src/routes/safe/components/AddressBook/style.ts
index a8314a31..e6611ec0 100644
--- a/src/routes/safe/components/AddressBook/style.ts
+++ b/src/routes/safe/components/AddressBook/style.ts
@@ -1,4 +1,4 @@
-import { lg, marginButtonImg, md, sm } from 'src/theme/variables'
+import { lg, md, sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
export const styles = createStyles({
@@ -37,7 +37,7 @@ export const styles = createStyles({
},
editEntryButton: {
cursor: 'pointer',
- marginBottom: marginButtonImg,
+ marginBottom: '16px',
},
editEntryButtonNonOwner: {
cursor: 'pointer',
@@ -45,13 +45,13 @@ export const styles = createStyles({
removeEntryButton: {
marginLeft: lg,
marginRight: lg,
- marginBottom: marginButtonImg,
+ marginBottom: '16px',
cursor: 'pointer',
},
removeEntryButtonDisabled: {
marginLeft: lg,
marginRight: lg,
- marginBottom: marginButtonImg,
+ marginBottom: '16px',
cursor: 'default',
},
removeEntryButtonNonOwner: {
@@ -65,14 +65,6 @@ export const styles = createStyles({
boxSizing: 'border-box',
justifyContent: 'flex-end',
},
- send: {
- width: '75px',
- minWidth: '75px',
- borderRadius: '4px',
- '& > span': {
- fontSize: '14px',
- },
- },
leftIcon: {
marginRight: sm,
},
diff --git a/src/routes/safe/components/Apps/components/ConfirmTxModal/ReviewConfirm.tsx b/src/routes/safe/components/Apps/components/ConfirmTxModal/ReviewConfirm.tsx
index 3a56ebcb..06e4fcc4 100644
--- a/src/routes/safe/components/Apps/components/ConfirmTxModal/ReviewConfirm.tsx
+++ b/src/routes/safe/components/Apps/components/ConfirmTxModal/ReviewConfirm.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useMemo, useState } from 'react'
-import { ModalFooterConfirmation } from '@gnosis.pm/safe-react-components'
+import { Text, EthHashInfo, ModalFooterConfirmation } from '@gnosis.pm/safe-react-components'
import styled from 'styled-components'
import { useDispatch } from 'react-redux'
@@ -20,11 +20,12 @@ import { EditableTxParameters } from 'src/routes/safe/components/Transactions/he
import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
import { md, lg, sm } from 'src/theme/variables'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
-import AddressInfo from 'src/components/AddressInfo'
import { DecodeTxs, BasicTxInfo } from 'src/components/DecodeTxs'
import { fetchTxDecoder } from 'src/utils/decodeTx'
import { DecodedData } from 'src/types/transactions/decode.d'
import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
+import { getExplorerInfo } from 'src/config'
+import Block from 'src/components/layout/Block'
import GasEstimationInfo from '../GasEstimationInfo'
import { ConfirmTxModalProps, DecodedTxDetail } from '.'
@@ -53,6 +54,19 @@ const DecodeTxsWrapper = styled.div`
margin: 24px -24px;
`
+const StyledBlock = styled(Block)`
+ background-color: ${({ theme }) => theme.colors.separator};
+ width: fit-content;
+ padding: 5px 10px;
+ border-radius: 3px;
+ margin: 4px 0 0 40px;
+
+ display: flex;
+ > :nth-child(1) {
+ margin-right: 5px;
+ }
+`
+
type Props = ConfirmTxModalProps & {
areTxsMalformed: boolean
showDecodedTxData: (decodedTxDetails: DecodedTxDetail) => void
@@ -81,6 +95,7 @@ export const ReviewConfirm = ({
const isMultiSend = txs.length > 1
const [decodedData, setDecodedData] = useState(null)
const dispatch = useDispatch()
+ const explorerUrl = getExplorerInfo(safeAddress)
const txRecipient: string | undefined = useMemo(() => (isMultiSend ? MULTI_SEND_ADDRESS : txs[0]?.to), [
txs,
@@ -97,6 +112,7 @@ export const ReviewConfirm = ({
const operation = useMemo(() => (isMultiSend ? DELEGATE_CALL : CALL), [isMultiSend])
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
gasLimit,
@@ -114,6 +130,7 @@ export const ReviewConfirm = ({
txAmount: txValue,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
@@ -176,6 +193,10 @@ export const ReviewConfirm = ({
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
@@ -198,7 +219,11 @@ export const ReviewConfirm = ({
{/* Safe */}
-
+
+
+ Balance:
+ {`${ethBalance} ${nativeCoin.symbol}`}
+
@@ -212,9 +237,7 @@ export const ReviewConfirm = ({
-
{!isMultiSend && }
-
{/* Warning gas estimation */}
{params?.safeTxGas && (
@@ -227,7 +250,6 @@ export const ReviewConfirm = ({
/>
)}
-
{/* Tx Parameters */}
{
{granted && (
- showSendFunds(row.asset.address)}
- size="small"
- testId="balance-send-btn"
+ size="md"
variant="contained"
+ data-testid="balance-send-btn"
>
- {/* */}
- Send
-
+
+
+ Send
+
+
)}
-
- {/* */}
- Receive
-
+
+
+
+ Receive
+
+
diff --git a/src/routes/safe/components/Balances/Coins/styles.ts b/src/routes/safe/components/Balances/Coins/styles.ts
index ae4ae6ac..bd221514 100644
--- a/src/routes/safe/components/Balances/Coins/styles.ts
+++ b/src/routes/safe/components/Balances/Coins/styles.ts
@@ -1,4 +1,4 @@
-import { sm, xs } from 'src/theme/variables'
+import { sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
export const styles = createStyles({
@@ -25,23 +25,6 @@ export const styles = createStyles({
justifyContent: 'flex-end',
visibility: 'hidden',
},
- receive: {
- width: '95px',
- minWidth: '95px',
- marginLeft: sm,
- borderRadius: xs,
- '& > span': {
- fontSize: '14px',
- },
- },
- send: {
- width: '75px',
- minWidth: '75px',
- borderRadius: xs,
- '& > span': {
- fontSize: '14px',
- },
- },
leftIcon: {
marginRight: sm,
},
diff --git a/src/routes/safe/components/Balances/SendModal/SafeInfo/index.tsx b/src/routes/safe/components/Balances/SendModal/SafeInfo/index.tsx
index 36691422..27f5ad53 100644
--- a/src/routes/safe/components/Balances/SendModal/SafeInfo/index.tsx
+++ b/src/routes/safe/components/Balances/SendModal/SafeInfo/index.tsx
@@ -32,7 +32,7 @@ const SafeInfo = (): React.ReactElement => {
hash={safeAddress}
name={safeName}
explorerUrl={getExplorerInfo(safeAddress)}
- showIdenticon
+ showAvatar
showCopyBtn
/>
{ethBalance && (
diff --git a/src/routes/safe/components/Balances/SendModal/index.tsx b/src/routes/safe/components/Balances/SendModal/index.tsx
index 7623d9fa..c90f1690 100644
--- a/src/routes/safe/components/Balances/SendModal/index.tsx
+++ b/src/routes/safe/components/Balances/SendModal/index.tsx
@@ -1,6 +1,5 @@
import CircularProgress from '@material-ui/core/CircularProgress'
import { makeStyles } from '@material-ui/core/styles'
-import cn from 'classnames'
import React, { Suspense, useEffect, useState } from 'react'
import Modal from 'src/components/Modal'
@@ -32,12 +31,6 @@ const SendCustomTx = React.lazy(() => import('./screens/ContractInteraction/Send
const ReviewCustomTx = React.lazy(() => import('./screens/ContractInteraction/ReviewCustomTx'))
const useStyles = makeStyles({
- scalableModalWindow: {
- height: 'auto',
- },
- scalableStaticModalWindow: {
- height: 'auto',
- },
loaderStyle: {
height: '500px',
width: '100%',
@@ -86,8 +79,6 @@ const SendModal = ({
setTx({})
}, [activeScreenType, isOpen])
- const scalableModalSize = activeScreen === 'chooseTxType'
-
const handleTxCreation = (txInfo: SendCollectibleTxInfo) => {
setActiveScreen('sendFundsReviewTx')
setTx(txInfo)
@@ -117,7 +108,7 @@ const SendModal = ({
description="Send Tokens Form"
handleClose={onClose}
open={isOpen}
- paperClassName={cn(scalableModalSize ? classes.scalableStaticModalWindow : classes.scalableModalWindow)}
+ paperClassName="smaller-modal-window"
title="Send Tokens"
>
)}
getOptionLabel={({ address }) => address}
- renderOption={({ address, name }) => }
+ renderOption={({ address, name }) => }
role="listbox"
style={{ display: 'flex', flexGrow: 1 }}
/>
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx
index 9352bcdb..d39700c2 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx
+++ b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx
@@ -2,9 +2,8 @@ import React, { useEffect, useState } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import { useDispatch, useSelector } from 'react-redux'
-import { getNetworkInfo } from 'src/config'
+import { getNetworkInfo, getExplorerInfo } from 'src/config'
import { toTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
-import AddressInfo from 'src/components/AddressInfo'
import Block from 'src/components/layout/Block'
import Button from 'src/components/layout/Button'
import Col from 'src/components/layout/Col'
@@ -30,6 +29,7 @@ import {
import { useEstimateTransactionGas, EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas'
import { TransactionFees } from 'src/components/TransactionsFees'
import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters'
+import { EthHashInfo } from '@gnosis.pm/safe-react-components'
const useStyles = makeStyles(styles)
@@ -52,11 +52,13 @@ type Props = {
const { nativeCoin } = getNetworkInfo()
const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
+ const explorerUrl = getExplorerInfo(tx.contractAddress as string)
const classes = useStyles()
const dispatch = useDispatch()
const safeAddress = useSelector(safeParamAddressFromStateSelector)
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const [txInfo, setTxInfo] = useState<{
txRecipient: string
@@ -79,6 +81,7 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
txData: txInfo?.txData,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
@@ -119,6 +122,10 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
@@ -144,7 +151,7 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
-
+
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx
index ea68ea2a..16e3abc5 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx
+++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx
@@ -56,6 +56,7 @@ const ReviewCollectible = ({ onClose, onPrev, tx }: Props): React.ReactElement =
const nftTokens = useSelector(nftTokensSelector)
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const txToken = nftTokens.find(
({ assetAddress, tokenId }) => assetAddress === tx.assetAddress && tokenId === tx.nftTokenId,
@@ -76,6 +77,7 @@ const ReviewCollectible = ({ onClose, onPrev, tx }: Props): React.ReactElement =
txRecipient: tx.assetAddress,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
@@ -133,6 +135,10 @@ const ReviewCollectible = ({ onClose, onPrev, tx }: Props): React.ReactElement =
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewSendFundsTx/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewSendFundsTx/index.tsx
index 1e2ab83c..aee98644 100644
--- a/src/routes/safe/components/Balances/SendModal/screens/ReviewSendFundsTx/index.tsx
+++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewSendFundsTx/index.tsx
@@ -100,6 +100,7 @@ const ReviewSendFundsTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactE
const data = useTxData(isSendingNativeToken, tx.amount, tx.recipientAddress, txToken)
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
gasCostFormatted,
@@ -117,6 +118,7 @@ const ReviewSendFundsTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactE
txAmount: txValue,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
const submitTx = async (txParameters: TxParameters) => {
@@ -171,6 +173,10 @@ const ReviewSendFundsTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactE
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
@@ -257,17 +263,21 @@ const ReviewSendFundsTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactE
{/* Tx Parameters */}
-
+ {/* FIXME TxParameters should be updated to be used with spending limits */}
+ {!sameString(tx.txType, 'spendingLimit') && (
+
+ )}
{/* Disclaimer */}
- {txEstimationExecutionStatus !== EstimationStatus.LOADING && (
+ {/* FIXME Estimation should be fixed to be used with spending limits */}
+ {!sameString(tx.txType, 'spendingLimit') && txEstimationExecutionStatus !== EstimationStatus.LOADING && (
diff --git a/src/routes/safe/components/Balances/index.tsx b/src/routes/safe/components/Balances/index.tsx
index d9dd2bdc..0470abc3 100644
--- a/src/routes/safe/components/Balances/index.tsx
+++ b/src/routes/safe/components/Balances/index.tsx
@@ -92,7 +92,7 @@ const Balances = (): React.ReactElement => {
}))
}
- const { assetDivider, assetTab, assetTabActive, assetTabs, controls, receiveModal, tokenControls } = classes
+ const { assetDivider, assetTab, assetTabActive, assetTabs, controls, tokenControls } = classes
const { erc721Enabled, sendFunds, showReceive } = state
return (
@@ -174,7 +174,7 @@ const Balances = (): React.ReactElement => {
description="Receive Tokens Form"
handleClose={() => onHide('Receive')}
open={showReceive}
- paperClassName={receiveModal}
+ paperClassName="receive-modal"
title="Receive Tokens"
>
onHide('Receive')} />
diff --git a/src/routes/safe/components/Balances/style.ts b/src/routes/safe/components/Balances/style.ts
index bcdaf782..2f408901 100644
--- a/src/routes/safe/components/Balances/style.ts
+++ b/src/routes/safe/components/Balances/style.ts
@@ -58,12 +58,6 @@ export const styles = createStyles({
marginLeft: '0',
},
},
- receiveModal: {
- height: 'auto',
- maxWidth: 'calc(100% - 30px)',
- minHeight: '544px',
- overflow: 'hidden',
- },
send: {
width: '75px',
minWidth: '75px',
diff --git a/src/routes/safe/components/Layout/style.ts b/src/routes/safe/components/Layout/style.ts
index ed951783..71f6b493 100644
--- a/src/routes/safe/components/Layout/style.ts
+++ b/src/routes/safe/components/Layout/style.ts
@@ -1,3 +1,4 @@
+// TODO: remove this file. It's no longer used
import { screenSm, sm } from 'src/theme/variables'
import { createStyles } from '@material-ui/core'
diff --git a/src/routes/safe/components/Settings/Advanced/RemoveModuleModal.tsx b/src/routes/safe/components/Settings/Advanced/RemoveModuleModal.tsx
index 042f47d3..a0d0ac32 100644
--- a/src/routes/safe/components/Settings/Advanced/RemoveModuleModal.tsx
+++ b/src/routes/safe/components/Settings/Advanced/RemoveModuleModal.tsx
@@ -57,6 +57,7 @@ export const RemoveModuleModal = ({ onClose, selectedModulePair }: RemoveModuleM
const dispatch = useDispatch()
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const [, moduleAddress] = selectedModulePair
const explorerInfo = getExplorerInfo(moduleAddress)
@@ -77,6 +78,7 @@ export const RemoveModuleModal = ({ onClose, selectedModulePair }: RemoveModuleM
txAmount: '0',
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
@@ -113,6 +115,10 @@ export const RemoveModuleModal = ({ onClose, selectedModulePair }: RemoveModuleM
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
@@ -122,7 +128,7 @@ export const RemoveModuleModal = ({ onClose, selectedModulePair }: RemoveModuleM
diff --git a/src/routes/safe/components/Settings/Advanced/style.ts b/src/routes/safe/components/Settings/Advanced/style.ts
index 69925f4f..7cfc93d1 100644
--- a/src/routes/safe/components/Settings/Advanced/style.ts
+++ b/src/routes/safe/components/Settings/Advanced/style.ts
@@ -101,11 +101,6 @@ export const styles = createStyles({
cursor: 'pointer',
},
},
- modal: {
- height: 'auto',
- maxWidth: 'calc(100% - 30px)',
- overflow: 'hidden',
- },
gasCostsContainer: {
backgroundColor: background,
padding: `0 ${lg}`,
diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx
index 864e1918..26d3e878 100644
--- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx
+++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx
@@ -1,4 +1,3 @@
-import { createStyles, makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
@@ -18,15 +17,6 @@ import { OwnerForm } from './screens/OwnerForm'
import { ReviewAddOwner } from './screens/Review'
import { ThresholdForm } from './screens/ThresholdForm'
-const styles = createStyles({
- biggerModalWindow: {
- width: '775px',
- height: 'auto',
- },
-})
-
-const useStyles = makeStyles(styles)
-
export type OwnerValues = {
ownerAddress: string
ownerName: string
@@ -39,7 +29,7 @@ export const sendAddOwner = async (
txParameters: TxParameters,
dispatch: Dispatch,
): Promise => {
- const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
+ const gnosisSafe = getGnosisSafeInstanceAt(safeAddress)
const txData = gnosisSafe.methods.addOwnerWithThreshold(values.ownerAddress, values.threshold).encodeABI()
const txHash = await dispatch(
@@ -66,7 +56,6 @@ type Props = {
}
export const AddOwnerModal = ({ isOpen, onClose }: Props): React.ReactElement => {
- const classes = useStyles()
const [activeScreen, setActiveScreen] = useState('selectOwner')
const [values, setValues] = useState({ ownerName: '', ownerAddress: '', threshold: '' })
const dispatch = useDispatch()
@@ -123,7 +112,7 @@ export const AddOwnerModal = ({ isOpen, onClose }: Props): React.ReactElement =>
description="Add owner to Safe"
handleClose={onClose}
open={isOpen}
- paperClassName={classes.biggerModalWindow}
+ paperClassName="bigger-modal-window"
title="Add owner to Safe"
>
<>
diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx
index 1acbd6ef..c606396f 100644
--- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx
+++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx
@@ -45,6 +45,7 @@ export const ReviewAddOwner = ({ onClickBack, onClose, onSubmit, values }: Revie
const owners = useSelector(safeOwnersSelector)
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
gasLimit,
@@ -60,14 +61,15 @@ export const ReviewAddOwner = ({ onClickBack, onClose, onSubmit, values }: Revie
txRecipient: safeAddress,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
let isCurrent = true
- const calculateAddOwnerData = async () => {
+ const calculateAddOwnerData = () => {
try {
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const txData = safeInstance.methods.addOwnerWithThreshold(values.ownerAddress, values.threshold).encodeABI()
if (isCurrent) {
@@ -94,6 +96,10 @@ export const ReviewAddOwner = ({ onClickBack, onClose, onSubmit, values }: Revie
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
diff --git a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx
index 914da3c2..d213c512 100644
--- a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx
+++ b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx
@@ -60,7 +60,7 @@ export const EditOwnerModal = ({ isOpen, onClose, ownerAddress, selectedOwnerNam
description="Edit owner from Safe"
handleClose={onClose}
open={isOpen}
- paperClassName={classes.smallerModalWindow}
+ paperClassName="smaller-modal-window"
title="Edit owner from Safe"
>
diff --git a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/style.ts b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/style.ts
index 968f88d2..73340afa 100644
--- a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/style.ts
+++ b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/style.ts
@@ -32,7 +32,4 @@ export const styles = createStyles({
cursor: 'pointer',
},
},
- smallerModalWindow: {
- height: 'auto',
- },
})
diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx
index 0875cac1..4f89c537 100644
--- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx
+++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/index.tsx
@@ -1,4 +1,3 @@
-import { createStyles, makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
@@ -15,15 +14,6 @@ import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/lo
import { Dispatch } from 'src/logic/safe/store/actions/types.d'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
-const styles = createStyles({
- biggerModalWindow: {
- width: '775px',
- height: 'auto',
- },
-})
-
-const useStyles = makeStyles(styles)
-
type OwnerValues = {
ownerAddress: string
ownerName: string
@@ -39,7 +29,7 @@ export const sendRemoveOwner = async (
txParameters: TxParameters,
threshold?: number,
): Promise => {
- const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
+ const gnosisSafe = getGnosisSafeInstanceAt(safeAddress)
const safeOwners = await gnosisSafe.methods.getOwners().call()
const index = safeOwners.findIndex(
(ownerAddress) => ownerAddress.toLowerCase() === ownerAddressToRemove.toLowerCase(),
@@ -78,7 +68,6 @@ export const RemoveOwnerModal = ({
ownerAddress,
ownerName,
}: RemoveOwnerProps): React.ReactElement => {
- const classes = useStyles()
const [activeScreen, setActiveScreen] = useState('checkOwner')
const [values, setValues] = useState({ ownerAddress, ownerName, threshold: '' })
const dispatch = useDispatch()
@@ -120,7 +109,7 @@ export const RemoveOwnerModal = ({
description="Remove owner from Safe"
handleClose={onClose}
open={isOpen}
- paperClassName={classes.biggerModalWindow}
+ paperClassName="bigger-modal-window"
title="Remove owner from Safe"
>
<>
diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx
index 7d77084c..268f5729 100644
--- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx
+++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx
@@ -59,6 +59,7 @@ export const ReviewRemoveOwnerModal = ({
const ownersWithAddressBookName = owners ? getOwnersWithNameFromAddressBook(addressBook, owners) : List([])
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
gasLimit,
@@ -74,6 +75,7 @@ export const ReviewRemoveOwnerModal = ({
txRecipient: safeAddress,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
@@ -86,7 +88,7 @@ export const ReviewRemoveOwnerModal = ({
const calculateRemoveOwnerData = async () => {
try {
- const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
+ const gnosisSafe = getGnosisSafeInstanceAt(safeAddress)
const safeOwners = await gnosisSafe.methods.getOwners().call()
const index = safeOwners.findIndex((owner) => sameAddress(owner, ownerAddress))
const prevAddress = index === 0 ? SENTINEL_ADDRESS : safeOwners[index - 1]
@@ -116,6 +118,10 @@ export const ReviewRemoveOwnerModal = ({
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx
index 74cd9f81..0f4cc8ac 100644
--- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx
+++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx
@@ -1,4 +1,3 @@
-import { createStyles, makeStyles } from '@material-ui/core/styles'
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
@@ -18,15 +17,6 @@ import { OwnerForm } from 'src/routes/safe/components/Settings/ManageOwners/Repl
import { ReviewReplaceOwnerModal } from 'src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
-const styles = createStyles({
- biggerModalWindow: {
- width: '775px',
- height: 'auto',
- },
-})
-
-const useStyles = makeStyles(styles)
-
type OwnerValues = {
newOwnerAddress: string
newOwnerName: string
@@ -40,7 +30,7 @@ export const sendReplaceOwner = async (
txParameters: TxParameters,
threshold?: number,
): Promise => {
- const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
+ const gnosisSafe = getGnosisSafeInstanceAt(safeAddress)
const safeOwners = await gnosisSafe.methods.getOwners().call()
const index = safeOwners.findIndex((ownerAddress) => sameAddress(ownerAddress, ownerAddressToRemove))
const prevAddress = index === 0 ? SENTINEL_ADDRESS : safeOwners[index - 1]
@@ -84,7 +74,6 @@ export const ReplaceOwnerModal = ({
ownerAddress,
ownerName,
}: ReplaceOwnerProps): React.ReactElement => {
- const classes = useStyles()
const [activeScreen, setActiveScreen] = useState('checkOwner')
const [values, setValues] = useState({
newOwnerAddress: '',
@@ -137,7 +126,7 @@ export const ReplaceOwnerModal = ({
description="Replace owner from Safe"
handleClose={onClose}
open={isOpen}
- paperClassName={classes.biggerModalWindow}
+ paperClassName="bigger-modal-window"
title="Replace owner from Safe"
>
<>
diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx
index 52327599..2c45a342 100644
--- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx
+++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx
@@ -67,6 +67,7 @@ export const ReviewReplaceOwnerModal = ({
const ownersWithAddressBookName = owners ? getOwnersWithNameFromAddressBook(addressBook, owners) : List([])
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
gasLimit,
@@ -82,12 +83,13 @@ export const ReviewReplaceOwnerModal = ({
txRecipient: safeAddress,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
let isCurrent = true
const calculateReplaceOwnerData = async () => {
- const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
+ const gnosisSafe = getGnosisSafeInstanceAt(safeAddress)
const safeOwners = await gnosisSafe.methods.getOwners().call()
const index = safeOwners.findIndex((owner) => owner.toLowerCase() === ownerAddress.toLowerCase())
const prevAddress = index === 0 ? SENTINEL_ADDRESS : safeOwners[index - 1]
@@ -113,6 +115,10 @@ export const ReviewReplaceOwnerModal = ({
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
diff --git a/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx b/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx
index 4d2f8b72..a846751d 100644
--- a/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx
+++ b/src/routes/safe/components/Settings/RemoveSafeModal/index.tsx
@@ -68,7 +68,7 @@ export const RemoveSafeModal = ({ isOpen, onClose }: RemoveSafeModalProps): Reac
description="Remove the selected Safe"
handleClose={onClose}
open={isOpen}
- paperClassName={classes.modal}
+ paperClassName="modal"
title="Remove Safe"
>
diff --git a/src/routes/safe/components/Settings/RemoveSafeModal/style.ts b/src/routes/safe/components/Settings/RemoveSafeModal/style.ts
index 2d41fb79..b034d782 100644
--- a/src/routes/safe/components/Settings/RemoveSafeModal/style.ts
+++ b/src/routes/safe/components/Settings/RemoveSafeModal/style.ts
@@ -52,9 +52,4 @@ export const styles = createStyles({
cursor: 'pointer',
},
},
- modal: {
- height: 'auto',
- maxWidth: 'calc(100% - 30px)',
- overflow: 'hidden',
- },
})
diff --git a/src/routes/safe/components/Settings/SpendingLimit/FormFields/Beneficiary.tsx b/src/routes/safe/components/Settings/SpendingLimit/FormFields/Beneficiary.tsx
index 0e960d6e..4002bc5c 100644
--- a/src/routes/safe/components/Settings/SpendingLimit/FormFields/Beneficiary.tsx
+++ b/src/routes/safe/components/Settings/SpendingLimit/FormFields/Beneficiary.tsx
@@ -81,7 +81,7 @@ const Beneficiary = (): ReactElement => {
hash={selectedEntry.address}
name={selectedEntry.name}
showCopyBtn
- showIdenticon
+ showAvatar
textSize="lg"
shortenHash={4}
explorerUrl={getExplorerInfo(selectedEntry.address)}
diff --git a/src/routes/safe/components/Settings/SpendingLimit/InfoDisplay/AddressInfo.tsx b/src/routes/safe/components/Settings/SpendingLimit/InfoDisplay/AddressInfo.tsx
index 9e98c1fc..c18fdd30 100644
--- a/src/routes/safe/components/Settings/SpendingLimit/InfoDisplay/AddressInfo.tsx
+++ b/src/routes/safe/components/Settings/SpendingLimit/InfoDisplay/AddressInfo.tsx
@@ -24,7 +24,7 @@ const AddressInfo = ({ address, cut = 4, title }: AddressInfoProps): ReactElemen
hash={address}
name={sameString(name, 'UNKNOWN') ? undefined : name}
showCopyBtn
- showIdenticon
+ showAvatar
textSize="lg"
explorerUrl={explorerUrl}
shortenHash={cut}
diff --git a/src/routes/safe/components/Settings/SpendingLimit/Modal/index.tsx b/src/routes/safe/components/Settings/SpendingLimit/Modal/index.tsx
deleted file mode 100644
index b546f02f..00000000
--- a/src/routes/safe/components/Settings/SpendingLimit/Modal/index.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { Icon, Text, Title } from '@gnosis.pm/safe-react-components'
-import React, { ReactElement, ReactNode, ReactNodeArray } from 'react'
-import styled from 'styled-components'
-
-import GnoModal from 'src/components/Modal'
-import { useStyles } from 'src/routes/safe/components/Settings/SpendingLimit/style'
-
-const TitleSection = styled.div`
- display: flex;
- justify-content: space-between;
- padding: 16px 24px;
- border-bottom: 2px solid ${({ theme }) => theme.colors.separator};
-`
-
-const StyledButton = styled.button`
- background: none;
- border: none;
- padding: 5px;
- width: 26px;
- height: 26px;
-
- span {
- margin-right: 0;
- }
-
- :hover {
- background: ${({ theme }) => theme.colors.separator};
- border-radius: 16px;
- cursor: pointer;
- }
-`
-
-const FooterSection = styled.div`
- border-top: 2px solid ${({ theme }) => theme.colors.separator};
- padding: 16px 24px;
-`
-
-const FooterWrapper = styled.div`
- display: flex;
- justify-content: space-around;
-`
-
-export interface TopBarProps {
- title: string
- titleNote?: string
- onClose: () => void
-}
-
-const TopBar = ({ title, titleNote, onClose }: TopBarProps): ReactElement => (
-
-
- {title}
- {titleNote && (
- <>
- {' '}
-
- {titleNote}
-
- >
- )}
-
-
-
-
-
-
-)
-
-interface FooterProps {
- children: ReactNodeArray
-}
-
-const Footer = ({ children }: FooterProps): ReactElement => (
-
- {children}
-
-)
-
-export interface ModalProps {
- children: ReactNode
- description: string
- handleClose: () => void
- open: boolean
- title: string
-}
-
-// TODO: this is a potential proposal for `safe-react-components` Modal
-// By being able to combine components for better flexibility, this way Buttons can be part of the form body
-const Modal = ({ children, ...props }: ModalProps): ReactElement => {
- const classes = useStyles()
-
- return (
-
- {children}
-
- )
-}
-
-Modal.TopBar = TopBar
-Modal.Footer = Footer
-
-export default Modal
diff --git a/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Create.tsx b/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Create.tsx
index 2c048203..ab417c26 100644
--- a/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Create.tsx
+++ b/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Create.tsx
@@ -1,12 +1,11 @@
-import { Button } from '@gnosis.pm/safe-react-components'
+import { Text } from '@gnosis.pm/safe-react-components'
import { FormState, Mutator } from 'final-form'
import React, { ReactElement } from 'react'
import styled from 'styled-components'
import GnoForm from 'src/components/forms/GnoForm'
-import GnoButton from 'src/components/layout/Button'
+import { Modal } from 'src/components/Modal'
import { Amount, Beneficiary, ResetTime, Token } from 'src/routes/safe/components/Settings/SpendingLimit/FormFields'
-import Modal from 'src/routes/safe/components/Settings/SpendingLimit/Modal'
const FormContainer = styled.div`
padding: 24px 8px 24px 24px;
@@ -24,14 +23,6 @@ const FormContainer = styled.div`
'resetTimeOption resetTimeOption';
`
-const YetAnotherButton = styled(GnoButton)`
- &.Mui-disabled {
- background-color: ${({ theme }) => theme.colors.primary};
- color: ${({ theme }) => theme.colors.white};
- opacity: 0.5;
- }
-`
-
const formMutators: Record> = {
setBeneficiary: (args, state, utils) => {
utils.changeValue(state, 'beneficiary', () => args[0])
@@ -55,7 +46,16 @@ const canReview = ({
const Create = ({ initialValues, onCancel, onReview }: NewSpendingLimitProps): ReactElement => {
return (
<>
-
+
+
+ <>
+ New Spending Limit
+
+ 1 of 2
+
+ >
+
+
{(...args) => {
@@ -69,21 +69,10 @@ const Create = ({ initialValues, onCancel, onReview }: NewSpendingLimitProps): R
-
- Cancel
-
-
- {/* TODO: replace this with safe-react-components button. */}
- {/* This is used as "submit" SRC Button does not triggers submission up until the 2nd click */}
-
- Review
-
+
>
)
diff --git a/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Review.tsx b/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Review.tsx
index 9d803ded..8330c121 100644
--- a/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Review.tsx
+++ b/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/Review.tsx
@@ -1,10 +1,10 @@
-import { Button, Text } from '@gnosis.pm/safe-react-components'
+import { Text } from '@gnosis.pm/safe-react-components'
import React, { ReactElement, useEffect, useMemo, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
-import Block from 'src/components/layout/Block'
import Col from 'src/components/layout/Col'
import Row from 'src/components/layout/Row'
+import { Modal } from 'src/components/Modal'
import { createTransaction, CreateTransactionArgs } from 'src/logic/safe/store/actions/createTransaction'
import { SafeRecordProps, SpendingLimit } from 'src/logic/safe/store/models/safe'
import {
@@ -22,7 +22,6 @@ import { fromTokenUnit, toTokenUnit } from 'src/logic/tokens/utils/humanReadable
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { RESET_TIME_OPTIONS } from 'src/routes/safe/components/Settings/SpendingLimit/FormFields/ResetTime'
import { AddressInfo, ResetTimeInfo, TokenInfo } from 'src/routes/safe/components/Settings/SpendingLimit/InfoDisplay'
-import Modal from 'src/routes/safe/components/Settings/SpendingLimit/Modal'
import { useStyles } from 'src/routes/safe/components/Settings/SpendingLimit/style'
import { safeParamAddressFromStateSelector, safeSpendingLimitsSelector } from 'src/logic/safe/store/selectors'
import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
@@ -155,6 +154,7 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
})
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
gasCostFormatted,
@@ -172,6 +172,7 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
operation: estimateGasArgs.operation,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
@@ -226,6 +227,10 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
@@ -242,9 +247,16 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
>
{(txParameters, toggleEditMode) => (
<>
-
+
+
+ New Spending Limit
+
+ 2 of 2
+
+
+
-
+
@@ -286,7 +298,7 @@ export const ReviewSpendingLimits = ({ onBack, onClose, txToken, values }: Revie
isTransactionExecution={isExecution}
isOffChainSignature={isOffChainSignature}
/>
-
+
- onBack({ values: {}, txToken: makeToken(), step: CREATE })}
- >
- Back
-
-
- handleSubmit(txParameters)}
- disabled={existentSpendingLimit === undefined || txEstimationExecutionStatus === EstimationStatus.LOADING}
- >
- Submit
-
+ onBack({ values: {}, txToken: makeToken(), step: CREATE }),
+ text: 'Back',
+ }}
+ confirmButtonProps={{
+ onClick: () => handleSubmit(txParameters),
+ disabled:
+ existentSpendingLimit === undefined || txEstimationExecutionStatus === EstimationStatus.LOADING,
+ }}
+ />
>
)}
diff --git a/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/index.tsx b/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/index.tsx
index 7cb5b9a5..84666696 100644
--- a/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/index.tsx
+++ b/src/routes/safe/components/Settings/SpendingLimit/NewLimitModal/index.tsx
@@ -2,10 +2,10 @@ import { List } from 'immutable'
import React, { ReactElement, Reducer, useCallback, useReducer } from 'react'
import { useSelector } from 'react-redux'
+import { Modal } from 'src/components/Modal'
import { makeToken, Token } from 'src/logic/tokens/store/model/token'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { extendedSafeTokensSelector } from 'src/routes/safe/container/selector'
-import Modal from 'src/routes/safe/components/Settings/SpendingLimit/Modal'
import Create from './Create'
import { ReviewSpendingLimits } from './Review'
diff --git a/src/routes/safe/components/Settings/SpendingLimit/RemoveLimitModal.tsx b/src/routes/safe/components/Settings/SpendingLimit/RemoveLimitModal.tsx
index 6ad0ec6e..89b9ef5a 100644
--- a/src/routes/safe/components/Settings/SpendingLimit/RemoveLimitModal.tsx
+++ b/src/routes/safe/components/Settings/SpendingLimit/RemoveLimitModal.tsx
@@ -1,29 +1,27 @@
-import { Button } from '@gnosis.pm/safe-react-components'
+import cn from 'classnames'
import React, { ReactElement, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
-import Block from 'src/components/layout/Block'
import Col from 'src/components/layout/Col'
+import Row from 'src/components/layout/Row'
+import { Modal } from 'src/components/Modal'
+import { TransactionFees } from 'src/components/TransactionsFees'
+import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
import useTokenInfo from 'src/logic/safe/hooks/useTokenInfo'
import { createTransaction } from 'src/logic/safe/store/actions/createTransaction'
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
import { getDeleteAllowanceTxData } from 'src/logic/safe/utils/spendingLimits'
import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
+import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters'
+import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
+import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
import { SPENDING_LIMIT_MODULE_ADDRESS } from 'src/utils/constants'
import { RESET_TIME_OPTIONS } from './FormFields/ResetTime'
import { AddressInfo, ResetTimeInfo, TokenInfo } from './InfoDisplay'
import { SpendingLimitTable } from './LimitsTable/dataFetcher'
-import Modal from './Modal'
import { useStyles } from './style'
-import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
-import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters'
-import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters'
-import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail'
-import Row from 'src/components/layout/Row'
-import { TransactionFees } from 'src/components/TransactionsFees'
-import cn from 'classnames'
interface RemoveSpendingLimitModalProps {
onClose: () => void
@@ -41,6 +39,7 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
const dispatch = useDispatch()
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
useEffect(() => {
const {
@@ -66,6 +65,7 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
txAmount: '0',
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
const removeSelectedSpendingLimit = async (txParameters: TxParameters): Promise => {
@@ -103,6 +103,10 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
@@ -126,9 +130,13 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
{(txParameters, toggleEditMode) => {
return (
<>
-
+
+
+ Remove Spending Limit
+
+
-
+
@@ -152,7 +160,7 @@ export const RemoveLimitModal = ({ onClose, spendingLimit, open }: RemoveSpendin
isTransactionExecution={isExecution}
isOffChainSignature={isOffChainSignature}
/>
-
+
-
- Cancel
-
- removeSelectedSpendingLimit(txParameters)}
- disabled={txEstimationExecutionStatus === EstimationStatus.LOADING}
- >
- Remove
-
+ removeSelectedSpendingLimit(txParameters),
+ disabled: txEstimationExecutionStatus === EstimationStatus.LOADING,
+ text: 'Remove',
+ }}
+ />
>
)
diff --git a/src/routes/safe/components/Settings/SpendingLimit/style.ts b/src/routes/safe/components/Settings/SpendingLimit/style.ts
index 0b8a9568..8df8e247 100644
--- a/src/routes/safe/components/Settings/SpendingLimit/style.ts
+++ b/src/routes/safe/components/Settings/SpendingLimit/style.ts
@@ -120,10 +120,6 @@ export const useStyles = makeStyles(
cursor: 'pointer',
},
},
- modal: {
- height: 'auto',
- maxWidth: 'calc(100% - 30px)',
- },
amountInput: {
width: '100% !important',
},
diff --git a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx
index 0560b57a..ff2c2785 100644
--- a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx
+++ b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx
@@ -50,6 +50,7 @@ export const ChangeThresholdModal = ({
const [data, setData] = useState('')
const [manualSafeTxGas, setManualSafeTxGas] = useState(0)
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const [editedThreshold, setEditedThreshold] = useState(threshold)
const {
@@ -66,12 +67,13 @@ export const ChangeThresholdModal = ({
txRecipient: safeAddress,
safeTxGas: manualSafeTxGas,
manualGasPrice,
+ manualGasLimit,
})
useEffect(() => {
let isCurrent = true
- const calculateChangeThresholdData = async () => {
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const calculateChangeThresholdData = () => {
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const txData = safeInstance.methods.changeThreshold(editedThreshold).encodeABI()
if (isCurrent) {
setData(txData)
@@ -111,6 +113,10 @@ export const ChangeThresholdModal = ({
setManualGasPrice(txParameters.ethGasPrice)
}
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit)
+ }
+
if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) {
setManualSafeTxGas(newSafeTxGas)
}
diff --git a/src/routes/safe/components/Transactions/TxList/AddressInfo.tsx b/src/routes/safe/components/Transactions/TxList/AddressInfo.tsx
index 46199746..45f4b654 100644
--- a/src/routes/safe/components/Transactions/TxList/AddressInfo.tsx
+++ b/src/routes/safe/components/Transactions/TxList/AddressInfo.tsx
@@ -5,7 +5,13 @@ import { getExplorerInfo } from 'src/config'
import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors'
-export const AddressInfo = ({ address }: { address: string }): ReactElement | null => {
+type Props = {
+ address: string
+ name?: string | undefined
+ avatarUrl?: string | undefined
+}
+
+export const AddressInfo = ({ address, name, avatarUrl }: Props): ReactElement | null => {
const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, address))
if (address === '') {
@@ -15,8 +21,9 @@ export const AddressInfo = ({ address }: { address: string }): ReactElement | nu
return (
diff --git a/src/routes/safe/components/Transactions/TxList/OwnerRow.tsx b/src/routes/safe/components/Transactions/TxList/OwnerRow.tsx
index ebfca691..6cd2cab5 100644
--- a/src/routes/safe/components/Transactions/TxList/OwnerRow.tsx
+++ b/src/routes/safe/components/Transactions/TxList/OwnerRow.tsx
@@ -12,7 +12,7 @@ export const OwnerRow = ({ ownerAddress }: { ownerAddress: string }): ReactEleme
{
if (isTokenTransferAsset(info)) {
@@ -116,6 +117,8 @@ export const TxCollapsed = ({
const { ref, lastItemId } = useContext(TxsInfiniteScrollContext)
const willBeReplaced = transaction?.txStatus === 'WILL_BE_REPLACED' ? ' will-be-replaced' : ''
+ const onChainRejection =
+ isCancelTxDetails(transaction.txInfo) && txLocation !== 'history' ? ' on-chain-rejection' : ''
const txCollapsedNonce = (
@@ -124,7 +127,7 @@ export const TxCollapsed = ({
)
const txCollapsedType = (
-
+
)
diff --git a/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx b/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx
index 1e753ecd..4751cc37 100644
--- a/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxCollapsedActions.tsx
@@ -46,7 +46,7 @@ export const TxCollapsedActions = ({ transaction }: TxCollapsedActionsProps): Re
{canCancel && (
-
+
diff --git a/src/routes/safe/components/Transactions/TxList/TxData.tsx b/src/routes/safe/components/Transactions/TxList/TxData.tsx
index d6c8dfa5..82be9fb6 100644
--- a/src/routes/safe/components/Transactions/TxList/TxData.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxData.tsx
@@ -1,7 +1,12 @@
import React, { ReactElement, ReactNode } from 'react'
import { getNetworkInfo } from 'src/config'
-import { ExpandedTxDetails, TransactionData } from 'src/logic/safe/store/models/types/gateway.d'
+import {
+ ExpandedTxDetails,
+ isCustomTxInfo,
+ TransactionData,
+ TransactionInfo,
+} from 'src/logic/safe/store/models/types/gateway.d'
import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
import {
DeleteSpendingLimitDetails,
@@ -20,23 +25,39 @@ const { nativeCoin } = getNetworkInfo()
type DetailsWithTxInfoProps = {
children: ReactNode
txData: TransactionData
+ txInfo: TransactionInfo
}
-const DetailsWithTxInfo = ({ children, txData }: DetailsWithTxInfoProps): ReactElement => (
- <>
-
- {children}
- >
-)
+const DetailsWithTxInfo = ({ children, txData, txInfo }: DetailsWithTxInfoProps): ReactElement => {
+ const amount = txData.value ? fromTokenUnit(txData.value, nativeCoin.decimals) : 'n/a'
+ let name
+ let avatarUrl
+
+ if (isCustomTxInfo(txInfo) && txInfo.toInfo) {
+ name = txInfo.toInfo.name
+ avatarUrl = txInfo.toInfo.logoUri
+ }
+
+ return (
+ <>
+
+
+ {children}
+ >
+ )
+}
type TxDataProps = {
txData: ExpandedTxDetails['txData']
+ txInfo: TransactionInfo
}
-export const TxData = ({ txData }: TxDataProps): ReactElement | null => {
+export const TxData = ({ txData, txInfo }: TxDataProps): ReactElement | null => {
// nothing to render
if (!txData) {
return null
@@ -51,7 +72,7 @@ export const TxData = ({ txData }: TxDataProps): ReactElement | null => {
// we render the hex encoded data
return (
-
+
)
@@ -74,7 +95,7 @@ export const TxData = ({ txData }: TxDataProps): ReactElement | null => {
// we render the decoded data
return (
-
+
)
diff --git a/src/routes/safe/components/Transactions/TxList/TxDetails.tsx b/src/routes/safe/components/Transactions/TxList/TxDetails.tsx
index 789afd82..fdc043ca 100644
--- a/src/routes/safe/components/Transactions/TxList/TxDetails.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxDetails.tsx
@@ -1,7 +1,6 @@
import { Icon, Link, Loader, Text } from '@gnosis.pm/safe-react-components'
import cn from 'classnames'
import React, { ReactElement, useContext } from 'react'
-import { useSelector } from 'react-redux'
import styled from 'styled-components'
import {
@@ -12,7 +11,6 @@ import {
MultiSigExecutionDetails,
Transaction,
} from 'src/logic/safe/store/models/types/gateway.d'
-import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import { TransactionActions } from './hooks/useTransactionActions'
import { useTransactionDetails } from './hooks/useTransactionDetails'
import { TxDetailsContainer, Centered, AlignItemsWithMargin } from './styled'
@@ -30,34 +28,44 @@ const NormalBreakingText = styled(Text)`
`
const TxDataGroup = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElement | null => {
- const safeAddress = useSelector(safeParamAddressFromStateSelector)
-
if (isTransferTxInfo(txDetails.txInfo) || isSettingsChangeTxInfo(txDetails.txInfo)) {
return
}
- if (isCancelTxDetails({ executedAt: txDetails.executedAt, txInfo: txDetails.txInfo, safeAddress })) {
+ if (isCancelTxDetails(txDetails.txInfo)) {
+ const txNonce = `${(txDetails.detailedExecutionInfo as MultiSigExecutionDetails).nonce ?? NOT_AVAILABLE}`
+ const isTxExecuted = txDetails.executedAt
+
+ // executed rejection transaction
+ let message = `This is an on-chain rejection that didn't send any funds.
+ This on-chain rejection replaced all transactions with nonce ${txNonce}.`
+
+ if (!isTxExecuted) {
+ // queued rejection transaction
+ message = `This is an on-chain rejection that doesn't send any funds.
+ Executing this on-chain rejection will replace all currently awaiting transactions with nonce ${txNonce}.`
+ }
return (
<>
-
- {`This is an empty cancelling transaction that doesn't send any funds.
- Executing this transaction will replace all currently awaiting transactions with nonce ${
- (txDetails.detailedExecutionInfo as MultiSigExecutionDetails).nonce ?? NOT_AVAILABLE
- }.`}
-
-
-
-
- Why do I need to pay for cancelling a transaction?
-
-
-
-
+ {message}
+ {!isTxExecuted && (
+ <>
+
+
+
+
+ Why do I need to pay for rejecting a transaction?
+
+
+
+
+ >
+ )}
>
)
}
@@ -66,7 +74,7 @@ const TxDataGroup = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElem
return null
}
- return
+ return
}
type TxDetailsProps = {
@@ -116,7 +124,7 @@ export const TxDetails = ({ transaction, actions }: TxDetailsProps): ReactElemen
'will-be-replaced': transaction.txStatus === 'WILL_BE_REPLACED',
})}
>
-
+
{!data.executedAt && txLocation !== 'history' && actions?.isUserAnOwner && (
diff --git a/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx b/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx
index 71e0c28c..81f7d804 100644
--- a/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxExpandedActions.tsx
@@ -41,7 +41,7 @@ export const TxExpandedActions = ({ transaction }: TxExpandedActionsProps): Reac
{canCancel && (
- Cancel
+ Reject
)}
>
diff --git a/src/routes/safe/components/Transactions/TxList/TxInfo.tsx b/src/routes/safe/components/Transactions/TxList/TxInfo.tsx
index 9a485d9a..725d6780 100644
--- a/src/routes/safe/components/Transactions/TxList/TxInfo.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxInfo.tsx
@@ -1,18 +1,10 @@
import React, { ReactElement } from 'react'
-import {
- ExpandedTxDetails,
- isSettingsChangeTxInfo,
- isTransferTxInfo,
-} from 'src/logic/safe/store/models/types/gateway.d'
+import { TransactionInfo, isSettingsChangeTxInfo, isTransferTxInfo } from 'src/logic/safe/store/models/types/gateway.d'
import { TxInfoSettings } from './TxInfoSettings'
import { TxInfoTransfer } from './TxInfoTransfer'
-type TxInfoProps = {
- txInfo: ExpandedTxDetails['txInfo']
-}
-
-export const TxInfo = ({ txInfo }: TxInfoProps): ReactElement | null => {
+export const TxInfo = ({ txInfo }: { txInfo: TransactionInfo }): ReactElement | null => {
if (isSettingsChangeTxInfo(txInfo)) {
return
}
diff --git a/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx b/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx
index 2ef7d24a..8c0a84a0 100644
--- a/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxInfoDetails.tsx
@@ -21,11 +21,20 @@ const SingleRow = styled.div`
type TxInfoDetailsProps = {
title: string
address: string
+ name?: string | undefined
+ avatarUrl?: string | undefined
isTransferType?: boolean
txInfo?: Transfer
}
-export const TxInfoDetails = ({ title, address, isTransferType, txInfo }: TxInfoDetailsProps): ReactElement => {
+export const TxInfoDetails = ({
+ title,
+ address,
+ isTransferType,
+ txInfo,
+ name,
+ avatarUrl,
+}: TxInfoDetailsProps): ReactElement => {
const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, address))
const knownAddress = recipientName !== 'UNKNOWN'
@@ -59,6 +68,7 @@ export const TxInfoDetails = ({ title, address, isTransferType, txInfo }: TxInfo
selectedToken: ZERO_ADDRESS,
tokenAmount: '0',
})
+
useEffect(() => {
if (txInfo) {
const isCollectible = txInfo.transferInfo.type === 'ERC721'
@@ -76,7 +86,7 @@ export const TxInfoDetails = ({ title, address, isTransferType, txInfo }: TxInfo
return (
-
+
{
+export const TxOwners = ({ txDetails }: { txDetails: ExpandedTxDetails }): ReactElement | null => {
+ const { txInfo, detailedExecutionInfo } = txDetails
+
if (!detailedExecutionInfo || isModuleExecutionDetails(detailedExecutionInfo)) {
return null
}
const confirmationsNeeded = detailedExecutionInfo.confirmationsRequired - detailedExecutionInfo.confirmations.length
+ const CreationNode = isCancelTxDetails(txInfo) ? (
+
+
+
+
+
+
+ On-chain rejection created
+
+
+
+ ) : (
+
+
+
+
+
+
+ Created
+
+
+
+ )
+
return (
-
-
-
-
-
-
- Created
-
-
-
+ {CreationNode}
{detailedExecutionInfo.confirmations.map(({ signer }) => (
-
+
@@ -55,7 +67,11 @@ export const TxOwners = ({ detailedExecutionInfo }: TxOwnersProps): ReactElement
{confirmationsNeeded <= 0 ? (
-
+ {detailedExecutionInfo.executor ? (
+
+ ) : (
+
+ )}
diff --git a/src/routes/safe/components/Transactions/TxList/TxSummary.tsx b/src/routes/safe/components/Transactions/TxList/TxSummary.tsx
index 84fb6cc7..4bc50b77 100644
--- a/src/routes/safe/components/Transactions/TxList/TxSummary.tsx
+++ b/src/routes/safe/components/Transactions/TxList/TxSummary.tsx
@@ -27,7 +27,7 @@ export const TxSummary = ({ txDetails }: { txDetails: ExpandedTxDetails }): Reac
)}
- {nonce && (
+ {nonce !== undefined && (
Nonce:{' '}
diff --git a/src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg b/src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg
new file mode 100644
index 00000000..ff8f6306
--- /dev/null
+++ b/src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts
index 5e3b7566..3609d6bc 100644
--- a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts
+++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionActions.ts
@@ -7,7 +7,6 @@ import { getQueuedTransactionsByNonce } from 'src/logic/safe/store/selectors/gat
import { sameAddress } from 'src/logic/wallets/ethAddresses'
import { userAccountSelector } from 'src/logic/wallets/store/selectors'
import { TxLocationContext } from 'src/routes/safe/components/Transactions/TxList/TxLocationProvider'
-import { isCancelTransaction } from 'src/routes/safe/components/Transactions/TxList/utils'
import { grantedSelector } from 'src/routes/safe/container/selector'
import { AppReduxState } from 'src/store'
@@ -60,14 +59,7 @@ export const useTransactionActions = (transaction: Transaction): TransactionActi
canConfirm,
canConfirmThenExecute: txLocation === 'queued.next' && canConfirm && oneToGo,
canExecute: txLocation === 'queued.next' && thresholdReached,
- canCancel: !transactionsByNonce.some(
- ({ txInfo }) =>
- isCustomTxInfo(txInfo) &&
- isCancelTransaction({
- txInfo,
- safeAddress,
- }),
- ),
+ canCancel: !transactionsByNonce.some(({ txInfo }) => isCustomTxInfo(txInfo) && txInfo.isCancellation),
isUserAnOwner,
oneToGo,
})
diff --git a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts
index e64637b7..4df71af3 100644
--- a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts
+++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionStatus.ts
@@ -37,10 +37,10 @@ export const useTransactionStatus = (transaction: Transaction): TransactionStatu
switch (transaction.txStatus) {
case 'AWAITING_CONFIRMATIONS':
- text = signaturePending(currentUser) ? 'Awaiting your confirmation' : 'Awaiting confirmations'
+ text = signaturePending(currentUser) ? 'Needs your confirmation' : 'Needs confirmations'
break
case 'AWAITING_EXECUTION':
- text = 'Awaiting execution'
+ text = 'Needs execution'
break
case 'PENDING':
case 'PENDING_FAILED':
diff --git a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts
index ca04efbc..8766fa38 100644
--- a/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts
+++ b/src/routes/safe/components/Transactions/TxList/hooks/useTransactionType.ts
@@ -4,13 +4,13 @@ import { useSelector } from 'react-redux'
import { Transaction } from 'src/logic/safe/store/models/types/gateway.d'
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
import CustomTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/custom.svg'
+import CircleCrossRed from 'src/routes/safe/components/Transactions/TxList/assets/circle-cross-red.svg'
import IncomingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/incoming.svg'
import OutgoingTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/outgoing.svg'
import SettingsTxIcon from 'src/routes/safe/components/Transactions/TxList/assets/settings.svg'
-import { isCancelTransaction } from 'src/routes/safe/components/Transactions/TxList/utils'
export type TxTypeProps = {
- icon: string
+ icon: string | null
text: string
}
@@ -40,20 +40,8 @@ export const useTransactionType = (tx: Transaction): TxTypeProps => {
break
}
- // TODO: isCancel
- // there are two 'cancelling' tx identification
- // this one is the candidate to remain when the client gateway implements
- // https://github.com/gnosis/safe-client-gateway/issues/255
- if (typeof tx.txInfo.isCancellation === 'boolean' && tx.txInfo.isCancellation) {
- setType({ icon: CustomTxIcon, text: 'Cancelling transaction' })
- break
- }
-
- // TODO: isCancel
- // remove the following condition when issue#255 is implemented
- // also remove `isCancelTransaction` function
- if (isCancelTransaction({ txInfo: tx.txInfo, safeAddress })) {
- setType({ icon: CustomTxIcon, text: 'Cancelling transaction' })
+ if (tx.txInfo.isCancellation) {
+ setType({ icon: CircleCrossRed, text: 'On-chain rejection' })
break
}
@@ -62,6 +50,12 @@ export const useTransactionType = (tx: Transaction): TxTypeProps => {
break
}
+ const toInfo = tx.txInfo.toInfo
+ if (toInfo) {
+ setType({ icon: toInfo.logoUri, text: toInfo.name })
+ break
+ }
+
setType({ icon: CustomTxIcon, text: 'Contract interaction' })
break
}
diff --git a/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx b/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx
index c8b24649..9816a7cd 100644
--- a/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx
+++ b/src/routes/safe/components/Transactions/TxList/modals/ApproveTxModal.tsx
@@ -229,6 +229,7 @@ export const ApproveTxModal = ({
const oneConfirmationLeft = !thresholdReached && _countingCurrentConfirmation === _threshold
const isTheTxReadyToBeExecuted = oneConfirmationLeft ? true : thresholdReached
const [manualGasPrice, setManualGasPrice] = useState()
+ const [manualGasLimit, setManualGasLimit] = useState()
const {
confirmations,
data,
@@ -262,7 +263,8 @@ export const ApproveTxModal = ({
preApprovingOwner: approveAndExecute ? userAddress : undefined,
safeTxGas,
operation,
- manualGasPrice: manualGasPrice,
+ manualGasPrice,
+ manualGasLimit,
})
const handleExecuteCheckbox = () => setApproveAndExecute((prevApproveAndExecute) => !prevApproveAndExecute)
@@ -312,6 +314,10 @@ export const ApproveTxModal = ({
if (newGasPrice && oldGasPrice !== newGasPrice) {
setManualGasPrice(newGasPrice.toString())
}
+
+ if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) {
+ setManualGasLimit(txParameters.ethGasLimit.toString())
+ }
}
return (
diff --git a/src/routes/safe/components/Transactions/TxList/styled.tsx b/src/routes/safe/components/Transactions/TxList/styled.tsx
index de8d7f19..9058bc80 100644
--- a/src/routes/safe/components/Transactions/TxList/styled.tsx
+++ b/src/routes/safe/components/Transactions/TxList/styled.tsx
@@ -163,6 +163,32 @@ const failedTransaction = css`
}
`
+const onChainRejection = css`
+ &.on-chain-rejection {
+ background-color: ${({ theme }) => theme.colors.errorTooltip};
+ border-left: 4px solid ${({ theme }) => theme.colors.error};
+ border-radius: 4px;
+ padding-left: 7px;
+ height: 22px;
+ max-width: 165px;
+
+ > div {
+ height: 17px;
+ align-items: center;
+ padding-top: 3px;
+ }
+
+ p {
+ font-size: 11px;
+ line-height: 16px;
+ letter-spacing: 1px;
+ font-weight: bold;
+ text-transform: uppercase;
+ margin-left: -2px;
+ }
+ }
+`
+
export const StyledTransaction = styled.div`
${willBeReplaced};
${failedTransaction};
@@ -175,6 +201,10 @@ export const StyledTransaction = styled.div`
align-self: center;
}
+ .tx-type {
+ ${onChainRejection};
+ }
+
.tx-votes {
justify-self: center;
}
diff --git a/src/routes/safe/components/Transactions/TxList/utils.ts b/src/routes/safe/components/Transactions/TxList/utils.ts
index d461e8cc..13b15057 100644
--- a/src/routes/safe/components/Transactions/TxList/utils.ts
+++ b/src/routes/safe/components/Transactions/TxList/utils.ts
@@ -2,7 +2,6 @@ import { BigNumber } from 'bignumber.js'
import { getNetworkInfo } from 'src/config'
import {
- Custom,
isCustomTxInfo,
isTransferTxInfo,
Transaction,
@@ -12,7 +11,6 @@ import {
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
import { sameAddress } from 'src/logic/wallets/ethAddresses'
-import { sameString } from 'src/utils/strings'
export const NOT_AVAILABLE = 'n/a'
@@ -90,27 +88,11 @@ export const getTxTokenData = (txInfo: Transfer): txTokenData => {
}
}
-// TODO: isCancel
-// how can we be sure that it's a cancel tx without asking for tx-details?
-// can the client-gateway service provide info about the tx, Like: `isCancelTransaction: boolean`?
-// it will be solved as part of: https://github.com/gnosis/safe-client-gateway/issues/255
-export const isCancelTransaction = ({ txInfo, safeAddress }: { txInfo: Custom; safeAddress: string }): boolean =>
- sameAddress(txInfo.to, safeAddress) &&
- sameString(txInfo.dataSize, '0') &&
- sameString(txInfo.value, '0') &&
- txInfo.methodName === null
-
-type IsCancelTxDetailsProps = {
- executedAt: number | null
- txInfo: Transaction['txInfo']
- safeAddress: string
-}
-export const isCancelTxDetails = ({ executedAt, txInfo, safeAddress }: IsCancelTxDetailsProps): boolean =>
- !executedAt &&
+export const isCancelTxDetails = (txInfo: Transaction['txInfo']): boolean =>
// custom transaction
isCustomTxInfo(txInfo) &&
- // verify that it's a cancel tx based on it's info
- isCancelTransaction({ safeAddress, txInfo })
+ // flag-based identification
+ txInfo.isCancellation
export const addressInList = (list: string[] = []) => (address: string): boolean =>
list.some((ownerAddress) => sameAddress(ownerAddress, address))
diff --git a/src/routes/safe/container/hooks/useTransactionParameters.ts b/src/routes/safe/container/hooks/useTransactionParameters.ts
index 1185c765..fb7215d9 100644
--- a/src/routes/safe/container/hooks/useTransactionParameters.ts
+++ b/src/routes/safe/container/hooks/useTransactionParameters.ts
@@ -81,7 +81,7 @@ export const useTransactionParameters = (props?: Props): TxParameters => {
useEffect(() => {
const getSafeNonce = async () => {
if (safeAddress) {
- const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ const safeInstance = getGnosisSafeInstanceAt(safeAddress)
const lastTx = await getLastTx(safeAddress)
const nonce = await getNewTxNonce(lastTx, safeInstance)
setSafeNonce(nonce)
diff --git a/src/test/safe.dom.create.tsx b/src/test/safe.dom.create.tsx
index d686009f..7c9d8475 100644
--- a/src/test/safe.dom.create.tsx
+++ b/src/test/safe.dom.create.tsx
@@ -130,7 +130,7 @@ describe('DOM > Feature > CREATE a Safe', () => {
expect(address).not.toBe(null)
expect(address).not.toBe(undefined)
- const gnosisSafe = await getGnosisSafeInstanceAt(address)
+ const gnosisSafe = getGnosisSafeInstanceAt(address)
const storedOwners = await gnosisSafe.methods.getOwners().call()
expect(storedOwners.length).toEqual(4)
const safeThreshold = await gnosisSafe.methods.getThreshold().call()
diff --git a/src/theme/variables.js b/src/theme/variables.js
index 71f4625d..66811e2a 100644
--- a/src/theme/variables.js
+++ b/src/theme/variables.js
@@ -5,7 +5,7 @@ const disabled = '#5D6D74'
const errorColor = '#f02525'
const fancyColor = '#f02525'
const fontColor = '#001428'
-const headerHeight = '53px'
+const headerHeight = '52px'
const lg = '24px'
const marginButtonImg = '12px'
const md = '16px'
diff --git a/src/theme/variables.scss b/src/theme/variables.scss
index ad157751..f94de36c 100644
--- a/src/theme/variables.scss
+++ b/src/theme/variables.scss
@@ -13,7 +13,7 @@ $warning: #ffc05f;
$fancy: #f02525;
$secondary: #008C73;
-$headerHeight: 53px;
+$headerHeight: 52px;
$marginButtonImg: 12px;
$lg: 24px;
diff --git a/yarn.lock b/yarn.lock
index 00a9ec29..79aa2e77 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1596,9 +1596,9 @@
solc "0.5.14"
truffle "^5.1.21"
-"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#80f5db6":
+"@gnosis.pm/safe-react-components@https://github.com/gnosis/safe-react-components.git#2e427ee":
version "0.5.0"
- resolved "https://github.com/gnosis/safe-react-components.git#80f5db672d417ea410d58c8d713e46e16e3c7e7f"
+ resolved "https://github.com/gnosis/safe-react-components.git#2e427ee36694c7964301fc155b0c080101a34bed"
dependencies:
classnames "^2.2.6"
react-media "^1.10.0"