-
Notifications
You must be signed in to change notification settings - Fork 458
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: enable recovery flow structure
- Loading branch information
Showing
9 changed files
with
274 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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,38 @@ | ||
import { Box, Button, Chip, Grid, Paper, Typography } from '@mui/material' | ||
import { useContext } from 'react' | ||
import type { ReactElement } from 'react' | ||
|
||
import { EnableRecoveryFlow } from '@/components/tx-flow/flows/EnableRecovery' | ||
import { TxModalContext } from '@/components/tx-flow' | ||
|
||
export function Recovery(): ReactElement { | ||
const { setTxFlow } = useContext(TxModalContext) | ||
|
||
return ( | ||
<Paper sx={{ p: 4 }}> | ||
<Grid container spacing={3}> | ||
<Grid item lg={4} xs={12}> | ||
<Box display="flex" alignItems="center" gap={1} mb={1}> | ||
<Typography variant="h4" fontWeight="bold"> | ||
Account recovery | ||
</Typography> | ||
|
||
{/* TODO: Extract when widget is merged https://github.com/safe-global/safe-wallet-web/pull/2768 */} | ||
<Chip label="New" color="primary" size="small" sx={{ borderRadius: '4px', fontSize: '12px' }} /> | ||
</Box> | ||
</Grid> | ||
|
||
<Grid item xs> | ||
<Typography mb={3}> | ||
Choose a trusted guardian to recover your Safe Account, in case you should ever lose access to your Account. | ||
Enabling the Account recovery module will require a transactions. | ||
</Typography> | ||
|
||
<Button variant="contained" onClick={() => setTxFlow(<EnableRecoveryFlow />)}> | ||
Set up recovery | ||
</Button> | ||
</Grid> | ||
</Grid> | ||
</Paper> | ||
) | ||
} |
This file contains 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
7 changes: 7 additions & 0 deletions
7
src/components/tx-flow/flows/EnableRecovery/EnableRecoveryFlowIntro.tsx
This file contains 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,7 @@ | ||
import type { ReactElement } from 'react' | ||
|
||
import TxCard from '../../common/TxCard' | ||
|
||
export function EnableRecoveryFlowIntro(): ReactElement { | ||
return <TxCard>EnableRecoveryFlowIntro</TxCard> | ||
} |
43 changes: 43 additions & 0 deletions
43
src/components/tx-flow/flows/EnableRecovery/EnableRecoveryFlowReview.tsx
This file contains 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,43 @@ | ||
import { useContext, useEffect, useMemo } from 'react' | ||
import type { ReactElement } from 'react' | ||
|
||
import SignOrExecuteForm from '@/components/tx/SignOrExecuteForm' | ||
import { Errors, logError } from '@/services/exceptions' | ||
import { createMultiSendCallOnlyTx } from '@/services/tx/tx-sender' | ||
import { SafeTxContext } from '@/components/tx-flow/SafeTxProvider' | ||
import { getRecoverySetup } from '@/services/recovery/setup' | ||
import { useWeb3 } from '@/hooks/wallets/web3' | ||
import useSafeInfo from '@/hooks/useSafeInfo' | ||
import type { EnableRecoveryFlowProps } from '.' | ||
|
||
export function EnableRecoveryFlowReview({ params }: { params: EnableRecoveryFlowProps }): ReactElement { | ||
const web3 = useWeb3() | ||
const { safe } = useSafeInfo() | ||
const { setSafeTx, safeTxError, setSafeTxError } = useContext(SafeTxContext) | ||
|
||
const recoverySetup = useMemo(() => { | ||
if (!web3) { | ||
return | ||
} | ||
|
||
return getRecoverySetup({ | ||
...params, | ||
safe, | ||
provider: web3, | ||
}) | ||
}, [params, safe, web3]) | ||
|
||
useEffect(() => { | ||
if (recoverySetup) { | ||
createMultiSendCallOnlyTx(recoverySetup.transactions).then(setSafeTx).catch(setSafeTxError) | ||
} | ||
}, [recoverySetup, setSafeTx, setSafeTxError]) | ||
|
||
useEffect(() => { | ||
if (safeTxError) { | ||
logError(Errors._809, safeTxError.message) | ||
} | ||
}, [safeTxError]) | ||
|
||
return <SignOrExecuteForm onSubmit={() => null} /> | ||
} |
14 changes: 14 additions & 0 deletions
14
src/components/tx-flow/flows/EnableRecovery/EnableRecoveryFlowSettings.tsx
This file contains 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,14 @@ | ||
import type { ReactElement } from 'react' | ||
|
||
import TxCard from '../../common/TxCard' | ||
import type { EnableRecoveryFlowProps } from '.' | ||
|
||
export function EnableRecoveryFlowSettings({ | ||
params, | ||
onSubmit, | ||
}: { | ||
params: EnableRecoveryFlowProps | ||
onSubmit: (formData: EnableRecoveryFlowProps) => void | ||
}): ReactElement { | ||
return <TxCard>EnableRecoveryFlowSettings</TxCard> | ||
} |
This file contains 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,58 @@ | ||
import type { ReactElement } from 'react' | ||
|
||
import TxLayout from '@/components/tx-flow/common/TxLayout' | ||
import CodeIcon from '@/public/images/apps/code-icon.svg' | ||
import useTxStepper from '../../useTxStepper' | ||
import { EnableRecoveryFlowReview } from './EnableRecoveryFlowReview' | ||
import { EnableRecoveryFlowSettings } from './EnableRecoveryFlowSettings' | ||
import { EnableRecoveryFlowIntro } from './EnableRecoveryFlowIntro' | ||
|
||
export enum EnableRecoveryFlowFields { | ||
guardians = 'guardians', | ||
txCooldown = 'txCooldown', | ||
txExpiration = 'txExpiration', | ||
} | ||
|
||
export type EnableRecoveryFlowProps = { | ||
[EnableRecoveryFlowFields.guardians]: Array<string> | ||
[EnableRecoveryFlowFields.txCooldown]: string | ||
[EnableRecoveryFlowFields.txExpiration]: string | ||
} | ||
|
||
export function EnableRecoveryFlow(): ReactElement { | ||
const { data, step, nextStep, prevStep } = useTxStepper<EnableRecoveryFlowProps>({ | ||
[EnableRecoveryFlowFields.guardians]: [], | ||
[EnableRecoveryFlowFields.txCooldown]: '0', | ||
[EnableRecoveryFlowFields.txExpiration]: '0', | ||
}) | ||
|
||
const steps = [ | ||
<EnableRecoveryFlowIntro key={0} />, | ||
<EnableRecoveryFlowSettings key={1} params={data} onSubmit={(formData) => nextStep({ ...data, ...formData })} />, | ||
<EnableRecoveryFlowReview key={1} params={data} />, | ||
] | ||
|
||
const isIntro = step === 0 | ||
const isSettings = step === 1 | ||
|
||
const subtitle = isIntro | ||
? 'How does recovery work?' | ||
: isSettings | ||
? 'Set up account settings' | ||
: 'Set up account recovery' | ||
|
||
const icon = isIntro ? undefined : CodeIcon | ||
|
||
return ( | ||
<TxLayout | ||
title="Account recovery" | ||
subtitle={subtitle} | ||
icon={icon} | ||
step={step} | ||
onBack={prevStep} | ||
hideNonce={isIntro} | ||
> | ||
{steps} | ||
</TxLayout> | ||
) | ||
} |
This file contains 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 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,24 @@ | ||
import Head from 'next/head' | ||
import type { NextPage } from 'next' | ||
|
||
import SettingsHeader from '@/components/settings/SettingsHeader' | ||
import { Recovery } from '@/components/settings/Recovery' | ||
|
||
// TODO: Condense to other setting section once confirmed | ||
const RecoveryPage: NextPage = () => { | ||
return ( | ||
<> | ||
<Head> | ||
<title>{'Safe{Wallet} – Settings – Recovery'}</title> | ||
</Head> | ||
|
||
<SettingsHeader /> | ||
|
||
<main> | ||
<Recovery /> | ||
</main> | ||
</> | ||
) | ||
} | ||
|
||
export default RecoveryPage |
This file contains 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,85 @@ | ||
import { getModuleInstance, KnownContracts, deployAndSetUpModule } from '@gnosis.pm/zodiac' | ||
import { Interface } from 'ethers/lib/utils' | ||
import type { Web3Provider } from '@ethersproject/providers' | ||
import type { SafeInfo } from '@safe-global/safe-gateway-typescript-sdk' | ||
import type { MetaTransactionData } from '@safe-global/safe-core-sdk-types' | ||
|
||
export function getRecoverySetup({ | ||
txCooldown, | ||
txExpiration, | ||
guardians, | ||
safe, | ||
provider, | ||
}: { | ||
txCooldown: string | ||
txExpiration: string | ||
guardians: Array<string> | ||
safe: SafeInfo | ||
provider: Web3Provider | ||
}): { | ||
expectedModuleAddress: string | ||
transactions: Array<MetaTransactionData> | ||
} { | ||
const safeAddress = safe.address.value | ||
|
||
const setupArgs: Parameters<typeof deployAndSetUpModule>[1] = { | ||
types: ['address', 'address', 'address', 'uint256', 'uint256'], | ||
values: [ | ||
safeAddress, // address _owner | ||
safeAddress, // address _avatar | ||
safeAddress, // address _target | ||
txCooldown, // uint256 _cooldown | ||
txExpiration, // uint256 _expiration | ||
], | ||
} | ||
|
||
const saltNonce: Parameters<typeof deployAndSetUpModule>[4] = Date.now().toString() | ||
|
||
const { transaction, expectedModuleAddress } = deployAndSetUpModule( | ||
KnownContracts.DELAY, | ||
setupArgs, | ||
provider, | ||
Number(safe.chainId), | ||
saltNonce, | ||
) | ||
|
||
const transactions: Array<MetaTransactionData> = [] | ||
|
||
// Deploy Delay Modifier | ||
const deployDeplayModifier: MetaTransactionData = { | ||
...transaction, | ||
value: transaction.value.toString(), | ||
} | ||
|
||
transactions.push(deployDeplayModifier) | ||
|
||
const safeAbi = ['function enableModule(address module)'] | ||
const safeInterface = new Interface(safeAbi) | ||
|
||
// Enable Delay Modifier on Safe | ||
const enableDelayModifier: MetaTransactionData = { | ||
to: safeAddress, | ||
value: '0', | ||
data: safeInterface.encodeFunctionData('enableModule', [expectedModuleAddress]), | ||
} | ||
|
||
transactions.push(enableDelayModifier) | ||
|
||
const delayModifierContract = getModuleInstance(KnownContracts.DELAY, expectedModuleAddress, provider) | ||
|
||
// Add guardians to Delay Modifier | ||
const enableDelayModifierModules: Array<MetaTransactionData> = guardians.map((guardian) => { | ||
return { | ||
to: expectedModuleAddress, | ||
data: delayModifierContract.interface.encodeFunctionData('enableModule', [guardian]), | ||
value: '0', | ||
} | ||
}) | ||
|
||
transactions.push(...enableDelayModifierModules) | ||
|
||
return { | ||
expectedModuleAddress, | ||
transactions, | ||
} | ||
} |