Skip to content

Commit

Permalink
refactor: inherit from BaseLightAccount
Browse files Browse the repository at this point in the history
  • Loading branch information
jaypaik committed Mar 10, 2024
1 parent d60557c commit 397a053
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 302 deletions.
172 changes: 20 additions & 152 deletions src/LightAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@ pragma solidity ^0.8.23;
/* solhint-disable reason-string */

import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

import {BaseAccount} from "account-abstraction/core/BaseAccount.sol";
import {SIG_VALIDATION_FAILED} from "account-abstraction/core/Helpers.sol";
import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {TokenCallbackHandler} from "account-abstraction/samples/callback/TokenCallbackHandler.sol";

import {CustomSlotInitializable} from "./CustomSlotInitializable.sol";
import {BaseLightAccount} from "./common/BaseLightAccount.sol";
import {CustomSlotInitializable} from "./common/CustomSlotInitializable.sol";

/**
* @title A simple ERC-4337 compatible smart contract account with a designated owner account
Expand Down Expand Up @@ -49,7 +46,7 @@ import {CustomSlotInitializable} from "./CustomSlotInitializable.sol";
*
* 5. Uses custom errors.
*/
contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, CustomSlotInitializable, IERC1271 {
contract LightAccount is BaseLightAccount, CustomSlotInitializable {
using ECDSA for bytes32;
using MessageHashUtils for bytes32;

Expand All @@ -58,14 +55,11 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus
// keccak256(abi.encode(uint256(keccak256("light_account_v1.initializable")) - 1)) & ~bytes32(uint256(0xff));
bytes32 internal constant _INITIALIZABLE_STORAGE_POSITION =
0x33e4b41198cc5b8053630ed667ea7c0c4c873f7fc8d9a478b5d7259cec0a4a00;
// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e;
IEntryPoint private immutable _ENTRY_POINT;
bytes32 private constant _DOMAIN_SEPARATOR_TYPEHASH =
bytes32 internal constant _DOMAIN_SEPARATOR_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 private constant _LA_MSG_TYPEHASH = keccak256("LightAccountMessage(bytes message)");
bytes32 private constant _NAME_HASH = keccak256("LightAccount");
bytes32 private constant _VERSION_HASH = keccak256("1");
bytes32 internal constant _LA_MSG_TYPEHASH = keccak256("LightAccountMessage(bytes message)");
bytes32 internal constant _NAME_HASH = keccak256("LightAccount");
bytes32 internal constant _VERSION_HASH = keccak256("1");

struct LightAccountStorage {
address owner;
Expand All @@ -86,88 +80,27 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus
*/
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/**
* @dev The length of the array does not match the expected length.
*/
error ArrayLengthMismatch();

/**
* @dev The new owner is not a valid owner (e.g., `address(0)`, the
* account itself, or the current owner).
*/
error InvalidOwner(address owner);

/**
* @dev The caller is not authorized.
*/
error NotAuthorized(address caller);

modifier onlyOwner() {
_onlyOwner();
_;
}

constructor(IEntryPoint anEntryPoint) CustomSlotInitializable(_INITIALIZABLE_STORAGE_POSITION) {
_ENTRY_POINT = anEntryPoint;
_disableInitializers();
}

// solhint-disable-next-line no-empty-blocks
receive() external payable {}

/**
* @notice Execute a transaction. This may only be called directly by the
* owner or by the entry point via a user operation signed by the owner.
* @param dest The target of the transaction
* @param value The amount of wei sent in the transaction
* @param func The transaction's calldata
*/
function execute(address dest, uint256 value, bytes calldata func) external {
_requireFromEntryPointOrOwner();
_call(dest, value, func);
}

/**
* @notice Execute a sequence of transactions
* @param dest An array of the targets for each transaction in the sequence
* @param func An array of calldata for each transaction in the sequence.
* Must be the same length as dest, with corresponding elements representing
* the parameters for each transaction.
*/
function executeBatch(address[] calldata dest, bytes[] calldata func) external {
_requireFromEntryPointOrOwner();
if (dest.length != func.length) {
revert ArrayLengthMismatch();
}
uint256 length = dest.length;
for (uint256 i = 0; i < length;) {
_call(dest[i], 0, func[i]);
unchecked {
++i;
}
}
}

/**
* @notice Execute a sequence of transactions
* @param dest An array of the targets for each transaction in the sequence
* @param value An array of value for each transaction in the sequence
* @param func An array of calldata for each transaction in the sequence.
* Must be the same length as dest, with corresponding elements representing
* the parameters for each transaction.
* @notice Called once as part of initialization, either during initial deployment or when first upgrading to
* this contract.
* @dev The _ENTRY_POINT member is immutable, to reduce gas consumption. To upgrade EntryPoint,
* a new implementation of LightAccount must be deployed with the new EntryPoint address, then upgrading
* the implementation by calling `upgradeTo()`
* @param anOwner The initial owner of the account
*/
function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) external {
_requireFromEntryPointOrOwner();
if (dest.length != func.length || dest.length != value.length) {
revert ArrayLengthMismatch();
}
uint256 length = dest.length;
for (uint256 i = 0; i < length;) {
_call(dest[i], value[i], func[i]);
unchecked {
++i;
}
}
function initialize(address anOwner) public virtual initializer {
_initialize(anOwner);
}

/**
Expand All @@ -183,39 +116,6 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus
_transferOwnership(newOwner);
}

/**
* @notice Called once as part of initialization, either during initial deployment or when first upgrading to
* this contract.
* @dev The _ENTRY_POINT member is immutable, to reduce gas consumption. To upgrade EntryPoint,
* a new implementation of LightAccount must be deployed with the new EntryPoint address, then upgrading
* the implementation by calling `upgradeTo()`
* @param anOwner The initial owner of the account
*/
function initialize(address anOwner) public virtual initializer {
_initialize(anOwner);
}

/**
* @notice Deposit more funds for this account in the entryPoint
*/
function addDeposit() public payable {
entryPoint().depositTo{value: msg.value}(address(this));
}

/**
* @notice Withdraw value from the account's deposit
* @param withdrawAddress Target to send to
* @param amount Amount to withdraw
*/
function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public onlyOwner {
entryPoint().withdrawTo(withdrawAddress, amount);
}

/// @inheritdoc BaseAccount
function entryPoint() public view virtual override returns (IEntryPoint) {
return _ENTRY_POINT;
}

/**
* @notice Return the current owner of this account
* @return The current owner
Expand All @@ -224,14 +124,6 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus
return _getStorage().owner;
}

/**
* @notice Check current account deposit in the entryPoint
* @return The current account deposit
*/
function getDeposit() public view returns (uint256) {
return entryPoint().balanceOf(address(this));
}

/**
* @notice Returns the domain separator for this contract, as defined in the EIP-712 standard.
* @return bytes32 The domain separator hash.
Expand Down Expand Up @@ -268,16 +160,16 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus
}

/**
* @inheritdoc IERC1271
* @dev The signature is valid if it is signed by the owner's private key
* (if the owner is an EOA) or if it is a valid ERC-1271 signature from the
* owner (if the owner is a contract). Note that unlike the signature
* validation used in `validateUserOp`, this does **not** wrap the digest in
* an "Ethereum Signed Message" envelope before checking the signature in
* the EOA-owner case.
* @inheritdoc IERC1271
*/
function isValidSignature(bytes32 digest, bytes memory signature) public view override returns (bytes4) {
bytes32 messageHash = getMessageHash(abi.encode(digest));
function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4) {
bytes32 messageHash = getMessageHash(abi.encode(hash));
if (SignatureChecker.isValidSignatureNow(owner(), messageHash, signature)) {
return _1271_MAGIC_VALUE;
}
Expand Down Expand Up @@ -333,32 +225,8 @@ contract LightAccount is BaseAccount, TokenCallbackHandler, UUPSUpgradeable, Cus
return SIG_VALIDATION_FAILED;
}

function _onlyOwner() internal view {
//directly from EOA owner, or through the account itself (which gets redirected through execute())
if (msg.sender != address(this) && msg.sender != owner()) {
revert NotAuthorized(msg.sender);
}
}

// Require the function call went through EntryPoint or owner
function _requireFromEntryPointOrOwner() internal view {
if (msg.sender != address(entryPoint()) && msg.sender != owner()) {
revert NotAuthorized(msg.sender);
}
}

function _call(address target, uint256 value, bytes memory data) internal {
(bool success, bytes memory result) = target.call{value: value}(data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
}

function _authorizeUpgrade(address newImplementation) internal view override {
(newImplementation);
_onlyOwner();
function _isFromOwner() internal view virtual override returns (bool) {
return msg.sender == owner();
}

function _getStorage() internal pure returns (LightAccountStorage storage storageStruct) {
Expand Down
Loading

0 comments on commit 397a053

Please sign in to comment.