Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(guild-kick): member is jailed during the kick process #524

Merged
merged 10 commits into from
Mar 17, 2022
2 changes: 1 addition & 1 deletion configs/contracts.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ export const contracts: Array<ContractConfig> = [
version: "1.0.0",
type: ContractType.Adapter,
acls: {
dao: [daoAccessFlagsMap.SUBMIT_PROPOSAL],
dao: [daoAccessFlagsMap.SUBMIT_PROPOSAL, daoAccessFlagsMap.JAIL_MEMBER],
extensions: {
[extensionsIdsMap.BANK_EXT]: [
bankExtensionAclFlagsMap.INTERNAL_TRANSFER,
Expand Down
4 changes: 4 additions & 0 deletions contracts/adapters/ERC1155Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ contract ERC1155AdapterContract is AdapterGuard {
uint256 nftTokenId,
uint256 amount
) external reentrancyGuard(dao) {
require(
adridadou marked this conversation as resolved.
Show resolved Hide resolved
dao.notJailed((DaoHelper.msgSender(dao, msg.sender))),
"member is jailed!"
);
ERC1155TokenExtension erc1155 = ERC1155TokenExtension(
dao.getExtensionAddress(DaoHelper.ERC1155_EXT)
);
Expand Down
10 changes: 2 additions & 8 deletions contracts/adapters/GuildKick.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,7 @@ contract GuildKickContract is IGuildKick, AdapterGuard, Reimbursable {
// Starts the voting process for the guild kick proposal.
votingContract.startNewVotingForProposal(dao, proposalId, data);

GuildKickHelper.lockMemberTokens(
dao,
kicks[address(dao)][proposalId].memberToKick
);
dao.jailMember(kicks[address(dao)][proposalId].memberToKick);

// Sponsors the guild kick proposal.
dao.sponsorProposal(proposalId, submittedBy, address(votingContract));
Expand Down Expand Up @@ -136,10 +133,7 @@ contract GuildKickContract is IGuildKick, AdapterGuard, Reimbursable {
votingState == IVoting.VotingState.NOT_PASS ||
votingState == IVoting.VotingState.TIE
) {
GuildKickHelper.unlockMemberTokens(
dao,
kicks[address(dao)][proposalId].memberToKick
);
dao.unjailMember(kicks[address(dao)][proposalId].memberToKick);
} else {
revert("voting is still in progress");
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/adapters/voting/KickBadReporterAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ contract KickBadReporterAdapter is MemberGuard {
dao,
proposalId
);
GuildKickHelper.unlockMemberTokens(dao, challengeAddress);
dao.unjailMember(challengeAddress);
} else {
revert("vote not finished yet");
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/adapters/voting/OffchainVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ contract OffchainVotingContract is
_getBank(dao).balanceOf(challengedReporter, DaoHelper.UNITS)
);

GuildKickHelper.lockMemberTokens(dao, challengedReporter);
dao.jailMember(challengedReporter);

dao.submitProposal(challengeProposalId);

Expand Down
52 changes: 50 additions & 2 deletions contracts/core/DaoRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ contract DaoRegistry is MemberGuard, AdapterGuard {
event AddressConfigurationUpdated(bytes32 key, address value);

enum MemberFlag {
EXISTS
EXISTS,
JAILED
}

enum ProposalFlag {
Expand All @@ -82,7 +83,8 @@ contract DaoRegistry is MemberGuard, AdapterGuard {
SET_CONFIGURATION,
ADD_EXTENSION,
REMOVE_EXTENSION,
NEW_MEMBER
NEW_MEMBER,
JAIL_MEMBER
}

/*
Expand Down Expand Up @@ -219,6 +221,52 @@ contract DaoRegistry is MemberGuard, AdapterGuard {
emit ConfigurationUpdated(key, value);
}

function jailMember(address memberAddress)
external
hasAccess(this, AclFlag.JAIL_MEMBER)
{
require(memberAddress != address(0x0), "invalid member address");

Member storage member = members[memberAddress];
require(
fforbeck marked this conversation as resolved.
Show resolved Hide resolved
DaoHelper.getFlag(member.flags, uint8(MemberFlag.EXISTS)),
"member does not exist"
);

member.flags = DaoHelper.setFlag(
member.flags,
uint8(MemberFlag.JAILED),
true
);
}

function notJailed(address memberAddress) external view returns (bool) {
return
!DaoHelper.getFlag(
members[memberAddress].flags,
uint8(MemberFlag.JAILED)
);
}

function unjailMember(address memberAddress)
external
hasAccess(this, AclFlag.JAIL_MEMBER)
{
require(memberAddress != address(0x0), "invalid member address");

Member storage member = members[memberAddress];
require(
fforbeck marked this conversation as resolved.
Show resolved Hide resolved
DaoHelper.getFlag(member.flags, uint8(MemberFlag.EXISTS)),
"member does not exist"
);

member.flags = DaoHelper.setFlag(
member.flags,
uint8(MemberFlag.JAILED),
false
);
}

/**
* @notice Registers a member address in the DAO if it is not registered or invalid.
* @notice A potential new member is a member that holds no shares, and its registration still needs to be voted on.
Expand Down
2 changes: 2 additions & 0 deletions contracts/extensions/bank/Bank.sol
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ contract BankExtension is IExtension, ERC165 {
address token,
uint256 amount
) external hasExtensionAccess(_dao, AclFlag.INTERNAL_TRANSFER) {
require(dao.notJailed(from), "no transfer from jail");
require(dao.notJailed(to), "no transfer from jail");
uint256 newAmount = balanceOf(from, token) - amount;
uint256 newAmount2 = balanceOf(to, token) + amount;

Expand Down
1 change: 1 addition & 0 deletions contracts/extensions/erc1155/ERC1155TokenExtension.sol
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ contract ERC1155TokenExtension is IExtension, IERC1155Receiver {
uint256 nftTokenId,
uint256 amount
) external hasExtensionAccess(_dao, AclFlag.INTERNAL_TRANSFER) {
require(_dao.notJailed(fromOwner), "member is jailed!");
// Checks if there token amount is valid and has enough funds
uint256 tokenAmount = _getTokenAmount(fromOwner, nftAddr, nftTokenId);
require(
Expand Down
1 change: 1 addition & 0 deletions contracts/extensions/nft/NFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ contract NFTExtension is IExtension, IERC721Receiver {
) external hasExtensionAccess(_dao, AclFlag.INTERNAL_TRANSFER) {
require(newOwner != address(0x0), "erc721::new owner is 0");
address currentOwner = _ownership[getNFTId(nftAddr, nftTokenId)];
require(_dao.notJailed(currentOwner), "member is jailed!");
require(currentOwner != address(0x0), "erc721::nft not found");

_ownership[getNFTId(nftAddr, nftTokenId)] = newOwner;
Expand Down
12 changes: 2 additions & 10 deletions contracts/helpers/DaoHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ library DaoHelper {
address internal constant ESCROW = address(0x4bec);
address internal constant TOTAL = address(0xbabe);
address internal constant UNITS = address(0xFF1CE);
address internal constant LOCKED_UNITS = address(0xFFF1CE);
address internal constant LOOT = address(0xB105F00D);
address internal constant LOCKED_LOOT = address(0xBB105F00D);
address internal constant ETH_TOKEN = address(0x0);
address internal constant MEMBER_COUNT = address(0xDECAFBAD);

Expand Down Expand Up @@ -105,11 +103,7 @@ library DaoHelper {
view
returns (uint256)
{
return
bank.balanceOf(member, UNITS) +
bank.balanceOf(member, LOCKED_UNITS) +
bank.balanceOf(member, LOOT) +
bank.balanceOf(member, LOCKED_LOOT);
return bank.balanceOf(member, UNITS) + bank.balanceOf(member, LOOT);
}

function msgSender(DaoRegistry dao, address addr)
Expand Down Expand Up @@ -138,9 +132,7 @@ library DaoHelper {
) internal view returns (uint256) {
return
bank.getPriorAmount(member, UNITS, at) +
bank.getPriorAmount(member, LOCKED_UNITS, at) +
bank.getPriorAmount(member, LOOT, at) +
bank.getPriorAmount(member, LOCKED_LOOT, at);
bank.getPriorAmount(member, LOOT, at);
}

//helper
Expand Down
74 changes: 5 additions & 69 deletions contracts/helpers/GuildKickHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,65 +36,11 @@ SOFTWARE.
library GuildKickHelper {
address internal constant TOTAL = address(0xbabe);
address internal constant UNITS = address(0xFF1CE);
address internal constant LOCKED_UNITS = address(0xFFF1CE);
address internal constant LOOT = address(0xB105F00D);
address internal constant LOCKED_LOOT = address(0xBB105F00D);

bytes32 internal constant BANK = keccak256("bank");
address internal constant GUILD = address(0xdead);

function lockMemberTokens(DaoRegistry dao, address potentialKickedMember)
internal
{
// Get the bank extension
BankExtension bank = BankExtension(dao.getExtensionAddress(BANK));
// Calculates the total units, loot and locked loot before any internal transfers
// it considers the locked loot to be able to calculate the fair amount to ragequit,
// but locked loot can not be burned.

uint256 unitsToBurn = bank.balanceOf(potentialKickedMember, UNITS);
uint256 lootToBurn = bank.balanceOf(potentialKickedMember, LOOT);

bank.registerPotentialNewToken(dao, LOCKED_UNITS);
bank.registerPotentialNewToken(dao, LOCKED_LOOT);

bank.addToBalance(
dao,
potentialKickedMember,
LOCKED_UNITS,
unitsToBurn
);
bank.subtractFromBalance(
dao,
potentialKickedMember,
UNITS,
unitsToBurn
);

bank.addToBalance(dao, potentialKickedMember, LOCKED_LOOT, lootToBurn);
bank.subtractFromBalance(dao, potentialKickedMember, LOOT, lootToBurn);
}

function unlockMemberTokens(DaoRegistry dao, address kickedMember)
internal
{
BankExtension bank = BankExtension(dao.getExtensionAddress(BANK));

uint256 unitsToReturn = bank.balanceOf(kickedMember, LOCKED_UNITS);
uint256 lootToReturn = bank.balanceOf(kickedMember, LOCKED_LOOT);

bank.addToBalance(dao, kickedMember, UNITS, unitsToReturn);
bank.subtractFromBalance(
dao,
kickedMember,
LOCKED_UNITS,
unitsToReturn
);

bank.addToBalance(dao, kickedMember, LOOT, lootToReturn);
bank.subtractFromBalance(dao, kickedMember, LOCKED_LOOT, lootToReturn);
}

/**
* @notice Transfers the funds from the Guild account to the kicked member account based on the current kick proposal id.
* @notice The amount of funds is caculated using the actual balance of the member to make sure the member has not ragequited.
Expand All @@ -113,10 +59,10 @@ library GuildKickHelper {
// but locked loot can not be burned.
uint256 initialTotalTokens = DaoHelper.totalTokens(bank);

uint256 unitsToBurn = bank.balanceOf(kickedMember, LOCKED_UNITS);
uint256 lootToBurn = bank.balanceOf(kickedMember, LOCKED_LOOT);
uint256 unitsToBurn = bank.balanceOf(kickedMember, UNITS);
uint256 lootToBurn = bank.balanceOf(kickedMember, LOOT);
uint256 unitsAndLootToBurn = unitsToBurn + lootToBurn;

dao.unjailMember(kickedMember);
if (unitsAndLootToBurn > 0) {
// Transfers the funds from the internal Guild account to the internal member's account.
for (uint256 i = 0; i < nbTokens; i++) {
Expand Down Expand Up @@ -148,18 +94,8 @@ library GuildKickHelper {
}
}

bank.subtractFromBalance(
dao,
kickedMember,
LOCKED_UNITS,
unitsToBurn
);
bank.subtractFromBalance(
dao,
kickedMember,
LOCKED_LOOT,
lootToBurn
);
bank.subtractFromBalance(dao, kickedMember, UNITS, unitsToBurn);
bank.subtractFromBalance(dao, kickedMember, LOOT, lootToBurn);
}
}
}
1 change: 1 addition & 0 deletions utils/access-control-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const daoAccessFlagsMap: Record<string, string> = {
ADD_EXTENSION: "ADD_EXTENSION",
REMOVE_EXTENSION: "REMOVE_EXTENSION",
NEW_MEMBER: "NEW_MEMBER",
JAIL_MEMBER: "JAIL_MEMBER"
};

export const daoAccessFlags: Array<string> = Object.values(daoAccessFlagsMap);
Expand Down