Skip to content

Commit

Permalink
🔥 Tests passing again
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroknots committed Mar 8, 2024
1 parent 3e5f10c commit 0e534ff
Show file tree
Hide file tree
Showing 9 changed files with 347 additions and 181 deletions.
5 changes: 5 additions & 0 deletions accounts/safe7579/src/SafeERC7579.sol
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,11 @@ contract SafeERC7579 is ISafeOp, IERC7579Account, AccessControl, IMSA, HookManag
emit Safe7579Initialized(msg.sender);
}

/**
* Safe7579 is using validator selection encoding in the userop nonce.
* to make it easier for SDKs / devs to integrate, this function can be
* called to get the next nonce for a specific validator
*/
function getNonce(address safe, address validator) external view returns (uint256 nonce) {
uint192 key = uint192(bytes24(bytes20(address(validator))));
nonce = IEntryPoint(entryPoint()).getNonce(safe, key);
Expand Down
12 changes: 12 additions & 0 deletions accounts/safe7579/src/interfaces/ISafe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@
pragma solidity ^0.8.0;

interface ISafe {
function setup(
address[] calldata _owners,
uint256 _threshold,
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver
)
external;

/**
* @dev Allows a Module to execute a Safe transaction without any further confirmations.
* @param to Destination address of module transaction.
Expand Down
69 changes: 0 additions & 69 deletions accounts/safe7579/src/utils/Boostrap.sol

This file was deleted.

84 changes: 82 additions & 2 deletions accounts/safe7579/src/utils/Launchpad.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,88 @@
import "../SafeERC7579.sol";
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity ^0.8.0;

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

/**
* Helper contract that gets delegatecalled byt SafeProxy.setup() to setup safe7579 as a module
* (safe module)
* as well as initializing Safe7579 for the SafeProxy
*/
contract Safe7579Launchpad {
address immutable safe7579Singleton;

constructor(address _safe7579Singleton) {
safe7579Singleton = _safe7579Singleton;
}

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

function predictSafeAddress(
address singleton,
address safeProxyFactory,
bytes memory creationCode,
bytes32 salt,
bytes memory initializer
)
external
pure
returns (address safeProxy)
{
salt = keccak256(abi.encodePacked(keccak256(initializer), salt));

safeProxy = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(safeProxyFactory),
salt,
keccak256(
abi.encodePacked(creationCode, uint256(uint160(address(singleton))))
)
)
)
)
)
);
}

function getInitCode(
address[] memory signers,
uint256 threshold,
address[] calldata validators,
bytes[] calldata validatorsInitCode,
address[] calldata executors,
bytes[] calldata executorsInitCode
)
external
view
returns (bytes memory initCode)
{
bytes memory safeLaunchPadSetup = abi.encodeCall(
this.initSafe7579,
(
address(safe7579Singleton),
abi.encode(validators, validatorsInitCode, executors, executorsInitCode)
)
);
// SETUP SAFE
initCode = abi.encodeCall(
ISafe.setup,
(
signers,
threshold,
address(this),
safeLaunchPadSetup,
safe7579Singleton,
address(0),
0,
payable(address(0))
)
);
}
}
15 changes: 0 additions & 15 deletions accounts/safe7579/src/utils/SignatureValidatorConstants.sol

This file was deleted.

143 changes: 66 additions & 77 deletions accounts/safe7579/test/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,106 +10,95 @@ import { MockFallback } from "./mocks/MockFallback.sol";
import { MockTarget } from "@rhinestone/modulekit/src/mocks/MockTarget.sol";

import { Safe } from "@safe-global/safe-contracts/contracts/Safe.sol";
import { SafeProxyFactory } from
"@safe-global/safe-contracts/contracts/proxies/SafeProxyFactory.sol";
import { LibClone } from "solady/src/utils/LibClone.sol";
import "src/utils/Launchpad.sol";

import { Solarray } from "solarray/Solarray.sol";
import "./dependencies/EntryPoint.sol";

contract Bootstrap is ModuleManager {
function singleInitMSA(
address validator,
bytes calldata validatorData,
address executor,
bytes calldata executorData
)
external
{
// init validator
_installValidator(address(validator), validatorData);
_installExecutor(executor, executorData);
}
}

contract TestBaseUtil is Test {
// singletons
SafeERC7579 internal erc7579Mod;
Safe internal safeImpl;
Safe internal safe;
IEntryPoint internal entrypoint = IEntryPoint(ENTRYPOINT_ADDR);
SafeERC7579 safe7579;
Safe singleton;
Safe safe;
SafeProxyFactory safeProxyFactory;
Safe7579Launchpad launchpad;

MockValidator internal defaultValidator;
MockExecutor internal defaultExecutor;
Bootstrap internal bootstrap;
MockValidator defaultValidator;
MockExecutor defaultExecutor;

MockTarget internal target;
Account signer1 = makeAccount("signer1");
Account signer2 = makeAccount("signer2");

Account internal signer1;
Account internal signer2;
IEntryPoint entrypoint;
bytes userOpInitCode;

function setUp() public virtual {
// Set up EntryPoint
etchEntrypoint();

// Set up MSA and Factory
bootstrap = new Bootstrap();
erc7579Mod = new SafeERC7579();
safeImpl = new Safe();

signer1 = makeAccount("signer1");
signer2 = makeAccount("signer2");
entrypoint = etchEntrypoint();
singleton = new Safe();
safeProxyFactory = new SafeProxyFactory();
safe7579 = new SafeERC7579();
launchpad = new Safe7579Launchpad(address(safe7579));

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

// Set up Target for testing
target = new MockTarget();

(safe,) = safeSetup();
vm.deal(address(safe), 100 ether);
}

function safeSetup() internal returns (Safe clone, address _defaultValidator) {
clone = Safe(payable(LibClone.clone(address(safeImpl))));
_defaultValidator = address(defaultValidator);

address[] memory signers = new address[](2);
signers[0] = signer1.addr;
signers[1] = signer2.addr;

// TODO: think about launchpad impl see: passkey for safe.
clone.setup({
_owners: signers,
_threshold: 2,
to: address(0), // optional delegatecall
data: "",
fallbackHandler: address(erc7579Mod),
paymentToken: address(0), // optional payment token
payment: 0,
paymentReceiver: payable(address(0)) // optional payment receiver
});
bytes32 salt;

vm.startPrank(address(clone));
clone.enableModule(address(erc7579Mod));
erc7579Mod.initializeAccount(
abi.encode(
address(bootstrap),
abi.encodeCall(
Bootstrap.singleInitMSA, (_defaultValidator, "", address(defaultExecutor), "")
)
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("")
});
// computer counterfactual address for SafeProxy
safe = Safe(
payable(
launchpad.predictSafeAddress({
singleton: address(singleton),
safeProxyFactory: address(safeProxyFactory),
creationCode: safeProxyFactory.proxyCreationCode(),
salt: salt,
initializer: initializer
})
)
);
vm.stopPrank();
userOpInitCode = initCode(initializer, salt);
}

function getNonce(address account, address validator) internal view returns (uint256 nonce) {
uint192 key = uint192(bytes24(bytes20(address(validator))));
nonce = entrypoint.getNonce(address(account), key);
function initCode(
bytes memory initializer,
bytes32 salt
)
internal
view
returns (bytes memory _initCode)
{
_initCode = abi.encodePacked(
address(safeProxyFactory),
abi.encodeCall(
SafeProxyFactory.createProxyWithNonce,
(address(singleton), initializer, uint256(salt))
)
);
}

function getDefaultUserOp() internal pure returns (PackedUserOperation memory userOp) {
function getDefaultUserOp(
address account,
address validator
)
internal
view
returns (PackedUserOperation memory userOp)
{
userOp = PackedUserOperation({
sender: address(0),
nonce: 0,
sender: account,
nonce: safe7579.getNonce(account, validator),
initCode: "",
callData: "",
accountGasLimits: bytes32(abi.encodePacked(uint128(2e6), uint128(2e6))),
Expand Down
Loading

0 comments on commit 0e534ff

Please sign in to comment.