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: simple account 1271 #511

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
162 changes: 162 additions & 0 deletions contracts/samples/SimpleAccountWith1271.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;

/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "../core/BaseAccount.sol";
import "../core/Helpers.sol";
import "./callback/TokenCallbackHandler.sol";
import "solady/src/accounts/ERC1271.sol";

/**
* minimal account.
* this is sample minimal account.
* has execute, eth handling methods
* has a single signer that can send requests through the entryPoint.
*/
contract SimpleAccountWith1271 is BaseAccount, ERC1271, TokenCallbackHandler, UUPSUpgradeable, Initializable {
address public owner;

IEntryPoint private immutable _entryPoint;

event SimpleAccountInitialized(IEntryPoint indexed entryPoint, address indexed owner);

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

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

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

constructor(IEntryPoint anEntryPoint) {
_entryPoint = anEntryPoint;
_disableInitializers();
}

function _onlyOwner() internal view {
//directly from EOA owner, or through the account itself (which gets redirected through execute())
require(msg.sender == owner || msg.sender == address(this), "only owner");
}

/**
* execute a transaction (called directly from owner, or by entryPoint)
* @param dest destination address to call
* @param value the value to pass in this call
* @param func the calldata to pass in this call
*/
function execute(address dest, uint256 value, bytes calldata func) external {
_requireFromEntryPointOrOwner();
_call(dest, value, func);
}

/**
* execute a sequence of transactions
* @dev to reduce gas consumption for trivial case (no value), use a zero-length array to mean zero value
* @param dest an array of destination addresses
* @param value an array of values to pass to each call. can be zero-length for no-value calls
* @param func an array of calldata to pass to each call
*/
function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) external {
_requireFromEntryPointOrOwner();
require(dest.length == func.length && (value.length == 0 || value.length == func.length), "wrong array lengths");
if (value.length == 0) {
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], 0, func[i]);
}
} else {
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], value[i], func[i]);
}
}
}

/**
* @dev The _entryPoint member is immutable, to reduce gas consumption. To upgrade EntryPoint,
* a new implementation of SimpleAccount must be deployed with the new EntryPoint address, then upgrading
* the implementation by calling `upgradeTo()`
* @param anOwner the owner (signer) of this account
*/
function initialize(address anOwner) public virtual initializer {
_initialize(anOwner);
}

function _initialize(address anOwner) internal virtual {
owner = anOwner;
emit SimpleAccountInitialized(_entryPoint, owner);
}

// Require the function call went through EntryPoint or owner
function _requireFromEntryPointOrOwner() internal view {
require(msg.sender == address(entryPoint()) || msg.sender == owner, "account: not Owner or EntryPoint");
}

/// @dev EIP712 domain name and version.
function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
name = "Simple Account With ERC1271";
version = "1.0.0";
}

/// @dev Uses the `owner` as the ERC1271 signer.
function _erc1271Signer() internal view virtual override(ERC1271) returns (address) {
return owner;
}

/// implement template method of BaseAccount
function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
internal override virtual returns (uint256 validationData) {
bytes32 hash = MessageHashUtils.toEthSignedMessageHash(userOpHash);
if (owner != ECDSA.recover(hash, userOp.signature))
return SIG_VALIDATION_FAILED;
return SIG_VALIDATION_SUCCESS;
}

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

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

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

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

function _authorizeUpgrade(address newImplementation) internal view override {
(newImplementation);
_onlyOwner();
}
}

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"dependencies": {
"@nomiclabs/hardhat-etherscan": "^2.1.6",
"@openzeppelin/contracts": "^5.0.0",
"solady": "github:vectorized/solady",
"@thehubbleproject/bls": "^0.5.1",
"@typechain/hardhat": "^2.3.0",
"@types/debug": "^4.1.12",
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8517,6 +8517,10 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"

"solady@github:vectorized/solady":
version "0.0.273"
resolved "https://codeload.github.com/vectorized/solady/tar.gz/3d0f41a9b83cbbddc0e2d3844efc0a63809d4db8"

solc@0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a"
Expand Down
Loading