From e4d8ccda0f897ed922fd17ece8cf73b3281373e7 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 18 Jul 2024 06:11:39 +0700 Subject: [PATCH] feat: users can be checked in once more --- src/interfaces/IResolver.sol | 3 --- src/resolver/Resolver.sol | 17 +++---------- test/EAS.t.sol | 48 +++++++++++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/interfaces/IResolver.sol b/src/interfaces/IResolver.sol index 035be5f..a9de103 100644 --- a/src/interfaces/IResolver.sol +++ b/src/interfaces/IResolver.sol @@ -19,9 +19,6 @@ interface IResolver { /// @return Whether the resolver supports ETH transfers. function isPayable() external pure returns (bool); - /// @dev Checks if a villager is checkedOut. - function checkedOutVillagers(address villager) external view returns (bool); - /// @dev Checks if a title is allowed to be attested. function allowedAttestationTitles(string memory title) external view returns (bool); diff --git a/src/resolver/Resolver.sol b/src/resolver/Resolver.sol index b7c350a..dd275cc 100644 --- a/src/resolver/Resolver.sol +++ b/src/resolver/Resolver.sol @@ -7,7 +7,6 @@ import { IResolver } from "../interfaces/IResolver.sol"; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import { AccessDenied, InvalidEAS, InvalidLength, uncheckedInc, EMPTY_UID, NO_EXPIRATION_TIME } from "../Common.sol"; -error AlreadyCheckedOut(); error AlreadyHasResponse(); error InsufficientValue(); error InvalidAttestationTitle(); @@ -30,9 +29,6 @@ contract Resolver is IResolver, AccessControl { bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); bytes32 public constant VILLAGER_ROLE = keccak256("VILLAGER_ROLE"); - // Maps addresses to booleans to check if a Villager has checked out - mapping(address => bool) private _checkedOutVillagers; - // Maps addresses to booleans to check if a Manager has been revoked mapping(address => bool) private _receivedManagerBadge; @@ -76,11 +72,6 @@ contract Resolver is IResolver, AccessControl { return false; } - /// @inheritdoc IResolver - function checkedOutVillagers(address villager) public view returns (bool) { - return _checkedOutVillagers[villager]; - } - /// @inheritdoc IResolver function allowedAttestationTitles(string memory title) public view returns (bool) { return _allowedAttestationTitles[keccak256(abi.encode(title))]; @@ -173,10 +164,9 @@ contract Resolver is IResolver, AccessControl { string memory status = abi.decode(attestation.data, (string)); - // Check if recipient doesn't have Villager Role and it's not checked out (haven't been checked in yet) + // Check if recipient doesn't have Villager Role (check-in) if ( !hasRole(VILLAGER_ROLE, attestation.recipient) && - !_checkedOutVillagers[attestation.recipient] && keccak256(abi.encode(status)) == keccak256(abi.encode("Check-in")) ) { _checkRole(MANAGER_ROLE, attestation.attester); @@ -184,10 +174,9 @@ contract Resolver is IResolver, AccessControl { return true; } - // Check if recipient has Villager Role and it's not checked out (is checked in) + // Check if recipient has Villager Role (check-out) if ( hasRole(VILLAGER_ROLE, attestation.recipient) && - !_checkedOutVillagers[attestation.recipient] && keccak256(abi.encode(status)) == keccak256(abi.encode("Check-out")) && (attestation.recipient == attestation.attester || hasRole(MANAGER_ROLE, attestation.attester)) ) { @@ -199,7 +188,6 @@ contract Resolver is IResolver, AccessControl { if (attesterRef.recipient != attestation.recipient) revert InvalidRefUID(); _revokeRole(VILLAGER_ROLE, attestation.recipient); - _checkedOutVillagers[attestation.recipient] = true; return true; } @@ -210,6 +198,7 @@ contract Resolver is IResolver, AccessControl { function attestEvent(Attestation calldata attestation) internal view returns (bool) { if (attestation.revocable) revert InvalidRevocability(); _checkRole(VILLAGER_ROLE, attestation.attester); + _checkRole(VILLAGER_ROLE, attestation.recipient); // Titles for attestations must be included in this contract by the managers // via the {setAttestationTitle} function diff --git a/test/EAS.t.sol b/test/EAS.t.sol index 4b7097d..fa228fa 100644 --- a/test/EAS.t.sol +++ b/test/EAS.t.sol @@ -65,22 +65,31 @@ contract ResolverTest is Test { vm.startPrank(villager); attest_villager_checkout(uids[1], villager, "Check-out", attestVillagerUID); assert(!IAccessControl(address(resolver)).hasRole(VILLAGER_ROLE, villager)); - assert(resolver.checkedOutVillagers(villager)); // Should fail to check-out again assert(!try_attest_villager_checkout(uids[1], villager, "Check-out", attestVillagerUID)); - // Should fail to check-in again - assert(!try_attest_villager(uids[1], villager, "Check-in")); // Check-Out Villager as Manager vm.startPrank(manager); bytes32 attestVillager2UID = attest_villager_checkin(uids[1], villager2, "Check-in"); attest_villager_checkout(uids[1], villager2, "Check-out", attestVillager2UID); assert(!IAccessControl(address(resolver)).hasRole(VILLAGER_ROLE, villager2)); - assert(resolver.checkedOutVillagers(villager2)); // Should fail to check-out again assert(!try_attest_villager_checkout(uids[1], villager2, "Check-out", attestVillager2UID)); - // Should fail to check-in again - assert(!try_attest_villager(uids[1], villager2, "Check-in")); + + // Villager cannot receive event badges after checkout + vm.startPrank(manager); + assert(!try_attest_event(uids[2], villager2, titles[1], "This address is a good person")); + + // Villager can be checked-in again + vm.startPrank(manager); + bytes32 attestVillagerUIDSecondTime = attest_villager_checkin(uids[1], villager, "Check-in"); + // Should have the VILLAGER_ROLE + assert(IAccessControl(address(resolver)).hasRole(VILLAGER_ROLE, villager)); + // Should be able to attest events again + vm.startPrank(villager); + attest_event(uids[2], manager, titles[2], "This address has a brilliant mind"); + // Should be able to check-out once more + attest_villager_checkout(uids[1], villager, "Check-out", attestVillagerUIDSecondTime); // Revoke Manager vm.startPrank(deployer); @@ -295,6 +304,33 @@ contract ResolverTest is Test { } } + function try_attest_event( + bytes32 schemaUID, + address recipient, + string memory title, + string memory comment + ) public returns (bool) { + try + eas.attest( + AttestationRequest({ + schema: schemaUID, + data: AttestationRequestData({ + recipient: recipient, + expirationTime: 0, + revocable: false, + refUID: 0, + data: abi.encode(title, comment), + value: 0 + }) + }) + ) + { + return true; + } catch { + return false; + } + } + function try_attest_response( bytes32 schemaUID, address recipient,