Skip to content

Commit

Permalink
fix: poc for bricked account
Browse files Browse the repository at this point in the history
  • Loading branch information
kopy-kat committed Oct 3, 2024
1 parent 144eb33 commit ce43c9a
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 10 deletions.
22 changes: 12 additions & 10 deletions src/core/Initializer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,19 @@ abstract contract Initializer is ISafe7579, ModuleManager {
override
onlyEntryPointOrSelf
{
// this will revert if already initialized
$validators.init({ account: msg.sender });
uint256 length = validators.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata validator = validators[i];
$validators.push({ account: msg.sender, newEntry: validator.module });
// @dev No events emitted here. Launchpad is expected to do this.
// at this point, the safeproxy singleton is not yet updated to the SafeSingleton
// calling execTransactionFromModule is not available yet.
if (!$validators.alreadyInitialized({ account: msg.sender })) {
// this will revert if already initialized
$validators.init({ account: msg.sender });
uint256 length = validators.length;
for (uint256 i; i < length; i++) {
ModuleInit calldata validator = validators[i];
$validators.push({ account: msg.sender, newEntry: validator.module });
// @dev No events emitted here. Launchpad is expected to do this.
// at this point, the safeproxy singleton is not yet updated to the SafeSingleton
// calling execTransactionFromModule is not available yet.
}
emit Safe7579Initialized(msg.sender);
}
emit Safe7579Initialized(msg.sender);
}

/**
Expand Down
252 changes: 252 additions & 0 deletions test/Bricked.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "forge-std/Test.sol";
import { Safe7579 } from "src/Safe7579.sol";
import { ISafe7579 } from "src/ISafe7579.sol";
import { IERC7484 } from "src/interfaces/IERC7484.sol";
import "src/DataTypes.sol";
import { ModuleManager } from "src/core/ModuleManager.sol";
import { MockValidator } from "module-bases/mocks/MockValidator.sol";
import { MockRegistry } from "./mocks/MockRegistry.sol";
import { MockExecutor } from "./mocks/MockExecutor.sol";
import { MockFallback } from "./mocks/MockFallback.sol";
import { ExecutionLib } from "erc7579/lib/ExecutionLib.sol";
import { ModeLib } from "erc7579/lib/ModeLib.sol";
import { IERC7579Account, Execution } from "erc7579/interfaces/IERC7579Account.sol";
import { MockTarget } from "./mocks/MockTarget.sol";

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

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

import { Simulator } from "@rhinestone/erc4337-validation/src/Simulator.sol";

contract RevertTarget {
function set(uint256 value) public {
revert("RevertTarget: revert");
}
}

contract BrickedTest is Test {
using Simulator for PackedUserOperation; // or UserOperation

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

MockValidator defaultValidator;
MockExecutor defaultExecutor;
RevertTarget target;

Account signer1 = makeAccount("signer1");
Account signer2 = makeAccount("signer2");

IEntryPoint entrypoint;
bytes userOpInitCode;
IERC7484 registry;

struct Setup {
address singleton;
address signerFactory;
bytes signerData;
address setupTo;
bytes setupData;
address fallbackHandler;
}

function setUp() public virtual {
// Set up EntryPoint
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 RevertTarget();

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(signer1.addr),
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(RevertTarget.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"
);

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

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

safe = 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 test_foo() public {
PackedUserOperation memory userOp =
getDefaultUserOp(address(safe), address(defaultValidator));

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(signer1.addr),
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(RevertTarget.set, (1337))
})
)
)
});

userOp.callData = abi.encodeCall(Safe7579Launchpad.setupSafe, (initData));

userOp.signature = abi.encodePacked(
uint48(0), uint48(type(uint48).max), hex"4141414141414141414141414141414141"
);

bytes32 userOpHash = entrypoint.getUserOpHash(userOp);
PackedUserOperation[] memory userOps = new PackedUserOperation[](1);
userOps[0] = userOp;

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

assertTrue(true);
}

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")
});
}
}

0 comments on commit ce43c9a

Please sign in to comment.