-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from lens-protocol/feat/more-rules
feat: BanMemberGroupRule & ReservedUsernameRule added
- Loading branch information
Showing
2 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
// Copyright (C) 2024 Lens Labs. All Rights Reserved. | ||
pragma solidity ^0.8.0; | ||
|
||
import {IGroupRule} from "./../../core/interfaces/IGroupRule.sol"; | ||
import {IAccessControl} from "./../../core/interfaces/IAccessControl.sol"; | ||
import {AccessControlLib} from "./../../core/libraries/AccessControlLib.sol"; | ||
import {Events} from "./../../core/types/Events.sol"; | ||
import {KeyValue} from "./../../core/types/Types.sol"; | ||
|
||
contract BanMemberGroupRule is IGroupRule { | ||
using AccessControlLib for IAccessControl; | ||
using AccessControlLib for address; | ||
|
||
uint256 constant BAN_MEMBER_PID = uint256(keccak256("BAN_MEMBER")); | ||
uint256 constant UNBAN_MEMBER_PID = uint256(keccak256("UNBAN_MEMBER")); | ||
|
||
// keccak256("lens.param.key.accessControl"); | ||
bytes32 immutable ACCESS_CONTROL_PARAM_KEY = 0x6552dd4db64bdb68f2725e4865ecb072df1c2befcfb455b69e2d2b886a8e185e; | ||
// keccak256("lens.param.key.banMember"); | ||
bytes32 immutable BAN_MEMBER_PARAM_KEY = 0xbb2ac1c157eaec4f8d53724664c35e575e75b44cc292e3d6dc6ff5c60a2b36a1; | ||
|
||
event Lens_BanMemberGroupRule_MemberBanned( | ||
address indexed group, bytes32 indexed configSalt, address indexed bannedAccount, address bannedBy | ||
); | ||
event Lens_BanMemberGroupRule_MemberUnbanned( | ||
address indexed group, bytes32 indexed configSalt, address indexed unbannedAccount, address unbannedBy | ||
); | ||
|
||
mapping(address => mapping(bytes32 => address)) internal _accessControl; | ||
mapping(address => mapping(bytes32 => mapping(address => bool))) internal _isMemberBanned; | ||
|
||
constructor() { | ||
emit Events.Lens_PermissionId_Available(BAN_MEMBER_PID, "BAN_MEMBER"); | ||
emit Events.Lens_PermissionId_Available(UNBAN_MEMBER_PID, "UNBAN_MEMBER"); | ||
} | ||
|
||
function ban(bytes32 configSalt, address group, address account) external { | ||
_accessControl[group][configSalt].requireAccess(msg.sender, BAN_MEMBER_PID); | ||
_isMemberBanned[group][configSalt][account] = true; | ||
emit Lens_BanMemberGroupRule_MemberBanned(group, configSalt, account, msg.sender); | ||
} | ||
|
||
function unban(bytes32 configSalt, address group, address account) external { | ||
_accessControl[group][configSalt].requireAccess(msg.sender, UNBAN_MEMBER_PID); | ||
_isMemberBanned[group][configSalt][account] = false; | ||
emit Lens_BanMemberGroupRule_MemberUnbanned(group, configSalt, account, msg.sender); | ||
} | ||
|
||
function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external override { | ||
address accessControl; | ||
for (uint256 i = 0; i < ruleParams.length; i++) { | ||
if (ruleParams[i].key == ACCESS_CONTROL_PARAM_KEY) { | ||
accessControl = abi.decode(ruleParams[i].value, (address)); | ||
break; | ||
} | ||
} | ||
accessControl.verifyHasAccessFunction(); | ||
_accessControl[msg.sender][configSalt] = accessControl; | ||
} | ||
|
||
function processAddition( | ||
bytes32 configSalt, | ||
address originalMsgSender, | ||
address account, | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata ruleParams | ||
) external override { | ||
if (_isMemberBanned[msg.sender][configSalt][account]) { | ||
for (uint256 i = 0; i < ruleParams.length; i++) { | ||
if (ruleParams[i].key == BAN_MEMBER_PARAM_KEY) { | ||
require(!abi.decode(ruleParams[i].value, (bool))); // Cannot ban while adding to the group. | ||
_isMemberBanned[msg.sender][configSalt][account] = false; | ||
_accessControl[msg.sender][configSalt].requireAccess(originalMsgSender, UNBAN_MEMBER_PID); | ||
emit Lens_BanMemberGroupRule_MemberUnbanned(msg.sender, configSalt, account, originalMsgSender); | ||
return; | ||
} | ||
} | ||
// If member is banned and the param to unban was not passed, revert. | ||
revert(); | ||
} | ||
} | ||
|
||
function processRemoval( | ||
bytes32 configSalt, | ||
address originalMsgSender, | ||
address account, | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata ruleParams | ||
) external override { | ||
for (uint256 i = 0; i < ruleParams.length; i++) { | ||
if (ruleParams[i].key == BAN_MEMBER_PARAM_KEY) { | ||
if (abi.decode(ruleParams[i].value, (bool))) { | ||
_isMemberBanned[msg.sender][configSalt][account] = true; | ||
_accessControl[msg.sender][configSalt].requireAccess(originalMsgSender, BAN_MEMBER_PID); | ||
emit Lens_BanMemberGroupRule_MemberBanned(msg.sender, configSalt, account, originalMsgSender); | ||
} else { | ||
// Cannot unban while kicking from the group. | ||
require(!_isMemberBanned[msg.sender][configSalt][account]); | ||
} | ||
return; | ||
} | ||
} | ||
} | ||
|
||
function processJoining( | ||
bytes32 configSalt, | ||
address account, | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata /* ruleParams */ | ||
) external view override { | ||
require(!_isMemberBanned[msg.sender][configSalt][account]); | ||
} | ||
|
||
function processLeaving( | ||
bytes32, /* configSalt */ | ||
address, /* account */ | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata /* ruleParams */ | ||
) external pure override { | ||
revert(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
// Copyright (C) 2024 Lens Labs. All Rights Reserved. | ||
pragma solidity ^0.8.0; | ||
|
||
import {IAccessControl} from "./../../core/interfaces/IAccessControl.sol"; | ||
import {IUsernameRule} from "./../../core/interfaces/IUsernameRule.sol"; | ||
import {AccessControlLib} from "./../../core/libraries/AccessControlLib.sol"; | ||
import {Events} from "./../../core/types/Events.sol"; | ||
import {KeyValue} from "./../../core/types/Types.sol"; | ||
|
||
contract ReservedUsernameRule is IUsernameRule { | ||
using AccessControlLib for IAccessControl; | ||
using AccessControlLib for address; | ||
|
||
// TODO: Think about renaming Username primitive to Namespace or something else | ||
event Lens_ReservedUsernameRule_UsernameReserved( | ||
address indexed usernamePrimitive, bytes32 indexed configSalt, string indexed indexedUsername, string username | ||
); | ||
event Lens_ReservedUsernameRule_UsernameReleased( | ||
address indexed usernamePrimitive, bytes32 indexed configSalt, string indexed indexedUsername, string username | ||
); | ||
event Lens_ReservedUsernameRule_ReservedUsernameCreated( | ||
address indexed usernamePrimitive, | ||
bytes32 indexed configSalt, | ||
string indexed indexedUsername, | ||
string username, | ||
address account, | ||
address createdBy | ||
); | ||
|
||
// keccak256("lens.param.key.accessControl"); | ||
bytes32 immutable ACCESS_CONTROL_PARAM_KEY = 0x6552dd4db64bdb68f2725e4865ecb072df1c2befcfb455b69e2d2b886a8e185e; | ||
// keccak256("lens.param.key.usernamesToReserve"); | ||
bytes32 immutable USERNAMES_TO_RESERVE_PARAM_KEY = 0xe35845d5270ebd172ba5dfaf14a7256cbc847d131e6a9d37dcd9bce7c75e9e77; | ||
// keccak256("lens.param.key.usernamesToRelease"); | ||
bytes32 immutable USERNAMES_TO_RELEASE_PARAM_KEY = 0x68f854736312031d89be36bdf38f9d77e90822b1a6417823a39f8721c4db9cb2; | ||
|
||
uint256 constant CREATE_RESERVED_USERNAME_PID = uint256(keccak256("CREATE_RESERVED_USERNAME")); | ||
|
||
mapping(address => mapping(bytes32 => address)) internal _accessControl; | ||
mapping(address => mapping(bytes32 => mapping(string => bool))) internal _isUsernameReserved; | ||
|
||
constructor() { | ||
emit Events.Lens_PermissionId_Available(CREATE_RESERVED_USERNAME_PID, "CREATE_RESERVED_USERNAME"); | ||
} | ||
|
||
function configure(bytes32 configSalt, KeyValue[] calldata ruleParams) external override { | ||
address accessControl; | ||
for (uint256 i = 0; i < ruleParams.length; i++) { | ||
if (ruleParams[i].key == ACCESS_CONTROL_PARAM_KEY) { | ||
accessControl = abi.decode(ruleParams[i].value, (address)); | ||
} else if (ruleParams[i].key == USERNAMES_TO_RESERVE_PARAM_KEY) { | ||
string[] memory usernamesToReserve = abi.decode(ruleParams[i].value, (string[])); | ||
for (uint256 j = 0; j < usernamesToReserve.length; j++) { | ||
require(!_isUsernameReserved[msg.sender][configSalt][usernamesToReserve[j]]); | ||
_isUsernameReserved[msg.sender][configSalt][usernamesToReserve[j]] = true; | ||
emit Lens_ReservedUsernameRule_UsernameReserved( | ||
msg.sender, configSalt, usernamesToReserve[j], usernamesToReserve[j] | ||
); | ||
} | ||
} else if (ruleParams[i].key == USERNAMES_TO_RELEASE_PARAM_KEY) { | ||
string[] memory usernamesToRelease = abi.decode(ruleParams[i].value, (string[])); | ||
for (uint256 j = 0; j < usernamesToRelease.length; j++) { | ||
require(_isUsernameReserved[msg.sender][configSalt][usernamesToRelease[j]]); | ||
_isUsernameReserved[msg.sender][configSalt][usernamesToRelease[j]] = false; | ||
emit Lens_ReservedUsernameRule_UsernameReleased( | ||
msg.sender, configSalt, usernamesToRelease[j], usernamesToRelease[j] | ||
); | ||
} | ||
} | ||
} | ||
accessControl.verifyHasAccessFunction(); | ||
_accessControl[msg.sender][configSalt] = accessControl; | ||
} | ||
|
||
function processCreation( | ||
bytes32 configSalt, | ||
address originalMsgSender, | ||
address account, | ||
string calldata username, | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata /* ruleParams */ | ||
) external override { | ||
if (_isUsernameReserved[msg.sender][configSalt][username]) { | ||
_accessControl[msg.sender][configSalt].requireAccess(originalMsgSender, CREATE_RESERVED_USERNAME_PID); | ||
emit Lens_ReservedUsernameRule_ReservedUsernameCreated( | ||
msg.sender, configSalt, username, username, account, originalMsgSender | ||
); | ||
} | ||
} | ||
|
||
function processRemoval( | ||
bytes32, /* configSalt */ | ||
address, /* originalMsgSender */ | ||
string calldata, /* username */ | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata /* ruleParams */ | ||
) external pure override { | ||
revert(); | ||
} | ||
|
||
function processAssigning( | ||
bytes32, /* configSalt */ | ||
address, /* originalMsgSender */ | ||
address, /* account */ | ||
string calldata, /* username */ | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata /* ruleParams */ | ||
) external pure override { | ||
revert(); | ||
} | ||
|
||
function processUnassigning( | ||
bytes32, /* configSalt */ | ||
address, /* originalMsgSender */ | ||
address, /* account */ | ||
string calldata, /* username */ | ||
KeyValue[] calldata, /* primitiveParams */ | ||
KeyValue[] calldata /* ruleParams */ | ||
) external pure override { | ||
revert(); | ||
} | ||
} |