Skip to content

Commit

Permalink
Zksync Account Abstraction:
Browse files Browse the repository at this point in the history
  • Loading branch information
cgrade committed Nov 9, 2024
1 parent 607c295 commit b0687f6
Show file tree
Hide file tree
Showing 111 changed files with 690 additions and 10 deletions.
Binary file added .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts
[submodule "lib/foundry-era-contracts"]
path = lib/foundry-era-contracts
url = https://github.com/cyfrin/foundry-era-contracts
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"figma.autocompleteBlocks": true,
"cursor.chat.showSuggestedFiles": true,
"cursor.cpp.enablePartialAccepts": true,
"debug.console.acceptSuggestionOnEnter": "on",
"editor.suggest.preview": true,
"editor.suggest.shareSuggestSelections": true
}
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Account Abstraction

This repository contains a minimal implementation of an account abstraction system using the Account Abstraction standard.

## Overview

1. Create a basic AA on Ethereum
2. Create a basic AA on Zkysync
3. Deploy and send a userOp/transaction through them
7 changes: 7 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,12 @@
src = "src"
out = "out"
libs = ["lib"]
remappings = [
"account-abstraction=lib/account-abstraction/contracts",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
# "@openzeppelin/contracts=lib/openzeppelin-contracts/contracts"

]
via-ir = true
is-system = true
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
2 changes: 1 addition & 1 deletion lib/account-abstraction
Submodule account-abstraction updated 48 files
+ audits/ERC-4337 Account Abstraction Incremental Audit Report Feb 20 2024.pdf
+0 −34 contracts/legacy/v06/IAccount06.sol
+0 −36 contracts/legacy/v06/IAggregator06.sol
+0 −204 contracts/legacy/v06/IEntryPoint06.sol
+0 −27 contracts/legacy/v06/INonceManager06.sol
+0 −51 contracts/legacy/v06/IPaymaster06.sol
+0 −104 contracts/legacy/v06/IStakeManager06.sol
+0 −30 contracts/legacy/v06/UserOperation06.sol
+1 −0 deployments/arbitrum/.chainId
+1,318 −0 deployments/arbitrum/EntryPoint.json
+68 −0 deployments/arbitrum/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+1 −0 deployments/gnosis/.chainId
+1,318 −0 deployments/gnosis/EntryPoint.json
+68 −0 deployments/gnosis/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+1 −0 deployments/goerli/.chainId
+1,318 −0 deployments/goerli/EntryPoint.json
+68 −0 deployments/goerli/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+1 −0 deployments/kovan/.chainId
+1,073 −0 deployments/kovan/EntryPoint.json
+338 −0 deployments/kovan/SimpleWallet.json
+118 −0 deployments/kovan/TestCounter.json
+86 −0 deployments/kovan/solcInputs/9255faacf3ae4e81db1326413027bfa0.json
+1 −0 deployments/mainnet/.chainId
+1,318 −0 deployments/mainnet/EntryPoint.json
+107 −0 deployments/mainnet/SimpleAccountFactory.json
+329 −0 deployments/mainnet/solcInputs/02113a2ed1850c3774563305ee607f11.json
+68 −0 deployments/mainnet/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+59 −0 deployments/mainnet/solcInputs/cfbebdf1101dd2bc0f310cb0b7d62cb7.json
+1 −0 deployments/matic/.chainId
+1,318 −0 deployments/matic/EntryPoint.json
+68 −0 deployments/matic/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+59 −0 deployments/matic/solcInputs/cfbebdf1101dd2bc0f310cb0b7d62cb7.json
+1 −0 deployments/mumbai/.chainId
+1,318 −0 deployments/mumbai/EntryPoint.json
+68 −0 deployments/mumbai/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+1 −0 deployments/optimism/.chainId
+1,318 −0 deployments/optimism/EntryPoint.json
+68 −0 deployments/optimism/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+1 −0 deployments/sepolia/.chainId
+1,318 −0 deployments/sepolia/EntryPoint.json
+68 −0 deployments/sepolia/solcInputs/a4c52f0671aad8941c53d6ead2063803.json
+1,024 −0 erc/ERCS/erc-4337.md
+392 −0 erc/ERCS/erc-7562.md
+1 −0 erc/assets/erc-4337/bundle-seq-pm.svg
+1 −0 erc/assets/erc-4337/bundle-seq.svg
+ erc/assets/erc-4337/image1.png
+ erc/assets/erc-4337/image2.png
+0 −5 funding.json
1 change: 1 addition & 0 deletions lib/foundry-era-contracts
Submodule foundry-era-contracts added at 3f99de
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
25 changes: 25 additions & 0 deletions script/DeployMinimal.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {MinimalAccount} from "../src/ethereum/MinimalAccount.sol";
import {Script} from "forge-std/Script.sol";
import {HelperConfig} from "./HelperConfig.s.sol";

contract DeployMinimal is Script {
// MinimalAccount public minimalAccount;
function run() external {
// minimalAccount = new deployMinimalAccount();
}

function deployMinimalAccount() public returns (MinimalAccount, HelperConfig) {
HelperConfig helperConfig = new HelperConfig();
HelperConfig.NetworkConfig memory config = helperConfig.getConfig();

vm.startBroadcast(config.account);
MinimalAccount minimalAccount = new MinimalAccount(config.entryPoint);
minimalAccount.transferOwnership(config.account);
vm.stopBroadcast();
return (minimalAccount, helperConfig);
}
}
66 changes: 66 additions & 0 deletions script/HelperConfig.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Script, console2} from "forge-std/Script.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";

contract HelperConfig is Script {
error HelperConfig__InvalidChainId();

struct NetworkConfig {
address entryPoint;
address account;
}

uint256 constant ETH_SEPOLIA_CHAIN_ID = 11155111;
uint256 constant ZKSYNC_SEPOLIA_CHAIN_ID = 300;
uint256 constant LOCAL_ANVIL_CHAIN_ID = 31337;
address constant BURNER_WALLET = 0x70997970C59cfa742bb70896FB3BB598232BC248;
address constant FOUNDRY_DEFAULT_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38;
address constant ANVIL_DEFAULT_ACCOUNT = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;

NetworkConfig public localNetworkConfig;
mapping(uint256 chainId => NetworkConfig networkConfig) public networkConfigs;

constructor() {
networkConfigs[ETH_SEPOLIA_CHAIN_ID] = getEthSepoliaConfig();
}

function getConfig() public returns (NetworkConfig memory) {
return getConfigByChainId(block.chainid);
}

function getConfigByChainId(uint256 chainId) public returns (NetworkConfig memory) {
if (chainId == LOCAL_ANVIL_CHAIN_ID) {
return getOrCreateAnvilEthConfig();
} else if (networkConfigs[chainId].entryPoint != address(0)) {
return networkConfigs[chainId];
} else {
revert HelperConfig__InvalidChainId();
}
}

function getEthSepoliaConfig() public pure returns (NetworkConfig memory) {
return NetworkConfig({entryPoint: 0x5Ff137d4B0fdCd49dcA30c7Cf57E578D498a27c4, account: BURNER_WALLET});
}

function getZkSyncSepoliaConfig() public pure returns (NetworkConfig memory) {
return NetworkConfig({entryPoint: address(0), account: BURNER_WALLET});
}

function getOrCreateAnvilEthConfig() public returns (NetworkConfig memory) {
if (localNetworkConfig.entryPoint != address(0)) {
return localNetworkConfig;
}
// deploy a mock
EntryPoint entryPoint;
console2.log("Deploying a mock EntryPoint");
vm.startBroadcast(ANVIL_DEFAULT_ACCOUNT);
entryPoint = new EntryPoint();
vm.stopBroadcast();

localNetworkConfig = NetworkConfig({entryPoint: address(entryPoint), account: ANVIL_DEFAULT_ACCOUNT});
return localNetworkConfig;
}
}
72 changes: 72 additions & 0 deletions script/SendPackedUserOp.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Script} from "forge-std/Script.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
import {PackedUserOperation} from "lib/account-abstraction/contracts/interfaces/PackedUserOperation.sol";
import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {MessageHashUtils} from "lib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol";

contract SendPackedUserOp is Script {
using MessageHashUtils for bytes32;

function run() external {}

function generateSignedUserOps(
bytes memory functionData,
HelperConfig.NetworkConfig memory config,
address minimalAccount
) public view returns (PackedUserOperation memory) {
// Add validation for EntryPoint address
// require(config.entryPoint != address(0), "EntryPoint address cannot be zero");

//1. Generate the unsigned userOps
uint256 nonce = vm.getNonce(minimalAccount) - 1;
PackedUserOperation memory userOp = _generateUnsignedUserOps(functionData, address(minimalAccount), nonce);

// Get the userOp Hash
bytes32 userOpHash = IEntryPoint(config.entryPoint).getUserOpHash(userOp);
bytes32 digest = userOpHash.toEthSignedMessageHash();

//2. Sign the userOps
uint8 v;
bytes32 r;
bytes32 s;
uint256 ANVIL_DEFAULT_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
if (block.chainid == 31337) {
(v, r, s) = vm.sign(ANVIL_DEFAULT_KEY, digest);
} else {
(v, r, s) = vm.sign(config.account, digest);
}
userOp.signature = abi.encodePacked(r, s, v);

//3. Return the signed userOps
return userOp;
}

function _generateUnsignedUserOps(bytes memory functionData, address sender, uint256 nonce)
internal
pure
returns (PackedUserOperation memory)
{
// Set more reasonable gas limits
uint128 verificationGasLimit = 16777216;
uint128 callGasLimit = verificationGasLimit; // Add explicit call gas limit
uint128 maxPriorityFeePerGas = 256; // 0.1 gwei
uint128 maxFeePerGas = maxPriorityFeePerGas;
bytes32 gasLimits = bytes32((uint256(verificationGasLimit) << 128) | uint256(callGasLimit));

return PackedUserOperation({
sender: sender,
nonce: nonce,
initCode: hex"",
callData: functionData,
accountGasLimits: gasLimits, // Combined verification and call gas limits
preVerificationGas: verificationGasLimit, // Standard transaction base cost
gasFees: bytes32(uint256(maxPriorityFeePerGas) << 128 | uint256(maxFeePerGas)),
paymasterAndData: hex"",
signature: hex""
});
}
}
Empty file removed script/SendPackedUserOps.s.sol
Empty file.
17 changes: 9 additions & 8 deletions src/ethereum/MinimalAccount.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IAccount} from "account-abstraction/interfaces/IAccount.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {Ownable} from "openzeppelin/access/Ownable.sol";
import {MessageHashUtils} from "openzeppelin/utils/cryptography/MessageHashUtils.sol";
import {ECDSA} from "openzeppelin/utils/cryptography/ECDSA.sol";
import {SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED} from "account-abstraction/core/Helpers.sol";
import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol";
import {IAccount} from "lib/account-abstraction/contracts/interfaces/IAccount.sol";
import {PackedUserOperation} from "lib/account-abstraction/contracts/interfaces/PackedUserOperation.sol";
import {Ownable} from "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
import {MessageHashUtils} from "lib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol";
import {ECDSA} from "lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import {SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED} from "lib/account-abstraction/contracts/core/Helpers.sol";
import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol";

contract MinimalAccount is IAccount, Ownable {
/*//////////////////////////////////////////////////////////////
Expand All @@ -16,6 +16,7 @@ contract MinimalAccount is IAccount, Ownable {
error MinimalAccount__NotFromEntryPoint(address);
error MinimalAccount__NotFromEntryPointOrOwner(address);
error MinimalAccount__ExecutionFailed(bytes);
error MinimalAccount__NotOwner();

/*//////////////////////////////////////////////////////////////
STATE VARIABLES
Expand Down Expand Up @@ -96,7 +97,7 @@ contract MinimalAccount is IAccount, Ownable {
GETTERS
//////////////////////////////////////////////////////////////*/

function entryPoint() external view returns (IEntryPoint) {
function getEntryPoint() external view returns (IEntryPoint) {
return _entryPoint;
}
}
Loading

0 comments on commit b0687f6

Please sign in to comment.