AccountERC7702.t.sol 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.20;
  3. import {Test} from "forge-std/Test.sol";
  4. import {AccountERC7702Mock} from "@openzeppelin/contracts/mocks/account/AccountMock.sol";
  5. import {CallReceiverMock} from "@openzeppelin/contracts/mocks/CallReceiverMock.sol";
  6. import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
  7. import {ERC7579Utils, Execution, Mode, ModeSelector, ModePayload} from "@openzeppelin/contracts/account/utils/draft-ERC7579Utils.sol";
  8. import {ERC4337Utils, IEntryPointExtra} from "@openzeppelin/contracts/account/utils/draft-ERC4337Utils.sol";
  9. import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
  10. import {PackedUserOperation} from "@openzeppelin/contracts/interfaces/draft-IERC4337.sol";
  11. import {ERC7821} from "@openzeppelin/contracts/account/extensions/draft-ERC7821.sol";
  12. contract AccountERC7702MockConstructor is AccountERC7702Mock {
  13. constructor() EIP712("MyAccount", "1") {}
  14. }
  15. contract AccountERC7702Test is Test {
  16. using ERC7579Utils for *;
  17. using ERC4337Utils for PackedUserOperation;
  18. using Strings for *;
  19. uint256 private constant MAX_ETH = type(uint128).max;
  20. // Test accounts
  21. CallReceiverMock private _target;
  22. // ERC-4337 signer
  23. uint256 private _signerPrivateKey;
  24. AccountERC7702MockConstructor private _signer;
  25. function setUp() public {
  26. // Deploy target contract
  27. _target = new CallReceiverMock();
  28. // Setup signer
  29. _signerPrivateKey = 0x1234;
  30. _signer = AccountERC7702MockConstructor(payable(vm.addr(_signerPrivateKey)));
  31. vm.deal(address(_signer), MAX_ETH);
  32. // Sign and attach delegation
  33. vm.signAndAttachDelegation(address(new AccountERC7702MockConstructor()), _signerPrivateKey);
  34. // Setup entrypoint
  35. vm.deal(address(ERC4337Utils.ENTRYPOINT_V08), MAX_ETH);
  36. vm.etch(address(ERC4337Utils.ENTRYPOINT_V08), vm.readFileBinary("test/bin/EntryPoint070.bytecode"));
  37. }
  38. function testExecuteBatch(uint256 argA, uint256 argB) public {
  39. // Create the mode for batch execution
  40. Mode mode = ERC7579Utils.CALLTYPE_BATCH.encodeMode(
  41. ERC7579Utils.EXECTYPE_DEFAULT,
  42. ModeSelector.wrap(0x00000000),
  43. ModePayload.wrap(0x00000000)
  44. );
  45. Execution[] memory execution = new Execution[](2);
  46. execution[0] = Execution({
  47. target: address(_target),
  48. value: 1 ether,
  49. callData: abi.encodeCall(CallReceiverMock.mockFunctionExtra, ())
  50. });
  51. execution[1] = Execution({
  52. target: address(_target),
  53. value: 0,
  54. callData: abi.encodeCall(CallReceiverMock.mockFunctionWithArgs, (argA, argB))
  55. });
  56. // Pack the batch within a PackedUserOperation
  57. PackedUserOperation[] memory ops = new PackedUserOperation[](1);
  58. ops[0] = PackedUserOperation({
  59. sender: address(_signer),
  60. nonce: 0,
  61. initCode: bytes(""),
  62. callData: abi.encodeCall(ERC7821.execute, (Mode.unwrap(mode), execution.encodeBatch())),
  63. preVerificationGas: 100000,
  64. accountGasLimits: bytes32(abi.encodePacked(uint128(100000), uint128(100000))),
  65. gasFees: bytes32(abi.encodePacked(uint128(1000000), uint128(1000000))),
  66. paymasterAndData: bytes(""),
  67. signature: bytes("")
  68. });
  69. (uint8 v, bytes32 r, bytes32 s) = vm.sign(
  70. _signerPrivateKey,
  71. IEntryPointExtra(address(ERC4337Utils.ENTRYPOINT_V08)).getUserOpHash(ops[0])
  72. );
  73. ops[0].signature = abi.encodePacked(r, s, v);
  74. // Expect the events to be emitted
  75. vm.expectEmit(true, true, true, true);
  76. emit CallReceiverMock.MockFunctionCalledExtra(address(_signer), 1 ether);
  77. vm.expectEmit(true, true, true, true);
  78. emit CallReceiverMock.MockFunctionCalledWithArgs(argA, argB);
  79. // Execute the batch
  80. _signer.entryPoint().handleOps(ops, payable(makeAddr("beneficiary")));
  81. }
  82. }