Skip to content

Commit 07eaec7

Browse files
committed
group management --wip-- [skip-ci]
1 parent 8b2a0ea commit 07eaec7

File tree

2 files changed

+219
-0
lines changed

2 files changed

+219
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
3+
pragma solidity >=0.5.0;
4+
5+
interface IPermissionedAssessment {
6+
struct AssessmentData {
7+
uint32 assessingGroupId;
8+
uint32 cooldownPeriod;
9+
}
10+
11+
struct AssessmentGroupView {
12+
uint id;
13+
bytes32 ipfsMetadata;
14+
uint[] assessorMemberIds;
15+
}
16+
17+
// Groups management
18+
19+
function makeNewGroup(address[] calldata assessors, bytes32 ipfsMetadata) external returns (uint groupId);
20+
21+
function addAssessorsToGroup(address[] calldata assessors, uint groupId) external;
22+
23+
function setGroupMetadata(uint groupId, bytes32 ipfsMetadata) external;
24+
25+
function removeAssessorsFromGroup(address[] calldata assessors, uint groupId) external;
26+
27+
function removeAssessorsFromAllGroups(address[] calldata assessors) external;
28+
29+
// View functions
30+
31+
function getGroupsCount() external view returns (uint groupCount);
32+
33+
function getGroupAssessorCount(uint groupId) external view returns (uint assessorCount);
34+
35+
function getGroupAssessors(uint groupId) external view returns (uint[] memory assessorMemberIds);
36+
37+
function isAssessorInGroup(uint assessorMemberId, uint groupId) external view returns (bool);
38+
39+
function getGroupsForAssessor(uint assessorMemberId) external view returns (uint[] memory groupIds);
40+
41+
function getGroupsData(uint[] calldata groupIds) external view returns (AssessmentGroupView[] memory groups);
42+
43+
// Events
44+
45+
event SetAssessmentDataForProductTypes(uint[] productTypeIds, uint cooldownPeriod, uint groupId);
46+
event AddAssessorToGroup(uint indexed groupId, uint assessorMemberId);
47+
event RemoveAssessorFromGroup(uint indexed groupId, uint assessorMemberId);
48+
event SetGroupMetadata(uint indexed groupId, uint ipfsMetadata);
49+
50+
// Errors
51+
52+
error MustBeMember(address);
53+
error InvalidGroupId();
54+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
3+
pragma solidity ^0.8.28;
4+
5+
import {EnumerableSet} from "@openzeppelin/contracts-v4/utils/structs/EnumerableSet.sol";
6+
7+
import {IPermissionedAssessment} from "../../interfaces/IPermissionedAssessment.sol";
8+
import {MasterAwareV2} from "../../abstract/MasterAwareV2.sol";
9+
import {SafeUintCast} from "../../libraries/SafeUintCast.sol";
10+
11+
contract PermissionedAssessment is IPermissionedAssessment, MasterAwareV2 {
12+
using EnumerableSet for EnumerableSet.AddressSet;
13+
using EnumerableSet for EnumerableSet.UintSet;
14+
using SafeUintCast for uint;
15+
16+
// todo: do we want this in struct or in a separate mappings
17+
mapping(uint groupId => EnumerableSet.UintSet) private _group;
18+
mapping(uint groupId => bytes32) private _groupMetadata;
19+
uint private _groupsCount;
20+
21+
// todo: should this be a set? maybe map is enough here
22+
mapping(uint assessorMemberId => EnumerableSet.UintSet) private _groupsForAssessor;
23+
24+
mapping(uint productTypeId => AssessmentData) private _assessmentData;
25+
26+
// todo: check if we want setProductTypes.setProductTypes to also be able to call this function
27+
function setAssessmentDataForProductTypes(
28+
uint[] calldata productTypeIds,
29+
uint cooldownPeriod,
30+
uint groupId
31+
) external onlyAdvisoryBoard {
32+
uint length = productTypeIds.length;
33+
for (uint i = 0; i < length; i++) {
34+
_assessmentData[productTypeIds[i]] = AssessmentData({
35+
assessingGroupId: groupId.toUint32(),
36+
cooldownPeriod: cooldownPeriod.toUint32()
37+
});
38+
}
39+
40+
emit SetAssessmentDataForProductTypes(productTypeIds, cooldownPeriod, groupId);
41+
}
42+
43+
// Group management
44+
45+
function addAssessorsToGroup(address[] calldata assessors, uint groupId) external onlyAdvisoryBoard {
46+
// make new group id
47+
if (groupId == 0) {
48+
groupId = ++_groupsCount;
49+
}
50+
51+
uint length = assessors.length;
52+
for (uint i = 0; i < length; i++) {
53+
uint assessorMemberId = _memberRoles().getMemberId(assessors[i]);
54+
require(assessorMemberId != 0, MustBeMember(assessors[i]));
55+
_group[groupId].add(assessorMemberId);
56+
_groupsForAssessor[assessorMemberId].add(groupId);
57+
emit AddAssessorToGroup(groupId, assessorMemberId);
58+
}
59+
}
60+
61+
function setGroupMetadata(uint groupId, bytes32 ipfsMetadata) external onlyAdvisoryBoard {
62+
require(groupId > 0 && groupId <= _groupsCount, InvalidGroupId());
63+
64+
_groupMetadata[groupId] = ipfsMetadata;
65+
emit SetGroupMetadata(groupId, bytes32 ipfsMetadata);
66+
}
67+
68+
// todo: remove by address or by memberId?
69+
function removeAssessorsFromGroup(address[] calldata assessors, uint groupId) external onlyAdvisoryBoard {
70+
require(groupId > 0 && groupId <= _groupsCount, InvalidGroupId());
71+
72+
uint length = assessors.length;
73+
for (uint i = 0; i < length; i++) {
74+
uint assessorMemberId = _memberRoles().getMemberId(assessors[i]);
75+
require(assessorMemberId != 0, MustBeMember(assessors[i]));
76+
_group[groupId].remove(assessorMemberId);
77+
_groupsForAssessor[assessorMemberId].remove(groupId);
78+
emit RemoveAssessorFromGroup(groupId, assessorMemberId);
79+
}
80+
}
81+
82+
function removeAssessorsFromAllGroups(address[] calldata assessors) external onlyAdvisoryBoard {
83+
uint length = assessors.length;
84+
for (uint i = 0; i < length; i++) {
85+
uint assessorMemberId = _memberRoles().getMemberId(assessors[i]);
86+
require(assessorMemberId != 0, MustBeMember(assessors[i]));
87+
88+
uint[] memory assessorsGroups = _groupsForAssessor[assessorMemberId].values();
89+
uint assessorsGroupsLength = assessorsGroups.length;
90+
for (uint groupIndex = 0; groupIndex < assessorsGroupsLength; groupIndex++) {
91+
uint groupId = assessorsGroups[groupIndex];
92+
_group[groupId].remove(assessorMemberId);
93+
emit RemoveAssessorFromGroup(groupId, assessorMemberId);
94+
}
95+
96+
_clearSet(_groupsForAssessor[assessorMemberId]._inner);
97+
}
98+
}
99+
100+
function _clearSet(EnumerableSet.Set storage set) internal {
101+
uint256 len = set._values.length;
102+
for (uint256 i = 0; i < len; ++i) {
103+
delete set._indexes[set._values[i]];
104+
}
105+
delete set._values;
106+
107+
// todo: check using a bit more optimized:
108+
// assembly {
109+
// sstore(set._values.slot, 0);
110+
// }
111+
}
112+
113+
// View functions
114+
115+
function getGroupsCount() external view returns (uint groupCount) {
116+
groupCount = _groupsCount;
117+
}
118+
119+
function getGroupAssessorCount(uint groupId) external view returns (uint assessorCount) {
120+
assessorCount = _group[groupId].length();
121+
}
122+
123+
function getGroupAssessors(uint groupId) external view returns (uint[] memory assessorMemberIds) {
124+
assessorMemberIds = _group[groupId].values();
125+
}
126+
127+
function isAssessorInGroup(uint assessorMemberId, uint groupId) external view returns (bool) {
128+
return _group[groupId].contains(assessorMemberId);
129+
}
130+
131+
function getGroupsForAssessor(uint assessorMemberId) external view returns (uint[] memory groupIds) {
132+
groupIds = _groupsForAssessor[assessorMemberId].values();
133+
}
134+
135+
function getGroupsData(uint[] calldata groupIds) external view returns (AssessmentGroupView[] memory groups) {
136+
uint length = groupIds.length;
137+
groups = new AssessmentGroupView[](length);
138+
139+
for (uint i = 0; i < length; i++) {
140+
uint groupId = groupIds[i];
141+
groups[i] = AssessmentGroupView({
142+
id: groupId,
143+
ipfsMetadata: _groupMetadata[groupId],
144+
assessorMemberIds: _group[groupId].values()
145+
});
146+
}
147+
148+
return groups;
149+
}
150+
151+
/* ========== DEPENDENCIES ========== */
152+
153+
/// @notice Gets the MemberRoles contract instance
154+
/// @return The MemberRoles contract interface
155+
/// @dev Used to access member role functionality throughout the contract
156+
function _memberRoles() internal view returns (IMemberRoles) {
157+
return IMemberRoles(getInternalContractAddress(ID.MR));
158+
}
159+
160+
/// @notice Updates internal contract addresses when contracts are added or upgraded
161+
/// @dev Automatically called by the master contract during system updates
162+
function changeDependentContractAddress() external override {
163+
internalContracts[uint(ID.MR)] = master.getLatestAddress("MR");
164+
}
165+
}

0 commit comments

Comments
 (0)