Skip to content

Commit

Permalink
Update safe unit base
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnGuilding committed Jul 2, 2024
1 parent fcc38ba commit fa6a208
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 172 deletions.
184 changes: 21 additions & 163 deletions test/unit/SafeUnitBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,15 @@
pragma solidity ^0.8.25;

import { console2 } from "forge-std/console2.sol";

import { Safe } from "@safe-global/safe-contracts/contracts/Safe.sol";
import {
SafeProxy,
SafeProxyFactory
} from "@safe-global/safe-contracts/contracts/proxies/SafeProxyFactory.sol";
import { Safe7579Launchpad } from "safe7579/Safe7579Launchpad.sol";
import { IERC7484 } from "safe7579/interfaces/IERC7484.sol";
import { Safe7579 } from "safe7579/Safe7579.sol";
import { ModuleInit } from "safe7579/DataTypes.sol";
import { IERC7579Account } from "erc7579/interfaces/IERC7579Account.sol";
import { ExecutionLib } from "erc7579/lib/ExecutionLib.sol";
import { ModeLib } from "erc7579/lib/ModeLib.sol";
import { ISafe7579 } from "safe7579/ISafe7579.sol";
import { PackedUserOperation } from "modulekit/external/ERC4337.sol";
import { etchEntrypoint, IEntryPoint } from "modulekit/test/predeploy/EntryPoint.sol";
import { MockExecutor, MockTarget } from "modulekit/Mocks.sol";
import { MockValidator } from "module-bases/mocks/MockValidator.sol";
import { EmailAuthMsg, EmailProof } from "ether-email-auth/packages/contracts/src/EmailAuth.sol";
import { SubjectUtils } from "ether-email-auth/packages/contracts/src/libraries/SubjectUtils.sol";
import { Solarray } from "solarray/Solarray.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";

import { EmailRecoveryManagerHarness } from "./EmailRecoveryManagerHarness.sol";
import { EmailRecoveryManager } from "src/EmailRecoveryManager.sol";
import { UniversalEmailRecoveryModule } from "src/modules/UniversalEmailRecoveryModule.sol";
import { SafeRecoverySubjectHandlerHarness } from "./SafeRecoverySubjectHandlerHarness.sol";
import { EmailRecoveryFactory } from "src/EmailRecoveryFactory.sol";
import { MockRegistry } from "src/test/MockRegistry.sol";
import { IntegrationBase } from "../integration/IntegrationBase.t.sol";

abstract contract SafeUnitBase is IntegrationBase {
Expand All @@ -46,20 +26,30 @@ abstract contract SafeUnitBase is IntegrationBase {
bytes recoveryCalldata;
bytes32 calldataHash;

Safe7579 safe7579;
Safe singleton;
Safe safe;
SafeProxyFactory safeProxyFactory;
Safe7579Launchpad launchpad;

MockValidator defaultValidator;
MockExecutor defaultExecutor;
MockTarget target;
/**
* Helper function to return if current account type is safe or not
*/
function isAccountTypeSafe() public returns (bool) {
string memory currentAccountType = vm.envOr("ACCOUNT_TYPE", string(""));
if (Strings.equal(currentAccountType, "SAFE")) {
return true;
} else {
return false;
}
}

IEntryPoint entrypoint;
IERC7484 registry;
function skipIfNotSafeAccountType() public {
if (isAccountTypeSafe()) {
vm.skip(false);
} else {
vm.skip(true);
}
}

function setUp() public virtual override {
if (!isAccountTypeSafe()) {
return;
}
super.setUp();

// Deploy handler, manager and module
Expand All @@ -81,9 +71,6 @@ abstract contract SafeUnitBase is IntegrationBase {
recoveryModuleAddress = address(emailRecoveryModule);
emailRecoveryManager.initialize(recoveryModuleAddress);

safe = deploySafe();
accountAddress1 = address(safe);

functionSelector = bytes4(keccak256(bytes("swapOwner(address,address,address)")));
address previousOwnerInLinkedList = address(1);
// address previousOwnerInLinkedList =
Expand All @@ -103,135 +90,6 @@ abstract contract SafeUnitBase is IntegrationBase {
emailRecoveryManager.computeEmailAuthAddress(instance1.account, accountSalt3);
}

/**
* Taken from safe7579/test/Launchpad.t.sol
*/
function deploySafe() internal returns (Safe) {
entrypoint = etchEntrypoint();
singleton = new Safe();
safeProxyFactory = new SafeProxyFactory();
registry = new MockRegistry();
safe7579 = new Safe7579();
launchpad = new Safe7579Launchpad(address(entrypoint), registry);

// Set up Modules
defaultValidator = new MockValidator();
defaultExecutor = new MockExecutor();
target = new MockTarget();

bytes32 salt;

ModuleInit[] memory validators = new ModuleInit[](1);
validators[0] = ModuleInit({ module: address(defaultValidator), initData: bytes("") });
ModuleInit[] memory executors = new ModuleInit[](1);
executors[0] = ModuleInit({ module: address(defaultExecutor), initData: bytes("") });
ModuleInit[] memory fallbacks = new ModuleInit[](0);
ModuleInit[] memory hooks = new ModuleInit[](0);

Safe7579Launchpad.InitData memory initData = Safe7579Launchpad.InitData({
singleton: address(singleton),
owners: Solarray.addresses(owner1),
threshold: 1,
setupTo: address(launchpad),
setupData: abi.encodeCall(
Safe7579Launchpad.initSafe7579,
(
address(safe7579),
executors,
fallbacks,
hooks,
Solarray.addresses(makeAddr("attester1"), makeAddr("attester2")),
2
)
),
safe7579: ISafe7579(safe7579),
validators: validators,
callData: abi.encodeCall(
IERC7579Account.execute,
(
ModeLib.encodeSimpleSingle(),
ExecutionLib.encodeSingle({
target: address(target),
value: 0,
callData: abi.encodeCall(MockTarget.set, (1337))
})
)
)
});
bytes32 initHash = launchpad.hash(initData);

bytes memory factoryInitializer =
abi.encodeCall(Safe7579Launchpad.preValidationSetup, (initHash, address(0), ""));

PackedUserOperation memory userOp =
getDefaultUserOp(address(safe), address(defaultValidator));

{
userOp.callData = abi.encodeCall(Safe7579Launchpad.setupSafe, (initData));
userOp.initCode = _initCode(factoryInitializer, salt);
}

address predict = launchpad.predictSafeAddress({
singleton: address(launchpad),
safeProxyFactory: address(safeProxyFactory),
creationCode: type(SafeProxy).creationCode,
salt: salt,
factoryInitializer: factoryInitializer
});
userOp.sender = predict;
assertEq(userOp.sender, predict);
userOp.signature = abi.encodePacked(
uint48(0), uint48(type(uint48).max), hex"4141414141414141414141414141414141"
);

entrypoint.getUserOpHash(userOp);
PackedUserOperation[] memory userOps = new PackedUserOperation[](1);
userOps[0] = userOp;
deal(address(userOp.sender), 1 ether);

entrypoint.handleOps(userOps, payable(address(0x69)));

return Safe(payable(predict));
}

function _initCode(
bytes memory initializer,
bytes32 salt
)
internal
view
returns (bytes memory initCode)
{
initCode = abi.encodePacked(
address(safeProxyFactory),
abi.encodeCall(
SafeProxyFactory.createProxyWithNonce,
(address(launchpad), initializer, uint256(salt))
)
);
}

function getDefaultUserOp(
address account,
address validator
)
internal
view
returns (PackedUserOperation memory userOp)
{
userOp = PackedUserOperation({
sender: account,
nonce: safe7579.getNonce(account, validator),
initCode: "",
callData: "",
accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))),
preVerificationGas: 2e6,
gasFees: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))),
paymasterAndData: bytes(""),
signature: abi.encodePacked(hex"41414141")
});
}

function generateMockEmailProof(
string memory subject,
bytes32 nullifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ contract SafeRecoverySubjectHandler_acceptanceSubjectTemplates_Test is SafeUnitB
super.setUp();
}

function test_AcceptanceSubjectTemplates_Succeeds() public view {
function test_AcceptanceSubjectTemplates_Succeeds() public {
skipIfNotSafeAccountType();
string[][] memory templates = safeRecoverySubjectHandler.acceptanceSubjectTemplates();

assertEq(templates.length, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ contract SafeRecoverySubjectHandler_extractRecoveredAccountFromAcceptanceSubject
super.setUp();
}

function test_ExtractRecoveredAccountFromAcceptanceSubject_Succeeds() public view {
function test_ExtractRecoveredAccountFromAcceptanceSubject_Succeeds() public {
skipIfNotSafeAccountType();
bytes[] memory subjectParams = new bytes[](1);
subjectParams[0] = abi.encode(accountAddress1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ contract SafeRecoverySubjectHandler_extractRecoveredAccountFromRecoverySubject_T
super.setUp();
}

function test_ExtractRecoveredAccountFromRecoverySubject_Succeeds() public view {
function test_ExtractRecoveredAccountFromRecoverySubject_Succeeds() public {
skipIfNotSafeAccountType();
bytes[] memory subjectParams = new bytes[](3);
subjectParams[0] = abi.encode(accountAddress1);
subjectParams[1] = abi.encode(newOwner1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ contract SafeRecoverySubjectHandler_getPreviousOwnerInLinkedList_Test is SafeUni
super.setUp();
}

function test_GetPreviousOwnerInLinkedList_InvalidOwner_ReturnsSentinel() public view {
function test_GetPreviousOwnerInLinkedList_InvalidOwner_ReturnsSentinel() public {
skipIfNotSafeAccountType();
address invalidOwner = address(0);

address previousOwner = safeRecoverySubjectHandler.exposed_getPreviousOwnerInLinkedList(
Expand All @@ -21,7 +22,8 @@ contract SafeRecoverySubjectHandler_getPreviousOwnerInLinkedList_Test is SafeUni
assertEq(previousOwner, SENTINEL_OWNERS);
}

function test_GetPreviousOwnerInLinkedList_OwnerIsSentinel_ReturnsSentinel() public view {
function test_GetPreviousOwnerInLinkedList_OwnerIsSentinel_ReturnsSentinel() public {
skipIfNotSafeAccountType();
address invalidOwner = SENTINEL_OWNERS;

address previousOwner = safeRecoverySubjectHandler.exposed_getPreviousOwnerInLinkedList(
Expand All @@ -32,13 +34,15 @@ contract SafeRecoverySubjectHandler_getPreviousOwnerInLinkedList_Test is SafeUni
}

function test_GetPreviousOwnerInLinkedList_RevertWhen_InvalidAccount() public {
skipIfNotSafeAccountType();
address invalidAccount = address(0);

vm.expectRevert();
safeRecoverySubjectHandler.exposed_getPreviousOwnerInLinkedList(invalidAccount, owner1);
}

function test_GetPreviousOwnerInLinkedList_Succeeds() public view {
function test_GetPreviousOwnerInLinkedList_Succeeds() public {
skipIfNotSafeAccountType();
address expectedPreviousOwner = address(1);
address previousOwner =
safeRecoverySubjectHandler.exposed_getPreviousOwnerInLinkedList(accountAddress1, owner1);
Expand All @@ -47,6 +51,7 @@ contract SafeRecoverySubjectHandler_getPreviousOwnerInLinkedList_Test is SafeUni
}

function test_GetPreviousOwnerInLinkedList_SucceedsWithMultipleAccounts() public {
skipIfNotSafeAccountType();
address expectedPreviousOwner = address(1);
address previousOwner =
safeRecoverySubjectHandler.exposed_getPreviousOwnerInLinkedList(accountAddress1, owner1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ contract SafeRecoverySubjectHandler_recoverySubjectTemplates_Test is SafeUnitBas
super.setUp();
}

function test_RecoverySubjectTemplates_Succeeds() public view {
function test_RecoverySubjectTemplates_Succeeds() public {
skipIfNotSafeAccountType();
string[][] memory templates = safeRecoverySubjectHandler.recoverySubjectTemplates();

assertEq(templates.length, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ contract SafeRecoverySubjectHandler_validateAcceptanceSubject_Test is SafeUnitBa
}

function test_ValidateAcceptanceSubject_RevertWhen_NoSubjectParams() public {
skipIfNotSafeAccountType();
bytes[] memory emptySubjectParams;

vm.expectRevert(SafeRecoverySubjectHandler.InvalidSubjectParams.selector);
safeRecoverySubjectHandler.validateAcceptanceSubject(templateIdx, emptySubjectParams);
}

function test_ValidateAcceptanceSubject_RevertWhen_TooManySubjectParams() public {
skipIfNotSafeAccountType();
bytes[] memory subjectParams = new bytes[](2);
subjectParams[0] = abi.encode(accountAddress1);
subjectParams[1] = abi.encode("extra param");
Expand All @@ -26,7 +28,8 @@ contract SafeRecoverySubjectHandler_validateAcceptanceSubject_Test is SafeUnitBa
safeRecoverySubjectHandler.validateAcceptanceSubject(templateIdx, subjectParams);
}

function test_ValidateAcceptanceSubject_Succeeds() public view {
function test_ValidateAcceptanceSubject_Succeeds() public {
skipIfNotSafeAccountType();
bytes[] memory subjectParams = new bytes[](1);
subjectParams[0] = abi.encode(accountAddress1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract SafeRecoverySubjectHandler_validateRecoverySubject_Test is SafeUnitBase
}

function test_ValidateAcceptanceSubject_RevertWhen_NoSubjectParams() public {
skipIfNotSafeAccountType();
bytes[] memory emptySubjectParams;

vm.expectRevert(SafeRecoverySubjectHandler.InvalidSubjectParams.selector);
Expand All @@ -31,6 +32,7 @@ contract SafeRecoverySubjectHandler_validateRecoverySubject_Test is SafeUnitBase
}

function test_ValidateAcceptanceSubject_RevertWhen_TooManySubjectParams() public {
skipIfNotSafeAccountType();
bytes[] memory longSubjectParams = new bytes[](5);
longSubjectParams[0] = subjectParams[0];
longSubjectParams[1] = subjectParams[1];
Expand All @@ -45,6 +47,7 @@ contract SafeRecoverySubjectHandler_validateRecoverySubject_Test is SafeUnitBase
}

function test_ValidateRecoverySubject_RevertWhen_InvalidOldOwner() public {
skipIfNotSafeAccountType();
subjectParams[1] = abi.encode(address(0));

vm.expectRevert(SafeRecoverySubjectHandler.InvalidOldOwner.selector);
Expand All @@ -54,6 +57,7 @@ contract SafeRecoverySubjectHandler_validateRecoverySubject_Test is SafeUnitBase
}

function test_ValidateRecoverySubject_RevertWhen_InvalidNewOwner() public {
skipIfNotSafeAccountType();
subjectParams[2] = abi.encode(address(0));

vm.expectRevert(SafeRecoverySubjectHandler.InvalidNewOwner.selector);
Expand All @@ -63,6 +67,7 @@ contract SafeRecoverySubjectHandler_validateRecoverySubject_Test is SafeUnitBase
}

function test_ValidateRecoverySubject_RevertWhen_RecoveryModuleAddressIsZero() public {
skipIfNotSafeAccountType();
subjectParams[3] = abi.encode(address(0));

vm.expectRevert(SafeRecoverySubjectHandler.InvalidRecoveryModule.selector);
Expand All @@ -74,6 +79,7 @@ contract SafeRecoverySubjectHandler_validateRecoverySubject_Test is SafeUnitBase
function test_ValidateRecoverySubject_RevertWhen_RecoveryModuleNotEqualToExpectedAddress()
public
{
skipIfNotSafeAccountType();
subjectParams[3] = abi.encode(address(1));

vm.expectRevert(SafeRecoverySubjectHandler.InvalidRecoveryModule.selector);
Expand All @@ -82,7 +88,8 @@ contract SafeRecoverySubjectHandler_validateRecoverySubject_Test is SafeUnitBase
);
}

function test_ValidateRecoverySubject_Succeeds() public view {
function test_ValidateRecoverySubject_Succeeds() public {
skipIfNotSafeAccountType();
(address accountFromEmail, bytes32 calldataHashFromEmail) = safeRecoverySubjectHandler
.validateRecoverySubject(templateIdx, subjectParams, emailRecoveryManagerAddress);
assertEq(accountFromEmail, accountAddress1);
Expand Down

0 comments on commit fa6a208

Please sign in to comment.