AccountERC7702.t.sol 4.2 KB

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