github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/EAS/resolver/SchemaResolver.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.19;
     3  
     4  import { IEAS, Attestation } from "src/EAS/IEAS.sol";
     5  import { AccessDenied, InvalidEAS, InvalidLength, uncheckedInc } from "src/EAS/Common.sol";
     6  
     7  import { ISchemaResolver } from "src/EAS/resolver/ISchemaResolver.sol";
     8  
     9  /// @title SchemaResolver
    10  /// @notice The base schema resolver contract.
    11  abstract contract SchemaResolver is ISchemaResolver {
    12      error InsufficientValue();
    13      error NotPayable();
    14  
    15      // The global EAS contract.
    16      IEAS internal immutable _eas;
    17  
    18      /// @dev Creates a new resolver.
    19      /// @param eas The address of the global EAS contract.
    20      constructor(IEAS eas) {
    21          if (address(eas) == address(0)) {
    22              revert InvalidEAS();
    23          }
    24  
    25          _eas = eas;
    26      }
    27  
    28      /// @dev Ensures that only the EAS contract can make this call.
    29      modifier onlyEAS() {
    30          _onlyEAS();
    31  
    32          _;
    33      }
    34  
    35      /// @inheritdoc ISchemaResolver
    36      function isPayable() public pure virtual returns (bool) {
    37          return false;
    38      }
    39  
    40      /// @dev ETH callback.
    41      receive() external payable virtual {
    42          if (!isPayable()) {
    43              revert NotPayable();
    44          }
    45      }
    46  
    47      /// @inheritdoc ISchemaResolver
    48      function attest(Attestation calldata attestation) external payable onlyEAS returns (bool) {
    49          return onAttest(attestation, msg.value);
    50      }
    51  
    52      /// @inheritdoc ISchemaResolver
    53      function multiAttest(
    54          Attestation[] calldata attestations,
    55          uint256[] calldata values
    56      )
    57          external
    58          payable
    59          onlyEAS
    60          returns (bool)
    61      {
    62          uint256 length = attestations.length;
    63          if (length != values.length) {
    64              revert InvalidLength();
    65          }
    66  
    67          // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
    68          // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
    69          // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be
    70          // possible to send too much ETH anyway.
    71          uint256 remainingValue = msg.value;
    72  
    73          for (uint256 i = 0; i < length; i = uncheckedInc(i)) {
    74              // Ensure that the attester/revoker doesn't try to spend more than available.
    75              uint256 value = values[i];
    76              if (value > remainingValue) {
    77                  revert InsufficientValue();
    78              }
    79  
    80              // Forward the attestation to the underlying resolver and return false in case it isn't approved.
    81              if (!onAttest(attestations[i], value)) {
    82                  return false;
    83              }
    84  
    85              unchecked {
    86                  // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount.
    87                  remainingValue -= value;
    88              }
    89          }
    90  
    91          return true;
    92      }
    93  
    94      /// @inheritdoc ISchemaResolver
    95      function revoke(Attestation calldata attestation) external payable onlyEAS returns (bool) {
    96          return onRevoke(attestation, msg.value);
    97      }
    98  
    99      /// @inheritdoc ISchemaResolver
   100      function multiRevoke(
   101          Attestation[] calldata attestations,
   102          uint256[] calldata values
   103      )
   104          external
   105          payable
   106          onlyEAS
   107          returns (bool)
   108      {
   109          uint256 length = attestations.length;
   110          if (length != values.length) {
   111              revert InvalidLength();
   112          }
   113  
   114          // We are keeping track of the remaining ETH amount that can be sent to resolvers and will keep deducting
   115          // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless
   116          // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be
   117          // possible to send too much ETH anyway.
   118          uint256 remainingValue = msg.value;
   119  
   120          for (uint256 i = 0; i < length; i = uncheckedInc(i)) {
   121              // Ensure that the attester/revoker doesn't try to spend more than available.
   122              uint256 value = values[i];
   123              if (value > remainingValue) {
   124                  revert InsufficientValue();
   125              }
   126  
   127              // Forward the revocation to the underlying resolver and return false in case it isn't approved.
   128              if (!onRevoke(attestations[i], value)) {
   129                  return false;
   130              }
   131  
   132              unchecked {
   133                  // Subtract the ETH amount, that was provided to this attestation, from the global remaining ETH amount.
   134                  remainingValue -= value;
   135              }
   136          }
   137  
   138          return true;
   139      }
   140  
   141      /// @notice A resolver callback that should be implemented by child contracts.
   142      /// @param attestation The new attestation.
   143      /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
   144      ///        both attest() and multiAttest() callbacks EAS-only callbacks and that in case of multi attestations,
   145      ///        it'll usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all
   146      ///        the attestations in the batch.
   147      /// @return Whether the attestation is valid.
   148      function onAttest(Attestation calldata attestation, uint256 value) internal virtual returns (bool);
   149  
   150      /// @notice Processes an attestation revocation and verifies if it can be revoked.
   151      /// @param attestation The existing attestation to be revoked.
   152      /// @param value An explicit ETH amount that was sent to the resolver. Please note that this value is verified in
   153      ///        both revoke() and multiRevoke() callbacks EAS-only callbacks and that in case of multi attestations,
   154      ///        it'll usually hold that msg.value != value, since msg.value aggregated the sent ETH amounts for all
   155      ///        the attestations in the batch.
   156      /// @return Whether the attestation can be revoked.
   157      function onRevoke(Attestation calldata attestation, uint256 value) internal virtual returns (bool);
   158  
   159      /// @notice Ensures that only the EAS contract can make this call.
   160      function _onlyEAS() private view {
   161          if (msg.sender != address(_eas)) {
   162              revert AccessDenied();
   163          }
   164      }
   165  }