Skip to content

Commit

Permalink
Get channel based entitlements for a specific permission from contrac…
Browse files Browse the repository at this point in the history
…ts (#4)

In order to mimic how we currently evaluate joinspace permissions in
river node now, this gives us a list of entitlement datas per (channel,
permission).

Refactor also of existing endpoint into a queryable interface to
separate concerns in the contracts.

---------

Co-authored-by: Giuseppe Rodriguez <[email protected]>
  • Loading branch information
clemire and giuseppecrj authored May 21, 2024
1 parent ec2e03c commit 0b2d214
Show file tree
Hide file tree
Showing 149 changed files with 2,519 additions and 1,353 deletions.
11 changes: 11 additions & 0 deletions contracts/scripts/deployments/DeploySpace.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {DeployERC721AQueryable} from "./facets/DeployERC721AQueryable.s.sol";
import {DeployBanning} from "contracts/scripts/deployments/facets/DeployBanning.s.sol";
import {DeployMembershipMetadata} from "contracts/scripts/deployments/facets/DeployMembershipMetadata.s.sol";
import {DeployMembership} from "contracts/scripts/deployments/DeployMembership.s.sol";
import {DeployEntitlementDataQueryable} from "./facets/DeployEntitlementDataQueryable.s.sol";
import {DeployMultiInit} from "contracts/scripts/deployments/DeployMultiInit.s.sol";

contract DeploySpace is DiamondDeployer {
Expand All @@ -51,6 +52,8 @@ contract DeploySpace is DiamondDeployer {
DeployMembership membershipHelper = new DeployMembership();
DeployMembershipMetadata membershipMetadataHelper =
new DeployMembershipMetadata();
DeployEntitlementDataQueryable entitlementDataQueryableHelper =
new DeployEntitlementDataQueryable();
DeployMultiInit deployMultiInit = new DeployMultiInit();

TokenOwnableHelper tokenOwnableHelper = new TokenOwnableHelper();
Expand Down Expand Up @@ -79,6 +82,7 @@ contract DeploySpace is DiamondDeployer {
address entitlementGated;
address erc721aQueryable;
address membershipMetadata;
address entitlementDataQueryable;
address multiInit;

function versionName() public pure override returns (string memory) {
Expand All @@ -96,6 +100,7 @@ contract DeploySpace is DiamondDeployer {
banning = banningHelper.deploy();
membership = membershipHelper.deploy();
membershipMetadata = membershipMetadataHelper.deploy();
entitlementDataQueryable = entitlementDataQueryableHelper.deploy();
multiInit = deployMultiInit.deploy();

vm.startBroadcast(deployerPK);
Expand Down Expand Up @@ -153,6 +158,12 @@ contract DeploySpace is DiamondDeployer {
IDiamond.FacetCutAction.Add
)
);
addCut(
entitlementDataQueryableHelper.makeCut(
entitlementDataQueryable,
IDiamond.FacetCutAction.Add
)
);

addInit(ownable, ownableHelper.makeInitData(deployer));
addInit(diamondCut, diamondCutHelper.makeInitData(""));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

//interfaces

//libraries

//contracts
import {Deployer} from "contracts/scripts/common/Deployer.s.sol";
import {FacetHelper} from "contracts/test/diamond/Facet.t.sol";
import {EntitlementDataQueryable} from "contracts/src/spaces/facets/entitlements/extensions/EntitlementDataQueryable.sol";

contract DeployEntitlementDataQueryable is Deployer, FacetHelper {
// FacetHelper
constructor() {
addSelector(
EntitlementDataQueryable.getEntitlementDataByPermission.selector
);
addSelector(
EntitlementDataQueryable.getChannelEntitlementDataByPermission.selector
);
}

// Deploying
function versionName() public pure override returns (string memory) {
return "entitlementDataQueryable";
}

function __deploy(
uint256 deployerPK,
address
) public override returns (address) {
vm.startBroadcast(deployerPK);
EntitlementDataQueryable facet = new EntitlementDataQueryable();
vm.stopBroadcast();
return address(facet);
}
}
49 changes: 0 additions & 49 deletions contracts/src/spaces/facets/entitlements/EntitlementsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,16 @@ pragma solidity ^0.8.23;

// interfaces
import {IEntitlementsManager} from "./IEntitlementsManager.sol";
import {IEntitlement} from "./../../entitlements/IEntitlement.sol";
import {IRolesBase} from "../../facets/roles/IRoles.sol";
import {IRuleEntitlement} from "./../../entitlements/rule/IRuleEntitlement.sol";
import {IUserEntitlement} from "./../../entitlements/user/IUserEntitlement.sol";

// libraries

// contracts
import {EntitlementsManagerBase} from "./EntitlementsManagerBase.sol";
import {Entitled} from "../Entitled.sol";
import {RolesBase} from "../../facets/roles/RolesBase.sol";

contract EntitlementsManager is
IEntitlementsManager,
EntitlementsManagerBase,
RolesBase,
Entitled
{
function addImmutableEntitlements(
Expand Down Expand Up @@ -59,47 +53,4 @@ contract EntitlementsManager is
) external view returns (bool) {
return _isEntitledToChannel(channelId, user, permission);
}

function getEntitlementDataByPermission(
string calldata permission
) external view returns (EntitlementData[] memory) {
IRolesBase.Role[] memory roles = _getRolesWithPermission(permission);
uint256 entitlementCount = 0;
for (uint256 i = 0; i < roles.length; i++) {
IRolesBase.Role memory role = roles[i];
if (!role.disabled) {
entitlementCount += role.entitlements.length;
}
}

EntitlementData[] memory entitlementData = new EntitlementData[](
entitlementCount
);

entitlementCount = 0;

for (uint256 i = 0; i < roles.length; i++) {
IRolesBase.Role memory role = roles[i];
if (!role.disabled) {
for (uint256 j = 0; j < role.entitlements.length; j++) {
IEntitlement entitlement = IEntitlement(role.entitlements[j]);
if (!entitlement.isCrosschain()) {
IUserEntitlement ue = IUserEntitlement(address(entitlement));
entitlementData[entitlementCount] = EntitlementData(
ue.moduleType(),
ue.getEntitlementDataByRoleId(role.id)
);
} else {
IRuleEntitlement re = IRuleEntitlement(address(entitlement));
entitlementData[entitlementCount] = EntitlementData(
re.moduleType(),
re.getEntitlementDataByRoleId(role.id)
);
}
entitlementCount++;
}
}
}
return entitlementData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ interface IEntitlementsManagerBase {
}

interface IEntitlementsManager is IEntitlementsManagerBase {
struct EntitlementData {
string entitlementType;
bytes entitlementData;
}

/// @notice Allows the space owner to add immutable entitlements to the space
/// @param entitlements The entitlements to add
function addImmutableEntitlements(address[] memory entitlements) external;
Expand Down Expand Up @@ -72,8 +67,4 @@ interface IEntitlementsManager is IEntitlementsManagerBase {
external
view
returns (Entitlement[] memory entitlements);

function getEntitlementDataByPermission(
string calldata permission
) external view returns (EntitlementData[] memory);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

// interfaces
import {IEntitlementDataQueryable} from "contracts/src/spaces/facets/entitlements/extensions/IEntitlementDataQueryable.sol";
import {IRolesBase} from "contracts/src/spaces/facets/roles/IRoles.sol";
import {IEntitlement} from "contracts/src/spaces/entitlements/IEntitlement.sol";
import {IRuleEntitlement} from "contracts/src/spaces/entitlements/rule/IRuleEntitlement.sol";
import {IUserEntitlement} from "contracts/src/spaces/entitlements/user/IUserEntitlement.sol";

// libraries
import {ChannelService} from "contracts/src/spaces/facets/channels/ChannelService.sol";

// contracts
import {RolesBase} from "contracts/src/spaces/facets/roles/RolesBase.sol";
import {Facet} from "contracts/src/diamond/facets/Facet.sol";

contract EntitlementDataQueryable is
IRolesBase,
IEntitlementDataQueryable,
RolesBase,
Facet
{
function getEntitlementDataByPermission(
string calldata permission
) external view returns (EntitlementData[] memory) {
Role[] memory roles = _getRolesWithPermission(permission);
return _getEntitlements(roles);
}

function getChannelEntitlementDataByPermission(
bytes32 channelId,
string calldata permission
) external view returns (EntitlementData[] memory) {
Role[] memory roles = _getChannelRolesWithPermission(channelId, permission);
return _getEntitlements(roles);
}

// =============================================================
// Internal
// =============================================================
function _getChannelRolesWithPermission(
bytes32 channelId,
string calldata permission
) internal view returns (Role[] memory) {
uint256[] memory channelRoles = ChannelService.getRolesByChannel(channelId);

uint256 roleCount = 0;
uint256[] memory matchedRoleIds = new uint256[](channelRoles.length);

bytes32 requestedPermission = keccak256(abi.encodePacked(permission));

// Count the number of roles that have the requested permission and record their ids.
for (uint256 i = 0; i < channelRoles.length; i++) {
Role memory role = _getRoleById(channelRoles[i]);
if (role.disabled) {
continue;
}
// Check if the role has the requested permission.
for (uint256 j = 0; j < role.permissions.length; j++) {
if (keccak256(bytes(role.permissions[j])) == requestedPermission) {
matchedRoleIds[roleCount] = role.id;
roleCount++;
break;
}
}
}

// Assemble the roles that have the requested permission for the specified channel.
Role[] memory roles = new Role[](roleCount);
for (uint256 i = 0; i < roleCount; i++) {
roles[i] = _getRoleById(matchedRoleIds[i]);
}

return roles;
}

function _getEntitlements(
Role[] memory roles
) internal view returns (EntitlementData[] memory) {
uint256 entitlementCount = 0;
for (uint256 i = 0; i < roles.length; i++) {
Role memory role = roles[i];
if (!role.disabled) {
entitlementCount += role.entitlements.length;
}
}

EntitlementData[] memory entitlementData = new EntitlementData[](
entitlementCount
);

entitlementCount = 0;

for (uint256 i = 0; i < roles.length; i++) {
Role memory role = roles[i];
if (!role.disabled) {
for (uint256 j = 0; j < role.entitlements.length; j++) {
IEntitlement entitlement = IEntitlement(role.entitlements[j]);
if (!entitlement.isCrosschain()) {
IUserEntitlement ue = IUserEntitlement(address(entitlement));
entitlementData[entitlementCount] = EntitlementData(
ue.moduleType(),
ue.getEntitlementDataByRoleId(role.id)
);
} else {
IRuleEntitlement re = IRuleEntitlement(address(entitlement));
entitlementData[entitlementCount] = EntitlementData(
re.moduleType(),
re.getEntitlementDataByRoleId(role.id)
);
}
entitlementCount++;
}
}
}
return entitlementData;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

// interfaces

// libraries

// contracts
interface IEntitlementDataQueryableBase {
struct EntitlementData {
string entitlementType;
bytes entitlementData;
}
}

interface IEntitlementDataQueryable is IEntitlementDataQueryableBase {
// Entitlement data pertaining to all roles in the space.
function getEntitlementDataByPermission(
string calldata permission
) external view returns (EntitlementData[] memory);

// Entitlement data pertaining to all roles assigned to a channel.
function getChannelEntitlementDataByPermission(
bytes32 channelId,
string calldata permission
) external view returns (EntitlementData[] memory);
}
12 changes: 7 additions & 5 deletions contracts/src/spaces/facets/roles/RolesBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ abstract contract RolesBase is IRolesBase {
uint256[] memory matchedRoleIds = new uint256[](roleIdLen);
uint256 count = 0;

bytes32 reqeustedPermission = keccak256(bytes(permission));
bytes32 requestedPermission = keccak256(bytes(permission));

// First pass to find roles with the permission and capture their IDs
for (uint256 i = 0; i < roleIdLen; i++) {
(, , string[] memory permissions, ) = _getRole(roleIds[i]);
for (uint256 j = 0; j < permissions.length; j++) {
if (keccak256(bytes(permissions[j])) == reqeustedPermission) {
if (keccak256(bytes(permissions[j])) == requestedPermission) {
matchedRoleIds[count] = roleIds[i];
count++;
break;
Expand Down Expand Up @@ -345,9 +345,11 @@ abstract contract RolesBase is IRolesBase {
IEntitlement[] memory entitlements
)
{
name = RolesStorage.layout().roleById[roleId].name;
isImmutable = RolesStorage.layout().roleById[roleId].isImmutable;
permissions = RolesStorage.layout().roleById[roleId].permissions.values();
RolesStorage.Layout storage rs = RolesStorage.layout();

name = rs.roleById[roleId].name;
isImmutable = rs.roleById[roleId].isImmutable;
permissions = rs.roleById[roleId].permissions.values();
entitlements = _getEntitlementsByRole(roleId);
}

Expand Down
Loading

0 comments on commit 0b2d214

Please sign in to comment.