Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions the-guild-smart-contracts/script/FullDeploymentScript.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {AttestationRequestData, AttestationRequest} from "eas-contracts/IEAS.sol
import {SchemaRegistry} from "eas-contracts/SchemaRegistry.sol";
import {TheGuildActivityToken} from "../src/TheGuildActivityToken.sol";
import {TheGuildAttestationResolver} from "../src/TheGuildAttestationResolver.sol";
import {TheGuildInternalResolver} from "../src/TheGuildInternalResolver.sol";
import {TheGuildBadgeRegistry} from "../src/TheGuildBadgeRegistry.sol";
import {TheGuildBadgeRanking} from "../src/TheGuildBadgeRanking.sol";
import {EASUtils} from "./utils/EASUtils.s.sol";
Expand Down Expand Up @@ -50,23 +51,23 @@ contract FullDeploymentScript is Script {
EASUtils.getSchemaRegistryAddress(vm)
);
bytes32 schemaId = schemaRegistry.register(schema, resolver, true);
console.logString("Schema ID:");
console.logString("Badge Attestation Schema ID:");
console.logBytes32(schemaId);

// Create some badges
badgeRegistry.createBadge(
bytes32("Rust"),
bytes("Know how to code in Rust")
);
badgeRegistry.createBadge(
bytes32("Solidity"),
bytes("Know how to code in Solidity")
);
badgeRegistry.createBadge(
bytes32("TypeScript"),
bytes("Know how to code in TypeScript")
);
// Deploy Internal Resolver via CREATE2
TheGuildInternalResolver internalResolver = new TheGuildInternalResolver{
salt: salt
}(eas, deployer);

// Register Skill Badge Schema
string memory skillSchema = "string skillDescription, bytes32[] linkedBadges";
bytes32 skillSchemaId = schemaRegistry.register(
skillSchema,
internalResolver,
true
);
console.logString("Skill Badge Schema ID:");
console.logBytes32(skillSchemaId);
// Deploy or attach to existing badge ranking via CREATE2
new TheGuildBadgeRanking{salt: salt}(badgeRegistry);

Expand Down
46 changes: 46 additions & 0 deletions the-guild-smart-contracts/script/TheGuildInternalResolver.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {SchemaResolver} from "eas-contracts/resolver/SchemaResolver.sol";
import {IEAS, Attestation} from "eas-contracts/IEAS.sol";
import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";

/// @title TheGuildInternalResolver
/// @notice EAS schema resolver that restricts attestations to authorized Guild accounts only.
contract TheGuildInternalResolver is SchemaResolver, Ownable {
mapping(address => bool) private _authorizedAttesters;

event AttesterAuthorized(address indexed attester, bool authorized);

constructor(IEAS eas, address initialOwner) SchemaResolver(eas) Ownable(initialOwner) {
_authorizedAttesters[initialOwner] = true;
emit AttesterAuthorized(initialOwner, true);
}

/// @notice Authorize or deauthorize an account to create attestations.
function setAuthorizedAttester(address attester, bool authorized) external onlyOwner {
_authorizedAttesters[attester] = authorized;
emit AttesterAuthorized(attester, authorized);
}

/// @notice Check if an account is an authorized attester.
function isAuthorizedAttester(address attester) public view returns (bool) {
return _authorizedAttesters[attester];
}

/// @inheritdoc SchemaResolver
function onAttest(
Attestation calldata attestation,
uint256
) internal view override returns (bool) {
return _authorizedAttesters[attestation.attester];
}

/// @inheritdoc SchemaResolver
function onRevoke(
Attestation calldata attestation,
uint256
) internal view override returns (bool) {
return _authorizedAttesters[attestation.attester];
}
}
46 changes: 46 additions & 0 deletions the-guild-smart-contracts/src/TheGuildInternalResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {SchemaResolver} from "eas-contracts/resolver/SchemaResolver.sol";
import {IEAS, Attestation} from "eas-contracts/IEAS.sol";
import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol";

/// @title TheGuildInternalResolver
/// @notice EAS schema resolver that restricts attestations to authorized Guild accounts only.
contract TheGuildInternalResolver is SchemaResolver, Ownable {
mapping(address => bool) private _authorizedAttesters;

event AttesterAuthorized(address indexed attester, bool authorized);

constructor(IEAS eas, address initialOwner) SchemaResolver(eas) Ownable(initialOwner) {
_authorizedAttesters[initialOwner] = true;
emit AttesterAuthorized(initialOwner, true);
}

/// @notice Authorize or deauthorize an account to create attestations.
function setAuthorizedAttester(address attester, bool authorized) external onlyOwner {
_authorizedAttesters[attester] = authorized;
emit AttesterAuthorized(attester, authorized);
}

/// @notice Check if an account is an authorized attester.
function isAuthorizedAttester(address attester) public view returns (bool) {
return _authorizedAttesters[attester];
}

/// @inheritdoc SchemaResolver
function onAttest(
Attestation calldata attestation,
uint256
) internal view override returns (bool) {
return _authorizedAttesters[attestation.attester];
}

/// @inheritdoc SchemaResolver
function onRevoke(
Attestation calldata attestation,
uint256
) internal view override returns (bool) {
return _authorizedAttesters[attestation.attester];
}
}
214 changes: 214 additions & 0 deletions the-guild-smart-contracts/test/TheGuildInternalResolver.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Test} from "forge-std/Test.sol";
import {TheGuildInternalResolver} from "../src/TheGuildInternalResolver.sol";


import {EAS} from "eas-contracts/EAS.sol";
import {SchemaRegistry} from "eas-contracts/SchemaRegistry.sol";
import {IEAS, AttestationRequest, AttestationRequestData, Attestation, RevocationRequestData, RevocationRequest} from "eas-contracts/IEAS.sol";

contract TheGuildInternalResolverTest is Test {
TheGuildInternalResolver private resolver;
SchemaRegistry private schemaRegistry;
EAS private eas;

address private owner = address(this);
address private authorizedAttester = address(0xA11CE);
address private unauthorizedAttester = address(0xBEEF);
address private recipient = address(0xCAFE);

function setUp() public {

schemaRegistry = new SchemaRegistry();
eas = new EAS(schemaRegistry);


resolver = new TheGuildInternalResolver(
IEAS(address(eas)),
owner
);
}

function _registerSchema() internal returns (bytes32) {

string memory schema = "string skillDescription, bytes32[] linkedBadges";

bytes32 schemaId = schemaRegistry.register(schema, resolver, true);
return schemaId;
}

function test_AttestationByOwnerSucceeds() public {
bytes32 schemaId = _registerSchema();

bytes32[] memory linkedBadges = new bytes32[](2);
linkedBadges[0] = bytes32("Solidity");
linkedBadges[1] = bytes32("Rust");

AttestationRequestData memory data = AttestationRequestData({
recipient: recipient,
expirationTime: 0,
revocable: true,
refUID: bytes32(0),
data: abi.encode("Master of Smart Contracts", linkedBadges),
value: 0
});

AttestationRequest memory request = AttestationRequest({
schema: schemaId,
data: data
});


eas.attest(request);

}

function test_AttestationByAuthorizedAttesterSucceeds() public {
bytes32 schemaId = _registerSchema();
resolver.setAuthorizedAttester(authorizedAttester, true);

bytes32[] memory linkedBadges = new bytes32[](1);
linkedBadges[0] = bytes32("Solidity");

AttestationRequestData memory data = AttestationRequestData({
recipient: recipient,
expirationTime: 0,
revocable: true,
refUID: bytes32(0),
data: abi.encode("Solidity Dev", linkedBadges),
value: 0
});

AttestationRequest memory request = AttestationRequest({
schema: schemaId,
data: data
});

vm.prank(authorizedAttester);
eas.attest(request);

}

function test_AttestationByUnauthorizedAttesterFails() public {
bytes32 schemaId = _registerSchema();

bytes32[] memory linkedBadges = new bytes32[](1);
linkedBadges[0] = bytes32("Solidity");

AttestationRequestData memory data = AttestationRequestData({
recipient: recipient,
expirationTime: 0,
revocable: true,
refUID: bytes32(0),
data: abi.encode("Solidity Dev", linkedBadges),
value: 0
});

AttestationRequest memory request = AttestationRequest({
schema: schemaId,
data: data
});

vm.prank(unauthorizedAttester);
vm.expectRevert();
eas.attest(request);
}

function test_RevocationByAuthorizedAttesterSucceeds() public {
bytes32 schemaId = _registerSchema();
resolver.setAuthorizedAttester(authorizedAttester, true);

bytes32[] memory linkedBadges = new bytes32[](1);
linkedBadges[0] = bytes32("Solidity");

AttestationRequestData memory data = AttestationRequestData({
recipient: recipient,
expirationTime: 0,
revocable: true,
refUID: bytes32(0),
data: abi.encode("Solidity Dev", linkedBadges),
value: 0
});

AttestationRequest memory request = AttestationRequest({
schema: schemaId,
data: data
});

vm.prank(authorizedAttester);
bytes32 uid = eas.attest(request);

vm.prank(authorizedAttester);
eas.revoke(
RevocationRequest({
schema: schemaId,
data: RevocationRequestData({uid: uid, value: 0})
})
);
// success == no revert
}

function test_RevocationByUnauthorizedAttesterFails() public {
bytes32 schemaId = _registerSchema();
resolver.setAuthorizedAttester(authorizedAttester, true);

bytes32[] memory linkedBadges = new bytes32[](1);
linkedBadges[0] = bytes32("Solidity");

AttestationRequestData memory data = AttestationRequestData({
recipient: recipient,
expirationTime: 0,
revocable: true,
refUID: bytes32(0),
data: abi.encode("Solidity Dev", linkedBadges),
value: 0
});

AttestationRequest memory request = AttestationRequest({
schema: schemaId,
data: data
});

vm.prank(authorizedAttester);
bytes32 uid = eas.attest(request);

vm.prank(unauthorizedAttester);
vm.expectRevert();
eas.revoke(
RevocationRequest({
schema: schemaId,
data: RevocationRequestData({uid: uid, value: 0})
})
);
}

function test_DeauthorizeAttester() public {
bytes32 schemaId = _registerSchema();
resolver.setAuthorizedAttester(authorizedAttester, true);
assertTrue(resolver.isAuthorizedAttester(authorizedAttester));

resolver.setAuthorizedAttester(authorizedAttester, false);
assertFalse(resolver.isAuthorizedAttester(authorizedAttester));

bytes32[] memory linkedBadges = new bytes32[](1);
linkedBadges[0] = bytes32("Solidity");

AttestationRequest memory request = AttestationRequest({
schema: schemaId,
data: AttestationRequestData({
recipient: recipient,
expirationTime: 0,
revocable: true,
refUID: bytes32(0),
data: abi.encode("Solidity Dev", linkedBadges),
value: 0
})
});

vm.prank(authorizedAttester);
vm.expectRevert();
eas.attest(request);
}
}
Loading