generated from PaulRBerg/foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
27f0508
commit 0656017
Showing
9 changed files
with
359 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,5 @@ | ||
@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ | ||
@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/ | ||
forge-std/=node_modules/forge-std/ | ||
@account-abstraction/contracts/=node_modules/@account-abstraction/contracts/ | ||
solady/=solady/src |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity >=0.8.25 <0.9.0; | ||
// // SPDX-License-Identifier: UNLICENSED | ||
// pragma solidity >=0.8.25 <0.9.0; | ||
|
||
import { Foo } from "../src/Foo.sol"; | ||
// import { SimplePlusAccount } from "../src/SimplePlusAccount.sol"; | ||
|
||
import { BaseScript } from "./Base.s.sol"; | ||
// import { BaseScript } from "./Base.s.sol"; | ||
|
||
/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting | ||
contract Deploy is BaseScript { | ||
function run() public broadcast returns (Foo foo) { | ||
foo = new Foo(); | ||
} | ||
} | ||
// /// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting | ||
// contract Deploy is BaseScript { | ||
// function run() public broadcast returns (Foo foo) { | ||
// foo = new Foo(); | ||
// } | ||
// } |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.25; | ||
|
||
import { SimpleAccount, IEntryPoint } from "@account-abstraction/contracts/samples/SimpleAccount.sol"; | ||
import { SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED } from "@account-abstraction/contracts/core/Helpers.sol"; | ||
import { PackedUserOperation } from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol"; | ||
import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; | ||
import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; | ||
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; | ||
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; | ||
|
||
contract SimplePlusAccount is SimpleAccount, IERC1271, EIP712 { | ||
using ECDSA for bytes32; | ||
using MessageHashUtils for bytes32; | ||
|
||
bytes32 internal constant _MESSAGE_TYPEHASH = keccak256("SimplePlusAccount(bytes message)"); | ||
|
||
modifier onlyAuthorized() { | ||
_onlyAuthorized(); | ||
_; | ||
} | ||
|
||
// @notice Signature types used for user operation validation and ERC-1271 signature validation. | ||
enum SignatureType { | ||
EOA, | ||
CONTRACT, | ||
CONTRACT_WITH_ADDR | ||
} | ||
|
||
error InvalidOwner(address newOwner); | ||
error NotAuthorized(); | ||
error InvalidSignatureType(); | ||
|
||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | ||
|
||
constructor(IEntryPoint anEntryPoint) SimpleAccount(anEntryPoint) EIP712("SimplePlusAccount", "1") { } | ||
|
||
/** | ||
* @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()` | ||
*/ | ||
function initialize(address anOwner) public virtual override initializer { | ||
_initialize(anOwner); | ||
} | ||
|
||
/// @dev Revert if the caller is not any of: | ||
/// 1. The entry point | ||
/// 2. The account itself (when redirected through `execute`, etc.) | ||
/// 3. An owner | ||
function _onlyAuthorized() internal view { | ||
if (msg.sender != address(entryPoint()) && msg.sender != address(this) && msg.sender != owner) { | ||
revert NotAuthorized(); | ||
} | ||
} | ||
|
||
/// @notice Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current | ||
/// owner or from the entry point via a user operation signed by the current owner. | ||
/// @param newOwner The new owner. | ||
function transferOwnership(address newOwner) external onlyAuthorized { | ||
if (newOwner == address(0) || newOwner == address(this) || owner == newOwner) { | ||
revert InvalidOwner(newOwner); | ||
} | ||
address oldOwner = owner; | ||
owner = newOwner; | ||
emit OwnershipTransferred(oldOwner, newOwner); | ||
} | ||
|
||
/* | ||
* @dev Validates the signature of a user operation. | ||
* | ||
* The signature is considered valid if: | ||
* - It is signed by the owner's private key (when the owner is an EOA), or | ||
* - It is a valid ERC-1271 signature from the owner (when the owner is a contract). | ||
* | ||
* Reverts if the signature is malformed or if the signature type is unrecognized. | ||
* | ||
* Note: | ||
* - This function differs from `validateUserOp` in that it does **not** wrap the hash in an | ||
* "Ethereum Signed Message" envelope before checking the signature for the EOA-owner case. | ||
*/ | ||
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4) { | ||
bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, keccak256(abi.encode(hash)))); | ||
bytes32 replaySafeHash = MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash); | ||
return _validateSignatureType(uint8(signature[0]), replaySafeHash, signature[1:]) == SIG_VALIDATION_SUCCESS | ||
? this.isValidSignature.selector | ||
: bytes4(0xffffffff); | ||
} | ||
|
||
function _validateSignature( | ||
PackedUserOperation calldata userOp, | ||
bytes32 userOpHash | ||
) | ||
internal | ||
virtual | ||
override | ||
returns (uint256 validationData) | ||
{ | ||
return _validateSignatureType(uint8(userOp.signature[0]), userOpHash, userOp.signature[1:]); | ||
} | ||
|
||
function _validateSignatureType( | ||
uint8 signatureType, | ||
bytes32 hash, | ||
bytes memory signature | ||
) | ||
private | ||
view | ||
returns (uint256) | ||
{ | ||
if (signature.length == 0) { | ||
revert InvalidSignatureType(); | ||
} | ||
|
||
if (signatureType == uint8(SignatureType.EOA)) { | ||
return _validateEOASignature(hash, signature); | ||
} else if (signatureType == uint8(SignatureType.CONTRACT)) { | ||
return _validateContractSignature(hash, signature); | ||
} | ||
|
||
revert InvalidSignatureType(); | ||
} | ||
|
||
function _validateEOASignature(bytes32 userOpHash, bytes memory signature) private view returns (uint256) { | ||
bytes32 signedHash = userOpHash.toEthSignedMessageHash(); | ||
address recovered = signedHash.recover(signature); | ||
return recovered == owner ? SIG_VALIDATION_SUCCESS : SIG_VALIDATION_FAILED; | ||
} | ||
|
||
function _validateContractSignature(bytes32 userOpHash, bytes memory signature) private view returns (uint256) { | ||
return SignatureChecker.isValidERC1271SignatureNow(owner, userOpHash, signature) | ||
? SIG_VALIDATION_SUCCESS | ||
: SIG_VALIDATION_FAILED; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.25; | ||
|
||
import "@openzeppelin/contracts/utils/Create2.sol"; | ||
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
|
||
import "./SimplePlusAccount.sol"; | ||
|
||
/** | ||
* A sample factory contract for SimplePlusAccount | ||
* A UserOperations "initCode" holds the address of the factory, and a method call (to createAccount, in this sample factory). | ||
* The factory's createAccount returns the target account address even if it is already installed. | ||
* This way, the entryPoint.getSenderAddress() can be called either before or after the account is created. | ||
*/ | ||
contract SimplePlusAccountFactory { | ||
SimplePlusAccount public immutable accountImplementation; | ||
|
||
constructor(IEntryPoint _entryPoint) { | ||
accountImplementation = new SimplePlusAccount(_entryPoint); | ||
} | ||
|
||
/** | ||
* create an account, and return its address. | ||
* returns the address even if the account is already deployed. | ||
* Note that during UserOperation execution, this method is called only if the account is not deployed. | ||
* This method returns an existing account address so that entryPoint.getSenderAddress() would work even after account creation | ||
*/ | ||
function createAccount(address owner,uint256 salt) public returns (SimplePlusAccount ret) { | ||
address addr = getAddress(owner, salt); | ||
uint256 codeSize = addr.code.length; | ||
if (codeSize > 0) { | ||
return SimplePlusAccount(payable(addr)); | ||
} | ||
ret = SimplePlusAccount(payable(new ERC1967Proxy{salt : bytes32(salt)}( | ||
address(accountImplementation), | ||
abi.encodeCall(SimplePlusAccount.initialize, (owner)) | ||
))); | ||
} | ||
|
||
/** | ||
* calculate the counterfactual address of this account as it would be returned by createAccount() | ||
*/ | ||
function getAddress(address owner,uint256 salt) public view returns (address) { | ||
return Create2.computeAddress(bytes32(salt), keccak256(abi.encodePacked( | ||
type(ERC1967Proxy).creationCode, | ||
abi.encode( | ||
address(accountImplementation), | ||
abi.encodeCall(SimplePlusAccount.initialize, (owner)) | ||
) | ||
))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.25; | ||
|
||
import "forge-std/src/Test.sol"; | ||
|
||
import { PackedUserOperation } from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol"; | ||
import { EntryPoint } from "@account-abstraction/contracts/core/EntryPoint.sol"; | ||
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; | ||
|
||
contract AccountTest is Test { | ||
using ECDSA for bytes32; | ||
using MessageHashUtils for bytes32; | ||
|
||
function getSignedOp( | ||
EntryPoint entryPoint, | ||
uint8 sigType, | ||
bytes memory callData, | ||
uint256 privateKey, | ||
address account | ||
) | ||
internal | ||
view | ||
returns (PackedUserOperation memory) | ||
{ | ||
PackedUserOperation memory op = getUnsignedOp(callData, account); | ||
bytes32 userOpHash = entryPoint.getUserOpHash(op); | ||
bytes32 ethSignedMessageHash = userOpHash.toEthSignedMessageHash(); | ||
bytes memory signature = abi.encodePacked(sigType, sign(privateKey, ethSignedMessageHash)); | ||
op.signature = signature; | ||
return op; | ||
} | ||
|
||
function getUnsignedOp(bytes memory callData, address account) internal pure returns (PackedUserOperation memory) { | ||
uint128 verificationGasLimit = 1 << 24; | ||
uint128 callGasLimit = 1 << 24; | ||
uint128 maxPriorityFeePerGas = 1 << 8; | ||
uint128 maxFeePerGas = 1 << 8; | ||
return PackedUserOperation({ | ||
sender: account, | ||
nonce: 0, | ||
initCode: "", | ||
callData: callData, | ||
accountGasLimits: bytes32(uint256(verificationGasLimit) << 128 | callGasLimit), | ||
preVerificationGas: 1 << 24, | ||
gasFees: bytes32(uint256(maxPriorityFeePerGas) << 128 | maxFeePerGas), | ||
paymasterAndData: "", | ||
signature: "" | ||
}); | ||
} | ||
|
||
function sign(uint256 privateKey, bytes32 digest) internal pure returns (bytes memory) { | ||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); | ||
return abi.encodePacked(r, s, v); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.