Skip to content

Implement GoodDaoHouses MVP smart contract build - #299

Open
L03TJ3 with Copilot wants to merge 20 commits into
masterfrom
copilot/gooddao-implementation-plan
Open

Implement GoodDaoHouses MVP smart contract build#299
L03TJ3 with Copilot wants to merge 20 commits into
masterfrom
copilot/gooddao-implementation-plan

Conversation

Copilot AI commented Jun 10, 2026

Copy link
Copy Markdown

This PR implements the GoodDaoHouses MVP as an actual smart-contract deliverable, replacing the earlier planning-only artifact. It adds the governance contract, the FlowSplitter integration surface it depends on, a mock for focused testing, and targeted governance tests covering the core membership and vote execution flows.

  • GoodDaoHouses contract

    • Adds contracts/governance/GoodDaoHouses.sol.
    • Implements the Citizens / Alignment house model with per-house minimum stake configuration.
    • Supports ERC677 transferAndCall registration, direct register-and-stake, additional staking, approval, revocation, and unstake flows.
    • Stores the requested house fields on-chain: citizen name / socialLinks, and alignment name / projectWebpage / missionStatement / distributionStrategy.
    • Uses repo-consistent upgradeable and DAO-aware patterns via DAOUpgradeableContract, NameService, access control, pausing, and avatar-authorized upgrades.
  • Membership and voting model

    • Stores member lifecycle state (None, Pending, Active, Revoked, Unstaked) and alignment eligibility records onchain.
    • Activates Citizens members immediately after staking and gates Alignment members behind committee-managed eligibility and approval.
    • Implements quarterly term voting with termDuration and votingTermLength, where the first ballot cast during the voting window lazily creates the vote record and uses timestamp / termDuration as the voteId.
    • Removes dedicated active-member and voter snapshot sets, and derives voter eligibility from active membership plus joinedAt relative to the current term vote start.
    • Stores direct recipient vote units in voteRecipientWeightedVotes, dividing weighted allocations by BASIS_POINTS.
    • Removes the separate finalize step and executes FlowSplitter updates directly from the stored recipient vote units after the voting window closes.
  • House of Alignment eligibility registry

    • Adds a GOVERNANCE_COMMITTEE_ROLE-managed on-chain pre-registration registry (HoaEligibilityRecord) for wallets allowed to join the House of Alignment.
    • setHoaEligibility(address, bool) grants or revokes eligibility; preserves full timestamp history (listedAt, updatedAt, delistedAt) on add, remove, and re-add operations.
    • getHoaEligibility(address) exposes the full record for audit and client use.
    • New Alignment member registration reverts with "Not HoA eligible" via the shared _registerMember internal path, so both registerAndStake and transferAndCall entry points behave identically; Citizens registration is unaffected.
    • Emits HoaEligibilityChanged(address indexed account, bool isEligible) on every registry change.
  • FlowSplitter execution boundary

    • Adds contracts/governance/IFlowSplitter.sol to mirror the pool-based integration surface needed by the MVP.
    • Executes vote results by translating stored voteRecipientWeightedVotes values into IFlowSplitter.Member[] at the integration boundary.
    • Requires the committee to pre-configure a pool via configureFlowSplitter(splitter, poolId) before vote execution; sends zero-unit updates on member removal or unstake.
  • Tests and supporting mock

    • Adds contracts/mocks/MockFlowSplitter.sol for focused governance testing.
    • Adds contracts/mocks/GoodDaoHousesHarness.sol — a test-only subclass that exposes a committee-gated weight setter used to inject values exceeding type(uint128).max for the overflow safety test.
    • Adds test/governance/GoodDaoHouses.test.ts.
    • Covers ERC677 registration, on-chain member field storage, HoA eligibility gating (both registration paths, Citizens bypass, Pending status after eligible registration, timestamp audit trail, committee-only access), alignment approval, lazy term vote creation, join-date-based voter eligibility, direct weighted unit storage, FlowSplitter pool configuration and unit updates, zero-unit cleanup on unstake, voting window boundary conditions, and uint128 cast safety (positive and negative paths).
  • Cleanup

    • Removes the mistakenly committed installed skills folder contents and skills-lock.json.

Testing

  • ⚠️ yarn compile (blocked in this sandbox because Hardhat could not reach binaries.soliditylang.org to download the Solidity compiler)
  • ⚠️ yarn hardhat test test/governance/GoodDaoHouses.test.ts (blocked for the same compiler-download reason)
  • ✅ Local solc compile check for contracts/governance/GoodDaoHouses.sol
  • ✅ TypeScript transpile check for test/governance/GoodDaoHouses.test.ts

Example of the implemented execution boundary:

_voteRecipientWeightedVotes[voteId][recipient] +=
  (allocation * voterWeight) / BASIS_POINTS;

IFlowSplitter.Member[] memory members = new IFlowSplitter.Member[](recipients.length);
members[i] = IFlowSplitter.Member({
  account: recipient,
  units: uint128(_voteRecipientWeightedVotes[voteId][recipient])
});

Summary by Sourcery

Introduce the GoodDaoHouses governance contract MVP with member-staking houses, a committee-curated HoA eligibility registry, and FlowSplitter-based vote execution.

New Features:

  • Add GoodDaoHouses upgradeable governance contract implementing Citizens and Alignment houses with configurable staking requirements and ERC677-based registration and staking flows.
  • Add committee-managed House of Alignment eligibility registry (HoaEligibilityRecord) with full timestamp audit trail; gates new Alignment registrations before any state is written.
  • Store rich on-chain metadata for members and manage their lifecycle and voting eligibility across term-based voting cycles.
  • Integrate with a FlowSplitter pool to translate finalized weighted vote results into member unit allocations, including zero-unit cleanup on removal or unstake.

Enhancements:

  • Add IFlowSplitter integration interface and a MockFlowSplitter implementation to provide a testable execution boundary for distributing vote outcomes.

Tests:

  • Add GoodDaoHouses governance test suite covering registration paths, HoA eligibility gating, alignment approval, term voting behavior, FlowSplitter execution, uint128 cast safety, and lifecycle edge cases.

Copilot AI linked an issue Jun 10, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Implement MVP outline for GoodDaoHouses Add maintainer-reviewable GoodDaoHouses MVP implementation plan Jun 10, 2026
Copilot AI requested a review from L03TJ3 June 10, 2026 10:00
@L03TJ3

L03TJ3 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@copilot The task was to execute on the plan, not create a new implementation plan. issue #298 is supposed be the implementation plan

Copilot AI commented Jun 10, 2026

Copy link
Copy Markdown
Author

@copilot The task was to execute on the plan, not create a new implementation plan. issue #298 is supposed be the implementation plan

Addressed in 9395d71.

@L03TJ3

L03TJ3 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
  1. remove the installed skills folder
  2. I want you to actually execute and implement the requested contract. please re-read [Plan]: GoodDaoHouses MVP implementation outline #298

The deliverible is: the smart-contract build.

@copilot

Copilot AI commented Jun 10, 2026

Copy link
Copy Markdown
Author
  1. remove the installed skills folder
  2. I want you to actually execute and implement the requested contract. please re-read [Plan]: GoodDaoHouses MVP implementation outline #298

...

Addressed in 478c1c7: removed the committed skills payload and replaced the plan-only change with the GoodDaoHouses contract implementation, FlowSplitter integration surface, and focused governance tests.

Copilot AI changed the title Add maintainer-reviewable GoodDaoHouses MVP implementation plan Implement GoodDaoHouses MVP smart contract build Jun 10, 2026
@openzeppelin-code

openzeppelin-code Bot commented Jun 10, 2026

Copy link
Copy Markdown

Implement GoodDaoHouses MVP smart contract build

Generated at commit: a465cbf36534b2f58da0ee30089626460692cf37

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
3
5
0
17
45
70
Dependencies Critical
High
Medium
Low
Note
Total
0
0
1
0
139
140

For more details view the full report in OpenZeppelin Code Inspector

@L03TJ3
L03TJ3 requested a review from sirpy June 11, 2026 05:54

@sirpy sirpy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. the fields should be written on chain. for distribution strategy it should be a url to a post on our forum.
  2. quarterly/term vote: modulo timestamp by termDuration if result is between 0 and votingTermLength then we are in voting period time frame, first vote will "create" the vote record by calling an internal createAlignmentVote, the voteId can simply be the result of timestamp divided by termDuration..
  3. no need for _activeAlignment or _activeCitizens.
  4. no need for _voteAlignmentVoters,_voteCitizensVoters, _voteWeightSnapshot assume voting weight for citizen/alignment wont change during vote. check member join date to verify he can participate in current vote.
  5. totalUnits + totalWeights not required for "vote" record in createAlignmentVote
  6. voteRecipientWeightedVotes should be divided by BASIS_POINTS
    7, finalizeAlignmentVote step is not required.
  7. in executeVote each member units is simply the value he got in voteRecipientWeightedVotes

@sirpy

sirpy commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
  1. the fields should be written on chain. for distribution strategy it should be a url to a post on our forum.
  2. quarterly/term vote: modulo timestamp by termDuration if result is between 0 and votingTermLength then we are in voting period time frame, first vote will "create" the vote record by calling an internal createAlignmentVote, the voteId can simply be the result of timestamp divided by termDuration..
  3. no need for _activeAlignment or _activeCitizens.
  4. no need for _voteAlignmentVoters,_voteCitizensVoters, _voteWeightSnapshot assume voting weight for citizen/alignment wont change during vote. check member join date to verify he can participate in current vote.
  5. totalUnits + totalWeights not required for "vote" record in createAlignmentVote
  6. voteRecipientWeightedVotes should be divided by BASIS_POINTS
    7, finalizeAlignmentVote step is not required.
  7. in executeVote each member units is simply the value he got in voteRecipientWeightedVotes

@copilot

Copilot AI commented Jun 11, 2026

Copy link
Copy Markdown
Author
  1. the fields should be written on chain. for distribution strategy it should be a url to a post on our forum.
  2. quarterly/term vote: modulo timestamp by termDuration if result is between 0 and ...

Addressed in 6fb9881.

Copilot AI requested a review from sirpy June 11, 2026 13:38
Copilot finished work on behalf of sirpy June 11, 2026 13:38
Comment thread contracts/governance/GoodDaoHouses.sol Outdated
Comment thread contracts/governance/GoodDaoHouses.sol Outdated
Comment thread contracts/governance/GoodDaoHouses.sol Outdated
expect(activeAlignmentMembers).to.deep.equal([alignmentOne.address]);
});

it("lets admin or committee set the voting schedule anchor and term lengths", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot add relevant tests

expect(activeAlignmentMembers).to.deep.equal([alignmentOne.address]);
});

it("lets admin or committee set the voting schedule anchor and term lengths", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot add tests suggested in comment

Comment thread contracts/governance/GoodDaoHouses.sol Outdated
Copilot finished work on behalf of sirpy July 23, 2026 09:13
Copilot AI requested a review from sirpy July 23, 2026 09:13
@sirpy

sirpy commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

@copilot fix latest comments by @sirpy

Copilot AI commented Jul 27, 2026

Copy link
Copy Markdown
Author

@copilot fix latest comments by @sirpy

Done in the latest commit. The three unresolved @sirpy threads are now addressed:

  1. HoA eligibility registry removed (line 73) — HoaEligibilityRecord struct, hoaEligibility mapping, setHoaEligibility, getHoaEligibility, HoaEligibilityChanged event, and the eligibility gate inside _registerMember are all removed. __gap is restored to [50]. Anyone can now stake and register as Alignment; the committee approves via approveAlignmentMember as before.

  2. stake() removed (line 292) — the standalone stake(uint256) function and its _addStake internal helper (plus the MemberStaked event) are removed.

  3. Empty-data staking path removed from onTokenTransfer (line 313) — onTokenTransfer now only handles the registration payload path; the _data.length == 0 staking branch is gone.

Tests updated to match: the registerAlignment helper no longer calls setHoaEligibility, and all HoA eligibility tests have been removed.

Copilot finished work on behalf of sirpy July 27, 2026 09:17
@sirpy
sirpy requested review from L03TJ3 and Copilot and removed request for L03TJ3 July 27, 2026 10:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (4)

contracts/governance/GoodDaoHouses.sol:526

  • getActiveMembers(house,startIndex,endIndex) can revert with an out-of-bounds read when startIndex >= memberAccounts.length (even though endIndex is clamped). Returning an empty list for an empty/out-of-range page makes the pagination API safer to consume.
		address[] memory memberAccounts = house == House.Alignment
			? hoaMembers
			: hocMembers;

		// Clamp end index to array length to avoid out-of-bounds reads.

contracts/governance/GoodDaoHouses.sol:436

  • executeVote will happily run even if flowSplitterConfig is unset, and it can also re-assign non-zero units to recipients that were revoked/unstaked after the vote was created (because execution uses the stored vote weights without checking current membership). Both cases can lead to incorrect or surprising pool state.
		require(vote.startTime > 0, "Vote missing");
		require(block.timestamp > vote.endTime, "Vote still open");
		require(!vote.executed, "Vote executed");

		// Translate finalized weighted votes into FlowSplitter unit updates.

contracts/governance/GoodDaoHouses.sol:629

  • The PR description mentions a committee-managed HoA eligibility registry (with setHoaEligibility/getHoaEligibility, audit timestamps, and a "Not HoA eligible" revert on Alignment registration). This contract currently has no eligibility record storage/events and _registerMember allows new House.Alignment registrations unconditionally (status just becomes Pending). Either the registry needs to be implemented here, or the PR description/tests should be updated to match the actual behavior.
		require(uint8(house) <= uint8(House.Alignment), "Invalid house");
		bool isNewMember = members[account].status == MemberStatus.None;
		uint64 joinedAt = isNewMember
			? uint64(block.timestamp)
			: members[account].joinedAt;

contracts/governance/GoodDaoHouses.sol:604

  • getFinalizedUnits truncates by casting the stored uint256 weight to uint128 without a bounds check. While executeVote guards against overflow, callers can read a silently-truncated value before execution (or if future weight constants change). Consider returning uint256 here or reverting when the value exceeds type(uint128).max.
	function getFinalizedUnits(
		uint256 voteId,
		address recipient
	) external view returns (uint128) {
		return uint128(voteRecipientWeightedVotes[voteId][recipient]);

Comment thread contracts/governance/GoodDaoHouses.sol
Comment thread contracts/governance/GoodDaoHouses.sol
Comment thread contracts/mocks/MockFlowSplitter.sol
socialLinks: "https://social.example/" + name
});

const registerAlignment = async (committee, goodDollar, houses, signer, name, distributionStrategy = alignmentForumUrl) => {
sirpy and others added 2 commits July 29, 2026 16:14
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@sirpy
sirpy requested review from L03TJ3 and removed request for L03TJ3 July 29, 2026 13:17
@sirpy

sirpy commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@L03TJ3

@sirpy

sirpy commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@copilot revert a0d9b95

Copilot finished work on behalf of sirpy July 30, 2026 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Plan]: GoodDaoHouses MVP implementation outline

4 participants