From d7b9cbaaec738bbb7a7dfd95573fe715861cd4dd Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 28 Apr 2023 12:21:16 +0200 Subject: [PATCH 1/6] feat: execution method for creation + batches --- .../create/steps/ReviewStep/index.tsx | 27 ++++++++++++++----- .../tx/ExecutionMethodSelector/index.tsx | 27 ++++++++++--------- .../ExecutionMethodSelector/styles.module.css | 21 +++++++-------- src/components/tx/SignOrExecuteForm/index.tsx | 22 ++++++++++----- .../tx/SponsoredBy/styles.module.css | 5 ++-- .../BatchExecuteModal/ReviewBatchExecute.tsx | 12 ++++++--- 6 files changed, 71 insertions(+), 43 deletions(-) diff --git a/src/components/new-safe/create/steps/ReviewStep/index.tsx b/src/components/new-safe/create/steps/ReviewStep/index.tsx index 29691bc83d..c7d6a3f1cf 100644 --- a/src/components/new-safe/create/steps/ReviewStep/index.tsx +++ b/src/components/new-safe/create/steps/ReviewStep/index.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react' +import { useMemo, useState } from 'react' import { Button, Grid, Typography, Divider, Box } from '@mui/material' import { lightPalette } from '@safe-global/safe-react-components' import ChainIndicator from '@/components/common/ChainIndicator' @@ -22,7 +22,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack' import NetworkWarning from '@/components/new-safe/create/NetworkWarning' import useIsWrongChain from '@/hooks/useIsWrongChain' import ReviewRow from '@/components/new-safe/ReviewRow' -import SponsoredBy from '@/components/tx/SponsoredBy' +import { ExecutionMethodSelector, ExecutionMethod } from '@/components/tx/ExecutionMethodSelector' import { useLeastRemainingRelays } from '@/hooks/useRemainingRelays' import classnames from 'classnames' @@ -35,12 +35,14 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps Date.now(), []) const [_, setPendingSafe] = useLocalStorage(SAFE_PENDING_CREATION_STORAGE_KEY) + const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY) const ownerAddresses = useMemo(() => data.owners.map((owner) => owner.address), [data.owners]) const [minRelays] = useLeastRemainingRelays(ownerAddresses) // Chain supports relaying and relay transactions are available - const willRelay = minRelays && minRelays.remaining > 0 + const canRelay = minRelays && minRelays.remaining > 0 + const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY const safeParams = useMemo(() => { return { @@ -121,8 +123,22 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps - - + + {canRelay && ( + + + } + /> + + )} + } /> - {willRelay ? } /> : null} diff --git a/src/components/tx/ExecutionMethodSelector/index.tsx b/src/components/tx/ExecutionMethodSelector/index.tsx index fb1426f9e1..118ff31b42 100644 --- a/src/components/tx/ExecutionMethodSelector/index.tsx +++ b/src/components/tx/ExecutionMethodSelector/index.tsx @@ -1,4 +1,3 @@ -import classNames from 'classnames' import { Box, FormControl, FormControlLabel, SvgIcon, Radio, RadioGroup, Typography } from '@mui/material' import type { Dispatch, SetStateAction, ReactElement, ChangeEvent } from 'react' @@ -19,10 +18,14 @@ export const ExecutionMethodSelector = ({ executionMethod, setExecutionMethod, relays, + noLabel, + tooltip, }: { executionMethod: ExecutionMethod setExecutionMethod: Dispatch> relays?: RelayResponse + noLabel?: boolean + tooltip?: string }): ReactElement | null => { const wallet = useWallet() @@ -33,18 +36,20 @@ export const ExecutionMethodSelector = ({ } return ( - - `${shape.borderRadius}px` }}> + `${shape.borderRadius}px` }}> + - palette.text.secondary }}> - Choose execution method - + {!noLabel ? ( + + Choose execution method + + ) : null} + Relayer @@ -55,7 +60,7 @@ export const ExecutionMethodSelector = ({ sx={{ flex: 1 }} value={ExecutionMethod.WALLET} label={ - + Connected wallet } @@ -65,11 +70,7 @@ export const ExecutionMethodSelector = ({ - {shouldRelay && relays ? ( - - - - ) : null} + {shouldRelay && relays ? : null} ) } diff --git a/src/components/tx/ExecutionMethodSelector/styles.module.css b/src/components/tx/ExecutionMethodSelector/styles.module.css index 82c7f59d1f..76109da156 100644 --- a/src/components/tx/ExecutionMethodSelector/styles.module.css +++ b/src/components/tx/ExecutionMethodSelector/styles.module.css @@ -1,22 +1,19 @@ .container { border: 1px solid var(--color-border-light); - padding: var(--space-2) var(--space-2) var(--space-1) var(--space-2); - margin-top: -1px; +} + +.method { + padding: var(--space-1) var(--space-2); } .label { + padding-top: var(--space-1); + color: var(--color-text-secondary); +} + +.radioLabel { font-weight: 700; display: flex; align-items: center; gap: calc(var(--space-1) / 2); } - -.noTopBorderRadius > div { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.noBorderRadius > div { - border-radius: 0; -} diff --git a/src/components/tx/SignOrExecuteForm/index.tsx b/src/components/tx/SignOrExecuteForm/index.tsx index d7bb1192ad..7b78c7c215 100644 --- a/src/components/tx/SignOrExecuteForm/index.tsx +++ b/src/components/tx/SignOrExecuteForm/index.tsx @@ -1,5 +1,5 @@ import { type ReactElement, type ReactNode, type SyntheticEvent, useEffect, useState } from 'react' -import { Button, DialogContent, Typography } from '@mui/material' +import { Box, Button, DialogContent, Typography } from '@mui/material' import type { SafeTransaction } from '@safe-global/safe-core-sdk-types' import useGasLimit from '@/hooks/useGasLimit' @@ -183,11 +183,21 @@ const SignOrExecuteForm = ({ /> {canRelay && ( - + div': { + marginTop: '-1px', + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + }, + }} + > + + )} void }) => { const [isSubmittable, setIsSubmittable] = useState(true) const [submitError, setSubmitError] = useState() + const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY) const chain = useCurrentChain() const { safe } = useSafeInfo() const [relays] = useRelaysBySafe() // Chain has relaying feature and available relays - const willRelay = relays && relays.remaining > 0 + const canRelay = relays && relays.remaining > 0 + const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY const onboard = useOnboard() const web3 = useWeb3() @@ -125,12 +127,14 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm - {willRelay ? ( + {canRelay ? ( <> Gas fees: - Date: Fri, 28 Apr 2023 15:40:01 +0200 Subject: [PATCH 2/6] fix: extract helper, update comment + grammar --- src/components/new-safe/create/steps/ReviewStep/index.tsx | 5 +++-- src/components/tx/ExecutionMethodSelector/index.tsx | 2 +- src/components/tx/SignOrExecuteForm/index.tsx | 7 ++++--- .../tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx | 3 ++- src/utils/relaying.ts | 5 +++++ 5 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 src/utils/relaying.ts diff --git a/src/components/new-safe/create/steps/ReviewStep/index.tsx b/src/components/new-safe/create/steps/ReviewStep/index.tsx index c7d6a3f1cf..5284d71cd0 100644 --- a/src/components/new-safe/create/steps/ReviewStep/index.tsx +++ b/src/components/new-safe/create/steps/ReviewStep/index.tsx @@ -25,6 +25,7 @@ import ReviewRow from '@/components/new-safe/ReviewRow' import { ExecutionMethodSelector, ExecutionMethod } from '@/components/tx/ExecutionMethodSelector' import { useLeastRemainingRelays } from '@/hooks/useRemainingRelays' import classnames from 'classnames' +import { hasRemainingRelays } from '@/utils/relaying' const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps) => { const isWrongChain = useIsWrongChain() @@ -40,8 +41,8 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps data.owners.map((owner) => owner.address), [data.owners]) const [minRelays] = useLeastRemainingRelays(ownerAddresses) - // Chain supports relaying and relay transactions are available - const canRelay = minRelays && minRelays.remaining > 0 + // Every owner has remaining relays and relay method is selected + const canRelay = hasRemainingRelays(minRelays) const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY const safeParams = useMemo(() => { diff --git a/src/components/tx/ExecutionMethodSelector/index.tsx b/src/components/tx/ExecutionMethodSelector/index.tsx index 118ff31b42..75c6efecc2 100644 --- a/src/components/tx/ExecutionMethodSelector/index.tsx +++ b/src/components/tx/ExecutionMethodSelector/index.tsx @@ -41,7 +41,7 @@ export const ExecutionMethodSelector = ({ {!noLabel ? ( - Choose execution method + Choose execution method: ) : null} diff --git a/src/components/tx/SignOrExecuteForm/index.tsx b/src/components/tx/SignOrExecuteForm/index.tsx index 7b78c7c215..c1253e98d0 100644 --- a/src/components/tx/SignOrExecuteForm/index.tsx +++ b/src/components/tx/SignOrExecuteForm/index.tsx @@ -21,6 +21,7 @@ import UnknownContractError from './UnknownContractError' import { useRelaysBySafe } from '@/hooks/useRemainingRelays' import useWalletCanRelay from '@/hooks/useWalletCanRelay' import { ExecutionMethod, ExecutionMethodSelector } from '../ExecutionMethodSelector' +import { hasRemainingRelays } from '@/utils/relaying' type SignOrExecuteProps = { safeTx?: SafeTransaction @@ -75,13 +76,13 @@ const SignOrExecuteForm = ({ const [walletCanRelay] = useWalletCanRelay(tx) // The transaction can be relayed - const canRelay = willExecute && !!relays && relays.remaining > 0 && !!walletCanRelay + const canRelay = hasRemainingRelays(relays) && !!walletCanRelay // We default to relay, but the option is only shown if we canRelay const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY) // The transaction will be executed through relaying - const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY + const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY && willExecute // Synchronize the tx with the safeTx useEffect(() => setTx(safeTx), [safeTx]) @@ -182,7 +183,7 @@ const SignOrExecuteForm = ({ willRelay={willRelay} /> - {canRelay && ( + {willExecute && canRelay && ( div': { diff --git a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx index c00a47902e..b38e0603d9 100644 --- a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx +++ b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx @@ -20,6 +20,7 @@ import { dispatchBatchExecution, dispatchBatchExecutionRelay } from '@/services/ import useOnboard from '@/hooks/wallets/useOnboard' import { WrongChainWarning } from '@/components/tx/WrongChainWarning' import { useWeb3 } from '@/hooks/wallets/web3' +import { hasRemainingRelays } from '@/utils/relaying' const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubmit: (data: null) => void }) => { const [isSubmittable, setIsSubmittable] = useState(true) @@ -30,7 +31,7 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm const [relays] = useRelaysBySafe() // Chain has relaying feature and available relays - const canRelay = relays && relays.remaining > 0 + const canRelay = hasRemainingRelays(relays) const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY const onboard = useOnboard() const web3 = useWeb3() diff --git a/src/utils/relaying.ts b/src/utils/relaying.ts new file mode 100644 index 0000000000..84b7db00aa --- /dev/null +++ b/src/utils/relaying.ts @@ -0,0 +1,5 @@ +import type { RelayResponse } from '@/services/tx/relaying' + +export const hasRemainingRelays = (relays?: RelayResponse): boolean => { + return !!relays && relays.remaining > 0 +} From 86b1bfd56861f9adc0618e9bea52499a089201d1 Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 28 Apr 2023 15:55:24 +0200 Subject: [PATCH 3/6] fix: code structure --- src/components/tx/SignOrExecuteForm/index.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/tx/SignOrExecuteForm/index.tsx b/src/components/tx/SignOrExecuteForm/index.tsx index c1253e98d0..19f720074f 100644 --- a/src/components/tx/SignOrExecuteForm/index.tsx +++ b/src/components/tx/SignOrExecuteForm/index.tsx @@ -72,16 +72,14 @@ const SignOrExecuteForm = ({ // If checkbox is checked and the transaction is executable, execute it, otherwise sign it const willExecute = (onlyExecute || shouldExecute) && canExecute + // We default to relay, but the option is only shown if we canRelay + const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY) + // SC wallets can relay fully signed transactions const [walletCanRelay] = useWalletCanRelay(tx) - // The transaction can be relayed + // The transaction can/will be relayed const canRelay = hasRemainingRelays(relays) && !!walletCanRelay - - // We default to relay, but the option is only shown if we canRelay - const [executionMethod, setExecutionMethod] = useState(ExecutionMethod.RELAY) - - // The transaction will be executed through relaying const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY && willExecute // Synchronize the tx with the safeTx From 6e92d79c013a33e881d76719d02bfc52e2469b07 Mon Sep 17 00:00:00 2001 From: iamacook Date: Fri, 28 Apr 2023 15:59:43 +0200 Subject: [PATCH 4/6] fix: variable --- src/components/tx/SignOrExecuteForm/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/tx/SignOrExecuteForm/index.tsx b/src/components/tx/SignOrExecuteForm/index.tsx index 19f720074f..1666a84227 100644 --- a/src/components/tx/SignOrExecuteForm/index.tsx +++ b/src/components/tx/SignOrExecuteForm/index.tsx @@ -79,8 +79,8 @@ const SignOrExecuteForm = ({ const [walletCanRelay] = useWalletCanRelay(tx) // The transaction can/will be relayed - const canRelay = hasRemainingRelays(relays) && !!walletCanRelay - const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY && willExecute + const canRelay = hasRemainingRelays(relays) && !!walletCanRelay && willExecute + const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY // Synchronize the tx with the safeTx useEffect(() => setTx(safeTx), [safeTx]) @@ -181,7 +181,7 @@ const SignOrExecuteForm = ({ willRelay={willRelay} /> - {willExecute && canRelay && ( + {canRelay && ( div': { From 1d37acb884c48fd84a7c5ba6f0af62de639a363e Mon Sep 17 00:00:00 2001 From: iamacook Date: Tue, 2 May 2023 10:08:11 +0200 Subject: [PATCH 5/6] fix: add sponsor logo for Goerli --- src/components/dashboard/Relaying/index.tsx | 7 +++++-- src/components/tx/SponsoredBy/index.tsx | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/dashboard/Relaying/index.tsx b/src/components/dashboard/Relaying/index.tsx index 7b746d2bea..07e955bb1b 100644 --- a/src/components/dashboard/Relaying/index.tsx +++ b/src/components/dashboard/Relaying/index.tsx @@ -8,10 +8,13 @@ import GasStationIcon from '@/public/images/common/gas-station.svg' import ExternalLink from '@/components/common/ExternalLink' import classnames from 'classnames' import css from './styles.module.css' +import { useCurrentChain } from '@/hooks/useChains' +import { SPONSOR_LOGOS } from '@/components/tx/SponsoredBy' const RELAYING_HELP_ARTICLE = 'https://help.safe.global/en/articles/7224713-what-is-gas-fee-sponsoring' const Relaying = () => { + const chain = useCurrentChain() const [relays, relaysError] = useRelaysBySafe() const limit = relays?.limit || MAX_HOUR_RELAYS @@ -32,9 +35,9 @@ const Relaying = () => { Gas fees sponsored by - Gnosis Chain + {chain?.chainName} - Gnosis Chain + {chain?.chainName} diff --git a/src/components/tx/SponsoredBy/index.tsx b/src/components/tx/SponsoredBy/index.tsx index 5a4f54bee3..95aab41f32 100644 --- a/src/components/tx/SponsoredBy/index.tsx +++ b/src/components/tx/SponsoredBy/index.tsx @@ -6,8 +6,9 @@ import chains from '@/config/chains' import { useCurrentChain } from '@/hooks/useChains' import type { RelayResponse } from '@/services/tx/relaying' -const LOGOS = { +export const SPONSOR_LOGOS = { [chains.gno]: '/images/common/gnosis-chain-logo.png', + [chains.gor]: '/images/common/token-placeholder.svg', } const SponsoredBy = ({ relays, tooltip }: { relays: RelayResponse; tooltip?: string }) => { @@ -21,7 +22,7 @@ const SponsoredBy = ({ relays, tooltip }: { relays: RelayResponse; tooltip?: str Sponsored by - {chain?.chainName} + {chain?.chainName} {chain?.chainName} From f65888a906fde7b56b8115f4db0598369abf2dee Mon Sep 17 00:00:00 2001 From: iamacook Date: Wed, 3 May 2023 12:21:57 +0200 Subject: [PATCH 6/6] fix: smoke test --- cypress/e2e/smoke/create_tx.cy.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index 49b879bb2d..0cb795d874 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -102,7 +102,6 @@ describe('Queue a transaction on 1/N', () => { // Asserting the sponsored info is present cy.contains('Sponsored by').should('be.visible') - cy.contains('Gnosis Chain').should('be.visible') cy.get('span').contains('Estimated fee').next().should('have.css', 'text-decoration-line', 'line-through') cy.contains('Transactions per hour')