Account.sol 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.20;
  3. import {PackedUserOperation, IAccount, IEntryPoint} from "../interfaces/draft-IERC4337.sol";
  4. import {ERC4337Utils} from "./utils/draft-ERC4337Utils.sol";
  5. import {AbstractSigner} from "../utils/cryptography/signers/AbstractSigner.sol";
  6. /**
  7. * @dev A simple ERC4337 account implementation. This base implementation only includes the minimal logic to process
  8. * user operations.
  9. *
  10. * Developers must implement the {AbstractSigner-_rawSignatureValidation} function to define the account's validation logic.
  11. *
  12. * NOTE: This core account doesn't include any mechanism for performing arbitrary external calls. This is an essential
  13. * feature that all Account should have. We leave it up to the developers to implement the mechanism of their choice.
  14. * Common choices include ERC-6900, ERC-7579 and ERC-7821 (among others).
  15. *
  16. * IMPORTANT: Implementing a mechanism to validate signatures is a security-sensitive operation as it may allow an
  17. * attacker to bypass the account's security measures. Check out {SignerECDSA}, {SignerP256}, or {SignerRSA} for
  18. * digital signature validation implementations.
  19. *
  20. * @custom:stateless
  21. */
  22. abstract contract Account is AbstractSigner, IAccount {
  23. /**
  24. * @dev Unauthorized call to the account.
  25. */
  26. error AccountUnauthorized(address sender);
  27. /**
  28. * @dev Revert if the caller is not the entry point or the account itself.
  29. */
  30. modifier onlyEntryPointOrSelf() {
  31. _checkEntryPointOrSelf();
  32. _;
  33. }
  34. /**
  35. * @dev Revert if the caller is not the entry point.
  36. */
  37. modifier onlyEntryPoint() {
  38. _checkEntryPoint();
  39. _;
  40. }
  41. /**
  42. * @dev Canonical entry point for the account that forwards and validates user operations.
  43. */
  44. function entryPoint() public view virtual returns (IEntryPoint) {
  45. return ERC4337Utils.ENTRYPOINT_V08;
  46. }
  47. /**
  48. * @dev Return the account nonce for the canonical sequence.
  49. */
  50. function getNonce() public view virtual returns (uint256) {
  51. return getNonce(0);
  52. }
  53. /**
  54. * @dev Return the account nonce for a given sequence (key).
  55. */
  56. function getNonce(uint192 key) public view virtual returns (uint256) {
  57. return entryPoint().getNonce(address(this), key);
  58. }
  59. /**
  60. * @inheritdoc IAccount
  61. */
  62. function validateUserOp(
  63. PackedUserOperation calldata userOp,
  64. bytes32 userOpHash,
  65. uint256 missingAccountFunds
  66. ) public virtual onlyEntryPoint returns (uint256) {
  67. uint256 validationData = _validateUserOp(userOp, userOpHash);
  68. _payPrefund(missingAccountFunds);
  69. return validationData;
  70. }
  71. /**
  72. * @dev Returns the validationData for a given user operation. By default, this checks the signature of the
  73. * signable hash (produced by {_signableUserOpHash}) using the abstract signer ({AbstractSigner-_rawSignatureValidation}).
  74. *
  75. * NOTE: The userOpHash is assumed to be correct. Calling this function with a userOpHash that does not match the
  76. * userOp will result in undefined behavior.
  77. */
  78. function _validateUserOp(
  79. PackedUserOperation calldata userOp,
  80. bytes32 userOpHash
  81. ) internal virtual returns (uint256) {
  82. return
  83. _rawSignatureValidation(_signableUserOpHash(userOp, userOpHash), userOp.signature)
  84. ? ERC4337Utils.SIG_VALIDATION_SUCCESS
  85. : ERC4337Utils.SIG_VALIDATION_FAILED;
  86. }
  87. /**
  88. * @dev Virtual function that returns the signable hash for a user operations. Since v0.8.0 of the entrypoint,
  89. * `userOpHash` is an EIP-712 hash that can be signed directly.
  90. */
  91. function _signableUserOpHash(
  92. PackedUserOperation calldata /*userOp*/,
  93. bytes32 userOpHash
  94. ) internal view virtual returns (bytes32) {
  95. return userOpHash;
  96. }
  97. /**
  98. * @dev Sends the missing funds for executing the user operation to the {entrypoint}.
  99. * The `missingAccountFunds` must be defined by the entrypoint when calling {validateUserOp}.
  100. */
  101. function _payPrefund(uint256 missingAccountFunds) internal virtual {
  102. if (missingAccountFunds > 0) {
  103. (bool success, ) = payable(msg.sender).call{value: missingAccountFunds}("");
  104. success; // Silence warning. The entrypoint should validate the result.
  105. }
  106. }
  107. /**
  108. * @dev Ensures the caller is the {entrypoint}.
  109. */
  110. function _checkEntryPoint() internal view virtual {
  111. address sender = msg.sender;
  112. if (sender != address(entryPoint())) {
  113. revert AccountUnauthorized(sender);
  114. }
  115. }
  116. /**
  117. * @dev Ensures the caller is the {entrypoint} or the account itself.
  118. */
  119. function _checkEntryPointOrSelf() internal view virtual {
  120. address sender = msg.sender;
  121. if (sender != address(this) && sender != address(entryPoint())) {
  122. revert AccountUnauthorized(sender);
  123. }
  124. }
  125. /**
  126. * @dev Receive Ether.
  127. */
  128. receive() external payable virtual {}
  129. }