Skip to content

Instantly share code, notes, and snippets.

@frangio
Last active November 25, 2024 00:05
Show Gist options
  • Save frangio/e40305b9f99de290b73750dff5ebe50a to your computer and use it in GitHub Desktop.
Save frangio/e40305b9f99de290b73750dff5ebe50a to your computer and use it in GitHub Desktop.
A simple ERC-4337 account for use as an EIP-7702 template. UNAUDITED
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
struct PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees;
bytes paymasterAndData;
bytes signature;
}
interface IAccount {
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
interface IERC1155Receiver {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
contract EIP7702Account is IAccount, IERC165, IERC1155Receiver {
address payable private immutable entrypoint = payable(0x0000000071727De22E5E9d8BAf0edAc6f37da032);
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData) {
require(msg.sender == entrypoint);
(bytes32 r, bytes32 vs) = abi.decode(userOp.signature, (bytes32, bytes32));
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8(uint256(vs >> 255) + 27);
if (address(this) == ecrecover(userOpHash, v, r, s)) {
(bool ok, ) = entrypoint.call{
value: missingAccountFunds,
gas: type(uint256).max
}("");
require(ok);
return 0; // success
} else {
return 1; // failure
}
}
struct Call {
address target;
uint256 value;
bytes data;
}
function execute(Call[] calldata calls) external {
require(msg.sender == entrypoint || msg.sender == address(this));
for (uint256 i = 0; i < calls.length; i++) {
Call calldata call = calls[i];
(bool ok, bytes memory retdata) = call.target.call{value: call.value}(call.data);
if (!ok) assembly {
revert(add(retdata, 32), mload(retdata))
}
}
}
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
function onERC1155Received(address, address, uint256, uint256, bytes calldata) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
function supportsInterface(bytes4 id) external pure returns (bool) {
return id == type(IERC165).interfaceId || id == type(IERC1155Receiver).interfaceId;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment