This repository was archived by the owner on Nov 10, 2023. It is now read-only.
  
  
  - 
                Notifications
    You must be signed in to change notification settings 
- Fork 361
Feature/2397 display guard information in settings #2662
          
     Merged
      
      
            katspaugh
  merged 18 commits into
  dev
from
feature/2397-display-guard-information-in-settings
  
      
      
   
  Sep 13, 2021 
      
    
  
     Merged
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            18 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      dda9501
              
                Added transaction guard to advanced settings
              
              
                juampibermani 11e66f6
              
                Merge branch 'dev' into feature/2397-display-guard-information-in-set…
              
              
                juampibermani f45b4af
              
                Get guard from the unified gateway
              
              
                juampibermani b0ab537
              
                Fixed tests
              
              
                juampibermani 2510e78
              
                Added TransactionGuard component
              
              
                juampibermani 0a0966d
              
                WIP: created component without contract instance
              
              
                juampibermani b9d1184
              
                Merge branch 'dev' into feature/2397-display-guard-information-in-set…
              
              
                juampibermani 320187d
              
                Call safe method to set guard tx address
              
              
                juampibermani 5208e47
              
                Deleted console log
              
              
                juampibermani c769dfd
              
                Added description to remove guard modal
              
              
                juampibermani c49f2c0
              
                Update gateway-sdk to v2.1.0
              
              
                 078d761
              
                Fix remove guard function
              
              
                 e77e8dc
              
                Minor fixes
              
              
                juampibermani cee6eb0
              
                Merge branch 'feature/2397-display-guard-information-in-settings' of …
              
              
                juampibermani 6819f3c
              
                Replace guard if fetchedSafe has a null guard
              
              
                juampibermani fae9148
              
                Fixed some tests
              
              
                juampibermani 8377a06
              
                Fixed test
              
              
                juampibermani f2af702
              
                Merge branch 'dev' into feature/2397-display-guard-information-in-set…
              
              
                juampibermani File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' | ||
| import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' | ||
|  | ||
| export const getSetGuardTxData = (guardAddress: string, safeAddress: string, safeVersion: string): string => { | ||
| const safeInstance = getGnosisSafeInstanceAt(safeAddress, safeVersion) | ||
|  | ||
| return safeInstance.methods.setGuard(guardAddress).encodeABI() | ||
| } | ||
|  | ||
| export const getRemoveGuardTxData = (safeAddress: string, safeVersion: string): string => { | ||
| return getSetGuardTxData(ZERO_ADDRESS, safeAddress, safeVersion) | ||
| } | 
        
          
          
            191 changes: 191 additions & 0 deletions
          
          191 
        
  src/routes/safe/components/Settings/Advanced/RemoveGuardModal.tsx
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| import { EthHashInfo } from '@gnosis.pm/safe-react-components' | ||
| import IconButton from '@material-ui/core/IconButton' | ||
| import Close from '@material-ui/icons/Close' | ||
| import cn from 'classnames' | ||
| import React, { ReactElement, 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 Hairline from 'src/components/layout/Hairline' | ||
| import Paragraph from 'src/components/layout/Paragraph' | ||
| import Row from 'src/components/layout/Row' | ||
| import Modal, { ButtonStatus, Modal as GenericModal } from 'src/components/Modal' | ||
| import { getExplorerInfo } from 'src/config' | ||
| import { createTransaction } from 'src/logic/safe/store/actions/createTransaction' | ||
|  | ||
| import { currentSafe } from 'src/logic/safe/store/selectors' | ||
| import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions' | ||
|  | ||
| import { useStyles } from './style' | ||
| import { EstimationStatus, useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' | ||
| import { useEstimationStatus } from 'src/logic/hooks/useEstimationStatus' | ||
| import { TransactionFees } from 'src/components/TransactionsFees' | ||
| import { TxParametersDetail } from 'src/routes/safe/components/Transactions/helpers/TxParametersDetail' | ||
| import { EditableTxParameters } from 'src/routes/safe/components/Transactions/helpers/EditableTxParameters' | ||
| import { TxParameters } from 'src/routes/safe/container/hooks/useTransactionParameters' | ||
| import { getRemoveGuardTxData } from 'src/logic/safe/utils/guardManager' | ||
| import { Errors, logError } from 'src/logic/exceptions/CodedException' | ||
|  | ||
| interface RemoveGuardModalProps { | ||
| onClose: () => void | ||
| guardAddress: string | ||
| } | ||
|  | ||
| export const RemoveGuardModal = ({ onClose, guardAddress }: RemoveGuardModalProps): ReactElement => { | ||
| const classes = useStyles() | ||
|  | ||
| const { address: safeAddress, currentVersion: safeVersion } = useSelector(currentSafe) | ||
| const dispatch = useDispatch() | ||
| const [manualSafeTxGas, setManualSafeTxGas] = useState(0) | ||
| const [manualGasPrice, setManualGasPrice] = useState<string | undefined>() | ||
| const [manualGasLimit, setManualGasLimit] = useState<string | undefined>() | ||
|  | ||
| const txData = useMemo(() => getRemoveGuardTxData(safeAddress, safeVersion), [safeAddress, safeVersion]) | ||
|  | ||
| const { | ||
| gasCostFormatted, | ||
| txEstimationExecutionStatus, | ||
| isExecution, | ||
| isOffChainSignature, | ||
| isCreation, | ||
| gasLimit, | ||
| gasEstimation, | ||
| gasPriceFormatted, | ||
| } = useEstimateTransactionGas({ | ||
| txData, | ||
| txRecipient: safeAddress, | ||
| txAmount: '0', | ||
| safeTxGas: manualSafeTxGas, | ||
| manualGasPrice, | ||
| manualGasLimit, | ||
| }) | ||
|  | ||
| const [buttonStatus] = useEstimationStatus(txEstimationExecutionStatus) | ||
|  | ||
| const removeTransactionGuard = async (txParameters: TxParameters): Promise<void> => { | ||
| try { | ||
| dispatch( | ||
| createTransaction({ | ||
| safeAddress, | ||
| to: safeAddress, | ||
| valueInWei: '0', | ||
| txData, | ||
| txNonce: txParameters.safeNonce, | ||
| safeTxGas: txParameters.safeTxGas ? Number(txParameters.safeTxGas) : undefined, | ||
| ethParameters: txParameters, | ||
| notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX, | ||
| }), | ||
| ) | ||
| } catch (e) { | ||
| logError(Errors._807, `${guardAddress} – ${e.message}`) | ||
| } | ||
| } | ||
|  | ||
| const closeEditModalCallback = (txParameters: TxParameters) => { | ||
| const oldGasPrice = Number(gasPriceFormatted) | ||
| const newGasPrice = Number(txParameters.ethGasPrice) | ||
| const oldSafeTxGas = Number(gasEstimation) | ||
| const newSafeTxGas = Number(txParameters.safeTxGas) | ||
|  | ||
| if (newGasPrice && oldGasPrice !== newGasPrice) { | ||
| setManualGasPrice(txParameters.ethGasPrice) | ||
| } | ||
|  | ||
| if (txParameters.ethGasLimit && gasLimit !== txParameters.ethGasLimit) { | ||
| setManualGasLimit(txParameters.ethGasLimit) | ||
| } | ||
|  | ||
| if (newSafeTxGas && oldSafeTxGas !== newSafeTxGas) { | ||
| setManualSafeTxGas(newSafeTxGas) | ||
| } | ||
| } | ||
|  | ||
| let confirmButtonText = 'Remove' | ||
| if (ButtonStatus.LOADING === buttonStatus) { | ||
| confirmButtonText = txEstimationExecutionStatus === EstimationStatus.LOADING ? 'Estimating' : 'Removing' | ||
| } | ||
|  | ||
| return ( | ||
| <Modal | ||
| description="Remove the selected Transaction Guard" | ||
| handleClose={onClose} | ||
| paperClassName="modal" | ||
| title="Remove Transaction Guard" | ||
| open | ||
| > | ||
| <EditableTxParameters | ||
| isOffChainSignature={isOffChainSignature} | ||
| isExecution={isExecution} | ||
| ethGasLimit={gasLimit} | ||
| ethGasPrice={gasPriceFormatted} | ||
| safeTxGas={gasEstimation.toString()} | ||
| closeEditModalCallback={closeEditModalCallback} | ||
| > | ||
| {(txParameters, toggleEditMode) => { | ||
| return ( | ||
| <> | ||
| <Row align="center" className={classes.modalHeading} grow> | ||
| <Paragraph className={classes.modalManage} noMargin weight="bolder"> | ||
| Remove Transaction Guard | ||
| </Paragraph> | ||
| <IconButton disableRipple onClick={onClose}> | ||
| <Close className={classes.modalClose} /> | ||
| </IconButton> | ||
| </Row> | ||
| <Hairline /> | ||
| <Block> | ||
| <Row className={classes.modalOwner}> | ||
| <Col align="center" xs={1}> | ||
| <EthHashInfo | ||
| hash={guardAddress} | ||
| showCopyBtn | ||
| showAvatar | ||
| explorerUrl={getExplorerInfo(guardAddress)} | ||
| /> | ||
| </Col> | ||
| </Row> | ||
| <Row className={classes.modalDescription}> | ||
| <Paragraph noMargin size="lg"> | ||
| Once the transaction guard has been removed, checks by the transaction guard will not be conducted | ||
| before or after any subsequent transactions. | ||
| </Paragraph> | ||
| </Row> | ||
| </Block> | ||
| <Block className={classes.accordionContainer}> | ||
| {/* Tx Parameters */} | ||
| <TxParametersDetail | ||
| txParameters={txParameters} | ||
| onEdit={toggleEditMode} | ||
| isTransactionCreation={isCreation} | ||
| isTransactionExecution={isExecution} | ||
| isOffChainSignature={isOffChainSignature} | ||
| /> | ||
| </Block> | ||
| <Row className={cn(classes.modalDescription, classes.gasCostsContainer)}> | ||
| <TransactionFees | ||
| gasCostFormatted={gasCostFormatted} | ||
| isExecution={isExecution} | ||
| isCreation={isCreation} | ||
| isOffChainSignature={isOffChainSignature} | ||
| txEstimationExecutionStatus={txEstimationExecutionStatus} | ||
| /> | ||
| </Row> | ||
| <GenericModal.Footer withoutBorder={buttonStatus !== ButtonStatus.LOADING}> | ||
| <GenericModal.Footer.Buttons | ||
| cancelButtonProps={{ onClick: onClose }} | ||
| confirmButtonProps={{ | ||
| color: 'error', | ||
| onClick: () => removeTransactionGuard(txParameters), | ||
| status: buttonStatus, | ||
| text: confirmButtonText, | ||
| }} | ||
| /> | ||
| </GenericModal.Footer> | ||
| </> | ||
| ) | ||
| }} | ||
| </EditableTxParameters> | ||
| </Modal> | ||
| ) | ||
| } | ||
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
        
          
          
            82 changes: 82 additions & 0 deletions
          
          82 
        
  src/routes/safe/components/Settings/Advanced/TransactionGuard.tsx
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import { Icon, EthHashInfo } from '@gnosis.pm/safe-react-components' | ||
| import TableContainer from '@material-ui/core/TableContainer' | ||
| import cn from 'classnames' | ||
| import React from 'react' | ||
| import { useSelector } from 'react-redux' | ||
|  | ||
| import { generateColumns } from './dataFetcher' | ||
| import { RemoveGuardModal } from './RemoveGuardModal' | ||
| import { useStyles } from './style' | ||
|         
                  juampibermani marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
|  | ||
| import ButtonHelper from 'src/components/ButtonHelper' | ||
| import { grantedSelector } from 'src/routes/safe/container/selector' | ||
| import Table from 'src/components/Table' | ||
| import { TableCell, TableRow } from 'src/components/layout/Table' | ||
| import Block from 'src/components/layout/Block' | ||
| import Row from 'src/components/layout/Row' | ||
| import { getExplorerInfo } from 'src/config' | ||
|  | ||
| const REMOVE_GUARD_BTN_TEST_ID = 'remove-guard-btn' | ||
| const GUARDS_ROW_TEST_ID = 'guards-row' | ||
|  | ||
| interface TransactionGuardProps { | ||
| address: string | ||
| } | ||
|  | ||
| export const TransactionGuard = ({ address }: TransactionGuardProps): React.ReactElement => { | ||
| const classes = useStyles() | ||
|  | ||
| const columns = generateColumns() | ||
| const autoColumns = columns.filter(({ custom }) => !custom) | ||
|  | ||
| const granted = useSelector(grantedSelector) | ||
|  | ||
| const [viewRemoveGuardModal, setViewRemoveGuardModal] = React.useState(false) | ||
| const hideRemoveGuardModal = () => setViewRemoveGuardModal(false) | ||
|  | ||
| const triggerRemoveSelectedGuard = (): void => { | ||
| setViewRemoveGuardModal(true) | ||
| } | ||
|  | ||
| return ( | ||
| <> | ||
|         
                  mmv08 marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| <TableContainer> | ||
| <Table columns={columns} data={[address]} defaultFixed disablePagination label="Modules" noBorder> | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know it was written on the ticket, but we don't really need a table there. It makes things complicated. There's only one transaction guard per safe, so it will always use 1 row. This is not required, but you can think about how we can remove it if you want. | ||
| {(sortedData) => | ||
| sortedData.map((row, index) => ( | ||
| <TableRow | ||
| className={cn(classes.hide, index >= 3 && index === sortedData.size - 1 && classes.noBorderBottom)} | ||
| data-testid={GUARDS_ROW_TEST_ID} | ||
| key={index} | ||
| tabIndex={-1} | ||
| > | ||
| {autoColumns.map((column, index) => { | ||
| const columnId = column.id | ||
| return ( | ||
| <React.Fragment key={`${columnId}-${index}`}> | ||
| <TableCell align={column.align} component="td" key={columnId}> | ||
| <Block justify="left"> | ||
| <EthHashInfo hash={row} showCopyBtn showAvatar explorerUrl={getExplorerInfo(row)} /> | ||
| </Block> | ||
| </TableCell> | ||
| <TableCell component="td"> | ||
| <Row align="end" className={classes.actions}> | ||
| {granted && ( | ||
| <ButtonHelper onClick={triggerRemoveSelectedGuard} data-testid={REMOVE_GUARD_BTN_TEST_ID}> | ||
| <Icon size="sm" type="delete" color="error" tooltip="Remove module" /> | ||
| </ButtonHelper> | ||
| )} | ||
| </Row> | ||
| </TableCell> | ||
| </React.Fragment> | ||
| ) | ||
| })} | ||
| </TableRow> | ||
| )) | ||
| } | ||
| </Table> | ||
| </TableContainer> | ||
| {viewRemoveGuardModal && address && <RemoveGuardModal onClose={hideRemoveGuardModal} guardAddress={address} />} | ||
| </> | ||
| ) | ||
| } | ||
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a lot of code duplication from the Review modal. For this PR it's fine but we should definitely think of a way to reuse the same code for tx popups.