Skip to content

Commit

Permalink
feat: skip recovery transactions (#2837)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamacook authored Nov 21, 2023
1 parent a32b713 commit 404aa9c
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/components/recovery/SkipRecoveryButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ErrorIcon from '@/public/images/notifications/error.svg'
import IconButton from '@mui/material/IconButton'
import CheckWallet from '@/components/common/CheckWallet'
import { TxModalContext } from '@/components/tx-flow'
import { SkipRecoveryFlow } from '@/components/tx-flow/flows/SkipRecovery'
import type { RecoveryQueueItem } from '@/store/recoverySlice'

export function SkipRecoveryButton({
Expand All @@ -21,8 +22,7 @@ export function SkipRecoveryButton({
e.stopPropagation()
e.preventDefault()

// TODO: Implement skip recovery flow
setTxFlow(undefined)
setTxFlow(<SkipRecoveryFlow recovery={recovery} />)
}

return (
Expand Down
7 changes: 5 additions & 2 deletions src/components/sidebar/SidebarNavigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { navItems } from './config'
import useSafeInfo from '@/hooks/useSafeInfo'
import { AppRoutes } from '@/config/routes'
import useTxQueue from '@/hooks/useTxQueue'
import { useAppSelector } from '@/store'
import { selectAllRecoveryQueues } from '@/store/recoverySlice'

const getSubdirectory = (pathname: string): string => {
return pathname.split('/')[1]
Expand All @@ -23,6 +25,7 @@ const Navigation = (): ReactElement => {
const { safe } = useSafeInfo()
const currentSubdirectory = getSubdirectory(router.pathname)
const hasQueuedTxs = Boolean(useTxQueue().page?.results.length)
const hasRecoveryTxs = Boolean(useAppSelector(selectAllRecoveryQueues).length)

// Indicate whether the current Safe needs an upgrade
const setupItem = navItems.find((item) => item.href === AppRoutes.settings.setup)
Expand All @@ -33,12 +36,12 @@ const Navigation = (): ReactElement => {
// Route Transactions to Queue if there are queued txs, otherwise to History
const getRoute = useCallback(
(href: string) => {
if (href === AppRoutes.transactions.history && hasQueuedTxs) {
if (href === AppRoutes.transactions.history && (hasQueuedTxs || hasRecoveryTxs)) {
return AppRoutes.transactions.queue
}
return href
},
[hasQueuedTxs],
[hasQueuedTxs, hasRecoveryTxs],
)

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Typography } from '@mui/material'
import { useContext, useEffect } from 'react'
import type { ReactElement } from 'react'

import { SafeTxContext } from '../../SafeTxProvider'
import SignOrExecuteForm from '@/components/tx/SignOrExecuteForm'
import { useWeb3ReadOnly } from '@/hooks/wallets/web3'
import { getRecoverySkipTransaction } from '@/services/recovery/transaction'
import { createTx } from '@/services/tx/tx-sender'
import type { RecoveryQueueItem } from '@/store/recoverySlice'

export function SkipRecoveryFlowReview({ recovery }: { recovery: RecoveryQueueItem }): ReactElement {
const web3ReadOnly = useWeb3ReadOnly()
const { setSafeTx, setSafeTxError } = useContext(SafeTxContext)

useEffect(() => {
if (!web3ReadOnly) {
return
}
const transaction = getRecoverySkipTransaction(recovery, web3ReadOnly)
createTx(transaction).then(setSafeTx).catch(setSafeTxError)
}, [setSafeTx, setSafeTxError, recovery, web3ReadOnly])

return (
<SignOrExecuteForm onSubmit={() => null} isBatchable={false}>
<Typography mb={2}>
To reject the recovery attempt, a separate transaction will be created to increase the nonce beyond the
proposal.
</Typography>

<Typography mb={2}>
Queue nonce: <b>{recovery.args.queueNonce.toNumber()}</b>
</Typography>

<Typography mb={2}>You will need to confirm the transaction with your currently connected wallet.</Typography>
</SignOrExecuteForm>
)
}
13 changes: 13 additions & 0 deletions src/components/tx-flow/flows/SkipRecovery/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { ReactElement } from 'react'

import TxLayout from '../../common/TxLayout'
import { SkipRecoveryFlowReview } from './SkipRecoveryFlowReview'
import type { RecoveryQueueItem } from '@/store/recoverySlice'

export function SkipRecoveryFlow({ recovery }: { recovery: RecoveryQueueItem }): ReactElement {
return (
<TxLayout title="Confirm transaction" subtitle="Skip recovery" step={0}>
<SkipRecoveryFlowReview recovery={recovery} />
</TxLayout>
)
}
21 changes: 20 additions & 1 deletion src/services/recovery/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import { getMultiSendCallOnlyDeployment, getSafeSingletonDeployment } from '@saf
import { SENTINEL_ADDRESS } from '@safe-global/safe-core-sdk/dist/src/utils/constants'
import { encodeMultiSendData } from '@safe-global/safe-core-sdk/dist/src/utils/transactions/utils'
import { OperationType } from '@safe-global/safe-core-sdk-types'
import { sameAddress } from '@/utils/addresses'
import { getModuleInstance, KnownContracts } from '@gnosis.pm/zodiac'
import type { MetaTransactionData } from '@safe-global/safe-core-sdk-types'
import type { AddressEx, SafeInfo } from '@safe-global/safe-gateway-typescript-sdk'
import { sameAddress } from '@/utils/addresses'
import type { RecoveryQueueItem } from '@/store/recoverySlice'
import type { JsonRpcProvider } from '@ethersproject/providers'

export function getRecoveryProposalTransactions({
safe,
Expand Down Expand Up @@ -140,3 +143,19 @@ export function getRecoveryProposalTransaction({
data: multiSendInterface.encodeFunctionData('multiSend', [multiSendData]),
}
}

export function getRecoverySkipTransaction(
recovery: RecoveryQueueItem,
provider: JsonRpcProvider,
): MetaTransactionData {
const delayModifier = getModuleInstance(KnownContracts.DELAY, recovery.address, provider)

const newTxNonce = recovery.args.queueNonce.add(1)

return {
to: delayModifier.address,
value: '0',
operation: OperationType.Call,
data: delayModifier.interface.encodeFunctionData('setTxNonce', [newTxNonce]),
}
}

0 comments on commit 404aa9c

Please sign in to comment.