draft-ERC4337Utils.sol 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.20;
  3. import {IEntryPoint, PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
  4. import {Math} from "../../utils/math/Math.sol";
  5. import {Packing} from "../../utils/Packing.sol";
  6. /**
  7. * @dev Library with common ERC-4337 utility functions.
  8. *
  9. * See https://eips.ethereum.org/EIPS/eip-4337[ERC-4337].
  10. */
  11. library ERC4337Utils {
  12. using Packing for *;
  13. /// @dev For simulation purposes, validateUserOp (and validatePaymasterUserOp) return this value on success.
  14. uint256 internal constant SIG_VALIDATION_SUCCESS = 0;
  15. /// @dev For simulation purposes, validateUserOp (and validatePaymasterUserOp) must return this value in case of signature failure, instead of revert.
  16. uint256 internal constant SIG_VALIDATION_FAILED = 1;
  17. /// @dev Parses the validation data into its components. See {packValidationData}.
  18. function parseValidationData(
  19. uint256 validationData
  20. ) internal pure returns (address aggregator, uint48 validAfter, uint48 validUntil) {
  21. validAfter = uint48(bytes32(validationData).extract_32_6(0x00));
  22. validUntil = uint48(bytes32(validationData).extract_32_6(0x06));
  23. aggregator = address(bytes32(validationData).extract_32_20(0x0c));
  24. if (validUntil == 0) validUntil = type(uint48).max;
  25. }
  26. /// @dev Packs the validation data into a single uint256. See {parseValidationData}.
  27. function packValidationData(
  28. address aggregator,
  29. uint48 validAfter,
  30. uint48 validUntil
  31. ) internal pure returns (uint256) {
  32. return uint256(bytes6(validAfter).pack_6_6(bytes6(validUntil)).pack_12_20(bytes20(aggregator)));
  33. }
  34. /// @dev Same as {packValidationData}, but with a boolean signature success flag.
  35. function packValidationData(bool sigSuccess, uint48 validAfter, uint48 validUntil) internal pure returns (uint256) {
  36. return
  37. packValidationData(
  38. address(uint160(Math.ternary(sigSuccess, SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED))),
  39. validAfter,
  40. validUntil
  41. );
  42. }
  43. /**
  44. * @dev Combines two validation data into a single one.
  45. *
  46. * The `aggregator` is set to {SIG_VALIDATION_SUCCESS} if both are successful, while
  47. * the `validAfter` is the maximum and the `validUntil` is the minimum of both.
  48. */
  49. function combineValidationData(uint256 validationData1, uint256 validationData2) internal pure returns (uint256) {
  50. (address aggregator1, uint48 validAfter1, uint48 validUntil1) = parseValidationData(validationData1);
  51. (address aggregator2, uint48 validAfter2, uint48 validUntil2) = parseValidationData(validationData2);
  52. bool success = aggregator1 == address(0) && aggregator2 == address(0);
  53. uint48 validAfter = uint48(Math.max(validAfter1, validAfter2));
  54. uint48 validUntil = uint48(Math.min(validUntil1, validUntil2));
  55. return packValidationData(success, validAfter, validUntil);
  56. }
  57. /// @dev Returns the aggregator of the `validationData` and whether it is out of time range.
  58. function getValidationData(uint256 validationData) internal view returns (address aggregator, bool outOfTimeRange) {
  59. (address aggregator_, uint48 validAfter, uint48 validUntil) = parseValidationData(validationData);
  60. return (aggregator_, block.timestamp < validAfter || validUntil < block.timestamp);
  61. }
  62. /// @dev Computes the hash of a user operation with the current entrypoint and chainid.
  63. function hash(PackedUserOperation calldata self) internal view returns (bytes32) {
  64. return hash(self, address(this), block.chainid);
  65. }
  66. /// @dev Sames as {hash}, but with a custom entrypoint and chainid.
  67. function hash(
  68. PackedUserOperation calldata self,
  69. address entrypoint,
  70. uint256 chainid
  71. ) internal pure returns (bytes32) {
  72. bytes32 result = keccak256(
  73. abi.encode(
  74. keccak256(
  75. abi.encode(
  76. self.sender,
  77. self.nonce,
  78. keccak256(self.initCode),
  79. keccak256(self.callData),
  80. self.accountGasLimits,
  81. self.preVerificationGas,
  82. self.gasFees,
  83. keccak256(self.paymasterAndData)
  84. )
  85. ),
  86. entrypoint,
  87. chainid
  88. )
  89. );
  90. return result;
  91. }
  92. /// @dev Returns `verificationGasLimit` from the {PackedUserOperation}.
  93. function verificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
  94. return uint128(self.accountGasLimits.extract_32_16(0x00));
  95. }
  96. /// @dev Returns `accountGasLimits` from the {PackedUserOperation}.
  97. function callGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
  98. return uint128(self.accountGasLimits.extract_32_16(0x10));
  99. }
  100. /// @dev Returns the first section of `gasFees` from the {PackedUserOperation}.
  101. function maxPriorityFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
  102. return uint128(self.gasFees.extract_32_16(0x00));
  103. }
  104. /// @dev Returns the second section of `gasFees` from the {PackedUserOperation}.
  105. function maxFeePerGas(PackedUserOperation calldata self) internal pure returns (uint256) {
  106. return uint128(self.gasFees.extract_32_16(0x10));
  107. }
  108. /// @dev Returns the total gas price for the {PackedUserOperation} (ie. `maxFeePerGas` or `maxPriorityFeePerGas + basefee`).
  109. function gasPrice(PackedUserOperation calldata self) internal view returns (uint256) {
  110. unchecked {
  111. // Following values are "per gas"
  112. uint256 maxPriorityFee = maxPriorityFeePerGas(self);
  113. uint256 maxFee = maxFeePerGas(self);
  114. return Math.ternary(maxFee == maxPriorityFee, maxFee, Math.min(maxFee, maxPriorityFee + block.basefee));
  115. }
  116. }
  117. /// @dev Returns the first section of `paymasterAndData` from the {PackedUserOperation}.
  118. function paymaster(PackedUserOperation calldata self) internal pure returns (address) {
  119. return address(bytes20(self.paymasterAndData[0:20]));
  120. }
  121. /// @dev Returns the second section of `paymasterAndData` from the {PackedUserOperation}.
  122. function paymasterVerificationGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
  123. return uint128(bytes16(self.paymasterAndData[20:36]));
  124. }
  125. /// @dev Returns the third section of `paymasterAndData` from the {PackedUserOperation}.
  126. function paymasterPostOpGasLimit(PackedUserOperation calldata self) internal pure returns (uint256) {
  127. return uint128(bytes16(self.paymasterAndData[36:52]));
  128. }
  129. }