github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/EAS/EAS.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity 0.8.19; 3 4 import { Address } from "@openzeppelin/contracts/utils/Address.sol"; 5 import { ISemver } from "src/universal/ISemver.sol"; 6 import { Predeploys } from "src/libraries/Predeploys.sol"; 7 import { EIP1271Verifier } from "src/EAS/eip1271/EIP1271Verifier.sol"; 8 import { ISchemaResolver } from "src/EAS/resolver/ISchemaResolver.sol"; 9 10 import { 11 AccessDenied, 12 EMPTY_UID, 13 Signature, 14 InvalidLength, 15 MAX_GAP, 16 NotFound, 17 NO_EXPIRATION_TIME, 18 uncheckedInc 19 } from "src/EAS/Common.sol"; 20 21 import { 22 Attestation, 23 AttestationRequest, 24 AttestationRequestData, 25 DelegatedAttestationRequest, 26 DelegatedRevocationRequest, 27 IEAS, 28 MultiAttestationRequest, 29 MultiDelegatedAttestationRequest, 30 MultiDelegatedRevocationRequest, 31 MultiRevocationRequest, 32 RevocationRequest, 33 RevocationRequestData 34 } from "src/EAS/IEAS.sol"; 35 36 import { ISchemaRegistry, SchemaRecord } from "src/EAS/ISchemaRegistry.sol"; 37 38 struct AttestationsResult { 39 uint256 usedValue; // Total ETH amount that was sent to resolvers. 40 bytes32[] uids; // UIDs of the new attestations. 41 } 42 43 /// @custom:proxied 44 /// @custom:predeploy 0x4200000000000000000000000000000000000021 45 /// @title EAS 46 /// @notice The Ethereum Attestation Service protocol. 47 contract EAS is IEAS, ISemver, EIP1271Verifier { 48 using Address for address payable; 49 50 error AlreadyRevoked(); 51 error AlreadyRevokedOffchain(); 52 error AlreadyTimestamped(); 53 error InsufficientValue(); 54 error InvalidAttestation(); 55 error InvalidAttestations(); 56 error InvalidExpirationTime(); 57 error InvalidOffset(); 58 error InvalidRegistry(); 59 error InvalidRevocation(); 60 error InvalidRevocations(); 61 error InvalidSchema(); 62 error InvalidVerifier(); 63 error Irrevocable(); 64 error NotPayable(); 65 error WrongSchema(); 66 67 // The global schema registry. 68 ISchemaRegistry private constant _schemaRegistry = ISchemaRegistry(Predeploys.SCHEMA_REGISTRY); 69 70 // The global mapping between attestations and their UIDs. 71 mapping(bytes32 uid => Attestation attestation) private _db; 72 73 // The global mapping between data and their timestamps. 74 mapping(bytes32 data => uint64 timestamp) private _timestamps; 75 76 // The global mapping between data and their revocation timestamps. 77 mapping(address revoker => mapping(bytes32 data => uint64 timestamp)) private _revocationsOffchain; 78 79 // Upgrade forward-compatibility storage gap 80 uint256[MAX_GAP - 3] private __gap; 81 82 /// @notice Semantic version. 83 /// @custom:semver 1.4.0 84 string public constant version = "1.4.0"; 85 86 /// @dev Creates a new EAS instance. 87 constructor() EIP1271Verifier("EAS", "1.3.0") { } 88 89 /// @inheritdoc IEAS 90 function getSchemaRegistry() external pure returns (ISchemaRegistry) { 91 return _schemaRegistry; 92 } 93 94 /// @inheritdoc IEAS 95 function attest(AttestationRequest calldata request) external payable returns (bytes32) { 96 AttestationRequestData[] memory data = new AttestationRequestData[](1); 97 data[0] = request.data; 98 99 return _attest(request.schema, data, msg.sender, msg.value, true).uids[0]; 100 } 101 102 /// @inheritdoc IEAS 103 function attestByDelegation(DelegatedAttestationRequest calldata delegatedRequest) 104 external 105 payable 106 returns (bytes32) 107 { 108 _verifyAttest(delegatedRequest); 109 110 AttestationRequestData[] memory data = new AttestationRequestData[](1); 111 data[0] = delegatedRequest.data; 112 return _attest(delegatedRequest.schema, data, delegatedRequest.attester, msg.value, true).uids[0]; 113 } 114 115 /// @inheritdoc IEAS 116 function multiAttest(MultiAttestationRequest[] calldata multiRequests) 117 external 118 payable 119 returns (bytes32[] memory) 120 { 121 // Since a multi-attest call is going to make multiple attestations for multiple schemas, we'd need to collect 122 // all the returned UIDs into a single list. 123 uint256 length = multiRequests.length; 124 bytes32[][] memory totalUids = new bytes32[][](length); 125 uint256 totalUidsCount = 0; 126 127 // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting 128 // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless 129 // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be 130 // possible to send too much ETH anyway. 131 uint256 availableValue = msg.value; 132 133 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 134 // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there 135 // is a remainder - it will be refunded back to the attester (something that we can only verify during the 136 // last and final batch). 137 bool last; 138 unchecked { 139 last = i == length - 1; 140 } 141 142 // Process the current batch of attestations. 143 MultiAttestationRequest calldata multiRequest = multiRequests[i]; 144 145 // Ensure that data isn't empty. 146 if (multiRequest.data.length == 0) { 147 revert InvalidLength(); 148 } 149 150 AttestationsResult memory res = 151 _attest(multiRequest.schema, multiRequest.data, msg.sender, availableValue, last); 152 153 // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. 154 availableValue -= res.usedValue; 155 156 // Collect UIDs (and merge them later). 157 totalUids[i] = res.uids; 158 unchecked { 159 totalUidsCount += res.uids.length; 160 } 161 } 162 163 // Merge all the collected UIDs and return them as a flatten array. 164 return _mergeUIDs(totalUids, totalUidsCount); 165 } 166 167 /// @inheritdoc IEAS 168 function multiAttestByDelegation(MultiDelegatedAttestationRequest[] calldata multiDelegatedRequests) 169 external 170 payable 171 returns (bytes32[] memory) 172 { 173 // Since a multi-attest call is going to make multiple attestations for multiple schemas, we'd need to collect 174 // all the returned UIDs into a single list. 175 uint256 length = multiDelegatedRequests.length; 176 bytes32[][] memory totalUids = new bytes32[][](length); 177 uint256 totalUidsCount = 0; 178 179 // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting 180 // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless 181 // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be 182 // possible to send too much ETH anyway. 183 uint256 availableValue = msg.value; 184 185 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 186 // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there 187 // is a remainder - it will be refunded back to the attester (something that we can only verify during the 188 // last and final batch). 189 bool last; 190 unchecked { 191 last = i == length - 1; 192 } 193 194 MultiDelegatedAttestationRequest calldata multiDelegatedRequest = multiDelegatedRequests[i]; 195 AttestationRequestData[] calldata data = multiDelegatedRequest.data; 196 197 // Ensure that no inputs are missing. 198 uint256 dataLength = data.length; 199 if (dataLength == 0 || dataLength != multiDelegatedRequest.signatures.length) { 200 revert InvalidLength(); 201 } 202 203 // Verify signatures. Please note that the signatures are assumed to be signed with increasing nonces. 204 for (uint256 j = 0; j < dataLength; j = uncheckedInc(j)) { 205 _verifyAttest( 206 DelegatedAttestationRequest({ 207 schema: multiDelegatedRequest.schema, 208 data: data[j], 209 signature: multiDelegatedRequest.signatures[j], 210 attester: multiDelegatedRequest.attester, 211 deadline: multiDelegatedRequest.deadline 212 }) 213 ); 214 } 215 216 // Process the current batch of attestations. 217 AttestationsResult memory res = 218 _attest(multiDelegatedRequest.schema, data, multiDelegatedRequest.attester, availableValue, last); 219 220 // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. 221 availableValue -= res.usedValue; 222 223 // Collect UIDs (and merge them later). 224 totalUids[i] = res.uids; 225 unchecked { 226 totalUidsCount += res.uids.length; 227 } 228 } 229 230 // Merge all the collected UIDs and return them as a flatten array. 231 return _mergeUIDs(totalUids, totalUidsCount); 232 } 233 234 /// @inheritdoc IEAS 235 function revoke(RevocationRequest calldata request) external payable { 236 RevocationRequestData[] memory data = new RevocationRequestData[](1); 237 data[0] = request.data; 238 239 _revoke(request.schema, data, msg.sender, msg.value, true); 240 } 241 242 /// @inheritdoc IEAS 243 function revokeByDelegation(DelegatedRevocationRequest calldata delegatedRequest) external payable { 244 _verifyRevoke(delegatedRequest); 245 246 RevocationRequestData[] memory data = new RevocationRequestData[](1); 247 data[0] = delegatedRequest.data; 248 249 _revoke(delegatedRequest.schema, data, delegatedRequest.revoker, msg.value, true); 250 } 251 252 /// @inheritdoc IEAS 253 function multiRevoke(MultiRevocationRequest[] calldata multiRequests) external payable { 254 // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting 255 // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless 256 // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be 257 // possible to send too much ETH anyway. 258 uint256 availableValue = msg.value; 259 260 uint256 length = multiRequests.length; 261 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 262 // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there 263 // is a remainder - it will be refunded back to the attester (something that we can only verify during the 264 // last and final batch). 265 bool last; 266 unchecked { 267 last = i == length - 1; 268 } 269 270 MultiRevocationRequest calldata multiRequest = multiRequests[i]; 271 272 // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. 273 availableValue -= _revoke(multiRequest.schema, multiRequest.data, msg.sender, availableValue, last); 274 } 275 } 276 277 /// @inheritdoc IEAS 278 function multiRevokeByDelegation(MultiDelegatedRevocationRequest[] calldata multiDelegatedRequests) 279 external 280 payable 281 { 282 // We are keeping track of the total available ETH amount that can be sent to resolvers and will keep deducting 283 // from it to verify that there isn't any attempt to send too much ETH to resolvers. Please note that unless 284 // some ETH was stuck in the contract by accident (which shouldn't happen in normal conditions), it won't be 285 // possible to send too much ETH anyway. 286 uint256 availableValue = msg.value; 287 288 uint256 length = multiDelegatedRequests.length; 289 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 290 // The last batch is handled slightly differently: if the total available ETH wasn't spent in full and there 291 // is a remainder - it will be refunded back to the attester (something that we can only verify during the 292 // last and final batch). 293 bool last; 294 unchecked { 295 last = i == length - 1; 296 } 297 298 MultiDelegatedRevocationRequest memory multiDelegatedRequest = multiDelegatedRequests[i]; 299 RevocationRequestData[] memory data = multiDelegatedRequest.data; 300 301 // Ensure that no inputs are missing. 302 uint256 dataLength = data.length; 303 if (dataLength == 0 || dataLength != multiDelegatedRequest.signatures.length) { 304 revert InvalidLength(); 305 } 306 307 // Verify signatures. Please note that the signatures are assumed to be signed with increasing nonces. 308 for (uint256 j = 0; j < dataLength; j = uncheckedInc(j)) { 309 _verifyRevoke( 310 DelegatedRevocationRequest({ 311 schema: multiDelegatedRequest.schema, 312 data: data[j], 313 signature: multiDelegatedRequest.signatures[j], 314 revoker: multiDelegatedRequest.revoker, 315 deadline: multiDelegatedRequest.deadline 316 }) 317 ); 318 } 319 320 // Ensure to deduct the ETH that was forwarded to the resolver during the processing of this batch. 321 availableValue -= 322 _revoke(multiDelegatedRequest.schema, data, multiDelegatedRequest.revoker, availableValue, last); 323 } 324 } 325 326 /// @inheritdoc IEAS 327 function timestamp(bytes32 data) external returns (uint64) { 328 uint64 time = _time(); 329 _timestamp(data, time); 330 return time; 331 } 332 333 /// @inheritdoc IEAS 334 function revokeOffchain(bytes32 data) external returns (uint64) { 335 uint64 time = _time(); 336 _revokeOffchain(msg.sender, data, time); 337 return time; 338 } 339 340 /// @inheritdoc IEAS 341 function multiRevokeOffchain(bytes32[] calldata data) external returns (uint64) { 342 uint64 time = _time(); 343 344 uint256 length = data.length; 345 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 346 _revokeOffchain(msg.sender, data[i], time); 347 } 348 349 return time; 350 } 351 352 /// @inheritdoc IEAS 353 function multiTimestamp(bytes32[] calldata data) external returns (uint64) { 354 uint64 time = _time(); 355 356 uint256 length = data.length; 357 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 358 _timestamp(data[i], time); 359 } 360 361 return time; 362 } 363 364 /// @inheritdoc IEAS 365 function getAttestation(bytes32 uid) external view returns (Attestation memory) { 366 return _db[uid]; 367 } 368 369 /// @inheritdoc IEAS 370 function isAttestationValid(bytes32 uid) public view returns (bool) { 371 return _db[uid].uid != EMPTY_UID; 372 } 373 374 /// @inheritdoc IEAS 375 function getTimestamp(bytes32 data) external view returns (uint64) { 376 return _timestamps[data]; 377 } 378 379 /// @inheritdoc IEAS 380 function getRevokeOffchain(address revoker, bytes32 data) external view returns (uint64) { 381 return _revocationsOffchain[revoker][data]; 382 } 383 384 /// @dev Attests to a specific schema. 385 /// @param schemaUID The unique identifier of the schema to attest to. 386 /// @param data The arguments of the attestation requests. 387 /// @param attester The attesting account. 388 /// @param availableValue The total available ETH amount that can be sent to the resolver. 389 /// @param last Whether this is the last attestations/revocations set. 390 /// @return The UID of the new attestations and the total sent ETH amount. 391 function _attest( 392 bytes32 schemaUID, 393 AttestationRequestData[] memory data, 394 address attester, 395 uint256 availableValue, 396 bool last 397 ) 398 private 399 returns (AttestationsResult memory) 400 { 401 uint256 length = data.length; 402 403 AttestationsResult memory res; 404 res.uids = new bytes32[](length); 405 406 // Ensure that we aren't attempting to attest to a non-existing schema. 407 SchemaRecord memory schemaRecord = _schemaRegistry.getSchema(schemaUID); 408 if (schemaRecord.uid == EMPTY_UID) { 409 revert InvalidSchema(); 410 } 411 412 Attestation[] memory attestations = new Attestation[](length); 413 uint256[] memory values = new uint256[](length); 414 415 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 416 AttestationRequestData memory request = data[i]; 417 418 // Ensure that either no expiration time was set or that it was set in the future. 419 if (request.expirationTime != NO_EXPIRATION_TIME && request.expirationTime <= _time()) { 420 revert InvalidExpirationTime(); 421 } 422 423 // Ensure that we aren't trying to make a revocable attestation for a non-revocable schema. 424 if (!schemaRecord.revocable && request.revocable) { 425 revert Irrevocable(); 426 } 427 428 Attestation memory attestation = Attestation({ 429 uid: EMPTY_UID, 430 schema: schemaUID, 431 refUID: request.refUID, 432 time: _time(), 433 expirationTime: request.expirationTime, 434 revocationTime: 0, 435 recipient: request.recipient, 436 attester: attester, 437 revocable: request.revocable, 438 data: request.data 439 }); 440 441 // Look for the first non-existing UID (and use a bump seed/nonce in the rare case of a conflict). 442 bytes32 uid; 443 uint32 bump = 0; 444 while (true) { 445 uid = _getUID(attestation, bump); 446 if (_db[uid].uid == EMPTY_UID) { 447 break; 448 } 449 450 unchecked { 451 ++bump; 452 } 453 } 454 attestation.uid = uid; 455 456 _db[uid] = attestation; 457 458 if (request.refUID != EMPTY_UID) { 459 // Ensure that we aren't trying to attest to a non-existing referenced UID. 460 if (!isAttestationValid(request.refUID)) { 461 revert NotFound(); 462 } 463 } 464 465 attestations[i] = attestation; 466 values[i] = request.value; 467 468 res.uids[i] = uid; 469 470 emit Attested(request.recipient, attester, uid, schemaUID); 471 } 472 473 res.usedValue = _resolveAttestations(schemaRecord, attestations, values, false, availableValue, last); 474 475 return res; 476 } 477 478 /// @dev Revokes an existing attestation to a specific schema. 479 /// @param schemaUID The unique identifier of the schema to attest to. 480 /// @param data The arguments of the revocation requests. 481 /// @param revoker The revoking account. 482 /// @param availableValue The total available ETH amount that can be sent to the resolver. 483 /// @param last Whether this is the last attestations/revocations set. 484 /// @return Returns the total sent ETH amount. 485 function _revoke( 486 bytes32 schemaUID, 487 RevocationRequestData[] memory data, 488 address revoker, 489 uint256 availableValue, 490 bool last 491 ) 492 private 493 returns (uint256) 494 { 495 // Ensure that a non-existing schema ID wasn't passed by accident. 496 SchemaRecord memory schemaRecord = _schemaRegistry.getSchema(schemaUID); 497 if (schemaRecord.uid == EMPTY_UID) { 498 revert InvalidSchema(); 499 } 500 501 uint256 length = data.length; 502 Attestation[] memory attestations = new Attestation[](length); 503 uint256[] memory values = new uint256[](length); 504 505 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 506 RevocationRequestData memory request = data[i]; 507 508 Attestation storage attestation = _db[request.uid]; 509 510 // Ensure that we aren't attempting to revoke a non-existing attestation. 511 if (attestation.uid == EMPTY_UID) { 512 revert NotFound(); 513 } 514 515 // Ensure that a wrong schema ID wasn't passed by accident. 516 if (attestation.schema != schemaUID) { 517 revert InvalidSchema(); 518 } 519 520 // Allow only original attesters to revoke their attestations. 521 if (attestation.attester != revoker) { 522 revert AccessDenied(); 523 } 524 525 // Please note that also checking of the schema itself is revocable is unnecessary, since it's not possible 526 // to 527 // make revocable attestations to an irrevocable schema. 528 if (!attestation.revocable) { 529 revert Irrevocable(); 530 } 531 532 // Ensure that we aren't trying to revoke the same attestation twice. 533 if (attestation.revocationTime != 0) { 534 revert AlreadyRevoked(); 535 } 536 attestation.revocationTime = _time(); 537 538 attestations[i] = attestation; 539 values[i] = request.value; 540 541 emit Revoked(attestations[i].recipient, revoker, request.uid, schemaUID); 542 } 543 544 return _resolveAttestations(schemaRecord, attestations, values, true, availableValue, last); 545 } 546 547 /// @dev Resolves a new attestation or a revocation of an existing attestation. 548 /// @param schemaRecord The schema of the attestation. 549 /// @param attestation The data of the attestation to make/revoke. 550 /// @param value An explicit ETH amount to send to the resolver. 551 /// @param isRevocation Whether to resolve an attestation or its revocation. 552 /// @param availableValue The total available ETH amount that can be sent to the resolver. 553 /// @param last Whether this is the last attestations/revocations set. 554 /// @return Returns the total sent ETH amount. 555 function _resolveAttestation( 556 SchemaRecord memory schemaRecord, 557 Attestation memory attestation, 558 uint256 value, 559 bool isRevocation, 560 uint256 availableValue, 561 bool last 562 ) 563 private 564 returns (uint256) 565 { 566 ISchemaResolver resolver = schemaRecord.resolver; 567 if (address(resolver) == address(0)) { 568 // Ensure that we don't accept payments if there is no resolver. 569 if (value != 0) { 570 revert NotPayable(); 571 } 572 573 if (last) { 574 _refund(availableValue); 575 } 576 577 return 0; 578 } 579 580 // Ensure that we don't accept payments which can't be forwarded to the resolver. 581 if (value != 0) { 582 if (!resolver.isPayable()) { 583 revert NotPayable(); 584 } 585 586 // Ensure that the attester/revoker doesn't try to spend more than available. 587 if (value > availableValue) { 588 revert InsufficientValue(); 589 } 590 591 // Ensure to deduct the sent value explicitly. 592 unchecked { 593 availableValue -= value; 594 } 595 } 596 597 if (isRevocation) { 598 if (!resolver.revoke{ value: value }(attestation)) { 599 revert InvalidRevocation(); 600 } 601 } else if (!resolver.attest{ value: value }(attestation)) { 602 revert InvalidAttestation(); 603 } 604 605 if (last) { 606 _refund(availableValue); 607 } 608 609 return value; 610 } 611 612 /// @dev Resolves multiple attestations or revocations of existing attestations. 613 /// @param schemaRecord The schema of the attestation. 614 /// @param attestations The data of the attestations to make/revoke. 615 /// @param values Explicit ETH amounts to send to the resolver. 616 /// @param isRevocation Whether to resolve an attestation or its revocation. 617 /// @param availableValue The total available ETH amount that can be sent to the resolver. 618 /// @param last Whether this is the last attestations/revocations set. 619 /// @return Returns the total sent ETH amount. 620 function _resolveAttestations( 621 SchemaRecord memory schemaRecord, 622 Attestation[] memory attestations, 623 uint256[] memory values, 624 bool isRevocation, 625 uint256 availableValue, 626 bool last 627 ) 628 private 629 returns (uint256) 630 { 631 uint256 length = attestations.length; 632 if (length == 1) { 633 return _resolveAttestation(schemaRecord, attestations[0], values[0], isRevocation, availableValue, last); 634 } 635 636 ISchemaResolver resolver = schemaRecord.resolver; 637 if (address(resolver) == address(0)) { 638 // Ensure that we don't accept payments if there is no resolver. 639 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 640 if (values[i] != 0) { 641 revert NotPayable(); 642 } 643 } 644 645 if (last) { 646 _refund(availableValue); 647 } 648 649 return 0; 650 } 651 652 uint256 totalUsedValue = 0; 653 bool isResolverPayable = resolver.isPayable(); 654 655 for (uint256 i = 0; i < length; i = uncheckedInc(i)) { 656 uint256 value = values[i]; 657 658 // Ensure that we don't accept payments which can't be forwarded to the resolver. 659 if (value == 0) { 660 continue; 661 } 662 663 if (!isResolverPayable) { 664 revert NotPayable(); 665 } 666 667 // Ensure that the attester/revoker doesn't try to spend more than available. 668 if (value > availableValue) { 669 revert InsufficientValue(); 670 } 671 672 // Ensure to deduct the sent value explicitly and add it to the total used value by the batch. 673 unchecked { 674 availableValue -= value; 675 totalUsedValue += value; 676 } 677 } 678 679 if (isRevocation) { 680 if (!resolver.multiRevoke{ value: totalUsedValue }(attestations, values)) { 681 revert InvalidRevocations(); 682 } 683 } else if (!resolver.multiAttest{ value: totalUsedValue }(attestations, values)) { 684 revert InvalidAttestations(); 685 } 686 687 if (last) { 688 _refund(availableValue); 689 } 690 691 return totalUsedValue; 692 } 693 694 /// @dev Calculates a UID for a given attestation. 695 /// @param attestation The input attestation. 696 /// @param bump A bump value to use in case of a UID conflict. 697 /// @return Attestation UID. 698 function _getUID(Attestation memory attestation, uint32 bump) private pure returns (bytes32) { 699 return keccak256( 700 abi.encodePacked( 701 attestation.schema, 702 attestation.recipient, 703 attestation.attester, 704 attestation.time, 705 attestation.expirationTime, 706 attestation.revocable, 707 attestation.refUID, 708 attestation.data, 709 bump 710 ) 711 ); 712 } 713 714 /// @dev Refunds remaining ETH amount to the attester. 715 /// @param remainingValue The remaining ETH amount that was not sent to the resolver. 716 function _refund(uint256 remainingValue) private { 717 if (remainingValue > 0) { 718 // Using a regular transfer here might revert, for some non-EOA attesters, due to exceeding of the 2300 719 // gas limit which is why we're using call instead (via sendValue), which the 2300 gas limit does not 720 // apply for. 721 payable(msg.sender).sendValue(remainingValue); 722 } 723 } 724 725 /// @dev Timestamps the specified bytes32 data. 726 /// @param data The data to timestamp. 727 /// @param time The timestamp. 728 function _timestamp(bytes32 data, uint64 time) private { 729 if (_timestamps[data] != 0) { 730 revert AlreadyTimestamped(); 731 } 732 733 _timestamps[data] = time; 734 735 emit Timestamped(data, time); 736 } 737 738 /// @dev Revokes the specified bytes32 data. 739 /// @param revoker The revoking account. 740 /// @param data The data to revoke. 741 /// @param time The timestamp the data was revoked with. 742 function _revokeOffchain(address revoker, bytes32 data, uint64 time) private { 743 mapping(bytes32 data => uint64 timestamp) storage revocations = _revocationsOffchain[revoker]; 744 745 if (revocations[data] != 0) { 746 revert AlreadyRevokedOffchain(); 747 } 748 749 revocations[data] = time; 750 751 emit RevokedOffchain(revoker, data, time); 752 } 753 754 /// @dev Merges lists of UIDs. 755 /// @param uidLists The provided lists of UIDs. 756 /// @param uidsCount Total UIDs count. 757 /// @return A merged and flatten list of all the UIDs. 758 function _mergeUIDs(bytes32[][] memory uidLists, uint256 uidsCount) private pure returns (bytes32[] memory) { 759 bytes32[] memory uids = new bytes32[](uidsCount); 760 761 uint256 currentIndex = 0; 762 uint256 uidListLength = uidLists.length; 763 for (uint256 i = 0; i < uidListLength; i = uncheckedInc(i)) { 764 bytes32[] memory currentUids = uidLists[i]; 765 uint256 currentUidsLength = currentUids.length; 766 for (uint256 j = 0; j < currentUidsLength; j = uncheckedInc(j)) { 767 uids[currentIndex] = currentUids[j]; 768 769 unchecked { 770 ++currentIndex; 771 } 772 } 773 } 774 775 return uids; 776 } 777 }