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')
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}
diff --git a/src/components/new-safe/create/steps/ReviewStep/index.tsx b/src/components/new-safe/create/steps/ReviewStep/index.tsx
index 29691bc83d..5284d71cd0 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,9 +22,10 @@ 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'
+import { hasRemainingRelays } from '@/utils/relaying'
const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps) => {
const isWrongChain = useIsWrongChain()
@@ -35,12 +36,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
+ // Every owner has remaining relays and relay method is selected
+ const canRelay = hasRemainingRelays(minRelays)
+ const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY
const safeParams = useMemo(() => {
return {
@@ -121,8 +124,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..75c6efecc2 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 ed19a2cdda..0e13af5d62 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'
@@ -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
@@ -71,16 +72,14 @@ const SignOrExecuteForm = ({
// If checkbox is checked and the transaction is executable, execute it, otherwise sign it
const willExecute = (onlyExecute || shouldExecute) && canExecute
- // SC wallets can relay fully signed transactions
- const [walletCanRelay] = useWalletCanRelay(tx)
-
- // The transaction can be relayed
- const canRelay = willExecute && !!relays && relays.remaining > 0 && !!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
+ // SC wallets can relay fully signed transactions
+ const [walletCanRelay] = useWalletCanRelay(tx)
+
+ // The transaction can/will be relayed
+ const canRelay = hasRemainingRelays(relays) && !!walletCanRelay && willExecute
const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY
// Synchronize the tx with the safeTx
@@ -183,11 +182,21 @@ const SignOrExecuteForm = ({
/>
{canRelay && (
-
+ div': {
+ marginTop: '-1px',
+ borderTopLeftRadius: 0,
+ borderTopRightRadius: 0,
+ },
+ }}
+ >
+
+
)}
{
@@ -21,7 +22,7 @@ const SponsoredBy = ({ relays, tooltip }: { relays: RelayResponse; tooltip?: str
Sponsored by
-
+
{chain?.chainName}
diff --git a/src/components/tx/SponsoredBy/styles.module.css b/src/components/tx/SponsoredBy/styles.module.css
index 29d150709a..2df83cc5b5 100644
--- a/src/components/tx/SponsoredBy/styles.module.css
+++ b/src/components/tx/SponsoredBy/styles.module.css
@@ -1,8 +1,9 @@
.sponsoredBy {
padding: 8px 12px;
background-color: var(--color-background-main);
- border-radius: 6px;
- border: 1px solid var(--color-border-light);
+ border-bottom-left-radius: 6px;
+ border-bottom-right-radius: 6px;
+ border-top: 1px solid var(--color-border-light);
display: flex;
}
diff --git a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx
index 64c136df00..b38e0603d9 100644
--- a/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx
+++ b/src/components/tx/modals/BatchExecuteModal/ReviewBatchExecute.tsx
@@ -15,21 +15,24 @@ import DecodedTxs from '@/components/tx/modals/BatchExecuteModal/DecodedTxs'
import { getMultiSendTxs, getTxsWithDetails } from '@/utils/transactions'
import { TxSimulation } from '@/components/tx/TxSimulation'
import { useRelaysBySafe } from '@/hooks/useRemainingRelays'
-import SponsoredBy from '@/components/tx/SponsoredBy'
+import { ExecutionMethod, ExecutionMethodSelector } from '../../ExecutionMethodSelector'
import { dispatchBatchExecution, dispatchBatchExecutionRelay } from '@/services/tx/tx-sender'
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)
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 = hasRemainingRelays(relays)
+ const willRelay = canRelay && executionMethod === ExecutionMethod.RELAY
const onboard = useOnboard()
const web3 = useWeb3()
@@ -125,12 +128,14 @@ const ReviewBatchExecute = ({ data, onSubmit }: { data: BatchExecuteData; onSubm
- {willRelay ? (
+ {canRelay ? (
<>
Gas fees:
- {
+ return !!relays && relays.remaining > 0
+}