Skip to content

Commit

Permalink
✨ Installation and deinstallation now works with calldata
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroknots committed Mar 14, 2024
1 parent 5c10bc7 commit c0271d7
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 36 deletions.
56 changes: 42 additions & 14 deletions accounts/safe7579/src/SafeERC7579.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,22 @@ import {
} from "@ERC4337/account-abstraction/contracts/core/UserOperationLib.sol";
import { _packValidationData } from "@ERC4337/account-abstraction/contracts/core/Helpers.sol";
import { IEntryPoint } from "@ERC4337/account-abstraction/contracts/interfaces/IEntryPoint.sol";
import { ISafe7579Init } from "./interfaces/ISafe7579Init.sol";

/**
* @title ERC7579 Adapter for Safe accounts.
* By using Safe's Fallback and Execution modules,
* this contract creates full ERC7579 compliance to Safe accounts
* @author zeroknots.eth | rhinestone.wtf
*/
contract SafeERC7579 is ISafeOp, IERC7579Account, AccessControl, IMSA, HookManager {
contract SafeERC7579 is
ISafeOp,
IERC7579Account,
ISafe7579Init,
AccessControl,
IMSA,
HookManager
{
using UserOperationLib for PackedUserOperation;
using ModeLib for ModeCode;
using ExecutionLib for bytes;
Expand Down Expand Up @@ -390,26 +398,46 @@ contract SafeERC7579 is ISafeOp, IERC7579Account, AccessControl, IMSA, HookManag
return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, block.chainid, this));
}

function initializeAccount(bytes calldata initCode) external payable {
function initializeAccount(bytes calldata callData) external payable override {
// TODO: destructuring callData
}

function initializeAccount(
ModuleInit[] calldata validators,
ModuleInit[] calldata executors,
ModuleInit[] calldata fallbacks,
ModuleInit[] calldata hooks
)
public
payable
override
{
_initModuleManager();

(
address[] memory validator,
bytes[] memory validatorInitcode,
address[] memory executors,
bytes[] memory executorsInitcode
) = abi.decode(initCode, (address[], bytes[], address[], bytes[]));

uint256 length = validator.length;
if (length != validatorInitcode.length) revert("Invalid input");
// InitData memory initDatas = abi.decode(initCode, (InitData));

uint256 length = validators.length;
for (uint256 i; i < length; i++) {
_installValidator(validator[i], validatorInitcode[i]);
ModuleInit calldata validator = validators[i];
_installValidator(validator.module, validator.initData);
}

length = executors.length;
if (length != executorsInitcode.length) revert("Invalid input");
for (uint256 i; i < length; i++) {
_installExecutor(executors[i], executorsInitcode[i]);
ModuleInit calldata executor = executors[i];
_installExecutor(executor.module, executor.initData);
}

length = fallbacks.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata fallBack = fallbacks[i];
_installFallbackHandler(fallBack.module, fallBack.initData);
}

length = hooks.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata hook = hooks[i];
_installFallbackHandler(hook.module, hook.initData);
}

emit Safe7579Initialized(msg.sender);
Expand Down
8 changes: 4 additions & 4 deletions accounts/safe7579/src/core/ModuleManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ abstract contract ModuleManager is AccessControl, Receiver, ExecutionHelper {
/**
* install and initialize validator module
*/
function _installValidator(address validator, bytes memory data) internal virtual {
function _installValidator(address validator, bytes calldata data) internal virtual {
$validators.push({ account: msg.sender, newEntry: validator });

// Initialize Validator Module via Safe
Expand All @@ -80,7 +80,7 @@ abstract contract ModuleManager is AccessControl, Receiver, ExecutionHelper {
/**
* Uninstall and de-initialize validator module
*/
function _uninstallValidator(address validator, bytes memory data) internal {
function _uninstallValidator(address validator, bytes calldata data) internal {
(address prev, bytes memory disableModuleData) = abi.decode(data, (address, bytes));
$validators.pop({ account: msg.sender, prevEntry: prev, popEntry: validator });

Expand Down Expand Up @@ -130,7 +130,7 @@ abstract contract ModuleManager is AccessControl, Receiver, ExecutionHelper {
// Manage Executors
////////////////////////////////////////////////////

function _installExecutor(address executor, bytes memory data) internal {
function _installExecutor(address executor, bytes calldata data) internal {
SentinelListLib.SentinelList storage $executors = $moduleManager[msg.sender]._executors;
$executors.push(executor);
// Initialize Executor Module via Safe
Expand Down Expand Up @@ -268,7 +268,7 @@ abstract contract ModuleManager is AccessControl, Receiver, ExecutionHelper {

// Add 20 bytes for the address appended add the end
let success :=
staticcall(gas(), handler, calldataPtr, add(calldatasize(), 20), 0, 0)
staticcall(gas(), handler, calldataPtr, add(calldatasize(), 20), 0, 0)

let returnDataPtr := allocate(returndatasize())
returndatacopy(returnDataPtr, 0, returndatasize())
Expand Down
20 changes: 20 additions & 0 deletions accounts/safe7579/src/interfaces/ISafe7579Init.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import { CallType } from "erc7579/lib/ModeLib.sol";

interface ISafe7579Init {
struct ModuleInit {
address module;
bytes initData;
}

function initializeAccount(
ModuleInit[] calldata validators,
ModuleInit[] calldata executors,
ModuleInit[] calldata fallbacks,
ModuleInit[] calldata hooks
)
external
payable;
}
32 changes: 21 additions & 11 deletions accounts/safe7579/src/utils/Launchpad.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.0;

import { ISafe, SafeERC7579 } from "../SafeERC7579.sol";
import { ISafe7579Init } from "../interfaces/ISafe7579Init.sol";

/**
* Helper contract that gets delegatecalled byt SafeProxy.setup() to setup safe7579 as a module
Expand All @@ -15,9 +16,22 @@ contract Safe7579Launchpad {
SAFE7579Singleton = _safe7579Singleton;
}

function initSafe7579(address safe7579, bytes calldata safe7579InitCode) public {
// function initSafe7579(address safe7579, bytes calldata safe7579InitCode) public {
// ISafe(address(this)).enableModule(safe7579);
// SafeERC7579(payable(safe7579)).initializeAccount(safe7579InitCode);
// }

function initSafe7579(
address safe7579,
ISafe7579Init.ModuleInit[] calldata validators,
ISafe7579Init.ModuleInit[] calldata executors,
ISafe7579Init.ModuleInit[] calldata fallbacks,
ISafe7579Init.ModuleInit[] calldata hooks
)
public
{
ISafe(address(this)).enableModule(safe7579);
SafeERC7579(payable(safe7579)).initializeAccount(safe7579InitCode);
SafeERC7579(payable(safe7579)).initializeAccount(validators, executors, fallbacks, hooks);
}

function predictSafeAddress(
Expand Down Expand Up @@ -54,21 +68,17 @@ contract Safe7579Launchpad {
function getInitCode(
address[] memory signers,
uint256 threshold,
address[] calldata validators,
bytes[] calldata validatorsInitCode,
address[] calldata executors,
bytes[] calldata executorsInitCode
ISafe7579Init.ModuleInit[] calldata validators,
ISafe7579Init.ModuleInit[] calldata executors,
ISafe7579Init.ModuleInit[] calldata fallbacks,
ISafe7579Init.ModuleInit[] calldata hooks
)
external
view
returns (bytes memory initCode)
{
bytes memory safeLaunchPadSetup = abi.encodeCall(
this.initSafe7579,
(
address(SAFE7579Singleton),
abi.encode(validators, validatorsInitCode, executors, executorsInitCode)
)
this.initSafe7579, (address(SAFE7579Singleton), validators, executors, fallbacks, hooks)
);
// SETUP SAFE
initCode = abi.encodeCall(
Expand Down
17 changes: 13 additions & 4 deletions accounts/safe7579/test/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,22 @@ contract TestBaseUtil is Test {

bytes32 salt;

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

bytes memory initializer = launchpad.getInitCode({
signers: Solarray.addresses(signer1.addr, signer2.addr),
threshold: 2,
validators: Solarray.addresses(address(defaultValidator)),
validatorsInitCode: Solarray.bytess(""),
executors: Solarray.addresses(address(defaultExecutor)),
executorsInitCode: Solarray.bytess("")
validators: validators,
executors: executors,
fallbacks: fallbacks,
hooks: hooks
});
// computer counterfactual address for SafeProxy
safe = Safe(
Expand Down
5 changes: 2 additions & 3 deletions accounts/safe7579/test/SafeERC7579.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "erc7579/lib/ExecutionLib.sol";
import { TestBaseUtil, MockTarget, MockFallback } from "./Base.t.sol";

import "forge-std/console2.sol";

CallType constant CALLTYPE_STATIC = CallType.wrap(0xFE);

contract Safe7579Test is TestBaseUtil {
Expand Down Expand Up @@ -169,13 +170,11 @@ contract Safe7579Test is TestBaseUtil {
IERC7579Account(address(safe)).installModule(
3, address(_fallback), abi.encode(MockFallback.target.selector, CALLTYPE_STATIC, "")
);
( ret, msgSender, context) = MockFallback(address(safe)).target(1337);
(ret, msgSender, context) = MockFallback(address(safe)).target(1337);
assertEq(ret, 1337);
assertEq(msgSender, address(safe7579));
assertEq(context, address(safe));



vm.prank(address(safe));
IERC7579Account(address(safe)).installModule(
3,
Expand Down

0 comments on commit c0271d7

Please sign in to comment.