Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/swap owner #9

Open
wants to merge 3 commits into
base: add-safe-1.4-rebase
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions contracts.base-sepolia.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"safeEmailRecoveryModule": "0x87a5647a625a0DB1237C550A27C3629a3de4B65c",
"universalEmailRecoveryModule": "0xc0EFFe5d3D240d35450A43a3F9Ebd98091f2e6a7",
"universalEmailRecoveryModule": "0x29a26b4f4fDA819415Ef2d93255998E8FF864066",
"validatorsAddress": "0xd9Ef4a48E4C067d640a9f784dC302E97B21Fd691",
"safe4337ModuleAddress": "0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2",
"safe4337ModuleAddress": "0x31724D48Ba93c3c31617d3346cB7Ec79705013e3",
"erc7569LaunchpadAddress": "0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE"
}
6 changes: 3 additions & 3 deletions src/components/burnerWallet/GuardianSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ const GuardianSetup = () => {
safeVersion: "1.4.1",
entryPoint: ENTRYPOINT_ADDRESS_V07,
saltNonce: saltNonce,
safe4337ModuleAddress,
erc7569LaunchpadAddress,
safe4337ModuleAddress: safe4337ModuleAddress as `0x${string}`,
erc7569LaunchpadAddress: erc7569LaunchpadAddress as `0x${string}`,
validators: [
{
address: validatorsAddress,
address: validatorsAddress as `0x${string}`,
context: "0x",
},
],
Expand Down
53 changes: 45 additions & 8 deletions src/components/burnerWallet/RequestedRecoveries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
import { templateIdx } from "../../utils/email";

import {
getPreviousOwnerInLinkedList,
getRecoveryCallData,
getRecoveryData,
} from "../../utils/recoveryDataUtils";
import { useGetSafeAccountAddress } from "../../utils/useGetSafeAccountAddress";
import { Button } from "../Button";
import InputField from "../InputField";
import { useReadContract } from "wagmi";

Check warning on line 29 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

`wagmi` import should occur before import of `wagmi/actions`

Check failure on line 29 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

'useReadContract' is defined but never used

Check warning on line 29 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

`wagmi` import should occur before import of `wagmi/actions`

Check failure on line 29 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

'useReadContract' is defined but never used
import { safeAbi } from "../../abi/Safe";

Check warning on line 30 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

`../../abi/Safe` import should occur before import of `../../abi/UniversalEmailRecoveryModule.json`

Check warning on line 30 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

`../../abi/Safe` import should occur before import of `../../abi/UniversalEmailRecoveryModule.json`

const BUTTON_STATES = {
TRIGGER_RECOVERY: "Trigger Recovery",
Expand All @@ -40,7 +43,7 @@
const navigate = useNavigate();

const [newOwner, setNewOwner] = useState<`0x${string}`>();
const safeWalletAddress = address;
const safeWalletAddress = address as `0x${string}`;
const [guardianEmailAddress, setGuardianEmailAddress] =
useState(guardianEmail);
const [buttonState, setButtonState] = useState(
Expand Down Expand Up @@ -96,38 +99,55 @@
throw new Error("guardian email not set");
}

// TODO: REMOVE IT LATER
const newOwner: `0x${string}` = "0xe2835b8cD5B16E1736Ff1bB27a390067948445d5";

if (!newOwner) {
throw new Error("new owner not set");
}

const recoveryCallData = getRecoveryCallData(newOwner);
const safeOwnersData = (await readContract(config, {
address: safeWalletAddress,
abi: safeAbi,
functionName: "getOwners",
args: [],
})) as `0x${string}`[];
if(!safeOwnersData) {
throw new Error("safe owners data not found");
}

const prevOwner = getPreviousOwnerInLinkedList(safeOwnersData[0], safeOwnersData);

const recoveryCallData = getRecoveryCallData(prevOwner, safeOwnersData[0], newOwner);

const recoveryData = getRecoveryData(
validatorsAddress,
recoveryCallData,
) as `0x${string}`;

const recoveryDataHash = keccak256(recoveryData);

Check failure on line 128 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

'recoveryDataHash' is assigned a value but never used

Check failure on line 128 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

'recoveryDataHash' is assigned a value but never used

// This function fetches the command template for the recoveryRequest API call. The command template will be in the following format: ['Recover', 'account', '{ethAddr}', 'using', 'recovery', 'hash', '{string}']
const subject = (await readContract(config, {
const command = (await readContract(config, {
abi: universalEmailRecoveryModuleAbi,
address: universalEmailRecoveryModule as `0x${string}`,
functionName: "recoveryCommandTemplates",
args: [],
})) as [][];
})) as readonly (readonly string[])[];


try {
// requestId
await relayer.recoveryRequest(
universalEmailRecoveryModule as string,
guardianEmailAddress,
templateIdx,
subject[0]
command[0]
.join()
?.replaceAll(",", " ")
.replace("{ethAddr}", safeWalletAddress)
.replace("{string}", recoveryDataHash),
.replace("{ethAddr}", safeOwnersData[0])
.replace("{ethAddr}", newOwner),
);

intervalRef.current = setInterval(() => {
Expand All @@ -138,7 +158,7 @@
toast.error("Something went wrong while requesting recovery");
setLoading(false);
}
}, [

Check warning on line 161 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useCallback has an unnecessary dependency: 'newOwner'. Either exclude it or remove the dependency array

Check warning on line 161 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useCallback has an unnecessary dependency: 'newOwner'. Either exclude it or remove the dependency array
safeWalletAddress,
guardianEmailAddress,
newOwner,
Expand All @@ -148,9 +168,26 @@
const completeRecovery = useCallback(async () => {
setLoading(true);

const recoveryCallData = getRecoveryCallData(newOwner!);
const safeOwnersData = (await readContract(config, {
address: safeWalletAddress,
abi: safeAbi,
functionName: "getOwners",
args: [],
})) as `0x${string}`[];
if(!safeOwnersData) {
throw new Error("safe owners data not found");
}

const prevOwner = getPreviousOwnerInLinkedList(safeOwnersData[0], safeOwnersData);

// TODO: REMOVE IT LATER
const newOwner: `0x${string}` = "0xe2835b8cD5B16E1736Ff1bB27a390067948445d5";

const recoveryCallData = getRecoveryCallData(prevOwner, safeOwnersData[0], newOwner);

// const recoveryCallData = getRecoveryCallData(newOwner!);

const recoveryData = getRecoveryData(validatorsAddress, recoveryCallData);
const recoveryData = getRecoveryData(safeWalletAddress, recoveryCallData);

try {
await relayer.completeRecovery(
Expand All @@ -165,7 +202,7 @@
} finally {
setLoading(false);
}
}, [newOwner, safeWalletAddress]);

Check warning on line 205 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useCallback has an unnecessary dependency: 'newOwner'. Either exclude it or remove the dependency array

Check warning on line 205 in src/components/burnerWallet/RequestedRecoveries.tsx

View workflow job for this annotation

GitHub Actions / eslint

React Hook useCallback has an unnecessary dependency: 'newOwner'. Either exclude it or remove the dependency array

const getButtonComponent = () => {
// Renders the appropriate buttons based on the button state.
Expand Down
4 changes: 2 additions & 2 deletions src/components/burnerWallet/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
safeAccount: object,
guardianAddr: string,
) {
const ownableValidatorAddress = validatorsAddress;

Check failure on line 51 in src/components/burnerWallet/deploy.ts

View workflow job for this annotation

GitHub Actions / eslint

'ownableValidatorAddress' is assigned a value but never used

Check failure on line 51 in src/components/burnerWallet/deploy.ts

View workflow job for this annotation

GitHub Actions / eslint

'ownableValidatorAddress' is assigned a value but never used
// Universal Email Recovery Module with
// ECDSAOwnedDKIMRegistry
// Verifier
Expand Down Expand Up @@ -94,7 +94,7 @@
const account: `0x${string}` = safeAccount.address as `0x${string}`;
const isInstalledContext = new Uint8Array([0]);
const functionSelector = keccak256(
new TextEncoder().encode("changeOwner(address)"),
new TextEncoder().encode("swapOwner(address,address,address)"),
).slice(0, 10);
const guardians = [guardianAddr];
const guardianWeights = [1];
Expand All @@ -107,7 +107,7 @@
"address, bytes, bytes4, address[], uint256[], uint256, uint256, uint256",
),
[
ownableValidatorAddress as `0x${string}`,
account,
isInstalledContext instanceof Uint8Array
? `0x${toHexString(isInstalledContext)}`
: isInstalledContext, // Convert Uint8Array to hex string if necessary
Expand Down
29 changes: 24 additions & 5 deletions src/utils/recoveryDataUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AbiCoder } from "ethers";
import { encodeFunctionData } from "viem";
import { abi as ownableValidatorAbi } from "../abi/OwnableValidator.json";
import { safeAbi as safeAbi } from "../abi/Safe.ts";

/**
* Encodes the recovery data using the validator address and recovery call data.
Expand All @@ -21,17 +21,36 @@ export const getRecoveryData = (

/**
* Generates the recoveryCallData for changing the owner of a contract.
* @param {string} prevOwner - The address of the previous owner.
* @param {string} oldOwner - The address of the old owner.
* @param {string} newOwner - The address of the new owner.
* @returns {string} The recoveryCallData for the ownership change.
*/
export const getRecoveryCallData = (newOwner: string): string => {
export const getRecoveryCallData = (prevOwner: `0x${string}`, oldOwner: `0x${string}`, newOwner: `0x${string}`): string => {
return encodeFunctionData({
abi: ownableValidatorAbi,
functionName: "changeOwner",
args: [newOwner],
abi: safeAbi,
functionName: "swapOwner",
args: [prevOwner, oldOwner, newOwner],
});
};

export function getPreviousOwnerInLinkedList(oldOwner: `0x${string}`, safeOwners: `0x${string}`[]): `0x${string}` {

const owners: `0x${string}`[] = safeOwners as `0x${string}`[];
const length: number = owners.length;

let oldOwnerIndex: number = -1;
for (let i = 0; i < length; i++) {
if (owners[i] === oldOwner) {
oldOwnerIndex = i;
break;
}
}

const sentinelOwner: `0x${string}` = "0x0000000000000000000000000000000000000001";
return oldOwnerIndex === 0 ? sentinelOwner : owners[oldOwnerIndex - 1];
}

// TIME_UNITS is an object that defines various time units.
// Each unit includes:
// - value: A string identifier for the unit.
Expand Down
Loading