22pragma solidity ^ 0.8.20 ;
33
44import {Test, console2} from "forge-std/Test.sol " ;
5- import {FLOpsPaymaster } from "../src/FLOpsPaymaster .sol " ;
5+ import {FlopsPaymaster } from "../src/FlopsPaymaster .sol " ;
66import {AtomicEntryPoint} from "../src/AtomicEntryPoint.sol " ;
77import {EntryPoint} from "lib/account-abstraction/contracts/core/EntryPoint.sol " ;
88import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol " ;
9+ import {BaseAccount} from "lib/account-abstraction/contracts/core/BaseAccount.sol " ;
10+ import {PackedUserOperation} from "lib/account-abstraction/contracts/interfaces/PackedUserOperation.sol " ;
11+ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol " ;
912
10- import {BasicAccount } from "./BasicAccount .sol " ;
13+ import {FlopsAccount } from "../src/FlopsAccount .sol " ;
1114
1215contract FLOpsTest is Test {
1316 EntryPoint public entryPoint;
14- FLOpsPaymaster public flopsPaymaster;
17+ FlopsPaymaster public flopsPaymaster;
1518 AtomicEntryPoint public atomicEntryPoint;
1619
17- BasicAccount public alice;
18- BasicAccount public bob;
20+ uint256 public alicePrivateKey;
21+ uint256 public bobPrivateKey;
22+ address public aliceAddress;
23+ address public bobAddress;
24+ FlopsAccount public alice;
25+ FlopsAccount public bob;
1926 address public owner;
2027
2128 // Force to canonical entrypoint address, todo use create2 deployer
@@ -29,23 +36,51 @@ contract FLOpsTest is Test {
2936 function setUp () public {
3037 owner = makeAddr ("owner " );
3138 entryPoint = deployEntryPoint ();
32- flopsPaymaster = new FLOpsPaymaster (IEntryPoint (address (entryPoint)), owner);
39+ flopsPaymaster = new FlopsPaymaster (IEntryPoint (address (entryPoint)), owner);
3340 atomicEntryPoint = new AtomicEntryPoint (address (entryPoint), address (flopsPaymaster));
3441
3542 vm.prank (owner);
3643 flopsPaymaster.setAtomicEntryPoint (address (atomicEntryPoint));
3744
38- // Setup smart accounts with ETH
39- alice = new BasicAccount (makeAddr ("alice " ));
45+ // Create smart accounts
46+ (aliceAddress, alicePrivateKey) = makeAddrAndKey ("alice " );
47+ (bobAddress, bobPrivateKey) = makeAddrAndKey ("bob " );
48+ alice = new FlopsAccount (aliceAddress);
49+ bob = new FlopsAccount (bobAddress);
50+
51+ // Fund smart accounts with ETH
4052 vm.deal (address (alice), 100 ether);
41- bob = new BasicAccount (makeAddr ("bob " ));
4253 vm.deal (address (bob), 100 ether);
4354
4455 // Pre-fill gas at entrypoint for smart accounts
4556 entryPoint.depositTo {value: 100 ether }(address (alice));
4657 entryPoint.depositTo {value: 100 ether }(address (bob));
4758 }
4859
60+ function buildUserOp (FlopsAccount account , address to , uint256 value , bytes memory paymasterAndData , uint256 privateKey )
61+ public
62+ returns (PackedUserOperation memory )
63+ {
64+ assertEq (vm.addr (privateKey), account.owner ());
65+ PackedUserOperation memory userOp = PackedUserOperation ({
66+ sender: address (account),
67+ nonce: account.getNonce (),
68+ initCode: "" ,
69+ callData: abi.encodeWithSelector (BaseAccount.execute.selector , to, value, "" ),
70+ accountGasLimits: bytes32 (abi.encodePacked (uint128 (100000 ), uint128 (100000 ))),
71+ preVerificationGas: 100000 ,
72+ gasFees: bytes32 (abi.encodePacked (uint128 (1000000000 ), uint128 (1000000000 ))),
73+ paymasterAndData: paymasterAndData,
74+ signature: ""
75+ });
76+
77+ bytes32 userOpHash = entryPoint.getUserOpHash (userOp);
78+ (uint8 v , bytes32 r , bytes32 s ) = vm.sign (privateKey, userOpHash);
79+ bytes memory signature = abi.encodePacked (r, s, v);
80+ userOp.signature = signature;
81+ return userOp;
82+ }
83+
4984 function test_setUp () public {
5085 // canonical entrypoint address
5186 assertEq (address (entryPoint), address (0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108 ));
@@ -59,6 +94,10 @@ contract FLOpsTest is Test {
5994 // setAtomicEntryPoint works
6095 assertEq (address (flopsPaymaster.atomicEntryPoint ()), address (atomicEntryPoint));
6196
97+ // account addresses are correct
98+ assertEq (aliceAddress, alice.owner ());
99+ assertEq (bobAddress, bob.owner ());
100+
62101 // alice and bob have ETH in their accounts
63102 assertEq (address (alice).balance, 100 ether);
64103 assertEq (address (bob).balance, 100 ether);
@@ -67,4 +106,57 @@ contract FLOpsTest is Test {
67106 assertEq (entryPoint.balanceOf (address (alice)), 100 ether);
68107 assertEq (entryPoint.balanceOf (address (bob)), 100 ether);
69108 }
109+
110+ function test_buildUserOp () public {
111+ address charlie = makeAddr ("charlie " );
112+ PackedUserOperation memory userOp = buildUserOp (alice, charlie, 1 ether, "" , alicePrivateKey);
113+ assertEq (userOp.sender, address (alice));
114+ assertEq (userOp.nonce, alice.getNonce ());
115+ assertEq (userOp.callData, abi.encodeWithSelector (BaseAccount.execute.selector , address (charlie), 1 ether, "" ));
116+ assertEq (userOp.accountGasLimits, bytes32 (abi.encodePacked (uint128 (100000 ), uint128 (100000 ))));
117+ assertEq (userOp.preVerificationGas, 100000 );
118+ assertEq (userOp.gasFees, bytes32 (abi.encodePacked (uint128 (1000000000 ), uint128 (1000000000 ))));
119+
120+ // verify signature
121+ bytes32 userOpHash = entryPoint.getUserOpHash (userOp);
122+ address recovered = ECDSA.recover (userOpHash, userOp.signature);
123+ assertEq (recovered, aliceAddress);
124+ }
125+
126+ function test_sendETH () public {
127+ address charlie = makeAddr ("charlie " );
128+ PackedUserOperation memory userOp = buildUserOp (alice, charlie, 1 ether, "" , alicePrivateKey);
129+ PackedUserOperation[] memory userOps = new PackedUserOperation [](1 );
130+ userOps[0 ] = userOp;
131+
132+ // Call from an EOA to satisfy EntryPoint's nonReentrant modifier
133+ // Use prank with both msg.sender and tx.origin set to the same EOA
134+ address bundler = makeAddr ("bundler " );
135+ vm.prank (bundler, bundler);
136+ entryPoint.handleOps (userOps, payable (makeAddr ("beneficiary " )));
137+
138+ assertEq (address (alice).balance, 99 ether);
139+ assertEq (address (charlie).balance, 1 ether);
140+ }
141+
142+ function test_foo () public {
143+ // Use EIP-7702 to attach AtomicEntryPoint code to the bundler
144+ (address bundler , uint256 privateKey ) = makeAddrAndKey ("bundler " );
145+ console2.log (bundler.code.length );
146+ vm.signAndAttachDelegation (address (atomicEntryPoint), privateKey);
147+
148+ console2.log (bundler.code.length );
149+
150+ // Create the user operation
151+ address charlie = makeAddr ("charlie " );
152+ PackedUserOperation memory userOp = buildUserOp (alice, charlie, 1 ether, "" , alicePrivateKey);
153+ PackedUserOperation[] memory userOps = new PackedUserOperation [](1 );
154+ userOps[0 ] = userOp;
155+
156+ // Call the handleOps function on the bundler
157+ bundler.call (abi.encodeWithSelector (atomicEntryPoint.handleOps.selector , userOps, payable (makeAddr ("beneficiary " ))));
158+
159+ assertEq (address (alice).balance, 99 ether);
160+ assertEq (address (charlie).balance, 1 ether);
161+ }
70162}
0 commit comments