Skip to content

Commit

Permalink
Merge pull request #1 from phipsae/localChainAA
Browse files Browse the repository at this point in the history
Merge into main
  • Loading branch information
phipsae authored Jul 12, 2024
2 parents 8da38d6 + 982d5bd commit 38e1b21
Show file tree
Hide file tree
Showing 20 changed files with 1,902 additions and 605 deletions.
9 changes: 6 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[submodule "packages/foundry/lib/forge-std"]
path = packages/foundry/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "packages/foundry/lib/openzeppelin-contracts"]
path = packages/foundry/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "packages/foundry/lib/solidity-bytes-utils"]
path = packages/foundry/lib/solidity-bytes-utils
url = https://github.com/gnsps/solidity-bytes-utils
[submodule "packages/foundry/lib/account-abstraction"]
path = packages/foundry/lib/account-abstraction
url = https://github.com/eth-infinitism/account-abstraction
[submodule "packages/foundry/lib/openzeppelin-contracts"]
path = packages/foundry/lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
# 👩‍🏫 Guide to Account Abstraction

This repository is designed to help you understand Account Abstraction using ERC4337. We utilize the Simple Account, Account Factory, Paymaster, and EntryPoint contract from the official implementation available at https://github.com/eth-infinitism/account-abstraction. Everything is built with SE2.

![overview aa tutorial](https://github.com/phipsae/AA-SE2/blob/localChainAA/assets/overview.png)

When a user submits a user operation to the EntryPoint contract, it checks if the smart account is already created. If not, it calls the account factory to create an account. Then, the call data is used to execute the transaction. The EntryPoint also ensures that it is reimbursed for the gas costs, so it needs to be funded upfront by the party paying for the transaction. In our example, we use a basic implementation of a paymaster, which covers all costs. If the validation is successful, the EntryPoint interacts with the mainnet to complete the transaction.

In this example, no bundler is used. We pass the user operation directly to the EntryPoint. A bundler collects multiple user operations and submits them as a bundle to the EntryPoint contract. The EntryPoint contract validates, executes, and processes these operations, ensuring efficient and secure transaction handling on the Ethereum network. One advantage of using a bundler is improved gas efficiency.

Start by examining the AccountSimple and AccountFactorySimple smart contracts to understand their functions. And then have a look into the EntryPoint contract, especially the handleOps function.

The repository breaks down each step to help you gain a better understanding of the process. Eventually, we can verify if we have incremented the count in our smart contract and check the counter (step 6).

This repository is a work in progress. If further clarification is needed, let me know and I will provide it. As a primary resource, I used the videos from Alchemy for Account, which were very helpful and highly recommended.

## 👩‍💻 Contracts

### AccountSimple.sol

AccountSimple inherits from the example Account in the https://github.com/eth-infinitism/account-abstraction repository by the Ethereum Foundation. As a result, it must implement the mandatory validateUserOp function. This function contains the entire validation logic, allowing you to choose the desired method (such as Passkeys, Email, etc.). In this example, we use the standard ECDSA key pair validation as used with EOAs on Ethereum. The difference is that the validation logic is embedded within the smart contract.

It's important to note that there is no verification required to call the execute function, meaning anyone can call it. A potential check could be to verify that the EntryPoint (having Entrypoint as a state variable in Account) is the msg.sender.

### AccountFactorySimple.sol

The Account Factory contains only a createAccount function, which is used to create Simple Accounts. This factory is called by the EntryPoint contract if an initcode is set.

To determine the smart account address, we use the Create method instead of Create2. This involves using a factory nonce that we specify inside our next.js project. Changing the factory nonce will result in a different smart account address for a given EOA. However, for scenarios involving a bundler, Create2 must be used since Create is a forbidden opcode.

### Paymaster.sol

The paymaster is a simple contract without any special features. It inherits two mandatory functions from the https://github.com/eth-infinitism/account-abstraction repository.

We have to deposit funds for the paymaster in the EntryPoint contract (using its depositTo function). Since the entrypoint has to pay for the account creation and tx execution, it needs to make sure it has the respective funds.

### EntryPoint.sol

We use the EntryPoint from the https://github.com/eth-infinitism/account-abstraction repository. After we create the singed userOps with initCode and callData we hand it over to the EntryPoint, via calling the handleOps function.

# 🏗 Scaffold-ETH 2

<h4 align="center">
Expand Down Expand Up @@ -66,7 +106,6 @@ Run smart contract test with `yarn foundry:test`
- Edit your frontend homepage at `packages/nextjs/app/page.tsx`. For guidance on [routing](https://nextjs.org/docs/app/building-your-application/routing/defining-routes) and configuring [pages/layouts](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts) checkout the Next.js documentation.
- Edit your deployment scripts in `packages/foundry/script`


## Documentation

Visit our [docs](https://docs.scaffoldeth.io) to learn how to start building with Scaffold-ETH 2.
Expand All @@ -77,4 +116,4 @@ To know more about its features, check out our [website](https://scaffoldeth.io)

We welcome contributions to Scaffold-ETH 2!

Please see [CONTRIBUTING.MD](https://github.com/scaffold-eth/scaffold-eth-2/blob/main/CONTRIBUTING.md) for more information and guidelines for contributing to Scaffold-ETH 2.
Please see [CONTRIBUTING.MD](https://github.com/scaffold-eth/scaffold-eth-2/blob/main/CONTRIBUTING.md) for more information and guidelines for contributing to Scaffold-ETH 2.
Binary file added assets/overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/placeholder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

35 changes: 0 additions & 35 deletions packages/foundry/contracts/Account123.sol

This file was deleted.

36 changes: 0 additions & 36 deletions packages/foundry/contracts/Account123Factory.sol

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity >=0.8.0 <0.9.0;

import "./AccountSimple.sol";

contract AccountSimpleFactory {
contract AccountFactorySimple {
function createAccount(address owner) external returns (address) {
AccountSimple acc = new AccountSimple(owner);
return address(acc);
Expand Down
30 changes: 17 additions & 13 deletions packages/foundry/contracts/AccountSimple.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

// import "/Users/philip/Programming/Ethereum/AABuild/PWAA/node_modules/@account-abstraction/contracts/core/EntryPoint.sol";
import "/Users/philip/Programming/Ethereum/AABuild/PWAA/node_modules/@account-abstraction/contracts/interfaces/IAccount.sol";
import "@account-abstraction/contracts/interfaces/IAccount.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "forge-std/console.sol";

contract AccountSimple is IAccount {
Expand All @@ -13,21 +11,27 @@ contract AccountSimple is IAccount {
address public owner;

constructor(address _owner) {
console.logString("AccountSimple constructor called");
console.logAddress(_owner);
owner = _owner;
}

function validateUserOp(UserOperation calldata userOp, bytes32 , uint256 ) view external returns (uint256 validationData) {
return 0;
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 ) view external returns (uint256 validationData) {
/// first we hash the message hello, then we use the EthSignedMessageHash to hash the message according to the Ethereum Standard
/// then we use ECDSA.recover to recover the address that signed the message, therefore we provide the message hash and the signature
address recovered = ECDSA.recover(ECDSA.toEthSignedMessageHash(userOpHash), userOp.signature);
/// not save because replay with signature possible, which can be found on chain
// address recovered = ECDSA.recover(MessageHashUtils.toEthSignedMessageHash(keccak256("hi")), userOp.signature);
console.logString("Recovered address: ");
console.logAddress(recovered);
/// 0 means valid, 1 means invalid (the other way around :))
return owner == recovered ? 0 : 1;
// /// if nothing should gets validated
// return 0;
}

/// no verification in here, execute can be called form everywhere
function execute() external {
count++;
}
}

// contract AccountSimpleFactory {
// function createAccount(address owner) external returns (address) {
// AccountSimple acc = new AccountSimple(owner);
// return address(acc);
// }
// }
}
3 changes: 2 additions & 1 deletion packages/foundry/contracts/Paymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
pragma solidity >=0.8.0 <0.9.0;


import "/Users/philip/Programming/Ethereum/AABuild/PWAA/node_modules/@account-abstraction/contracts/interfaces/IPaymaster.sol";
import "@account-abstraction/contracts/interfaces/IPaymaster.sol";

contract Paymaster is IPaymaster {

function validatePaymasterUserOp(UserOperation calldata, bytes32 , uint256)
external pure returns (bytes memory context, uint256 validationData) {
// that means we pay for every user operation that comes through here
context = new bytes(0);
validationData = 0;
}
Expand Down
15 changes: 0 additions & 15 deletions packages/foundry/contracts/Test.sol

This file was deleted.

84 changes: 0 additions & 84 deletions packages/foundry/contracts/YourContract.sol

This file was deleted.

1 change: 1 addition & 0 deletions packages/foundry/lib/account-abstraction
Submodule account-abstraction added at abff2a
2 changes: 1 addition & 1 deletion packages/foundry/lib/openzeppelin-contracts
1 change: 1 addition & 0 deletions packages/foundry/remappings.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts
@account-abstraction/=lib/account-abstraction/
Loading

0 comments on commit 38e1b21

Please sign in to comment.