From cfcab86fb510bf8a65ad4af042d366deb31097b9 Mon Sep 17 00:00:00 2001 From: jacque006 Date: Wed, 1 May 2024 01:17:20 -0600 Subject: [PATCH] Check if connected account is Safe Remove unusued testing components. Add safe deployment npm lib to get active safe deployments. Will need to update other abi/addr refs to it. --- packages/demos/email-recovery/package.json | 1 + packages/demos/email-recovery/src/App.tsx | 4 +- .../demos/email-recovery/src/abi/IProxy.json | 1 + .../email-recovery/src/abi/SafeProxy.json | 1 + .../src/components/ConfigureSafeModule.tsx | 173 ------------------ .../src/components/PerformRecovery.tsx | 94 ---------- .../src/components/SafeModuleRecovery.tsx | 28 ++- packages/demos/email-recovery/yarn.lock | 9 +- 8 files changed, 34 insertions(+), 277 deletions(-) create mode 100644 packages/demos/email-recovery/src/abi/IProxy.json create mode 100644 packages/demos/email-recovery/src/abi/SafeProxy.json delete mode 100644 packages/demos/email-recovery/src/components/ConfigureSafeModule.tsx delete mode 100644 packages/demos/email-recovery/src/components/PerformRecovery.tsx diff --git a/packages/demos/email-recovery/package.json b/packages/demos/email-recovery/package.json index f899974..57dae9c 100644 --- a/packages/demos/email-recovery/package.json +++ b/packages/demos/email-recovery/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "dependencies": { + "@safe-global/safe-deployments": "^1.35.0", "@tanstack/react-query": "^5.28.14", "@wagmi/cli": "^2.1.4", "axios": "^1.6.8", diff --git a/packages/demos/email-recovery/src/App.tsx b/packages/demos/email-recovery/src/App.tsx index a03289e..4c22130 100644 --- a/packages/demos/email-recovery/src/App.tsx +++ b/packages/demos/email-recovery/src/App.tsx @@ -1,4 +1,4 @@ -import { createContext, useEffect, useState } from "react"; +import { createContext, useState } from "react"; import "./App.css"; import ConnectWallets from "./components/ConnectWallets"; import Navbar from "./components/Navbar"; @@ -8,8 +8,6 @@ import SafeModuleRecovery from "./components/SafeModuleRecovery"; import TriggerAccountRecovery from "./components/TriggerAccountRecovery"; import { STEPS } from "./constants"; import { Web3Provider } from "./providers/Web3Provider"; -import { ConnectKitButton } from "connectkit"; -import { useAccount } from "wagmi"; import { AppContextProvider } from "./context/AppContextProvider"; export const StepsContext = createContext(null); diff --git a/packages/demos/email-recovery/src/abi/IProxy.json b/packages/demos/email-recovery/src/abi/IProxy.json new file mode 100644 index 0000000..3618d0f --- /dev/null +++ b/packages/demos/email-recovery/src/abi/IProxy.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"masterCopy","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"masterCopy()":"a619486e"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"masterCopy\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"Richard Meissner - @rmeissner\",\"kind\":\"dev\",\"methods\":{},\"title\":\"IProxy - Helper interface to access the singleton address of the Proxy on-chain.\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"lib/safe-contracts/contracts/proxies/SafeProxy.sol\":\"IProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@eth-infinitism/account-abstraction/=lib/reference-implementation/lib/account-abstraction/contracts/\",\":@ether-email-auth/=lib/ether-email-auth/node_modules/@ether-email-auth/contracts/\",\":@getwax/circuits/=node_modules/@getwax/circuits/\",\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":@zk-email/contracts/=lib/zk-email-verify/packages/contracts/\",\":I4337/=lib/kernel/lib/I4337/src/\",\":account-abstraction/=lib/account-abstraction/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":erc6900-reference-implementation/=lib/reference-implementation/src/\",\":erc7579-implementation/=lib/erc7579-implementation/\",\":ether-email-auth/=lib/ether-email-auth/\",\":forge-std/=lib/forge-std/src/\",\":kernel/=lib/kernel/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/reference-implementation/lib/openzeppelin-contracts/contracts/\",\":reference-implementation/=lib/reference-implementation/src/\",\":safe-contracts/=lib/safe-contracts/\",\":sentinellist/=lib/erc7579-implementation/node_modules/sentinellist/src/\",\":solady/=lib/kernel/lib/solady/src/\",\":solarray/=lib/erc7579-implementation/node_modules/solarray/src/\",\":zk-email-verify/=lib/zk-email-verify/\"]},\"sources\":{\"lib/safe-contracts/contracts/proxies/SafeProxy.sol\":{\"keccak256\":\"0x5dccbe86285c1d4c4b2fed0ae8007620c3186d22411f43feecfbf8a028e5c7e2\",\"license\":\"LGPL-3.0-only\",\"urls\":[\"bzz-raw://a1a02d01a92895110505e141d05b0924ea457a25b89a161f0aeedf6b5cb41aec\",\"dweb:/ipfs/QmYWEsFbWwtrVvEF9MNQtf4X3Qt13dAE8o44tEyAS9jmKP\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.23+commit.f704f362"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"view","type":"function","name":"masterCopy","outputs":[{"internalType":"address","name":"","type":"address"}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"remappings":["@eth-infinitism/account-abstraction/=lib/reference-implementation/lib/account-abstraction/contracts/","@ether-email-auth/=lib/ether-email-auth/node_modules/@ether-email-auth/contracts/","@getwax/circuits/=node_modules/@getwax/circuits/","@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","@zk-email/contracts/=lib/zk-email-verify/packages/contracts/","I4337/=lib/kernel/lib/I4337/src/","account-abstraction/=lib/account-abstraction/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","erc6900-reference-implementation/=lib/reference-implementation/src/","erc7579-implementation/=lib/erc7579-implementation/","ether-email-auth/=lib/ether-email-auth/","forge-std/=lib/forge-std/src/","kernel/=lib/kernel/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/","openzeppelin/=lib/reference-implementation/lib/openzeppelin-contracts/contracts/","reference-implementation/=lib/reference-implementation/src/","safe-contracts/=lib/safe-contracts/","sentinellist/=lib/erc7579-implementation/node_modules/sentinellist/src/","solady/=lib/kernel/lib/solady/src/","solarray/=lib/erc7579-implementation/node_modules/solarray/src/","zk-email-verify/=lib/zk-email-verify/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"lib/safe-contracts/contracts/proxies/SafeProxy.sol":"IProxy"},"evmVersion":"paris","libraries":{}},"sources":{"lib/safe-contracts/contracts/proxies/SafeProxy.sol":{"keccak256":"0x5dccbe86285c1d4c4b2fed0ae8007620c3186d22411f43feecfbf8a028e5c7e2","urls":["bzz-raw://a1a02d01a92895110505e141d05b0924ea457a25b89a161f0aeedf6b5cb41aec","dweb:/ipfs/QmYWEsFbWwtrVvEF9MNQtf4X3Qt13dAE8o44tEyAS9jmKP"],"license":"LGPL-3.0-only"}},"version":1},"id":143} \ No newline at end of file diff --git a/packages/demos/email-recovery/src/abi/SafeProxy.json b/packages/demos/email-recovery/src/abi/SafeProxy.json new file mode 100644 index 0000000..ee47041 --- /dev/null +++ b/packages/demos/email-recovery/src/abi/SafeProxy.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[{"name":"_singleton","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"fallback","stateMutability":"payable"}],"bytecode":{"object":"0x608060405234801561001057600080fd5b5060405161017238038061017283398101604081905261002f916100b9565b6001600160a01b0381166100945760405162461bcd60e51b815260206004820152602260248201527f496e76616c69642073696e676c65746f6e20616464726573732070726f766964604482015261195960f21b606482015260840160405180910390fd5b600080546001600160a01b0319166001600160a01b03929092169190911790556100e9565b6000602082840312156100cb57600080fd5b81516001600160a01b03811681146100e257600080fd5b9392505050565b607b806100f76000396000f3fe6080604052600080546001600160a01b0316632cf35bc960e11b823501602757808252602082f35b3682833781823684845af490503d82833e806040573d82fd5b503d81f3fea26469706673582212205d790113d286e7d67b6198a81f137afe7f28f65d39bb7453d4523bec7b92a86c64736f6c63430008170033","sourceMap":"520:1508:143:-:0;;;965:152;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;1015:24:143;;1007:71;;;;-1:-1:-1;;;1007:71:143;;511:2:202;1007:71:143;;;493:21:202;550:2;530:18;;;523:30;589:34;569:18;;;562:62;-1:-1:-1;;;640:18:202;;;633:32;682:19;;1007:71:143;;;;;;;;1088:9;:22;;-1:-1:-1;;;;;;1088:22:143;-1:-1:-1;;;;;1088:22:143;;;;;;;;;;520:1508;;14:290:202;84:6;137:2;125:9;116:7;112:23;108:32;105:52;;;153:1;150;143:12;105:52;179:16;;-1:-1:-1;;;;;224:31:202;;214:42;;204:70;;270:1;267;260:12;204:70;293:5;14:290;-1:-1:-1;;;14:290:202:o;309:398::-;520:1508:143;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"0x6080604052600080546001600160a01b0316632cf35bc960e11b823501602757808252602082f35b3682833781823684845af490503d82833e806040573d82fd5b503d81f3fea26469706673582212205d790113d286e7d67b6198a81f137afe7f28f65d39bb7453d4523bec7b92a86c64736f6c63430008170033","sourceMap":"520:1508:143:-:0;;;1363:1;1357:8;;-1:-1:-1;;;;;1353:57:143;-1:-1:-1;;;1528:15:143;;1525:87;1522:176;;1641:10;1363:1;1631:21;1679:4;1363:1;1669:15;1522:176;1730:14;1363:1;;1711:34;1363:1;;1730:14;1363:1;1793:10;1786:5;1773:56;1758:71;;1863:16;1363:1;;1842:38;1899:7;1893:77;;1939:16;1363:1;1929:27;1893:77;;1993:16;1363:1;1983:27","linkReferences":{}},"methodIdentifiers":{},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_singleton\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"}],\"devdoc\":{\"author\":\"Stefan George - Richard Meissner - \",\"kind\":\"dev\",\"methods\":{\"constructor\":{\"params\":{\"_singleton\":\"Singleton address.\"}}},\"title\":\"SafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"constructor\":{\"notice\":\"Constructor function sets address of singleton contract.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"lib/safe-contracts/contracts/proxies/SafeProxy.sol\":\"SafeProxy\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@eth-infinitism/account-abstraction/=lib/reference-implementation/lib/account-abstraction/contracts/\",\":@ether-email-auth/=lib/ether-email-auth/node_modules/@ether-email-auth/contracts/\",\":@getwax/circuits/=node_modules/@getwax/circuits/\",\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":@zk-email/contracts/=lib/zk-email-verify/packages/contracts/\",\":I4337/=lib/kernel/lib/I4337/src/\",\":account-abstraction/=lib/account-abstraction/contracts/\",\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":erc6900-reference-implementation/=lib/reference-implementation/src/\",\":erc7579-implementation/=lib/erc7579-implementation/\",\":ether-email-auth/=lib/ether-email-auth/\",\":forge-std/=lib/forge-std/src/\",\":kernel/=lib/kernel/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/reference-implementation/lib/openzeppelin-contracts/contracts/\",\":reference-implementation/=lib/reference-implementation/src/\",\":safe-contracts/=lib/safe-contracts/\",\":sentinellist/=lib/erc7579-implementation/node_modules/sentinellist/src/\",\":solady/=lib/kernel/lib/solady/src/\",\":solarray/=lib/erc7579-implementation/node_modules/solarray/src/\",\":zk-email-verify/=lib/zk-email-verify/\"]},\"sources\":{\"lib/safe-contracts/contracts/proxies/SafeProxy.sol\":{\"keccak256\":\"0x5dccbe86285c1d4c4b2fed0ae8007620c3186d22411f43feecfbf8a028e5c7e2\",\"license\":\"LGPL-3.0-only\",\"urls\":[\"bzz-raw://a1a02d01a92895110505e141d05b0924ea457a25b89a161f0aeedf6b5cb41aec\",\"dweb:/ipfs/QmYWEsFbWwtrVvEF9MNQtf4X3Qt13dAE8o44tEyAS9jmKP\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.23+commit.f704f362"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"_singleton","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"stateMutability":"payable","type":"fallback"}],"devdoc":{"kind":"dev","methods":{"constructor":{"params":{"_singleton":"Singleton address."}}},"version":1},"userdoc":{"kind":"user","methods":{"constructor":{"notice":"Constructor function sets address of singleton contract."}},"version":1}},"settings":{"remappings":["@eth-infinitism/account-abstraction/=lib/reference-implementation/lib/account-abstraction/contracts/","@ether-email-auth/=lib/ether-email-auth/node_modules/@ether-email-auth/contracts/","@getwax/circuits/=node_modules/@getwax/circuits/","@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/","@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","@zk-email/contracts/=lib/zk-email-verify/packages/contracts/","I4337/=lib/kernel/lib/I4337/src/","account-abstraction/=lib/account-abstraction/contracts/","ds-test/=lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/","erc6900-reference-implementation/=lib/reference-implementation/src/","erc7579-implementation/=lib/erc7579-implementation/","ether-email-auth/=lib/ether-email-auth/","forge-std/=lib/forge-std/src/","kernel/=lib/kernel/","openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/","openzeppelin-contracts/=lib/openzeppelin-contracts/","openzeppelin/=lib/reference-implementation/lib/openzeppelin-contracts/contracts/","reference-implementation/=lib/reference-implementation/src/","safe-contracts/=lib/safe-contracts/","sentinellist/=lib/erc7579-implementation/node_modules/sentinellist/src/","solady/=lib/kernel/lib/solady/src/","solarray/=lib/erc7579-implementation/node_modules/solarray/src/","zk-email-verify/=lib/zk-email-verify/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"lib/safe-contracts/contracts/proxies/SafeProxy.sol":"SafeProxy"},"evmVersion":"paris","libraries":{}},"sources":{"lib/safe-contracts/contracts/proxies/SafeProxy.sol":{"keccak256":"0x5dccbe86285c1d4c4b2fed0ae8007620c3186d22411f43feecfbf8a028e5c7e2","urls":["bzz-raw://a1a02d01a92895110505e141d05b0924ea457a25b89a161f0aeedf6b5cb41aec","dweb:/ipfs/QmYWEsFbWwtrVvEF9MNQtf4X3Qt13dAE8o44tEyAS9jmKP"],"license":"LGPL-3.0-only"}},"version":1},"id":143} \ No newline at end of file diff --git a/packages/demos/email-recovery/src/components/ConfigureSafeModule.tsx b/packages/demos/email-recovery/src/components/ConfigureSafeModule.tsx deleted file mode 100644 index 1c58d0f..0000000 --- a/packages/demos/email-recovery/src/components/ConfigureSafeModule.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { useState, useCallback, useMemo } from 'react' -import { useAccount, useWriteContract, useReadContract } from 'wagmi' -import { abi as safeAbi } from '../abi/Safe.json' -import { abi as recoveryPluginAbi } from '../abi/SafeZkEmailRecoveryPlugin.json' -import { safeZkSafeZkEmailRecoveryPlugin } from '../../contracts.base-sepolia.json' -import { Button } from './Button' -import { genAccountCode, getRequestGuardianSubject, templateIdx } from '../utils/email' -import { readContract } from 'wagmi/actions' -import { config } from '../providers/config' -import { pad } from 'viem' -import { relayer } from '../services/relayer' -import { useAppContext } from '../context/AppContextHook' - -export function ConfigureSafeModule() { - const { address } = useAccount() - const { writeContractAsync } = useWriteContract() - - const { - guardianEmail, - setGuardianEmail, - accountCode, - setAccountCode - } = useAppContext() - // TODO 0 sets recovery to default of 2 weeks, likely want a warning here - // Also, better time duration setting component - const [recoveryDelay, setRecoveryDelay] = useState(0) - - const { data: isModuleEnabled } = useReadContract({ - address, - abi: safeAbi, - functionName: 'isModuleEnabled', - args: [safeZkSafeZkEmailRecoveryPlugin] - }); - - const { data: safeOwnersData } = useReadContract({ - address, - abi: safeAbi, - functionName: 'getOwners', - }); - const firstSafeOwner = useMemo(() => { - const safeOwners = safeOwnersData as string[]; - if (!safeOwners?.length) { - return; - } - return safeOwners[0]; - }, [safeOwnersData]); - - // const checkGuardianAcceptance = useCallback(async () => { - // if (!gurdianRequestId) { - // throw new Error('missing guardian request id') - // } - - // const resBody = await relayer.requestStatus(gurdianRequestId) - // console.debug('guardian req res body', resBody); - // }, [gurdianRequestId]) - - const enableEmailRecoveryModule = useCallback(async () => { - if (!address) { - throw new Error('unable to get account address'); - } - - await writeContractAsync({ - abi: safeAbi, - address, - functionName: 'enableModule', - args: [safeZkSafeZkEmailRecoveryPlugin], - }) - }, [address, writeContractAsync]) - - const configureRecoveryAndRequestGuardian = useCallback(async () => { - if (!address) { - throw new Error('unable to get account address'); - } - - if (!guardianEmail) { - throw new Error('guardian email not set') - } - - if (!firstSafeOwner) { - throw new Error('safe owner not found') - } - - const acctCode = await genAccountCode(); - setAccountCode(accountCode); - - const guardianSalt = await relayer.getAccountSalt(acctCode, guardianEmail); - const guardianAddr = await readContract(config, { - abi: recoveryPluginAbi, - address: safeZkSafeZkEmailRecoveryPlugin as `0x${string}`, - functionName: 'computeEmailAuthAddress', - args: [guardianSalt] - }) - // TODO Should this be something else? - const previousOwnerInLinkedList = pad("0x1", { - size: 20 - }) - - await writeContractAsync({ - abi: recoveryPluginAbi, - address: safeZkSafeZkEmailRecoveryPlugin as `0x${string}`, - functionName: 'configureRecovery', - args: [ - firstSafeOwner, - guardianAddr, - recoveryDelay, - previousOwnerInLinkedList - ], - }) - - console.debug('recovery configured'); - - const recoveryRouterAddr = await readContract(config, { - abi: recoveryPluginAbi, - address: safeZkSafeZkEmailRecoveryPlugin as `0x${string}`, - functionName: 'getRouterForSafe', - args: [address] - }) as string; - - const subject = getRequestGuardianSubject(address); - const { requestId } = await relayer.acceptanceRequest( - recoveryRouterAddr, - guardianEmail, - acctCode, - templateIdx, - subject, - ); - - console.debug('req guard req id', requestId) - // TODO poll until guard req is complete or fails - }, [ - address, - firstSafeOwner, - guardianEmail, - recoveryDelay, - accountCode, - setAccountCode, - writeContractAsync - ]) - - return ( - <> - { - isModuleEnabled ? -
Recovery Module Enabled
: - - } -
- - - -
- - ); -} diff --git a/packages/demos/email-recovery/src/components/PerformRecovery.tsx b/packages/demos/email-recovery/src/components/PerformRecovery.tsx deleted file mode 100644 index d58bbd4..0000000 --- a/packages/demos/email-recovery/src/components/PerformRecovery.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { useState, useCallback } from 'react' -import { Button } from './Button' -import { relayer } from '../services/relayer' -import { abi as recoveryPluginAbi } from '../abi/SafeZkEmailRecoveryPlugin.json' -import { useReadContract, useAccount } from 'wagmi' -import { - getRequestsRecoverySubject, - templateIdx -} from '../utils/email' -import { safeZkSafeZkEmailRecoveryPlugin } from '../../contracts.base-sepolia.json' -import { useAppContext } from '../context/AppContextHook' - -export function PerformRecovery() { - const { address } = useAccount() - - const { guardianEmail } = useAppContext() - - const [newOwner, setNewOwner] = useState() - - // TODO pull from recovery module - // const { data: timelock } = useReadContract({ - // address: simpleWalletAddress as HexStr, - // abi: simpleWalletAbi, - // functionName: 'timelock', - // }); - - const { data: recoveryRouterAddr } = useReadContract({ - abi: recoveryPluginAbi, - address: safeZkSafeZkEmailRecoveryPlugin as `0x${string}`, - functionName: 'getRouterForSafe', - args: [address] - }); - - const requestRecovery = useCallback(async () => { - if (!address) { - throw new Error('unable to get account address'); - } - - if (!guardianEmail) { - throw new Error('guardian email not set') - } - - if (!newOwner) { - throw new Error('new owner not set') - } - - if (!recoveryRouterAddr) { - throw new Error('could not find recovery router for safe') - } - - const subject = getRequestsRecoverySubject(address, newOwner) - - const { requestId } = await relayer.recoveryRequest( - recoveryRouterAddr as string, - guardianEmail, - templateIdx, - subject, - ) - console.debug('recovery request id', requestId) - - }, [recoveryRouterAddr, address, guardianEmail, newOwner]) - - const completeRecovery = useCallback(async () => { - if (!recoveryRouterAddr) { - throw new Error('could not find recovery router for safe') - } - - console.debug('recovery router addr', recoveryRouterAddr); - const res = relayer.completeRecovery( - recoveryRouterAddr as string - ); - - console.debug('complete recovery res', res) - }, [recoveryRouterAddr]); - - return ( - <> - - - - {/*
{`TEST timelock: ${timelock}`}
*/} - - - ); -} diff --git a/packages/demos/email-recovery/src/components/SafeModuleRecovery.tsx b/packages/demos/email-recovery/src/components/SafeModuleRecovery.tsx index d09446e..301b8f0 100644 --- a/packages/demos/email-recovery/src/components/SafeModuleRecovery.tsx +++ b/packages/demos/email-recovery/src/components/SafeModuleRecovery.tsx @@ -1,13 +1,16 @@ import { ConnectKitButton } from "connectkit"; import { Button } from "./Button"; -import { useAccount, useReadContract, useWriteContract } from "wagmi"; +import { useAccount, useBytecode, useChainId, useReadContract, useWriteContract } from "wagmi"; import { safeZkSafeZkEmailRecoveryPlugin } from "../../contracts.base-sepolia.json"; import { abi as safeAbi } from "../abi/Safe.json"; -import { useCallback, useContext, useEffect, useState } from "react"; +import { abi as proxyAbi } from "../abi/IProxy.json"; +import { useCallback, useContext, useEffect, useState, useMemo } from "react"; import { StepsContext } from "../App"; import { STEPS } from "../constants"; +import { getSafeL2SingletonDeployment } from "@safe-global/safe-deployments"; const SafeModuleRecovery = () => { + const chainId = useChainId(); const { address } = useAccount(); const { writeContractAsync } = useWriteContract(); const stepsContext = useContext(StepsContext); @@ -19,6 +22,18 @@ const SafeModuleRecovery = () => { } }, [address, stepsContext]); + // See https://ethereum.stackexchange.com/a/141258 + const { data: addressBytecode } = useBytecode({ address }); + const { data: masterCopy } = useReadContract({ + address, + abi: proxyAbi, + functionName: "masterCopy", + }); + const isSafeAccount = useMemo(() => { + const safeL2SingletonAddr = getSafeL2SingletonDeployment({ network: `${chainId}` }); + return addressBytecode && masterCopy === safeL2SingletonAddr; + }, [chainId, addressBytecode, masterCopy]) + const { data: isModuleEnabled } = useReadContract({ address, abi: safeAbi, @@ -26,8 +41,6 @@ const SafeModuleRecovery = () => { args: [safeZkSafeZkEmailRecoveryPlugin], }); - console.log(isModuleEnabled); - if (isModuleEnabled) { console.log("Module is enabled"); setLoading(false); @@ -53,11 +66,14 @@ const SafeModuleRecovery = () => {
Connected wallet:
- {!isModuleEnabled ? ( + {!isSafeAccount && ( +
Connected account is not a Safe, please connect a Safe
+ )} + {isSafeAccount && !isModuleEnabled && ( - ) : null} + )} ); }; diff --git a/packages/demos/email-recovery/yarn.lock b/packages/demos/email-recovery/yarn.lock index fae62ae..ef9b85c 100644 --- a/packages/demos/email-recovery/yarn.lock +++ b/packages/demos/email-recovery/yarn.lock @@ -1589,6 +1589,13 @@ "@safe-global/safe-gateway-typescript-sdk" "^3.5.3" viem "^1.0.0" +"@safe-global/safe-deployments@^1.35.0": + version "1.35.0" + resolved "https://registry.npmjs.org/@safe-global/safe-deployments/-/safe-deployments-1.35.0.tgz#6930a86a006526a9791ebd2a11cf8f5d8358563b" + integrity sha512-Of8WQEcvL5Fm+xxnCDjah6Hkw+sNdzcApQnzr+OsPBxYtZL0RRtbmesypj36oOD8BQmyrH54V8DVN+pYjrfJ9g== + dependencies: + semver "^7.6.0" + "@safe-global/safe-gateway-typescript-sdk@^3.5.3": version "3.19.0" resolved "https://registry.npmjs.org/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.19.0.tgz#18637c205c83bfc0a6be5fddbf202d6bb4927302" @@ -5671,7 +5678,7 @@ semver@^6.3.1: resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.8, semver@^7.5.4: +semver@^7.3.8, semver@^7.5.4, semver@^7.6.0: version "7.6.0" resolved "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==