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(create): add performCreate[2] method. #168

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
47 changes: 47 additions & 0 deletions src/account/UpgradeableModularAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ contract UpgradeableModularAccount is
error UnrecognizedFunction(bytes4 selector);
error UserOpNotFromEntryPoint();
error UserOpValidationFunctionMissing(bytes4 selector);
error CreateFailed();
andysim3d marked this conversation as resolved.
Show resolved Hide resolved

constructor(IEntryPoint anEntryPoint) {
_ENTRY_POINT = anEntryPoint;
Expand Down Expand Up @@ -376,6 +377,52 @@ contract UpgradeableModularAccount is
_postNativeFunction(postExecHooks, postHookArgs);
}

///
/// @param value The value to send to the new contract constructor
/// @param initCode The initCode to deploy.
andysim3d marked this conversation as resolved.
Show resolved Hide resolved
function performCreate(uint256 value, bytes calldata initCode)
external
payable
virtual
returns (address createdAddr)
{
assembly ("memory-safe") {
let fmp := mload(0x40)
let len := initCode.length
calldatacopy(fmp, initCode.offset, len)

createdAddr := create(value, fmp, len)

if iszero(createdAddr) {
mstore(0x00, 0x7e16b8cd)
revert(0x1c, 0x04)
}
}
}

///
/// @param value The value to send to the new contract constructor.
/// @param initCode The initCode to deploy.
/// @param salt The salt to use for the create2 operation.
andysim3d marked this conversation as resolved.
Show resolved Hide resolved
function performCreate2(uint256 value, bytes calldata initCode, bytes32 salt)
external
payable
virtual
returns (address createdAddr)
{
assembly ("memory-safe") {
let fmp := mload(0x40)
let len := initCode.length
calldatacopy(fmp, initCode.offset, len)

createdAddr := create2(value, fmp, len, salt)
if iszero(createdAddr) {
mstore(0x00, 0x7e16b8cd)
revert(0x1c, 0x04)
}
}
}

/// @inheritdoc IERC777Recipient
/// @dev Runtime validation is bypassed for this function, but we still allow pre and post exec hooks to be
/// assigned and run.
Expand Down
31 changes: 31 additions & 0 deletions test/account/UpgradeableModularAccount.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,37 @@ contract UpgradeableModularAccountTest is Test {
vm.stopPrank();
}

function testCreate() public {
address account = factory.createAccount(0, owners1);

address expectedAddr = computeCreateAddress(account, vm.getNonce(account));
andysim3d marked this conversation as resolved.
Show resolved Hide resolved
address returnedAddr = account1.performCreate(
0, abi.encodePacked(type(UpgradeableModularAccount).creationCode, abi.encode(address(entryPoint)))
);

assertEq(address(UpgradeableModularAccount(payable(expectedAddr)).entryPoint()), address(entryPoint));
assertEq(returnedAddr, expectedAddr);
}

function testCreate2() public {
address account = factory.createAccount(0, owners1);

bytes memory initCode =
abi.encodePacked(type(UpgradeableModularAccount).creationCode, abi.encode(address(entryPoint)));
bytes32 initCodeHash = keccak256(initCode);
bytes32 salt = bytes32(hex"01234b");

address expectedAddr = computeCreate2Address(salt, initCodeHash, address(account));
address returnedAddr = account1.performCreate2(0, initCode, salt);

assertEq(address(UpgradeableModularAccount(payable(expectedAddr)).entryPoint()), address(entryPoint));
assertEq(returnedAddr, expectedAddr);

vm.expectRevert(UpgradeableModularAccount.CreateFailed.selector);
// multi-depoly with same salt got reverted
andysim3d marked this conversation as resolved.
Show resolved Hide resolved
account1.performCreate2(0, initCode, salt);
}

// Internal Functions

function _printStorageReadsAndWrites(address addr) internal {
Expand Down
Loading