Skip to content

Commit

Permalink
validate individual party members and voting power
Browse files Browse the repository at this point in the history
  • Loading branch information
arr00 committed Aug 21, 2023
1 parent 1758411 commit 9fd0602
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 43 deletions.
64 changes: 40 additions & 24 deletions contracts/crowdfund/AtomicManualParty.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import { MetadataProvider } from "../renderers/MetadataProvider.sol";

/// @title AtomicManualParty
/// @notice Singleton that is called to create a party manually with an array
/// of party members and their votes.
/// of party members and their voting power.
contract AtomicManualParty {
/// @notice Returned if the `AtomicManualParty` is created with no members
error NoPartyMembers();
/// @notice Returned if the lengths of `partyMembers` and `partyMemberVotes` do not match
/// @notice Returned if the lengths of `partyMembers` and `partyMemberVotingPower` do not match
error PartyMembersArityMismatch();
/// @notice Returned if a party card would be issued to the null address
error InvalidPartyMember();
/// @notice Returned if a party card would be issued with no voting power
error InvalidPartyMemberVotingPower();

// The `Globals` contract storing global configuration values. This contract
// is immutable and it’s address will never change.
Expand All @@ -33,18 +37,17 @@ contract AtomicManualParty {
uint256[] memory preciousTokenIds,
uint40 rageQuitTimestamp,
address[] memory partyMembers,
uint96[] memory partyMemberVotes
uint96[] memory partyMemberVotingPower
) public returns (Party party) {
_validateAtomicManualPartyArrays(partyMembers, partyMemberVotes);
uint96 totalVotingPower = _validateAtomicManualPartyArrays(
partyMembers,
partyMemberVotingPower
);

address[] memory authorities = new address[](1);
authorities[0] = address(this);

uint96 totalVotes;
for (uint256 i; i < partyMemberVotes.length; i++) {
totalVotes += partyMemberVotes[i];
}
opts.governance.totalVotingPower = totalVotes;
opts.governance.totalVotingPower = totalVotingPower;

party = IPartyFactory(_GLOBALS.getAddress(LibGlobals.GLOBAL_PARTY_FACTORY)).createParty(
partyImpl,
Expand All @@ -55,7 +58,7 @@ contract AtomicManualParty {
rageQuitTimestamp
);

_issuePartyCards(party, partyMembers, partyMemberVotes);
_issuePartyCards(party, partyMembers, partyMemberVotingPower);
}

/// @notice Atomically creates the party and distributes the party cards
Expand All @@ -69,18 +72,17 @@ contract AtomicManualParty {
MetadataProvider provider,
bytes memory metadata,
address[] memory partyMembers,
uint96[] memory partyMemberVotes
uint96[] memory partyMemberVotingPower
) external returns (Party party) {
_validateAtomicManualPartyArrays(partyMembers, partyMemberVotes);
uint96 totalVotingPower = _validateAtomicManualPartyArrays(
partyMembers,
partyMemberVotingPower
);

address[] memory authorities = new address[](1);
authorities[0] = address(this);

uint96 totalVotes;
for (uint256 i; i < partyMemberVotes.length; i++) {
totalVotes += partyMemberVotes[i];
}
opts.governance.totalVotingPower = totalVotes;
opts.governance.totalVotingPower = totalVotingPower;

party = IPartyFactory(_GLOBALS.getAddress(LibGlobals.GLOBAL_PARTY_FACTORY))
.createPartyWithMetadata(
Expand All @@ -94,33 +96,47 @@ contract AtomicManualParty {
metadata
);

_issuePartyCards(party, partyMembers, partyMemberVotes);
_issuePartyCards(party, partyMembers, partyMemberVotingPower);
}

/// @notice Issue party cards to the party members
/// @param party The party to issue cards for
/// @param partyMembers The party members to issue cards to
/// @param partyMemberVotes The number of votes each party member gets
/// @param partyMemberVotingPower The voting power each party member gets
function _issuePartyCards(
Party party,
address[] memory partyMembers,
uint96[] memory partyMemberVotes
uint96[] memory partyMemberVotingPower
) internal {
for (uint256 i; i < partyMembers.length; i++) {
party.mint(partyMembers[i], partyMemberVotes[i], partyMembers[i]);
party.mint(partyMembers[i], partyMemberVotingPower[i], partyMembers[i]);
}
party.abdicateAuthority();
}

/// @notice Valid manual party cards arrays, returns total voting power
/// @param partyMembers The party members to issue cards to
/// @param partyMemberVotingPower The voting power each party member gets
/// @return totalVotingPower The total voting power of the party
function _validateAtomicManualPartyArrays(
address[] memory partyMembers,
uint96[] memory partyMemberVotes
) private pure {
uint96[] memory partyMemberVotingPower
) private pure returns (uint96 totalVotingPower) {
if (partyMembers.length == 0) {
revert NoPartyMembers();
}
if (partyMembers.length != partyMemberVotes.length) {
if (partyMembers.length != partyMemberVotingPower.length) {
revert PartyMembersArityMismatch();
}

for (uint256 i = 0; i < partyMemberVotingPower.length; ++i) {
if (partyMemberVotingPower[i] == 0) {
revert InvalidPartyMemberVotingPower();
}
if (partyMembers[i] == address(0)) {
revert InvalidPartyMember();
}
totalVotingPower += partyMemberVotingPower[i];
}
}
}
102 changes: 83 additions & 19 deletions test/crowdfund/AtomicManualParty.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ contract AtomicManualPartyTest is SetupPartyHelper {
opts.governance.totalVotingPower = 180;

address[] memory partyMembers = new address[](2);
uint96[] memory partyMemberVotes = new uint96[](2);
uint96[] memory partyMemberVotingPower = new uint96[](2);

partyMembers[0] = john;
partyMembers[1] = danny;

partyMemberVotes[0] = 100;
partyMemberVotes[1] = 80;
partyMemberVotingPower[0] = 100;
partyMemberVotingPower[1] = 80;

// Not checking address of the party
vm.expectEmit(false, true, true, true);
Expand All @@ -65,7 +65,7 @@ contract AtomicManualPartyTest is SetupPartyHelper {
preciousTokenIds,
0,
partyMembers,
partyMemberVotes
partyMemberVotingPower
);

// Ensure `atomicManualParty` is not an authority after creation
Expand All @@ -87,13 +87,13 @@ contract AtomicManualPartyTest is SetupPartyHelper {
opts.governance.totalVotingPower = 180;

address[] memory partyMembers = new address[](2);
uint96[] memory partyMemberVotes = new uint96[](2);
uint96[] memory partyMemberVotingPower = new uint96[](2);

partyMembers[0] = john;
partyMembers[1] = danny;

partyMemberVotes[0] = 100;
partyMemberVotes[1] = 80;
partyMemberVotingPower[0] = 100;
partyMemberVotingPower[1] = 80;

// Not checking address of the party
vm.expectEmit(false, true, true, true);
Expand All @@ -115,7 +115,7 @@ contract AtomicManualPartyTest is SetupPartyHelper {
MetadataProvider(address(0)),
"",
partyMembers,
partyMemberVotes
partyMemberVotingPower
);

// Ensure `atomicManualParty` is not an authority after creation
Expand All @@ -136,14 +136,14 @@ contract AtomicManualPartyTest is SetupPartyHelper {
opts.governance.totalVotingPower = 180;

address[] memory partyMembers = new address[](3);
uint96[] memory partyMemberVotes = new uint96[](2);
uint96[] memory partyMemberVotingPower = new uint96[](2);

partyMembers[0] = john;
partyMembers[1] = danny;
partyMembers[2] = steve;

partyMemberVotes[0] = 100;
partyMemberVotes[1] = 80;
partyMemberVotingPower[0] = 100;
partyMemberVotingPower[1] = 80;

Party partyImpl = Party(payable(address(Proxy(payable(address(party))).IMPL())));
vm.expectRevert(AtomicManualParty.PartyMembersArityMismatch.selector);
Expand All @@ -154,7 +154,7 @@ contract AtomicManualPartyTest is SetupPartyHelper {
preciousTokenIds,
0,
partyMembers,
partyMemberVotes
partyMemberVotingPower
);
}

Expand All @@ -168,7 +168,7 @@ contract AtomicManualPartyTest is SetupPartyHelper {
opts.governance.totalVotingPower = 180;

address[] memory partyMembers = new address[](0);
uint96[] memory partyMemberVotes = new uint96[](0);
uint96[] memory partyMemberVotingPower = new uint96[](0);

Party partyImpl = Party(payable(address(Proxy(payable(address(party))).IMPL())));
vm.expectRevert(AtomicManualParty.NoPartyMembers.selector);
Expand All @@ -179,7 +179,7 @@ contract AtomicManualPartyTest is SetupPartyHelper {
preciousTokenIds,
0,
partyMembers,
partyMemberVotes
partyMemberVotingPower
);
}

Expand All @@ -193,15 +193,15 @@ contract AtomicManualPartyTest is SetupPartyHelper {
opts.governance.totalVotingPower = 260;

address[] memory partyMembers = new address[](3);
uint96[] memory partyMemberVotes = new uint96[](3);
uint96[] memory partyMemberVotingPower = new uint96[](3);

partyMembers[0] = john;
partyMembers[1] = danny;
partyMembers[2] = john;

partyMemberVotes[0] = 100;
partyMemberVotes[1] = 80;
partyMemberVotes[2] = 80;
partyMemberVotingPower[0] = 100;
partyMemberVotingPower[1] = 80;
partyMemberVotingPower[2] = 80;

// Not checking address of the party
vm.expectEmit(false, true, true, true);
Expand All @@ -219,7 +219,7 @@ contract AtomicManualPartyTest is SetupPartyHelper {
preciousTokenIds,
0,
partyMembers,
partyMemberVotes
partyMemberVotingPower
);

// Ensure `atomicManualParty` is not an authority after creation
Expand All @@ -233,4 +233,68 @@ contract AtomicManualPartyTest is SetupPartyHelper {
assertEq(atomicParty.getVotingPowerAt(john, uint40(block.timestamp)), 180);
assertEq(atomicParty.getVotingPowerAt(danny, uint40(block.timestamp)), 80);
}

function test_atomicManualParty_invalidPartyMember() public {
Party.PartyOptions memory opts;
opts.name = "PARTY";
opts.symbol = "PR-T";
opts.governance.voteDuration = 99;
opts.governance.executionDelay = _EXECUTION_DELAY;
opts.governance.passThresholdBps = 1000;
opts.governance.totalVotingPower = 180;

address[] memory partyMembers = new address[](2);
uint96[] memory partyMemberVotingPower = new uint96[](2);

partyMembers[0] = john;
partyMembers[1] = address(0);

partyMemberVotingPower[0] = 100;
partyMemberVotingPower[1] = 80;

Party partyImpl = Party(payable(address(Proxy(payable(address(party))).IMPL())));

vm.expectRevert(AtomicManualParty.InvalidPartyMember.selector);
Party atomicParty = atomicManualParty.createParty(
partyImpl,
opts,
preciousTokens,
preciousTokenIds,
0,
partyMembers,
partyMemberVotingPower
);
}

function test_atomicManualParty_invalidPartyMemberVotingPower() public {
Party.PartyOptions memory opts;
opts.name = "PARTY";
opts.symbol = "PR-T";
opts.governance.voteDuration = 99;
opts.governance.executionDelay = _EXECUTION_DELAY;
opts.governance.passThresholdBps = 1000;
opts.governance.totalVotingPower = 180;

address[] memory partyMembers = new address[](2);
uint96[] memory partyMemberVotingPower = new uint96[](2);

partyMembers[0] = john;
partyMembers[1] = danny;

partyMemberVotingPower[0] = 100;
partyMemberVotingPower[1] = 0;

Party partyImpl = Party(payable(address(Proxy(payable(address(party))).IMPL())));

vm.expectRevert(AtomicManualParty.InvalidPartyMemberVotingPower.selector);
Party atomicParty = atomicManualParty.createParty(
partyImpl,
opts,
preciousTokens,
preciousTokenIds,
0,
partyMembers,
partyMemberVotingPower
);
}
}

0 comments on commit 9fd0602

Please sign in to comment.