Skip to content

Commit 9dc37fc

Browse files
committed
renaming
1 parent 1f8c485 commit 9dc37fc

5 files changed

Lines changed: 138 additions & 17 deletions

File tree

src/AtomicEntryPoint.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ pragma solidity ^0.8.20;
44
import {IEntryPoint, PackedUserOperation} from "lib/openzeppelin-contracts/contracts/interfaces/draft-IERC4337.sol";
55
import "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
66

7-
import {IFLOpsPaymaster} from "./IFLOpsPaymaster.sol";
7+
import {IFlopsPaymaster} from "./IFlopsPaymaster.sol";
88

99
contract AtomicEntryPoint is ReentrancyGuardTransient {
1010
IEntryPoint public immutable entryPoint;
11-
IFLOpsPaymaster public immutable flops;
11+
IFlopsPaymaster public immutable flops;
1212

1313
constructor(address _entryPoint, address _flopsPaymaster) {
1414
entryPoint = IEntryPoint(_entryPoint);
15-
flops = IFLOpsPaymaster(_flopsPaymaster);
15+
flops = IFlopsPaymaster(_flopsPaymaster);
1616
}
1717

1818
function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary) external nonReentrant {

src/FLOpsPaymaster.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import {PackedUserOperation} from "lib/account-abstraction/contracts/interfaces/
55
import {BasePaymaster} from "lib/account-abstraction/contracts/core/BasePaymaster.sol";
66
import {IEntryPoint} from "lib/account-abstraction/contracts/interfaces/IEntryPoint.sol";
77

8-
import {IFLOpsPaymaster} from "./IFLOpsPaymaster.sol";
8+
import {IFlopsPaymaster} from "./IFlopsPaymaster.sol";
99

10-
contract FLOpsPaymaster is BasePaymaster, IFLOpsPaymaster {
10+
contract FlopsPaymaster is BasePaymaster, IFlopsPaymaster {
1111
bool public bundleBroken;
1212

1313
address public atomicEntryPoint;
1414

1515
constructor(IEntryPoint _entryPoint, address _owner) BasePaymaster(_entryPoint, _owner) {}
1616

1717
modifier _requireFromAtomicEntryPoint() {
18-
require(msg.sender == address(atomicEntryPoint), "FLOpsPaymaster: Not from atomic entrypoint");
18+
require(msg.sender == address(atomicEntryPoint), "FlopsPaymaster: Not from atomic entrypoint");
1919
_;
2020
}
2121

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {PackedUserOperation} from "lib/account-abstraction/contracts/interfaces/
77
import "account-abstraction/core/Helpers.sol";
88
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
99

10-
contract BasicAccount is BaseAccount {
10+
contract FlopsAccount is BaseAccount {
1111
address public constant ENTRY_POINT_ADDRESS = 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108;
1212
address public owner;
1313

@@ -31,4 +31,33 @@ contract BasicAccount is BaseAccount {
3131
}
3232
return SIG_VALIDATION_SUCCESS;
3333
}
34+
35+
function _requireForExecute() internal view override {
36+
_requireFromEntryPoint();
37+
// todo check if bundle is broken at the paymaster
38+
}
39+
40+
/**
41+
* Validate the nonce of the UserOperation.
42+
* This method may validate the nonce requirement of this account.
43+
* e.g.
44+
* To limit the nonce to use sequenced UserOps only (no "out of order" UserOps):
45+
* `require(nonce < type(uint64).max)`
46+
* For a hypothetical account that *requires* the nonce to be out-of-order:
47+
* `require(nonce & type(uint64).max == 0)`
48+
*
49+
* The actual nonce uniqueness is managed by the EntryPoint, and thus no other
50+
* action is needed by the account itself.
51+
*
52+
* @param nonce to validate
53+
*
54+
* solhint-disable-next-line no-empty-blocks
55+
*/
56+
function _validateNonce(uint256 nonce) internal view override {
57+
// currently does nothing
58+
}
3459
}
60+
61+
62+
63+

src/IFLOpsPaymaster.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.20;
33

4-
interface IFLOpsPaymaster {
4+
interface IFlopsPaymaster {
55
function resetBundle() external;
66
function bundleBroken() external view returns (bool);
77
}

test/FLOps.t.sol

Lines changed: 101 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,27 @@
22
pragma solidity ^0.8.20;
33

44
import {Test, console2} from "forge-std/Test.sol";
5-
import {FLOpsPaymaster} from "../src/FLOpsPaymaster.sol";
5+
import {FlopsPaymaster} from "../src/FlopsPaymaster.sol";
66
import {AtomicEntryPoint} from "../src/AtomicEntryPoint.sol";
77
import {EntryPoint} from "lib/account-abstraction/contracts/core/EntryPoint.sol";
88
import {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

1215
contract 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

Comments
 (0)