From 63ec7224ad8345f1fbef81c1da8a502225f4e4b7 Mon Sep 17 00:00:00 2001 From: phipsae Date: Sun, 7 Jul 2024 12:51:23 +0200 Subject: [PATCH 01/17] implement - aa on local foundry chain w/o bundler --- packages/foundry/contracts/Account123.sol | 35 - .../foundry/contracts/Account123Factory.sol | 36 - packages/foundry/contracts/AccountSimple.sol | 25 +- packages/foundry/contracts/Paymaster.sol | 3 +- packages/foundry/contracts/Test.sol | 3 +- packages/foundry/script/Deploy.s.sol | 61 +- packages/nextjs/app/localChain/page.tsx | 177 +- packages/nextjs/app/page.tsx | 209 --- packages/nextjs/app/test.tsx | 23 - .../nextjs/contracts/deployedContracts.ts | 1625 ++++++++++++++++- 10 files changed, 1801 insertions(+), 396 deletions(-) delete mode 100644 packages/foundry/contracts/Account123.sol delete mode 100644 packages/foundry/contracts/Account123Factory.sol delete mode 100644 packages/nextjs/app/test.tsx diff --git a/packages/foundry/contracts/Account123.sol b/packages/foundry/contracts/Account123.sol deleted file mode 100644 index 6d55c7d..0000000 --- a/packages/foundry/contracts/Account123.sol +++ /dev/null @@ -1,35 +0,0 @@ -// 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 "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; -import "forge-std/console.sol"; - -contract Account123 is IAccount { - - uint public count; - address public owner; - - constructor(address _owner) { - owner = _owner; - } - - function validateUserOp(UserOperation calldata userOp, bytes32 , uint256 ) view external returns (uint256 validationData) { - address recovered = ECDSA.recover(MessageHashUtils.toEthSignedMessageHash(keccak256("hello")), userOp.signature); - console.logString("validateUserOp from Account123"); - console.logAddress(recovered); - console.logAddress(owner); - - // return owner == recovered ? 1 : 0; - return 0; - } - - function execute() external { - count++; - } - - - -} \ No newline at end of file diff --git a/packages/foundry/contracts/Account123Factory.sol b/packages/foundry/contracts/Account123Factory.sol deleted file mode 100644 index 9c1f7dc..0000000 --- a/packages/foundry/contracts/Account123Factory.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0 <0.9.0; - -import "./Account123.sol"; - - -contract Account123Factory { - function createAccount(address owner) external returns (address) { - Account123 account = new Account123(owner); - return address(account); - } - - - // function createAccount(address owner) external returns (address) { - // bytes32 salt = bytes32(uint256(uint160(owner))); - // bytes memory creationCode = type(Account).creationCode; - // bytes memory bytecode = abi.encodePacked(creationCode, abi.encode(owner)); - - // address addr = Create2.computeAddress(salt, keccak256(bytecode)); - // uint256 codeSize = addr.code.length; - // if (codeSize > 0) { - // return addr; - // } - - // return deploy(salt, bytecode); - // } - - // function deploy(bytes32 salt, bytes memory bytecode) internal returns (address addr) { - // require(bytecode.length != 0, "Create2: bytecode length is zero"); - // /// @solidity memory-safe-assembly - // assembly { - // addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt) - // } - // require(addr != address(0), "Create2: Failed on deploy"); - // } -} \ No newline at end of file diff --git a/packages/foundry/contracts/AccountSimple.sol b/packages/foundry/contracts/AccountSimple.sol index ca0e5e0..6f8ec59 100644 --- a/packages/foundry/contracts/AccountSimple.sol +++ b/packages/foundry/contracts/AccountSimple.sol @@ -2,7 +2,7 @@ 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 "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@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"; @@ -13,21 +13,24 @@ 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) { + address recovered = ECDSA.recover(MessageHashUtils.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; } function execute() external { count++; } -} - -// contract AccountSimpleFactory { -// function createAccount(address owner) external returns (address) { -// AccountSimple acc = new AccountSimple(owner); -// return address(acc); -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/packages/foundry/contracts/Paymaster.sol b/packages/foundry/contracts/Paymaster.sol index 2cc2a05..27e650b 100644 --- a/packages/foundry/contracts/Paymaster.sol +++ b/packages/foundry/contracts/Paymaster.sol @@ -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 "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@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; } diff --git a/packages/foundry/contracts/Test.sol b/packages/foundry/contracts/Test.sol index dcb23fe..d7e567a 100644 --- a/packages/foundry/contracts/Test.sol +++ b/packages/foundry/contracts/Test.sol @@ -8,7 +8,8 @@ import "forge-std/console.sol"; /// to test if ECDSA.recover works contract Test { constructor(bytes memory sig) { - /// first we hash the message hello, then we use the EthSignedMessageHash to sign it + /// 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(MessageHashUtils.toEthSignedMessageHash(keccak256("hello")), sig); console.logAddress(recovered); } diff --git a/packages/foundry/script/Deploy.s.sol b/packages/foundry/script/Deploy.s.sol index 2484076..0bdc743 100644 --- a/packages/foundry/script/Deploy.s.sol +++ b/packages/foundry/script/Deploy.s.sol @@ -1,15 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; -import "../contracts/Account123.sol"; -import "../contracts/Account123Factory.sol"; import "../contracts/AccountSimple.sol"; import "../contracts/AccountSimpleFactory.sol"; import "../contracts/Paymaster.sol"; import "../contracts/Test.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; -import "/Users/philip/Programming/Ethereum/AABuild/PWAA/node_modules/@account-abstraction/contracts/core/EntryPoint.sol"; +import "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/EntryPoint.sol"; import "./DeployHelpers.s.sol"; contract DeployScript is ScaffoldETHDeploy { @@ -26,53 +24,56 @@ contract DeployScript is ScaffoldETHDeploy { ); } - // Start broadcasting transactions + /// Start broadcasting transactions vm.startBroadcast(deployerPrivateKey); /// Deploy EntryPoint contract -- 0x5FbDB2315678afecb367f032d93F642f64180aa3 - // EntryPoint entryPoint = new EntryPoint(); + EntryPoint entryPoint = new EntryPoint(); - // /// Deploy Account - // AccountSimple yourAccount = new AccountSimple(vm.addr(deployerPrivateKey)); + /// Deploy Account + AccountSimple yourAccount = new AccountSimple(vm.addr(deployerPrivateKey)); - // /// Deploy Simple Account Factory --- 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 - // AccountSimpleFactory accountSimpleFactory= new AccountSimpleFactory(); - // // Account123Factory accountFactory = new Account123Factory(); + /// Deploy Simple Account Factory --- 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + AccountSimpleFactory accountSimpleFactory= new AccountSimpleFactory(); + - // /// Deploy Paymaster - // Paymaster paymaster = new Paymaster(); + /// Deploy Paymaster + Paymaster paymaster = new Paymaster(); - // /// Log deployment addresses - // console.logString( - // string.concat( - // "EntryPoint deployed at: ", vm.toString(address(entryPoint)), - // // "YourAccount deployed at: ", vm.toString(address(yourAccount)), - // // "AccountFactory deployed at: ", vm.toString(address(accountFactory)), - // "AccountSimpleFactory deployed at: ", vm.toString(address(accountSimpleFactory)), - // "Paymaster deployed at: ", vm.toString(address(paymaster)) - // ) - // ); + /// Log deployment addresses + console.logString( + string.concat( + "EntryPoint deployed at: ", vm.toString(address(entryPoint)), "\n", + "AccountSimpleFactory deployed at: ", vm.toString(address(accountSimpleFactory)), "\n", + "Paymaster deployed at: ", vm.toString(address(paymaster)) + ) + ); - /// Test.sol contract test --> to verify signing works correctly - bytes32 messageHash = keccak256(abi.encodePacked("hello")); - bytes32 ethSignedMessageHash = MessageHashUtils.toEthSignedMessageHash(messageHash); + // /// Test.sol contract test --> to verify signing works correctly + // bytes32 messageHash = keccak256(abi.encodePacked("hello")); + // bytes32 ethSignedMessageHash = MessageHashUtils.toEthSignedMessageHash(messageHash); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(deployerPrivateKey, ethSignedMessageHash); - bytes memory signature = abi.encodePacked(r, s, v); + // (uint8 v, bytes32 r, bytes32 s) = vm.sign(deployerPrivateKey, ethSignedMessageHash); + // bytes memory signature = abi.encodePacked(r, s, v); - Test test = new Test(signature); + // // string memory signatureStr = _toHexString(signature); + // // console.logString(string.concat("Signature: ", signatureStr)); - console.logString(string.concat("Signer Address: ", vm.toString(vm.addr(deployerPrivateKey)))); + // Test test = new Test(signature); + // console.logString(string.concat("Signer Address: ", vm.toString(vm.addr(deployerPrivateKey)))); + /// Signature js 0xf16ea9a3478698f695fd1401bfe27e9e4a7e8e3da94aa72b021125e31fa899cc573c48ea3fe1d4ab61a9db10c19032026e3ed2dbccba5a178235ac27f94504311c // Stop broadcasting transactions vm.stopBroadcast(); // Export contract deployments exportDeployments(); + + } function test() public {} -} +} \ No newline at end of file diff --git a/packages/nextjs/app/localChain/page.tsx b/packages/nextjs/app/localChain/page.tsx index 9ef8467..47a6505 100644 --- a/packages/nextjs/app/localChain/page.tsx +++ b/packages/nextjs/app/localChain/page.tsx @@ -1,24 +1,37 @@ "use client"; +import { useState } from "react"; import { ethers } from "ethers"; import type { NextPage } from "next"; -import { createPublicClient, encodeFunctionData, getContract, getContractAddress, http, parseUnits } from "viem"; +import { + createPublicClient, + createWalletClient, + encodeFunctionData, + getContractAddress, + http, + parseUnits, + toBytes, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; import { foundry } from "viem/chains"; -import { useAccount, useWriteContract } from "wagmi"; import { useScaffoldContract, useScaffoldReadContract, useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; -import { getMetadata } from "~~/utils/scaffold-eth/getMetadata"; -const EP_ADDRESS = "0x4826533B4897376654Bb4d4AD88B7faFD0C98528"; -const FACTORY_ADDRESS = "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF"; +// const EP_ADDRESS = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"; const FACTORY_NONCE = 1; -const SMART_ACCOUNT = "0xD4eF5bFBe5925B905BD3EC0921bFe28b04ac61aE"; -const PM_ADDRESS = "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf"; +const FACTORY_ADDRESS = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; +const PM_ADDRESS = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; const LocalChain: NextPage = () => { const { data: entryPoint } = useScaffoldContract({ contractName: "EntryPoint", }); + const account1 = privateKeyToAccount(process.env.NEXT_PUBLIC_FOUNDRY_PRIVATE_KEY_1 as `0x${string}`); + // const account2 = privateKeyToAccount(process.env.NEXT_PUBLIC_FOUNDRY_PRIVATE_KEY_2 as `0x${string}`); + + const [signature, setSignature] = useState(""); + const [userOp, setUserOp] = useState({} as any); + const { writeContractAsync: handleOpsAsync } = useScaffoldWriteContract("EntryPoint"); const { writeContractAsync: depositToAsync } = useScaffoldWriteContract("EntryPoint"); @@ -30,16 +43,23 @@ const LocalChain: NextPage = () => { contractName: "AccountSimpleFactory", }); - const signer = useAccount(); + // const { data: paymaster } = useScaffoldContract({ + // contractName: "Paymaster", + // }); + + // FACTORY_ADDRESS = accountSimpleFactory?.address as `0x${string}`; + // PM_ADDRESS = paymaster?.address as `0x${string}`; const sender = getContractAddress({ // bytecode: "0x6394198df16000526103ff60206004601c335afa6040516060f3", - from: FACTORY_ADDRESS, + from: FACTORY_ADDRESS as `0x${string}`, nonce: BigInt(FACTORY_NONCE), opcode: "CREATE", // salt: "0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331", }); + const SMART_ACCOUNT = sender; + const { data: nonceFromEP } = useScaffoldReadContract({ contractName: "EntryPoint", functionName: "getNonce", @@ -52,7 +72,7 @@ const LocalChain: NextPage = () => { createAccountEncoded = encodeFunctionData({ abi: accountSimpleFactory?.abi, functionName: "createAccount", - args: [signer.address as `0x${string}`], + args: [account1.address as `0x${string}`], }); } @@ -65,34 +85,84 @@ const LocalChain: NextPage = () => { }); } - let userOp = {} as any; - - // const createUserOp = async () => { - if (executeEncoded && createAccountEncoded) { - /// if already SA created then use 0x - const initCode = "0x"; - // const initCode = accountSimpleFactory?.address + createAccountEncoded.slice(2); - - // console.log("InitCode", initCode); - - // const callData = "0x"; - const callData = executeEncoded; - - userOp = { - sender, // smart account address - nonce: nonceFromEP, // nonce from the entrypoint nonce manager - initCode, - callData, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 50_000, - maxFeePerGas: ethers.parseUnits("10", "gwei"), //parseUnits("10", 9), - maxPriorityFeePerGas: ethers.parseUnits("5", "gwei"), - paymasterAndData: PM_ADDRESS, - signature: "0x", - }; - } - // }; + /// create userOP without signature -- append sig later after signed UserOp + const createUserOp = async () => { + if (executeEncoded && createAccountEncoded) { + /// if already SA created then use 0x + const initCode = "0x"; + // const initCode = accountSimpleFactory?.address + createAccountEncoded.slice(2); + + // console.log("InitCode", initCode); + + // const callData = "0x"; + const callData = executeEncoded; + + const userOp = { + sender, // smart account address + nonce: nonceFromEP, // nonce from the entrypoint nonce manager + initCode, + callData, + callGasLimit: 500_000, + verificationGasLimit: 500_000, + preVerificationGas: 50_000, + maxFeePerGas: ethers.parseUnits("10", "gwei"), //parseUnits("10", 9), + maxPriorityFeePerGas: ethers.parseUnits("5", "gwei"), + paymasterAndData: PM_ADDRESS, + signature: "0x", + }; + console.log("User Op", userOp); + setUserOp(userOp); + } + }; + + const [userOpHash, setUserOpHash] = useState<`0x${string}`>(); + + const createUserOpHash = async () => { + console.log(userOp); + const userOpHash = await entryPoint?.read.getUserOpHash([userOp]); + console.log("User Op Hash", userOpHash); + setUserOpHash(userOpHash); + }; + + // /// Create Signature viem + const client = createWalletClient({ + account: account1, + chain: foundry, + // mode: "anvil", + transport: http(), + }); + + const createSignature = async () => { + console.log("Signer", account1); + // /// only for messages like "hi" which are not secure + // const message = toBytes(keccak256(toBytes(userOpHash as `0x${string}`))); + /// for userOpHash + const message = toBytes(userOpHash as `0x${string}`); + console.log("Message viem", message); + const signature = await client.signMessage({ + // account: account1, + // prettier-ignore + message: {raw: message}, + }); + console.log(signature); + setSignature(signature); + }; + + /// use ethers.js to create signature + const createSignatureEthers = async () => { + const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); + const wallet = new ethers.Wallet(process.env.NEXT_PUBLIC_FOUNDRY_PRIVATE_KEY_1 as `0x${string}`); + const signer = wallet.connect(provider); + console.log("Signer", signer); + // /// very important!!! thats why viem is not working with just hi as a message + // const message = ethers.getBytes(ethers.id(`hi`)); + /// to make it more secure + const message = ethers.getBytes(userOpHash as `0x${string}`); + console.log("Message ethers", message); + const signature = await signer.signMessage(message); + console.log("Signature", signature); + setSignature(signature); + }; /// read count from account const publicClient = createPublicClient({ @@ -103,7 +173,7 @@ const LocalChain: NextPage = () => { const getCount = async () => { if (accountSimple) { const data = await publicClient.readContract({ - address: SMART_ACCOUNT, + address: SMART_ACCOUNT as `0x${string}`, abi: accountSimple?.abi, functionName: "count", }); @@ -119,9 +189,10 @@ const LocalChain: NextPage = () => { + + + + ); diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index aef049f..1cbf38e 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -1,144 +1,10 @@ "use client"; -import { useEffect, useState } from "react"; -import { Test } from "./test"; -import { ethers } from "ethers"; import type { NextPage } from "next"; -import { createPublicClient, encodeFunctionData, getContract, getContractAddress, http, parseUnits } from "viem"; -import { foundry } from "viem/chains"; -import { useAccount, useWriteContract } from "wagmi"; -import { Address } from "~~/components/scaffold-eth"; -import { useScaffoldContract, useScaffoldReadContract, useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; -const SMART_ACCOUNT = "0xE451980132E65465d0a498c53f0b5227326Dd73F"; - -const FACTORY_NONCE = 1; // to deploy a new smart account change nonce to 2, etc. -const FACTORY_ADDRESS = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; const Home: NextPage = () => { - const { address: connectedAddress } = useAccount(); - // const EP_ADDRESS = "0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1"; - // const ACCOUNT_ADDRESS = "0x0B306BF915C4d645ff596e518fAf3F9669b97016"; - // const [txCount, setTxCount] = useState(); - const [userOp, setUserOp] = useState({} as any); - const [createAccountEncoded, setCreateAccountEncoded] = useState(""); - const [executeEncoded, setExecuteEncoded] = useState(""); - // const [testSender, setTestSender] = useState(); - - const { data: entryPoint } = useScaffoldContract({ - contractName: "EntryPoint", - }); - - const { data: accountFactory } = useScaffoldContract({ - contractName: "Account123Factory", - }); - - const { data: account } = useScaffoldContract({ - contractName: "Account123", - }); - - const { data: payMaster } = useScaffoldContract({ - contractName: "Paymaster", - }); - - const publicClient = createPublicClient({ - chain: foundry, - transport: http(), - }); - - /// Get address of smart account that gets created - const getAddressAA = async () => { - const sender = ethers.getCreateAddress({ - from: FACTORY_ADDRESS, - nonce: FACTORY_NONCE, - }); - console.log("Sender from getAddressAA", sender); - // setTestSender(sender); - }; - // CREATE: hash(deployer + nonce) - // CREATE2: hash(0xff + deployer + salt + bytecode) - const sender = getContractAddress({ - // bytecode: "0x6394198df16000526103ff60206004601c335afa6040516060f3", - from: FACTORY_ADDRESS, - nonce: BigInt(FACTORY_NONCE), - opcode: "CREATE", - // salt: "0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331", - }); - - const getCountFromSmartAccount = async () => { - if (account) { - const contract = getContract({ - address: SMART_ACCOUNT, - abi: account?.abi, - // 1a. Insert a single client - client: publicClient, - }); - const result = await contract.read.count(); - console.log(result); - } - }; - - const signer = useAccount(); - - const { writeContractAsync: handleOpsAsync } = useScaffoldWriteContract("EntryPoint"); - const { writeContractAsync: depositToAsync, data: hash } = useScaffoldWriteContract("EntryPoint"); - - useEffect(() => { - if (accountFactory) { - const createAccountEncoded = encodeFunctionData({ - abi: accountFactory?.abi, - functionName: "createAccount", - args: [signer.address], - }); - setCreateAccountEncoded(createAccountEncoded); - } - - if (account) { - const executeEncoded = encodeFunctionData({ - abi: account?.abi, - functionName: "execute", - }); - - setExecuteEncoded(executeEncoded); - } - }, [accountFactory, account, signer]); - - const { data: nonceAccountFactory } = useScaffoldReadContract({ - contractName: "EntryPoint", - functionName: "getNonce", - args: [sender, BigInt(0)], - }); - - const createUserOp = async () => { - if (executeEncoded && createAccountEncoded) { - // const initCode = "0x"; - const initCode = accountFactory?.address + createAccountEncoded.slice(2); - - console.log("InitCode", initCode); - - // const callData = "0x"; - const callData = executeEncoded; - - const userOp = { - sender, // smart account address - nonce: nonceAccountFactory, - initCode, - callData, - callGasLimit: 500_000, - verificationGasLimit: 500_000, - preVerificationGas: 50_000, - maxFeePerGas: ethers.parseUnits("10", "gwei"), //parseUnits("10", 9), - maxPriorityFeePerGas: ethers.parseUnits("5", "gwei"), - paymasterAndData: payMaster?.address, - signature: "0x", - }; - setUserOp(userOp); - console.log(userOp); - } - }; - return ( <> -

@@ -147,83 +13,8 @@ const Home: NextPage = () => {

Connected Address:

-
- - - - - - - - - {hash &&
Transaction Hash: {hash}
}
); diff --git a/packages/nextjs/app/test.tsx b/packages/nextjs/app/test.tsx deleted file mode 100644 index 2d9f0c1..0000000 --- a/packages/nextjs/app/test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { createTestClient, http } from "viem"; -import { foundry } from "viem/chains"; -import { useScaffoldContract } from "~~/hooks/scaffold-eth"; - -export const Test = () => { - const client = createTestClient({ - chain: foundry, - mode: "anvil", - transport: http(), - }); - - const { data: test } = useScaffoldContract({ - contractName: "Test", - }); - - return ( - <> -
-

HUHU

-
- - ); -}; diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 085199d..c5e4a83 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -6,20 +6,1487 @@ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; const deployedContracts = { 31337: { - Test: { - address: "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + EntryPoint: { + address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", abi: [ { - type: "constructor", + type: "receive", + stateMutability: "payable", + }, + { + type: "function", + name: "SIG_VALIDATION_FAILED", + inputs: [], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "_validateSenderAndPaymaster", + inputs: [ + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "view", + }, + { + type: "function", + name: "addStake", + inputs: [ + { + name: "unstakeDelaySec", + type: "uint32", + internalType: "uint32", + }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "balanceOf", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "depositTo", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "deposits", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "deposit", + type: "uint112", + internalType: "uint112", + }, + { + name: "staked", + type: "bool", + internalType: "bool", + }, + { + name: "stake", + type: "uint112", + internalType: "uint112", + }, + { + name: "unstakeDelaySec", + type: "uint32", + internalType: "uint32", + }, + { + name: "withdrawTime", + type: "uint48", + internalType: "uint48", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getDepositInfo", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "info", + type: "tuple", + internalType: "struct IStakeManager.DepositInfo", + components: [ + { + name: "deposit", + type: "uint112", + internalType: "uint112", + }, + { + name: "staked", + type: "bool", + internalType: "bool", + }, + { + name: "stake", + type: "uint112", + internalType: "uint112", + }, + { + name: "unstakeDelaySec", + type: "uint32", + internalType: "uint32", + }, + { + name: "withdrawTime", + type: "uint48", + internalType: "uint48", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getNonce", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "key", + type: "uint192", + internalType: "uint192", + }, + ], + outputs: [ + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getSenderAddress", + inputs: [ + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "getUserOpHash", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct UserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "handleAggregatedOps", + inputs: [ + { + name: "opsPerAggregator", + type: "tuple[]", + internalType: "struct IEntryPoint.UserOpsPerAggregator[]", + components: [ + { + name: "userOps", + type: "tuple[]", + internalType: "struct UserOperation[]", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "aggregator", + type: "address", + internalType: "contract IAggregator", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "beneficiary", + type: "address", + internalType: "address payable", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "handleOps", + inputs: [ + { + name: "ops", + type: "tuple[]", + internalType: "struct UserOperation[]", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "beneficiary", + type: "address", + internalType: "address payable", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "incrementNonce", + inputs: [ + { + name: "key", + type: "uint192", + internalType: "uint192", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "innerHandleOp", + inputs: [ + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "opInfo", + type: "tuple", + internalType: "struct EntryPoint.UserOpInfo", + components: [ + { + name: "mUserOp", + type: "tuple", + internalType: "struct EntryPoint.MemoryUserOp", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymaster", + type: "address", + internalType: "address", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "userOpHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "prefund", + type: "uint256", + internalType: "uint256", + }, + { + name: "contextOffset", + type: "uint256", + internalType: "uint256", + }, + { + name: "preOpGas", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "context", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "actualGasCost", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "nonceSequenceNumber", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "", + type: "uint192", + internalType: "uint192", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "simulateHandleOp", + inputs: [ + { + name: "op", + type: "tuple", + internalType: "struct UserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "targetCallData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "simulateValidation", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct UserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "unlockStake", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "withdrawStake", + inputs: [ + { + name: "withdrawAddress", + type: "address", + internalType: "address payable", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "withdrawTo", + inputs: [ + { + name: "withdrawAddress", + type: "address", + internalType: "address payable", + }, + { + name: "withdrawAmount", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "AccountDeployed", + inputs: [ + { + name: "userOpHash", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "sender", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "factory", + type: "address", + indexed: false, + internalType: "address", + }, + { + name: "paymaster", + type: "address", + indexed: false, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "BeforeExecution", + inputs: [], + anonymous: false, + }, + { + type: "event", + name: "Deposited", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "totalDeposit", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "SignatureAggregatorChanged", + inputs: [ + { + name: "aggregator", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "StakeLocked", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "totalStaked", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "StakeUnlocked", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "withdrawTime", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "StakeWithdrawn", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "withdrawAddress", + type: "address", + indexed: false, + internalType: "address", + }, + { + name: "amount", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "UserOperationEvent", + inputs: [ + { + name: "userOpHash", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "sender", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "paymaster", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "success", + type: "bool", + indexed: false, + internalType: "bool", + }, + { + name: "actualGasCost", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "actualGasUsed", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "UserOperationRevertReason", + inputs: [ + { + name: "userOpHash", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "sender", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "revertReason", + type: "bytes", + indexed: false, + internalType: "bytes", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Withdrawn", + inputs: [ + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "withdrawAddress", + type: "address", + indexed: false, + internalType: "address", + }, + { + name: "amount", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ExecutionResult", inputs: [ { - name: "sig", + name: "preOpGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paid", + type: "uint256", + internalType: "uint256", + }, + { + name: "validAfter", + type: "uint48", + internalType: "uint48", + }, + { + name: "validUntil", + type: "uint48", + internalType: "uint48", + }, + { + name: "targetSuccess", + type: "bool", + internalType: "bool", + }, + { + name: "targetResult", type: "bytes", internalType: "bytes", }, ], + }, + { + type: "error", + name: "FailedOp", + inputs: [ + { + name: "opIndex", + type: "uint256", + internalType: "uint256", + }, + { + name: "reason", + type: "string", + internalType: "string", + }, + ], + }, + { + type: "error", + name: "SenderAddressResult", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "SignatureValidationFailed", + inputs: [ + { + name: "aggregator", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ValidationResult", + inputs: [ + { + name: "returnInfo", + type: "tuple", + internalType: "struct IEntryPoint.ReturnInfo", + components: [ + { + name: "preOpGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "prefund", + type: "uint256", + internalType: "uint256", + }, + { + name: "sigFailed", + type: "bool", + internalType: "bool", + }, + { + name: "validAfter", + type: "uint48", + internalType: "uint48", + }, + { + name: "validUntil", + type: "uint48", + internalType: "uint48", + }, + { + name: "paymasterContext", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "senderInfo", + type: "tuple", + internalType: "struct IStakeManager.StakeInfo", + components: [ + { + name: "stake", + type: "uint256", + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "factoryInfo", + type: "tuple", + internalType: "struct IStakeManager.StakeInfo", + components: [ + { + name: "stake", + type: "uint256", + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "paymasterInfo", + type: "tuple", + internalType: "struct IStakeManager.StakeInfo", + components: [ + { + name: "stake", + type: "uint256", + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + internalType: "uint256", + }, + ], + }, + ], + }, + { + type: "error", + name: "ValidationResultWithAggregation", + inputs: [ + { + name: "returnInfo", + type: "tuple", + internalType: "struct IEntryPoint.ReturnInfo", + components: [ + { + name: "preOpGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "prefund", + type: "uint256", + internalType: "uint256", + }, + { + name: "sigFailed", + type: "bool", + internalType: "bool", + }, + { + name: "validAfter", + type: "uint48", + internalType: "uint48", + }, + { + name: "validUntil", + type: "uint48", + internalType: "uint48", + }, + { + name: "paymasterContext", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "senderInfo", + type: "tuple", + internalType: "struct IStakeManager.StakeInfo", + components: [ + { + name: "stake", + type: "uint256", + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "factoryInfo", + type: "tuple", + internalType: "struct IStakeManager.StakeInfo", + components: [ + { + name: "stake", + type: "uint256", + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "paymasterInfo", + type: "tuple", + internalType: "struct IStakeManager.StakeInfo", + components: [ + { + name: "stake", + type: "uint256", + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "aggregatorInfo", + type: "tuple", + internalType: "struct IEntryPoint.AggregatorStakeInfo", + components: [ + { + name: "aggregator", + type: "address", + internalType: "address", + }, + { + name: "stakeInfo", + type: "tuple", + internalType: "struct IStakeManager.StakeInfo", + components: [ + { + name: "stake", + type: "uint256", + internalType: "uint256", + }, + { + name: "unstakeDelaySec", + type: "uint256", + internalType: "uint256", + }, + ], + }, + ], + }, + ], + }, + ], + inheritedFunctions: { + addStake: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + balanceOf: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + depositTo: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + getDepositInfo: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + getNonce: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/NonceManager.sol", + getSenderAddress: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", + getUserOpHash: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", + handleAggregatedOps: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", + handleOps: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", + incrementNonce: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/NonceManager.sol", + simulateHandleOp: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", + simulateValidation: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", + unlockStake: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + withdrawStake: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + withdrawTo: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + deposits: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", + nonceSequenceNumber: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/NonceManager.sol", + }, + }, + AccountSimple: { + address: "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + abi: [ + { + type: "constructor", + inputs: [ + { + name: "_owner", + type: "address", + internalType: "address", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "count", + inputs: [], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "execute", + inputs: [], + outputs: [], stateMutability: "nonpayable", }, + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "validateUserOp", + inputs: [ + { + name: "userOp", + type: "tuple", + internalType: "struct UserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "userOpHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "validationData", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, { type: "error", name: "ECDSAInvalidSignature", @@ -50,6 +1517,156 @@ const deployedContracts = { ], inheritedFunctions: {}, }, + AccountSimpleFactory: { + address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0", + abi: [ + { + type: "function", + name: "createAccount", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "nonpayable", + }, + ], + inheritedFunctions: {}, + }, + Paymaster: { + address: "0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9", + abi: [ + { + type: "function", + name: "postOp", + inputs: [ + { + name: "mode", + type: "uint8", + internalType: "enum IPaymaster.PostOpMode", + }, + { + name: "context", + type: "bytes", + internalType: "bytes", + }, + { + name: "actualGasCost", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "validatePaymasterUserOp", + inputs: [ + { + name: "", + type: "tuple", + internalType: "struct UserOperation", + components: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "nonce", + type: "uint256", + internalType: "uint256", + }, + { + name: "initCode", + type: "bytes", + internalType: "bytes", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "callGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "verificationGasLimit", + type: "uint256", + internalType: "uint256", + }, + { + name: "preVerificationGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "maxPriorityFeePerGas", + type: "uint256", + internalType: "uint256", + }, + { + name: "paymasterAndData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + }, + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "context", + type: "bytes", + internalType: "bytes", + }, + { + name: "validationData", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "pure", + }, + ], + inheritedFunctions: { + postOp: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IPaymaster.sol", + validatePaymasterUserOp: + "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IPaymaster.sol", + }, + }, }, } as const; From 66f601dde1949d0176584f9980c6001cf1c8b836 Mon Sep 17 00:00:00 2001 From: phipsae Date: Thu, 11 Jul 2024 15:26:32 +0200 Subject: [PATCH 02/17] readme and clean code --- README.md | 25 +- ...leFactory.sol => AccountFactorySimple.sol} | 2 +- packages/foundry/contracts/AccountSimple.sol | 3 + packages/foundry/contracts/Test.sol | 16 - packages/foundry/contracts/YourContract.sol | 84 ----- packages/foundry/script/Deploy.s.sol | 7 +- packages/foundry/test/YourContract.t.sol | 28 -- packages/nextjs/app/localChain/page.tsx | 311 ++++++++++-------- .../nextjs/contracts/deployedContracts.ts | 10 +- 9 files changed, 205 insertions(+), 281 deletions(-) rename packages/foundry/contracts/{AccountSimpleFactory.sol => AccountFactorySimple.sol} (88%) delete mode 100644 packages/foundry/contracts/Test.sol delete mode 100644 packages/foundry/contracts/YourContract.sol delete mode 100644 packages/foundry/test/YourContract.t.sol diff --git a/README.md b/README.md index 6ff5330..121610a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ +# Guide to Account Abstraction + +This repo should support you in understanding Account Abstraction with ERC4337. Therefor we are using are Simple Account, Account Factory, Paymaster and the EntryPoint contract from the official implementation https://github.com/eth-infinitism/account-abstraction. Everything is build with SE2. + +In this example no bundler is used, therefor Create and Create2 is used. Additionally the Smart Account doesnt have any verification inside the execute function, which means everyone can call it, but for this example it doesnt matter. + +The repo tries to break all steps down, so that you get a better understanding whats going on. + +The first thing you should do is to look at the smart contracts AccountSimple and AccountFactorySimple itself. To understands whats going on there. + +AccountSimple inherits form the example Account of the https://github.com/eth-infinitism/account-abstraction. Therefore it needs to implement the validateUserOp function which is mandatory. It contains the whole validation logic, where you can decide which one you want to use (like Passkeys, Email, etc.) In this case we are going with the β€œnormal” ECDSA key pair validation, but the validation logic is embedded in the smart contract. This is the difference to EOAs where they only can use a standard cryptographic signature scheme (ECDSA for Ethereum) for transaction validation. + +Account Factory only has a createAccount function where the Simple Accounts can be created. This factory will be called by the entrypoint contract if a initcode is set. + +The paymaster has nothing special. Its just an empty contract which inherits two mandatory functions from the https://github.com/eth-infinitism/account-abstraction + +First we deposit funds for the paymaster in the EntryPoint contract, since the entrypoint has to pay for the account creation and tx execution, it needs the respective funds. As a second step we create the userOp, where we hand over the initCode, if the smart account wasnt created yet and the calldata. Addtionally the smart contract address (here called sender), the nonce from the entrypoint (replay protection), the paymaster address and a couple of gas parameter are added. + +After that we hash the userOp and sign it. Then we append the signature to the userOp and finally we call handleOps in Entrypoint contract, where it then executes the transaction. + +To doublecheck if we increased the count of our smart contract and check to counter (step 6). + # πŸ— Scaffold-ETH 2

@@ -66,7 +88,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. @@ -77,4 +98,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. \ No newline at end of file +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. diff --git a/packages/foundry/contracts/AccountSimpleFactory.sol b/packages/foundry/contracts/AccountFactorySimple.sol similarity index 88% rename from packages/foundry/contracts/AccountSimpleFactory.sol rename to packages/foundry/contracts/AccountFactorySimple.sol index a996c1f..0b2b3b9 100644 --- a/packages/foundry/contracts/AccountSimpleFactory.sol +++ b/packages/foundry/contracts/AccountFactorySimple.sol @@ -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); diff --git a/packages/foundry/contracts/AccountSimple.sol b/packages/foundry/contracts/AccountSimple.sol index 6f8ec59..14c4a70 100644 --- a/packages/foundry/contracts/AccountSimple.sol +++ b/packages/foundry/contracts/AccountSimple.sol @@ -19,6 +19,8 @@ contract AccountSimple is IAccount { } 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(MessageHashUtils.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); @@ -30,6 +32,7 @@ contract AccountSimple is IAccount { // return 0; } + /// no verification in here, execute can be called form everywhere function execute() external { count++; } diff --git a/packages/foundry/contracts/Test.sol b/packages/foundry/contracts/Test.sol deleted file mode 100644 index d7e567a..0000000 --- a/packages/foundry/contracts/Test.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0 <0.9.0; - -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; -import "forge-std/console.sol"; - -/// to test if ECDSA.recover works -contract Test { - constructor(bytes memory sig) { - /// 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(MessageHashUtils.toEthSignedMessageHash(keccak256("hello")), sig); - console.logAddress(recovered); - } -} \ No newline at end of file diff --git a/packages/foundry/contracts/YourContract.sol b/packages/foundry/contracts/YourContract.sol deleted file mode 100644 index 631b8b1..0000000 --- a/packages/foundry/contracts/YourContract.sol +++ /dev/null @@ -1,84 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity >=0.8.0 <0.9.0; - -// Useful for debugging. Remove when deploying to a live network. -import "forge-std/console.sol"; - -// Use openzeppelin to inherit battle-tested implementations (ERC20, ERC721, etc) -// import "@openzeppelin/contracts/access/Ownable.sol"; - -/** - * A smart contract that allows changing a state variable of the contract and tracking the changes - * It also allows the owner to withdraw the Ether in the contract - * @author BuidlGuidl - */ -contract YourContract { - // State Variables - address public immutable owner; - string public greeting = "Building Unstoppable Apps!!!"; - bool public premium = false; - uint256 public totalCounter = 0; - mapping(address => uint256) public userGreetingCounter; - - // Events: a way to emit log statements from smart contract that can be listened to by external parties - event GreetingChange( - address indexed greetingSetter, - string newGreeting, - bool premium, - uint256 value - ); - - // Constructor: Called once on contract deployment - // Check packages/foundry/deploy/Deploy.s.sol - constructor(address _owner) { - owner = _owner; - } - - // Modifier: used to define a set of rules that must be met before or after a function is executed - // Check the withdraw() function - modifier isOwner() { - // msg.sender: predefined variable that represents address of the account that called the current function - require(msg.sender == owner, "Not the Owner"); - _; - } - - /** - * Function that allows anyone to change the state variable "greeting" of the contract and increase the counters - * - * @param _newGreeting (string memory) - new greeting to save on the contract - */ - function setGreeting(string memory _newGreeting) public payable { - // Print data to the anvil chain console. Remove when deploying to a live network. - - console.logString("Setting new greeting"); - console.logString(_newGreeting); - - greeting = _newGreeting; - totalCounter += 1; - userGreetingCounter[msg.sender] += 1; - - // msg.value: built-in global variable that represents the amount of ether sent with the transaction - if (msg.value > 0) { - premium = true; - } else { - premium = false; - } - - // emit: keyword used to trigger an event - emit GreetingChange(msg.sender, _newGreeting, msg.value > 0, msg.value); - } - - /** - * Function that allows the owner to withdraw all the Ether in the contract - * The function can only be called by the owner of the contract as defined by the isOwner modifier - */ - function withdraw() public isOwner { - (bool success,) = owner.call{value: address(this).balance}(""); - require(success, "Failed to send Ether"); - } - - /** - * Function that allows the contract to receive ETH - */ - receive() external payable {} -} diff --git a/packages/foundry/script/Deploy.s.sol b/packages/foundry/script/Deploy.s.sol index 0bdc743..4ed6419 100644 --- a/packages/foundry/script/Deploy.s.sol +++ b/packages/foundry/script/Deploy.s.sol @@ -2,9 +2,8 @@ pragma solidity ^0.8.9; import "../contracts/AccountSimple.sol"; -import "../contracts/AccountSimpleFactory.sol"; +import "../contracts/AccountFactorySimple.sol"; import "../contracts/Paymaster.sol"; -import "../contracts/Test.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/EntryPoint.sol"; @@ -35,7 +34,7 @@ contract DeployScript is ScaffoldETHDeploy { AccountSimple yourAccount = new AccountSimple(vm.addr(deployerPrivateKey)); /// Deploy Simple Account Factory --- 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 - AccountSimpleFactory accountSimpleFactory= new AccountSimpleFactory(); + AccountFactorySimple accountFactorySimple = new AccountFactorySimple(); /// Deploy Paymaster @@ -45,7 +44,7 @@ contract DeployScript is ScaffoldETHDeploy { console.logString( string.concat( "EntryPoint deployed at: ", vm.toString(address(entryPoint)), "\n", - "AccountSimpleFactory deployed at: ", vm.toString(address(accountSimpleFactory)), "\n", + "AccountSimpleFactory deployed at: ", vm.toString(address(accountFactorySimple)), "\n", "Paymaster deployed at: ", vm.toString(address(paymaster)) ) ); diff --git a/packages/foundry/test/YourContract.t.sol b/packages/foundry/test/YourContract.t.sol deleted file mode 100644 index 2bb348e..0000000 --- a/packages/foundry/test/YourContract.t.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import "../contracts/YourContract.sol"; - -contract YourContractTest is Test { - YourContract public yourContract; - - function setUp() public { - yourContract = new YourContract(vm.addr(1)); - } - - function testMessageOnDeployment() public view { - require( - keccak256(bytes(yourContract.greeting())) - == keccak256("Building Unstoppable Apps!!!") - ); - } - - function testSetNewMessage() public { - yourContract.setGreeting("Learn Scaffold-ETH 2! :)"); - require( - keccak256(bytes(yourContract.greeting())) - == keccak256("Learn Scaffold-ETH 2! :)") - ); - } -} diff --git a/packages/nextjs/app/localChain/page.tsx b/packages/nextjs/app/localChain/page.tsx index 47a6505..ef05b65 100644 --- a/packages/nextjs/app/localChain/page.tsx +++ b/packages/nextjs/app/localChain/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { ethers } from "ethers"; import type { NextPage } from "next"; import { @@ -14,68 +14,121 @@ import { } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { foundry } from "viem/chains"; +import { Address } from "~~/components/scaffold-eth"; import { useScaffoldContract, useScaffoldReadContract, useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; -// const EP_ADDRESS = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"; +/// change manually to create different smart accounts for EOA +/// (not how it should be done in production) - normally Create2 const FACTORY_NONCE = 1; -const FACTORY_ADDRESS = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"; -const PM_ADDRESS = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; const LocalChain: NextPage = () => { const { data: entryPoint } = useScaffoldContract({ contractName: "EntryPoint", }); + /// change between different accounts (EOAs) const account1 = privateKeyToAccount(process.env.NEXT_PUBLIC_FOUNDRY_PRIVATE_KEY_1 as `0x${string}`); // const account2 = privateKeyToAccount(process.env.NEXT_PUBLIC_FOUNDRY_PRIVATE_KEY_2 as `0x${string}`); - const [signature, setSignature] = useState(""); + const [accountFactoryAddress, setAccountFactoryAddress] = useState(); + const [paymasterAddress, setPaymasterAddress] = useState(); + const [created, setCreated] = useState(false); const [userOp, setUserOp] = useState({} as any); + const [userOpHash, setUserOpHash] = useState<`0x${string}`>(); - const { writeContractAsync: handleOpsAsync } = useScaffoldWriteContract("EntryPoint"); - const { writeContractAsync: depositToAsync } = useScaffoldWriteContract("EntryPoint"); + const { writeContractAsync: entryPointWriteAsync } = useScaffoldWriteContract("EntryPoint"); const { data: accountSimple } = useScaffoldContract({ contractName: "AccountSimple", }); - const { data: accountSimpleFactory } = useScaffoldContract({ - contractName: "AccountSimpleFactory", + const { data: accountFactorySimple } = useScaffoldContract({ + contractName: "AccountFactorySimple", + }); + + const { data: paymaster } = useScaffoldContract({ + contractName: "Paymaster", }); - // const { data: paymaster } = useScaffoldContract({ - // contractName: "Paymaster", - // }); + useEffect(() => { + setPaymasterAddress(paymaster?.address); + }, [paymaster]); - // FACTORY_ADDRESS = accountSimpleFactory?.address as `0x${string}`; - // PM_ADDRESS = paymaster?.address as `0x${string}`; + useEffect(() => { + setAccountFactoryAddress(accountFactorySimple?.address); + }, [accountFactorySimple]); + + /// Create clients with viem + const publicClient = createPublicClient({ + chain: foundry, + transport: http(), + }); - const sender = getContractAddress({ - // bytecode: "0x6394198df16000526103ff60206004601c335afa6040516060f3", - from: FACTORY_ADDRESS as `0x${string}`, - nonce: BigInt(FACTORY_NONCE), - opcode: "CREATE", - // salt: "0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331", + const walletClient = createWalletClient({ + account: account1, + chain: foundry, + transport: http(), }); - const SMART_ACCOUNT = sender; + /// determine smart account address (sender) upfront (counterfactual) with normal Create method + /// does not with bundler, therefore Create2 is necessacry because Create is a forbidden opcode + /// because the nonce can change from inception to execution + let sender = ""; + + if (accountFactoryAddress) { + sender = getContractAddress({ + from: accountFactoryAddress as `0x${string}`, + nonce: BigInt(FACTORY_NONCE), + opcode: "CREATE", + }); + } + + const smartAccountAddress = sender; + + useEffect(() => { + const code = getCode(smartAccountAddress as `0x${string}`); + console.log("Code", code); + // const checkCode = async () => { + // const code = await getCode(smartAccountAddress as `0x${string}`); + // if (code === "0x") { + // setCreated(false); + // } else { + // console.log("Code", code); + // // setCreated(true); + // } + // }; + + // checkCode(); + }); const { data: nonceFromEP } = useScaffoldReadContract({ contractName: "EntryPoint", functionName: "getNonce", - args: [sender, BigInt(0)], + args: [sender as `0x${string}`, BigInt(0)], }); + const getCode = async (_address: `0x${string}`) => { + if (accountSimple) { + const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); + const code = await provider.getCode(_address as `0x${string}`); + console.log("Code", code); + setCreated(code !== "0x"); + return code; + } + }; + + /// determine part for initCode for smart account creation let createAccountEncoded = ""; - if (accountSimpleFactory) { + if (accountFactorySimple) { createAccountEncoded = encodeFunctionData({ - abi: accountSimpleFactory?.abi, + abi: accountFactorySimple?.abi, functionName: "createAccount", args: [account1.address as `0x${string}`], }); } + /// determine call data for userOp, which function should be called of smart account let executeEncoded = ""; if (accountSimple) { @@ -85,16 +138,17 @@ const LocalChain: NextPage = () => { }); } - /// create userOP without signature -- append sig later after signed UserOp + /// create userOP without signature -- append sig after UserOp is signed const createUserOp = async () => { if (executeEncoded && createAccountEncoded) { - /// if already SA created then use 0x - const initCode = "0x"; - // const initCode = accountSimpleFactory?.address + createAccountEncoded.slice(2); - - // console.log("InitCode", initCode); + /// check if smart account already exists if not set initCode for userOp + const code = await getCode(smartAccountAddress as `0x${string}`); + let initCode = "0x"; + if (code === "0x") { + initCode = accountFactoryAddress + createAccountEncoded.slice(2); + } - // const callData = "0x"; + /// set Calldata for userOp const callData = executeEncoded; const userOp = { @@ -105,9 +159,9 @@ const LocalChain: NextPage = () => { callGasLimit: 500_000, verificationGasLimit: 500_000, preVerificationGas: 50_000, - maxFeePerGas: ethers.parseUnits("10", "gwei"), //parseUnits("10", 9), + maxFeePerGas: ethers.parseUnits("10", "gwei"), maxPriorityFeePerGas: ethers.parseUnits("5", "gwei"), - paymasterAndData: PM_ADDRESS, + paymasterAndData: paymasterAddress, signature: "0x", }; console.log("User Op", userOp); @@ -115,8 +169,7 @@ const LocalChain: NextPage = () => { } }; - const [userOpHash, setUserOpHash] = useState<`0x${string}`>(); - + /// hash userOp, requirement to create signature const createUserOpHash = async () => { console.log(userOp); const userOpHash = await entryPoint?.read.getUserOpHash([userOp]); @@ -124,56 +177,21 @@ const LocalChain: NextPage = () => { setUserOpHash(userOpHash); }; - // /// Create Signature viem - const client = createWalletClient({ - account: account1, - chain: foundry, - // mode: "anvil", - transport: http(), - }); - + /// sign userOp const createSignature = async () => { - console.log("Signer", account1); - // /// only for messages like "hi" which are not secure - // const message = toBytes(keccak256(toBytes(userOpHash as `0x${string}`))); - /// for userOpHash const message = toBytes(userOpHash as `0x${string}`); - console.log("Message viem", message); - const signature = await client.signMessage({ - // account: account1, - // prettier-ignore - message: {raw: message}, + const signature = await walletClient.signMessage({ + message: { raw: message }, }); - console.log(signature); - setSignature(signature); - }; - - /// use ethers.js to create signature - const createSignatureEthers = async () => { - const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); - const wallet = new ethers.Wallet(process.env.NEXT_PUBLIC_FOUNDRY_PRIVATE_KEY_1 as `0x${string}`); - const signer = wallet.connect(provider); - console.log("Signer", signer); - // /// very important!!! thats why viem is not working with just hi as a message - // const message = ethers.getBytes(ethers.id(`hi`)); - /// to make it more secure - const message = ethers.getBytes(userOpHash as `0x${string}`); - console.log("Message ethers", message); - const signature = await signer.signMessage(message); - console.log("Signature", signature); - setSignature(signature); + /// add signature to userOp + userOp.signature = signature; }; /// read count from account - const publicClient = createPublicClient({ - chain: foundry, - transport: http(), - }); - const getCount = async () => { if (accountSimple) { const data = await publicClient.readContract({ - address: SMART_ACCOUNT as `0x${string}`, + address: smartAccountAddress as `0x${string}`, abi: accountSimple?.abi, functionName: "count", }); @@ -184,72 +202,83 @@ const LocalChain: NextPage = () => { return ( <>
-

Local Chain

+

AA Tutorial

+
+ +
+
+
+
Signer Address:
+
+
+
+
Paymaster Address:
+
+
+
+
Paymaster Address:
+
+
+
+
SmartAcc. Address:
+
+
{created ? "βœ… (Created)" : "❌(Not Created)"}
+
+
+ {/* */} + + + + + +
- - - - - - - - + {/* {smartAccountAddress && getCount()} */} ); }; diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index c5e4a83..526a247 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; const deployedContracts = { 31337: { EntryPoint: { - address: "0x5fbdb2315678afecb367f032d93f642f64180aa3", + address: "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9", abi: [ { type: "receive", @@ -1355,7 +1355,7 @@ const deployedContracts = { }, }, AccountSimple: { - address: "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512", + address: "0x5fc8d32690cc91d4c39d9d3abcbd16989f875707", abi: [ { type: "constructor", @@ -1517,8 +1517,8 @@ const deployedContracts = { ], inheritedFunctions: {}, }, - AccountSimpleFactory: { - address: "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0", + AccountFactorySimple: { + address: "0x0165878a594ca255338adfa4d48449f69242eb8f", abi: [ { type: "function", @@ -1543,7 +1543,7 @@ const deployedContracts = { inheritedFunctions: {}, }, Paymaster: { - address: "0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9", + address: "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853", abi: [ { type: "function", From 419f4a5d75387e35a1a8cd694e037f8037f931e5 Mon Sep 17 00:00:00 2001 From: phipsae Date: Thu, 11 Jul 2024 15:58:17 +0200 Subject: [PATCH 03/17] Create test.md --- assets/test.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/test.md diff --git a/assets/test.md b/assets/test.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/assets/test.md @@ -0,0 +1 @@ + From 88492da99cf63de455a5719c83398ba6e1c1a785 Mon Sep 17 00:00:00 2001 From: phipsae Date: Thu, 11 Jul 2024 15:59:30 +0200 Subject: [PATCH 04/17] Delete assets directory --- assets/test.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 assets/test.md diff --git a/assets/test.md b/assets/test.md deleted file mode 100644 index 8b13789..0000000 --- a/assets/test.md +++ /dev/null @@ -1 +0,0 @@ - From 5311214d245dc3297491cade1b5dee800d53a273 Mon Sep 17 00:00:00 2001 From: phipsae Date: Thu, 11 Jul 2024 15:59:53 +0200 Subject: [PATCH 05/17] Create placeholder.md --- assets/placeholder.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/placeholder.md diff --git a/assets/placeholder.md b/assets/placeholder.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/assets/placeholder.md @@ -0,0 +1 @@ + From d73dff9a8eb5fec1eb3e33093a4d3bf3072b87ab Mon Sep 17 00:00:00 2001 From: phipsae Date: Thu, 11 Jul 2024 16:00:17 +0200 Subject: [PATCH 06/17] Add files via upload --- assets/overview.png | Bin 0 -> 123434 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/overview.png diff --git a/assets/overview.png b/assets/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..f33808b29f3cf6ad67a6a96c3793f1eb075f367b GIT binary patch literal 123434 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%0DS|{_gdqn=DX@ClUiC&*jus%MNHl&5p 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 Date: Thu, 11 Jul 2024 16:02:55 +0200 Subject: [PATCH 07/17] add picture --- README.md | 2 ++ packages/nextjs/app/localChain/page.tsx | 46 +++++++++++-------------- packages/nextjs/app/page.tsx | 8 ++--- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 121610a..a955cad 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ This repo should support you in understanding Account Abstraction with ERC4337. Therefor we are using are Simple Account, Account Factory, Paymaster and the EntryPoint contract from the official implementation https://github.com/eth-infinitism/account-abstraction. Everything is build with SE2. +![overview aa tutorial](https://github.com/phipsae/AA-SE2/blob/localChainAA/assets/overview.png) + In this example no bundler is used, therefor Create and Create2 is used. Additionally the Smart Account doesnt have any verification inside the execute function, which means everyone can call it, but for this example it doesnt matter. The repo tries to break all steps down, so that you get a better understanding whats going on. diff --git a/packages/nextjs/app/localChain/page.tsx b/packages/nextjs/app/localChain/page.tsx index ef05b65..c2c1759 100644 --- a/packages/nextjs/app/localChain/page.tsx +++ b/packages/nextjs/app/localChain/page.tsx @@ -35,6 +35,7 @@ const LocalChain: NextPage = () => { const [created, setCreated] = useState(false); const [userOp, setUserOp] = useState({} as any); const [userOpHash, setUserOpHash] = useState<`0x${string}`>(); + const [count, setCount] = useState(undefined); const { writeContractAsync: entryPointWriteAsync } = useScaffoldWriteContract("EntryPoint"); @@ -86,19 +87,7 @@ const LocalChain: NextPage = () => { const smartAccountAddress = sender; useEffect(() => { - const code = getCode(smartAccountAddress as `0x${string}`); - console.log("Code", code); - // const checkCode = async () => { - // const code = await getCode(smartAccountAddress as `0x${string}`); - // if (code === "0x") { - // setCreated(false); - // } else { - // console.log("Code", code); - // // setCreated(true); - // } - // }; - - // checkCode(); + getCode(smartAccountAddress as `0x${string}`); }); const { data: nonceFromEP } = useScaffoldReadContract({ @@ -108,12 +97,18 @@ const LocalChain: NextPage = () => { }); const getCode = async (_address: `0x${string}`) => { - if (accountSimple) { - const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); - const code = await provider.getCode(_address as `0x${string}`); - console.log("Code", code); - setCreated(code !== "0x"); - return code; + try { + if (accountSimple) { + const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); + if (!ethers.isAddress(_address)) { + throw new Error(`Invalid Ethereum address: ${_address}`); + } + const code = await provider.getCode(_address as `0x${string}`); + setCreated(code !== "0x"); + return code; + } + } catch (error) { + console.error("Error fetching contract code:", error); } }; @@ -196,16 +191,13 @@ const LocalChain: NextPage = () => { functionName: "count", }); console.log(data); + setCount(Number(data)); } }; return ( <> -

-

AA Tutorial

-
- -
+
Signer Address:
@@ -277,8 +269,12 @@ const LocalChain: NextPage = () => { + {count && ( +
+

Count: {count}

+
+ )}
- {/* {smartAccountAddress && getCount()} */} ); }; diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index 1cbf38e..5cce2cc 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -1,5 +1,6 @@ "use client"; +import LocalChain from "./localChain/page"; import type { NextPage } from "next"; const Home: NextPage = () => { @@ -9,11 +10,10 @@ const Home: NextPage = () => {

Welcome to - Scaffold-ETH 2 + Account Abstraction Tutorial + with Scaffold-ETH 2

-
-

Connected Address:

-
+
From 4ea145a3685eee97e6bb1bb2f79de64a6267a42e Mon Sep 17 00:00:00 2001 From: phipsae Date: Thu, 11 Jul 2024 16:09:47 +0200 Subject: [PATCH 08/17] oz error with commit --- packages/foundry/lib/openzeppelin-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/foundry/lib/openzeppelin-contracts b/packages/foundry/lib/openzeppelin-contracts index dbb6104..dcdfc74 160000 --- a/packages/foundry/lib/openzeppelin-contracts +++ b/packages/foundry/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 +Subproject commit dcdfc74194aa3a446e418372e697107316e32362 From bd56a5aa506e07a1dd7bab8e0cff0e86fa95d5fb Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 10:27:46 +0200 Subject: [PATCH 09/17] readme --- README.md | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a955cad..5240bcb 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,42 @@ # Guide to Account Abstraction -This repo should support you in understanding Account Abstraction with ERC4337. Therefor we are using are Simple Account, Account Factory, Paymaster and the EntryPoint contract from the official implementation https://github.com/eth-infinitism/account-abstraction. Everything is build with SE2. +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) -In this example no bundler is used, therefor Create and Create2 is used. Additionally the Smart Account doesnt have any verification inside the execute function, which means everyone can call it, but for this example it doesnt matter. +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. -The repo tries to break all steps down, so that you get a better understanding whats going on. +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. -The first thing you should do is to look at the smart contracts AccountSimple and AccountFactorySimple itself. To understands whats going on there. +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. -AccountSimple inherits form the example Account of the https://github.com/eth-infinitism/account-abstraction. Therefore it needs to implement the validateUserOp function which is mandatory. It contains the whole validation logic, where you can decide which one you want to use (like Passkeys, Email, etc.) In this case we are going with the β€œnormal” ECDSA key pair validation, but the validation logic is embedded in the smart contract. This is the difference to EOAs where they only can use a standard cryptographic signature scheme (ECDSA for Ethereum) for transaction validation. +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). -Account Factory only has a createAccount function where the Simple Accounts can be created. This factory will be called by the entrypoint contract if a initcode is set. +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. -The paymaster has nothing special. Its just an empty contract which inherits two mandatory functions from the https://github.com/eth-infinitism/account-abstraction +**##Contracts** -First we deposit funds for the paymaster in the EntryPoint contract, since the entrypoint has to pay for the account creation and tx execution, it needs the respective funds. As a second step we create the userOp, where we hand over the initCode, if the smart account wasnt created yet and the calldata. Addtionally the smart contract address (here called sender), the nonce from the entrypoint (replay protection), the paymaster address and a couple of gas parameter are added. +**###AccountSimple.sol** -After that we hash the userOp and sign it. Then we append the signature to the userOp and finally we call handleOps in Entrypoint contract, where it then executes the transaction. +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. -To doublecheck if we increased the count of our smart contract and check to counter (step 6). +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 From e1ba161508067786211e7759b521c5b972ddedf3 Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 10:29:21 +0200 Subject: [PATCH 10/17] readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5240bcb..a09241f 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,21 @@ The repository breaks down each step to help you gain a better understanding of 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** +## Contracts -**###AccountSimple.sol** +### 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** +### 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** +### 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. From ec45462b4186214702e47f67da0066a0f603a22c Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 10:31:37 +0200 Subject: [PATCH 11/17] readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a09241f..4dc7fb3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Guide to Account Abstraction +# πŸ‘©β€πŸ« 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. @@ -14,7 +14,7 @@ The repository breaks down each step to help you gain a better understanding of 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 +## πŸ‘©β€πŸ’» Contracts ### AccountSimple.sol @@ -34,7 +34,7 @@ The paymaster is a simple contract without any special features. It inherits two 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** +### 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. From e4e1a9870ce32eec844f23d950eccf2b023b56bd Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 10:57:05 +0200 Subject: [PATCH 12/17] forge install: account-abstraction v0.7.0 --- .gitmodules | 3 +++ packages/foundry/lib/account-abstraction | 1 + 2 files changed, 4 insertions(+) create mode 160000 packages/foundry/lib/account-abstraction diff --git a/.gitmodules b/.gitmodules index baaf2c0..0562439 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [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 diff --git a/packages/foundry/lib/account-abstraction b/packages/foundry/lib/account-abstraction new file mode 160000 index 0000000..7af70c8 --- /dev/null +++ b/packages/foundry/lib/account-abstraction @@ -0,0 +1 @@ +Subproject commit 7af70c8993a6f42973f520ae0752386a5032abe7 From c9cc0d6462c5111106fae53e581c858ce1f50785 Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 11:07:35 +0200 Subject: [PATCH 13/17] playing around with forge install lib --- .gitmodules | 3 --- packages/foundry/lib/account-abstraction | 1 - 2 files changed, 4 deletions(-) delete mode 160000 packages/foundry/lib/account-abstraction diff --git a/.gitmodules b/.gitmodules index 0562439..baaf2c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,3 @@ [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 diff --git a/packages/foundry/lib/account-abstraction b/packages/foundry/lib/account-abstraction deleted file mode 160000 index 7af70c8..0000000 --- a/packages/foundry/lib/account-abstraction +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7af70c8993a6f42973f520ae0752386a5032abe7 From c80a8e9c75ba40758d615fd0b0cb5673a54f9626 Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 11:07:42 +0200 Subject: [PATCH 14/17] forge install: account-abstraction v0.6.0 --- .gitmodules | 3 +++ packages/foundry/lib/account-abstraction | 1 + 2 files changed, 4 insertions(+) create mode 160000 packages/foundry/lib/account-abstraction diff --git a/.gitmodules b/.gitmodules index baaf2c0..0562439 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [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 diff --git a/packages/foundry/lib/account-abstraction b/packages/foundry/lib/account-abstraction new file mode 160000 index 0000000..abff2ac --- /dev/null +++ b/packages/foundry/lib/account-abstraction @@ -0,0 +1 @@ +Subproject commit abff2aca61a8f0934e533d0d352978055fddbd96 From 8446fc4948af0a070ae5c7f5cb9958096fa8ca2e Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 11:14:29 +0200 Subject: [PATCH 15/17] playing around with forge install lib --- .gitmodules | 3 --- packages/foundry/contracts/AccountSimple.sol | 3 +-- packages/foundry/contracts/Paymaster.sol | 2 +- packages/foundry/lib/openzeppelin-contracts | 1 - packages/foundry/remappings.txt | 1 + packages/foundry/script/Deploy.s.sol | 2 +- 6 files changed, 4 insertions(+), 8 deletions(-) delete mode 160000 packages/foundry/lib/openzeppelin-contracts diff --git a/.gitmodules b/.gitmodules index 0562439..a50c4c5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [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 diff --git a/packages/foundry/contracts/AccountSimple.sol b/packages/foundry/contracts/AccountSimple.sol index 14c4a70..cd6111f 100644 --- a/packages/foundry/contracts/AccountSimple.sol +++ b/packages/foundry/contracts/AccountSimple.sol @@ -1,8 +1,7 @@ // 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/AA-SE/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"; diff --git a/packages/foundry/contracts/Paymaster.sol b/packages/foundry/contracts/Paymaster.sol index 27e650b..d74e5fe 100644 --- a/packages/foundry/contracts/Paymaster.sol +++ b/packages/foundry/contracts/Paymaster.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0 <0.9.0; -import "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IPaymaster.sol"; +import "@account-abstraction/contracts/interfaces/IPaymaster.sol"; contract Paymaster is IPaymaster { diff --git a/packages/foundry/lib/openzeppelin-contracts b/packages/foundry/lib/openzeppelin-contracts deleted file mode 160000 index dcdfc74..0000000 --- a/packages/foundry/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dcdfc74194aa3a446e418372e697107316e32362 diff --git a/packages/foundry/remappings.txt b/packages/foundry/remappings.txt index df3cb81..fcd255a 100644 --- a/packages/foundry/remappings.txt +++ b/packages/foundry/remappings.txt @@ -1 +1,2 @@ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts +@account-abstraction/=lib/account-abstraction/ diff --git a/packages/foundry/script/Deploy.s.sol b/packages/foundry/script/Deploy.s.sol index 4ed6419..f0a3888 100644 --- a/packages/foundry/script/Deploy.s.sol +++ b/packages/foundry/script/Deploy.s.sol @@ -6,7 +6,7 @@ import "../contracts/AccountFactorySimple.sol"; import "../contracts/Paymaster.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; -import "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/EntryPoint.sol"; +import "@account-abstraction/contracts/core/EntryPoint.sol"; import "./DeployHelpers.s.sol"; contract DeployScript is ScaffoldETHDeploy { From 90b4a3b82e4ff0aa2ea439f7e9fa8cad134ea784 Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 11:14:36 +0200 Subject: [PATCH 16/17] forge install: openzeppelin-contracts v4.2.0 --- .gitmodules | 3 +++ packages/foundry/lib/openzeppelin-contracts | 1 + 2 files changed, 4 insertions(+) create mode 160000 packages/foundry/lib/openzeppelin-contracts diff --git a/.gitmodules b/.gitmodules index a50c4c5..7e841e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [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 diff --git a/packages/foundry/lib/openzeppelin-contracts b/packages/foundry/lib/openzeppelin-contracts new file mode 160000 index 0000000..9fbc1d7 --- /dev/null +++ b/packages/foundry/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 9fbc1d71c0ed4c68a0bc160c69df1f85e94d2d8e From 982d5bd7cba58fbf8ab10176f2e73a2e62ce0460 Mon Sep 17 00:00:00 2001 From: phipsae Date: Fri, 12 Jul 2024 11:21:52 +0200 Subject: [PATCH 17/17] add account-abstraction lib and version of oz changed --- packages/foundry/contracts/AccountSimple.sol | 3 +- packages/foundry/script/Deploy.s.sol | 3 +- .../nextjs/contracts/deployedContracts.ts | 93 +++++-------------- 3 files changed, 24 insertions(+), 75 deletions(-) diff --git a/packages/foundry/contracts/AccountSimple.sol b/packages/foundry/contracts/AccountSimple.sol index cd6111f..3679621 100644 --- a/packages/foundry/contracts/AccountSimple.sol +++ b/packages/foundry/contracts/AccountSimple.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.0 <0.9.0; 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 { @@ -20,7 +19,7 @@ contract AccountSimple is IAccount { 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(MessageHashUtils.toEthSignedMessageHash(userOpHash), userOp.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: "); diff --git a/packages/foundry/script/Deploy.s.sol b/packages/foundry/script/Deploy.s.sol index f0a3888..509a1b5 100644 --- a/packages/foundry/script/Deploy.s.sol +++ b/packages/foundry/script/Deploy.s.sol @@ -5,7 +5,6 @@ import "../contracts/AccountSimple.sol"; import "../contracts/AccountFactorySimple.sol"; import "../contracts/Paymaster.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import "@account-abstraction/contracts/core/EntryPoint.sol"; import "./DeployHelpers.s.sol"; @@ -51,7 +50,7 @@ contract DeployScript is ScaffoldETHDeploy { // /// Test.sol contract test --> to verify signing works correctly // bytes32 messageHash = keccak256(abi.encodePacked("hello")); - // bytes32 ethSignedMessageHash = MessageHashUtils.toEthSignedMessageHash(messageHash); + // bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(messageHash); // (uint8 v, bytes32 r, bytes32 s) = vm.sign(deployerPrivateKey, ethSignedMessageHash); // bytes memory signature = abi.encodePacked(r, s, v); diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 526a247..61a8069 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; const deployedContracts = { 31337: { EntryPoint: { - address: "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9", + address: "0x8a791620dd6260079bf849dc5567adc3f2fdc318", abi: [ { type: "receive", @@ -1318,44 +1318,27 @@ const deployedContracts = { }, ], inheritedFunctions: { - addStake: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - balanceOf: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - depositTo: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - getDepositInfo: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - getNonce: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/NonceManager.sol", - getSenderAddress: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", - getUserOpHash: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", - handleAggregatedOps: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", - handleOps: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", - incrementNonce: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/NonceManager.sol", - simulateHandleOp: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", - simulateValidation: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IEntryPoint.sol", - unlockStake: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - withdrawStake: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - withdrawTo: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - deposits: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/StakeManager.sol", - nonceSequenceNumber: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/core/NonceManager.sol", + addStake: "lib/account-abstraction/contracts/core/StakeManager.sol", + balanceOf: "lib/account-abstraction/contracts/core/StakeManager.sol", + depositTo: "lib/account-abstraction/contracts/core/StakeManager.sol", + getDepositInfo: "lib/account-abstraction/contracts/core/StakeManager.sol", + getNonce: "lib/account-abstraction/contracts/core/NonceManager.sol", + getSenderAddress: "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol", + getUserOpHash: "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol", + handleAggregatedOps: "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol", + handleOps: "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol", + incrementNonce: "lib/account-abstraction/contracts/core/NonceManager.sol", + simulateHandleOp: "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol", + simulateValidation: "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol", + unlockStake: "lib/account-abstraction/contracts/core/StakeManager.sol", + withdrawStake: "lib/account-abstraction/contracts/core/StakeManager.sol", + withdrawTo: "lib/account-abstraction/contracts/core/StakeManager.sol", + deposits: "lib/account-abstraction/contracts/core/StakeManager.sol", + nonceSequenceNumber: "lib/account-abstraction/contracts/core/NonceManager.sol", }, }, AccountSimple: { - address: "0x5fc8d32690cc91d4c39d9d3abcbd16989f875707", + address: "0x610178da211fef7d417bc0e6fed39f05609ad788", abi: [ { type: "constructor", @@ -1487,38 +1470,11 @@ const deployedContracts = { ], stateMutability: "view", }, - { - type: "error", - name: "ECDSAInvalidSignature", - inputs: [], - }, - { - type: "error", - name: "ECDSAInvalidSignatureLength", - inputs: [ - { - name: "length", - type: "uint256", - internalType: "uint256", - }, - ], - }, - { - type: "error", - name: "ECDSAInvalidSignatureS", - inputs: [ - { - name: "s", - type: "bytes32", - internalType: "bytes32", - }, - ], - }, ], inheritedFunctions: {}, }, AccountFactorySimple: { - address: "0x0165878a594ca255338adfa4d48449f69242eb8f", + address: "0xb7f8bc63bbcad18155201308c8f3540b07f84f5e", abi: [ { type: "function", @@ -1543,7 +1499,7 @@ const deployedContracts = { inheritedFunctions: {}, }, Paymaster: { - address: "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853", + address: "0xa51c1fc2f0d1a1b8494ed1fe312d7c3a78ed91c0", abi: [ { type: "function", @@ -1660,12 +1616,7 @@ const deployedContracts = { stateMutability: "pure", }, ], - inheritedFunctions: { - postOp: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IPaymaster.sol", - validatePaymasterUserOp: - "/Users/philip/Programming/Ethereum/AABuild/AA-SE/node_modules/@account-abstraction/contracts/interfaces/IPaymaster.sol", - }, + inheritedFunctions: {}, }, }, } as const;