mirror of
https://github.com/luxfi/eoa-contracts.git
synced 2026-10-30 16:58:32 +00:00
183 lines
5.9 KiB
Solidity
183 lines
5.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.17;
|
|
|
|
import "./Proxy.sol";
|
|
import "./BaseSmartAccountV1.sol";
|
|
import {DefaultCallbackHandler} from "../../../handler/DefaultCallbackHandler.sol";
|
|
|
|
/**
|
|
* @title Smart Account Factory - factory responsible for deploying Smart Accounts using CREATE2 and CREATE
|
|
* @dev It deploys Smart Accounts as proxies pointing to `basicImplementation` that is immutable.
|
|
* This allows keeping the same address for the same Smart Account owner on various chains via CREATE2
|
|
* @author Chirag Titiya - <[email protected]>
|
|
*/
|
|
contract SmartAccountFactoryV1 {
|
|
address public immutable basicImplementation;
|
|
DefaultCallbackHandler public immutable minimalHandler;
|
|
|
|
event AccountCreation(
|
|
address indexed account,
|
|
address indexed owner,
|
|
uint256 indexed index
|
|
);
|
|
event AccountCreationWithoutIndex(
|
|
address indexed account,
|
|
address indexed owner
|
|
);
|
|
|
|
constructor(address _basicImplementation) {
|
|
require(
|
|
_basicImplementation != address(0),
|
|
"implementation cannot be zero"
|
|
);
|
|
basicImplementation = _basicImplementation;
|
|
minimalHandler = new DefaultCallbackHandler();
|
|
}
|
|
|
|
/**
|
|
* @notice Deploys account using create2 and points it to basicImplementation
|
|
* @param _owner EOA signatory for the account to be deployed
|
|
* @param _index extra salt that allows to deploy more account if needed for same EOA (default 0)
|
|
*/
|
|
function deployCounterFactualAccount(
|
|
address _owner,
|
|
uint256 _index
|
|
) public returns (address proxy) {
|
|
// create initializer data based on init method, _owner and minimalHandler
|
|
bytes memory initializer = _getInitializer(_owner);
|
|
|
|
bytes32 salt = keccak256(
|
|
abi.encodePacked(keccak256(initializer), _index)
|
|
);
|
|
|
|
bytes memory deploymentData = abi.encodePacked(
|
|
type(Proxy).creationCode,
|
|
uint256(uint160(basicImplementation))
|
|
);
|
|
|
|
assembly {
|
|
proxy := create2(
|
|
0x0,
|
|
add(0x20, deploymentData),
|
|
mload(deploymentData),
|
|
salt
|
|
)
|
|
}
|
|
require(address(proxy) != address(0), "Create2 call failed");
|
|
|
|
// calldata for init method
|
|
if (initializer.length > 0) {
|
|
assembly {
|
|
if eq(
|
|
call(
|
|
gas(),
|
|
proxy,
|
|
0,
|
|
add(initializer, 0x20),
|
|
mload(initializer),
|
|
0,
|
|
0
|
|
),
|
|
0
|
|
) {
|
|
revert(0, 0)
|
|
}
|
|
}
|
|
}
|
|
emit AccountCreation(proxy, _owner, _index);
|
|
}
|
|
|
|
/**
|
|
* @dev Allows to retrieve the creation code used for the Proxy deployment.
|
|
* @return The creation code for the Proxy.
|
|
*/
|
|
function accountCreationCode() public pure returns (bytes memory) {
|
|
return type(Proxy).creationCode;
|
|
}
|
|
|
|
/**
|
|
* @notice Deploys account using create and points it to _implementation
|
|
* @param _owner EOA signatory for the account to be deployed
|
|
* @return proxy address of the deployed account
|
|
*/
|
|
function deployAccount(address _owner) public returns (address proxy) {
|
|
bytes memory deploymentData = abi.encodePacked(
|
|
type(Proxy).creationCode,
|
|
uint256(uint160(basicImplementation))
|
|
);
|
|
|
|
assembly {
|
|
proxy := create(
|
|
0x0,
|
|
add(0x20, deploymentData),
|
|
mload(deploymentData)
|
|
)
|
|
}
|
|
require(address(proxy) != address(0), "Create call failed");
|
|
|
|
bytes memory initializer = _getInitializer(_owner);
|
|
|
|
// calldata for init method
|
|
if (initializer.length > 0) {
|
|
assembly {
|
|
if eq(
|
|
call(
|
|
gas(),
|
|
proxy,
|
|
0,
|
|
add(initializer, 0x20),
|
|
mload(initializer),
|
|
0,
|
|
0
|
|
),
|
|
0
|
|
) {
|
|
revert(0, 0)
|
|
}
|
|
}
|
|
}
|
|
emit AccountCreationWithoutIndex(proxy, _owner);
|
|
}
|
|
|
|
/**
|
|
* @dev Allows to retrieve the initializer data for the account.
|
|
* @param _owner EOA signatory for the account to be deployed
|
|
* @return initializer bytes for init method
|
|
*/
|
|
function _getInitializer(
|
|
address _owner
|
|
) internal view returns (bytes memory) {
|
|
return
|
|
abi.encodeCall(
|
|
BaseSmartAccount.init,
|
|
(_owner, address(minimalHandler))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @notice Allows to find out account address prior to deployment
|
|
* @param _owner EOA signatory for the account to be deployed
|
|
* @param _index extra salt that allows to deploy more accounts if needed for same EOA (default 0)
|
|
*/
|
|
function getAddressForCounterFactualAccount(
|
|
address _owner,
|
|
uint256 _index
|
|
) external view returns (address _account) {
|
|
// create initializer data based on init method, _owner and minimalHandler
|
|
bytes memory initializer = _getInitializer(_owner);
|
|
bytes memory code = abi.encodePacked(
|
|
type(Proxy).creationCode,
|
|
uint256(uint160(basicImplementation))
|
|
);
|
|
bytes32 salt = keccak256(
|
|
abi.encodePacked(keccak256(initializer), _index)
|
|
);
|
|
bytes32 hash = keccak256(
|
|
abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(code))
|
|
);
|
|
_account = address(uint160(uint256(hash)));
|
|
}
|
|
// off-chain calculation
|
|
// return ethers.utils.getCreate2Address(<factory address>, <create2 salt>, ethers.utils.keccak256(creationCode + implementation));
|
|
}
|